ramekin 0.0.9 → 0.0.11

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: 1454704618e8123c1ade7251f1a30cc66b2f2febacd10e1e8f438e4b3fe3023c
4
- data.tar.gz: 82453a74e36685f30e7637092fc47d242a63b609c7e4b1f586b17fafae05289f
3
+ metadata.gz: f9fce3c7ec61c39d7597621bf02fbd71788df731ee7a98cb89018549275eb44e
4
+ data.tar.gz: 0c123977b81c24d92f4f6b3514973ca4713aedf4c8292aa30d126f6b20d541ea
5
5
  SHA512:
6
- metadata.gz: f33e028002e58ad86ae808bcc4a0a77b9c8b324e3a47f652bf20dcf1bc4a83381f13e23e3ec980f5350cad8686f13097f1996e08c3de798614e4092ca55a8cbf
7
- data.tar.gz: 4df89465d887a660c7353a6e4b16e3817603efd861c60315c39ca531026af27b5a72bf0a7b32aa73328f9b0160f3f7f5aa868a2de9fbe6bf493bb4f1cc345aff
6
+ metadata.gz: 0d4484cc76d708e7b1d36929e2a4b95efe19b34ad5882f51e71d526bd08d1c500725de40054b17e5401922fd388854a2866fca0a2210a15589bdbd5e46bc85b4
7
+ data.tar.gz: cd54afeb74289aaf055344b45aca6b75679b494db546308c0d86d05866c0a4e4cc855921232f7c6507ac8cc6a2b8e6656247ba6ea579806c1589786470ce2f31
data/lib/ramekin/cli.rb CHANGED
@@ -172,6 +172,7 @@ module Ramekin
172
172
  NoteAggregator,
173
173
  RestAggregator,
174
174
  Legato,
175
+ LoopAllocator,
175
176
  Inspector,
176
177
  )
177
178
 
@@ -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'] == '1'
18
+ if ENV['RAMEKIN_DEBUG'] == '1' && ENV['DISABLE_PRY'] != 'true'
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)
@@ -25,6 +25,8 @@ module Ramekin
25
25
 
26
26
  binding.pry
27
27
  end
28
+
29
+ nil
28
30
  end
29
31
 
30
32
  def initialize(start, fin, message)
@@ -32,10 +32,10 @@ module Ramekin
32
32
  @note.ticks / 2
33
33
  end
34
34
 
35
- def to_amk(current_octave=nil)
35
+ def to_amk(current_octave=nil, divisor=1)
36
36
  post_ticks = @note.ticks - ticks
37
37
  post_len = KNOWN_LENGTHS.fetch(post_ticks) { "=#{post_ticks}" }
38
- "#{octave_amk(current_octave)}#{note_name}#{length_amk}$f4$01^#{post_len}"
38
+ "#{octave_amk(current_octave)}#{note_name}#{length_amk(divisor)}$f4$01^#{post_len}"
39
39
  end
40
40
 
41
41
  def inspect
@@ -0,0 +1,47 @@
1
+ module Ramekin
2
+ class LoopAllocator < Processor
3
+ def call(&b)
4
+ tokens = @stream.to_a
5
+
6
+ loop_defs = tokens.select do |tok|
7
+ Token === tok && tok.type == :loop && !tok.value.empty?
8
+ end
9
+
10
+ index = 0
11
+ indices = {}
12
+ used = Set.new
13
+
14
+ numbered, named = loop_defs.partition do |tok|
15
+ /\A\d+\z/.match?(tok.value)
16
+ end
17
+
18
+ numbered.each do |tok|
19
+ num = indices[tok.value] = tok.value.to_i
20
+ tok.meta[:number] = num
21
+ used << num
22
+ end
23
+
24
+ named.each do |tok|
25
+ if indices.key?(tok.value)
26
+ error! "multiple definitions of (#{tok.value})", el: tok
27
+ next
28
+ end
29
+
30
+ index += 1
31
+ index += 1 while used.include?(index)
32
+ indices[tok.value] = index
33
+ tok.meta[:number] = index
34
+ end
35
+
36
+ tokens.each do |token|
37
+ if Token === token && token.type == :loop_call
38
+ token.meta[:number] = indices.fetch(token.value) do
39
+ error! "no such loop", el: token
40
+ end
41
+ end
42
+
43
+ yield token
44
+ end
45
+ end
46
+ end
47
+ end
data/lib/ramekin/meta.rb CHANGED
@@ -48,6 +48,34 @@ module Ramekin
48
48
  )
