Skip to content

Commit

Permalink
Add basic envelope implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
scottmacphersonmusic committed May 27, 2015
1 parent 43bb80c commit 23f42e3
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 6 deletions.
1 change: 1 addition & 0 deletions lib/music_theory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
require "music_theory/output"
require "music_theory/play"
require "music_theory/pitch"
require "music_theory/envelope"


module MusicTheory
Expand Down
39 changes: 35 additions & 4 deletions lib/music_theory/envelope.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,42 @@
class Envelope
attr_accessor :attack, :sustain, :decay
attr_accessor :duration, :attack, :sustain, :decay, :sample_rate, :total_frames

def initialize(options = {})
@attack = options[:attack]
@sustain = options[:sustain]
@decay = options[:decay]
@duration = options[:duration]
@attack = options[:attack] # in seconds ex: 0.15
@sustain = options[:sustain] # percentage ex: 30
@decay = options[:decay] # percentage ex: 60
@sample_rate = 22050
@total_frames = @sample_rate * @duration
end

def attack_frames
attack_frames_count = (@sample_rate * @attack).to_i
growth_rate = (1.0 / attack_frames_count.to_f)
attack_frames = Array.new(attack_frames_count){ |index| index * growth_rate }
attack_frames
end

def sustain_frames
sustain_frames_count = (@total_frames - attack_frames.length) * (@sustain.to_f / 100.0)
sustain_frames = Array.new(sustain_frames_count) { 1 }
sustain_frames
end

def decay_frames
decay_frames_count = (@total_frames - attack_frames.length) * (@decay.to_f / 100.0)
decay_rate = (1.0 / decay_frames_count)
decay_frames = Array.new(decay_frames_count){ |index| index * decay_rate }.reverse
decay_frames
end

def envelope_frames
envelope_frames = attack_frames + sustain_frames + decay_frames
if envelope_frames.length < @total_frames
remainder = @total_frames - envelope_frames.length
filler = Array.new(remainder){ 0 }
envelope_frames += filler
end
envelope_frames
end
end
8 changes: 6 additions & 2 deletions lib/music_theory/note.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Note

def initialize(options = {})
@frequency = options[:pitch] ? options[:pitch].frequency
: options[:frequency].to_f || 440 # pitch ex) Pitch.new(:Ds, 3)
: options[:frequency].to_f || 440 # pitch ex: Pitch.new(:Ds, 3)
@duration = options[:duration] || 1.0 # Number of seconds per note
@distort = options[:distort] || false
@envelope = options[:envelope]
Expand Down Expand Up @@ -41,6 +41,7 @@ def samples
sample
end
samples = distort!(samples) if distort
samples = envelope!(samples) if envelope
samples
end

Expand All @@ -57,7 +58,10 @@ def distort!(samples)
end

def envelope!(samples)
nil # fill-in after i've built the Envelope class
envelope_frames = @envelope.envelope_frames
zipped_frames = samples.zip(envelope_frames)
enveloped_samples = zipped_frames.map! { |i| i[0] * i[1] }
enveloped_samples
end
end
end
12 changes: 12 additions & 0 deletions test/envelope_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

class TestEnvelope < MiniTest::Test
def setup
duration = 3.0
@envelope_note = MusicTheory::Note.new(pitch: Pitch.new(:A, 4),
duration: duration,
envelope: Envelope.new(duration: duration,
attack: 0.15,
sustain: 10,
decay: 70),
output_file_name: 'delete_me')
end

def test_hear_results
puts @envelope_note
@envelope_note.play
end
end

0 comments on commit 23f42e3

Please sign in to comment.