music_coder 0.7.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -214,4 +214,35 @@ end
214
214
  #sets Composer#scale
215
215
  def set_scale sc
216
216
  Composer.scale = sc
217
+ end
218
+ # the typical series of commands at the start of a simple new music coder file.
219
+ def start
220
+ log_level 1
221
+ clear
222
+ set_bpm 128
223
+ end
224
+ ## return first dist in queue
225
+ #def root
226
+ # App.out.snddists[0]
227
+ #end
228
+
229
+
230
+ def random_sound root_f = 440, len = beat(1), f_range = 0.2, tone_num = 1+rand(16), parts = 1+rand(3), max_delay = 0.6
231
+ main = len.Dist
232
+ tone_num.times do |i|
233
+ main << len.Dist
234
+ main.last_born << Snd.new
235
+ snd = main.last_born.snd
236
+ snd.length = len.to_f
237
+ delay = rand*max_delay
238
+ 1.times {delay*=rand}
239
+ snd.toneseq.random(rand(parts), [true,false].sample, delay, 0.333)
240
+ snd.tone.set_freq root_f
241
+ range = root_f * f_range
242
+ snd.tone.set_freq_final root_f - range/2 + rand*range
243
+ snd.toneseq.amp_reduce tone_num
244
+ main.last_born.clear_hits
245
+ main.last_born << delay
246
+ end
247
+ main
217
248
  end
@@ -8,7 +8,49 @@ class Dist < Api
8
8
  @dist=SndDist.new
9
9
  @hits=HitSq.new
10
10
  @hits.add_parent self
11
- persist_hits
11
+ persist_hits(true)
12
+ end
13
+
14
+
15
+ #make children with the hits you need for a bar of dubstep percussion. set their #length to mine
16
+ #this dist has 5 children, in order: bass drum, secondary bass drum, snare, hi hat, second hi hat
17
+ def dubstep_percussion
18
+ self << Dist.new # bass drum
19
+ self.last_born.clear_hits
20
+ self.last_born << [0.0]
21
+ self << Dist.new # 2nd bass drum
22
+ self.last_born.clear_hits
23
+ self.last_born << [0.75, 0.875] #two last drum hits
24
+ self << Dist.new # snare
25
+ self.last_born.clear_hits
26
+ self.last_born << [0.5] # third beat
27
+ self << Dist.new # hi hat
28
+ self.last_born.clear_hits
29
+ self.last_born << [0.375,0.625] # off beats
30
+ self << Dist.new # 2nd hi hat
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
46
+ end
47
+ end
48
+
49
+ #sets the length of all children Dist to val
50
+ def set_child_len val
51
+ branches.times do |i|
52
+ self[i].length = val
53
+ end
12
54
  end
13
55
 
14
56
  #add num TonePart to Snd at i's ToneSeq, with my #length as max.
@@ -16,6 +58,10 @@ class Dist < Api
16
58
  snd(i).toneseq.make(num)
17
59
  end
18
60
 
61
+ #shortcut to create a Mapper, with Mapper#map_to= me
62
+ def Mapper
63
+ Mapper.new(self)
64
+ end
19
65
  # delete another Dist
20
66
  def del todel
21
67
  @dist.snd.delete todel.dist
@@ -106,9 +152,9 @@ class Dist < Api
106
152
  @dist.tss.count
107
153
  end
108
154
  # (internal use only) copy our hits down to the underlining object
109
- def persist_hits
155
+ def persist_hits(is_def = false)
110
156
  @dist.hits = hits.hits
111
- @dist.hits = [0.0] if hits.hits.count == 0
157
+ @dist.hits = [0.0] if is_def && hits.hits.count == 0
112
158
  self
113
159
  end
114
160
  protected
@@ -38,6 +38,66 @@ class HitSq < Api
38
38
  end
39
39
  end
40
40
  public
