ramekin 0.0.5b

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