ramekin 0.0.7 → 0.0.9
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 -0
- data/lib/ramekin/amk_runner.rb +6 -1
- data/lib/ramekin/cli.rb +7 -2
- data/lib/ramekin/errors.rb +2 -2
- data/lib/ramekin/meta.rb +18 -10
- data/lib/ramekin/renderer.rb +29 -14
- data/lib/ramekin/sample_pack.rb +4 -0
- data/lib/ramekin/spc_player.rb +5 -5
- data/lib/ramekin/tokenizer.rb +5 -3
- 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: 1454704618e8123c1ade7251f1a30cc66b2f2febacd10e1e8f438e4b3fe3023c
|
4
|
+
data.tar.gz: 82453a74e36685f30e7637092fc47d242a63b609c7e4b1f586b17fafae05289f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f33e028002e58ad86ae808bcc4a0a77b9c8b324e3a47f652bf20dcf1bc4a83381f13e23e3ec980f5350cad8686f13097f1996e08c3de798614e4092ca55a8cbf
|
7
|
+
data.tar.gz: 4df89465d887a660c7353a6e4b16e3817603efd861c60315c39ca531026af27b5a72bf0a7b32aa73328f9b0160f3f7f5aa868a2de9fbe6bf493bb4f1cc345aff
|
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
Ramekin is a pre-processor for AddMusicK syntax that is in very early development.
|
4
4
|
|
5
|
+
Please keep in mind that as alpha software, **I may make breaking changes at any time.** After initial testing is done, I will release 1.0.0, at which point I will make a reasonable commitment to back-compatibility. That time is not yet.
|
6
|
+
|
5
7
|
# Installation
|
6
8
|
|
7
9
|
First, [Install Ruby](https://ruby-lang.org). Windows users can use [RubyInstaller](https://rubyinstaller.org/).
|
@@ -210,6 +212,24 @@ After the instrument declaration, you can use several commands to change the def
|
|
210
212
|
|
211
213
|
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.
|
212
214
|
|
215
|
+
### Echo settings
|
216
|
+
|
217
|
+
Echo settings can be set up with the `#echo/...` family of directives:
|
218
|
+
|
219
|
+
```elisp
|
220
|
+
#echo/channels:0,1,2,3
|
221
|
+
; or
|
222
|
+
#echo/channels:all
|
223
|
+
; or
|
224
|
+
#echo/channels:none
|
225
|
+
|
226
|
+
#echo/volume:20 ; range from -7f to 80, use negatives for surround
|
227
|
+
#echo/volume:20,30 ; set different values for left vs right echo
|
228
|
+
#echo/feedback:48 ; range from -7f to 80, use negatives for surround
|
229
|
+
#echo/fir:1 ; 1 or 0 to enable/disable the FIR filter
|
230
|
+
```
|
231
|
+
|
232
|
+
|
213
233
|
## Channel Commands
|
214
234
|
|
215
235
|
With your metadata all set up, it's time to add some notes!
|
data/lib/ramekin/amk_runner.rb
CHANGED
@@ -3,6 +3,8 @@ require_relative 'amk_runner/sample_groups'
|
|
3
3
|
|
4
4
|
module Ramekin
|
5
5
|
class AMKRunner
|
6
|
+
include Util
|
7
|
+
|
6
8
|
def initialize(filename, meta, txt)
|
7
9
|
@filename = File.expand_path(filename)
|
8
10
|
@amk_path = Ramekin.config.amk_dir
|
@@ -40,6 +42,8 @@ module Ramekin
|
|
40
42
|
|
41
43
|
ln("#@amk_path/asm", "#@workdir/asm")
|
42
44
|
|
45
|
+
FileUtils.mkdir_p("#@workdir/Visualizations")
|
46
|
+
|
43
47
|
FileUtils.mkdir_p("#@workdir/samples")
|
44
48
|
ln("#@amk_path/samples/default", "#@workdir/samples/default")
|
45
49
|
ln("#@amk_path/samples/optimized", "#@workdir/samples/optimized")
|
@@ -62,7 +66,7 @@ module Ramekin
|
|
62
66
|
File.write("#@workdir/Addmusic_sound effects.txt", '')
|
63
67
|
|
64
68
|
Dir.chdir(@workdir) do
|
65
|
-
|
69
|
+
sys './AddmusicK', '-norom', '-visualize', "ramekin/#{basename}.txt"
|
66
70
|
res = $?
|
67
71
|
binding.pry unless res.success?
|
68
72
|
end
|
@@ -73,6 +77,7 @@ module Ramekin
|
|
73
77
|
FileUtils.cp(@filename, "./#{basename}.rmk")
|
74
78
|
FileUtils.cp(spc_file, "./#{basename}.spc")
|
75
79
|
FileUtils.cp(stats_file, "./#{basename}.stats.txt")
|
80
|
+
FileUtils.cp("#@workdir/Visualizations/#{basename}.png", "./#{basename}.png")
|
76
81
|
File.write("./#{basename}.txt", @txt)
|
77
82
|
|
78
83
|
FileUtils.mkdir_p("samples/#{basename}")
|
data/lib/ramekin/cli.rb
CHANGED
@@ -111,6 +111,11 @@ module Ramekin
|
|
111
111
|
when '--package'
|
112
112
|
@output_package = argv.shift or usage! 'missing dirname after --package'
|
113
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
|
114
119
|
@play = true
|
115
120
|
when '--wav'
|
116
121
|
arg = argv.shift or usage! 'missing wav file after --render'
|
@@ -134,7 +139,7 @@ module Ramekin
|
|
134
139
|
# have a path to render the SPC file to.
|
135
140
|
if @play || @wav_file
|
136
141
|
if @output_package
|
137
|
-
@play_spc = "#@output_package/#{File.basename(@infile)}.spc"
|
142
|
+
@play_spc = "#@output_package/#{File.basename(@infile, '.rmk')}.spc"
|
138
143
|
elsif @output_spc
|
139
144
|
@play_spc = @output_spc
|
140
145
|
else
|
@@ -232,7 +237,7 @@ module Ramekin
|
|
232
237
|
|
233
238
|
if @play
|
234
239
|
SPCPlayer.instance.setup!
|
235
|
-
SPCPlayer.instance.play(@play_spc)
|
240
|
+
SPCPlayer.instance.play(@play_spc, @play_offset)
|
236
241
|
end
|
237
242
|
|
238
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
@@ -22,7 +22,7 @@ module Ramekin
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def echo_channel_bits
|
25
|
-
(@echo_channels ||= Set.new).map { |x| 1 << x }.inject(&:|)
|
25
|
+
(@echo_channels ||= Set.new).map { |x| 1 << x }.inject(0, &:|)
|
26
26
|
end
|
27
27
|
|
28
28
|
def echo_volumes
|
@@ -63,8 +63,6 @@ module Ramekin
|
|
63
63
|
error! 'invalid token in header'
|
64
64
|
end
|
65
65
|
end
|
66
|
-
|
67
|
-
@sample_groups << 'default' if @sample_groups.empty?
|
68
66
|
end
|
69
67
|
|
70
68
|
SURROUND_R = (-0x7f..0x80)
|
@@ -84,21 +82,25 @@ module Ramekin
|
|
84
82
|
when 'instrument'
|
85
83
|
name, path = expect_args(:instrument, :string)
|
86
84
|
extensions = []
|
87
|
-
while (el = check_arg(:adsr, :tuning, :o))
|
85
|
+
while (el = check_arg(:adsr, :tuning, :gain, :o))
|
88
86
|
extensions << el
|
89
87
|
end
|
90
88
|
|
91
89
|
return unless @current_pack
|
92
|
-
@instruments << Instrument.new(@current_pack, @
|
90
|
+
@instruments << Instrument.new(@current_pack, @current_directive, name, path, extensions)
|
93
91
|
when 'default', 'optimized' then @sample_groups << @current
|
94
92
|
|
95
93
|
# TODO: real echo syntax
|
96
94
|
when 'echo' then @echo = expect_args(*([:hex] * 8))
|
97
95
|
when 'echo/channels'
|
98
96
|
channels = @current_directive.values[1]
|
99
|
-
|
97
|
+
case channels
|
98
|
+
when 'all'
|
100
99
|
@echo_channels = Set.new(0..7)
|
101
100
|
return
|
101
|
+
when 'none'
|
102
|
+
@echo_channels = Set.new
|
103
|
+
return
|
102
104
|
end
|
103
105
|
|
104
106
|
unless channels && channels =~ /\A\d(,\d)+\z/
|
@@ -250,12 +252,12 @@ module Ramekin
|
|
250
252
|
|
251
253
|
def adsr
|
252
254
|
@adsr ||= ext_adsr || pack_adsr \
|
253
|
-
or error! "no adsr configured for #{name}"
|
255
|
+
or error! "no adsr configured for #{name.value}", el: name
|
254
256
|
end
|
255
257
|
|
256
258
|
def tuning
|
257
259
|
@tuning ||= ext_tuning || pack_tuning \
|
258
|
-
or error! "no tuning configured for #{name}"
|
260
|
+
or error! "no tuning configured for #{name.value}", el: name
|
259
261
|
end
|
260
262
|
|
261
263
|
def pack_tuning
|
@@ -284,8 +286,7 @@ module Ramekin
|
|
284
286
|
end
|
285
287
|
|
286
288
|
def gain
|
287
|
-
@gain ||= ext_gain || pack_gain
|
288
|
-
or error! "no gain configured for #{name}"
|
289
|
+
@gain ||= ext_gain || pack_gain || 0xFF
|
289
290
|
end
|
290
291
|
|
291
292
|
def hexes
|
@@ -293,6 +294,8 @@ module Ramekin
|
|
293
294
|
t1, t2 = self.tuning
|
294
295
|
g = self.gain
|
295
296
|
|
297
|
+
return [] unless a && d && s && r && t1 && t2 && g
|
298
|
+
|
296
299
|
adsr1 = ((7 - d)*16 | 0x80) + (15 - a)
|
297
300
|
adsr2 = (s*32 + (31-r))
|
298
301
|
|
@@ -300,6 +303,11 @@ module Ramekin
|
|
300
303
|
end
|
301
304
|
|
302
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
|
+
|
303
311
|
"#{File.basename(sample_name).inspect} #{hexes.map { |h| "$#{h}" }.join(' ')}"
|
304
312
|
end
|
305
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|
|
@@ -109,7 +113,7 @@ module Ramekin
|
|
109
113
|
def tempo_of(el)
|
110
114
|
case el.type
|
111
115
|
when :t then el.value
|
112
|
-
when :bpm then (el.value.to_i *
|
116
|
+
when :bpm then (el.value.to_i * 8192 / 20025.0).round
|
113
117
|
end
|
114
118
|
end
|
115
119
|
|
@@ -170,13 +174,7 @@ module Ramekin
|
|
170
174
|
# reset tick counter for newlines
|
171
175
|
@tick = 0
|
172
176
|
when :transpose
|
173
|
-
|
174
|
-
|
175
|
-
if interval < 0
|
176
|
-
interval = 0x80 - interval
|
177
|
-
end
|
178
|
-
|
179
|
-
yield sprintf("$fa$02$%02x", token.value.to_i)
|
177
|
+
yield sprintf("$fa$02$%02x", token.value.to_i & 0x7f)
|
180
178
|
when :instrument
|
181
179
|
case token.value
|
182
180
|
when /\A\d+\z/ then yield "@#{token.value}"
|
@@ -189,11 +187,14 @@ module Ramekin
|
|
189
187
|
yield "@#{@instrument_index[token.value]}"
|
190
188
|
end
|
191
189
|
when :v
|
192
|
-
|
193
|
-
|
190
|
+
vol, time = token.values.compact.map(&:to_i)
|
191
|
+
yield velocity_command(vol, time)
|
192
|
+
@volume = vol
|
194
193
|
when :relv
|
195
194
|
relvol, duration = token.values
|
196
|
-
yield
|
195
|
+
yield velocity_command(@volume + relvol.to_i, duration)
|
196
|
+
when :q
|
197
|
+
yield "q#{token.value}"
|
197
198
|
when :adsr
|
198
199
|
vals = token.value.split(',').map { |x| x.to_i(16) }
|
199
200
|
error! 'invalid #adsr, expected 4 arguments' unless vals.size == 4
|
@@ -254,6 +255,20 @@ module Ramekin
|
|
254
255
|
end
|
255
256
|
end
|
256
257
|
|
258
|
+
def velocity_command(vol, time=nil)
|
259
|
+
vol = 255 if vol > 255
|
260
|
+
vol = 0 if vol < 0
|
261
|
+
|
262
|
+
if time.nil?
|
263
|
+
"v#{vol}"
|
264
|
+
else
|
265
|
+
time = time.to_i
|
266
|
+
time = 255 if time > 255
|
267
|
+
time = 0 if time < 0
|
268
|
+
sprintf("$E8$%02x$%02x", time, vol)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
257
272
|
def render_directive(token)
|
258
273
|
case token.value
|
259
274
|
when 'SPC'
|
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 /~/
|
@@ -227,6 +228,7 @@ module Ramekin
|
|
227
228
|
return [:instrument, m(1)] if match /@(\w+)/
|
228
229
|
return [:hex, m(1)] if match /[$](\h\h)/
|
229
230
|
return [:t, m(1)] if match /t(\d+)/
|
231
|
+
return [:q, m(1)] if match /q(\d\h?)/
|
230
232
|
|
231
233
|
return note(:note, m) if match /[abcdefg][+-]?/
|
232
234
|
return note(:r) if match /r/
|
@@ -259,7 +261,7 @@ module Ramekin
|
|
259
261
|
return [:q, m(1)] if match /q(\h\h)/
|
260
262
|
return [:n, m(1)] if match /n(\h\h?)/
|
261
263
|
|
262
|
-
error! "unknown token near: #{@scanner.peek(10)}"
|
264
|
+
error! "unknown token near: #{@scanner.peek(10)}", el: @last_token
|
263
265
|
|
264
266
|
return [:unknown, m] if match /./
|
265
267
|
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.9
|
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
|