41
+
42
+ def delete_random chance = 0.5
43
+ to_del = []
44
+ hits.each do |h|
45
+ if rand < chance
46
+ to_del << h
47
+ end
48
+ end
49
+ to_del.each {|d| self.hits.delete d }
50
+ persist
51
+ self
52
+ end
53
+
54
+ def trim_start portion=0.25
55
+ save = []
56
+ hits.count.times do |i|
57
+ upto = i.to_f / hits.count
58
+ if upto >= portion
59
+ save << hits[i]
60
+ end
61
+ end
62
+ self.hits = save
63
+ persist
64
+ end
65
+
66
+ def trim_end portion=0.25
67
+ save = []
68
+ hits.count.times do |i|
69
+ upto = i.to_f / hits.count
70
+ if upto < 1.0 - portion
71
+ save << hits[i]
72
+ end
73
+ end
74
+ self.hits = save
75
+ persist
76
+ end
77
+
78
+ def trim_both portion=0.25
79
+ save = []
80
+ hits.count.times do |i|
81
+ upto = i.to_f / hits.count
82
+ if upto < 1.0 - portion
83
+ save << hits[i]
84
+ end
85
+ end
86
+
87
+ save2 = []
88
+ hits.count.times do |i|
89
+ upto = i.to_f / hits.count
90
+ if upto >= portion
91
+ save2 << hits[i]
92
+ end
93
+ end
94
+ self.hits = save&save2
95
+ persist
96
+ end
97
+ #(internal use only)
98
+ def persist
99
+ parent.dist.hits = hits if parent
100
+ end
41
101
  #Adds into #hits.
42
102
  #possible_hits:: number of hits that can occur. Must be int
43
103
  #chance:: chance a hit will be included. range: 0 to 1
@@ -0,0 +1,80 @@
1
+ #a mapper is a collection of Dist all as long as a Dist being mapped too,
2
+ #each of these Dist have a child who is getting "mapped" with hits across that length.
3
+ #everything that you need a mapper for can be
4
+ #done without one but it is handy, since it is a common patten.
5
+ class Mapper < Api
6
+ #the main Dist containing all mappings
7
+ attr_accessor :dist
8
+ def initialize(map_to_dist=nil)
9
+ @dist=Dist.new
10
+ @len=0
11
+ self.map_to= map_to_dist if !map_to_dist.nil?
12
+ end
13
+ #set the right hits to mix all children one after eachother.
14
+ #(this can create a dj mix)
15
+ #mixed_ammount:: the ammount of overlap between children
16
+ def mix mixed_ammount = 0.25
17
+ last_start = 0.0
18
+ dist.branches.times do |i|
19
+ self[i].clear_hits
20
+ self[i] << last_start
21
+ last_start = last_start + (1.0 - mixed_ammount) * (1.0/dist.branches)
22
+ end
23
+ end
24
+ def count
25
+ dist.branches
26
+ end
27
+ #length of each child is set to length of val, and we get added to val
28
+ #val:: a Dist
29
+ def map_to= val
30
+ @len=val.length
31
+ dist.set_child_len @len
32
+ val << dist #add me to him
33
+ end
34
+ #get a mapper at i from #dist
35
+ def [] i=0
36
+ dist[i]
37
+ end
38
+ #return last child
39
+ def last
40
+ dist.last_born
41
+ end
42
+ #return last #mapee
43
+ def mapee_last
44
+ last[0]
45
+ end
46
+ #get the child of a mapper (the dist to being mapped) at i
47
+ def mapee i=0
48
+ dist[i][0]
49
+ end
50
+
51
+ #add dists with the hits you need for a bar of techno percussion.
52
+ #len:: length of bar
53
+ #3 children, in order: bass drum, snare, hi hat
54
+ def techno_percussion len
55
+ self << len.Dist # bass drum
56
+ mapee_last.clear_hits
57
+ mapee_last << 4.eqly_spaced
58
+ self << len.Dist # snare
59
+ mapee_last.clear_hits
60
+ mapee_last << [0.25,0.75]
61
+ self << len.Dist # hi
62
+ mapee_last.clear_hits
63
+ mapee_last << 4.eqly_spaced
64
+ mapee_last.hits.move(0.125) # offbeats
65
+ self
66
+ end
67
+
68
+ private
69
+ # Add Dist to me.
70
+ def add_single toadd
71
+ case toadd
72
+ when Dist
73
+ dist << @len.Dist
74
+ dist.last_born << toadd
75
+ else
76
+ return false
77
+ end
78
+ true
79
+ end
80
+ end
@@ -73,7 +73,7 @@ class AudioOutput
73
73
  # raise " couldn't find a value... fuck." if found_val.nil?
74
74
  # print percent
75
75
  frames_index += values.count
76
- App.logger.print_loading_bar(frames_index, len, percent_complete)
76
+ App.logger.print_loading_bar(frames_index, len, percent_complete, 50)
77
77
  self.percent_complete = ((frames_index.to_f/len)*100)
78
78
  # values=nil
79
79
  # GC.start
@@ -100,11 +100,13 @@ class AudioOutput
100
100
  end
101
101
  def print_tail_info total_frames
102
102
  time_taken = App.time_since
103
- log 'wrote file: ' + outfile, 1
103
+ log 'wrote file: ' + outfile, 2
104
104
  log 'seconds: ' + (total_frames.to_f / Composer.samplerate).round(2).to_s +
