Fixed audio stuff

This commit is contained in:
Jan Racek
2026-03-26 20:26:51 +01:00
parent 19bb2254f3
commit eacbf4de46
3 changed files with 68 additions and 31 deletions

View File

@@ -1,6 +1,8 @@
#include "audiomanager.hh" #include "audiomanager.hh"
#include "common/hardware/dma.h"
#include "common/hardware/spu.h" #include "common/hardware/spu.h"
#include <psyqo/kernel.hh>
#include <psyqo/spu.hh> #include <psyqo/spu.hh>
#include <psyqo/xprintf.h> #include <psyqo/xprintf.h>
@@ -65,25 +67,34 @@ bool AudioManager::loadClip(int clipIndex, const uint8_t* adpcmData, uint32_t si
return false; return false;
} }
// psyqo::SPU::dmaWrite takes dataSize as uint16_t so upload in chunks // psyqo::SPU::dmaWrite takes dataSize as uint16_t and uses blockSize=4:
// for clips larger than 65532 bytes (largest multiple-of-4 that fits). // BCR = blockSize | ((dataSize / blockSize) << 16)
// block_count = dataSize / blockSize (integer division - truncates!)
// actual bytes = block_count * blockSize * 4
// //
// psyqo DMA math: BCR = blockSize | ((dataSize/blockSize) << 16) // With blockSize=4: each block = 16 bytes. Max block_count that fits
// blockSize=4 → 4 words per block = 16 bytes per block // in uint16_t's BCR field: 4095. Max clean transfer: 4095 * 16 = 65520.
// block count = dataSize/blockSize // bytesThisRound MUST be a multiple of 16 to avoid the integer division
// total bytes = blockSize × (dataSize/blockSize) × 4 = dataSize × 4 // truncation causing fewer bytes to be DMA'd than the pointer advances.
// So dataSize = bytesThisRound / 4 gives the correct byte count.
const uint8_t* src = adpcmData; const uint8_t* src = adpcmData;
uint32_t remaining = alignedSize; uint32_t remaining = alignedSize;
uint32_t dstAddr = addr; uint32_t dstAddr = addr;
while (remaining > 0) { while (remaining > 0) {
// Max transfer per call: 65532 bytes (16383 blocks × 4 bytes each). // Max transfer per call: 65520 bytes (4095 blocks * 16 bytes each).
uint32_t bytesThisRound = (remaining > 65532u) ? 65532u : remaining; uint32_t bytesThisRound = (remaining > 65520u) ? 65520u : remaining;
bytesThisRound &= ~3u; // DMA alignment bytesThisRound &= ~15u; // 16-byte block alignment
if (bytesThisRound == 0) break; if (bytesThisRound == 0) break;
uint16_t dmaSizeParam = (uint16_t)(bytesThisRound / 4); uint16_t dmaSizeParam = (uint16_t)(bytesThisRound / 4);
psyqo::SPU::dmaWrite(dstAddr, src, dmaSizeParam, 4); psyqo::SPU::dmaWrite(dstAddr, src, dmaSizeParam, 4);
// PSYQo's internal waitForStatus only spins ~10000 iterations (~1.8ms).
// On real hardware, SPU DMA for 65KB takes tens of milliseconds.
// The timeout fires, the function returns, and the next chunk starts
// while the previous transfer is still in progress - corrupting data.
// Spin here until the DMA controller's busy bit actually clears.
while (DMA_CTRL[DMA_SPU].CHCR & (1 << 24)) {}
src += bytesThisRound; src += bytesThisRound;
dstAddr += bytesThisRound; dstAddr += bytesThisRound;
remaining -= bytesThisRound; remaining -= bytesThisRound;
@@ -92,6 +103,12 @@ bool AudioManager::loadClip(int clipIndex, const uint8_t* adpcmData, uint32_t si
// dmaWrite() now properly restores transfer mode to idle after each // dmaWrite() now properly restores transfer mode to idle after each
// DMA transfer, so no manual SPU_CTRL fix-up is needed here. // DMA transfer, so no manual SPU_CTRL fix-up is needed here.
// Restore SPU to manual (non-DMA) mode after upload.
// psyqo::SPU::dmaWrite sets SPU_CTRL bit 5 (DMA write mode) but never
// clears it. On real hardware, voice register writes (pitch, volume, etc.)
// may be ignored while the SPU bus is still in DMA mode.
SPU_CTRL &= ~(0b11 << 4);
m_clips[clipIndex].spuAddr = addr; m_clips[clipIndex].spuAddr = addr;
m_clips[clipIndex].size = sizeBytes; m_clips[clipIndex].size = sizeBytes;
m_clips[clipIndex].sampleRate = sampleRate; m_clips[clipIndex].sampleRate = sampleRate;
@@ -125,21 +142,10 @@ int AudioManager::play(int clipIndex, int volume, int pan) {
rightVol = (uint16_t)((uint32_t)vol * p / 127); rightVol = (uint16_t)((uint32_t)vol * p / 127);
} }
psyqo::SPU::ChannelPlaybackConfig config;
config.sampleRate.value = static_cast<uint16_t>(((uint32_t)clip.sampleRate << 12) / 44100);
config.volumeLeft = leftVol;
config.volumeRight = rightVol;
config.adsr = DEFAULT_ADSR;
// Set the repeat address depending on loop mode. // Set the repeat address depending on loop mode.
// The new psyqo::SPU::getNextFreeChannel() uses the ENDX register: // Looping clips: repeat -> clip start (loop back to beginning).
// a channel is "free" when its ENDX bit is set (voice reached loop-end). // Non-looping clips: repeat -> dummy 0x1000 (go silent after clip ends,
// silenceChannels() points voices at psyqo's silent dummy sample at 0x1000 // dummy's loop-end flag re-sets ENDX -> channel freed).
// that immediately sets ENDX, so stopped channels are detected as free.
//
// Looping clips: repeat → clip start (loop back to beginning).
// Non-looping clips: repeat → dummy 0x1000 (go silent after clip ends,
// dummy's loop-end flag re-sets ENDX → channel freed).
constexpr uint16_t DUMMY_SPU_ADDR = 0x1000; constexpr uint16_t DUMMY_SPU_ADDR = 0x1000;
if (clip.loop) { if (clip.loop) {
SPU_VOICES[ch].sampleRepeatAddr = static_cast<uint16_t>(clip.spuAddr / 8); SPU_VOICES[ch].sampleRepeatAddr = static_cast<uint16_t>(clip.spuAddr / 8);
@@ -147,9 +153,38 @@ int AudioManager::play(int clipIndex, int volume, int pan) {
SPU_VOICES[ch].sampleRepeatAddr = DUMMY_SPU_ADDR / 8; SPU_VOICES[ch].sampleRepeatAddr = DUMMY_SPU_ADDR / 8;
} }
psyqo::SPU::playADPCM(static_cast<uint8_t>(ch), // Build playback config
static_cast<uint16_t>(clip.spuAddr), psyqo::SPU::ChannelPlaybackConfig config;
config, true); config.sampleRate.value = static_cast<uint16_t>(((uint32_t)clip.sampleRate << 12) / 44100);
config.volumeLeft = leftVol;
config.volumeRight = rightVol;
config.adsr = DEFAULT_ADSR;
// Write SPU voice registers directly instead of PSYQo's playADPCM(),
// which truncates addresses above 64KB (uint16_t parameter).
// The sampleStartAddr register stores addr/8, so uint16_t covers
// the full 512KB SPU RAM range.
// KEY_OFF (hard cut)
if (ch > 15) {
SPU_KEY_OFF_HIGH = 1 << (ch - 16);
} else {
SPU_KEY_OFF_LOW = 1 << ch;
}
SPU_VOICES[ch].volumeLeft = config.volumeLeft;
SPU_VOICES[ch].volumeRight = config.volumeRight;
SPU_VOICES[ch].sampleRate = config.sampleRate.value;
SPU_VOICES[ch].sampleStartAddr = static_cast<uint16_t>(clip.spuAddr / 8);
SPU_VOICES[ch].ad = config.adsr & 0xFFFF;
SPU_VOICES[ch].sr = (config.adsr >> 16) & 0xFFFF;
// KEY_ON
if (ch > 15) {
SPU_KEY_ON_HIGH = 1 << (ch - 16);
} else {
SPU_KEY_ON_LOW = 1 << ch;
}
return static_cast<int>(ch); return static_cast<int>(ch);
} }

View File

@@ -15,10 +15,12 @@ static constexpr int MAX_VOICES = 24;
/// psyqo places a 16-byte silent dummy sample at 0x1000. /// psyqo places a 16-byte silent dummy sample at 0x1000.
/// User clips start at 0x1010. /// User clips start at 0x1010.
/// ///
/// Upper bound is 0x10000 (64KB) because psyqo::SPU::playADPCM() /// Note: psyqo::SPU::playADPCM() takes a uint16_t for the address,
/// takes a uint16_t for the SPU RAM address. /// which would limit to 64KB. We bypass it and write SPU registers
/// directly to address the full 512KB range (register stores addr/8,
/// so uint16_t covers 0-0x7FFF8).
static constexpr uint32_t SPU_RAM_START = 0x1010; static constexpr uint32_t SPU_RAM_START = 0x1010;
static constexpr uint32_t SPU_RAM_END = 0x10000; static constexpr uint32_t SPU_RAM_END = 0x80000;
/// Default ADSR: instant attack, sustain at max, ~46ms linear release. /// Default ADSR: instant attack, sustain at max, ~46ms linear release.
/// Lower 16-bit (AD): attack linear shift=0 step=0("+7"), decay shift=0, /// Lower 16-bit (AD): attack linear shift=0 step=0("+7"), decay shift=0,