feat(desktop): add opt-in Darwin audio output
This commit is contained in:
parent
902c892d3f
commit
2ea7164d9a
21 changed files with 1311 additions and 30 deletions
|
|
@ -152,6 +152,9 @@ The repository is a Cargo workspace with three crates:
|
|||
| `crates/gba-demos/src/mosaic_demo.rs` | CPU-driven background/sprite mosaic demo runner |
|
||||
| `crates/gba-demos/src/raster_demo.rs` | Original ARM HBlank DMA raster program and color table |
|
||||
| `crates/gba-core/src/video/effects.rs` | Region masks and RGB555 alpha/brightness arithmetic |
|
||||
| `crates/desktop/src/audio.rs` | Host-audio platform boundary and explicit non-Darwin diagnostic |
|
||||
| `crates/desktop/src/audio/device.rs` | CPAL/CoreAudio lifecycle, nonblocking output callback, runtime errors, and counters |
|
||||
| `crates/desktop/src/audio/playback.rs` | Bounded host queue, prefill, stereo mapping, linear rate conversion, gain, and DC removal |
|
||||
| `crates/desktop/src/desktop.rs` | Native window, display test, and keyboard controls |
|
||||
| `crates/desktop/src/desktop/affine.rs` | Affine background window and smoke-test pixel checks |
|
||||
| `crates/desktop/src/desktop/affine_raster.rs` | Affine raster window and every-pixel smoke checks |
|
||||
|
|
@ -169,7 +172,7 @@ The repository is a Cargo workspace with three crates:
|
|||
| `crates/desktop/src/cartridge/rtc_clock.rs` | Host-only UTC seed and monotonic elapsed-time adapter, with injected epoch/duration tests |
|
||||
| `crates/desktop/src/cartridge/window.rs` | ROM-window session, bounded slices, capture, STOP/input handling, and progress limits |
|
||||
| `crates/desktop/src/cartridge/window/tests.rs` | Original ROM pixels, keyboard mapping, STOP wake, HALT, frame budgets, and diagnostics |
|
||||
| `crates/desktop/src/desktop/rom.rs` | Main-thread ROM window, input polling, presentation pacing, and frame-limited exit |
|
||||
| `crates/desktop/src/desktop/rom.rs` | Main-thread ROM window, input polling, optional audio draining/muting, bounded pacing, and frame-limited exit |
|
||||
| `crates/desktop/examples/write_rom_demo.rs` | Non-overwriting export of the original input-test ROM |
|
||||
| `crates/desktop/src/cartridge/tests.rs` | ROM options, bounded readers, instruction/DMA/IRQ budgets, HALT/STOP, and diagnostic reports |
|
||||
| `crates/desktop/tests/rom_cli.rs` | File loading and exit-status checks; opt-in native ROM-window integration test |
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@ Verified locally on macOS 15.7.3:
|
|||
- The headless suite runner passes generated ARM/Thumb/BIOS cases, produces repeatable JSON, and rejects deliberate assertion mismatches.
|
||||
|
||||
These results cover the CPU core, memory, Mode 0–5 snapshots and row capture, input mapping, and desktop windows.
|
||||
They do not verify a complete GBA display controller or audio.
|
||||
They do not verify a complete GBA display controller or hardware audio fidelity.
|
||||
CPAL 0.16 is a macOS-only dependency of the desktop crate. The existing Nix Darwin shell builds its CoreAudio backend without extra shell packages.
|
||||
Native tests submit an original tone and original ROM-generated samples to the default output device.
|
||||
Use `--audio` with a ROM window to opt in. Ordinary tests and muted windows do not require an audio device.
|
||||
Other host audio platforms are not implemented. Underruns remain possible; device callbacks do not prove listening quality.
|
||||
Arrow-key movement in the original host display test was confirmed manually on this Mac.
|
||||
CPU demos and ROM windows have automated input-mapping tests; their physical keyboard behavior still needs manual confirmation.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Audio device subset
|
||||
|
||||
The core models Direct Sound A/B, bounded sound DMA, and programmable sound generator (PSG) channels 1–4.
|
||||
Wave channel 3 supports single-bank playback only. The core does not produce desktop audio.
|
||||
Wave channel 3 supports single-bank playback only. Optional fixed-rate capture supplies samples to the separate Darwin output adapter.
|
||||
`Memory::audio_level()` exposes the current digital stereo level for deterministic inspection, not a continuous sample stream.
|
||||
|
||||
## Registers and PSG state
|
||||
|
|
@ -262,6 +262,43 @@ Capture is disabled by default. Changing enable state clears queued frames, phas
|
|||
Repeating the current enable state preserves capture state. Configure capture between machine steps.
|
||||
Original tests cover exact sample boundaries, timer consumption, pulse phases, batching, queue overflow, HALT/STOP, and transactional rollback.
|
||||
|
||||
## Darwin output adapter
|
||||
|
||||
Use `--audio` with `--rom ... --window` to enable CPAL 0.16/CoreAudio output on macOS.
|
||||
Windows remain muted by default. Terminal runs, ROM suites, and demos do not open an audio device.
|
||||
The flag is invalid in terminal mode and cannot be repeated. Other platforms currently report an explicit unsupported-platform error.
|
||||
|
||||
The adapter opens the default output device at its default configuration.
|
||||
Supported formats are float32, signed16, and unsigned16, with 8–192 kHz rates and 1–32 channels.
|
||||
Stereo maps to the first two channels. Mono averages left/right; additional channels receive silence.
|
||||
Device initialization, unsupported formats, and stream errors fail the audio-enabled run rather than silently switching to muted execution.
|
||||
Omit `--audio` to run without an audio device.
|
||||
|
||||
The core's 32,768 Hz frames enter a bounded 8,192-frame host queue.
|
||||
Playback waits for 1,024 frames before starting or resuming after an underrun.
|
||||
Linear interpolation converts to the device rate. A 20 Hz high-pass filter removes DC, followed by conservative 25% host gain and clipping.
|
||||
These are presentation choices, not a measured GBA analog model or band-limited reconstruction.
|
||||
High-frequency aliases, startup transients, and channel-model inaccuracies remain possible.
|
||||
|
||||
The data callback performs no allocation, blocking lock, emulation step, or log write.
|
||||
It tries the queue lock once; contention produces silence and increments a counter.
|
||||
An underrun produces silence and returns to the prefill state. Overflow discards new input frames and records the loss.
|
||||
The final report includes submitted/nonzero-input frames, callbacks, nonzero-output frames, underruns, dropped input, and lock misses.
|
||||
Zero submitted frames can indicate an unfocused window, not a failed output device.
|
||||
The window treats a core capture overflow as an error; it drains samples between bounded CPU slices.
|
||||
The callback cannot advance emulator clocks or request extra emulation work.
|
||||
|
||||
Focus loss and STOP clear queued host audio and conversion state. Muted frames are drained without being submitted.
|
||||
The device may still finish its already-submitted buffer. Resume waits for fresh prefill instead of replaying stale samples.
|
||||
Audio-enabled windows use absolute frame deadlines with at most one frame of catch-up.
|
||||
This reduces sleep drift without skipping emulated frames or accumulating unbounded catch-up work.
|
||||
Slow hosts can still underrun. Sample delivery is not a claim of uninterrupted or hardware-faithful sound.
|
||||
|
||||
Native checks on Darwin arm64 exercised a quiet original tone and an original wave-ROM window through CoreAudio.
|
||||
A 900-frame local Emerald run reached its limit with 554,484 nonzero device-output frames, zero dropped input frames, and 13 underruns.
|
||||
Those counters demonstrate output submission, not human listening, correct music, or game compatibility.
|
||||
Audio quality and physical speaker/headphone output still require a manual listening check.
|
||||
|
||||
## Validation and remaining work
|
||||
|
||||
Original tests cover signed samples, byte order, queue capacity, partial writes, resets, underflow, stereo routing, and clipping.
|
||||
|
|
@ -278,13 +315,16 @@ CPU/DMA tests include noise register writes at bus completion and failed-step ro
|
|||
BIOS reset tests verify that selected sound reset stops all supported PSG channels, while unselected sound state remains active.
|
||||
|
||||
These tests validate the documented nominal model, not hardware audio fidelity.
|
||||
Remaining audio work: two-bank wave playback, verified live bank changes, hardware PWM sampling, filtering/resampling, and a Darwin host backend.
|
||||
Remaining audio work: two-bank wave playback, verified live bank changes, hardware PWM sampling, improved reconstruction, and lower-underrun pacing.
|
||||
A local Emerald input probe exposed a wave trigger with the gate disabled. This is now accepted without activating playback.
|
||||
The same scheduled-input probe passes that point and completes its extended frame budget; audible output and gameplay remain unverified.
|
||||
The same scheduled-input probe passes that point and completes its extended frame budget; gameplay remains unverified.
|
||||
Later native output checks exercise the host audio path separately; they do not establish game-audio accuracy.
|
||||
See [the local runtime result](../research/emerald-reset.md).
|
||||
|
||||
## References
|
||||
|
||||
- [CPAL 0.16.0](https://docs.rs/cpal/0.16.0/cpal/): default device/configuration, typed output callbacks, sample formats, CoreAudio backend, and stream lifecycle. CPAL is a macOS-only desktop dependency here.
|
||||
|
||||
- [GBATEK wave channel 3](https://problemkaputt.de/gbatek-gba-sound-channel-3-wave-output.htm): bank selection, nibble order, rotating RAM, sample rate, length, and force volume.
|
||||
- [NanoBoyAdvance wave at 55b5cf0a](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/hw/apu/channel/wave_channel.cc) and [header](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/hw/apu/channel/wave_channel.hh): signed gain and rate comparison; pointer-based RAM and two-bank/retrigger differences are not copied.
|
||||
|
||||
|
|
|
|||
|
|
@ -544,6 +544,7 @@ Unsupported SWIs, unsupported exception vectors, and null/misaligned IRQ callbac
|
|||
The CPU reports `CpuError::UnsupportedInstruction` with `bios::UNSUPPORTED_TRAP`, rather than silently treating a service as a no-op.
|
||||
Invalid arithmetic/decompression arguments use the same CPU error type with the distinct `bios::INVALID_ARGUMENT_TRAP` instruction.
|
||||
Prior boot/service steps remain committed on failure.
|
||||
Two-bank wave playback, host audio output, connected serial links, HardReset, and serial/Game Pak STOP wake-up remain unimplemented.
|
||||
Two-bank wave playback, connected serial links, HardReset, and serial/Game Pak STOP wake-up remain unimplemented.
|
||||
Darwin ROM windows can enable host audio with `--audio`; this does not extend BIOS coverage.
|
||||
Disconnected normal serial transfers use the [bounded serial model](serial.md); cancel active transfers before BIOS serial reset.
|
||||
This subset is not sufficient for Pokémon Emerald compatibility.
|
||||
|
|
|
|||
|
|
@ -135,5 +135,9 @@ The CPU writes the palette: blue at rest, red while Z is held, and green while R
|
|||
Right takes priority over Z. Other buttons have no visible effect in this small program.
|
||||
No game or firmware content is included.
|
||||
|
||||
Both ROM modes lack audio, saves, and per-step tracing. Commercial-game compatibility remains unverified and unsupported.
|
||||
ROM windows can enable Darwin output with `--audio`; they are muted by default.
|
||||
The option requires a usable default audio device. Device/configuration errors fail explicitly; omit the flag to run muted.
|
||||
Focus loss and STOP clear queued audio. See [audio output and its limits](audio.md#darwin-output-adapter).
|
||||
Terminal mode has no audio. Both modes lack save writing/persistence and per-step tracing.
|
||||
Commercial-game compatibility remains unverified and unsupported.
|
||||
Only load files that you may lawfully use. Do not commit game ROMs or firmware.
|
||||
|
|
|
|||
|
|
@ -125,7 +125,8 @@ This result does not establish gameplay, linked transfers, or cartridge/serial G
|
|||
Noise now has deterministic counter clocks and shares tested length/envelope logic with the independent pulse channels.
|
||||
Sweep applies only to channel 1. Direct Sound has FIFO clocks and nominal DMA requests.
|
||||
All supported channels use the instantaneous digital-level inspection interface.
|
||||
Wave channel 3 now supports single-bank playback. Two-bank playback, a continuous sample stream, and desktop output are still missing.
|
||||
Wave channel 3 now supports single-bank playback. Two-bank playback remains missing.
|
||||
Fixed-rate capture and optional Darwin `--audio` output were added later; their counters do not establish correct game music.
|
||||
Do not treat digital-level tests as audible-game validation or suppress remaining diagnostics without implementing their behavior.
|
||||
Cartridge saves remain a separate required feature. SRAM alone will not support Emerald's Flash save protocol.
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ A separate 600-frame Emerald native run still reaches its frame limit without in
|
|||
## Remaining requirements
|
||||
|
||||
- Verify visible menu text and gameplay through direct window testing or explicit independently justified assertions.
|
||||
- Implement continuous sample production and a Darwin output backend before claiming audible sound.
|
||||
- Fixed-rate capture and opt-in Darwin `--audio` output were added later. Verify listening quality and reduce observed underruns.
|
||||
- Research two-bank wave playback and live bank changes separately. Both remain diagnostic during active playback.
|
||||
- Implement Flash programming/erase and safe persistence before claiming saving.
|
||||
|
||||
|
|
@ -93,5 +93,6 @@ For a manual native check, use:
|
|||
direnv exec . cargo run --locked --release -- --rom roms/pokemon-emerald.gba --rtc --save-type flash128 --window
|
||||
```
|
||||
|
||||
Enter maps to Start. Z maps to A. Escape exits. This command does not supply the deterministic probe schedule or fixed RTC.
|
||||
Enter maps to Start. Z maps to A. Escape exits. Add `--audio` for optional Darwin output.
|
||||
This command does not supply the deterministic probe schedule or fixed RTC.
|
||||
Hardware sources and implementation comparisons are recorded in [audio references](../hardware/audio.md#references).
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ Update this file when a feature lands or a limit is removed.
|
|||
- Wave channel 3 supports single-bank nibble playback, rotating RAM, volume/force-volume, length, activity status, and digital mixing.
|
||||
- Two-bank wave playback, active bank changes, and unsupported serial operations remain diagnostic with whole-access and block-store rollback.
|
||||
- ROM windows with scanline capture, keyboard input, focus-loss release, bounded execution slices, and interactive keypad wake from STOP.
|
||||
- Opt-in macOS CoreAudio output, with tested original tones and wave-ROM playback; focus loss and STOP mute queued audio.
|
||||
- Optional ROM-window frame limits and an original input-test ROM generator.
|
||||
- Headless JSON ROM suites with PC or VBlank completion, register/CPSR/memory/pixel assertions, scheduled buttons, bounded budgets, and structured failure reports.
|
||||
- Hash-pinned preparation of public `jsmolka/gba-tests` ARM, Thumb, memory, and BIOS ROMs, with verified result-register checkpoints.
|
||||
|
|
@ -146,7 +147,7 @@ Internal affine origins, vertical window flags, and vertical mosaic counters tra
|
|||
Horizontal window comparators retain four-cycle event history.
|
||||
Sprite rows prepare one line ahead with a whole-row cycle40 sample and nominal per-row work limits.
|
||||
Individual fetch timing, hardware-accurate work cutoffs, video-bus contention, and per-pixel color composition remain unimplemented.
|
||||
There is no host audio output or save writing/persistence.
|
||||
ROM windows offer opt-in Darwin audio output with `--audio`. Save writing/persistence remains unavailable.
|
||||
The [Flash subset](hardware/saves.md) supports explicit device selection, identification, array reads, and banking.
|
||||
Flash programming, erase, busy polling, wider accesses, unverified aliases, and other save-device protocols remain diagnostic or unimplemented.
|
||||
The optional [cartridge GPIO/RTC subset](hardware/cartridge-gpio.md) supports calendar/control transfers and explicit clock updates, selected with `--rtc` in ROM modes.
|
||||
|
|
@ -170,7 +171,9 @@ The presentation framebuffer is separate from emulated video RAM.
|
|||
All named graphics demos connect desktop input and rendering to the emulated CPU. The default display test remains host-generated.
|
||||
ROM files can run in bounded terminal mode or a window with the original BIOS replacement.
|
||||
ROM windows have no demo-specific startup checks. Input is sampled between bounded execution slices.
|
||||
Audio, saves, and external BIOS loading remain unavailable. ROM loading does not establish commercial-game compatibility.
|
||||
Audio is muted by default; `--audio` opens the macOS output device for ROM windows only. Saves and external BIOS loading remain unavailable.
|
||||
The host output has bounded buffers, linear rate conversion, DC removal, and underrun/drop counters. Slow hosts can stutter.
|
||||
ROM loading and nonzero audio callbacks do not establish commercial-game compatibility or correct music.
|
||||
A local Emerald run now completes its all-device reset and master sound enable.
|
||||
It also passes the pulse/noise triggers and normal-mode RCNT configuration.
|
||||
It also completes a CpuSet call to address zero through the corrected ignored BIOS-write behavior.
|
||||
|
|
|
|||
|
|
@ -81,7 +81,25 @@ An original desktop ROM installs its Flash byte reader in IWRAM; terminal/native
|
|||
They verify explicit activation errors and atomic byte/word/block-store/DMA failure behavior.
|
||||
Wave tests cover every frequency and bank, independent nibble rotation, full rotations, bounded maximum batches, and fractional gains.
|
||||
They also check inactive triggers, gates, length, status, master/BIOS reset, HALT/STOP, live-write limits, and CPU/DMA rollback.
|
||||
Original terminal/native programs activate wave playback without external assets or host audio.
|
||||
Original terminal/native programs activate wave playback without external assets.
|
||||
Fixed-rate capture tests verify sample boundaries, timer/PSG ordering, rollback, queue limits, drop counts, and HALT/STOP.
|
||||
Host conversion tests require no device and cover rates, stereo/mono mapping, prefill, overflow, underflow, DC removal, and callback splitting.
|
||||
CLI tests keep output muted by default and reject `--audio` outside ROM-window mode.
|
||||
|
||||
The following ignored tests require a native audio device and play original test tones:
|
||||
|
||||
```sh
|
||||
direnv exec . cargo test --locked --release -p gameboy-rust-desktop native_audio_output -- --ignored --nocapture --test-threads=1
|
||||
direnv exec . cargo test --locked --release -p gameboy-rust-desktop --test rom_cli native_rom_audio_window -- --ignored --test-threads=1
|
||||
```
|
||||
|
||||
The device test generates samples with the actual core and does not require window focus.
|
||||
The window test requires focus. It checks that ROM-generated samples reach a nonzero CoreAudio callback.
|
||||
An unfocused window correctly submits no samples, so that test fails rather than claiming playback success.
|
||||
The focused window check passed locally; a later unattended run remained unfocused and did not validate playback.
|
||||
Use the reported submitted-frame count to distinguish this condition from an output-device failure.
|
||||
These checks do not prove physical speaker output, correct game music, or hardware audio fidelity.
|
||||
Normal Cargo/Python tests do not open an audio device. Underruns are reported and can occur on slow hosts.
|
||||
The optional [local Emerald input probe](research/emerald-wave.md) now completes its scheduled Start/A frame budget.
|
||||
Frame completion and different pixels are not assertions about readable menus, correct graphics, or gameplay.
|
||||
[Jev evidence review](jev-debugging.md) is optional and never changes deterministic test verdicts.
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ A [fixed-time input probe](research/emerald-wave.md) then exposed an unsupported
|
|||
Single-bank wave support now passes that point and completes 1,320 frames with scheduled Start/A input.
|
||||
Captured output differs from a no-input control; readable menus and gameplay remain unverified.
|
||||
The core has tested Direct Sound, both pulse channels, single-bank wave channel 3, and noise channel 4.
|
||||
Two-bank wave playback, a continuous sample stream, and desktop audio output remain missing.
|
||||
Fixed-rate stereo capture and opt-in Darwin `--audio` output now work with original native probes.
|
||||
A local Emerald run submits nonzero device frames but records underruns; listening quality and correct music remain unverified.
|
||||
Two-bank wave playback, hardware PWM sampling, and improved reconstruction/pacing remain missing.
|
||||
See [the runtime result](research/emerald-reset.md). Next, verify visible menu progress rather than treating frame counts as playability.
|
||||
The separate save requirement includes Flash programming/erase and safe host persistence. Identification/read support is not successful saving.
|
||||
SRAM is byte-addressable battery-backed save memory. Flash and EEPROM remain separate protocols.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue