ramekin 0.3.2 → 0.4.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.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/lib/ramekin/cli.rb +1 -0
- data/lib/ramekin/fades.rb +30 -0
- data/lib/ramekin/meta.rb +1 -1
- data/lib/ramekin/note_aggregator.rb +1 -1
- data/lib/ramekin/renderer.rb +66 -45
- data/lib/ramekin/scanner.rb +56 -0
- data/lib/ramekin/tokenizer.rb +16 -7
- data/lib/ramekin/util.rb +18 -0
- data/lib/ramekin.rb +3 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 229317a8158d77d931e1cbb6b9a13e2755076137caa64ca70ff52b2ebc784d8a
|
4
|
+
data.tar.gz: 5b79ad0f53ec5b36c76abc993647e186561c1fdd8b69782915ea16a27d61add2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b916559016efa4bc6f35dffff5d0f06d27789585ab50ffbaa4db7b5cfc6ce7d35ed7361cc6ca73676c79f182d3645bf69d899636f4cd72bb33d652694a7a73a
|
7
|
+
data.tar.gz: bad898187585898529a0ae108f8c170a2bc7076efecadb23cc050714435b4172a508b002fd3984c3a385e3d8d4f08b67a76a39321092daf3e9dd50218bb5a9b3
|
data/README.md
CHANGED
@@ -131,8 +131,10 @@ A list of some ramekin preprocessor features (see `spec/rmk` for some examples!)
|
|
131
131
|
* `yLX` and `yRX` commands for panning left and right. `yC` to set back to center. Way easier for me to remember.
|
132
132
|
* The implementation of `&` is more robust, and it compiles to a proper `$dd` command. It can also much more flexibly chain multiple bends together, using a non-breaking tie `^^X` to establish where the boundaries of the bend should be, and handles quite a few idiosyncracies of `$dd` automatically behind the scenes. You should be able to use `&` and `^^` for all pitch bending needs. See "Pitch Expression" below for details.
|
133
133
|
```elisp
|
134
|
-
; bends to c two octaves up for two 16th notes, then continues for a quarter note
|
135
|
-
|
134
|
+
; bends to c two octaves up for two 16th notes, then continues for a quarter note.
|
135
|
+
; note the ties don't affect the bend, and a special tie ^^ is used to mark the true
|
136
|
+
; start of the bend.
|
137
|
+
b8 ^^ b16^16 & >>c4
|
136
138
|
```
|
137
139
|
* Instruments are named rather than numbered. See `doc/smw-instruments.rmk` for a list.
|
138
140
|
* Switching instruments always sets that instrument's default octave, by default `o4`. It does *not* reset transposition (you can use `_0` for that).
|
data/lib/ramekin/cli.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Ramekin
|
2
|
+
class Fades < Processor
|
3
|
+
SUPPORTED = [
|
4
|
+
:y, :rely,
|
5
|
+
:v, :relv,
|
6
|
+
:p,
|
7
|
+
:w,
|
8
|
+
:t,
|
9
|
+
]
|
10
|
+
|
11
|
+
def call(&b)
|
12
|
+
prev = nil
|
13
|
+
each do |el|
|
14
|
+
if NoteEvent === el && el.note.type == :fade
|
15
|
+
if prev.nil? || !(Token === prev) || !SUPPORTED.include?(prev.type)
|
16
|
+
error! 'invalid fade: must follow pan, volume, global volume, tempo, or #echo:vol commands'
|
17
|
+
end
|
18
|
+
|
19
|
+
prev.meta[:fade] = el
|
20
|
+
|
21
|
+
prev = nil
|
22
|
+
else
|
23
|
+
prev = el
|
24
|
+
yield el
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
data/lib/ramekin/meta.rb
CHANGED
data/lib/ramekin/renderer.rb
CHANGED
@@ -167,7 +167,14 @@ module Ramekin
|
|
167
167
|
def render_token(token, &b)
|
168
168
|
case token.type
|
169
169
|
when :t, :bpm
|
170
|
-
|
170
|
+
tempo = tempo_of(token)
|
171
|
+
fade = fade_duration(token)
|
172
|
+
|
173
|
+
if fade == 0
|
174
|
+
yield "t#{tempo_of(token)}"
|
175
|
+
else
|
176
|
+
yield hex(0xE3, fade, tempo)
|
177
|
+
end
|
171
178
|
when :directive
|
172
179
|
render_directive(token, &b)
|
173
180
|
when :channel
|
@@ -179,7 +186,7 @@ module Ramekin
|
|
179
186
|
# reset tick counter for newlines
|
180
187
|
@tick = 0
|
181
188
|
when :transpose
|
182
|
-
yield
|
189
|
+
yield hex(0xFA, 0x02, token.value.to_i & 0x7f)
|
183
190
|
when :instrument
|
184
191
|
case token.value
|
185
192
|
when /\A\d+\z/ then yield "@#{token.value}"
|
@@ -192,18 +199,16 @@ module Ramekin
|
|
192
199
|
yield "@#{@instrument_index[token.value]}"
|
193
200
|
end
|
194
201
|
when :v
|
195
|
-
vol
|
202
|
+
vol = token.value.to_i
|
196
203
|
|
197
204
|
unless Token === peek && [:v, :relv].include?(peek.type)
|
198
|
-
yield velocity_command(vol,
|
205
|
+
yield velocity_command(vol, fade_duration(token))
|
199
206
|
end
|
200
207
|
|
201
208
|
@volume = vol
|
202
209
|
when :relv
|
203
|
-
relvol, duration = token.values
|
204
|
-
|
205
210
|
unless Token === peek && [:v, :relv].include?(peek.type)
|
206
|
-
yield velocity_command(@volume +
|
211
|
+
yield velocity_command(@volume + token.value.to_i, fade_duration(token))
|
207
212
|
end
|
208
213
|
when :q
|
209
214
|
token.value =~ /(\d),?(\h)?/
|
@@ -211,40 +216,34 @@ module Ramekin
|
|
211
216
|
gain = $2 || 'F'
|
212
217
|
yield "q#{qval}#{gain}"
|
213
218
|
when :w
|
214
|
-
|
215
|
-
when :adsr
|
216
|
-
vals = token.value.split(',').map { |x| x.to_i(16) }
|
217
|
-
error! 'invalid #adsr, expected 4 arguments' unless vals.size == 4
|
218
|
-
|
219
|
-
a, d, s, r = vals
|
220
|
-
error! 'invalid attack (must be 0-F)' unless (0..15).include?(a)
|
221
|
-
error! 'invalid decay (must be 0-7)' unless (0..7).include?(d)
|
222
|
-
error! 'invalid sustain (must be 0-7)' unless (0..7).include?(s)
|
223
|
-
error! 'invalid release (must be 0-1F)' unless (0..31).include?(r)
|
224
|
-
|
225
|
-
yield sprintf("$ed$%02x$%02x", (7-d) * 16 + (15-a), s * 32 + (31-r))
|
226
|
-
when :y
|
227
|
-
yield "y#{token.value}"
|
228
|
-
when :rely
|
229
|
-
token.value =~ /\A([LRC])(\d*)\z/
|
230
|
-
pan = case $1
|
231
|
-
when 'L' then 10 + $2.to_i
|
232
|
-
when 'R' then 10 - $2.to_i
|
233
|
-
when 'C' then 10
|
234
|
-
end
|
219
|
+
fade = fade_duration(token)
|
235
220
|
|
236
|
-
if
|
237
|
-
|
238
|
-
|
239
|
-
|
221
|
+
if fade.to_i == 0
|
222
|
+
yield "w#{token.value}"
|
223
|
+
else
|
224
|
+
yield hex(0xE1, fade, token.value.to_i)
|
225
|
+
end
|
226
|
+
when :adsr
|
227
|
+
a, d, s, r = Util.adsr_value(token)
|
228
|
+
|
229
|
+
yield hex(0xED, (7-d) * 16 + (15-a), s * 32 + (31-r))
|
230
|
+
when :y, :rely
|
231
|
+
pan_number = case token.type
|
232
|
+
when :y then token.value.to_i
|
233
|
+
when :rely
|
234
|
+
token.value =~ /\A([LRC])(\d*)\z/
|
235
|
+
pan = case $1
|
236
|
+
when 'L' then 10 + $2.to_i
|
237
|
+
when 'R' then 10 - $2.to_i
|
238
|
+
when 'C' then 10
|
239
|
+
end
|
240
240
|
end
|
241
241
|
|
242
|
-
yield
|
243
|
-
|
242
|
+
yield pan_command(pan_number, fade_duration(token))
|
244
243
|
when :p
|
245
244
|
# it's free aram
|
246
245
|
if token.value == '0,0'
|
247
|
-
yield
|
246
|
+
yield hex(0xDF)
|
248
247
|
else
|
249
248
|
yield "p#{token.value} "
|
250
249
|
end
|
@@ -266,9 +265,6 @@ module Ramekin
|
|
266
265
|
yield "*#{token.value}"
|
267
266
|
when :superloop_end
|
268
267
|
yield " ]]#{token.value} "
|
269
|
-
when :amp
|
270
|
-
yield '&'
|
271
|
-
|
272
268
|
# triplets are handled in NoteAggregator
|
273
269
|
when :lbrace, :rbrace
|
274
270
|
# pass
|
@@ -277,18 +273,39 @@ module Ramekin
|
|
277
273
|
end
|
278
274
|
end
|
279
275
|
|
280
|
-
def
|
276
|
+
def pan_command(pan, time=0)
|
277
|
+
error! 'invalid pan: must be R10 to L10 (0 to 20)' unless (0..20).include?(pan)
|
278
|
+
|
279
|
+
if time.to_i == 0
|
280
|
+
"y#{pan}"
|
281
|
+
else
|
282
|
+
hex(0xDC, time, pan)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def velocity_command(vol, time=0)
|
281
287
|
vol = 255 if vol > 255
|
282
288
|
vol = 0 if vol < 0
|
283
289
|
|
284
|
-
if time.
|
290
|
+
if time.to_i == 0
|
285
291
|
"v#{vol}"
|
286
292
|
else
|
287
|
-
time = time.to_i
|
293
|
+
time = time.to_i
|
288
294
|
time = 255 if time > 255
|
289
295
|
time = 0 if time < 0
|
290
|
-
|
296
|
+
hex(0xE8, time, vol)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def fade_duration(tok)
|
301
|
+
return 0 unless tok.meta[:fade]
|
302
|
+
out = tok.meta[:fade].ticks / @track.meta.divisor
|
303
|
+
|
304
|
+
if out > 0xFF
|
305
|
+
error! "fade too long, cannot exceed 255 ticks (was #{out} ticks)"
|
291
306
|
end
|
307
|
+
|
308
|
+
out
|
292
309
|
end
|
293
310
|
|
294
311
|
def render_directive(token)
|
@@ -296,16 +313,20 @@ module Ramekin
|
|
296
313
|
when 'SPC'
|
297
314
|
# pass
|
298
315
|
when 'legato', 'legato/toggle'
|
299
|
-
yield
|
316
|
+
yield hex(0xF4, 0x01)
|
300
317
|
when 'sustain/global-toggle'
|
301
|
-
yield
|
318
|
+
yield hex(0xF4, 0x02)
|
302
319
|
when 'echo/toggle'
|
303
|
-
yield
|
320
|
+
yield hex(0xF4, 0x03)
|
304
321
|
else
|
305
322
|
error! "unexpected directive ##{token.value}"
|
306
323
|
end
|
307
324
|
end
|
308
325
|
|
326
|
+
def hex(*args)
|
327
|
+
args.map { |a| sprintf("$%02x", a) }.join
|
328
|
+
end
|
329
|
+
|
309
330
|
def render_string
|
310
331
|
out = StringIO.new
|
311
332
|
render { |chunk| out << chunk }
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Ramekin
|
2
|
+
class Scanner
|
3
|
+
attr_reader :match
|
4
|
+
attr_accessor :pos
|
5
|
+
def initialize(str)
|
6
|
+
@str = str
|
7
|
+
@cache = {}
|
8
|
+
@pos = 0
|
9
|
+
@match = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def inspect
|
13
|
+
prev = @str[@pos-5...@pos]
|
14
|
+
prev = "...#{prev}" if @pos > 5
|
15
|
+
prev = "#{prev} " unless prev.empty?
|
16
|
+
|
17
|
+
post = @str[@pos+1...@pos+5]
|
18
|
+
post = "#{post}..." unless @pos+5 >= @str.length
|
19
|
+
post = " #{post}" unless post.empty?
|
20
|
+
|
21
|
+
"Scanner([#{@pos}] #{prev}>#{@str[@pos]}<#{post})"
|
22
|
+
end
|
23
|
+
|
24
|
+
def scan(re)
|
25
|
+
anchored = anchored_re(re)
|
26
|
+
|
27
|
+
@match = anchored.match(@str, @pos)
|
28
|
+
@pos = @match.end(0) if @match
|
29
|
+
@match
|
30
|
+
end
|
31
|
+
|
32
|
+
def peek(n=1)
|
33
|
+
@str[@pos...@pos+n]
|
34
|
+
end
|
35
|
+
|
36
|
+
def eos?
|
37
|
+
@pos >= @str.size
|
38
|
+
end
|
39
|
+
|
40
|
+
def [](k)
|
41
|
+
@match && @match[k]
|
42
|
+
end
|
43
|
+
|
44
|
+
def captures
|
45
|
+
@match && @match.captures
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def anchored_re(re)
|
50
|
+
@cache[re] ||= begin
|
51
|
+
raise 'bad regex, do not nest quantifiers' unless Regexp.linear_time?(re)
|
52
|
+
Regexp.new("\\G(?:#{re.source})", re.options)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/ramekin/tokenizer.rb
CHANGED
@@ -13,6 +13,14 @@ module Ramekin
|
|
13
13
|
"#{@line}:#{@col}"
|
14
14
|
end
|
15
15
|
|
16
|
+
def repr
|
17
|
+
"#{linecol}@#{@pos}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
"Location(#{repr})"
|
22
|
+
end
|
23
|
+
|
16
24
|
def start
|
17
25
|
self
|
18
26
|
end
|
@@ -91,7 +99,7 @@ module Ramekin
|
|
91
99
|
end
|
92
100
|
|
93
101
|
def initialize(str)
|
94
|
-
@scanner =
|
102
|
+
@scanner = Scanner.new(str)
|
95
103
|
@macros = []
|
96
104
|
@last_match = nil
|
97
105
|
@last_captures = []
|
@@ -118,7 +126,7 @@ module Ramekin
|
|
118
126
|
matched = @scanner.scan(regexp)
|
119
127
|
return false unless matched
|
120
128
|
|
121
|
-
update_linecol(matched)
|
129
|
+
update_linecol(matched.match(0))
|
122
130
|
true
|
123
131
|
end
|
124
132
|
|
@@ -207,7 +215,7 @@ module Ramekin
|
|
207
215
|
return [:ifndef, m(1)] if match /#ifndef\s*(.*?)\n/
|
208
216
|
return [:endif, m(1)] if match /#endif/
|
209
217
|
|
210
|
-
return [:adsr, m(1)] if match /#adsr:(\
|
218
|
+
return [:adsr, m(1)] if match /#adsr:([\w,]+)/
|
211
219
|
return [:gain, m(1)] if match /#gain:(\h+)/
|
212
220
|
return [:tuning, m(1)] if match /#tuning:(\h\h\h\h)/
|
213
221
|
return [:bpm, m(1)] if match /#bpm:(\d+)/
|
@@ -222,8 +230,8 @@ module Ramekin
|
|
222
230
|
return [:directive, m(1)] if match /#([\w\/-]+)/
|
223
231
|
return [:lbrace] if match /[{]/
|
224
232
|
return [:rbrace] if match /[}]/
|
225
|
-
return [:v, m(1), m(2)] if match /v(\d+)
|
226
|
-
return [:relv, m(1), m(2)] if match /v([+-]\d+)
|
233
|
+
return [:v, m(1), m(2)] if match /v(\d+)/
|
234
|
+
return [:relv, m(1), m(2)] if match /v([+-]\d+)/
|
227
235
|
return [:instrument, m(1)] if match /@(\d+)/
|
228
236
|
return [:instrument, m(1)] if match /@(\w+)/
|
229
237
|
return [:hex, m(1)] if match /[$](\h\h)/
|
@@ -233,6 +241,7 @@ module Ramekin
|
|
233
241
|
return note(:r) if match /r/
|
234
242
|
return note(:native_tie, m(1)) if match /\^\^/
|
235
243
|
return note(:tie, m(1)) if match /\^/
|
244
|
+
return note(:fade) if match /\\/
|
236
245
|
return [:slash] if match /\//
|
237
246
|
|
238
247
|
return [:octave, m] if match /[<>]/
|
@@ -251,8 +260,8 @@ module Ramekin
|
|
251
260
|
return remote(:remote_call, m(1)) if match /[(]!(\d*)/
|
252
261
|
|
253
262
|
return [:star, m(1)] if match /\*(\d*)/
|
254
|
-
return [:y, m(1)] if match /y(\d+)
|
255
|
-
return [:rely, m(1)] if match /y([LR]\d+)
|
263
|
+
return [:y, m(1)] if match /y(\d+)(?:,(\d+))?/
|
264
|
+
return [:rely, m(1)] if match /y([LR]\d+)(?:,(\d+))?/
|
256
265
|
return [:rely, 'C'] if match /yC/
|
257
266
|
return [:w, m(1)] if match /w(\d+)/
|
258
267
|
return [:l, m(1)] if match /l(\d+)/
|
data/lib/ramekin/util.rb
CHANGED
@@ -2,6 +2,24 @@ require 'zip'
|
|
2
2
|
|
3
3
|
module Ramekin
|
4
4
|
module Util
|
5
|
+
include Error::Helpers
|
6
|
+
def adsr_value(adsr)
|
7
|
+
case adsr.value
|
8
|
+
when 'flat'
|
9
|
+
[0, 7, 7, 31]
|
10
|
+
else
|
11
|
+
vals = adsr.value.split(',').map { |x| x.to_i(16) }
|
12
|
+
error! 'invalid #adsr, expected 4 arguments', el: adsr unless vals.size == 4
|
13
|
+
|
14
|
+
a, d, s, r = vals
|
15
|
+
error! 'invalid attack (must be 0-F)', el: adsr unless (0..15).include?(a)
|
16
|
+
error! 'invalid decay (must be 0-7)', el: adsr unless (0..7).include?(d)
|
17
|
+
error! 'invalid sustain (must be 0-7)', el: adsr unless (0..7).include?(s)
|
18
|
+
error! 'invalid release (must be 0-1F)', el: adsr unless (0..31).include?(r)
|
19
|
+
[a, d, s, r]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
5
23
|
extend self
|
6
24
|
|
7
25
|
KNOWN_LENGTHS = {}.tap do |out|
|
data/lib/ramekin.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require_relative 'ramekin/errors'
|
4
|
+
require_relative 'ramekin/scanner'
|
3
5
|
require_relative 'ramekin/util'
|
4
6
|
require_relative 'ramekin/smw'
|
5
7
|
require_relative 'ramekin/config'
|
6
8
|
require_relative 'ramekin/amk_setup'
|
7
9
|
require_relative 'ramekin/spc_player'
|
8
|
-
require_relative 'ramekin/errors'
|
9
10
|
require_relative 'ramekin/element'
|
10
11
|
require_relative 'ramekin/tokenizer'
|
11
12
|
require_relative 'ramekin/processor'
|
12
13
|
require_relative 'ramekin/macros'
|
13
14
|
require_relative 'ramekin/note_aggregator'
|
15
|
+
require_relative 'ramekin/fades'
|
14
16
|
require_relative 'ramekin/bends'
|
15
17
|
require_relative 'ramekin/legato'
|
16
18
|
require_relative 'ramekin/loop_allocator'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ramekin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jneen
|
8
8
|
bindir: gembin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-06-01 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: strscan
|
@@ -83,6 +83,7 @@ files:
|
|
83
83
|
- lib/ramekin/config.rb
|
84
84
|
- lib/ramekin/element.rb
|
85
85
|
- lib/ramekin/errors.rb
|
86
|
+
- lib/ramekin/fades.rb
|
86
87
|
- lib/ramekin/legato.rb
|
87
88
|
- lib/ramekin/loop_allocator.rb
|
88
89
|
- lib/ramekin/macros.rb
|
@@ -91,6 +92,7 @@ files:
|
|
91
92
|
- lib/ramekin/processor.rb
|
92
93
|
- lib/ramekin/renderer.rb
|
93
94
|
- lib/ramekin/sample_pack.rb
|
95
|
+
- lib/ramekin/scanner.rb
|
94
96
|
- lib/ramekin/smw.rb
|
95
97
|
- lib/ramekin/spc_player.rb
|
96
98
|
- lib/ramekin/tokenizer.rb
|