49
49
  end
50
50
 
51
+ def t_value
52
+ t_with_divisor[0]
53
+ end
54
+
55
+ def divisor
56
+ t_with_divisor[1]
57
+ end
58
+
59
+ def t_with_divisor
60
+ return [50, 1] if @tempo.nil?
61
+
62
+ @t_with_divisor ||= begin
63
+ t = case @tempo.type
64
+ when :t then @tempo.value
65
+ when :bpm then (@tempo.value.to_f * 8192 / 20025.0)
66
+ end
67
+
68
+ t = t.to_f
69
+ divisor = 1
70
+ while t > 60
71
+ t /= 2
72
+ divisor *= 2
73
+ end
74
+
75
+ [t.round, divisor]
76
+ end
77
+ end
78
+
51
79
  def parse
52
80
  loop do
53
81
  break if @elements.empty?
@@ -58,7 +86,7 @@ module Ramekin
58
86
  when :amk then @amk = @current
59
87
  when :option then @options[@current.value] = true
60
88
  when :w then @volume = @current
61
- when :t, :bpm then @tempo = @current
89
+ when :t, :bpm then @tempo = @current; @t_with_divisor = nil
62
90
  else
63
91
  error! 'invalid token in header'
64
92
  end
@@ -282,7 +310,7 @@ module Ramekin
282
310
 
283
311
  def ext_gain
284
312
  gain = @extensions.select { |e| e.type == :gain }.last
285
- gain && gain.value
313
+ gain && to_hex(gain.value.to_i(16))
286
314
  end
287
315
 
288
316
  def gain
@@ -299,7 +327,13 @@ module Ramekin
299
327
  adsr1 = ((7 - d)*16 | 0x80) + (15 - a)
300
328
  adsr2 = (s*32 + (31-r))
301
329
 
302
- [adsr1.to_s(16), adsr2.to_s(16), g, t1, t2]
330
+ [to_hex(adsr1), to_hex(adsr2), g, t1, t2]
331
+ end
332
+
333
+ def to_hex(x)
334
+ return x if x.is_a?(String)
335
+
336
+ sprintf("%02x", x)
303
337
  end
304
338
 
305
339
  def to_amk
@@ -57,10 +57,16 @@ module Ramekin
57
57
  end
58
58
  end
59
59
 
60
- def length_amk
60
+ def length_amk(div)
61
+ if ticks % div != 0
62
+ divisible = div == 2 ? 'even' : "divisible by #{div}"
63
+ error!("too fast! tick count must be #{divisible} at this tempo", el: self)
64
+ end
65
+
66
+ t = ticks / div
61
67
  # we use l16
62
- return '' if ticks == 12
63
- KNOWN_LENGTHS.fetch(ticks) { "=#{ticks}" }
68
+ return '' if t == 12
69
+ KNOWN_LENGTHS.fetch(t) { "=#{t}" }
64
70
  end
65
71
 
66
72
  def octave_amk(current_octave=nil)
@@ -80,8 +86,8 @@ module Ramekin
80
86
  end
81
87
  end
82
88
 
83
- def to_amk(current_octave=nil)
84
- return "#{octave_amk(current_octave)}#{note_name}#{length_amk}"
89
+ def to_amk(current_octave=nil, divisor=1)
90
+ return "#{octave_amk(current_octave)}#{note_name}#{length_amk(divisor)}"
85
91
  end