105
105
  " (frames: #{(total_frames/1000).round},000)", 2
106
106
  log 'time taken: ' + time_taken.round(2).to_s +
107
107
  " seconds (fps: #{(total_frames/time_taken/1000).round()},000)", 2
108
+ #end the very last line
109
+ puts ""
108
110
  end
109
111
 
110
112
  end#class
@@ -15,6 +15,10 @@ def initialize(start=nil,final=nil,exp=0)
15
15
  @final = final
16
16
  @exp = exp
17
17
  end
18
+ # randomize the exp with good values.
19
+ def rand_exp
20
+ self.exp = 0.20 * rand(100)
21
+ end
18
22
  # set #final to a percentage of start
19
23
  def %(percent)
20
24
  self.final = start.to_f*(percent/100.0)
@@ -2,8 +2,8 @@
2
2
  class Logger
3
3
  #higher level means more is logged.
4
4
  #0 means silent.
5
- #1 heading and written files.
6
- #2 condensed stats and updative info from a process (loading bars)
5
+ #1 heading and %.
6
+ #2 condensed stats and written files
7
7
  #3 warnings
8
8
  #4 memory updates and debug info
9
9
  #5 raw data
@@ -21,13 +21,12 @@ class Logger
21
21
  end
22
22
 
23
23
  # a loading bar
24
- def print_loading_bar(frames_index, len, percent_complete=nil)
24
+ def print_loading_bar(frames_index, len, percent_complete=nil, skipped_percent= 0, max= 100)
25
25
  percent = ((frames_index.to_f/len)*100).round(4)
26
26
  percent = percent.round if !percent_complete.nil?
27
- # puts "percent #{percent}"
28
- # puts "fun percent_complete #{percent_complete}"
29
- if level > 1 && (percent_complete.nil? || (percent_complete.round < percent))
30
- App.logger.print_and_flush("__#{percent}%__")
27
+ percent = skipped_percent + (percent.to_f/100.0)*(max-skipped_percent)
28
+ if level > 0 && (percent_complete.nil? || (percent_complete.round < percent))
29
+ App.logger.print_and_flush(" #{percent.round}% ")
31
30
  end
32
31
  percent
33
32
  end
@@ -1,5 +1,7 @@
1
1
  # mixes two tracks together or plays one or the other
2
2
  class Mixer
3
+
4
+
3
5
  # get volumes at a position
4
6
  def self.get(first, second, reduction)
5
7
  reduction2 = reduction > 1 ? 1 : reduction
@@ -100,6 +100,7 @@ def self.load_all
100
100
  load File.dirname(__FILE__) + '/api/dist.rb'
101
101
  load File.dirname(__FILE__) + '/api/hit_sq.rb'
102
102
  load File.dirname(__FILE__) + '/api/snd.rb'
103
+ load File.dirname(__FILE__) + '/api/mapper.rb'
103
104
  end
104
105
 
105
106
  def self.clear_dir dir_path
@@ -80,8 +80,11 @@ end
80
80
  files.addlist sn.render(into), delay_in_frames
81
81
  end
82
82
  tss.each {|sn| files.addlist sn.render(into), delay_in_frames}
83
- App.done += 1 if !tss.empty?
84
- App.logger.print_loading_bar(App.done, App.total)
83
+ # puts "#{ App.done} #{App.total}"
84
+ if !tss.empty?
85
+ App.done += 1
86
+ App.logger.print_loading_bar(App.done, App.total, nil, 0, 50)
87
+ end
85
88
  end
86
89
  files.child_len = (len)
87
90
  files
@@ -1,6 +1,26 @@
1
1
  load File.dirname(__FILE__) + "/music_coder.rb"
2
2
  require "test/unit"
3
3
 
4
+ class TestTones < Test::Unit::TestCase
5
+
6
+ def test_setters
7
+ t= Tone.new
8
+ t.freq.start = (200)
9
+ assert_equal(200, t.freq.start)
10
+ t.freq.final = (20)
11
+ assert_equal(20, t.freq.final)
12
+ t.set_freq_final (30)
13
+ assert_equal(30, t.freq.final)
14
+ t.set_freq_final(20, false)
15
+ assert_equal(-180, t.freq.final)
16
+ assert_equal(-180, t.freq_final)
17
+ assert_equal(20, t.freq_final(false))
18
+ t.set_freq(250)
19
+ assert_equal(-230, t.freq_final)
20
+ t.set_freq(240)
21
+ assert_equal(-220, t.freq_final)
22
+ end
23
+ end
4
24
  class TestNotes < Test::Unit::TestCase
5
25
  def test_ops
6
26
  n=Note.new(1,5) + (-3)
