Class: InevitableCacophony::Parser::RhythmLine

Inherits:
Object
  • Object
show all
Defined in:
lib/inevitable_cacophony/parser/rhythm_line.rb

Constant Summary collapse

BEAT_VALUES =

Amplitude symbols used by Dwarf Fortress These are in no particular scale; the maximum volume will be whatever’s loudest in any particular string.

{
        
        # Silence
        '-' => 0,
        
        # Regular beat
        'x' => 4,
        
        # Accented beat
        'X' => 6,
        
        # Primary accent
        '!' => 9
}
TIMING_VALUES =

Values for each kind of timing symbol. By default a beat is in the middle of its time-slice (0.0); a value of 1.0 means to play it as late as possible, and -1.0 means play as early as possible.

Technically position of these matters, but we handle that in the parser regexp.

{
        
        # Normal beat (no special timing)
        '' => 0.0,
        
        # Early beat
        '`' => -1.0,
        
        # Late beat
        '\'' => 1.0
}
BAR_LINE =
'|'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.parse(rhythm_string) ⇒ Object



74
75
76
# File 'lib/inevitable_cacophony/parser/rhythm_line.rb', line 74

def self.parse(rhythm_string)
	new.parse(rhythm_string)
end

Instance Method Details

#parse(rhythm_string) ⇒ Rhythm

Parameters:

  • rhythm_string (String)

    In the notation Dwarf Fortress produces, like | X x ! x |

Returns:



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/inevitable_cacophony/parser/rhythm_line.rb', line 49

def parse(rhythm_string)
        
        # TODO: should I be ignoring bar lines? Is there anything I can do with them?
        raw_beats = rhythm_string.split(/ |(?=`)|(?<=')/).reject { |beat| beat == BAR_LINE }.map do |beat|
                timing_symbol = beat.chars.reject { |char| BEAT_VALUES.keys.include?(char) }.join
                timing = TIMING_VALUES[timing_symbol] || raise("Unknown timing symbol #{timing_symbol}")
        
                accent_symbol = beat.delete(timing_symbol)
                amplitude = BEAT_VALUES[accent_symbol] || raise("Unknown beat symbol #{accent_symbol}")
        
                Rhythm::Beat.new(amplitude, 1, timing)
        end
        
        # Ensure all our amplitudes are between 0.0 and 1.0
        # TODO: find a way to do this without creating twice as many beats as we need.
        highest_volume = raw_beats.map(&:amplitude).max
        scaled_beats = raw_beats.map do |beat|
                scaled = beat.amplitude.to_f / highest_volume
        
                Rhythm::Beat.new(scaled, 1, beat.timing)
        end
        
	Rhythm.new(scaled_beats)
end