badline 0.6.1 → 0.7.0
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 +4 -4
- data/CHANGELOG.md +37 -0
- data/README.md +125 -46
- data/exe/badline-ruby +9 -1
- data/lib/badline/address_bus/extra_sids.rb +30 -0
- data/lib/badline/address_bus/sid_slots.rb +42 -0
- data/lib/badline/address_bus.rb +5 -0
- data/lib/badline/audio/aiff.rb +2 -2
- data/lib/badline/audio/bare_player.rb +23 -12
- data/lib/badline/audio/cli.rb +153 -58
- data/lib/badline/audio/jukebox.rb +121 -36
- data/lib/badline/audio/machine_player.rb +25 -10
- data/lib/badline/audio/pcm_writer.rb +10 -6
- data/lib/badline/audio/playback.rb +6 -5
- data/lib/badline/audio/queued_tune.rb +89 -0
- data/lib/badline/audio/renderer.rb +57 -10
- data/lib/badline/audio/stereo.rb +97 -0
- data/lib/badline/audio/terminal.rb +39 -6
- data/lib/badline/audio/wav.rb +1 -1
- data/lib/badline/audio.rb +2 -0
- data/lib/badline/frontend/audio_sink.rb +6 -5
- data/lib/badline/frontend/boot.rb +1 -1
- data/lib/badline/frontend/buttons.rb +90 -0
- data/lib/badline/frontend/filter_view.rb +96 -0
- data/lib/badline/frontend/info_view.rb +120 -0
- data/lib/badline/frontend/painter.rb +175 -0
- data/lib/badline/frontend/player_commands.rb +33 -0
- data/lib/badline/frontend/player_footer.rb +86 -0
- data/lib/badline/frontend/player_header.rb +60 -0
- data/lib/badline/frontend/player_screen.rb +124 -0
- data/lib/badline/frontend/player_state.rb +67 -0
- data/lib/badline/frontend/player_window.rb +228 -0
- data/lib/badline/frontend/scope.rb +42 -0
- data/lib/badline/frontend/sid_history.rb +175 -0
- data/lib/badline/frontend/sid_view.rb +140 -0
- data/lib/badline/frontend/visualizer_view.rb +101 -0
- data/lib/badline/frontend.rb +14 -0
- data/lib/badline/media/queue.rb +158 -0
- data/lib/badline/media.rb +13 -8
- data/lib/badline/options/help.rb +34 -5
- data/lib/badline/options/option.rb +1 -1
- data/lib/badline/options/table.rb +4 -2
- data/lib/badline/options/validation.rb +54 -0
- data/lib/badline/options.rb +62 -43
- data/lib/badline/sdl.rb +16 -6
- data/lib/badline/sid/catch_up.rb +11 -1
- data/lib/badline/sid/filter.rb +9 -0
- data/lib/badline/sid/voice.rb +6 -0
- data/lib/badline/sid/waveform.rb +20 -5
- data/lib/badline/sid.rb +35 -3
- data/lib/badline/storage/hvsc.rb +41 -0
- data/lib/badline/storage/sid_file.rb +45 -25
- data/lib/badline/storage/song_lengths.rb +6 -29
- data/lib/badline/storage/stil/credit.rb +50 -0
- data/lib/badline/storage/stil.rb +126 -0
- data/lib/badline/storage.rb +8 -0
- data/lib/badline/version.rb +1 -1
- metadata +24 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 41235fb28f9f33c9a88333beec1c09cd9825d45875f24d22b8cb5f66f9528467
|
|
4
|
+
data.tar.gz: c214b37efce97c404dcd0838283e65e0f42aaa86a8376f18ef2323933b01b2f2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ab2c530282a35d8ac65c217d6901d46c6f844cde1d7c7ca143acb8ea8f05ac08d1da6b8ee290fb41bd016ce2079e308bfd5c5f338be18ed269be693612e07a50
|
|
7
|
+
data.tar.gz: 6c27c6d41e73e495bb27e536fae7424b5524c12b9c1d12cdb3bccc2efb3a73be6137e5209c4356394594d71ceaa205309989f193344003a3ed65245df0ec74fa
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.0](https://github.com/elektronaut/badline/compare/v0.6.1...v0.7.0) (2026-10-02)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* call a .sid tune's parts subtunes everywhere
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* add a badline sid subcommand that plays tunes and directories ([7dfc21b](https://github.com/elektronaut/badline/commit/7dfc21b5e4ba47cd37a4593da61e95bb1d2329ab))
|
|
13
|
+
* drop .sid files and folders on the SID player to queue them ([cc1f434](https://github.com/elektronaut/badline/commit/cc1f434b7404ded067ef43d1d3d5aa53d338de4c))
|
|
14
|
+
* end a subtune with no known length when it falls silent ([46c4f10](https://github.com/elektronaut/badline/commit/46c4f1064f6382872ec22989253bc9c159f78dcf))
|
|
15
|
+
* open a .sid tune, or no tune at all, in the SID player ([396c010](https://github.com/elektronaut/badline/commit/396c010ee626f95fbb71ced8bf3905909c470c8e))
|
|
16
|
+
* play 2SID and 3SID tunes in stereo in the SID player ([7c72eef](https://github.com/elektronaut/badline/commit/7c72eeff7e5e7b1c56286f9bf6030ae3408f7176))
|
|
17
|
+
* play a queue of tunes, with shuffle, loop and play-all subtunes ([d163106](https://github.com/elektronaut/badline/commit/d163106eea99d90f6ffe74fb91474a2aca4c64eb))
|
|
18
|
+
* play sid tunes in a window that shows what the SID is doing ([60bfd3f](https://github.com/elektronaut/badline/commit/60bfd3f75fe82d928afa374d257d3d21e5ac29ad))
|
|
19
|
+
* seek forward from where the subtune is, not from its start ([34059e8](https://github.com/elektronaut/badline/commit/34059e8f9b12cab44c94124e6455fef9c915d8eb))
|
|
20
|
+
* seek within a subtune, and let the slow warning lapse ([b98ac6e](https://github.com/elektronaut/badline/commit/b98ac6eb6f9c0ce088545f55cc5f2445dd38dd2d))
|
|
21
|
+
* show a tune's STIL entry in the SID player ([82d9dca](https://github.com/elektronaut/badline/commit/82d9dca26d7cefe853a6af41e50fc723a3e90170))
|
|
22
|
+
* show each voice's live output in the SID view ([c61a128](https://github.com/elektronaut/badline/commit/c61a1289983887c0175fca3dc4c37b470e96de92))
|
|
23
|
+
* show HVSC's STIL in the SID player ([bd55c0c](https://github.com/elektronaut/badline/commit/bd55c0c87695ba8cc84dbfd51ab2119a97eccded))
|
|
24
|
+
* show where a seek is headed on the SID player's bar ([1a84b37](https://github.com/elektronaut/badline/commit/1a84b3746a960dba28dc9c789b3d2cd1520d2f94))
|
|
25
|
+
* step subtunes and tunes on separate keys, add --all-subtunes ([eaef338](https://github.com/elektronaut/badline/commit/eaef3384e6205163e2a4b63f5aa8d9204fe5190f))
|
|
26
|
+
* switch a SID between the 6581 and 8580 while it runs ([5920391](https://github.com/elektronaut/badline/commit/592039123d7936b8d22c8fd5f144a9bc9a8f383b))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Bug Fixes
|
|
30
|
+
|
|
31
|
+
* build the native badline with a Spinel that keeps an array joined mid-expression alive ([d2c4eb2](https://github.com/elektronaut/badline/commit/d2c4eb20d6b80494fb2a20105c0532f3778b205c))
|
|
32
|
+
* build the native badline with a Spinel that reports render progress ([58524ea](https://github.com/elektronaut/badline/commit/58524eaf4881661e7e0e9dd38c32071545bf1fae))
|
|
33
|
+
* show non-ASCII text from a .sid header in the native badline ([959bb6f](https://github.com/elektronaut/badline/commit/959bb6f7d07de4c90ac50ae51ae63f3c66601a78))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Performance Improvements
|
|
37
|
+
|
|
38
|
+
* draw the SID player's traces in one call each ([afcae15](https://github.com/elektronaut/badline/commit/afcae15ed273aa7a6dd49cb31e0f7180c56feb75))
|
|
39
|
+
|
|
3
40
|
## [0.6.1](https://github.com/elektronaut/badline/compare/v0.6.0...v0.6.1) (2026-09-30)
|
|
4
41
|
|
|
5
42
|
|
data/README.md
CHANGED
|
@@ -56,46 +56,53 @@ badline game.prg # Load and run a program
|
|
|
56
56
|
badline game.d64 # Mount a disk image as device 8 and load it
|
|
57
57
|
badline game.tap # Insert a tape and load it
|
|
58
58
|
badline game.crt # Attach a cartridge
|
|
59
|
-
badline tune.sid # Play a SID tune
|
|
59
|
+
badline tune.sid # Play a SID tune in the SID player
|
|
60
60
|
badline game.vsf # Restore a snapshot, from badline or VICE
|
|
61
61
|
badline ~/c64 # Mount a directory as device 8
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
`badline-ruby` takes the same media and the same options, with the
|
|
65
|
-
differences noted below.
|
|
66
|
-
|
|
67
64
|
Programs, disk and tape images and SID tunes start automatically, and
|
|
68
65
|
a cartridge starts itself. A mounted directory waits for you to `LOAD`
|
|
69
|
-
from it.
|
|
70
|
-
you can type the `LOAD` yourself. `--read-only` mounts a disk image
|
|
71
|
-
write-protected, so the drive reports `26,WRITE PROTECT ON` for any
|
|
72
|
-
write and the image file stays as it was. `--song N` picks a subtune of a
|
|
73
|
-
`.sid` file, `--sid 8580` fits the newer SID and `--reu 512` plugs in a
|
|
74
|
-
512K RAM Expansion Unit. `--ntsc` runs an NTSC C64, with the 6567R8
|
|
75
|
-
VIC-II, instead of a PAL one. `badline-ruby` also
|
|
76
|
-
takes `--disable-jit`, which runs without YJIT, otherwise switched on at
|
|
77
|
-
startup. `--help` lists the options.
|
|
66
|
+
from it.
|
|
78
67
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
68
|
+
`badline-ruby` takes the same media and the same options, with the
|
|
69
|
+
differences noted in the table. `--help` lists the options for either.
|
|
70
|
+
|
|
71
|
+
### Options
|
|
72
|
+
|
|
73
|
+
| Option | Effect |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| `--no-autostart` | Attach the media and stop at `READY.`, so you can type the `LOAD` yourself |
|
|
76
|
+
| `--read-only` | Mount a disk image write-protected. The drive reports `26,WRITE PROTECT ON` for any write and the image file stays as it was |
|
|
77
|
+
| `--true-drive` | Put an emulated 1541 on device 8 instead of the KERNAL traps. See [Media](#media) |
|
|
78
|
+
| `-s`, `--subtune N` | Pick a subtune of a `.sid` file, counting from 1 |
|
|
79
|
+
| `--sid 6581`, `--sid 8580` | Fit the older or newer SID. `--sid auto`, the default, takes a `.sid` tune's own |
|
|
80
|
+
| `--reu SIZE` | Plug in a RAM Expansion Unit of `SIZE` K: 128, 256, 512 (a 1750) or up to 16384 |
|
|
81
|
+
| `--ntsc` | Run an NTSC C64, with the 6567R8 VIC-II, instead of a PAL one |
|
|
82
|
+
| `--no-sound` | Don't play the SID (`badline` only, where sound is on by default) |
|
|
83
|
+
| `--sound` | Play the SID (`badline-ruby`, where sound is off by default) |
|
|
84
|
+
| `--no-vsync` | Pace the window by a timer, or by the sound while it plays, instead of the display's vsync |
|
|
85
|
+
| `--verbose` | Print the display's refresh rate, the sound's sample rate and the game controllers found as the window opens, then the frame rate and the time each frame takes, once a second |
|
|
86
|
+
| `--disable-jit` | Run without YJIT, otherwise switched on at startup (`badline-ruby` only) |
|
|
87
|
+
| `--version` | Show the version and what built it (`badline` only) |
|
|
88
|
+
| `-h`, `--help` | List the options |
|
|
85
89
|
|
|
86
90
|
Both run the same window. `badline` plays the SID through the host's
|
|
87
|
-
audio device, and `F10` mutes and unmutes it.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
audio device, and `F10` mutes and unmutes it. In `badline-ruby` the
|
|
92
|
+
machine runs below real time, so the sound stutters: it plays in bursts
|
|
93
|
+
with silent gaps between them, at the right pitch, and never slows the
|
|
94
|
+
emulation down.
|
|
95
|
+
|
|
96
|
+
### Scripted runs
|
|
97
|
+
|
|
98
|
+
| Option | Effect |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `--frames N` | Quit after N frames |
|
|
101
|
+
| `--unpaced` | Run as fast as it can, without vsync or pacing |
|
|
102
|
+
| `--at FRAME:EVENT` | Press keys, type, swap media or take a screenshot once `FRAME` frames have run |
|
|
103
|
+
| `--script FILE` | Run the events in `FILE`, one `FRAME:EVENT` a line |
|
|
104
|
+
| `--screenshot FILE` | Save the last frame as a `.bmp` |
|
|
105
|
+
| `--save-snapshot FILE` | Save the machine as a `.vsf` snapshot after the last frame |
|
|
99
106
|
|
|
100
107
|
```sh
|
|
101
108
|
badline --unpaced --frames 12000 --true-drive disk1.d64 \
|
|
@@ -106,10 +113,14 @@ badline --unpaced --frames 12000 --true-drive disk1.d64 \
|
|
|
106
113
|
`--help` lists the events, and [native/README.md](native/README.md#running)
|
|
107
114
|
describes them.
|
|
108
115
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
### ROMs
|
|
117
|
+
|
|
118
|
+
The KERNAL, BASIC and character ROMs come with the gem. To run other
|
|
119
|
+
images, such as a patched KERNAL, point `BADLINE_ROM_PATH` at a
|
|
120
|
+
directory that holds `kernal.rom`, `basic.rom` and `character.rom`,
|
|
121
|
+
plus `eapi/eapi-am29f040-14` if you attach EasyFlash cartridges. From
|
|
122
|
+
Ruby, `Badline.rom_path = dir` does the same before a
|
|
123
|
+
`Badline::Computer` is built, and `nil` restores the bundled set.
|
|
113
124
|
|
|
114
125
|
## Media
|
|
115
126
|
|
|
@@ -215,39 +226,107 @@ out and switches the machine off and on.
|
|
|
215
226
|
|
|
216
227
|
`badline-ruby --headless` plays a `.sid` tune on the host's audio
|
|
217
228
|
device without opening the window, and `--audio-out` renders it to a
|
|
218
|
-
16-bit PCM file instead. The file's extension picks the format, `.wav`
|
|
229
|
+
16-bit stereo PCM file instead. The file's extension picks the format, `.wav`
|
|
219
230
|
or `.aiff`. The native `badline` has both modes too.
|
|
220
231
|
|
|
221
232
|
```sh
|
|
233
|
+
badline sid ~/C64Music/MUSICIANS/H/Hubbard_Rob # play every tune below a directory
|
|
234
|
+
badline sid tune.sid other.sid # play a queue of tunes
|
|
222
235
|
badline-ruby --headless tune.sid # play, length from HVSC
|
|
223
236
|
badline-ruby --headless -s 3 tune.sid # play the third subtune
|
|
237
|
+
badline-ruby --headless --all-subtunes tune.sid # play on through every subtune
|
|
224
238
|
badline-ruby --seconds 180 tune.sid --audio-out out.aiff
|
|
225
239
|
badline-ruby -s 3 --rate 48000 tune.sid --audio-out out.wav
|
|
226
240
|
badline-ruby --headless --sid 8580 tune.sid
|
|
227
241
|
badline-ruby --filter-chunk 1 tune.sid --audio-out out.wav # exact filter, slower
|
|
228
242
|
```
|
|
229
243
|
|
|
244
|
+
`badline sid FILE|DIR...` plays a queue of tunes in the SID player's
|
|
245
|
+
window, in the order given, and a directory adds every `.sid` tune below
|
|
246
|
+
it in path order. `badline sid --headless` plays the queue in the
|
|
247
|
+
terminal instead. `badline sid` on its own opens the window with an
|
|
248
|
+
empty queue. Dropping `.sid` files or folders on the window adds them
|
|
249
|
+
to the end of the queue, and an empty queue starts playing them. It
|
|
250
|
+
takes
|
|
251
|
+
the options below except `--audio-out`, and `--subtune` picks the first
|
|
252
|
+
tune's subtune. `--sid auto`, the default, fits each of a tune's SIDs
|
|
253
|
+
the model its header names. A tune written for 2 or 3 SIDs plays on as
|
|
254
|
+
many, at the addresses its header gives, in stereo: SID 1 on the left,
|
|
255
|
+
SID 2 on the right and SID 3 in the centre. A tune on one SID plays the
|
|
256
|
+
same on both channels. A file that isn't a tune is skipped.
|
|
257
|
+
`badline tune.sid` plays the tune in the SID player too, unless an
|
|
258
|
+
option of the emulator's window, such as `--ntsc` or `--reu`, asks for
|
|
259
|
+
the machine: then a tune for one SID runs on the emulated C64, started
|
|
260
|
+
through a small driver after boot.
|
|
261
|
+
|
|
230
262
|
Both modes take the same options. The window's own, `--no-autostart`,
|
|
231
263
|
`--read-only`, `--sound`, `--true-drive`, `--reu`, `--ntsc` and
|
|
232
|
-
`--verbose`, don't apply to them. `--
|
|
233
|
-
does, and defaults to the tune's own start
|
|
264
|
+
`--verbose`, don't apply to them. `--subtune` (or `-s`) picks the subtune, counting from 1 as HVSC
|
|
265
|
+
does, and defaults to the tune's own start subtune. Playback asks the
|
|
234
266
|
device for 44.1 kHz and takes whatever rate it offers, unless `--rate`
|
|
235
267
|
says otherwise. Ctrl-C stops it.
|
|
236
268
|
|
|
237
|
-
Played on a terminal, `--headless`
|
|
238
|
-
release
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
269
|
+
Played on a terminal, `--headless` and `sid` show each tune's name,
|
|
270
|
+
author and release as it starts, then the tune's place in the queue,
|
|
271
|
+
the subtune number and the time played against the subtune's length. `--headless`
|
|
272
|
+
queues just the one tune. Each tune plays its own subtune: `--subtune`, or
|
|
273
|
+
the tune's start subtune. When that subtune ends the player goes on to the next
|
|
274
|
+
tune, and it stops after the last. `a`, or `--all-subtunes`, turns on
|
|
275
|
+
playing all subtunes, so that a subtune that ends goes on to the tune's next
|
|
276
|
+
subtune, and a tune stepped to starts on its first subtune.
|
|
277
|
+
|
|
278
|
+
→ and ← step to the tune's next and previous subtune, stopping at its
|
|
279
|
+
first and last. `n` and `p` step to the next and previous tune. `s`
|
|
280
|
+
turns shuffle on and off, which plays the queue's tunes in a random
|
|
281
|
+
order, and `l` turns looping on and off, so that the end of the queue
|
|
282
|
+
goes on to its start. `,` and `.` seek 10 seconds back and forward
|
|
283
|
+
within the subtune, which plays it again from its start and runs silently
|
|
284
|
+
up to the point asked for. Space pauses and `q` quits. The status line
|
|
285
|
+
shows which modes are on, and they last until the player quits.
|
|
286
|
+
|
|
287
|
+

|
|
288
|
+
|
|
289
|
+
The SID player's window is worked with the mouse. Its header shows the
|
|
290
|
+
tune's name, author and release, the tune the subtune covers at the
|
|
291
|
+
moment when HVSC's STIL credits one, following the times STIL gives, and
|
|
292
|
+
buttons that switch between three views and between the tune's own SID
|
|
293
|
+
model, the 6581 and the 8580, which changes the chips playing on the
|
|
294
|
+
spot. Its footer shows the time played on a bar you can click to seek,
|
|
295
|
+
and buttons that pause, step between tunes and between a tune's
|
|
296
|
+
subtunes, and turn shuffle, looping and all subtunes on and off. The
|
|
297
|
+
visualizer view shows each voice's note, and how far off it is in cents,
|
|
298
|
+
over a scope of the voice's output, and the mixed output below them. A
|
|
299
|
+
tune on more than one SID gets a row of voices for each SID, and the mix
|
|
300
|
+
splits into its left and right. The SID view shows one SID at a time,
|
|
301
|
+
and for a tune on more than one, a row of SID 1, 2 and 3 buttons at its
|
|
302
|
+
top picks which. It shows each voice's waveforms and the shape
|
|
303
|
+
they make, its control bits, pulse width, envelope settings, and the
|
|
304
|
+
envelope's level and stage, and the filter's modes, cutoff, resonance,
|
|
305
|
+
volume and its response on the chip playing. The INFO view shows the
|
|
306
|
+
tune's STIL entry and the subtune's, with a scroll bar, the mouse wheel
|
|
307
|
+
or the up and down keys for the long ones. The terminal's keys work in
|
|
308
|
+
the window too, along with Tab to switch views and `c` to step through
|
|
309
|
+
the SID models.
|
|
310
|
+
|
|
311
|
+
`--no-tui`, or output that isn't a terminal, gives plain progress
|
|
312
|
+
output instead and plays through the queue without the keys, while
|
|
313
|
+
`--audio-out` renders just the one subtune.
|
|
244
314
|
|
|
245
315
|
A `.sid` file doesn't store its length, so `badline-ruby` looks the
|
|
246
316
|
tune up by MD5 in HVSC's `Songlengths.md5`. It finds the database
|
|
247
317
|
through `--songlengths`, in a `DOCUMENTS` directory in any of the
|
|
248
318
|
tune's parent directories (the layout of an HVSC collection), or under
|
|
249
319
|
`$HVSC_BASE/DOCUMENTS`. Without a database or `--seconds` it runs for
|
|
250
|
-
60 seconds
|
|
320
|
+
60 seconds, but a subtune that falls silent for 5 seconds before then
|
|
321
|
+
ends there, when played and when rendered alike. Silent means the
|
|
322
|
+
output holds within 16 steps of one level, since a 6581 idles at a DC
|
|
323
|
+
offset rather than at zero. A subtune with a known length plays to its
|
|
324
|
+
length whatever it sounds like.
|
|
325
|
+
|
|
326
|
+
A tune in an HVSC collection also gets its entry in HVSC's `STIL.txt`,
|
|
327
|
+
found the same way: comments, covers, and subtune names and composers.
|
|
328
|
+
The tune's own fields follow its header, and each subtune's print as it
|
|
329
|
+
starts.
|
|
251
330
|
|
|
252
331
|
PSID tunes run on a CPU and RAM with only the SID clocked, at about
|
|
253
332
|
twice real time, so they play smoothly. RSID tunes set up their own
|
data/exe/badline-ruby
CHANGED
|
@@ -36,13 +36,21 @@ end
|
|
|
36
36
|
|
|
37
37
|
if options.headless?
|
|
38
38
|
require "badline/audio/console"
|
|
39
|
+
console = if options.player_window?
|
|
40
|
+
lambda do |input:, output:|
|
|
41
|
+
load_sdl
|
|
42
|
+
Badline::Frontend::PlayerWindow.new(input:, output:)
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
Badline::Audio::Console.method(:new)
|
|
46
|
+
end
|
|
39
47
|
sink = lambda do |rate:, exact_rate:|
|
|
40
48
|
load_sdl
|
|
41
49
|
Badline::Frontend::AudioSink.new(rate:, exact_rate:)
|
|
42
50
|
rescue Badline::FFI::Error => e
|
|
43
51
|
raise Badline::Audio::Playback::DeviceError, e.message
|
|
44
52
|
end
|
|
45
|
-
exit Badline::Audio::CLI.run(options, sink:, console:
|
|
53
|
+
exit Badline::Audio::CLI.run(options, sink:, console:)
|
|
46
54
|
end
|
|
47
55
|
|
|
48
56
|
begin
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class AddressBus
|
|
5
|
+
# The second and third SIDs the SID player fits for a tune written for
|
|
6
|
+
# them, each over its 32 bytes of the I/O area (SIDSlots). The SID
|
|
7
|
+
# player's machine has nothing else in I/O 1 and 2, so the rest of a
|
|
8
|
+
# page there reads the open bus.
|
|
9
|
+
module ExtraSIDs
|
|
10
|
+
# Fits a SID at the address it was built at, where it takes its 32
|
|
11
|
+
# bytes over from whatever maps there.
|
|
12
|
+
def add_sid(sid)
|
|
13
|
+
page = sid.start >> 8
|
|
14
|
+
slots = @sid_slots.find { |candidate| candidate.page == page }
|
|
15
|
+
if slots.nil?
|
|
16
|
+
slots = SIDSlots.new(page, @sid, @open_bus)
|
|
17
|
+
@sid_slots << slots
|
|
18
|
+
end
|
|
19
|
+
slots.place(sid)
|
|
20
|
+
update_overlays!
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def map_extra_sids
|
|
26
|
+
@sid_slots.each { |slots| @read_pages[slots.page] = @write_pages[slots.page] = slots }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class AddressBus
|
|
5
|
+
# Stands in for a page of I/O that a second or third SID shares: each 32
|
|
6
|
+
# bytes of it go to the SID placed there, or else to the first SID's
|
|
7
|
+
# mirrors in `$d400-$d7ff`, and to the open bus in I/O 1 and 2.
|
|
8
|
+
class SIDSlots
|
|
9
|
+
attr_reader :page
|
|
10
|
+
|
|
11
|
+
def initialize(page, first, open_bus)
|
|
12
|
+
@page = page
|
|
13
|
+
@io = page >= 0xde
|
|
14
|
+
@first = first
|
|
15
|
+
@open_bus = open_bus
|
|
16
|
+
@sids = []
|
|
17
|
+
@slots = Array.new(8, -1)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def place(sid)
|
|
21
|
+
@slots[(sid.start >> 5) & 7] = @sids.size
|
|
22
|
+
@sids << sid
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def peek(addr)
|
|
26
|
+
index = @slots[(addr >> 5) & 7]
|
|
27
|
+
return @sids[index].peek(addr) unless index.negative?
|
|
28
|
+
|
|
29
|
+
@io ? @open_bus.peek(addr) : @first.peek(addr)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def poke(addr, value)
|
|
33
|
+
index = @slots[(addr >> 5) & 7]
|
|
34
|
+
if !index.negative?
|
|
35
|
+
@sids[index].poke(addr, value)
|
|
36
|
+
elsif !@io
|
|
37
|
+
@first.poke(addr, value)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/badline/address_bus.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "badline/address_bus/saved_state"
|
|
4
|
+
require "badline/address_bus/sid_slots"
|
|
5
|
+
require "badline/address_bus/extra_sids"
|
|
4
6
|
require "badline/address_bus/ultimax_pages"
|
|
5
7
|
|
|
6
8
|
module Badline
|
|
@@ -32,6 +34,7 @@ module Badline
|
|
|
32
34
|
class AddressBus
|
|
33
35
|
include Addressable
|
|
34
36
|
include UltimaxPages
|
|
37
|
+
include ExtraSIDs
|
|
35
38
|
|
|
36
39
|
# I/O 1 and 2, and the Ultimax holes, with nothing on the bus. A read
|
|
37
40
|
# picks up the byte the VIC fetched in the preceding phi1 half-cycle,
|
|
@@ -82,6 +85,7 @@ module Badline
|
|
|
82
85
|
@control_ports.port_a_source = @cia1
|
|
83
86
|
@cia1.on_port_b4_change { |high| @vic.lightpen_level(high) }
|
|
84
87
|
@sid = SID.new(model: sid_model, pots: @control_ports)
|
|
88
|
+
@sid_slots = []
|
|
85
89
|
|
|
86
90
|
@datasette = Datasette.new
|
|
87
91
|
@datasette.on_flag { @cia1.flag! }
|
|
@@ -252,6 +256,7 @@ module Badline
|
|
|
252
256
|
@read_pages[0xd7] = @write_pages[0xd7] = @debug_register if @debug_register
|
|
253
257
|
@read_pages[0xdf] = @write_pages[0xdf] = @reu if @reu
|
|
254
258
|
map_cartridge_io if @cartridge
|
|
259
|
+
map_extra_sids unless @sid_slots.empty?
|
|
255
260
|
end
|
|
256
261
|
|
|
257
262
|
def map_cartridge_io
|
data/lib/badline/audio/aiff.rb
CHANGED
|
@@ -13,14 +13,14 @@ module Badline
|
|
|
13
13
|
|
|
14
14
|
def header
|
|
15
15
|
["FORM", 0, "AIFF",
|
|
16
|
-
"COMM", 18,
|
|
16
|
+
"COMM", 18, channels, 0, 16].pack("a4Na4a4NnNn") +
|
|
17
17
|
extended(rate) +
|
|
18
18
|
["SSND", 0, 0, 0].pack("a4NNN")
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def patch
|
|
22
22
|
write_at(4, [46 + data_size].pack("N"))
|
|
23
|
-
write_at(22, [
|
|
23
|
+
write_at(22, [frames].pack("N"))
|
|
24
24
|
write_at(42, [8 + data_size].pack("N"))
|
|
25
25
|
end
|
|
26
26
|
|
|
@@ -44,12 +44,12 @@ module Badline
|
|
|
44
44
|
# The KERNAL's PAL/NTSC flag, 1 for PAL.
|
|
45
45
|
VIDEO_STANDARD = 0x02a6
|
|
46
46
|
|
|
47
|
-
attr_reader :sid
|
|
47
|
+
attr_reader :sid, :stereo
|
|
48
48
|
|
|
49
|
-
def initialize(tune,
|
|
49
|
+
def initialize(tune, subtune: nil, sid_models: tune.sid_models)
|
|
50
50
|
@tune = tune
|
|
51
|
-
@
|
|
52
|
-
@bus = AddressBus.new(sid_model:)
|
|
51
|
+
@subtune = (subtune || tune.start_subtune).clamp(1, tune.subtunes) - 1
|
|
52
|
+
@bus = AddressBus.new(sid_model: sid_models[0])
|
|
53
53
|
# The CPU port as the KERNAL leaves it, which a PSID tune expects.
|
|
54
54
|
@bus.poke(0x00, 0x2f)
|
|
55
55
|
@bus.poke(0x01, 0x37)
|
|
@@ -57,28 +57,38 @@ module Badline
|
|
|
57
57
|
@bus.cia1.timer_a_latch = tune.ntsc? ? NTSC_KERNAL_TIMER : KERNAL_TIMER
|
|
58
58
|
@cpu = CPU.new(@bus)
|
|
59
59
|
@sid = @bus.sid
|
|
60
|
+
@stereo = Stereo.new(@bus, tune, sid_models)
|
|
61
|
+
@extras = @stereo.extras
|
|
62
|
+
@multi = !@extras.empty?
|
|
60
63
|
@idle = false
|
|
61
64
|
end
|
|
62
65
|
|
|
63
66
|
def start
|
|
64
67
|
@bus.ram.write(@tune.load_address, @tune.data)
|
|
65
68
|
@bus.ram.write(stub_address, stub)
|
|
66
|
-
@
|
|
69
|
+
@stereo.synthesize!
|
|
67
70
|
@cpu.status.interrupt = true
|
|
68
71
|
@cpu.program_counter = idle_address
|
|
69
72
|
install_dispatch
|
|
70
|
-
call(@tune.init_address, @
|
|
73
|
+
call(@tune.init_address, @subtune)
|
|
71
74
|
settle
|
|
72
75
|
end
|
|
73
76
|
|
|
74
77
|
# Advances one call of play, or `budget` cycles if that is shorter,
|
|
75
|
-
# then yields whatever the
|
|
76
|
-
# advanced.
|
|
78
|
+
# then yields whatever the SIDs recorded over it, in stereo. Returns
|
|
79
|
+
# the cycles advanced.
|
|
77
80
|
def frame(budget, &)
|
|
78
81
|
call(@tune.play_address)
|
|
79
82
|
cycles = [budget, period].min
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
if @multi
|
|
84
|
+
cycles.times { step }
|
|
85
|
+
else
|
|
86
|
+
cycles.times do
|
|
87
|
+
@sid.cycle!
|
|
88
|
+
@cpu.cycle!
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
@stereo.mix(&)
|
|
82
92
|
cycles
|
|
83
93
|
end
|
|
84
94
|
|
|
@@ -86,10 +96,10 @@ module Badline
|
|
|
86
96
|
|
|
87
97
|
private
|
|
88
98
|
|
|
89
|
-
# A video frame, or for a CIA-timed
|
|
99
|
+
# A video frame, or for a CIA-timed subtune one period of CIA 1 timer A,
|
|
90
100
|
# which init and play are both free to reprogram.
|
|
91
101
|
def period
|
|
92
|
-
return (@tune.ntsc? ? NTSC_FRAME_CYCLES : FRAME_CYCLES) unless @tune.cia_timed?(@
|
|
102
|
+
return (@tune.ntsc? ? NTSC_FRAME_CYCLES : FRAME_CYCLES) unless @tune.cia_timed?(@subtune + 1)
|
|
93
103
|
|
|
94
104
|
@bus.cia1.timer_a_latch + 1
|
|
95
105
|
end
|
|
@@ -117,6 +127,7 @@ module Badline
|
|
|
117
127
|
|
|
118
128
|
def step
|
|
119
129
|
@sid.cycle!
|
|
130
|
+
@extras.each(&:cycle!) if @multi
|
|
120
131
|
@cpu.cycle!
|
|
121
132
|
end
|
|
122
133
|
|