ramekin 0.0.5b
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 42635a3432e6ba8fc0f53454dab984170902a215d2530834278b7c70e088b354
|
4
|
+
data.tar.gz: 265b13b18ce9a6840a5d550e675b2ed20d417d95edf01af57467a2b8cdd83c95
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5740cf99c3ba2ec568c2075d1ba40ee96a790c3617638e7c17f293051a0ad02803e4ebe7f20e8f76315050b2cfb55149918932eddad3675c3613795529b5f686
|
7
|
+
data.tar.gz: a42be3e89c445f84639fcbc963a199a9ebaf84b174dde945526914a49b881ca2b74ad8ee346527c2ffb460deb1a5a4ff3c331842d989a730807ca6dce61992a6
|
data/README.md
ADDED
@@ -0,0 +1,277 @@
|
|
1
|
+
# Ramekin
|
2
|
+
|
3
|
+
Ramekin is a pre-processor for AddMusicK syntax that is in very early development.
|
4
|
+
|
5
|
+
# Installation
|
6
|
+
|
7
|
+
1. [Install Ruby](https://ruby-lang.org). Windows users can use [RubyInstaller](https://rubyinstaller.org/).
|
8
|
+
2. [Download the script](https://codeberg.org/jneen/ramekin/releases).
|
9
|
+
3. Install the scripts:
|
10
|
+
- For Windows users, put both `ramekin` and `ramekin.bat` in `%LOCALAPPDATA\Microsoft\WindowsApps\`.
|
11
|
+
- For MacOS and Linux users, ignore `ramekin.bat` - make the script executable with `chmod +x` and put it somewhere on your PATH.
|
12
|
+
4. Run `ramekin setup` to set up dependencies and download sample packs. You will need about 90M of free space.
|
13
|
+
|
14
|
+
# Usage
|
15
|
+
|
16
|
+
```
|
17
|
+
usage: ramekin [command] [flags]
|
18
|
+
(default command: compile)
|
19
|
+
|
20
|
+
ramekin compile -i filename.rmk [flags]
|
21
|
+
flags:
|
22
|
+
-h --help
|
23
|
+
display this message
|
24
|
+
-i filename.rmk
|
25
|
+
set the input file
|
26
|
+
--txt filename.txt
|
27
|
+
output amk txt to this file
|
28
|
+
--spc filename.spc
|
29
|
+
attempt to invoke AddmusicK to output an SPC
|
30
|
+
requires ADDMUSICK_DIR and ASAR_COMMAND
|
31
|
+
to be set.
|
32
|
+
--play
|
33
|
+
play the generated SPC file. will automatically
|
34
|
+
set up an SPC player if none is detected.
|
35
|
+
--wav filename.wav
|
36
|
+
--wav filename.wav:N
|
37
|
+
(non-windows only)
|
38
|
+
render N seconds of the file to WAV.
|
39
|
+
|
40
|
+
ramekin package [flags]
|
41
|
+
flags:
|
42
|
+
--update
|
43
|
+
download BRR packages from SMW Central.
|
44
|
+
--search [text]
|
45
|
+
search for BRR filenames.
|
46
|
+
--list [text]
|
47
|
+
list all samples in packages matching [text].
|
48
|
+
|
49
|
+
ramekin setup [flags]
|
50
|
+
flags:
|
51
|
+
--amk
|
52
|
+
downloads and sets up AddMusicK and asar.
|
53
|
+
(requires cmake and make to be available)
|
54
|
+
--packages
|
55
|
+
downloads packages from SMW Central.
|
56
|
+
(equivalent to package --update)
|
57
|
+
--spc
|
58
|
+
downloads and sets up a default SPC player.
|
59
|
+
--all (default)
|
60
|
+
runs all of the above setup steps
|
61
|
+
--force
|
62
|
+
deletes and redownloads files instead of skipping.
|
63
|
+
```
|
64
|
+
|
65
|
+
# Configuration
|
66
|
+
|
67
|
+
The first time ramekin runs, it will create a configuration file in `~/.ramekin/config.yml` (where `~` is your [home directory](https://en.wikipedia.org/wiki/Home_directory)). Edit this file with your favourite text editor, and be sure to remove the `#` from the start of lines you want to take effect. If you want Ramekin to keep its files in some other place, override the `RAMEKIN_HOME` environment variable.
|
68
|
+
|
69
|
+
# If you already know AddmusicK
|
70
|
+
|
71
|
+
A list of some ramekin preprocessor features (see `spec/rmk` for some examples!):
|
72
|
+
|
73
|
+
* The metadata section (before any channels) is fairly different, I recommend reading the metadata part of the guide below.
|
74
|
+
* Macros ("replacements") are implemented in a much more robust way (*with* tokenization), and can no longer interrupt other tokens. The generated txt will not contain any replacements.
|
75
|
+
* Adopted the transposition syntax `_+N`, `_-N`, and `_0` from General MML. Compiles to `$fa$02$XX`.
|
76
|
+
* `#adsr:A,D,S,RR` syntax for ADSR. Also usable on an `#instrument` declaration, with the same syntax. A, D, and R are flipped so that higher numbers are longer times.
|
77
|
+
* `v+X` and `v-X` are volumes relative to the last `vXXX` command. Useful for separating expression from mixing.
|
78
|
+
* `yLX` and `yRX` commands for panning left and right. `yC` to set back to center. Way easier for me to remember.
|
79
|
+
* `#bend` followed by a note with any length, including ties, will bend for that note's duration. Continue the note with a non-collapsing tie `^^`. Octave changes, `l` commands, and other note modifiers are allowed here. This will compile to a correct `$DD` command.
|
80
|
+
```elisp
|
81
|
+
; bends to c two octaves up for two 16th notes, then continues for a quarter note
|
82
|
+
b8 #bend >>c16^16 ^^4
|
83
|
+
```
|
84
|
+
* Instruments are named rather than numbered. See `doc/smw-instruments.rmk` for a list.
|
85
|
+
* Switching instruments always sets that instrument's default octave, by default `o4`. It does *not* reset transposition (you can use `_0` for that).
|
86
|
+
* Samples are loaded directly from SMWCentral using `#pack` syntax:
|
87
|
+
|
88
|
+
```elisp
|
89
|
+
; references the BRR pack "Chrono Trigger" from SMW Central
|
90
|
+
#pack "Chrono Trigger"
|
91
|
+
|
92
|
+
; declares an instrument @kick with tuning parameters parsed from the BRR pack
|
93
|
+
#instrument @kick "Kick"
|
94
|
+
|
95
|
+
; parameters and default octave can be overridden
|
96
|
+
#instrument @crash "Crash Cymbal" o4 #tuning:1FBC #adsr:0,0,7,1F
|
97
|
+
```
|
98
|
+
|
99
|
+
* In ramekin, there is no difference between instrument `#adsr` syntax and the one in the pattern data. They are both compiled to their respective syntaxes in the txt.
|
100
|
+
|
101
|
+
* Tempo can alternately be declared with `#bpm:128`. This will be converted to the nearest `tXX` command. A major TODO is automatic `#halvetempo` etc.
|
102
|
+
* `p0,0` compiles to `$df`.
|
103
|
+
* "Legato tie" represented by `~`, which will use `$f4$01` at appropriate times to cause the note transition to be played legato.
|
104
|
+
* If you ever want to see what exactly Ramekin is generating, just use `--txt` and you can inspect the generated AddmusicK syntax manually!
|
105
|
+
|
106
|
+
# Syntax
|
107
|
+
|
108
|
+
Ramekin syntax is very similar to AddmusicK syntax. Comments begin with `;` and span to the end of the line, and most commands are either single characters, or directives marked with `#`.
|
109
|
+
|
110
|
+
## Metadata / preamble
|
111
|
+
|
112
|
+
The top of a Ramekin file, before the first channel is declared, is the *preamble*. Here, various commands can be used, starting with `#`.
|
113
|
+
|
114
|
+
### Metadata commands
|
115
|
+
|
116
|
+
These commands set important metadata headers for the SPC.
|
117
|
+
|
118
|
+
```elisp
|
119
|
+
#title "My cool songle"
|
120
|
+
#game "title of the game, if any"
|
121
|
+
#author "my name"
|
122
|
+
#comment "usually something about who ported the track"
|
123
|
+
```
|
124
|
+
|
125
|
+
Additionally, you can specify additional `#readme` text to be included in the packaged song:
|
126
|
+
|
127
|
+
```elisp
|
128
|
+
#readme "
|
129
|
+
Here is some very important information about running this track.
|
130
|
+
"
|
131
|
+
```
|
132
|
+
|
133
|
+
Finally, some general options can be set:
|
134
|
+
|
135
|
+
```elisp
|
136
|
+
; causes the track not to speed up when the timer is low
|
137
|
+
#option TempoImmunity
|
138
|
+
|
139
|
+
; sets the global volume, 0-255.
|
140
|
+
; Try to aim for -21 to -19 lufs to balance well with sound effects.
|
141
|
+
w150
|
142
|
+
|
143
|
+
; sets the bpm. (todo: mention the reasonable range for this)
|
144
|
+
#bpm:130
|
145
|
+
|
146
|
+
; echo settings. this syntax is likely to change in the future, because it sucks
|
147
|
+
#echo $ef$ff$df$df $f1$02$cf$01
|
148
|
+
```
|
149
|
+
|
150
|
+
### Instrument definitions
|
151
|
+
|
152
|
+
#### Default Instruments
|
153
|
+
|
154
|
+
Ramekin can use the `#default` and `#optimized` native sample groups for SMW. One of these directives must be included for sound effects to work properly in the game. Using native SMW instruments is the easiest way to get started quickly.
|
155
|
+
|
156
|
+
The `#default` sample group is implied if nothing else is specified, but you can use `#optimized` if you are running low on ARAM (more on that later).
|
157
|
+
|
158
|
+
To see the full list of available native instruments, see `doc/smw-instruments.rmk`
|
159
|
+
|
160
|
+
#### Sample Packs
|
161
|
+
|
162
|
+
To get more diverse sounds on the SNES, it is often nice to use samples, either ripped from other games or converted to BRR (the SNES's native sample format).
|
163
|
+
|
164
|
+
Ramekin allows you to extremely easily use BRR packs from SMW Central's repositories. These not only contain BRR samples, but also the tuning and ADSR data to make them sound great by default.
|
165
|
+
|
166
|
+
To load a pack from SMW Central, use `#pack` with the pack's name on SMW Central. For example, let's say I wanted to use samples from Chrono Trigger. I can search that with:
|
167
|
+
|
168
|
+
```console
|
169
|
+
ramekin package --list chrono
|
170
|
+
```
|
171
|
+
|
172
|
+
This will show us a pack named "Chrono Trigger" along with a bunch of files with a `.brr` extension. To use the pack, add:
|
173
|
+
|
174
|
+
```elisp
|
175
|
+
#pack "Chrono Trigger"
|
176
|
+
```
|
177
|
+
|
178
|
+
Then, to load, say, the sitar from the pack, we use:
|
179
|
+
|
180
|
+
```elisp
|
181
|
+
#instrument @sitar "Sitar.brr"
|
182
|
+
```
|
183
|
+
|
184
|
+
This defines a new instrument called `@sitar` to point to `Sitar.brr` with the default settings. You can also leave off the ".brr" extension if you like. Later, you can use the command `@sitar` to switch to this instrument.
|
185
|
+
|
186
|
+
After the instrument declaration, you can use several commands to change the default settings, such as `#adsr`, `#tuning` and `o`:
|
187
|
+
|
188
|
+
```elisp
|
189
|
+
#instrument @sitar #adsr:0,4,2,23 #tuning:1a2f o5
|
190
|
+
```
|
191
|
+
|
192
|
+
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.
|
193
|
+
|
194
|
+
## Channel Commands
|
195
|
+
|
196
|
+
With your metadata all set up, it's time to add some notes!
|
197
|
+
|
198
|
+
SNES audio is organized into 8 channels, numbered 0-7. But in the context of a romhack, channels 6 and 7 are reserved for sound effects.
|
199
|
+
|
200
|
+
**This is the biggest limitation of romhack music: You must generally arrange your music to fit on 6 channels.** Each channel can only play one note at a time - for chords, you must use multiple channels. However, channels can switch instruments extremely quickly - so things can be interleaved if you are clever with instrument switches.
|
201
|
+
|
202
|
+
### Notes
|
203
|
+
|
204
|
+
To start adding notes to a channel, use `#N`, where N is the channel number:
|
205
|
+
|
206
|
+
```elisp
|
207
|
+
#0
|
208
|
+
; now we're in channel 0!
|
209
|
+
```
|
210
|
+
|
211
|
+
Next, switch to an instrument, and add some notes:
|
212
|
+
|
213
|
+
```elisp
|
214
|
+
#0
|
215
|
+
@smwacousticbass
|
216
|
+
o3
|
217
|
+
c8d8e8f8
|
218
|
+
```
|
219
|
+
|
220
|
+
this plays 8th notes c, d, e, and f in octave 3 on the instrument `@smwacousticbass`.
|
221
|
+
|
222
|
+
For sharps and flats, use `+` and `-`, and for rests, use the special note `r`:
|
223
|
+
|
224
|
+
```elisp
|
225
|
+
#0 @smwacousticbass o5
|
226
|
+
d8 e-8 f+8 g8 r2
|
227
|
+
```
|
228
|
+
|
229
|
+
This plays d, e-flat, f-sharp, g in 8th notes, followed by a half-note rest. To convert this to an spc, save this to a file named `my-cool-song.rmk`, and run
|
230
|
+
|
231
|
+
```
|
232
|
+
ramekin my-cool-song.rmk --spc my-cool-song.spc
|
233
|
+
```
|
234
|
+
|
235
|
+
This will create a file called `my-cool-song.spc`, which you can open with an SPC player. Notice that it loops to the beginning by default! The default behaviour of the AddMusicK/smw music system is to loop as soon as the first defined channel runs out of notes.
|
236
|
+
|
237
|
+
To set the loop point somewhere that is not the beginning, use the `/` command:
|
238
|
+
|
239
|
+
```elisp
|
240
|
+
; will return to the / when it hits the loop point
|
241
|
+
#0 @smwacousticbass o5
|
242
|
+
d8 e-8 / f+8 g8 r2
|
243
|
+
```
|
244
|
+
|
245
|
+
For more complex note lengths, **dots** and **ties** are available. For example, `b8.` plays a `b` for a dotted eigth note length. This is equivalent to `b8^16`, which plays for an 8th note tied to a 16th note.
|
246
|
+
|
247
|
+
Because it is common to play a bunch of fast notes all the same length, the `l` command allows setting a default length to use when no note length is specified. So our example from before is equivalent to:
|
248
|
+
|
249
|
+
```elisp
|
250
|
+
#0 @smwacousticbass o5
|
251
|
+
l8 de-f+g r2
|
252
|
+
```
|
253
|
+
|
254
|
+
Personally, I tend to use `l16` across the whole track for consistency.
|
255
|
+
|
256
|
+
### Loops and loop calls
|
257
|
+
|
258
|
+
Just as in Addmusick, there are two kinds of loops:
|
259
|
+
|
260
|
+
* Regular loops, marked with `[ ]`, which can optionally be tagged with a number, as in `(1)[ ]`. These can not only loop immediately but also be called at arbitrary points.
|
261
|
+
* Super loops, marked with `[[ ]]`, which can only loop immediately, but can nest either inside or outside of regular loops.
|
262
|
+
|
263
|
+
This is best shown with an example:
|
264
|
+
|
265
|
+
```elisp
|
266
|
+
#0 @smwacousticbass l16
|
267
|
+
[ defg ]4 ; plays defg four times (regular loop)
|
268
|
+
[[ abcd ]]4 ; plays abcd four times (superloop)
|
269
|
+
* ; calls the last defined regular loop, in this case plays defg
|
270
|
+
(01)[ fedc ] ; defines a loop named (01), also plays fedc immediately
|
271
|
+
(01) ; calls the loop (01)
|
272
|
+
(01)2 ; calls the loop (01) twice
|
273
|
+
```
|
274
|
+
|
275
|
+
### Expression and effects
|
276
|
+
|
277
|
+
Volume can be set with `vXXX`, where `XXX` is a number between 0 and 255.
|
data/gembin/ramekin
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Editable default configs.
|
4
|
+
# These can be edited by packagers or by you if you're feeling spicy.
|
5
|
+
# You can also override these values with environment variables (of the same name),
|
6
|
+
# or with a file `config.yml` in RAMEKIN_HOME.
|
7
|
+
RAMEKIN_HOME = '~/.ramekin'
|
8
|
+
RAMEKIN_AMK_DIR = './addmusick'
|
9
|
+
RAMEKIN_ASAR_DIR = './asar'
|
10
|
+
RAMEKIN_SPC_PLAYER = '__auto__'
|
11
|
+
RAMEKIN_VERSION = Gem.loaded_specs['ramekin'].version.to_s
|
12
|
+
|
13
|
+
require_relative '../lib/ramekin'
|
14
|
+
require_relative '../lib/ramekin/cli'
|
15
|
+
|
16
|
+
Ramekin::CLI.main(*ARGV)
|
@@ -0,0 +1,65 @@
|
|
1
|
+
SAMPLE_GROUPS = <<~'___'
|
2
|
+
#default
|
3
|
+
{
|
4
|
+
"default/00 SMW @0.brr"!
|
5
|
+
"default/01 SMW @1.brr"!
|
6
|
+
"default/02 SMW @2.brr"!
|
7
|
+
"default/03 SMW @3.brr"!
|
8
|
+
"default/04 SMW @4.brr"!
|
9
|
+
"default/05 SMW @8.brr"!
|
10
|
+
"default/06 SMW @22.brr"!
|
11
|
+
"default/07 SMW @5.brr"!
|
12
|
+
"default/08 SMW @6.brr"!
|
13
|
+
"default/09 SMW @7.brr"!
|
14
|
+
"default/0A SMW @9.brr"!
|
15
|
+
"default/0B SMW @10.brr"!
|
16
|
+
"default/0C SMW @13.brr"!
|
17
|
+
"default/0D SMW @14.brr"
|
18
|
+
"default/0E SMW @29.brr"!
|
19
|
+
"default/0F SMW @21.brr"
|
20
|
+
"default/10 SMW @12.brr"!
|
21
|
+
"default/11 SMW @17.brr"
|
22
|
+
"default/12 SMW @15.brr"!
|
23
|
+
"default/13 SMW Thunder.brr"!
|
24
|
+
}
|
25
|
+
|
26
|
+
#optimized
|
27
|
+
{
|
28
|
+
"optimized/00 SMW @0.brr"!
|
29
|
+
"optimized/01 SMW @1.brr"!
|
30
|
+
"optimized/02 SMW @2.brr"!
|
31
|
+
"optimized/03 SMW @3.brr"!
|
32
|
+
"optimized/04 SMW @4.brr"!
|
33
|
+
"optimized/05 SMW @8.brr"!
|
34
|
+
"optimized/06 SMW @22.brr"!
|
35
|
+
"optimized/07 SMW @5.brr"!
|
36
|
+
"optimized/08 SMW @6.brr"!
|
37
|
+
"optimized/09 SMW @7.brr"!
|
38
|
+
"optimized/0A SMW @9.brr"!
|
39
|
+
"optimized/0B SMW @10.brr"!
|
40
|
+
"optimized/0C SMW @13.brr"!
|
41
|
+
"optimized/0D SMW @14.brr"
|
42
|
+
"optimized/0E SMW @29.brr"!
|
43
|
+
"optimized/0F SMW @21.brr"
|
44
|
+
"optimized/10 SMW @12.brr"!
|
45
|
+
"optimized/11 SMW @17.brr"
|
46
|
+
"optimized/12 SMW @15.brr"!
|
47
|
+
"optimized/13 SMW Thunder.brr"!
|
48
|
+
}
|
49
|
+
|
50
|
+
#AMM
|
51
|
+
{
|
52
|
+
"EMPTY.brr"!
|
53
|
+
"EMPTY.brr"
|
54
|
+
"EMPTY.brr"
|
55
|
+
"EMPTY.brr"
|
56
|
+
"EMPTY.brr"
|
57
|
+
"EMPTY.brr"
|
58
|
+
"EMPTY.brr"
|
59
|
+
"EMPTY.brr"
|
60
|
+
"EMPTY.brr"
|
61
|
+
"EMPTY.brr"
|
62
|
+
"EMPTY.brr"
|
63
|
+
"EMPTY.brr"
|
64
|
+
}
|
65
|
+
___
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require_relative 'amk_runner/sample_groups'
|
3
|
+
|
4
|
+
module Ramekin
|
5
|
+
class AMKRunner
|
6
|
+
def initialize(filename, meta, txt)
|
7
|
+
@filename = File.expand_path(filename)
|
8
|
+
@amk_path = Ramekin.config.amk_dir
|
9
|
+
AMKSetup.setup! unless AMKSetup.setup_ok?
|
10
|
+
@meta = meta
|
11
|
+
@txt = txt
|
12
|
+
@workdir = Dir.mktmpdir
|
13
|
+
end
|
14
|
+
|
15
|
+
def basename
|
16
|
+
File.basename(@filename).chomp('.rmk')
|
17
|
+
end
|
18
|
+
|
19
|
+
def ln(src, dest)
|
20
|
+
# windows permissions don't allow linking from temp dirs
|
21
|
+
if Ramekin.config.windows?
|
22
|
+
FileUtils.cp_r(src, dest)
|
23
|
+
else
|
24
|
+
FileUtils.ln_s(src, dest)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def compile
|
29
|
+
FileUtils.mkdir_p("#@workdir/music/ramekin")
|
30
|
+
FileUtils.mkdir_p("#@workdir/SPCs")
|
31
|
+
FileUtils.mkdir_p("#@workdir/stats")
|
32
|
+
|
33
|
+
if Ramekin.config.windows?
|
34
|
+
ln("#@amk_path/AddmusicK.exe", "#@workdir/AddmusicK.exe")
|
35
|
+
ln("#@amk_path/asar.dll", "#@workdir/asar.dll")
|
36
|
+
else
|
37
|
+
ln("#@amk_path/AddmusicK", "#@workdir/AddmusicK")
|
38
|
+
ln(Ramekin.config.asar_lib, "#@workdir/libasar.#{Ramekin.config.lib_ext}")
|
39
|
+
end
|
40
|
+
|
41
|
+
ln("#@amk_path/asm", "#@workdir/asm")
|
42
|
+
|
43
|
+
FileUtils.mkdir_p("#@workdir/samples")
|
44
|
+
ln("#@amk_path/samples/default", "#@workdir/samples/default")
|
45
|
+
ln("#@amk_path/samples/optimized", "#@workdir/samples/optimized")
|
46
|
+
ln("#@amk_path/samples/EMPTY.brr", "#@workdir/samples/EMPTY.brr")
|
47
|
+
if @meta.instruments.any?
|
48
|
+
sample_dir = "#@workdir/samples/#{basename}"
|
49
|
+
FileUtils.mkdir_p(sample_dir)
|
50
|
+
|
51
|
+
@meta.instruments.each do |inst|
|
52
|
+
ln(inst.sample_path, "#{sample_dir}/#{inst.sample_name}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
File.write("#@workdir/music/ramekin/#{basename}.txt", @txt)
|
57
|
+
File.write("#@workdir/Addmusic_sample groups.txt", SAMPLE_GROUPS)
|
58
|
+
File.write("#@workdir/Addmusic_list.txt", <<~___)
|
59
|
+
Globals:
|
60
|
+
01 ramekin/#{basename}.txt
|
61
|
+
___
|
62
|
+
File.write("#@workdir/Addmusic_sound effects.txt", '')
|
63
|
+
|
64
|
+
Dir.chdir(@workdir) do
|
65
|
+
system "./AddmusicK -norom ramekin/#{basename}.txt"
|
66
|
+
res = $?
|
67
|
+
binding.pry unless res.success?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def export(outdir)
|
72
|
+
Dir.chdir outdir do
|
73
|
+
FileUtils.cp(@filename, "./#{basename}.rmk")
|
74
|
+
FileUtils.cp(spc_file, "./#{basename}.spc")
|
75
|
+
FileUtils.cp(stats_file, "./#{basename}.stats.txt")
|
76
|
+
File.write("./#{basename}.txt", @txt)
|
77
|
+
|
78
|
+
FileUtils.mkdir_p("samples/#{basename}")
|
79
|
+
@meta.instruments.each do |inst|
|
80
|
+
FileUtils.cp(inst.sample_path, "./samples/#{basename}/#{inst.sample_name}")
|
81
|
+
end
|
82
|
+
|
83
|
+
packs = @meta.instruments.map(&:pack).uniq
|
84
|
+
|
85
|
+
pack_info = packs.map do |pack|
|
86
|
+
out = StringIO.new
|
87
|
+
out << " #{pack.name}"
|
88
|
+
out << " (posted by #{pack.authors.join(', ')})" if pack.authors.any?
|
89
|
+
out << "\n"
|
90
|
+
|
91
|
+
out << " - #{pack.url}"
|
92
|
+
out.string
|
93
|
+
end.join("\n\n")
|
94
|
+
|
95
|
+
readme = @meta.readme&.value || '(you should edit this space before publishing)'
|
96
|
+
|
97
|
+
File.write("./README.txt", <<~README)
|
98
|
+
#{readme}
|
99
|
+
|
100
|
+
--
|
101
|
+
|
102
|
+
#{basename}.txt was composed as #{basename}.rmk with Ramekin
|
103
|
+
https://codeberg.org/jneen/ramekin
|
104
|
+
|
105
|
+
Sample sources:
|
106
|
+
#{pack_info}
|
107
|
+
README
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def spc_file
|
112
|
+
"#@workdir/SPCs/#{basename}.spc"
|
113
|
+
end
|
114
|
+
|
115
|
+
def stats_file
|
116
|
+
"#@workdir/stats/#{basename}.txt"
|
117
|
+
end
|
118
|
+
|
119
|
+
def spc
|
120
|
+
File.read(spc_file)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Ramekin
|
6
|
+
module AMKSetup
|
7
|
+
def self.setup!(**opts)
|
8
|
+
Dir.chdir(HOME) do
|
9
|
+
if Ramekin.config.windows?
|
10
|
+
WindowsSetup.new(**opts).setup!
|
11
|
+
else
|
12
|
+
NormalSetup.new(**opts).setup!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.setup_ok?
|
18
|
+
if Ramekin.config.windows?
|
19
|
+
File.exist?("#{Ramekin.config.amk_dir}/AddmusicK.exe")
|
20
|
+
else
|
21
|
+
Ramekin.config.try_asar_lib && File.executable?("#{Ramekin.config.amk_dir}/Addmusick")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Setup
|
26
|
+
include Util
|
27
|
+
|
28
|
+
def initialize(force: false)
|
29
|
+
@force = force
|
30
|
+
end
|
31
|
+
|
32
|
+
def error!(msg)
|
33
|
+
$stderr.puts(msg)
|
34
|
+
exit 1
|
35
|
+
end
|
36
|
+
|
37
|
+
def latest_tag(repo, host='github.com')
|
38
|
+
host = 'api.github.com' if host == 'github.com'
|
39
|
+
host = 'codeberg.org/api/v1' if host == 'codeberg.org'
|
40
|
+
|
41
|
+
tag_info = JSON.parse(URI.open("https://#{host}/repos/#{repo}/git/refs/tags").read)
|
42
|
+
tag_info.last['ref']
|
43
|
+
end
|
44
|
+
|
45
|
+
def unpack_repo_source!(repo, dir, host: 'github.com')
|
46
|
+
clear_dir(dir, force: @force) or return false
|
47
|
+
|
48
|
+
tag = latest_tag(repo, host)
|
49
|
+
tag = File.basename(tag) if host == 'codeberg.org'
|
50
|
+
|
51
|
+
source_url = "https://#{host}/#{repo}/archive/#{tag}.zip"
|
52
|
+
|
53
|
+
Dir.chdir(dir) do
|
54
|
+
zip = URI.open(source_url).read
|
55
|
+
$stderr.puts "extracting #{repo} from #{source_url}"
|
56
|
+
$stderr.puts "into #{dir}"
|
57
|
+
unpack_zip_here(zip) do |name|
|
58
|
+
$stderr.puts " -> #{name}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class WindowsSetup < Setup
|
67
|
+
def setup!
|
68
|
+
clear_dir(Ramekin.config.amk_dir, force: @force) or return false
|
69
|
+
|
70
|
+
Dir.chdir Ramekin.config.amk_dir do
|
71
|
+
amk_zip = URI.open('https://dl.smwcentral.net/37906/AddmusicK%201.0.11.zip').read
|
72
|
+
unpack_zip_here(amk_zip)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class NormalSetup < Setup
|
78
|
+
def setup!
|
79
|
+
setup_asar!
|
80
|
+
setup_amk!
|
81
|
+
end
|
82
|
+
|
83
|
+
def setup_asar!
|
84
|
+
unpack_repo_source!('RPGHacker/asar', Ramekin.config.asar_dir) or return
|
85
|
+
|
86
|
+
Dir.chdir(Ramekin.config.asar_dir) do
|
87
|
+
system(cmake, 'src')
|
88
|
+
error! "failed to configure asar" unless $?.success?
|
89
|
+
system(make)
|
90
|
+
error! "failed to compile asar" unless $?.success?
|
91
|
+
|
92
|
+
lib_file = Dir.glob('asar/lib/*').find do |entry|
|
93
|
+
next false unless File.file?(entry)
|
94
|
+
next false if File.symlink?(entry)
|
95
|
+
entry.end_with?('.dylib') || entry.end_with?('.so') || entry.end_with?('.dll')
|
96
|
+
end
|
97
|
+
|
98
|
+
@asar_lib_file = File.expand_path(lib_file, Ramekin.config.asar_dir)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Using AddMusicKFF as it is the only one that seems to compile cleanly
|
103
|
+
# outside of Windows.
|
104
|
+
def setup_amk!
|
105
|
+
unpack_repo_source!('KungFuFurby/AddMusicKFF', Ramekin.config.amk_dir) or return
|
106
|
+
|
107
|
+
Dir.chdir(Ramekin.config.amk_dir) do
|
108
|
+
system(make)
|
109
|
+
|
110
|
+
# link in the silly asar lib file
|
111
|
+
FileUtils.ln_s(@asar_lib_file, '.')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Ramekin
|
2
|
+
class ChannelSeparator
|
3
|
+
def self.parse(stream)
|
4
|
+
new.tap { |x| x.parse(stream) }
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :preamble, :channels, :meta
|
8
|
+
def initialize
|
9
|
+
@preamble = []
|
10
|
+
@channels = []
|
11
|
+
|
12
|
+
@current = @preamble
|
13
|
+
end
|
14
|
+
|
15
|
+
def preamble?
|
16
|
+
@current.equal?(@preamble)
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse(stream)
|
20
|
+
stream.each do |el|
|
21
|
+
if Token === el && el.type == :channel
|
22
|
+
@meta = Meta.new(@preamble.dup).tap(&:parse) if preamble?
|
23
|
+
@meta.parse
|
24
|
+
|
25
|
+
@current = (@channels[el.value.to_i] ||= [])
|
26
|
+
end
|
27
|
+
|
28
|
+
next (@current << el) if preamble?
|
29
|
+
|
30
|
+
if el.type == :instrument && el.value !~ /\A\d+\z/
|
31
|
+
el.meta[:inst] = @meta.instruments.find do |inst|
|
32
|
+
inst.name.value == el.value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
@current << el
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|