music_coder 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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