ramekin 0.0.6 → 0.0.8
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 +20 -1
- data/lib/ramekin/cli.rb +11 -3
- data/lib/ramekin/errors.rb +2 -2
- data/lib/ramekin/meta.rb +103 -8
- data/lib/ramekin/renderer.rb +32 -14
- data/lib/ramekin/sample_pack.rb +4 -0
- data/lib/ramekin/spc_player.rb +5 -5
- data/lib/ramekin/tokenizer.rb +6 -4
- data/lib/ramekin/util.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 463b021dd0273151c13a7590db845acf584a2df021596dd715721964e777f6d9
|
4
|
+
data.tar.gz: 432c1eccc9c5443cc63ec828f87a319141caeb3080c847948634b7935553ec86
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fbe078d3e43b620748e3bcd91360d06e1bf2b9b804821d21a6479479fe519abb7d48643a8a95f45abfcfe512b2654b1a1f6424773282d294503799e2ee31175b
|
7
|
+
data.tar.gz: bd829fcd1272ac8279757c73963e407b90ef9257d104475aa07a5c52cf3ed16ce691d25ade84a3333fa2c243854948b92a7fcbfa90b47ee2fedad9c845b54c33
|
data/README.md
CHANGED
@@ -42,7 +42,8 @@ ramekin compile -i filename.rmk [flags]
|
|
42
42
|
-i filename.rmk
|
43
43
|
set the input file
|
44
44
|
--txt filename.txt
|
45
|
-
output amk txt to this file
|
45
|
+
output amk txt to this file.
|
46
|
+
use a single dash (-) to print txt to stdout.
|
46
47
|
--spc filename.spc
|
47
48
|
attempt to invoke AddmusicK to output an SPC
|
48
49
|
requires ADDMUSICK_DIR and ASAR_COMMAND
|
@@ -209,6 +210,24 @@ After the instrument declaration, you can use several commands to change the def
|
|
209
210
|
|
210
211
|
It is generally advised to stick with the default tuning. The `o5` declaration sets the *default octave* for the instrument to octave 5 (the default is 4). Whenever this instrument is selected, Ramekin will insert a switch to the specified octave.
|
211
212
|
|
213
|
+
### Echo settings
|
214
|
+
|
215
|
+
Echo settings can be set up with the `#echo/...` family of directives:
|
216
|
+
|
217
|
+
```elisp
|
218
|
+
#echo/channels:0,1,2,3
|
219
|
+
; or
|
220
|
+
#echo/channels:all
|
221
|
+
; or
|
222
|
+
#echo/channels:none
|
223
|
+
|
224
|
+
#echo/volume:20 ; range from -7f to 80, use negatives for surround
|
225
|
+
#echo/volume:20,30 ; set different values for left vs right echo
|
226
|
+
#echo/feedback:48 ; range from -7f to 80, use negatives for surround
|
227
|
+
#echo/fir:1 ; 1 or 0 to enable/disable the FIR filter
|
228
|
+
```
|
229
|
+
|
230
|
+
|
212
231
|
## Channel Commands
|
213
232
|
|
214
233
|
With your metadata all set up, it's time to add some notes!
|
data/lib/ramekin/cli.rb
CHANGED
@@ -29,7 +29,8 @@ module Ramekin
|
|
29
29
|
$stderr.puts " -i filename.rmk"
|
30
30
|
$stderr.puts " set the input file"
|
31
31
|
$stderr.puts " --txt filename.txt"
|
32
|
-
$stderr.puts " output amk txt to this file"
|
32
|
+
$stderr.puts " output amk txt to this file."
|
33
|
+
$stderr.puts " use a single dash (-) to print txt to stdout."
|
33
34
|
$stderr.puts " --spc filename.spc"
|
34
35
|
$stderr.puts " attempt to invoke AddmusicK to output an SPC"
|
35
36
|
$stderr.puts " requires ADDMUSICK_DIR and ASAR_COMMAND"
|
@@ -110,6 +111,11 @@ module Ramekin
|
|
110
111
|
when '--package'
|
111
112
|
@output_package = argv.shift or usage! 'missing dirname after --package'
|
112
113
|
when '--play'
|
114
|
+
if argv.any? && argv[0] =~ /\A\d+\z/
|
115
|
+
@play_offset = argv.shift
|
116
|
+
else
|
117
|
+
@play_offset = 0
|
118
|
+
end
|
113
119
|
@play = true
|
114
120
|
when '--wav'
|
115
121
|
arg = argv.shift or usage! 'missing wav file after --render'
|
@@ -199,7 +205,9 @@ module Ramekin
|
|
199
205
|
WARN
|
200
206
|
end
|
201
207
|
|
202
|
-
if @output_txt
|
208
|
+
if @output_txt == '-'
|
209
|
+
$stdout.puts(txt)
|
210
|
+
elsif @output_txt
|
203
211
|
File.write(@output_txt, txt)
|
204
212
|
end
|
205
213
|
|
@@ -229,7 +237,7 @@ module Ramekin
|
|
229
237
|
|
230
238
|
if @play
|
231
239
|
SPCPlayer.instance.setup!
|
232
|
-
SPCPlayer.instance.play(@play_spc)
|
240
|
+
SPCPlayer.instance.play(@play_spc, @play_offset)
|
233
241
|
end
|
234
242
|
|
235
243
|
if @verbose
|
data/lib/ramekin/errors.rb
CHANGED
@@ -15,7 +15,7 @@ module Ramekin
|
|
15
15
|
def self.error!(e, nesting: 0)
|
16
16
|
all << e
|
17
17
|
|
18
|
-
if ENV['RAMEKIN_DEBUG']
|
18
|
+
if ENV['RAMEKIN_DEBUG'] == '1'
|
19
19
|
if nesting >= 0
|
20
20
|
Pry.config.hooks.add_hook(:before_session, :ramekin) do |output, binding, pry|
|
21
21
|
Pry.config.hooks.delete_hook(:before_session, :ramekin)
|
@@ -35,7 +35,7 @@ module Ramekin
|
|
35
35
|
|
36
36
|
def present(orig)
|
37
37
|
out = []
|
38
|
-
out << "line #{@range.start.line}, col #{@range.start.col}:"
|
38
|
+
out << "line #{@range.start.line+1}, col #{@range.start.col+1}:"
|
39
39
|
out << " ---"
|
40
40
|
out << " #{orig[@range.pos_range]}"
|
41
41
|
out << " ---"
|
data/lib/ramekin/meta.rb
CHANGED
@@ -21,6 +21,33 @@ module Ramekin
|
|
21
21
|
@options = {}
|
22
22
|
end
|
23
23
|
|
24
|
+
def echo_channel_bits
|
25
|
+
(@echo_channels ||= Set.new).map { |x| 1 << x }.inject(0, &:|)
|
26
|
+
end
|
27
|
+
|
28
|
+
def echo_volumes
|
29
|
+
[(@echo_volume_l || 0) & 0xFF, (@echo_volume_r || 0) & 0xFF]
|
30
|
+
end
|
31
|
+
|
32
|
+
def echo_delay
|
33
|
+
@echo_delay || 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def echo_feedback
|
37
|
+
(@echo_feedback || 0) & 0xFF
|
38
|
+
end
|
39
|
+
|
40
|
+
def echo_fir
|
41
|
+
@echo_fir || 0
|
42
|
+
end
|
43
|
+
|
44
|
+
def echo_hex
|
45
|
+
sprintf("$ef$%02x$%02x$%02x $f1$%02x$%02x$%02x",
|
46
|
+
echo_channel_bits, *echo_volumes,
|
47
|
+
echo_delay, echo_feedback, echo_fir
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
24
51
|
def parse
|
25
52
|
loop do
|
26
53
|
break if @elements.empty?
|
@@ -36,10 +63,10 @@ module Ramekin
|
|
36
63
|
error! 'invalid token in header'
|
37
64
|
end
|
38
65
|
end
|
39
|
-
|
40
|
-
@sample_groups << 'default' if @sample_groups.empty?
|
41
66
|
end
|
42
67
|
|
68
|
+
SURROUND_R = (-0x7f..0x80)
|
69
|
+
|
43
70
|
def parse_directive
|
44
71
|
@current_directive = @current
|
45
72
|
case @current.value
|
@@ -55,16 +82,78 @@ module Ramekin
|
|
55
82
|
when 'instrument'
|
56
83
|
name, path = expect_args(:instrument, :string)
|
57
84
|
extensions = []
|
58
|
-
while (el = check_arg(:adsr, :tuning, :o))
|
85
|
+
while (el = check_arg(:adsr, :tuning, :gain, :o))
|
59
86
|
extensions << el
|
60
87
|
end
|
61
88
|
|
62
89
|
return unless @current_pack
|
63
|
-
@instruments << Instrument.new(@current_pack, @
|
90
|
+
@instruments << Instrument.new(@current_pack, @current_directive, name, path, extensions)
|
64
91
|
when 'default', 'optimized' then @sample_groups << @current
|
65
92
|
|
66
93
|
# TODO: real echo syntax
|
67
94
|
when 'echo' then @echo = expect_args(*([:hex] * 8))
|
95
|
+
when 'echo/channels'
|
96
|
+
channels = @current_directive.values[1]
|
97
|
+
case channels
|
98
|
+
when 'all'
|
99
|
+
@echo_channels = Set.new(0..7)
|
100
|
+
return
|
101
|
+
when 'none'
|
102
|
+
@echo_channels = Set.new
|
103
|
+
return
|
104
|
+
end
|
105
|
+
|
106
|
+
unless channels && channels =~ /\A\d(,\d)+\z/
|
107
|
+
error! 'invalid echo channels (list of single digit numbers separated by comma)'
|
108
|
+
end
|
109
|
+
|
110
|
+
@echo_channels = Set.new(channels.split(',').map(&:to_i))
|
111
|
+
unless @echo_channels.all? { |c| (0..7).include?(c) }
|
112
|
+
error! 'invalid echo channels (must be 0-7)'
|
113
|
+
end
|
114
|
+
when 'echo/volume'
|
115
|
+
vols = @current_directive.values[1]
|
116
|
+
|
117
|
+
unless vols && vols =~ /\A-?\h\h?(,\h\h?)?/
|
118
|
+
error! 'invalid echo volume (one or two hex numbers)'
|
119
|
+
end
|
120
|
+
|
121
|
+
vols = vols.split(',').map { |v| v.to_i(16) }
|
122
|
+
if vols.size == 1
|
123
|
+
@echo_volume_l = @echo_volume_r = vols[0]
|
124
|
+
else
|
125
|
+
@echo_volume_l, @echo_volume_r = vols
|
126
|
+
end
|
127
|
+
|
128
|
+
unless SURROUND_R.include?(@echo_volume_l) && SURROUND_R.include?(@echo_volume_r)
|
129
|
+
error! 'invalid echo volume (-7f to 80, use negative for surround)'
|
130
|
+
end
|
131
|
+
|
132
|
+
when 'echo/delay'
|
133
|
+
delay = @current_directive.values[1]
|
134
|
+
unless delay && delay =~ /\A[0-7]\z/
|
135
|
+
error! 'invalid echo delay (single digit 0-7)'
|
136
|
+
end
|
137
|
+
|
138
|
+
@echo_delay = delay.to_i
|
139
|
+
when 'echo/feedback'
|
140
|
+
feedback = @current_directive.values[1]
|
141
|
+
unless feedback && feedback =~ /\A-?\h\h?\z/
|
142
|
+
error! 'invalid echo feedback (one or two hex digits)'
|
143
|
+
end
|
144
|
+
|
145
|
+
@echo_feedback = feedback.to_i(16)
|
146
|
+
|
147
|
+
unless SURROUND_R.include?(@echo_feedback)
|
148
|
+
error! 'invalid echo feedback (-7F to 80, use negative for surround)'
|
149
|
+
end
|
150
|
+
when 'echo/fir'
|
151
|
+
fir = @current_directive.values[1]
|
152
|
+
unless fir && fir =~ /\A\h\h?\z/
|
153
|
+
error! 'invalid echo FIR setting (one or two hex digits)'
|
154
|
+
end
|
155
|
+
|
156
|
+
@echo_fir = fir.to_i(16)
|
68
157
|
else
|
69
158
|
error! 'invalid directive in header'
|
70
159
|
end
|
@@ -163,12 +252,12 @@ module Ramekin
|
|
163
252
|
|
164
253
|
def adsr
|
165
254
|
@adsr ||= ext_adsr || pack_adsr \
|
166
|
-
or error! "no adsr configured for #{name}"
|
255
|
+
or error! "no adsr configured for #{name.value}", el: name
|
167
256
|
end
|
168
257
|
|
169
258
|
def tuning
|
170
259
|
@tuning ||= ext_tuning || pack_tuning \
|
171
|
-
or error! "no tuning configured for #{name}"
|
260
|
+
or error! "no tuning configured for #{name.value}", el: name
|
172
261
|
end
|
173
262
|
|
174
263
|
def pack_tuning
|
@@ -197,8 +286,7 @@ module Ramekin
|
|
197
286
|
end
|
198
287
|
|
199
288
|
def gain
|
200
|
-
@gain ||= ext_gain || pack_gain
|
201
|
-
or error! "no gain configured for #{name}"
|
289
|
+
@gain ||= ext_gain || pack_gain || 0xFF
|
202
290
|
end
|
203
291
|
|
204
292
|
def hexes
|
@@ -206,6 +294,8 @@ module Ramekin
|
|
206
294
|
t1, t2 = self.tuning
|
207
295
|
g = self.gain
|
208
296
|
|
297
|
+
return [] unless a && d && s && r && t1 && t2 && g
|
298
|
+
|
209
299
|
adsr1 = ((7 - d)*16 | 0x80) + (15 - a)
|
210
300
|
adsr2 = (s*32 + (31-r))
|
211
301
|
|
@@ -213,6 +303,11 @@ module Ramekin
|
|
213
303
|
end
|
214
304
|
|
215
305
|
def to_amk
|
306
|
+
unless @pack.has?(@path.value)
|
307
|
+
error! "no sample named #{path.value.inspect} in pack #{pack.name.inspect}", el: @path
|
308
|
+
return ''
|
309
|
+
end
|
310
|
+
|
216
311
|
"#{File.basename(sample_name).inspect} #{hexes.map { |h| "$#{h}" }.join(' ')}"
|
217
312
|
end
|
218
313
|
|
data/lib/ramekin/renderer.rb
CHANGED
@@ -74,13 +74,17 @@ module Ramekin
|
|
74
74
|
yield "; https://codeberg.org/jneen/ramekin\n\n"
|
75
75
|
|
76
76
|
# TODO
|
77
|
-
yield "#amk 2\n\n"
|
77
|
+
yield "#amk #{@track.meta.amk&.value || 2}\n\n"
|
78
78
|
|
79
79
|
if m.instruments.any?
|
80
80
|
yield "#path #{@filename.chomp('.rmk').inspect}\n"
|
81
81
|
yield "#samples {\n"
|
82
|
-
m.sample_groups.
|
83
|
-
yield "
|
82
|
+
if m.sample_groups.empty?
|
83
|
+
yield " #default"
|
84
|
+
else
|
85
|
+
m.sample_groups.each do |group|
|
86
|
+
yield " ##{group.value}\n"
|
87
|
+
end
|
84
88
|
end
|
85
89
|
|
86
90
|
m.instruments.map(&:sample_name).sort.uniq.each do |sample|
|
@@ -99,7 +103,10 @@ module Ramekin
|
|
99
103
|
|
100
104
|
yield "t#{tempo_of(m.tempo)} ; main tempo (0-60)\n" if m.tempo
|
101
105
|
yield "w#{m.volume.value} ; main volume (0-255)\n" if m.volume
|
102
|
-
yield "l16"
|
106
|
+
yield "l16\n\n"
|
107
|
+
|
108
|
+
yield "; echo settings\n"
|
109
|
+
yield m.echo_hex
|
103
110
|
# binding.pry
|
104
111
|
end
|
105
112
|
|
@@ -167,13 +174,7 @@ module Ramekin
|
|
167
174
|
# reset tick counter for newlines
|
168
175
|
@tick = 0
|
169
176
|
when :transpose
|
170
|
-
|
171
|
-
|
172
|
-
if interval < 0
|
173
|
-
interval = 0x80 - interval
|
174
|
-
end
|
175
|
-
|
176
|
-
yield sprintf("$fa$02$%02x", token.value.to_i)
|
177
|
+
yield sprintf("$fa$02$%02x", token.value.to_i & 0x7f)
|
177
178
|
when :instrument
|
178
179
|
case token.value
|
179
180
|
when /\A\d+\z/ then yield "@#{token.value}"
|
@@ -186,11 +187,12 @@ module Ramekin
|
|
186
187
|
yield "@#{@instrument_index[token.value]}"
|
187
188
|
end
|
188
189
|
when :v
|
189
|
-
|
190
|
-
|
190
|
+
vol, time = token.values.compact.map(&:to_i)
|
191
|
+
yield velocity_command(vol, time)
|
192
|
+
@volume = vol
|
191
193
|
when :relv
|
192
194
|
relvol, duration = token.values
|
193
|
-
yield
|
195
|
+
yield velocity_command(@volume + relvol.to_i, duration)
|
194
196
|
when :adsr
|
195
197
|
vals = token.value.split(',').map { |x| x.to_i(16) }
|
196
198
|
error! 'invalid #adsr, expected 4 arguments' unless vals.size == 4
|
@@ -251,6 +253,20 @@ module Ramekin
|
|
251
253
|
end
|
252
254
|
end
|
253
255
|
|
256
|
+
def velocity_command(vol, time=nil)
|
257
|
+
vol = 255 if vol > 255
|
258
|
+
vol = 0 if vol < 0
|
259
|
+
|
260
|
+
if time.nil?
|
261
|
+
"v#{vol}"
|
262
|
+
else
|
263
|
+
time = time.to_i
|
264
|
+
time = 255 if time > 255
|
265
|
+
time = 0 if time < 0
|
266
|
+
sprintf("$E8$%02x$%02x", time, vol)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
254
270
|
def render_directive(token)
|
255
271
|
case token.value
|
256
272
|
when 'SPC'
|
@@ -274,6 +290,8 @@ module Ramekin
|
|
274
290
|
@octave = note.octave
|
275
291
|
when 'legato'
|
276
292
|
yield '$f4$01'
|
293
|
+
when 'echo/toggle'
|
294
|
+
yield '$f4$03'
|
277
295
|
else
|
278
296
|
error! "unexpected directive ##{token.value}"
|
279
297
|
end
|
data/lib/ramekin/sample_pack.rb
CHANGED
@@ -105,6 +105,10 @@ module Ramekin
|
|
105
105
|
File.join(prefix_dir, "#{path}.brr")
|
106
106
|
end
|
107
107
|
|
108
|
+
def has?(path)
|
109
|
+
File.exist?(find(path))
|
110
|
+
end
|
111
|
+
|
108
112
|
def tunings_for(path)
|
109
113
|
rel = Pathname.new(find(path)).relative_path_from(dir).to_s
|
110
114
|
(tunings[rel] || []).map(&:last)
|
data/lib/ramekin/spc_player.rb
CHANGED
@@ -37,7 +37,7 @@ module Ramekin
|
|
37
37
|
File.exist?("#@spcplay_dir/spcplay.exe")
|
38
38
|
end
|
39
39
|
|
40
|
-
def play(fname)
|
40
|
+
def play(fname, offset_=0)
|
41
41
|
fname = File.expand_path(fname)
|
42
42
|
Dir.chdir(@spcplay_dir) do
|
43
43
|
sys "spcplay.exe", fname
|
@@ -61,8 +61,8 @@ module Ramekin
|
|
61
61
|
end
|
62
62
|
|
63
63
|
class NormalSPCPlayer < SPCPlayer
|
64
|
-
def play(fname)
|
65
|
-
sys "#@spct_dir/spct", 'play', fname
|
64
|
+
def play(fname, offset=0)
|
65
|
+
sys "#@spct_dir/spct", 'play', fname, '--seek', offset
|
66
66
|
end
|
67
67
|
|
68
68
|
def render(fname, outfile, seconds=nil)
|
@@ -110,9 +110,9 @@ module Ramekin
|
|
110
110
|
sys(*args)
|
111
111
|
end
|
112
112
|
|
113
|
-
def play(fname)
|
113
|
+
def play(fname, offset=0)
|
114
114
|
if File.basename(@path, '.exe') == 'spct'
|
115
|
-
sys @path, 'play', fname
|
115
|
+
sys @path, 'play', fname, '--seek', offset
|
116
116
|
else
|
117
117
|
sys @path, fname
|
118
118
|
end
|
data/lib/ramekin/tokenizer.rb
CHANGED
@@ -135,9 +135,9 @@ module Ramekin
|
|
135
135
|
newlines = matched.count("\n")
|
136
136
|
@line += newlines
|
137
137
|
if newlines > 0
|
138
|
-
@col = find_colno(matched
|
138
|
+
@col = find_colno(matched)
|
139
139
|
else
|
140
|
-
@col += find_colno(matched
|
140
|
+
@col += find_colno(matched)
|
141
141
|
end
|
142
142
|
end
|
143
143
|
|
@@ -208,6 +208,7 @@ module Ramekin
|
|
208
208
|
return [:endif, m(1)] if match /#endif/
|
209
209
|
|
210
210
|
return [:adsr, m(1)] if match /#adsr:(\h+(?:,\h+)*)/
|
211
|
+
return [:gain, m(1)] if match /#gain:(\h+)/
|
211
212
|
return [:tuning, m(1)] if match /#tuning:(\h\h\h\h)/
|
212
213
|
return [:bpm, m(1)] if match /#bpm:(\d+)/
|
213
214
|
return [:legato_tie] if match /~/
|
@@ -217,7 +218,8 @@ module Ramekin
|
|
217
218
|
return [:amk, m(1)] if match /#amk\s*(\d+)/
|
218
219
|
return [:option, m(1)] if match /#option (\w+)/
|
219
220
|
return [:channel, m(1)] if match /#(\d+)/
|
220
|
-
return [:directive, m(1)] if match /#([\w
|
221
|
+
return [:directive, m(1), m(2)] if match /#([\w\/-]+):(\S+)/
|
222
|
+
return [:directive, m(1)] if match /#([\w\/-]+)/
|
221
223
|
return [:lbrace] if match /[{]/
|
222
224
|
return [:rbrace] if match /[}]/
|
223
225
|
return [:v, m(1), m(2)] if match /v(\d+)(?:,(\d+))?/
|
@@ -258,7 +260,7 @@ module Ramekin
|
|
258
260
|
return [:q, m(1)] if match /q(\h\h)/
|
259
261
|
return [:n, m(1)] if match /n(\h\h?)/
|
260
262
|
|
261
|
-
error! "unknown token near: #{@scanner.peek(10)}"
|
263
|
+
error! "unknown token near: #{@scanner.peek(10)}", el: @last_token
|
262
264
|
|
263
265
|
return [:unknown, m] if match /./
|
264
266
|
end
|
data/lib/ramekin/util.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ramekin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jneen
|
8
8
|
autorequire:
|
9
9
|
bindir: gembin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-02-
|
11
|
+
date: 2025-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: strscan
|