Class: Beats::Song

Inherits:
Object
  • Object
show all
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

Instance Method Summary collapse

Constructor Details

#initializeSong

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

#flowObject

Returns the value of attribute flow.



104
105
106
# File 'lib/beats/song.rb', line 104

def flow
  @flow
end

#patternsObject (readonly)

Returns the value of attribute patterns.



103
104
105
# File 'lib/beats/song.rb', line 103

def patterns
  @patterns
end

#tempoObject

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_flowObject

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_patternsObject

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

#splitObject

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_tracksObject

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_namesObject

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