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.
@@ -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
@@ -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: []