@@ -60,6 +80,24 @@ class TestHitSq < Test::Unit::TestCase
60
80
  assert_equal(4, @h.count)
61
81
  end
62
82
 
83
+ def test_hits_trim
84
+ h=HitSq.new
85
+ h.eqly_spaced(8)
86
+ h.trim_end(0.25)
87
+ assert_equal(([0.0, 0.125, 0.25, 0.375, 0.5, 0.625]), h.hits)
88
+ h.trim_start(0.25)
89
+ assert_equal(([0.25, 0.375, 0.5, 0.625]), h.hits)
90
+
91
+
92
+ h.hits = []
93
+ h << 4.eqly_spaced
94
+ assert_equal([0.0,0.25,0.5,0.75], h.hits)
95
+ h.trim_start(0.25)
96
+ assert_equal([0.25,0.5,0.75], h.hits)
97
+ h.trim_end(0.34)
98
+ assert_equal([0.25,0.5], h.hits)
99
+ end
100
+
63
101
  def test_hits_move
64
102
  assert_equal(([0.2,0.3,0.4]), @h.hits)
65
103
  @h.move(0.1)
@@ -21,6 +21,88 @@ def initialize(args = nil)
21
21
  init_hash(args)
22
22
  end
23
23
 
24
+ # set frequency #freq#start
25
+ def set_freq (val)
26
+ dif = freq.start - val
27
+ freq.start = val
28
+ set_freq_final freq_final(false) + dif, false
29
+ end
30
+
31
+ def freq_final(is_relative=true)
32
+ return is_relative ? freq.final : freq.start + freq.final
33
+ end
34
+
35
+ # random amp.
36
+ def rand_amp_both(max = 0.8, min = 0.0025)
37
+ upper = max - min
38
+
39
+ val = rand * upper
40
+ self.amp.start = min + val
41
+ val = rand * val # can't be more
42
+ self.set_amp_final (min + val), false
43
+ self.amp.rand_exp
44
+ end
45
+
46
+ # random ammount of detail in a wave. weighted toward lower values weight times.
47
+ def rand_detail_both(min = 3, max = 512, weight=2)
48
+ upper = max - min
49
+
50
+ val = rand * upper
51
+ weight.times {val *= rand}
52
+ self.wave.detail.start= min + val
53
+ val = rand * upper
54
+ weight.times {val *= rand}
55
+ self.wave.detail.final= min + val
56
+ # puts "start detail #{val}"
57
+ # puts "final detail #{val}"
58
+ self.wave.detail.rand_exp
59
+ end
60
+
61
+ # random ammount of saturation, weighted toward lower values weight times.
62
+ def rand_sat_both weight=2, max=1.0
63
+ sat = max
64
+ weight.times {sat *= rand}
65
+ self.saturations.start= sat
66
+
67
+ sat = max
68
+ weight.times {sat *= rand}
69
+ self.saturations.final= sat
70
+ self.saturations.rand_exp
71
+ # puts "saturations.start #{saturations.start}"
72
+ # puts "saturations.final #{saturations.final}"
73
+ # puts "saturations.x #{saturations.exp}"
74
+ end
75
+
76
+ # random start frequency in range.
77
+ def rand_freq(min = 12, max = 20_000)
78
+ upper = max - min
79
+ val = min + rand*upper
80
+ self.freq.start = val
81
+ end
82
+
83
+ # random frequencies in range.
84
+ def rand_freq_both(min = 12, max = 20_000)
85
+ upper = max - min
86
+
87
+ rand_freq min, max
88
+ val = min + rand*upper # final val
89
+ self.set_freq_final val, false
90
+ self.freq.rand_exp
91
+ # puts "freq.start #{freq.start}"
92
+ # puts "freq.f #{freq.final}"
93
+ # puts "freq.x #{freq.exp}"
94
+ end
95
+
96
+ #is_relative:: false for setting it absolute
97
+ def set_freq_final fq, is_relative=true
98
+ self.freq.final = is_relative ? fq : fq - freq.start
99
+ end
100
+
101
+ #is_relative:: false for setting it absolute
102
+ def set_amp_final val, is_relative=true
103
+ self.amp.final = is_relative ? val : val - amp.start
104
+ end
105
+
24
106
  # set saturation for both start and end
25
107
  def saturation= val
26
108
  wave.saturation= val
@@ -100,12 +182,6 @@ def out
100
182
  data
101
183
  end
102
184
 
103
- # setting it absolute
104
- def set_freq_final_no_relative(final)
105
- dif=final - freq.start
106
- freq.final=dif
107
- end
108
-
109
185
  # set #freq based off a Note
