ramekin 0.0.5b

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,101 @@
1
+ module Ramekin
2
+ class MacroDefinition < Element
3
+ attr_reader :token, :elements
4
+ def initialize(token, elements=[])
5
+ @token = token
6
+ @elements = elements
7
+ end
8
+
9
+ def name
10
+ @token.value
11
+ end
12
+
13
+ def inspect
14
+ "macro:#{name}=[#{@elements.map(&:first).map(&:repr_basic).join(',')}]"
15
+ end
16
+ end
17
+
18
+ class MacroExpander < Processor
19
+ def initialize
20
+ @definitions = Hash.new { |h, k| h[k] = [] }
21
+ @defines = {}
22
+ @ifdef_depth = 0
23
+ @skip_depth = nil
24
+ end
25
+
26
+ def call(&b)
27
+ @level = 0
28
+ parse_chunk(nil) do |el, stack|
29
+ el.macro_stack = stack
30
+ yield el
31
+ end
32
+ end
33
+
34
+ def parse_chunk(expected_end=nil, &b)
35
+ @level += 1
36
+
37
+ each do |el|
38
+ if @skip_depth && @ifdef_depth >= @skip_depth
39
+ if Token === el && el.type == :endif
40
+ @ifdef_depth -= 1
41
+
42
+ if @ifdef_depth < @ifdef_depth
43
+ @skip_depth = nil
44
+ end
45
+ end
46
+
47
+ next
48
+ end
49
+
50
+ el.macro_stack = @stack.dup
51
+ next yield el, [] unless Token === el
52
+
53
+ if el.type == expected_end
54
+ break
55
+ end
56
+
57
+ case el.type
58
+ when :macro
59
+ definition = MacroDefinition.new(el)
60
+ @definitions[el.value] = definition
61
+ parse_chunk :endmacro do |sub, stack|
62
+ definition.elements << [sub, stack + [el]]
63
+ end
64
+
65
+ # yield definition, []
66
+ when :macrocall
67
+ @definitions[el.value].elements.each do |sub, stack|
68
+ yield sub.dup, stack
69
+ end
70
+ when :endmacro
71
+ error! 'endmacro token outside of a macro definition'
72
+ when :define
73
+ # TODO: value defines (needs lexer support)
74
+ @defines[el.value] = 1
75
+ when :ifdef
76
+ @ifdef_depth += 1
77
+ unless @defines.key?(el.value)
78
+ @skip_depth = @ifdef_depth
79
+ end
80
+ when :ifndef
81
+ @ifdef_depth += 1
82
+ if @defines.key?(el.value)
83
+ @skip_depth = @ifdef_depth
84
+ end
85
+ when :endif
86
+ @ifdef_depth -= 1
87
+ else
88
+ yield el, []
89
+ end
90
+ end
91
+ rescue StopIteration
92
+ if @level > 1
93
+ error! "unclosed replacement: missing double quote" if @level > 1
94
+ end
95
+ # pass
96
+ ensure
97
+ @level -= 1
98
+ end
99
+ end
100
+ end
101
+
@@ -0,0 +1,227 @@
1
+ module Ramekin
2
+ class Meta
3
+ include Error::Helpers
4
+
5
+ attr_reader :title
6
+ attr_reader :game
7
+ attr_reader :author
8
+ attr_reader :comment
9
+ attr_reader :readme
10
+ attr_reader :amk
11
+ attr_reader :instruments
12
+ attr_reader :sample_groups
13
+ attr_reader :options
14
+ attr_reader :volume
15
+ attr_reader :tempo
16
+ attr_reader :echo
17
+ def initialize(elements)
18
+ @elements = elements
19
+ @instruments = []
20
+ @sample_groups = []
21
+ @options = {}
22
+ end
23
+
24
+ def parse
25
+ loop do
26
+ break if @elements.empty?
27
+ next!
28
+
29
+ case @current.type
30
+ when :directive then parse_directive
31
+ when :amk then @amk = @current
32
+ when :option then @options[@current.value] = true
33
+ when :w then @volume = @current
34
+ when :t, :bpm then @tempo = @current
35
+ else
36
+ error! 'invalid token in header'
37
+ end
38
+ end
39
+
40
+ @sample_groups << 'default' if @sample_groups.empty?
41
+ end
42
+
43
+ def parse_directive
44
+ @current_directive = @current
45
+ case @current.value
46
+ when 'title' then @title = expect_arg(:string)
47
+ when 'game' then @game = expect_arg(:string)
48
+ when 'author' then @author = expect_arg(:string)
49
+ when 'comment' then @comment = expect_arg(:string)
50
+ when 'readme' then @readme = expect_arg(:string)
51
+ when 'pack' then
52
+ pack_name = expect_arg(:string)
53
+ @current_pack = SamplePack.find(pack_name.value) \
54
+ or error!("can't find sample pack #{pack_name.value.inspect}, try running `ramekin package update`")
55
+ when 'instrument'
56
+ name, path = expect_args(:instrument, :string)
57
+ extensions = []
58
+ while (el = check_arg(:adsr, :tuning, :o))
59
+ extensions << el
60
+ end
61
+
62
+ return unless @current_pack
63
+ @instruments << Instrument.new(@current_pack, @current, name, path, extensions)
64
+ when 'default', 'optimized' then @sample_groups << @current
65
+
66
+ # TODO: real echo syntax
67
+ when 'echo' then @echo = expect_args(*([:hex] * 8))
68
+ else
69
+ error! 'invalid directive in header'
70
+ end
71
+ end
72
+
73
+ def expect_args(*types)
74
+ types.map do |type|
75
+ next!
76
+ unless @current.type == type
77
+ error! "expected #{type} in ##{@current_directive.value} directive"
78
+ end
79
+
80
+ @current
81
+ end
82
+ end
83
+
84
+ def expect_arg(type)
85
+ expect_args(type).first
86
+ end
87
+
88
+ def check_arg(*types)
89
+ if peek && types.include?(peek.type)
90
+ next!
91
+ @current
92
+ else
93
+ nil
94
+ end
95
+ end
96
+
97
+ def next!
98
+ if @peek
99
+ @current = @peek
100
+ @peek = nil
101
+ return @current
102
+ end
103
+
104
+ @current = @elements.shift
105
+ end
106
+
107
+ def peek
108
+ @peek ||= @elements.shift
109
+ end
110
+ end
111
+
112
+ # TODO: customization options for instrument
113
+ class Instrument < Element
114
+ attr_reader :pack, :directive, :name, :path
115
+ def initialize(pack, directive, name, path, extensions)
116
+ @pack = pack
117
+ @directive = directive
118
+ @name = name
119
+ @path = path
120
+ @extensions = extensions
121
+ end
122
+
123
+ include Error::Helpers
124
+ # @override
125
+ def default_error_location
126
+ @directive
127
+ end
128
+
129
+ def inspect
130
+ "inst:@#{name.value} #{@extensions.inspect}"
131
+ end
132
+
133
+ def sample_name
134
+ @sample_name ||= File.basename(sample_path)
135
+ end
136
+
137
+ def sample_path
138
+ @pack.find(@path.value)
139
+ end
140
+
141
+ def pack_adsr
142
+ return if pack_hexes.nil?
143
+
144
+ hex1, hex2, _ = pack_hexes
145
+ h1 = hex1.to_i(16)
146
+ h2 = hex2.to_i(16)
147
+
148
+ # unset the first bit, as it is unused
149
+ h1 &= ~0x80
150
+
151
+ decay = (7 - (h1 >> 4))
152
+ attack = (15 - (h1 & 0b1111))
153
+ sustain = h2 >> 5
154
+ release = (31 - (h2 & 0b11111))
155
+
156
+ [attack, decay, sustain, release]
157
+ end
158
+
159
+ def ext_adsr
160
+ adsr = @extensions.select { |e| e.type == :adsr }.last
161
+ adsr && adsr.value.split(',').map(&:to_i)
162
+ end
163
+
164
+ def adsr
165
+ @adsr ||= ext_adsr || pack_adsr \
166
+ or error! "no adsr configured for #{name}"
167
+ end
168
+
169
+ def tuning
170
+ @tuning ||= ext_tuning || pack_tuning \
171
+ or error! "no tuning configured for #{name}"
172
+ end
173
+
174
+ def pack_tuning
175
+ _, _, _, d, e = pack_hexes
176
+ [d, e]
177
+ end
178
+
179
+ def ext_tuning
180
+ tuning = @extensions.select { |e| e.type == :tuning }.last
181
+ tuning && tuning.value.scan(/../)
182
+ end
183
+
184
+ def pack_hexes
185
+ # TODO: alts
186
+ @pack_hexes ||= @pack.tunings_for(sample_name).first
187
+ end
188
+
189
+ def pack_gain
190
+ _, _, g, _, _ = pack_hexes
191
+ return g
192
+ end
193
+
194
+ def ext_gain
195
+ gain = @extensions.select { |e| e.type == :gain }.last
196
+ gain && gain.value
197
+ end
198
+
199
+ def gain
200
+ @gain ||= ext_gain || pack_gain \
201
+ or error! "no gain configured for #{name}"
202
+ end
203
+
204
+ def hexes
205
+ a, d, s, r = self.adsr
206
+ t1, t2 = self.tuning
207
+ g = self.gain
208
+
209
+ adsr1 = ((7 - d)*16 | 0x80) + (15 - a)
210
+ adsr2 = (s*32 + (31-r))
211
+
212
+ [adsr1.to_s(16), adsr2.to_s(16), g, t1, t2]
213
+ end
214
+
215
+ def to_amk
216
+ "#{File.basename(sample_name).inspect} #{hexes.map { |h| "$#{h}" }.join(' ')}"
217
+ end
218
+
219
+ def octave
220
+ @extensions.reverse_each do |ext|
221
+ return ext.value.to_i if Token === ext && ext.type == :o
222
+ end
223
+
224
+ 4
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,252 @@
1
+ module Ramekin
2
+ class NoteEvent < Element
3
+ include Error::Helpers
4
+ def default_error_location
5
+ self
6
+ end
7
+
8
+ attr_reader :note, :extensions, :octave
9
+ def initialize(note, octave, triplets, extensions=[])
10
+ @note = note
11
+ @octave = octave
12
+ @triplets = triplets
13
+ @extensions = extensions
14
+ end
15
+
16
+ def start
17
+ @note.start
18
+ end
19
+
20
+ def fin
21
+ (@extensions.last || @note).fin
22
+ end
23
+
24
+ def octave_num
25
+ case @octave
26
+ when Token then @octave.value.to_i
27
+ else @octave.to_i
28
+ end
29
+ end
30
+
31
+ def note_name
32
+ return 'r' if rest?
33
+ return '^' if tie?
34
+ @note.value
35
+ end
36
+
37
+ def rest?
38
+ @note.type == :r
39
+ end
40
+
41
+ def tie?
42
+ @note.type == :native_tie
43
+ end
44
+
45
+ KNOWN_LENGTHS = {}.tap do |out|
46
+ ticks = 192
47
+ length = 1
48
+
49
+ loop do
50
+ out[ticks] = length.to_s
51
+
52
+ break unless ticks % 2 == 0
53
+
54
+ out[ticks * 3 / 2] = "#{length}."
55
+ length *= 2
56
+ ticks /= 2
57
+ end
58
+ end
59
+
60
+ def length_amk
61
+ # we use l16
62
+ return '' if ticks == 12
63
+ KNOWN_LENGTHS.fetch(ticks) { "=#{ticks}" }
64
+ end
65
+
66
+ def octave_amk(current_octave=nil)
67
+ return '' if rest? || tie?
68
+
69
+ return "o#{octave_num}" if current_octave.nil?
70
+
71
+ case octave_num
72
+ when current_octave
73
+ ""
74
+ when current_octave + 1
75
+ ">"
76
+ when current_octave - 1
77
+ "<"
78
+ else
79
+ "o#{octave_num}"
80
+ end
81
+ end
82
+
83
+ def to_amk(current_octave=nil)
84
+ return "#{octave_amk(current_octave)}#{note_name}#{length_amk}"
85
+ end
86
+
87
+ def repr
88
+ "note:#{to_amk}"
89
+ end
90
+
91
+ def default_length
92
+ @note.meta[:l]
93
+ end
94
+
95
+ def ticks
96
+ if @extensions.empty? && default_length.nil?
97
+ error! "no length and no default specified with l", el: @note
98
+ end
99
+
100
+ exts = @extensions
101
+ exts = [default_length] if exts.empty?
102
+
103
+ base = exts.map do |ext|
104
+ ext = ext.value if Token === ext
105
+
106
+ case ext
107
+ when /\A=(\d+)\z/ then $1.to_i
108
+ when '0' then 192 * 2
109
+ when /\A(\d+)\z/ then 192 / $1.to_i
110
+ when /\A(\d+)[.]\z/ then 192 * 3 / ($1.to_i * 2)
111
+ else
112
+ error! "unknown length specifier #{ext}", el: ext
113
+ end
114
+ end.sum
115
+
116
+ base = (base * 2) / 3 if @triplets
117
+
118
+ base
119
+ end
120
+
121
+ def inspect
122
+ repr
123
+ end
124
+ end
125
+
126
+ class CombinedRestEvent < NoteEvent
127
+ def initialize(rests)
128
+ @rests = rests
129
+ error! "empty CombinedRestEvent" if rests.empty?
130
+ end
131
+
132
+ def start
133
+ rests.first.start
134
+ end
135
+
136
+ def fin
137
+ rests.last.fin
138
+ end
139
+
140
+ def rest?
141
+ true
142
+ end
143
+
144
+ def ticks
145
+ @rests.map(&:ticks).sum
146
+ end
147
+ end
148
+
149
+ class ScanForL < Processor
150
+ def call(&b)
151
+ @current_l = nil
152
+
153
+ each do |el|
154
+ case el.type
155
+ when :l
156
+ @current_l = el
157
+ when :note, :r, :native_tie
158
+ el.meta[:l] = @current_l
159
+ yield el
160
+ else
161
+ yield el
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ class NoteAggregator < Processor
168
+ def initialize
169
+ @current_note = nil
170
+ end
171
+
172
+ def flush!(&b)
173
+ if @current_note
174
+ yield @current_note
175
+ @current_note = nil
176
+ end
177
+
178
+ buffer.each(&b)
179
+ buffer.clear
180
+ end
181
+
182
+ def call(&b)
183
+ return enum_for(:call) unless block_given?
184
+
185
+ current_l = nil
186
+ current_octave = 4
187
+ @triplets = false
188
+
189
+ each do |el|
190
+ next yield el unless Token === el
191
+
192
+ case el.type
193
+ when :lbrace
194
+ @triplets = true
195
+ yield el
196
+ when :rbrace
197
+ @triplets = false
198
+ yield el
199
+ when :instrument
200
+ inst = el.meta[:inst]
201
+
202
+ current_octave = inst ? inst.octave : 4
203
+ buffer << el
204
+ when :l
205
+ current_l = el
206
+ when :note, :r, :native_tie
207
+ flush!(&b)
208
+ @current_note = NoteEvent.new(el, current_octave, @triplets)
209
+ when :o
210
+ current_octave = el.value.to_i
211
+ when :octave
212
+ current_octave += (el.value == '<' ? -1 : 1)
213
+ when :tie
214
+ # handle, for example, r^8
215
+ if @current_note.extensions.empty?
216
+ @current_note.extensions << @current_note.default_length
217
+ end
218
+
219
+ next
220
+ when :length
221
+ @current_note.extensions << el
222
+ else
223
+ buffer << el
224
+ end
225
+ end
226
+
227
+ flush!(&b)
228
+ end
229
+ end
230
+
231
+ class RestAggregator < Processor
232
+ def flush!(&b)
233
+ return if buffer.empty?
234
+ yield CombinedRestEvent.new(buffer)
235
+ @buffer = []
236
+ end
237
+
238
+ def call(&b)
239
+ each do |evt|
240
+ if NoteEvent === evt && evt.rest?
241
+ buffer << evt
242
+ next
243
+ end
244
+
245
+ flush!(&b)
246
+ yield evt
247
+ end
248
+
249
+ flush!(&b)
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,78 @@
1
+ module Ramekin
2
+ class Processor
3
+ attr_accessor :stream
4
+
5
+ include Error::Helpers
6
+
7
+ def buffer
8
+ @buffer ||= []
9
+ end
10
+
11
+ def flush!(&b)
12
+ return if buffer.empty?
13
+ buffer.each(&b)
14
+ buffer.clear
15
+ end
16
+
17
+ def self.call(enum, *a, &b)
18
+ return enum_for(:call, enum, *a) unless block_given?
19
+
20
+ enum = enum.each if enum.is_a?(Array)
21
+
22
+ inst = new(*a)
23
+ inst.stream = enum
24
+ inst.call(&b)
25
+ end
26
+
27
+ def each
28
+ loop do
29
+ next! or break
30
+ yield @current
31
+ end
32
+ end
33
+
34
+ def next!
35
+ if @peek
36
+ @current, @peek = @peek, nil
37
+ return @current
38
+ end
39
+
40
+ @current = @stream.next
41
+ end
42
+
43
+ def peek
44
+ @peek ||= @stream.next
45
+ rescue StopIteration
46
+ nil
47
+ end
48
+
49
+ def self.compose(*processors)
50
+ Chain.new(processors)
51
+ end
52
+ end
53
+
54
+ class Chain < Processor
55
+ def initialize(processors)
56
+ @processors = processors
57
+ end
58
+
59
+ def call(stream, &b)
60
+ out = stream
61
+
62
+ @processors.each do |p|
63
+ out = p.call(out)
64
+ end
65
+
66
+ out
67
+ end
68
+ end
69
+
70
+ class Inspector < Processor
71
+ def call(&b)
72
+ each do |el|
73
+ # p el
74
+ yield el
75
+ end
76
+ end
77
+ end
78
+ end