ramekin 0.0.5b
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 +7 -0
- data/README.md +277 -0
- data/gembin/ramekin +16 -0
- data/lib/ramekin/amk_runner/sample_groups.rb +65 -0
- data/lib/ramekin/amk_runner.rb +123 -0
- data/lib/ramekin/amk_setup.rb +116 -0
- data/lib/ramekin/channel_separator.rb +40 -0
- data/lib/ramekin/cli.rb +367 -0
- data/lib/ramekin/config.rb +126 -0
- data/lib/ramekin/element.rb +24 -0
- data/lib/ramekin/errors.rb +57 -0
- data/lib/ramekin/legato.rb +142 -0
- data/lib/ramekin/macros.rb +101 -0
- data/lib/ramekin/meta.rb +227 -0
- data/lib/ramekin/note_aggregator.rb +252 -0
- data/lib/ramekin/processor.rb +78 -0
- data/lib/ramekin/renderer.rb +288 -0
- data/lib/ramekin/sample_pack.rb +296 -0
- data/lib/ramekin/spc_player.rb +122 -0
- data/lib/ramekin/tokenizer.rb +287 -0
- data/lib/ramekin/util.rb +120 -0
- data/lib/ramekin/volume.rb +16 -0
- data/lib/ramekin.rb +19 -0
- metadata +122 -0
@@ -0,0 +1,287 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
module Ramekin
|
4
|
+
class Location
|
5
|
+
attr_reader :pos, :line, :col
|
6
|
+
def initialize(pos, line, col)
|
7
|
+
@pos = pos
|
8
|
+
@line = line
|
9
|
+
@col = col
|
10
|
+
end
|
11
|
+
|
12
|
+
def linecol
|
13
|
+
"#{@line}:#{@col}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def fin
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class LocRange
|
26
|
+
def self.between(a, b)
|
27
|
+
new(a.start, b.fin)
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :start, :fin
|
31
|
+
def initialize(start, fin)
|
32
|
+
@start = start
|
33
|
+
@fin = fin
|
34
|
+
end
|
35
|
+
|
36
|
+
def pos_range
|
37
|
+
(@start.pos...@fin.pos)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Token < Element
|
42
|
+
def initialize(type, *values)
|
43
|
+
@type = type
|
44
|
+
@values = values
|
45
|
+
@meta = {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def components
|
49
|
+
[self]
|
50
|
+
end
|
51
|
+
|
52
|
+
def value
|
53
|
+
@values[0]
|
54
|
+
end
|
55
|
+
|
56
|
+
def dup
|
57
|
+
Token.new(type, *values).tap { |t| t.loc_range = loc_range }
|
58
|
+
end
|
59
|
+
|
60
|
+
attr_reader :type, :values, :meta
|
61
|
+
attr_accessor :loc_range
|
62
|
+
|
63
|
+
def start
|
64
|
+
loc_range.start
|
65
|
+
end
|
66
|
+
|
67
|
+
def fin
|
68
|
+
loc_range.fin
|
69
|
+
end
|
70
|
+
|
71
|
+
def repr_basic
|
72
|
+
return @type if @values.empty?
|
73
|
+
|
74
|
+
"#{@type}(#{@values.join(',')})"
|
75
|
+
end
|
76
|
+
|
77
|
+
def repr
|
78
|
+
return repr_basic if macro_stack.empty?
|
79
|
+
|
80
|
+
"#{repr_basic}[#{macro_stack.map(&:value).join(',')}]"
|
81
|
+
end
|
82
|
+
|
83
|
+
def inspect
|
84
|
+
"t:#{repr}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Tokenizer
|
89
|
+
def self.tokenize(str, &b)
|
90
|
+
new(str).tokenize(&b)
|
91
|
+
end
|
92
|
+
|
93
|
+
def initialize(str)
|
94
|
+
@scanner = StringScanner.new(str)
|
95
|
+
@macros = []
|
96
|
+
@last_match = nil
|
97
|
+
@last_captures = []
|
98
|
+
@line = 0
|
99
|
+
@col = 0
|
100
|
+
@in_macro = false
|
101
|
+
end
|
102
|
+
|
103
|
+
include Error::Helpers
|
104
|
+
# @override
|
105
|
+
def default_error_location
|
106
|
+
@last_token
|
107
|
+
end
|
108
|
+
|
109
|
+
def skip_whitespace_and_comments
|
110
|
+
match %r(
|
111
|
+
(?: \s+
|
112
|
+
| ;.*?$
|
113
|
+
)+
|
114
|
+
)mx
|
115
|
+
end
|
116
|
+
|
117
|
+
def match(regexp)
|
118
|
+
matched = @scanner.scan(regexp)
|
119
|
+
return false unless matched
|
120
|
+
|
121
|
+
update_linecol(matched)
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
125
|
+
def match_exact(string)
|
126
|
+
return false unless @scanner.peek(string.size) == string
|
127
|
+
@scanner.pos += string.size
|
128
|
+
update_linecol(string)
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
def update_linecol(matched)
|
133
|
+
@index += matched.size
|
134
|
+
|
135
|
+
newlines = matched.count("\n")
|
136
|
+
@line += newlines
|
137
|
+
if newlines > 0
|
138
|
+
@col = find_colno(matched[0])
|
139
|
+
else
|
140
|
+
@col += find_colno(matched[0])
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def find_colno(str)
|
145
|
+
str.rpartition("\n").last.size
|
146
|
+
end
|
147
|
+
|
148
|
+
def location
|
149
|
+
Location.new(@scanner.pos, @line, @col)
|
150
|
+
end
|
151
|
+
|
152
|
+
def tokenize(&b)
|
153
|
+
return enum_for(:tokenize) unless block_given?
|
154
|
+
|
155
|
+
@index = 0
|
156
|
+
@lineno = 1
|
157
|
+
@colno = 1
|
158
|
+
skip_whitespace_and_comments
|
159
|
+
|
160
|
+
until @scanner.eos?
|
161
|
+
start = self.location
|
162
|
+
category, *values = self.scan
|
163
|
+
t = Token.new(category, *values)
|
164
|
+
# t.source_name = @source_name
|
165
|
+
# t.original = @input
|
166
|
+
t.loc_range = LocRange.between(start, self.location)
|
167
|
+
|
168
|
+
@last_token = t
|
169
|
+
yield t
|
170
|
+
skip_whitespace_and_comments
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def scan
|
175
|
+
if @is_note
|
176
|
+
@is_note = false
|
177
|
+
return [:length, m] if match /\d+[.]?|=\d+/
|
178
|
+
end
|
179
|
+
|
180
|
+
if @is_remote
|
181
|
+
end
|
182
|
+
|
183
|
+
@macros.each do |pattern|
|
184
|
+
return [:macrocall, pattern] if match_exact(pattern)
|
185
|
+
end
|
186
|
+
|
187
|
+
if !@in_macro and match /"([^="]+?)=/
|
188
|
+
@macros << m(1)
|
189
|
+
|
190
|
+
@macros.sort_by! { |m| -m.size }
|
191
|
+
@macros.uniq!
|
192
|
+
|
193
|
+
@in_macro = true
|
194
|
+
|
195
|
+
return [:macro, m(1)]
|
196
|
+
end
|
197
|
+
|
198
|
+
if @in_macro and match /"/
|
199
|
+
@in_macro = false
|
200
|
+
return [:endmacro]
|
201
|
+
end
|
202
|
+
|
203
|
+
return [:string, unescape(m(1))] if match /"((?:\\.|[^"])*)"/
|
204
|
+
|
205
|
+
return [:define, m(1)] if match /#define\s*(.*?)\n/
|
206
|
+
return [:ifdef, m(1)] if match /#ifdef\s*(.*?)\n/
|
207
|
+
return [:ifndef, m(1)] if match /#ifndef\s*(.*?)\n/
|
208
|
+
return [:endif, m(1)] if match /#endif/
|
209
|
+
|
210
|
+
return [:adsr, m(1)] if match /#adsr:(\h+(?:,\h+)*)/
|
211
|
+
return [:tuning, m(1)] if match /#tuning:(\h\h\h\h)/
|
212
|
+
return [:bpm, m(1)] if match /#bpm:(\d+)/
|
213
|
+
return [:legato_tie] if match /~/
|
214
|
+
|
215
|
+
return [:transpose, m(1)] if match /_([+-]\d+)/
|
216
|
+
return [:transpose, '0'] if match /_0/
|
217
|
+
return [:amk, m(1)] if match /#amk\s*(\d+)/
|
218
|
+
return [:option, m(1)] if match /#option (\w+)/
|
219
|
+
return [:channel, m(1)] if match /#(\d+)/
|
220
|
+
return [:directive, m(1)] if match /#([\w-]+)/
|
221
|
+
return [:lbrace] if match /[{]/
|
222
|
+
return [:rbrace] if match /[}]/
|
223
|
+
return [:v, m(1), m(2)] if match /v(\d+)(?:,(\d+))?/
|
224
|
+
return [:relv, m(1), m(2)] if match /v([+-]\d+)(?:,(\d+))?/
|
225
|
+
return [:instrument, m(1)] if match /@(\d+)/
|
226
|
+
return [:instrument, m(1)] if match /@(\w+)/
|
227
|
+
return [:hex, m(1)] if match /[$](\h\h)/
|
228
|
+
return [:t, m(1)] if match /t(\d+)/
|
229
|
+
|
230
|
+
return note(:note, m) if match /[abcdefg][+-]?/
|
231
|
+
return note(:r) if match /r/
|
232
|
+
return note(:native_tie, m(1)) if match /\^\^/
|
233
|
+
return note(:tie, m(1)) if match /\^/
|
234
|
+
return [:slash] if match /\//
|
235
|
+
|
236
|
+
return [:octave, m] if match /[<>]/
|
237
|
+
return [:o, m(1)] if match /o(\d+)/
|
238
|
+
return [:p, m(1)] if match /p(\d+,\d+(?:,\d+)?)/
|
239
|
+
|
240
|
+
return [:superloop] if match /\[\[/
|
241
|
+
return [:superloop_end, m(1)] if match /\]\](\d*)/
|
242
|
+
|
243
|
+
return [:remote_def, m(1)] if match /[(]!(\d+)[)]\[/
|
244
|
+
return [:loop, m(1)] if match /(?:[(](\d+)[)])?\[/
|
245
|
+
return [:loop_end, m(1)] if match /\](\d*)/
|
246
|
+
return [:loop_call, m(1), m(2)] if match /[(](\d+)[)](\d+)?/
|
247
|
+
|
248
|
+
# this needs a reparse
|
249
|
+
return remote(:remote_call, m(1)) if match /[(]!(\d*)/
|
250
|
+
|
251
|
+
return [:star, m(1)] if match /\*(\d*)/
|
252
|
+
return [:y, m(1)] if match /y(\d+)/
|
253
|
+
return [:rely, m(1)] if match /y([LR]\d+)/
|
254
|
+
return [:rely, 'C'] if match /yC/
|
255
|
+
return [:w, m(1)] if match /w(\d+)/
|
256
|
+
return [:l, m(1)] if match /l(\d+)/
|
257
|
+
return [:amp] if match /[&]/
|
258
|
+
return [:q, m(1)] if match /q(\h\h)/
|
259
|
+
return [:n, m(1)] if match /n(\h\h?)/
|
260
|
+
|
261
|
+
error! "unknown token near: #{@scanner.peek(10)}"
|
262
|
+
|
263
|
+
return [:unknown, m] if match /./
|
264
|
+
end
|
265
|
+
|
266
|
+
def note(*a)
|
267
|
+
@is_note = true
|
268
|
+
a
|
269
|
+
end
|
270
|
+
|
271
|
+
def remote(*a)
|
272
|
+
@is_remote = true
|
273
|
+
a
|
274
|
+
end
|
275
|
+
|
276
|
+
def unescape(str)
|
277
|
+
str.gsub /\\(.)/ do
|
278
|
+
next $1 if $1 == '"'
|
279
|
+
error! "invalid escape - only double quote allowed: #{$1.inspect}"
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def m(i=0)
|
284
|
+
@scanner[i]
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
data/lib/ramekin/util.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'zip'
|
2
|
+
|
3
|
+
module Ramekin
|
4
|
+
module Util
|
5
|
+
extend self
|
6
|
+
|
7
|
+
private
|
8
|
+
def clear_dir(dir, force: false, remake: true)
|
9
|
+
if Dir.exist?(dir) && !Dir.empty?(dir)
|
10
|
+
unless @force
|
11
|
+
$stderr.print "#{dir} already exists. delete? [y/n] "
|
12
|
+
input = $stdin.gets.strip
|
13
|
+
if input.downcase != 'y'
|
14
|
+
$stderr.puts 'skipping...'
|
15
|
+
return false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
FileUtils.remove_entry_secure(dir) if Dir.exist?(dir)
|
20
|
+
end
|
21
|
+
|
22
|
+
FileUtils.mkdir_p(dir) if remake
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def unpack_zip_here(zip, &b)
|
27
|
+
Zip::File.open_buffer(zip).each do |entry|
|
28
|
+
next if entry.directory?
|
29
|
+
# chop off the first directory
|
30
|
+
name = entry.name.split('/', 2).last
|
31
|
+
next if name.empty?
|
32
|
+
next if name.start_with?('.github')
|
33
|
+
next if name =~ /__MACOSX/
|
34
|
+
yield name if block_given?
|
35
|
+
|
36
|
+
next unless File.expand_path(name).start_with?(File.expand_path('.'))
|
37
|
+
|
38
|
+
FileUtils.mkdir_p(File.dirname(name))
|
39
|
+
entry.extract(name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def sys(*a, **kw)
|
44
|
+
env = {}
|
45
|
+
kw.each { |k, v| env[k.to_s] = v }
|
46
|
+
|
47
|
+
system(env, *a)
|
48
|
+
rescue Interrupt
|
49
|
+
# allow interrupt out of the subprocess but capture it for ramekin itself
|
50
|
+
end
|
51
|
+
|
52
|
+
def executable?(path)
|
53
|
+
stat = File.stat(path)
|
54
|
+
stat.file? && stat.executable?
|
55
|
+
rescue SystemCallError
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
def config_string(key)
|
60
|
+
s = RbConfig::MAKEFILE_CONFIG[key]
|
61
|
+
if block_given?
|
62
|
+
yield s
|
63
|
+
else
|
64
|
+
s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def find_executable(bin, path=ENV['PATH'])
|
69
|
+
executable_file = proc do |name|
|
70
|
+
begin
|
71
|
+
rescue SystemCallError
|
72
|
+
else
|
73
|
+
next name if stat.file? and stat.executable?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
exts = config_string('EXECUTABLE_EXTS')&.split || config_string('EXEEXT') {|s| [s]}
|
78
|
+
if File.expand_path(bin) == bin
|
79
|
+
return bin if executable?(bin)
|
80
|
+
if exts
|
81
|
+
exts.each {|ext| executable?(file = bin + ext) and return file}
|
82
|
+
end
|
83
|
+
return nil
|
84
|
+
end
|
85
|
+
|
86
|
+
if path
|
87
|
+
path = path.split(File::PATH_SEPARATOR)
|
88
|
+
else
|
89
|
+
path = %w[/usr/local/bin /usr/ucb /usr/bin /bin]
|
90
|
+
end
|
91
|
+
|
92
|
+
path.each do |dir|
|
93
|
+
dir.sub!(/\A"(.*)"\z/m, '\1') if $mswin or $mingw
|
94
|
+
file = File.join(dir, bin)
|
95
|
+
return file if executable?(file)
|
96
|
+
|
97
|
+
(exts || []).each do |ext|
|
98
|
+
fname = file + ext
|
99
|
+
return fname if executable?(fname)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def cmake
|
106
|
+
@cmake ||= find_executable('cmake') \
|
107
|
+
or error! 'please install cmake to set up addmusick/asar.'
|
108
|
+
end
|
109
|
+
|
110
|
+
def make
|
111
|
+
@make ||= find_executable('make') \
|
112
|
+
or error! 'please install make to set up addmusick/asar.'
|
113
|
+
end
|
114
|
+
|
115
|
+
def git
|
116
|
+
@git ||= find_executable('git') \
|
117
|
+
or error! 'please install git to set up spct'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Ramekin
|
2
|
+
module Volume
|
3
|
+
extend self
|
4
|
+
|
5
|
+
BASE = Math.log(2) / 6
|
6
|
+
INF = 1.0/0
|
7
|
+
def volume_to_db(vol)
|
8
|
+
return -INF if vol == 0
|
9
|
+
Math.log((vol+1) / 256.0) / BASE
|
10
|
+
end
|
11
|
+
|
12
|
+
def db_to_volume(db)
|
13
|
+
(256.0 * Math.exp(BASE * db) - 1).clamp(0..)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/ramekin.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative 'ramekin/util'
|
4
|
+
require_relative 'ramekin/config'
|
5
|
+
require_relative 'ramekin/amk_setup'
|
6
|
+
require_relative 'ramekin/spc_player'
|
7
|
+
require_relative 'ramekin/errors'
|
8
|
+
require_relative 'ramekin/element'
|
9
|
+
require_relative 'ramekin/tokenizer'
|
10
|
+
require_relative 'ramekin/processor'
|
11
|
+
require_relative 'ramekin/macros'
|
12
|
+
require_relative 'ramekin/note_aggregator'
|
13
|
+
require_relative 'ramekin/legato'
|
14
|
+
require_relative 'ramekin/meta'
|
15
|
+
require_relative 'ramekin/channel_separator'
|
16
|
+
require_relative 'ramekin/volume'
|
17
|
+
require_relative 'ramekin/renderer'
|
18
|
+
require_relative 'ramekin/amk_runner'
|
19
|
+
require_relative 'ramekin/sample_pack'
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ramekin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5b
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- jneen
|
8
|
+
autorequire:
|
9
|
+
bindir: gembin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-02-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: strscan
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubyzip
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.13'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.13'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry-stack_explorer
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.6'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.6'
|
69
|
+
description:
|
70
|
+
email: jneen@jneen.net
|
71
|
+
executables:
|
72
|
+
- ramekin
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- README.md
|
77
|
+
- gembin/ramekin
|
78
|
+
- lib/ramekin.rb
|
79
|
+
- lib/ramekin/amk_runner.rb
|
80
|
+
- lib/ramekin/amk_runner/sample_groups.rb
|
81
|
+
- lib/ramekin/amk_setup.rb
|
82
|
+
- lib/ramekin/channel_separator.rb
|
83
|
+
- lib/ramekin/cli.rb
|
84
|
+
- lib/ramekin/config.rb
|
85
|
+
- lib/ramekin/element.rb
|
86
|
+
- lib/ramekin/errors.rb
|
87
|
+
- lib/ramekin/legato.rb
|
88
|
+
- lib/ramekin/macros.rb
|
89
|
+
- lib/ramekin/meta.rb
|
90
|
+
- lib/ramekin/note_aggregator.rb
|
91
|
+
- lib/ramekin/processor.rb
|
92
|
+
- lib/ramekin/renderer.rb
|
93
|
+
- lib/ramekin/sample_pack.rb
|
94
|
+
- lib/ramekin/spc_player.rb
|
95
|
+
- lib/ramekin/tokenizer.rb
|
96
|
+
- lib/ramekin/util.rb
|
97
|
+
- lib/ramekin/volume.rb
|
98
|
+
homepage: https://codeberg.org/jneen/ramekin
|
99
|
+
licenses:
|
100
|
+
- GPL-3.0-only
|
101
|
+
metadata:
|
102
|
+
source_code_uri: https://codeberg.org/jneen/ramekin
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 3.0.0
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubygems_version: 3.5.11
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: A tool for making SMW music with AddmusicK
|
122
|
+
test_files: []
|