music_coder 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,136 @@
1
+ # a sound to be played.
2
+ # * a Chord is made of these.
3
+ class Tone
4
+ # duration of the tone in seconds.
5
+ attr_accessor :frames
6
+ # Wave for one single wave in the tone. (Repeated for #frames)
7
+ attr_accessor :wave
8
+ # Fader for amplitude or volume (loudness), 0 to 1. 0 is silent.
9
+ # Fader.final is relative to Fader.start.
10
+ attr_accessor :amp
11
+ # Fader for tone frequency (HZ, cycles/second).
12
+ # Fader.final is relative to Fader.start.
13
+ attr_accessor :freq
14
+
15
+ # args:: a Hash containing attributes
16
+ def initialize(args = nil)
17
+ @frames = 0
18
+ @wave = Wave.new
19
+ @amp = Fader.new(0.5,0,MathUtils::GR)
20
+ @freq = Fader.new(220,0,MathUtils::GR)
21
+ init_hash(args)
22
+ end
23
+
24
+ def saturation= val
25
+ wave.saturation= val
26
+ end
27
+ def saturations
28
+ wave.saturations
29
+ end
30
+ def detail
31
+ wave.detail
32
+ end
33
+ # returns a buffer with a chord (collection of Tone whos collective amplitude equals
34
+ # the amplitude set in tone) in #wd. one tone on each element
35
+ # name:: String containing the chord name. Range, strings in Composer.chords
36
+ # tone:: Tone settings to use for each tone within the chord. Tone#freq#start
37
+ # will be ignored since we are using the note.
38
+ # element:: Element to save the used tones and notes to.
39
+ def chord(note, name, element)
40
+ out=Buffer.new
41
+ chrs=Composer.chord_notes note.note, name
42
+ notes_total = chrs.count
43
+ chrs.each_with_index do |v,i|
44
+ ltone = deep_copy
45
+ lamp = 1.0/notes_total # lower vol of each tone
46
+ ltone.amp.start *= lamp # lower vol of each tone
47
+ ltone.amp.final *= lamp # lower range of vol
48
+ # use a reduced amplitude. (by tones in chord).
49
+ # mulitplied by givin amplitude
50
+ realnote = note+v
51
+ ltone.note= realnote # set to right freq
52
+
53
+ out.push ltone.out(i,notes_total)
54
+ element.add_t ltone
55
+ element.notes.push realnote
56
+ end
57
+ out
58
+ end
59
+
60
+ # output the WaveData for a full tone (chirp). All sound created flows into this atm.
61
+ # freq_exp:: default 0. 0 means linear. higher means it reachs the frequency at the end of it's range later.
62
+ def out
63
+ # puts "out amp: #{amp.start}"
64
+ buffer_len = frames
65
+ inc_x = 0.0
66
+ data = WaveData.new
67
+ lfreq = freq.start
68
+ lamp = amp.start
69
+ wave_exp = wave.detail.exp
70
+ freq_exp = freq.exp
71
+ amp_exp = amp.exp
72
+ wave_into=0
73
+ while data.dps.count < buffer_len do
74
+ wave_data = wave.out(lfreq, lamp, wave_into)
75
+ data + wave_data
76
+ inc_x += wave_data.count
77
+ x = (inc_x.to_f / buffer_len)
78
+
79
+ #freq
80
+ freq_multiplier = x # when exp is off
81
+ #fade exponentially if on.
82
+ freq_multiplier = x ** (1.0 /((1.0/freq_exp)*x)) if freq_exp
83
+ lfreq = freq.start + freq_multiplier*freq.final
84
+
85
+ #amp
86
+ amp_multiplier = x # when exp is off
87
+ #fade exponentially if on.
88
+ amp_multiplier = x ** (1.0 /((1.0/amp_exp)*x)) if amp_exp
89
+ lamp = amp.start + amp_multiplier*amp.final
90
+
91
+ #wave
92
+ wave_into = x # when exp is off
93
+ #fade exponentially if on.
94
+ wave_into = x ** (1.0 /((1.0/wave_exp)*x)) if wave_exp
95
+
96
+ end
97
+ data.fit_to buffer_len
98
+ data
99
+ end
100
+
101
+ # setting it absolute
102
+ def set_freq_final_no_relative(final)
103
+ dif=final - freq.start
104
+ freq.final=dif
105
+ end
106
+
107
+ # set #freq based off a Note
108
+ def note=(note)
109
+ freq.start = note.freq
110
+ end
111
+ def note_end=(note)
112
+ # freq.final = note.freq
113
+ end
114
+
115
+ # return the note closest to the set frequency
116
+ def note
117
+ out = Note.new
118
+ fre = freq.start
119
+ linear_frequency = Math.log(fre/220.0,2.0) + 4
120
+ # puts "linear_frequency #{linear_frequency}"
121
+ out.octave= ( linear_frequency ).floor
122
+ cents = 1200.0 * (linear_frequency - out.octave)
123
+ not_wrap = (cents / 99.0)
124
+ # puts "note no wrap #{not_wrap}"
125
+ out.note = not_wrap.floor % 12
126
+ out
127
+ end
128
+
129
+ # make it end with an amlitute of 0 (complete fade out).
130
+ def fade
131
+ amp.final = -amp.start
132
+ self
133
+ end
134
+
135
+
136
+ end
@@ -0,0 +1,96 @@
1
+ # two tones that can be faded between
2
+ class TonePart
3
+ #Fader
4
+ attr_accessor :tones
5
+ attr_accessor :max_frames
6
+ attr_accessor :tone_single
7
+ attr_accessor :tone_count
8
+ def initialize(m,t1=Tone.new,t2=Tone.new)
9
+ @tones = Fader.new(t1,t2,0)
10
+ @max_frames = m
11
+ self.frames = m
12
+ @tone_count = 1
13
+ @tone_single = t1
14
+ end
15
+
16
+ def tone i=0
17
+ return tone_single if tone_count == 1
18
+ i==1 ? @tones.final : @tones.start
19
+ end
20
+
21
+ def max_frames= m
22
+ @max_frames = m
23
+ # set if none
24
+ self.frames = m if @tones.start.frames == 0 && @tones.final.frames == 0
25
+ end
26
+
27
+ def frames= val
28
+ @tones.start.frames = val
29
+ @tones.final.frames = val
30
+ end
31
+
32
+ # get main freq
33
+ def freq
34
+ tones.start.freq.start
35
+ end
36
+ #get main note
37
+ def note
38
+ tones.start.note
39
+ end
40
+
41
+ def freq= val
42
+ @tones.start.freq.start = val
43
+ @tones.final.freq.start = val
44
+ end
45
+
46
+ def amp_mult(factor)
47
+ tones.start.amp.start *= factor
48
+ #puts "amp: #{tone.tones.start.amp.start}"
49
+ tones.start.amp.final *= factor
50
+ tones.final.amp.start *= factor
51
+ tones.final.amp.final *= factor
52
+ end
53
+
54
+
55
+ #return tone with mixed settings of tones#start with tones#final.
56
+ #into:: how much of the final tone is mixed in. 0 is none. Range: 0 to 1
57
+ def out(into)
58
+ return tone_single if tone_count == 1
59
+
60
+ out = tones.start.deep_copy
61
+
62
+ #wave
63
+ range = tones.final.wave.detail.start - tones.start.wave.detail.start
64
+ out.wave.detail.start += range*into
65
+ range = tones.final.wave.detail.final - tones.start.wave.detail.final
66
+ out.wave.detail.final += range*into
67
+ range = tones.final.wave.saturations.start - tones.start.wave.saturations.start
68
+ out.wave.saturations.start += range*into
69
+ range = tones.final.wave.saturations.final - tones.start.wave.saturations.final
70
+ out.wave.saturations.final += range*into
71
+
72
+ #frames
73
+ range = tones.final.frames - tones.start.frames
74
+ out.frames += range*into
75
+ # puts "frames range #{out.frames}"
76
+
77
+ #freq
78
+ range = tones.final.freq.start - tones.start.freq.start
79
+ out.freq.start += range*into
80
+ range = tones.final.freq.final - tones.start.freq.final
81
+ out.freq.final += range*into
82
+ range = tones.final.freq.exp_no_nil - tones.start.freq.exp_no_nil
83
+ out.freq.exp = out.freq.exp_no_nil + range*into
84
+
85
+ #amp
86
+ range = tones.final.amp.start - tones.start.amp.start
87
+ out.amp.start += range*into
88
+ range = tones.final.amp.final - tones.start.amp.final
89
+ out.amp.final += range*into
90
+ range = tones.final.amp.exp_no_nil - tones.start.amp.exp_no_nil
91
+ out.amp.exp = out.amp.exp_no_nil + range*into
92
+
93
+ out
94
+ end
95
+
96
+ end
@@ -0,0 +1,42 @@
1
+ # a sequence of TonePart that will play sequentially. These are highest level sounds without timing.
2
+ class ToneSeq
3
+ #Array of TonePart
4
+ attr_accessor :toneparts
5
+ def initialize()
6
+ @toneparts = []
7
+ make(1)
8
+ end
9
+ def tonepart i=0
10
+ @toneparts[i]
11
+ end
12
+ def frames= val
13
+ @toneparts.each {|tp| tp.frames = val}
14
+ end
15
+ def fade
16
+ @toneparts.each {|tp|
17
+ tp.tones.start.fade
18
+ tp.tones.final.fade
19
+ }
20
+ self
21
+ end
22
+ def len= set
23
+ @toneparts.each {|tp|
24
+ tp.max_frames = set / toneparts.count
25
+ tp.frames = set / toneparts.count
26
+ }
27
+ end
28
+ # add num TonePart to self, with it's max allowable frames as len.
29
+ def make(num,len=0)
30
+ # puts "ToneSeq: making #{num} parts in tone"
31
+ num.times { self.toneparts.push TonePart.new((len.to_f/num).round) }
32
+ end
33
+
34
+ def render(parent_hit_index=nil)
35
+ files=FileList.new
36
+ toneparts.each do |tp|
37
+ files.write tp.out(parent_hit_index).out
38
+ end
39
+ files
40
+ end
41
+
42
+ end
@@ -0,0 +1,77 @@
1
+ #a waveform. this is looped to create a Tone.
2
+ class Wave
3
+ # stores the detail amount in a #Fader.start. nil means use sin wave during generation.
4
+ # Fader.start is the main
5
+ # Fader.final is at the end. nil means same as wave start
6
+ attr_accessor :detail
7
+ #saturation effect (a random fluctuation on each data point to the cycle).
8
+ #degree:: The ammount of saturation. Higher is more, 0 is none. range: 0 to 1
9
+ attr_accessor :saturations
10
+ # hack to dramatically speed it up when on.
11
+ attr_accessor :cache_wave, :old_wave
12
+
13
+ def initialize(start=512,final=512,exp=0)
14
+ @detail = Fader.new(start, final, exp)
15
+ @saturations = Fader.new(0,0,0)
16
+ @cache_wave=Array.new(2)
17
+ @old_wave = nil#Wave.new # so false first time with is_eql
18
+ end
19
+
20
+ def saturation= val
21
+ saturations.start = val
22
+ saturations.final = val
23
+ end
24
+
25
+ # return WaveData of data for a single wave cycle
26
+ # len:: length in frames
27
+ # amp:: amplitude or volume (loudness), 0 to 1. 0 is silent
28
+ # wave_into:: how much of the final wave data is used this time. range: 0 to 1.
29
+ #
30
+ def out(freq, amp = 1, wave_into = 1)
31
+ final=WaveData.new
32
+ start=WaveData.new
33
+ if is_eql old_wave
34
+ # puts "using cahce"
35
+ start = cache_wave[0]
36
+ final = cache_wave[1]
37
+ else
38
+ # puts "not using cahce"
39
+ self.cache_wave=[nil,nil]
40
+ self.old_wave=self.dup
41
+ self.cache_wave[0] =WaveData.new(MathUtils.sinwave(detail.start, saturations.start))
42
+ self.cache_wave[1] =WaveData.new(MathUtils.sinwave(detail.final, saturations.final))
43
+ start = cache_wave[0]
44
+ final = cache_wave[1]
45
+ end
46
+
47
+ data = []
48
+ len = Composer.samplerate / freq
49
+
50
+ len.to_i.times do |i|
51
+ progress=i.to_f/len # from 0 to 1
52
+ if (detail.start > 0)
53
+ val = start.interpolate progress
54
+ # merging two waveforms
55
+ if (detail.final > 0)
56
+ val2 = final.interpolate progress
57
+ val_range = val2-val
58
+ val = val + val_range*wave_into
59
+ end
60
+ else # normal sign wave
61
+ raise "Error, wave detail isn't > 0"
62
+ # val = Math.sin(2.0*Math::PI*progress)
63
+ end
64
+ # puts "amp #{amp}, val #{val}"
65
+ val *= amp # reduce volume by this
66
+ data[i] = val
67
+ end
68
+ # puts "--> values: #{data.join(', ')}"
69
+ result=WaveData.new(data)
70
+ return result
71
+ end
72
+
73
+ def is_eql(other)
74
+ vars_eql?(other, ['@detail','@saturations'])
75
+ end
76
+
77
+ end
@@ -0,0 +1,103 @@
1
+ # basically an array of data of a fully rendered down sound.
2
+ class WaveData
3
+ # the data points in an Array of Float. range -1 to 1 unless you like distortion.
4
+ attr_accessor :dps
5
+ # Array containing blanks frames at the start as Integer at [0], end at [1]
6
+ attr_accessor :blanks
7
+ attr_accessor :out_file
8
+ def initialize(dps=Array.new,file=nil)
9
+ @dps = dps.dup
10
+ @blanks = [0,0]
11
+ @out_file=file
12
+ end
13
+
14
+ #TODO
15
+ def included_blanks
16
+ end
17
+
18
+
19
+ #return the datapoints within a range of indexs.
20
+ def get(start, final=nil)
21
+ final ||= dps.count
22
+ WaveData.new dps.slice(start..final)
23
+ end
24
+
25
+ # fit to the size of #dps to len by removing or adding tailing points
26
+ # fade_frames:: stop the annoying popping at end of sound by fading out this many frames
27
+ def fit_to(len, fade_frames=250)
28
+ meant_to_be = len
29
+ self.dps.pop(dps.count- meant_to_be) if meant_to_be < dps.count
30
+ while meant_to_be > dps.count # too short
31
+ self.dps.push 0
32
+ end
33
+ # stop the annoying popping
34
+ if dps.count > fade_frames
35
+ fade_frames.times do |i|
36
+ dps[dps.count-1-i] *= i.to_f / fade_frames
37
+ end
38
+ end
39
+ self
40
+ end
41
+
42
+ #duplicate the current data n times.
43
+ def dup(n=1)
44
+ n.times{dps.concat(dps)}
45
+ self
46
+ end
47
+
48
+ #return the value of a wave at the specifed progress 0 to 1
49
+ def interpolate(progress)
50
+ progress *= dps.count # range from 0 to wavedata length.
51
+ val = dps[progress.to_i] # truncate progress.
52
+ #puts progress.to_i
53
+ # now add the interpolation to the next point
54
+ if (progress < dps.count-1) # avoid error on last point
55
+ dif_x = progress.to_i+1 - progress # how far to next datapoint? 0 to 1
56
+ dif_y = dps[progress.to_i+1] - dps[progress.to_i]
57
+ interpolation = dif_x * dif_y
58
+ val += interpolation
59
+ end
60
+ return val
61
+ end
62
+
63
+ # return num of #dps
64
+ def count
65
+ dps.count
66
+ end
67
+
68
+ def add(array)
69
+ self.dps+= array
70
+ self
71
+ end
72
+
73
+ #appends Array data to dps
74
+ def +(data)
75
+ self.dps+= data.dps
76
+ self
77
+ end
78
+
79
+ #append silence to #dps.
80
+ #frames:: duration of the silence in seconds
81
+ #default: one beat.
82
+ #val:: constant middle value in buffer.
83
+ #default: 0 Range: -1 to 1
84
+ def silence(frames, val = 0)
85
+ d=WaveData.new(Array.new(frames, val))
86
+ self+d if frames > 0
87
+ self
88
+ end
89
+
90
+ # combine my dps with WaveData wave.
91
+ # adding length if necessary.
92
+ def mix(wave)
93
+ other = wave.dps
94
+ longest = dps.count > other.count ? dps : other
95
+ longest.each_with_index do |a,i|
96
+ my_v = (dps[i].nil? ? 0 : dps[i])
97
+ oth_v = (other[i].nil? ? 0 : other[i])
98
+ dps[i] = my_v + oth_v
99
+ end
100
+ self
101
+ end
102
+
103
+ end