110
186
  def note=(note)
111
187
  freq.start = note.freq
@@ -1,6 +1,6 @@
1
- # one tone or two tones that can be morphed between eachother
1
+ # one or two Tone that can be morphed between eachother
2
2
  class TonePart
3
- #Fader
3
+ #Fader of Tone
4
4
  attr_accessor :tones
5
5
  attr_accessor :max_frames
6
6
  attr_accessor :tone_single
@@ -2,10 +2,63 @@
2
2
  class ToneSeq
3
3
  #Array of TonePart
4
4
  attr_accessor :toneparts
5
+ #when joined, a tone sequence makes the end of each tone
6
+ #the same as the start of the next one.
7
+ #this creates a smooth sound.
8
+ #todo
9
+ def join
10
+ toneparts.count.times do |i|
11
+ # if more after me, do it
12
+ if i+1 < toneparts.count
13
+ me = toneparts[i].tone(0)
14
+ nxt = toneparts[i+1].tone(0)
15
+ me.detail.final = nxt.detail.start
16
+ me.saturations.final = nxt.saturations.start
17
+ me.set_freq_final( nxt.freq.start, false)
18
+ me.set_amp_final( nxt.amp.start, false)
19
+
20
+ me = toneparts[i].tone(1)
21
+ nxt = toneparts[i+1].tone(1)
22
+ me.detail.final = nxt.detail.start
23
+ me.saturations.final = nxt.saturations.start
24
+ me.set_freq_final( nxt.freq.start, false)
25
+ me.set_amp_final( nxt.amp.start, false)
26
+ end
27
+ end
28
+ end
29
+
5
30
  def initialize()
6
31
  @toneparts = []
7
32
  make(1)
8
33
  end
34
+
35
+ #random everything
36
+ def random extra_detail = 5, even = false, delay=0, start_amp = 0.5,
37
+ max_f = 2000, min_f = 120, max_sat=0.2, min_detail=50
38
+ make(extra_detail) # sets the lens evenly.
39
+ frames_left = self.frames - self.frames.to_f*delay #- extra_detail+1 #minus a little so it must have 1 frame
40
+ toneparts.count.times do |i|
41
+ # sets the lens randomly.
42
+ portion = frames_left * rand
43
+ # puts "portion #{portion}"
44
+ if i == toneparts.count
45
+ portion = frames_left
46
+ end
47
+ frames_left -= 1+portion
48
+ toneparts[i].tone.frames = 1+portion if !even
49
+ #
50
+ toneparts[i].tone.rand_sat_both 3, max_sat
51
+ toneparts[i].tone.rand_freq_both(min_f, max_f)
52
+ toneparts[i].tone.rand_detail_both min_detail
53
+ max_amp = start_amp
54
+ if i>0 # it can't be higher than the last amp
55
+ max_amp = toneparts[i-1].tone.amp.start
56
+ end
57
+ toneparts[i].tone.rand_amp_both max_amp, max_amp * 0.75 # not much lower
58
+ end
59
+ join
60
+ end
61
+
9
62
  #return the total frames of all toneparts combined.
10
63
  def frames
11
64
  total=0
@@ -50,5 +103,10 @@ class ToneSeq
50
103
  files.write data
51
104
  files
52
105
  end
106
+
107
+ #reduce amp of all tones by this val
108
+ def amp_reduce val
109
+ toneparts.each { |tp| tp.amp_mult(1.0/val) }
110
+ end
53
111
 
54
112
  end
@@ -5,7 +5,7 @@ class Wave
5
5
  # Fader.final is at the end. nil means same as wave start
6
6
  attr_accessor :detail
7
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
8
+ #the ammount of saturation is higher is more, 0 is none. range: 0 to 1.
9
9
  attr_accessor :saturations
10
10
  # hack to dramatically speed it up when on.
11
11
  attr_accessor :cache_wave, :old_wave
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.7.2
4
+ version: 0.9.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -48,6 +48,7 @@ files:
48
48
  - lib/audio.rb
49
49
  - lib/fader.rb
50
50
  - lib/api/snd.rb
51
+ - lib/api/mapper.rb
51
52
  - lib/api/hit_sq.rb
52
53
  - lib/api/dist.rb
53
54
  - lib/api/api.rb
@@ -79,7 +80,7 @@ rubyforge_project:
79
80
  rubygems_version: 1.8.23
80
81
  signing_key:
81
82
  specification_version: 3
82
- summary: An application to programmatically create music through code.
83
+ summary: An application to programmatically create music by coding.
83
84
  test_files:
84
85
  - lib/tests.rb
85
86
  has_rdoc: true