86
92
 
87
93
  def repr
@@ -94,7 +100,7 @@ module Ramekin
94
100
 
95
101
  def ticks
96
102
  if @extensions.empty? && default_length.nil?
97
- error! "no length and no default specified with l", el: @note
103
+ return 192 / 16
98
104
  end
99
105
 
100
106
  exts = @extensions
@@ -58,7 +58,7 @@ module Ramekin
58
58
 
59
59
  render_preamble(&b)
60
60
 
61
- @track.channels.each { |c| render_channel(c, &b) }
61
+ @track.channels.compact.each { |c| render_channel(c, &b) }
62
62
  end
63
63
 
64
64
  def render_preamble(&b)
@@ -101,7 +101,7 @@ module Ramekin
101
101
  yield "}\n\n"
102
102
  end
103
103
 
104
- yield "t#{tempo_of(m.tempo)} ; main tempo (0-60)\n" if m.tempo
104
+ yield "t#{m.t_value} ; main tempo (0-60)\n" if m.tempo
105
105
  yield "w#{m.volume.value} ; main volume (0-255)\n" if m.volume
106
106
  yield "l16\n\n"
107
107
 
@@ -143,12 +143,14 @@ module Ramekin
143
143
  if (old_tick-1) / 192 != (@tick-1) / 192
144
144
  yield "\n"
145
145
  end
146
- yield el.to_amk(@octave)
146
+ divisor = @track.meta.divisor
147
+
148
+ yield el.to_amk(@octave, @track.meta.divisor)
147
149
  @octave = el.octave unless el.rest?
148
150
  when MacroDefinition
149
151
  # pass
150
152
  when LegatoStart, LegatoLastNote
151
- yield el.to_amk
153
+ yield el.to_amk(@octave, @track.meta.divisor)
152
154
  when Instrument
153
155
  yield "@#{@instrument_index[el.name.value]}"
154
156
  when Token
@@ -188,13 +190,25 @@ module Ramekin
188
190
  end
189
191
  when :v
190
192
  vol, time = token.values.compact.map(&:to_i)
191
- yield velocity_command(vol, time)
193
+
194
+ unless Token === peek && [:v, :relv].include?(peek.type)
195
+ yield velocity_command(vol, time)
196
+ end
197
+
192
198
  @volume = vol
193
199
  when :relv
194
200
  relvol, duration = token.values
195
- yield velocity_command(@volume + relvol.to_i, duration)
201
+
202
+ unless Token === peek && [:v, :relv].include?(peek.type)
203
+ yield velocity_command(@volume + relvol.to_i, duration)
204
+ end
196
205
  when :q
197
- yield "q#{token.value}"
206
+ token.value =~ /(\d),?(\h)?/
207
+ qval = $1
208
+ gain = $2 || 'F'
209
+ yield "q#{qval}#{gain}"
210
+ when :w
211
+ yield "w#{token.value}"
198
212
  when :adsr
199
213
  vals = token.value.split(',').map { |x| x.to_i(16) }
200
214
  error! 'invalid #adsr, expected 4 arguments' unless vals.size == 4
@@ -229,14 +243,14 @@ module Ramekin
229
243
  yield " [[ "
230
244
  when :loop
231
245
  if token.value
232
- yield " (#{token.value})[ "
246
+ yield " (#{token.meta[:number]})[ "
233
247
  else
234
248
  yield " [ "
235
249
  end
236
250
  when :loop_end
237
251
  yield " ]#{token.value} "
238
252
  when :loop_call
239
- yield " (#{token.values[0]})#{token.values[1]} "
253
+ yield " (#{token.meta[:number]})#{token.values[1]} "
240
254
  when :star
241
255
  yield "*#{token.value}"
242
256
  when :superloop_end
