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,108 @@
1
+ #base class for most of the things the user will touch directly
2
+ class Api
3
+ def initialize
4
+ @parents=[]
5
+ end
6
+ # Add things to me
7
+ def << toadd
8
+ # puts "type: #{toadd.class}"
9
+ case toadd
10
+ when Array
11
+ # puts "recognised array"
12
+ toadd.each do |ta|
13
+ r = add_single ta
14
+ raise "This type is not recongnised." if r ==false
15
+ end
16
+ else
17
+ r=add_single toadd
18
+ raise "This type is not recongnised." if r==false
19
+ end
20
+ self
21
+ end
22
+ # Delete things from me
23
+ def >> todel
24
+ # puts "type: #{todel.class}"
25
+ case todel
26
+ when Array
27
+ # puts "deleting array #{todel}"
28
+ todel.each do |ta|
29
+ r = del_single ta
30
+ raise "This type is not recongnised." if r ==false
31
+ end
32
+ else
33
+ r=del_single todel
34
+ raise "This type is not recongnised." if r==false
35
+ end
36
+ self
37
+ end
38
+
39
+ def parent index=0
40
+ @parents[index]
41
+ end
42
+
43
+ protected
44
+ def add_parent obj
45
+ @parents << obj
46
+ self
47
+ end
48
+
49
+ end#class
50
+
51
+ def load_state
52
+ f=File.open("../output/text/saved.rb",'r')
53
+ content=f.read
54
+ # puts content
55
+ App.out= YAML.load(content)
56
+ f.close
57
+ end
58
+
59
+ def save_state
60
+ App.out.write_text_files
61
+ end
62
+
63
+ def render
64
+ App.out.render
65
+ end
66
+
67
+ def make
68
+ App.out.make_audio_file
69
+ end
70
+
71
+ def beat i=1
72
+ z=Composer.beat i
73
+ z.to_i
74
+ end
75
+ def bar i=1
76
+ z=Composer.beat i*4
77
+ z.to_i
78
+ end
79
+
80
+ def queue dist
81
+ App.out.snddists<<dist.instance_variable_get(:@dist)
82
+ end
83
+
84
+ # _rendered files
85
+ def clear
86
+ App.clear_ready
87
+ end
88
+
89
+ def bpm_set val
90
+ Composer.bpm = val
91
+ end
92
+ def bpm
93
+ Composer.bpm
94
+ end
95
+ def compute
96
+ render
97
+ save_state
98
+ make
99
+ end
100
+ # send str to be logged.
101
+ # level:: it won't be logged unless Logger.level is at or above this level.
102
+ def log str, level=3
103
+ App.logger.log str, level
104
+ end
105
+ # set Logger.level. Higher means more logging. 0 is silent.
106
+ def log_level set
107
+ App.logger.level = set
108
+ end
@@ -0,0 +1,145 @@
1
+ # a consecutave sequence of morphable tones (TonePart)
2
+ # of varying lengths, and rate of morphs, played without gaps.
3
+ class Dist < Api
4
+ attr_accessor :dist
5
+ def initialize
6
+ super
7
+ @dist=SndDist.new
8
+ @hits=HitSq.new
9
+ @hits.add_parent self
10
+ persist_hits
11
+ end
12
+ # delete another Dist
13
+ def del todel
14
+ @dist.snd.delete todel.dist
15
+ self
16
+ end
17
+ # set the total length in frames
18
+ def length= set
19
+ @dist.len = set
20
+ @dist.tss.each {|tss| tss.len = set }
21
+ self
22
+ end
23
+ # get the total length in frames
24
+ def length
25
+ @dist.len
26
+ end
27
+ # delete all hits
28
+ def clear_hits
29
+ @hits.hits = []
30
+ persist_hits
31
+ self
32
+ end
33
+ # get a child
34
+ def [] i
35
+ child=@dist.get_children[i]
36
+ raise "This Dist has no child at index #{i}. " +
37
+ "It has #{branches} children." if child.nil?
38
+ d=Dist.new
39
+ d.dist=child
40
+ d.make_hits
41
+ d
42
+ end
43
+ # count children
44
+ def branches
45
+ @dist.get_children.count
46
+ end
47
+ # getter for HitSq. Will persist any changes you make to it.
48
+ def hits
49
+ @hits
50
+ end
51
+ # getter for Snd. Will persist any changes you make to it.
52
+ def snd i=0
53
+ snd=Snd.new
54
+ snd.add_parent self
55
+ new=@dist.tss[i]
56
+ raise "Dist has no sound at index #{i}. It has #{sounds} sounds." if new.nil?
57
+ snd.snd = new
58
+ snd
59
+ end
60
+ # make a new sound with len
61
+ def make_sound
62
+ snd=Snd.new
63
+ snd.snd.len = @dist.len
64
+ self<<snd
65
+ self
66
+ end
67
+ # count sounds
68
+ def sounds
69
+ @dist.tss.count
70
+ end
71
+ # (internal use only) copy our hits down to the underlining object
72
+ def persist_hits
73
+ @dist.hits = hits.hits
74
+ @dist.hits = [0.0] if hits.hits.count == 0
75
+ self
76
+ end
77
+ protected
78
+ # (internal use only) create our hits from the underlining object
79
+ def make_hits
80
+ @hits.hits = @dist.hits
81
+ self
82
+ end
83
+
84
+ private
85
+ # adds a sound if it's valid to do so
86
+ def validate_snd! val
87
+ raise "This Dist can't have sounds, it has children. " if branches > 0
88
+ @dist.tss << val
89
+ val.toneparts.each {|tp|
90
+ # puts "RUNNING #{length / val.toneparts.count.to_f}"
91
+ tp.max_frames = length / val.toneparts.count }
92
+ end
93
+ # adds a dist if it's valid to do so
94
+ def validate_dist! val
95
+ raise "This Dist can't have children, it has sounds. " if sounds > 0
96
+ @dist.add val
97
+ end
98
+ # Add hits, sounds or other dists to me.
99
+ def add_single toadd
100
+ case toadd
101
+ when HitSq, Float, Integer
102
+ @hits << toadd
103
+ persist_hits
104
+ when Snd
105
+ validate_snd! toadd.snd
106
+ when Dist
107
+ validate_dist! toadd.dist
108
+ else
109
+ return false
110
+ end
111
+ true
112
+ end
113
+ # Delete hits, sounds or other dists from my lists.
114
+ def del_single todel
115
+ case todel
116
+ when HitSq, Float, Integer
117
+ @hits >> todel
118
+ persist_hits
119
+ when Snd
120
+ @dist.tss.delete todel.snd
121
+ when Dist
122
+ @dist.snd.delete (todel.dist)
123
+ else
124
+ return false
125
+ end
126
+ true
127
+ end
128
+ end
129
+
130
+ # shortcut e.g. 10.Dist for a dist with length of 10 frames
131
+ class Integer
132
+ def Dist
133
+ h=Dist.new
134
+ h.length = self.to_i
135
+ h
136
+ end
137
+ end
138
+ # shortcut e.g. 0.0.Dist for the most used, a single hit at 0.0
139
+ class Float
140
+ def Dist
141
+ h=Dist.new
142
+ h<<self
143
+ h
144
+ end
145
+ end
@@ -0,0 +1,108 @@
1
+ # a pattern of hits to time a Snd
2
+ class HitSq < Api
3
+ attr_accessor :hits
4
+ def initialize
5
+ @hits=[]
6
+ super
7
+ end
8
+ private
9
+ # delete value
10
+ def del_single todel
11
+ @hits.delete todel.to_f
12
+ self
13
+ end
14
+ def validate! input
15
+ val = input.to_f
16
+ raise "Hit is too high #{input}. Range is 0 to 1." if val > 1.0
17
+ raise "Hit is too low #{input}. Range is 0 to 1." if val < 0.0
18
+
19
+ #dup?
20
+ a = hits.dup
21
+ a << val
22
+ if a.uniq.length != a.length
23
+ log "Warning: skipping duplicate hit.", 3
24
+ else
25
+ @hits << val
26
+ end
27
+ @parents.each {|par| par.persist_hits}
28
+ end
29
+ # Add hits.
30
+ def add_single toadd
31
+ case toadd
32
+ when Float, Integer
33
+ validate! toadd
34
+ when HitSq
35
+ toadd.hits.each { |val| validate! val }
36
+ else
37
+ false
38
+ end
39
+ end
40
+ public
41
+ #Adds into #hits.
42
+ #possible_hits:: number of hits that can occur. Must be int
43
+ #chance:: chance a hit will be included. range: 0 to 1
44
+ #ignore_first:: skip the first n possible hits
45
+ #ignore_first:: skip the last n possible hits
46
+ #e.g. disperse_hits(16,1,4,4) makes this pattern [-|-|-|-|+|+|+|+|+|+|+|+|-|-|-|-|]
47
+ def eqly_spaced(possible_hits = 4, chance = 1, ignore_first=0, ignore_last=0)
48
+ possible_hits.times do |i|
49
+ if ignore_first <= i && possible_hits - ignore_last > i
50
+ delay = i/possible_hits.to_f
51
+ @hits.push delay if (rand + chance >= 1)
52
+ end
53
+ end
54
+ self
55
+ end
56
+ #return number of hits
57
+ def count
58
+ hits.count
59
+ end
60
+ # Shift all hits by val, no validation atm
61
+ def move(val=0.5)
62
+ self.hits.collect! {|x|
63
+ z = (x+val)
64
+ z = (z*1000).round / 1000.0 # rounding
65
+ x = z
66
+ } # delay all
67
+ self
68
+ end
69
+ end
70
+ # shortcut e.g. 0.HitSq for the most used, a single hit at 0
71
+ class Integer
72
+ def HitSq
73
+ h=HitSq.new
74
+ h<<self
75
+ h
76
+ end
77
+ end
78
+ # shortcut e.g. 0.5.HitSq
79
+ class Float
80
+ def HitSq
81
+ h=HitSq.new
82
+ h<<self
83
+ h
84
+ end
85
+ end
86
+ class Array
87
+ def HitSq
88
+ h=HitSq.new
89
+ h<<self
90
+ h
91
+ end
92
+
93
+ #return a new HitSq. a shortcut for HitSq.new.move(val)
94
+ def move(val)
95
+ self.collect! {|x| x=x+val} # delay all
96
+ h=HitSq.new
97
+ h<<self
98
+ h
99
+ end
100
+ end
101
+ class Integer
102
+ #a shortcut. e.g. 4.eqly_spaced gives you HitSq.new.eqly_spaced(4)
103
+ def eqly_spaced(chance = 1, ignore_first=0, ignore_last=0)
104
+ h=HitSq.new
105
+ possible_hits=self
106
+ h.eqly_spaced(possible_hits, chance, ignore_first, ignore_last)
107
+ end
108
+ end
@@ -0,0 +1,59 @@
1
+ # A musical note.
2
+ # E.g. A sharp in octave 3.
3
+ class Note
4
+ # note without octave. from 0 to 11 starting at A ending in G#
5
+ attr_accessor :note
6
+ # octave, starts at 0, an 'A' in '5' is 440 hz
7
+ attr_accessor :octave
8
+ def initialize(note = 0, octave = 4)
9
+ if note.class == String
10
+ @note = Composer.note_m(note)
11
+ else
12
+ @note = note
13
+ end
14
+ @octave = octave
15
+ end
16
+
17
+ # returns a value up n semitones. (changes octave where necessary)
18
+ def +(n = 1)
19
+ i=0
20
+ out = deep_copy
21
+ while i < n do
22
+ out = Note.new(out.note+1, out.octave)
23
+ out = Note.new(0, out.octave+1) if out.note==12
24
+ i+=1
25
+ end
26
+ out
27
+ end
28
+ # returns a value down n semitones. (changes octave where necessary)
29
+ def -(n = 1)
30
+ i=0
31
+ out = deep_copy
32
+ while i < n do
33
+ out = Note.new(out.note-1, out.octave)
34
+ out = Note.new(11, out.octave-1) if out.note==-1
35
+ i+=1
36
+ end
37
+ out
38
+ end
39
+
40
+ #return the frequency (HZ) of a note.
41
+ def freq
42
+ raise "no note or oct given" if (!note || !octave)
43
+ a=2 ** (octave.to_f-1)
44
+ b=1.059463 ** note.to_f
45
+ out = 27.5*a*b
46
+ return out
47
+ end
48
+
49
+ # return true if this note has the same values as another note.
50
+ def is_eql(other)
51
+ vars_eql? other
52
+ end
53
+
54
+ # create a sound based off this note.
55
+ def Snd
56
+ s=freq.Snd
57
+ s
58
+ end
59
+ end
@@ -0,0 +1,62 @@
1
+ # a consecutave sequence of morphable tones (TonePart)
2
+ # of varying lengths, and rate of morphs, played without gaps.
3
+ class Snd < Api
4
+ def initialize
5
+ @snd=ToneSeq.new
6
+ super
7
+ end
8
+
9
+ def tonepart i=0
10
+ @snd.tonepart i
11
+ end
12
+
13
+ def tone i=0, j=0
14
+ @snd.tonepart(j).tone(i)
15
+ end
16
+ # set length
17
+ def length= val
18
+ @snd.frames=val
19
+ self
20
+ end
21
+ def t i=0
22
+ child = @snd.toneparts[i]
23
+ raise "This Snd has no tone at index #{i}. " +
24
+ "It has #{count} tones." if child.nil?
25
+ child
26
+ end
27
+ # number of tones
28
+ def count
29
+ snd.toneparts.count
30
+ end
31
+ def fade
32
+ snd.fade
33
+ end
34
+ attr_accessor :snd
35
+ private
36
+ # Add hits, sounds or other dists to me.
37
+ def add_single toadd
38
+ case toadd
39
+ when Snd
40
+ @snd = toadd.instance_variable_get(:@snd)
41
+ else
42
+ return false
43
+ end
44
+ true
45
+ end
46
+
47
+ end
48
+
49
+ class Fixnum
50
+ def Snd
51
+ a = Snd.new
52
+ a.t.freq = self
53
+ a
54
+ end
55
+ end
56
+ class Float
57
+ def Snd
58
+ a = Snd.new
59
+ a.t.freq = self
60
+ a
61
+ end
62
+ end