Module: HeadMusic::Notation::DottedDuration
- Defined in:
- lib/head_music/notation/dotted_duration.rb
Overview
The exact fractional length of a (possibly dotted) rhythmic value in terms of its own unit, and the inverse: the rhythmic value a fraction of a whole note denotes. RhythmicValue's own value methods return Floats, so the fraction is rebuilt here from the unit's integer numerator and denominator to keep downstream arithmetic (ABC multipliers, MusicXML divisions, LilyPond bar sums) exact.
Constant Summary collapse
- MAX_FRACTION =
Longest supported duration: a maxima (8 whole notes).
Rational(8)
- DOTS_BY_ODD_FACTOR =
A reduced binary fraction's odd factor determines the dot count: 1 -> plain, 3 -> dotted, 7 -> double-dotted, 15 -> triple-dotted.
{1 => 0, 3 => 1, 7 => 2, 15 => 3}.freeze
- UNIT_NAMES_BY_MULTIPLE =
{1 => "whole", 2 => "double whole", 4 => "longa", 8 => "maxima"}.freeze
Class Method Summary collapse
-
.build_rhythmic_value(fraction) ⇒ Object
Fractions whose odd factor is one less than a power of two map onto a single (possibly dotted) note; anything else becomes a chain of tied notes, peeling off the largest dotted-expressible head each pass.
- .dotted_unit_fraction(rhythmic_value) ⇒ Object
- .expressible?(fraction) ⇒ Boolean
-
.greedy_head(fraction) ⇒ Object
The largest leading run of set bits (capped at four, i.e. triple-dotted) forms a dotted-expressible head for the tied-value decomposition.
-
.leading_set_bits(numerator, bits) ⇒ Object
Length of the leading run of set bits, capped at four (triple-dotted).
- .odd_factor(integer) ⇒ Object
- .power_of_two?(integer) ⇒ Boolean
-
.rhythmic_value_for(fraction) ⇒ Object
The rhythmic value spanning a fraction of a whole note, or nil when no binary note value (or tied chain of them) can express it.
- .single_value(fraction, dots, tied_value: nil) ⇒ Object
- .unit_for(unit_fraction) ⇒ Object
Instance Method Summary collapse
-
#build_rhythmic_value(fraction) ⇒ Object
private
Fractions whose odd factor is one less than a power of two map onto a single (possibly dotted) note; anything else becomes a chain of tied notes, peeling off the largest dotted-expressible head each pass.
- #dotted_unit_fraction(rhythmic_value) ⇒ Object private
- #expressible?(fraction) ⇒ Object private
-
#greedy_head(fraction) ⇒ Object
private
The largest leading run of set bits (capped at four, i.e. triple-dotted) forms a dotted-expressible head for the tied-value decomposition.
-
#leading_set_bits(numerator, bits) ⇒ Object
private
Length of the leading run of set bits, capped at four (triple-dotted).
- #odd_factor(integer) ⇒ Object private
- #power_of_two?(integer) ⇒ Object private
-
#rhythmic_value_for(fraction) ⇒ Object
private
The rhythmic value spanning a fraction of a whole note, or nil when no binary note value (or tied chain of them) can express it.
- #single_value(fraction, dots, tied_value: nil) ⇒ Object private
- #unit_for(unit_fraction) ⇒ Object private
Class Method Details
.build_rhythmic_value(fraction) ⇒ Object
Fractions whose odd factor is one less than a power of two map onto a single (possibly dotted) note; anything else becomes a chain of tied notes, peeling off the largest dotted-expressible head each pass.
44 45 46 47 48 49 50 51 52 53 |
# File 'lib/head_music/notation/dotted_duration.rb', line 44 def build_rhythmic_value(fraction) dots = DOTS_BY_ODD_FACTOR[odd_factor(fraction.numerator)] return single_value(fraction, dots) if dots head = greedy_head(fraction) tail = build_rhythmic_value(fraction - head) return unless tail single_value(head, DOTS_BY_ODD_FACTOR.fetch(odd_factor(head.numerator)), tied_value: tail) end |
.dotted_unit_fraction(rhythmic_value) ⇒ Object
21 22 23 24 25 26 |
# File 'lib/head_music/notation/dotted_duration.rb', line 21 def dotted_unit_fraction(rhythmic_value) unit = rhythmic_value.unit dots = rhythmic_value.dots # A value with d dots spans (2^(d+1) - 1) / 2^d of its own unit. Rational(unit.numerator, unit.denominator) * Rational((2**(dots + 1)) - 1, 2**dots) end |
.expressible?(fraction) ⇒ Boolean
37 38 39 |
# File 'lib/head_music/notation/dotted_duration.rb', line 37 def expressible?(fraction) fraction.positive? && fraction <= MAX_FRACTION && power_of_two?(fraction.denominator) end |
.greedy_head(fraction) ⇒ Object
The largest leading run of set bits (capped at four, i.e. triple-dotted) forms a dotted-expressible head for the tied-value decomposition.
65 66 67 68 69 70 |
# File 'lib/head_music/notation/dotted_duration.rb', line 65 def greedy_head(fraction) numerator = fraction.numerator bits = numerator.bit_length run = leading_set_bits(numerator, bits) Rational(((1 << run) - 1) << (bits - run), fraction.denominator) end |
.leading_set_bits(numerator, bits) ⇒ Object
Length of the leading run of set bits, capped at four (triple-dotted).
73 74 75 76 77 |
# File 'lib/head_music/notation/dotted_duration.rb', line 73 def leading_set_bits(numerator, bits) run = 0 run += 1 while run < 4 && run < bits && numerator[bits - 1 - run] == 1 run end |
.odd_factor(integer) ⇒ Object
88 89 90 91 |
# File 'lib/head_music/notation/dotted_duration.rb', line 88 def odd_factor(integer) integer >>= 1 while integer.even? integer end |
.power_of_two?(integer) ⇒ Boolean
93 94 95 |
# File 'lib/head_music/notation/dotted_duration.rb', line 93 def power_of_two?(integer) (integer & (integer - 1)).zero? end |
.rhythmic_value_for(fraction) ⇒ Object
The rhythmic value spanning a fraction of a whole note, or nil when no binary note value (or tied chain of them) can express it.
30 31 32 33 34 35 |
# File 'lib/head_music/notation/dotted_duration.rb', line 30 def rhythmic_value_for(fraction) fraction = Rational(fraction) return unless expressible?(fraction) build_rhythmic_value(fraction) end |
.single_value(fraction, dots, tied_value: nil) ⇒ Object
55 56 57 58 59 60 61 |
# File 'lib/head_music/notation/dotted_duration.rb', line 55 def single_value(fraction, dots, tied_value: nil) # A value with d dots spans (2^(d+1) - 1) / 2^d of its unit. unit = unit_for(fraction * Rational(2**dots, (2**(dots + 1)) - 1)) return unless unit HeadMusic::Rudiment::RhythmicValue.new(unit, dots: dots, tied_value: tied_value) end |
.unit_for(unit_fraction) ⇒ Object
79 80 81 82 83 84 85 86 |
# File 'lib/head_music/notation/dotted_duration.rb', line 79 def unit_for(unit_fraction) if unit_fraction >= 1 name = UNIT_NAMES_BY_MULTIPLE[unit_fraction.numerator] name && HeadMusic::Rudiment::RhythmicUnit.get(name) else HeadMusic::Rudiment::RhythmicUnit.for_denominator_value(unit_fraction.denominator) end end |
Instance Method Details
#build_rhythmic_value(fraction) ⇒ Object (private)
Fractions whose odd factor is one less than a power of two map onto a single (possibly dotted) note; anything else becomes a chain of tied notes, peeling off the largest dotted-expressible head each pass.
44 45 46 47 48 49 50 51 52 53 |
# File 'lib/head_music/notation/dotted_duration.rb', line 44 def build_rhythmic_value(fraction) dots = DOTS_BY_ODD_FACTOR[odd_factor(fraction.numerator)] return single_value(fraction, dots) if dots head = greedy_head(fraction) tail = build_rhythmic_value(fraction - head) return unless tail single_value(head, DOTS_BY_ODD_FACTOR.fetch(odd_factor(head.numerator)), tied_value: tail) end |
#dotted_unit_fraction(rhythmic_value) ⇒ Object (private)
21 22 23 24 25 26 |
# File 'lib/head_music/notation/dotted_duration.rb', line 21 def dotted_unit_fraction(rhythmic_value) unit = rhythmic_value.unit dots = rhythmic_value.dots # A value with d dots spans (2^(d+1) - 1) / 2^d of its own unit. Rational(unit.numerator, unit.denominator) * Rational((2**(dots + 1)) - 1, 2**dots) end |
#expressible?(fraction) ⇒ Object (private)
37 38 39 |
# File 'lib/head_music/notation/dotted_duration.rb', line 37 def expressible?(fraction) fraction.positive? && fraction <= MAX_FRACTION && power_of_two?(fraction.denominator) end |
#greedy_head(fraction) ⇒ Object (private)
The largest leading run of set bits (capped at four, i.e. triple-dotted) forms a dotted-expressible head for the tied-value decomposition.
65 66 67 68 69 70 |
# File 'lib/head_music/notation/dotted_duration.rb', line 65 def greedy_head(fraction) numerator = fraction.numerator bits = numerator.bit_length run = leading_set_bits(numerator, bits) Rational(((1 << run) - 1) << (bits - run), fraction.denominator) end |
#leading_set_bits(numerator, bits) ⇒ Object (private)
Length of the leading run of set bits, capped at four (triple-dotted).
73 74 75 76 77 |
# File 'lib/head_music/notation/dotted_duration.rb', line 73 def leading_set_bits(numerator, bits) run = 0 run += 1 while run < 4 && run < bits && numerator[bits - 1 - run] == 1 run end |
#odd_factor(integer) ⇒ Object (private)
88 89 90 91 |
# File 'lib/head_music/notation/dotted_duration.rb', line 88 def odd_factor(integer) integer >>= 1 while integer.even? integer end |
#power_of_two?(integer) ⇒ Object (private)
93 94 95 |
# File 'lib/head_music/notation/dotted_duration.rb', line 93 def power_of_two?(integer) (integer & (integer - 1)).zero? end |
#rhythmic_value_for(fraction) ⇒ Object (private)
The rhythmic value spanning a fraction of a whole note, or nil when no binary note value (or tied chain of them) can express it.
30 31 32 33 34 35 |
# File 'lib/head_music/notation/dotted_duration.rb', line 30 def rhythmic_value_for(fraction) fraction = Rational(fraction) return unless expressible?(fraction) build_rhythmic_value(fraction) end |
#single_value(fraction, dots, tied_value: nil) ⇒ Object (private)
55 56 57 58 59 60 61 |
# File 'lib/head_music/notation/dotted_duration.rb', line 55 def single_value(fraction, dots, tied_value: nil) # A value with d dots spans (2^(d+1) - 1) / 2^d of its unit. unit = unit_for(fraction * Rational(2**dots, (2**(dots + 1)) - 1)) return unless unit HeadMusic::Rudiment::RhythmicValue.new(unit, dots: dots, tied_value: tied_value) end |
#unit_for(unit_fraction) ⇒ Object (private)
79 80 81 82 83 84 85 86 |
# File 'lib/head_music/notation/dotted_duration.rb', line 79 def unit_for(unit_fraction) if unit_fraction >= 1 name = UNIT_NAMES_BY_MULTIPLE[unit_fraction.numerator] name && HeadMusic::Rudiment::RhythmicUnit.get(name) else HeadMusic::Rudiment::RhythmicUnit.for_denominator_value(unit_fraction.denominator) end end |