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,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