Repository navigation
STE DMA sound: 8-bit PCM through the DMA chip, the YM elsewhere - #9
Merged
Merged
Conversation
On an STE or a Mega STE the sound goes through the DMA sound chip: 8-bit samples at 12,517 Hz, mono, no interrupt per sample. The chip reads ST RAM only, so userfw loops it over a 2 KB ring just below screen page A ($6F800) and fills the ring every VBL, right after the wake: it reads where the chip plays (the frame counter's middle and low bytes, the middle one read again as it moves), reports it by 8 bytes at $FB8Dxx, and copies the cart buffer, the ring's mirror, from where its last copy ended up to 768 bytes (3 VBLs) ahead of the chip, so a missed VBL still finds samples. About 0.35 ms at 8 MHz; Timer-B stays off on this path and its 1.6 ms per VBL are free. At boot userfw sets the Microwire as TOS does (0 dB, tone flat, the YM mixed in); it stops the chip on the way out to GEM and before a cold reset. The ST takes the DMA path when the hello's machine family is the STE's and the RP has not asked for the YM (a word in slot 6 of the shared variables); audio.c applies the same rule at each hello. The audio buffer grows to 2 KB, the mirror; on the YM path its first 1 KB holds the slices as before. APP_FREE moves to $FA4980. audio.c's writer keeps the mirror written 928 bytes ahead of where the chip plays now (its last report plus the samples played since: a missed VBL sends no report), never where the ST copies; late writes and underruns are counted, and an empty FIFO holds the last sample. The FIFO holds a VBL more than AUDIO_FIFO_SLICES so the main loop may still go 80 ms between top-ups. Apps give YM pairs as before or signed 8-bit PCM at any rate (audio_play_pcm_loop, audio_set_pcm_callback); either plays on both outputs, converted at runtime through the 64-entry table both ways (a pair outside it by the YM's volume curve) with linear resampling. A YM source on the YM path goes through byte for byte as before. audio_prefer_ym() keeps an STE on the YM from its next boot; audio_uses_dma() and the audioOutput counter say which output plays. tests/host/test_audio_dma.c runs the output against a simulated STE: steady play, main-loop stalls within and beyond the tolerance, a missed VBL, an ST reset, YM pairs on the DMA chip, PCM on the YM, the output switch. Every sample played is the source in order, the held last sample, or silence before the start. Cartridge code: 8,928 bytes.
swd.py app audio_out 0|1 picks, from the ST's next boot, the DMA chip where there is one or the YM, so both outputs play on one STE with a reset in between. tone HZ plays a sine through the PCM path, whose clicks are easy to hear; tone 0 goes back to DEMO.YMS. swd.py counters shows the audio output.
--mode pcm writes a C header of signed 8-bit samples at the target rate for audio_play_pcm_loop() (12,517 Hz plays unchanged on the DMA chip; the RP converts it for the YM). The tests also check that audio.c's copies of the two-channel table and the YM volume curve match the tool's.
CLAUDE.md: the audio pipeline for two outputs (the rule both sides apply, the ring, the per-VBL copy, the writer ahead of the chip, the conversions, latency and tuning), the Atari RAM the DMA path uses, the 2 KB audio buffer and the layout after it, $FB8Dxx, and the ST's budget on the DMA path. README's audio section (PCM, the YM preference), the framebuffer-app skill, tools/dev/README.md and the changelog.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On an STE or a Mega STE the sound now goes through the DMA sound chip: 8-bit samples at 12,517 Hz, with no interrupt per sample. A plain ST or Mega ST plays through the YM as before. Both run from the same firmware.
How it works
$6F800). Every VBL, right after the wake,userfw:$FB8Dxx;userfwsets the Microwire as TOS does. It stops the chip on the way out to GEM and before a cold reset.audio_prefer_ym()).audio.c's writer keeps the mirror written 928 bytes ahead of where the chip plays now: the last report, plus the samples played since, since a missed VBL sends no report. It never writes where the ST copies.audio_play_pcm_loop,audio_set_pcm_callback).APP_FREEmoves to$FA4980and has about 14.4 KB.wav_to_ym4.py --mode pcmwrites PCM headers.audio_out 0|1, which chooses the output from the ST's next boot, andtone HZ.audio outputcounter inswd.py counters.Tests
test_audio_dma.cruns the output against a simulated STE:test_wav_to_ym4.pychecks thataudio.c's tables match the converter's.Hardware
DEMO.YMS, converted to PCM.YMSfiles play on both outputs);AUDIO_FIFO_SLICESlowers it.Docs
CLAUDE.md(the audio pipeline for two outputs, the Atari RAM it uses, the layout),README.md(audio), the framebuffer-app skill,tools/dev/README.mdandCHANGELOG.md.