@@ -228,7 +228,6 @@ module Ramekin
228
228
  return [:instrument, m(1)] if match /@(\w+)/
229
229
  return [:hex, m(1)] if match /[$](\h\h)/
230
230
  return [:t, m(1)] if match /t(\d+)/
231
- return [:q, m(1)] if match /q(\d\h?)/
232
231
 
233
232
  return note(:note, m) if match /[abcdefg][+-]?/
234
233
  return note(:r) if match /r/
@@ -244,9 +243,9 @@ module Ramekin
244
243
  return [:superloop_end, m(1)] if match /\]\](\d*)/
245
244
 
246
245
  return [:remote_def, m(1)] if match /[(]!(\d+)[)]\[/
247
- return [:loop, m(1)] if match /(?:[(](\d+)[)])?\[/
246
+ return [:loop, m(1)] if match /(?:[(]([\w-]+)[)])?\[/
248
247
  return [:loop_end, m(1)] if match /\](\d*)/
249
- return [:loop_call, m(1), m(2)] if match /[(](\d+)[)](\d+)?/
248
+ return [:loop_call, m(1), m(2)] if match /[(]([\w-]+)[)](\d+)?/
250
249
 
251
250
  # this needs a reparse
252
251
  return remote(:remote_call, m(1)) if match /[(]!(\d*)/
@@ -258,7 +257,7 @@ module Ramekin
258
257
  return [:w, m(1)] if match /w(\d+)/
259
258
  return [:l, m(1)] if match /l(\d+)/
260
259
  return [:amp] if match /[&]/
261
- return [:q, m(1)] if match /q(\h\h)/
260
+ return [:q, m(1)] if match /q(\d(?:,\h)?)/
262
261
  return [:n, m(1)] if match /n(\h\h?)/
263
262
 
264
263
  error! "unknown token near: #{@scanner.peek(10)}", el: @last_token
data/lib/ramekin/util.rb CHANGED
@@ -44,11 +44,24 @@ module Ramekin
44
44
  env = {}
45
45
  kw.each { |k, v| env[k.to_s] = v }
46
46
 
47
+ $stderr.puts "run: #{a.map(&method(:shell_inspect)).join(' ')}"
47
48
  system(env, *a.map(&:to_s))
48
49
  rescue Interrupt
49
50
  # allow interrupt out of the subprocess but capture it for ramekin itself
50
51
  end
51
52
 
53
+ # for diagnostic purposes only, not a real escape system.
54
+ # our shellouts don't need escapes since they are not actually
55
+ # interpreted by a shell.
56
+ def shell_inspect(s)
57
+ s = s.to_s
58
+ if s =~ /[\s'"&*]/
59
+ s.inspect
60
+ else
61
+ s
62
+ end
63
+ end
64
+
52
65
  def executable?(path)
53
66
  stat = File.stat(path)
54
67
  stat.file? && stat.executable?
data/lib/ramekin.rb CHANGED
@@ -11,6 +11,7 @@ require_relative 'ramekin/processor'
11
11
  require_relative 'ramekin/macros'
12
12
  require_relative 'ramekin/note_aggregator'
13
13
  require_relative 'ramekin/legato'
14
+ require_relative 'ramekin/loop_allocator'
14
15
  require_relative 'ramekin/meta'
15
16
  require_relative 'ramekin/channel_separator'
16
17
  require_relative 'ramekin/volume'
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.9
4
+ version: 0.0.11
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-18 00:00:00.000000000 Z
11
+ date: 2025-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: strscan
@@ -85,6 +85,7 @@ files:
85
85
  - lib/ramekin/element.rb
86
86
  - lib/ramekin/errors.rb
87
87
  - lib/ramekin/legato.rb
88
+ - lib/ramekin/loop_allocator.rb
88
89
  - lib/ramekin/macros.rb
89
90
  - lib/ramekin/meta.rb
90
91
  - lib/ramekin/note_aggregator.rb