ramekin 0.0.6 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|