music_coder 0.9.0 → 0.9.1
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.
- data/lib/api/api.rb +18 -19
- data/lib/api/dist.rb +106 -38
- data/lib/api/hit_sq.rb +17 -1
- data/lib/api/mapper.rb +252 -13
- data/lib/api/snd.rb +19 -0
- data/lib/audio_output.rb +2 -1
- data/lib/fader.rb +2 -2
- data/lib/file_list.rb +20 -8
- data/lib/math_utils.rb +1 -0
- data/lib/music_coder.rb +6 -1
- data/lib/snd_dist.rb +5 -3
- data/lib/tests.rb +4 -0
- data/lib/tone.rb +5 -1
- data/lib/tone_part.rb +16 -4
- data/lib/tone_seq.rb +17 -3
- metadata +12 -4
data/lib/api/api.rb
CHANGED
|
@@ -226,23 +226,22 @@ end
|
|
|
226
226
|
# App.out.snddists[0]
|
|
227
227
|
#end
|
|
228
228
|
|
|
229
|
-
|
|
230
|
-
def
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
229
|
+
# return random Integer in range with min and max
|
|
230
|
+
def rand_range max, min=0, weight =1
|
|
231
|
+
rand_p = max - min
|
|
232
|
+
weight.times {rand_p *= rand}
|
|
233
|
+
min + rand_p
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
#the main function. output the code block to numbered files.
|
|
238
|
+
#times:: number of times to run the block into a new file
|
|
239
|
+
def music times=1
|
|
240
|
+
times.times do |i|
|
|
241
|
+
clear
|
|
242
|
+
yield
|
|
243
|
+
render
|
|
244
|
+
App.out.outfile= "#{App.outpath}#{App::OUT_SND_DIR}#{i+1}#{App::EXT}"
|
|
245
|
+
make
|
|
246
246
|
end
|
|
247
|
-
|
|
248
|
-
end
|
|
247
|
+
end
|
data/lib/api/dist.rb
CHANGED
|
@@ -11,41 +11,104 @@ class Dist < Api
|
|
|
11
11
|
persist_hits(true)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
#return a new Dist with the same hits and length but not children or sounds.
|
|
15
|
+
def copy
|
|
16
|
+
out=Dist.new
|
|
17
|
+
out.length = length
|
|
18
|
+
out.clear_hits
|
|
19
|
+
out.hits << hits
|
|
20
|
+
out
|
|
21
|
+
end
|
|
14
22
|
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
def
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
self.last_born.clear_hits
|
|
32
|
-
self.last_born << [0.125,0.875] # off beats
|
|
33
|
-
set_child_len length
|
|
34
|
-
self
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
#evenly disperse each child across len by modifying the length and hits. uses #length
|
|
39
|
-
def space_children_across
|
|
40
|
-
len = self.length
|
|
41
|
-
branches.times do |i|
|
|
42
|
-
hit = i.to_f / branches
|
|
43
|
-
self[i].length = len #(1/branches)* len + hit * len
|
|
44
|
-
self[i].clear_hits
|
|
45
|
-
self[i] << hit
|
|
23
|
+
#add children who have sounds that make a drum like sound
|
|
24
|
+
#weight:: 0 is lowest drum, 1 is highest
|
|
25
|
+
def drum weight, amp, f_range=0.0, tone_num = 1, layers=1
|
|
26
|
+
layers.times do |i|
|
|
27
|
+
self << Dist.new
|
|
28
|
+
flay = last_born
|
|
29
|
+
min_oc = 1
|
|
30
|
+
max_oc = 5
|
|
31
|
+
oran = max_oc - min_oc
|
|
32
|
+
pos_num = scale_notes.count * oran #possible
|
|
33
|
+
ind = (pos_num * weight).round
|
|
34
|
+
oct_ind = min_oc + ind / scale_notes.count
|
|
35
|
+
log "drum notes #{ind % scale_notes.count} #{oct_ind}, weight #{weight}", 3
|
|
36
|
+
freq = Note.new(scale_notes[ind % scale_notes.count], oct_ind).freq
|
|
37
|
+
flay.drumify freq, amp/layers/2, f_range, tone_num #test
|
|
38
|
+
|
|
46
39
|
end
|
|
40
|
+
flay = self[0]
|
|
41
|
+
self << flay.copy
|
|
42
|
+
under_sound = last_born
|
|
43
|
+
under_sound << Snd.new
|
|
44
|
+
under_sound.snd.length = flay.snd.length
|
|
45
|
+
under_sound.snd.freq = flay.snd.freq
|
|
46
|
+
under_sound.snd.amp = amp/2.0
|
|
47
|
+
under_sound.snd.fade
|
|
48
|
+
under_sound.snd.tone.amp.exp = flay.snd.tone.amp.exp
|
|
49
|
+
under_sound.clear_hits # to test
|
|
50
|
+
# branches.times do |i|
|
|
51
|
+
# self[i].snd.amp=0 if i !=branches-1 # to test
|
|
52
|
+
# end
|
|
47
53
|
end
|
|
48
54
|
|
|
55
|
+
#assumes an already existing Snd and HitSq, just makes the Snd more like a percussive instrument
|
|
56
|
+
def drumify freq, amp, f_range, tone_num
|
|
57
|
+
random_sound(freq, f_range, tone_num, amp)
|
|
58
|
+
snd.toneseq.toneparts.last.do_all {|tone|
|
|
59
|
+
tone.fade
|
|
60
|
+
tone.amp.rand_exp true #below linear
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
#print hits for debugging
|
|
64
|
+
def ph
|
|
65
|
+
puts hits.hits.inspect
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
#add an Snd to self with some random properties
|
|
69
|
+
def random_sound root_f, f_range, parts, amp, max_delay = 0.0
|
|
70
|
+
# tone_num.times do |i|
|
|
71
|
+
self << Snd.new
|
|
72
|
+
delay = rand*max_delay
|
|
73
|
+
1.times {delay*=rand}
|
|
74
|
+
snd.toneseq.random(rand(parts).to_i, [true,false].sample, delay, amp, root_f+f_range*root_f, root_f-f_range*root_f)
|
|
75
|
+
snd.toneseq.toneparts.each do |tp|
|
|
76
|
+
# tp.do_all {|tone| tone.set_freq root_f}
|
|
77
|
+
# range = root_f * f_range
|
|
78
|
+
# tp.do_all {|tone| tone.set_freq_final root_f - range/2.0 + rand*range}
|
|
79
|
+
end
|
|
80
|
+
# last_born.clear_hits
|
|
81
|
+
# last_born << delay
|
|
82
|
+
# end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
#give mel_layers children each with a unique melody in incrementing octaves.
|
|
86
|
+
def random_melodies min_b_len, reps, mel_layers = 4, start_octave = 2, max_amp = 0.5,
|
|
87
|
+
seqs_max =0, chance = 0.4, random_del_chance = 0.25, max_b_mult=4
|
|
88
|
+
mel_layers.times do |i|
|
|
89
|
+
self << Dist.new
|
|
90
|
+
melody = last_born
|
|
91
|
+
map = melody.Mapper
|
|
92
|
+
map.random_melody min_b_len, reps, start_octave+i, chance, max_amp/mel_layers, rand(seqs_max).to_i, max_b_mult
|
|
93
|
+
|
|
94
|
+
# each layer picks a few bars to delete every note from.
|
|
95
|
+
to_del = []
|
|
96
|
+
map.mapee(0).hits.count.times do |h|
|
|
97
|
+
if rand < random_del_chance
|
|
98
|
+
to_del << h
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
# to_del = [1] #test
|
|
102
|
+
map.each_dist do |d|
|
|
103
|
+
d[0].hits.delete_arr to_del
|
|
104
|
+
# d[0].ph
|
|
105
|
+
end
|
|
106
|
+
# puts to_del
|
|
107
|
+
# map.dist.branches.times {|j| todel << map[j] if j!=0 }
|
|
108
|
+
# map.dist >> todel
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
49
112
|
#sets the length of all children Dist to val
|
|
50
113
|
def set_child_len val
|
|
51
114
|
branches.times do |i|
|
|
@@ -62,15 +125,15 @@ end
|
|
|
62
125
|
def Mapper
|
|
63
126
|
Mapper.new(self)
|
|
64
127
|
end
|
|
65
|
-
# delete another Dist
|
|
66
|
-
def del
|
|
67
|
-
@dist.snd.
|
|
128
|
+
# delete another Dist at index ind
|
|
129
|
+
def del ind
|
|
130
|
+
@dist.snd.delete_at ind
|
|
68
131
|
self
|
|
69
132
|
end
|
|
70
133
|
# set the total length in frames
|
|
71
134
|
def length= set
|
|
72
|
-
@dist.len = set
|
|
73
|
-
@dist.tss.each {|tss| tss.len = set }
|
|
135
|
+
@dist.len = set.round
|
|
136
|
+
# @dist.tss.each {|tss| tss.len = set }
|
|
74
137
|
self
|
|
75
138
|
end
|
|
76
139
|
# get the total length in frames
|
|
@@ -175,7 +238,7 @@ end
|
|
|
175
238
|
|
|
176
239
|
}
|
|
177
240
|
end
|
|
178
|
-
# adds a dist if it's valid to do so
|
|
241
|
+
# adds a dist if it's valid to do so.
|
|
179
242
|
def validate_dist! val
|
|
180
243
|
raise "This Dist can't have children, it has sounds. " if sounds > 0
|
|
181
244
|
@dist.add val
|
|
@@ -189,6 +252,9 @@ end
|
|
|
189
252
|
when Snd
|
|
190
253
|
validate_snd! toadd.toneseq
|
|
191
254
|
when Dist
|
|
255
|
+
if toadd.length == 0 #sets his length to mine if his is 0.
|
|
256
|
+
toadd.length = length
|
|
257
|
+
end
|
|
192
258
|
validate_dist! toadd.dist
|
|
193
259
|
else
|
|
194
260
|
return false
|
|
@@ -198,13 +264,15 @@ end
|
|
|
198
264
|
# Delete hits, sounds or other dists from my lists.
|
|
199
265
|
def del_single todel
|
|
200
266
|
case todel
|
|
201
|
-
when HitSq, Float
|
|
267
|
+
when HitSq, Float
|
|
202
268
|
@hits >> todel
|
|
203
269
|
persist_hits
|
|
204
270
|
when Snd
|
|
205
271
|
@dist.tss.delete todel.snd
|
|
206
272
|
when Dist
|
|
207
273
|
@dist.snd.delete (todel.dist)
|
|
274
|
+
when Integer
|
|
275
|
+
del todel
|
|
208
276
|
else
|
|
209
277
|
return false
|
|
210
278
|
end
|
data/lib/api/hit_sq.rb
CHANGED
|
@@ -5,6 +5,16 @@ class HitSq < Api
|
|
|
5
5
|
@hits=[]
|
|
6
6
|
super
|
|
7
7
|
end
|
|
8
|
+
def delete_arr to_del
|
|
9
|
+
to_del.each {|di| self.hits.delete_at di }
|
|
10
|
+
persist
|
|
11
|
+
end
|
|
12
|
+
# multiply all hits by mult
|
|
13
|
+
def * mult
|
|
14
|
+
hn = []
|
|
15
|
+
self.hits.collect! {|hit| hit = hit*mult}
|
|
16
|
+
persist
|
|
17
|
+
end
|
|
8
18
|
private
|
|
9
19
|
# delete value
|
|
10
20
|
def del_single todel
|
|
@@ -39,6 +49,7 @@ class HitSq < Api
|
|
|
39
49
|
end
|
|
40
50
|
public
|
|
41
51
|
|
|
52
|
+
#chance:: how likely it is a hit will be DELETED
|
|
42
53
|
def delete_random chance = 0.5
|
|
43
54
|
to_del = []
|
|
44
55
|
hits.each do |h|
|
|
@@ -113,6 +124,11 @@ class HitSq < Api
|
|
|
113
124
|
end
|
|
114
125
|
self
|
|
115
126
|
end
|
|
127
|
+
|
|
128
|
+
def +(val)
|
|
129
|
+
move val
|
|
130
|
+
persist
|
|
131
|
+
end
|
|
116
132
|
#return number of hits
|
|
117
133
|
def count
|
|
118
134
|
hits.count
|
|
@@ -121,7 +137,6 @@ class HitSq < Api
|
|
|
121
137
|
def move(val=0.5)
|
|
122
138
|
self.hits.collect! {|x|
|
|
123
139
|
z = (x+val)
|
|
124
|
-
z = (z*1000).round / 1000.0 # rounding
|
|
125
140
|
x = z
|
|
126
141
|
} # delay all
|
|
127
142
|
self
|
|
@@ -157,6 +172,7 @@ class Array
|
|
|
157
172
|
h<<self
|
|
158
173
|
h
|
|
159
174
|
end
|
|
175
|
+
|
|
160
176
|
end
|
|
161
177
|
class Integer
|
|
162
178
|
#a shortcut. e.g. 4.eqly_spaced gives you HitSq#eqly_spaced(4)
|
data/lib/api/mapper.rb
CHANGED
|
@@ -10,6 +10,224 @@ class Mapper < Api
|
|
|
10
10
|
@len=0
|
|
11
11
|
self.map_to= map_to_dist if !map_to_dist.nil?
|
|
12
12
|
end
|
|
13
|
+
#so that no mapees are a Dist, they are only Snd. it flattens the tree of Dist
|
|
14
|
+
def flatten
|
|
15
|
+
todel = []
|
|
16
|
+
count.times do |i|
|
|
17
|
+
mpe = mapee(i)
|
|
18
|
+
mpe.branches.times do |j|
|
|
19
|
+
self << mapee(i).copy
|
|
20
|
+
mapee_last << mpe[j].snd
|
|
21
|
+
end
|
|
22
|
+
todel << mapee(i)
|
|
23
|
+
end
|
|
24
|
+
dist >> todel
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# trim some hits
|
|
28
|
+
def reduce_hits
|
|
29
|
+
# mapee(0).hits.delete_random(0.08) no work
|
|
30
|
+
mapee(0).hits.trim_both(0.10)
|
|
31
|
+
mapee(1).hits.trim_both(0.25) if count > 1
|
|
32
|
+
mapee(2).hits.trim_start(0.34) if count > 2
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def random_melody min_b_len, reps =4, oct = 3, chance = 0.75, amp=0.5, seqs=0, max_b_mult = 4
|
|
36
|
+
self.length = length.to_f / reps
|
|
37
|
+
random_melody_hits(min_b_len, max_b_mult)
|
|
38
|
+
# map.random_melody_hits(beat(1.0).to_f, 0, 1.0) # max len is beat
|
|
39
|
+
delete_rand_dist 1.0 - chance
|
|
40
|
+
random_melody_notes(oct,rand(scale_notes.count))
|
|
41
|
+
|
|
42
|
+
count.times do |i|
|
|
43
|
+
freq = mapee(i).snd.tone.freq.start
|
|
44
|
+
mapee(i).snd.toneseq.random(seqs, true, 0, amp)
|
|
45
|
+
mapee(i).snd.toneseq.toneparts.each do |tp|
|
|
46
|
+
|
|
47
|
+
tp.tone(0).set_freq freq
|
|
48
|
+
tp.tone(1).set_freq freq
|
|
49
|
+
tp.tone(0).set_freq_final 0
|
|
50
|
+
tp.tone(1).set_freq_final 0
|
|
51
|
+
|
|
52
|
+
# tp.max_frames = len
|
|
53
|
+
# rand len
|
|
54
|
+
extra = 0.5 + rand
|
|
55
|
+
extra = 1.0 if extra > 1
|
|
56
|
+
tp.tone(0).frames = tp.tone.frames * extra
|
|
57
|
+
extra = 0.5 + rand
|
|
58
|
+
extra = 1.0 if extra > 1
|
|
59
|
+
tp.tone(1).frames = tp.tone.frames * extra
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
extend(reps)
|
|
64
|
+
transfer_hits
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def random_melody_hits(min_inc, max_inc_mult)
|
|
68
|
+
upto = 0
|
|
69
|
+
len = @len
|
|
70
|
+
while upto < len
|
|
71
|
+
next_len =(min_inc*(2**(rand(max_inc_mult).to_i))).round# next len
|
|
72
|
+
if upto + next_len > len # limit to len
|
|
73
|
+
next_len = len - upto
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
self << next_len.Dist
|
|
77
|
+
last.clear_hits
|
|
78
|
+
last << upto.to_f / len
|
|
79
|
+
upto += next_len #inc
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
#call #random_melody_hits first.
|
|
84
|
+
def random_melody_notes(start_oct = 3, start_note = 0, increments = [1,0,-1])
|
|
85
|
+
keys = scale_notes()
|
|
86
|
+
note_index = start_note
|
|
87
|
+
first_note = Note.new(keys[note_index % keys.count], start_oct)
|
|
88
|
+
count.times do |i|
|
|
89
|
+
key = keys[note_index % keys.count]
|
|
90
|
+
used_note = first_note + note_index
|
|
91
|
+
mapee(i) << Note.new(key, used_note.octave).Snd
|
|
92
|
+
# puts Note.new(key, used_note.octave).inspect
|
|
93
|
+
|
|
94
|
+
#inc
|
|
95
|
+
dinc = increments.sample
|
|
96
|
+
note_index += dinc
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
#extend length by mult and repeat all childs hits.
|
|
102
|
+
def extend mult
|
|
103
|
+
self.length = @len*mult
|
|
104
|
+
dist.branches.times do |i|
|
|
105
|
+
hits = self[i].hits
|
|
106
|
+
self[i].clear_hits
|
|
107
|
+
mult.times do |j|
|
|
108
|
+
new_hits = HitSq.new
|
|
109
|
+
new_hits << hits
|
|
110
|
+
new_hits * (1.0/mult)
|
|
111
|
+
new_hits + (j.to_f / mult)
|
|
112
|
+
self[i] << new_hits
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def transfer_hits
|
|
118
|
+
dist.branches.times do |i|
|
|
119
|
+
# hits = mapee(i).hits
|
|
120
|
+
# len = mapee(i).length
|
|
121
|
+
#
|
|
122
|
+
mapee(i).clear_hits
|
|
123
|
+
mapee(i).length = @len
|
|
124
|
+
mapee(i) << self[i].hits
|
|
125
|
+
self[i].clear_hits
|
|
126
|
+
self[i] << 0.0
|
|
127
|
+
# mapee(i).ph
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def delete_rand_dist chance
|
|
132
|
+
del = []
|
|
133
|
+
each_dist do |d|
|
|
134
|
+
del << d if chance >= rand
|
|
135
|
+
end
|
|
136
|
+
del.each {|d| dist >> d}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
#if no reps given, will use length to determin it.
|
|
140
|
+
def fully_extend_all reps=nil
|
|
141
|
+
dist.branches.times do |i|
|
|
142
|
+
hits = mapee(i).hits
|
|
143
|
+
len = mapee(i).length
|
|
144
|
+
mapee(i).clear_hits
|
|
145
|
+
reps = (@len.to_f / len).round if reps.nil?
|
|
146
|
+
mapee(i).length = @len
|
|
147
|
+
reps.times do |j|
|
|
148
|
+
new_hits = HitSq.new
|
|
149
|
+
new_hits << hits
|
|
150
|
+
new_hits * (1.0/reps)
|
|
151
|
+
new_hits + (j.to_f / reps)
|
|
152
|
+
# puts new_hits.hits.inspect
|
|
153
|
+
mapee(i) << new_hits
|
|
154
|
+
end
|
|
155
|
+
# puts mapee(i).hits.hits.inspect
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def do_mapee
|
|
160
|
+
count.times {|i| yield(mapee(i))}
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
#make all drum sounds
|
|
165
|
+
#always will be an extra layer anyway
|
|
166
|
+
def drum_sounds max_amp=0.5, layers =1
|
|
167
|
+
tot = count
|
|
168
|
+
amp = max_amp.to_f / tot #out of total drums
|
|
169
|
+
tot.times do |i|
|
|
170
|
+
this_amp = amp
|
|
171
|
+
wei = (i+1).to_f/(tot+1)
|
|
172
|
+
# this_amp = max_amp if wei < 0.4 # if bass-ish
|
|
173
|
+
range = 0.0
|
|
174
|
+
seqs= 1
|
|
175
|
+
if wei > 0.7 # if hihat-ish
|
|
176
|
+
this_amp /= 3
|
|
177
|
+
mapee(i).drum(wei, this_amp, 0.04, 1, layers)
|
|
178
|
+
mapee(i)[0].snd.toneseq.tonepart(0).do_all {|tone| tone.saturations.start = 0.5 + rand(3).to_f/10}
|
|
179
|
+
mapee(i)[0].snd.toneseq.do_all{|tp| tp.do_all {|tone|
|
|
180
|
+
tone.detail = 10000
|
|
181
|
+
tone.amp.exp = 0.12
|
|
182
|
+
} }
|
|
183
|
+
else
|
|
184
|
+
if wei > 0.35 && wei < 0.65
|
|
185
|
+
range = 0.5
|
|
186
|
+
seqs= 2+rand(2)
|
|
187
|
+
end
|
|
188
|
+
mapee(i).drum(wei, this_amp, range, seqs, layers)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
#add dists with the hits you need for a bar of techno percussion.
|
|
194
|
+
#len:: length of bar
|
|
195
|
+
#3 children, in order: bass drum, snare, hi hat
|
|
196
|
+
def techno_percussion len
|
|
197
|
+
self << len.Dist # bass drum
|
|
198
|
+
mapee_last.clear_hits
|
|
199
|
+
mapee_last << 4.eqly_spaced
|
|
200
|
+
# max len is full, min quarter
|
|
201
|
+
h_num = mapee_last.hits.count
|
|
202
|
+
h_num = 1 if h_num == 0
|
|
203
|
+
mapee_last.length= rand_range(len*0.666/h_num, len*0.11/h_num)
|
|
204
|
+
|
|
205
|
+
self << len.Dist # snare
|
|
206
|
+
mapee_last.clear_hits
|
|
207
|
+
mapee_last << [0.25,0.75]
|
|
208
|
+
h_num = mapee_last.hits.count
|
|
209
|
+
h_num = 1 if h_num == 0
|
|
210
|
+
mapee_last.length= rand_range(len/h_num, len/h_num/2)
|
|
211
|
+
|
|
212
|
+
self << len.Dist # hi
|
|
213
|
+
mapee_last.clear_hits
|
|
214
|
+
mapee_last << 4.eqly_spaced
|
|
215
|
+
mapee_last.hits.move(0.125) # offbeats
|
|
216
|
+
h_num = mapee_last.hits.count
|
|
217
|
+
h_num = 1 if h_num == 0
|
|
218
|
+
mapee_last.length= rand_range(len*0.666/h_num, len*0.666/h_num/2)
|
|
219
|
+
|
|
220
|
+
self
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
#evenly disperse each child across length
|
|
224
|
+
def fillout
|
|
225
|
+
count.times do |j|
|
|
226
|
+
self[j].clear_hits
|
|
227
|
+
reps = (@len.to_f / (mapee(j).length)).round
|
|
228
|
+
self[j] << reps.eqly_spaced
|
|
229
|
+
end
|
|
230
|
+
end
|
|
13
231
|
#set the right hits to mix all children one after eachother.
|
|
14
232
|
#(this can create a dj mix)
|
|
15
233
|
#mixed_ammount:: the ammount of overlap between children
|
|
@@ -24,11 +242,21 @@ class Mapper < Api
|
|
|
24
242
|
def count
|
|
25
243
|
dist.branches
|
|
26
244
|
end
|
|
245
|
+
|
|
246
|
+
#set length
|
|
247
|
+
def length= val
|
|
248
|
+
@len=val.round
|
|
249
|
+
dist.set_child_len @len
|
|
250
|
+
end
|
|
251
|
+
#get length
|
|
252
|
+
def length
|
|
253
|
+
@len
|
|
254
|
+
end
|
|
255
|
+
|
|
27
256
|
#length of each child is set to length of val, and we get added to val
|
|
28
257
|
#val:: a Dist
|
|
29
258
|
def map_to= val
|
|
30
|
-
|
|
31
|
-
dist.set_child_len @len
|
|
259
|
+
self.length= val.length
|
|
32
260
|
val << dist #add me to him
|
|
33
261
|
end
|
|
34
262
|
#get a mapper at i from #dist
|
|
@@ -47,24 +275,35 @@ class Mapper < Api
|
|
|
47
275
|
def mapee i=0
|
|
48
276
|
dist[i][0]
|
|
49
277
|
end
|
|
50
|
-
|
|
51
|
-
#add
|
|
52
|
-
#
|
|
53
|
-
|
|
54
|
-
def techno_percussion len
|
|
278
|
+
|
|
279
|
+
#add dist with the hits you need for a bar of dubstep percussion
|
|
280
|
+
#this dist has 5 children, in order: bass drum, secondary bass drum, snare, hi hat, second hi hat
|
|
281
|
+
def dubstep_percussion len
|
|
55
282
|
self << len.Dist # bass drum
|
|
56
283
|
mapee_last.clear_hits
|
|
57
|
-
mapee_last <<
|
|
284
|
+
mapee_last << [0.0]
|
|
285
|
+
self << len.Dist # 2nd bass drum
|
|
286
|
+
mapee_last.clear_hits
|
|
287
|
+
mapee_last << [0.75, 0.875] #two last drum hits
|
|
58
288
|
self << len.Dist # snare
|
|
59
289
|
mapee_last.clear_hits
|
|
60
|
-
mapee_last << [0.
|
|
61
|
-
self << len.Dist # hi
|
|
290
|
+
mapee_last << [0.5] # third beat
|
|
291
|
+
self << len.Dist # hi hat
|
|
62
292
|
mapee_last.clear_hits
|
|
63
|
-
mapee_last <<
|
|
64
|
-
|
|
293
|
+
mapee_last << [0.375,0.625] # off beats
|
|
294
|
+
self << len.Dist # 2nd hi hat
|
|
295
|
+
mapee_last.clear_hits
|
|
296
|
+
mapee_last << [0.125,0.875] # offbeats
|
|
65
297
|
self
|
|
66
|
-
end
|
|
298
|
+
end
|
|
299
|
+
|
|
67
300
|
|
|
301
|
+
#pass a block to be run on each dist
|
|
302
|
+
def each_dist
|
|
303
|
+
count.times do |i|
|
|
304
|
+
yield( self[i])
|
|
305
|
+
end
|
|
306
|
+
end
|
|
68
307
|
private
|
|
69
308
|
# Add Dist to me.
|
|
70
309
|
def add_single toadd
|
data/lib/api/snd.rb
CHANGED
|
@@ -6,6 +6,25 @@ class Snd < Api
|
|
|
6
6
|
super
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
#assumes one tone only
|
|
10
|
+
def freq= val
|
|
11
|
+
tone.set_freq val
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
#freq of first tone only
|
|
15
|
+
def freq
|
|
16
|
+
tone.freq.start
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def amp= val
|
|
20
|
+
toneseq.do_all {|tp|
|
|
21
|
+
tp.do_all {|tone|
|
|
22
|
+
tone.amp.start = val
|
|
23
|
+
tone.amp.final = 0.0
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
9
28
|
# return its TonePart.
|
|
10
29
|
def tonepart i=0
|
|
11
30
|
child = @snd.tonepart i
|
data/lib/audio_output.rb
CHANGED
|
@@ -23,7 +23,7 @@ class AudioOutput
|
|
|
23
23
|
log "phase 1 complete.", 2
|
|
24
24
|
end
|
|
25
25
|
def make_audio_file
|
|
26
|
-
log "phase 2 of 2: merging all tmp files into #{App::EXT} file.
|
|
26
|
+
log "phase 2 of 2: merging all tmp files into #{App::EXT} file. secs:#{App.time_since}", 2
|
|
27
27
|
frames_index = 0
|
|
28
28
|
len = 0
|
|
29
29
|
# get max len
|
|
@@ -61,6 +61,7 @@ class AudioOutput
|
|
|
61
61
|
end
|
|
62
62
|
log "CHUNK_@#{frames_index} asked for #{jump}. total: #{len}", 4
|
|
63
63
|
values = filelist.root_get_value(frames_index,jump)#, jump)+
|
|
64
|
+
|
|
64
65
|
# if values.count==0
|
|
65
66
|
# puts "VALUES RETURNED 0"
|
|
66
67
|
# frames_index = len # end this shit
|
data/lib/fader.rb
CHANGED
|
@@ -16,8 +16,8 @@ def initialize(start=nil,final=nil,exp=0)
|
|
|
16
16
|
@exp = exp
|
|
17
17
|
end
|
|
18
18
|
# randomize the exp with good values.
|
|
19
|
-
def rand_exp
|
|
20
|
-
self.exp = 0.
|
|
19
|
+
def rand_exp below_linear =[true,false].sample
|
|
20
|
+
self.exp = below_linear ? 0.1 + 0.9 * rand : 1+rand(20)
|
|
21
21
|
end
|
|
22
22
|
# set #final to a percentage of start
|
|
23
23
|
def %(percent)
|
data/lib/file_list.rb
CHANGED
|
@@ -12,7 +12,7 @@ class FileList
|
|
|
12
12
|
# delays of children
|
|
13
13
|
attr_accessor :filelist_delays
|
|
14
14
|
# I want each child in the fileslist to fill len frames from my start
|
|
15
|
-
attr_accessor :child_len, :mark_of_death
|
|
15
|
+
attr_accessor :child_len, :mark_of_death, :to_del
|
|
16
16
|
def initialize()
|
|
17
17
|
@files = []
|
|
18
18
|
@loaded_file_index = nil
|
|
@@ -22,6 +22,7 @@ class FileList
|
|
|
22
22
|
@file_content_lens=[]
|
|
23
23
|
@loaded_file_data = nil
|
|
24
24
|
@mark_of_death = false
|
|
25
|
+
@to_del=[]
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def root_get_value(index,size=1)
|
|
@@ -55,35 +56,41 @@ class FileList
|
|
|
55
56
|
out=[]
|
|
56
57
|
fl=filelists[i]
|
|
57
58
|
App.checks+=1
|
|
58
|
-
delay = filelist_delays[i]
|
|
59
|
+
delay = filelist_delays[i]
|
|
60
|
+
|
|
59
61
|
# delay = 0 if delay.nil?
|
|
60
62
|
# puts "deciding if we should go into filelist #{i+1} of #{filelists.count},"+
|
|
61
63
|
# " is #{delay} <= #{index} ? "
|
|
62
64
|
# puts "child len #{@child_len}"
|
|
63
65
|
needed_until_in = delay - index
|
|
66
|
+
# puts "needed till in #{needed_until_in}"
|
|
64
67
|
if needed_until_in <= 0
|
|
65
|
-
|
|
68
|
+
|
|
66
69
|
if fl.mark_of_death
|
|
67
70
|
# puts "old count: #{filelists.count}"
|
|
68
71
|
# fl.loaded_file_data=nil
|
|
69
72
|
# GC.start
|
|
70
|
-
self.
|
|
71
|
-
|
|
72
|
-
# puts "#{i} is dead! new count: #{filelists.count}"
|
|
73
|
+
self.to_del << i
|
|
74
|
+
# puts "#{i} is dead! new count: #{filelists.count}"
|
|
73
75
|
else
|
|
74
76
|
# normal result
|
|
75
77
|
result =fl.get_value(index-delay,size)
|
|
76
|
-
|
|
78
|
+
|
|
77
79
|
out= result
|
|
78
80
|
end
|
|
79
81
|
else
|
|
80
82
|
spoof_req_len = size - needed_until_in
|
|
83
|
+
# puts "checking if we can spoof, next move is #{size} and we will be in in #{needed_until_in}."
|
|
81
84
|
if spoof_req_len > 0
|
|
85
|
+
# puts "checking buggy dude spoofed!! #{index}"
|
|
86
|
+
|
|
82
87
|
# Read a smaller chunk in the futre that will be missed if we increment by size.
|
|
83
88
|
index_to_get_in = needed_until_in + index
|
|
84
89
|
delay_on_future_chunk = Array.new(needed_until_in, 0)
|
|
85
90
|
future_chunk = fl.get_value(index_to_get_in-delay, spoof_req_len)
|
|
86
91
|
# puts future_chunk.count
|
|
92
|
+
|
|
93
|
+
# puts "spoofing at #{index} for #{delay} and got #{future_chunk.count} results."
|
|
87
94
|
combo = [] + delay_on_future_chunk + future_chunk
|
|
88
95
|
# puts combo.count
|
|
89
96
|
out= combo # with resizing, but will be chunk len anyway.
|
|
@@ -109,9 +116,14 @@ class FileList
|
|
|
109
116
|
self.mark_of_death = true
|
|
110
117
|
return []
|
|
111
118
|
end
|
|
112
|
-
|
|
119
|
+
self.to_del = []
|
|
120
|
+
filelists.count.times do |i|
|
|
113
121
|
out.add_e lookup_child(index,size,i)
|
|
114
122
|
end
|
|
123
|
+
self.to_del.each do |i|
|
|
124
|
+
self.filelist_delays.delete_at i
|
|
125
|
+
self.filelists.delete_at i
|
|
126
|
+
end
|
|
115
127
|
out
|
|
116
128
|
end
|
|
117
129
|
|
data/lib/math_utils.rb
CHANGED
data/lib/music_coder.rb
CHANGED
|
@@ -109,8 +109,12 @@ end
|
|
|
109
109
|
|
|
110
110
|
# clears objects ready to write to file.
|
|
111
111
|
def self.clear_ready
|
|
112
|
-
App.
|
|
112
|
+
App.clear_rendered
|
|
113
113
|
App.out.snddists = []
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def self.clear_rendered
|
|
117
|
+
App.clear_dir App::TMP_DIR
|
|
114
118
|
App.out.filelist = FileList.new
|
|
115
119
|
end
|
|
116
120
|
|
|
@@ -189,6 +193,7 @@ App.checks = 0
|
|
|
189
193
|
self.out = AudioOutput.new
|
|
190
194
|
# make dirs
|
|
191
195
|
require 'fileutils'
|
|
196
|
+
App::OUT_SND_DIR = 'sound/' unless const_defined?(:OUT_SND_DIR)
|
|
192
197
|
FileUtils.mkdir_p App.outpath+"sound/"
|
|
193
198
|
FileUtils.mkdir_p App::TMP_DIR
|
|
194
199
|
#
|
data/lib/snd_dist.rb
CHANGED
|
@@ -72,14 +72,16 @@ end
|
|
|
72
72
|
files=FileList.new
|
|
73
73
|
raise "forgot to put a length of this sound dist" if len.nil?
|
|
74
74
|
log "Warning: one of your sound distributions have no hits. on purpose? ", 3 if hits.empty?
|
|
75
|
-
hits.each_with_index do |delay,i|
|
|
76
|
-
delay_in_frames= (delay*len).round
|
|
75
|
+
hits.each_with_index do |delay, i|
|
|
76
|
+
delay_in_frames = (delay *len).round
|
|
77
77
|
into=(i+1).to_f/hits.count
|
|
78
78
|
snd.each do |sn|
|
|
79
79
|
# puts "another snd dist "
|
|
80
80
|
files.addlist sn.render(into), delay_in_frames
|
|
81
81
|
end
|
|
82
|
-
tss.each
|
|
82
|
+
tss.each do |sn|
|
|
83
|
+
files.addlist sn.render(into), delay_in_frames
|
|
84
|
+
end
|
|
83
85
|
# puts "#{ App.done} #{App.total}"
|
|
84
86
|
if !tss.empty?
|
|
85
87
|
App.done += 1
|
data/lib/tests.rb
CHANGED
|
@@ -19,6 +19,10 @@ class TestTones < Test::Unit::TestCase
|
|
|
19
19
|
assert_equal(-230, t.freq_final)
|
|
20
20
|
t.set_freq(240)
|
|
21
21
|
assert_equal(-220, t.freq_final)
|
|
22
|
+
t.set_freq_final (0)
|
|
23
|
+
assert_equal(0, t.freq.final)
|
|
24
|
+
t.set_freq(10)
|
|
25
|
+
assert_equal(0, t.freq_final)
|
|
22
26
|
end
|
|
23
27
|
end
|
|
24
28
|
class TestNotes < Test::Unit::TestCase
|
data/lib/tone.rb
CHANGED
|
@@ -25,7 +25,7 @@ end
|
|
|
25
25
|
def set_freq (val)
|
|
26
26
|
dif = freq.start - val
|
|
27
27
|
freq.start = val
|
|
28
|
-
set_freq_final freq_final(false) + dif, false
|
|
28
|
+
set_freq_final freq_final(false) + dif, false if freq_final !=0
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def freq_final(is_relative=true)
|
|
@@ -113,6 +113,10 @@ end
|
|
|
113
113
|
def detail
|
|
114
114
|
wave.detail
|
|
115
115
|
end
|
|
116
|
+
def detail= val
|
|
117
|
+
detail.start = val
|
|
118
|
+
detail.final = val
|
|
119
|
+
end
|
|
116
120
|
# returns a buffer with a chord (collection of Tone whos collective amplitude equals
|
|
117
121
|
# the amplitude set in tone) in #wd. one tone on each element
|
|
118
122
|
# name:: String containing the chord name. Range, strings in Composer.chords
|
data/lib/tone_part.rb
CHANGED
|
@@ -5,12 +5,12 @@ attr_accessor :tones
|
|
|
5
5
|
attr_accessor :max_frames
|
|
6
6
|
attr_accessor :tone_single
|
|
7
7
|
attr_accessor :tone_count
|
|
8
|
-
def initialize(m=0,t1=Tone.new
|
|
9
|
-
@tones = Fader.new(t1,t2,0)
|
|
10
|
-
@max_frames = m
|
|
11
|
-
self.frames = m
|
|
8
|
+
def initialize(m=0,t1=Tone.new)
|
|
12
9
|
@tone_count = 1
|
|
13
10
|
@tone_single = t1
|
|
11
|
+
@tones = Fader.new(t1,nil,0)
|
|
12
|
+
@max_frames = m
|
|
13
|
+
self.frames = m
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def tone i=0
|
|
@@ -18,6 +18,12 @@ def tone i=0
|
|
|
18
18
|
i==1 ? @tones.final : @tones.start
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
def two_tones
|
|
22
|
+
self.tone_count = 2
|
|
23
|
+
tones.start = tone_single
|
|
24
|
+
tones.final = tone_single.deep_copy
|
|
25
|
+
end
|
|
26
|
+
|
|
21
27
|
# set #max_frames and frames on each Tone if 0.
|
|
22
28
|
def max_frames= m
|
|
23
29
|
@max_frames = m
|
|
@@ -66,6 +72,12 @@ def amp_mult(factor)
|
|
|
66
72
|
end
|
|
67
73
|
end
|
|
68
74
|
|
|
75
|
+
#perform the block on all tones
|
|
76
|
+
def do_all
|
|
77
|
+
yield (tone(0))
|
|
78
|
+
yield (tone(1))
|
|
79
|
+
end
|
|
80
|
+
|
|
69
81
|
|
|
70
82
|
#return tone with mixed settings of tones#start with tones#final.
|
|
71
83
|
#into:: how much of the final tone is mixed in. 0 is none. Range: 0 to 1
|
data/lib/tone_seq.rb
CHANGED
|
@@ -32,9 +32,15 @@ class ToneSeq
|
|
|
32
32
|
make(1)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
def do_all
|
|
36
|
+
toneparts.each do |tp|
|
|
37
|
+
yield tp
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
35
41
|
#random everything
|
|
36
42
|
def random extra_detail = 5, even = false, delay=0, start_amp = 0.5,
|
|
37
|
-
max_f = 2000, min_f = 120, max_sat=0.
|
|
43
|
+
max_f = 2000, min_f = 120, max_sat=0.8, min_detail=20
|
|
38
44
|
make(extra_detail) # sets the lens evenly.
|
|
39
45
|
frames_left = self.frames - self.frames.to_f*delay #- extra_detail+1 #minus a little so it must have 1 frame
|
|
40
46
|
toneparts.count.times do |i|
|
|
@@ -55,6 +61,13 @@ class ToneSeq
|
|
|
55
61
|
max_amp = toneparts[i-1].tone.amp.start
|
|
56
62
|
end
|
|
57
63
|
toneparts[i].tone.rand_amp_both max_amp, max_amp * 0.75 # not much lower
|
|
64
|
+
|
|
65
|
+
tp = toneparts[i]
|
|
66
|
+
tp.two_tones
|
|
67
|
+
tp.tone(1).rand_sat_both 3, max_sat
|
|
68
|
+
tp.tone(1).rand_freq_both(min_f, max_f)
|
|
69
|
+
tp.tone(1).rand_detail_both min_detail
|
|
70
|
+
|
|
58
71
|
end
|
|
59
72
|
join
|
|
60
73
|
end
|
|
@@ -72,10 +85,11 @@ class ToneSeq
|
|
|
72
85
|
def frames= val
|
|
73
86
|
@toneparts.each {|tp| tp.frames = val}
|
|
74
87
|
end
|
|
88
|
+
|
|
75
89
|
def fade
|
|
76
90
|
@toneparts.each {|tp|
|
|
77
|
-
tp.
|
|
78
|
-
tp.
|
|
91
|
+
tp.tone(0).fade
|
|
92
|
+
tp.tone(1).fade
|
|
79
93
|
}
|
|
80
94
|
self
|
|
81
95
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: music_coder
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.1
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-03-
|
|
12
|
+
date: 2012-03-24 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: sndfile
|
|
@@ -27,7 +27,15 @@ dependencies:
|
|
|
27
27
|
- - ! '>='
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
29
|
version: 0.2.0
|
|
30
|
-
description:
|
|
30
|
+
description: ! "Music Coder is a music programming library. It generates music entirely
|
|
31
|
+
through code in the chosen programming language (Ruby). \n There are
|
|
32
|
+
three main reasons a technically minded person would use Music Coder:\n Producing
|
|
33
|
+
music: Write scripts that mimic human creativity, and put some randomness in it,
|
|
34
|
+
giving you a program that can generate aesthetically pleasing music that is completely
|
|
35
|
+
unique in each file generated.\n Scientific exploration: Use the functions
|
|
36
|
+
in Music Coder to concisely apply algorithms or math to the structure and properties
|
|
37
|
+
of sound.\n Making samples: create samples or sections of music that
|
|
38
|
+
are too complex to be done by hand in graphical audio programs such as Ableton."
|
|
31
39
|
email: karl.is.god@gmail.com
|
|
32
40
|
executables: []
|
|
33
41
|
extensions: []
|
|
@@ -80,7 +88,7 @@ rubyforge_project:
|
|
|
80
88
|
rubygems_version: 1.8.23
|
|
81
89
|
signing_key:
|
|
82
90
|
specification_version: 3
|
|
83
|
-
summary:
|
|
91
|
+
summary: write programs that create music. music audio library.
|
|
84
92
|
test_files:
|
|
85
93
|
- lib/tests.rb
|
|
86
94
|
has_rdoc: true
|