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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8ecaeee8d7551d229bd4d659e37db4fb7ce51b8c8b02db5951afe2889c5766d
4
- data.tar.gz: 306dd97179707ab87cbf615c0740495f230a5be78476c89855c5e65c3cbc8d54
3
+ metadata.gz: 1454704618e8123c1ade7251f1a30cc66b2f2febacd10e1e8f438e4b3fe3023c
4
+ data.tar.gz: 82453a74e36685f30e7637092fc47d242a63b609c7e4b1f586b17fafae05289f
5
5
  SHA512:
6
- metadata.gz: f6496a57a0d10da8cb22d3c0da7593fc8db6949f36fa78b6bf124b39bb727f9b05176fff29961111f510029c7ec93e7d4b8edad2ee0704e312bff82b2d5b02e0
7
- data.tar.gz: 494634f3636b32f99fd77c3337b7981021f39850aee811cbe15e6b8154fe5d1474ca51659bce91fe46e09ec4e64e7d5344488ba171523b5036ca7e53713cfcf5
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!
@@ -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
- system "./AddmusicK -norom ramekin/#{basename}.txt"
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
@@ -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, @current, name, path, extensions)
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
- if channels == 'all'
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
 
@@ -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.each do |group|
83
- yield " ##{group.value}\n"
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 * 256 / 625.0).round
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
- interval = token.value.to_i
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
- yield "v#{token.values.compact.join(',')}"
193
- @volume = token.values[0].to_i
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 "v#{[@volume + relvol.to_i, duration].compact.join(',')}"
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'
@@ -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)
@@ -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
@@ -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[0])
138
+ @col = find_colno(matched)
139
139
  else
140
- @col += find_colno(matched[0])
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
@@ -44,7 +44,7 @@ module Ramekin
44
44
  env = {}
45
45
  kw.each { |k, v| env[k.to_s] = v }
46
46
 
47
- system(env, *a)
47
+ system(env, *a.map(&:to_s))
48
48
  rescue Interrupt
49
49
  # allow interrupt out of the subprocess but capture it for ramekin itself
50
50
  end
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.7
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-17 00:00:00.000000000 Z
11
+ date: 2025-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: strscan