Class: Beats::Song
- Inherits:
-
Object
- Object
- Beats::Song
- Defined in:
- lib/beats/song.rb
Overview
Domain object which models the ‘sheet music’ for a full song. Models the Patterns that should be played, in which order (i.e. the flow), and at which tempo.
This is the top-level model object that is used by the AudioEngine to produce actual audio data. A Song tells the AudioEngine what sounds to trigger and when. A Kit provides the sample data for each of these sounds. With a Song and a Kit the AudioEngine can produce the audio data that is saved to disk.
Constant Summary collapse
- DEFAULT_TEMPO =
120
Instance Attribute Summary collapse
-
#flow ⇒ Object
Returns the value of attribute flow.
-
#patterns ⇒ Object
readonly
Returns the value of attribute patterns.
-
#tempo ⇒ Object
Returns the value of attribute tempo.
Instance Method Summary collapse
-
#copy_ignoring_patterns_and_flow ⇒ Object
Returns a new Song that is identical but with no patterns or flow.
-
#initialize ⇒ Song
constructor
A new instance of Song.
-
#pattern(name) ⇒ Object
Adds a new pattern to the song, with the specified name.
-
#remove_unused_patterns ⇒ Object
Removes any patterns that aren’t referenced in the flow.
-
#split ⇒ Object
Splits a Song object into multiple Song objects, where each new Song only has 1 track.
-
#to_yaml(kit) ⇒ Object
Serializes the current Song to a YAML string.
-
#total_tracks ⇒ Object
The number of tracks that the pattern with the greatest number of tracks has.
-
#track_names ⇒ Object
The unique track names used in each of the song’s patterns.
Constructor Details
#initialize ⇒ Song
Returns a new instance of Song.
15 16 17 18 19 |
# File 'lib/beats/song.rb', line 15 def initialize self.tempo = DEFAULT_TEMPO @patterns = {} @flow = [] end |
Instance Attribute Details
#flow ⇒ Object
Returns the value of attribute flow.
123 124 125 |
# File 'lib/beats/song.rb', line 123 def flow @flow end |
#patterns ⇒ Object (readonly)
Returns the value of attribute patterns.
122 123 124 |
# File 'lib/beats/song.rb', line 122 def patterns @patterns end |
#tempo ⇒ Object
Returns the value of attribute tempo.
122 123 124 |
# File 'lib/beats/song.rb', line 122 def tempo @tempo end |
Instance Method Details
#copy_ignoring_patterns_and_flow ⇒ Object
Returns a new Song that is identical but with no patterns or flow.
63 64 65 66 67 68 |
# File 'lib/beats/song.rb', line 63 def copy_ignoring_patterns_and_flow copy = Song.new() copy.tempo = @tempo copy end |
#pattern(name) ⇒ Object
Adds a new pattern to the song, with the specified name.
22 23 24 25 |
# File 'lib/beats/song.rb', line 22 def pattern(name) @patterns[name] = Pattern.new(name) @patterns[name] end |
#remove_unused_patterns ⇒ Object
Removes any patterns that aren’t referenced in the flow.
100 101 102 103 |
# File 'lib/beats/song.rb', line 100 def remove_unused_patterns # Using reject() here because for some reason select() returns an Array not a Hash. @patterns.reject! {|k, pattern| !@flow.member?(pattern.name) } end |
#split ⇒ Object
Splits a Song object into multiple Song objects, where each new Song only has 1 track. For example, if a Song has 5 tracks, this will return a hash of 5 songs, each with one of the original Song’s tracks.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/beats/song.rb', line 73 def split split_songs = {} track_names = track_names() track_names.each do |track_name| new_song = copy_ignoring_patterns_and_flow() @patterns.each do |name, original_pattern| new_pattern = new_song.pattern(name) if original_pattern.tracks.has_key?(track_name) original_track = original_pattern.tracks[track_name] new_pattern.track(original_track.name, original_track.rhythm) else new_pattern.track(track_name, "." * original_pattern.step_count) end end new_song.flow = @flow split_songs[track_name] = new_song end split_songs end |
#to_yaml(kit) ⇒ Object
Serializes the current Song to a YAML string. This string can then be used to construct a new Song using the SongParser class. This lets you save a Song to disk, to be re-loaded later. Produces nicer looking output than the default version of to_yaml().
108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/beats/song.rb', line 108 def to_yaml(kit) # This implementation intentionally builds up a YAML string manually instead of using YAML::dump(). # Ruby 1.8 makes it difficult to ensure a consistent ordering of hash keys, which makes the output ugly # and also hard to test. yaml_output = "Song:\n" yaml_output += " Tempo: #{@tempo}\n" yaml_output += flow_to_yaml() yaml_output += kit.to_yaml(2) yaml_output += patterns_to_yaml() yaml_output end |
#total_tracks ⇒ Object
The number of tracks that the pattern with the greatest number of tracks has. TODO: Is it a problem that an optimized song can have a different total_tracks() value than the original? Or is that actually a good thing? TODO: Investigate replacing this with a method max_sounds_playing_at_once() or something like that. Would look each pattern along with it’s incoming overflow.
33 34 35 |
# File 'lib/beats/song.rb', line 33 def total_tracks @patterns.keys.collect {|pattern_name| @patterns[pattern_name].tracks.length }.max || 0 end |
#track_names ⇒ Object
The unique track names used in each of the song’s patterns. Sorted in alphabetical order. For example calling this method for this song:
Verse:
- bass: X...
- snare: ..X.
Chorus:
- bass: X.X.
- snare: X.X.
- hihat: XXXX
Will return: [“bass”, “hihat”, “snare”]
50 51 52 |
# File 'lib/beats/song.rb', line 50 def track_names @patterns.values.inject([]) {|track_names, pattern| track_names | pattern.tracks.keys }.sort end |