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.
Defined Under Namespace
Classes: InvalidTempoError
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.
-
#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.
14 15 16 17 18 |
# File 'lib/beats/song.rb', line 14 def initialize self.tempo = DEFAULT_TEMPO @patterns = {} @flow = [] end |
Instance Attribute Details
#flow ⇒ Object
Returns the value of attribute flow.
104 105 106 |
# File 'lib/beats/song.rb', line 104 def flow @flow end |
#patterns ⇒ Object (readonly)
Returns the value of attribute patterns.
103 104 105 |
# File 'lib/beats/song.rb', line 103 def patterns @patterns end |
#tempo ⇒ Object
Returns the value of attribute tempo.
103 104 105 |
# File 'lib/beats/song.rb', line 103 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.
61 62 63 64 65 66 |
# File 'lib/beats/song.rb', line 61 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.
21 22 23 |
# File 'lib/beats/song.rb', line 21 def pattern(name) @patterns[name] = Pattern.new(name) end |
#remove_unused_patterns ⇒ Object
Removes any patterns that aren’t referenced in the flow.
98 99 100 101 |
# File 'lib/beats/song.rb', line 98 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.
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/beats/song.rb', line 71 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 |
#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.
31 32 33 |
# File 'lib/beats/song.rb', line 31 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”]
48 49 50 |
# File 'lib/beats/song.rb', line 48 def track_names @patterns.values.inject([]) {|track_names, pattern| track_names | pattern.tracks.keys }.sort end |