mirror of
https://github.com/emu-russia/pureikyubu.git
synced 2025-04-02 10:42:15 -04:00
247 lines
6.6 KiB
Text
247 lines
6.6 KiB
Text
// AX library sound output (playback) module
|
|
// rev. by org <ogamespec@gmail.com>
|
|
|
|
// AX playback init :
|
|
// 1. DSPInit - reset DSP, install DSP interrupt handler (see my dsp.txt)
|
|
// 2. boot AX slave microcode; DSP jumps to start vector
|
|
// 3. DSP signals interrupt and confirm it by "dsp init" mail in input mailbox
|
|
// 4. DSP interrupt handler calls AX "dsp init" callback
|
|
// 5. callback is setting __AXDSPInitFlag to TRUE
|
|
// 6. warm-up AX by playing initial empty AI DMA buffer.
|
|
|
|
// AX playback flow :
|
|
// 1. AI buffer DMA end -> AI interrupt -> AX AI callback
|
|
// 2. sending audio list (ALIST) to DSP
|
|
// 3. set next output buffer for AI DMA (AI DMA plays all the time)
|
|
// 4. DSP confirm end of ALIST execution by DSP interrupt and "dsp resume" mail
|
|
// 5. DSP handler syncs DSP processing
|
|
// 6. goto 1.
|
|
|
|
static u16 __AXOutBuffer[2][320]; // primary and secondary AI DMA buffers
|
|
static u16 __AXOutSBuffer[320]; // software streaming buffer (to mix)
|
|
|
|
static BOOL __AXDebugSteppingMode; // step-by-step frames
|
|
static u32 __AXOutDspReady;
|
|
static int __AXOutFrame; // selects current AI DMA buffer
|
|
|
|
static BOOL __AXDSPInitFlag;
|
|
static BOOL __AXDSPDoneFlag;
|
|
|
|
// called every audio frame (if registered)
|
|
static AXUserCallback __AXUserFrameCallback;
|
|
|
|
// AX profiler data block, for this module
|
|
// not important, but hacked anyway :p
|
|
static AXPROFILE __AXLocalProfile;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// playback section
|
|
|
|
void __AXOutNewFrame(OSTick lessDspCycles)
|
|
{
|
|
void *cl;
|
|
|
|
__AXLocalProfile.axFrameStart = OSGetTime();
|
|
|
|
__AXSyncPBs(lessDspCycles);
|
|
__AXPrintStudio();
|
|
|
|
// send audio list to DSP
|
|
cl = __AXGetCommandListAddress();
|
|
|
|
DSPSendMailToDSP(0xBABE0180);
|
|
while(DSPCheckMailToDSP());
|
|
|
|
DSPSendMailToDSP(cl);
|
|
while(DSPCheckMailToDSP());
|
|
|
|
// service, callback and stack, yeah :p
|
|
__AXServiceCallbackStack();
|
|
|
|
// AUX processing (e.g. new ALIST generation)
|
|
__AXLocalProfile.auxProcessingStart = OSGetTime();
|
|
__AXProcessAux();
|
|
__AXLocalProfile.auxProcessingEnd = OSGetTime();
|
|
|
|
// take control to AX user callback
|
|
__AXLocalProfile.userCallbackStart = OSGetTime();
|
|
if(__AXUserFrameCallback) __AXUserFrameCallback();
|
|
__AXLocalProfile.userCallbackEnd = OSGetTime();
|
|
|
|
// next frame
|
|
// note : it could simplier to use for toggle : __AXOutFrame ^= 1
|
|
__AXNextFrame(__AXOutSBuffer, &__AXOutBuffer[__AXOutFrame++][0]);
|
|
__AXOutFrame &= 1;
|
|
AIInitDMA(&__AXOutBuffer[__AXOutFrame][0], 640);
|
|
|
|
__AXLocalProfile.axFrameEnd = OSGetTime();
|
|
__AXLocalProfile.axNumVoices = __AXGetNumVoices();
|
|
|
|
// copy local profile, or something
|
|
// not interesting crap
|
|
profile = __AXGetCurrentProfile();
|
|
if(profile)
|
|
{
|
|
i, src, dest
|
|
}
|
|
}
|
|
|
|
AIDCallback __AXOutAiCallback()
|
|
{
|
|
// update time (for what?)
|
|
if(__AXOutDspReady == 0)
|
|
{
|
|
__AXOsTime = OSGetTime();
|
|
}
|
|
|
|
// new frame
|
|
if(__AXOutDspReady == 1)
|
|
{
|
|
__AXOutDspReady = 0;
|
|
__AXOutNewFrame(0);
|
|
return;
|
|
}
|
|
|
|
__AXOutDspReady = 2;
|
|
DSPAssertTask(&task);
|
|
}
|
|
|
|
// signal to caller, that DSP is initialized
|
|
DSPCallback __AXDSPInitCallback()
|
|
{
|
|
__AXDSPInitFlag = TRUE;
|
|
}
|
|
|
|
// set step-by-step debug mode
|
|
void AXSetStepMode(BOOL i)
|
|
{
|
|
__AXDebugSteppingMode = i;
|
|
}
|
|
|
|
DSPCallback __AXDSPResumeCallback()
|
|
{
|
|
if(__AXDebugSteppingMode)
|
|
{
|
|
__AXOutDspReady = 1;
|
|
}
|
|
else
|
|
{
|
|
if(__AXOutDspReady == 2)
|
|
{
|
|
__AXOutDspReady = 0;
|
|
__AXOutNewFrame(OSGetTime() - __AXOsTime);
|
|
}
|
|
else __AXOutDspReady = 1;
|
|
}
|
|
}
|
|
|
|
DSPCallback __AXDSPDoneCallback()
|
|
{
|
|
__AXDSPDoneFlag = TRUE;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// init section
|
|
|
|
// initialize DSP hardware and boot AX microcode program
|
|
void __AXOutInitDSP(void)
|
|
{
|
|
static DSPTaskInfo ax_task;
|
|
|
|
ax_task.iram_mmem_addr = axDspSlave;
|
|
ax_task.iram_length = axDspSlaveLength;
|
|
ax_task.iram_addr = 0;
|
|
|
|
ax_task.dram_mmem_addr = ax_dram_image;
|
|
ax_task.dram_length = 8192;
|
|
ax_task.dram_addr = 0;
|
|
|
|
ax_task.dsp_init_vector = 0x0010;
|
|
ax_task.dsp_resume_vector = 0x0030;
|
|
|
|
ax_task.init_cb = __AXDSPInitCallback;
|
|
ax_task.res_cb = __AXDSPResumeCallback;
|
|
ax_task.done_cb = __AXDSPDoneCallback;
|
|
ax_task.req_cb = NULL;
|
|
|
|
ax_task.state = DSP_TASK_STATE_INIT;
|
|
|
|
__AXDSPInitFlag = FALSE;
|
|
__AXDSPDoneFlag = FALSE;
|
|
|
|
// reset DSP
|
|
if(DSPCheckInit() == 0)
|
|
{
|
|
DSPInit();
|
|
}
|
|
|
|
DSPAddTask(&ax_task);
|
|
|
|
while(__AXDSPInitFlag == 0);
|
|
}
|
|
|
|
// init AX sound output hardware and buffers
|
|
void __AXOutInit(void)
|
|
{
|
|
OSReport("Initializing AXOut code module\n");
|
|
|
|
ASSERT(((u32)&__AXOutBuffer[0][0] & 0x1F) == 0);
|
|
ASSERT(((u32)&__AXOutBuffer[1][0] & 0x1F) == 0);
|
|
ASSERT(((u32)&__AXOutSBuffer[0] & 0x1F) == 0);
|
|
|
|
__AXOutFrame = 0; // current buffer
|
|
__AXDebugSteppingMode = 0; // only for debug
|
|
|
|
// clear output sample buffers
|
|
memset(__AXOutBuffer, 0, sizeof(__AXOutBuffer));
|
|
DCFlushRange(__AXOutBuffer, sizeof(__AXOutBuffer));
|
|
|
|
// clear streaming buffer
|
|
memset(__AXOutSBuffer, 0, sizeof(__AXOutSBuffer));
|
|
DCFlushRange(__AXOutSBuffer, sizeof(__AXOutSBuffer));
|
|
|
|
// initialize audio hardware
|
|
__AXOutInitDSP();
|
|
AIRegisterDMACallback(__AXOutAiCallback);
|
|
|
|
// prepare AX
|
|
__AXNextFrame(__AXOutSBuffer, &__AXOutBuffer[1][0]);
|
|
__AXOutDspReady = 1;
|
|
__AXUserFrameCallback = NULL;
|
|
|
|
// warm-up AX by playing first empty buffer
|
|
AIInitDMA(&__AXOutBuffer[__AXOutFrame][0], 640);
|
|
AIStartDMA();
|
|
}
|
|
|
|
// user callback
|
|
void AXRegisterCallback(AXUserCallback callback)
|
|
{
|
|
__AXUserFrameCallback = callback;
|
|
}
|
|
|
|
// quick AX timing note
|
|
/*/
|
|
Gekko CPU clock is 486000000 ticks per second
|
|
|
|
1 NTSC frame 486000000 / 50 = 9720000 ticks per second
|
|
1 PAL frame 486000000 / 60 = 8100000 ticks per second
|
|
|
|
AX always plays 160 16-bit stereo samples per audio frame (160 * 2 * 2 = 640 bytes)
|
|
so AX frame length is 0.005 seconds for 32000 hz (5 msec)
|
|
and 0.00333333333.. seconds for 48000 hz (4 msec)
|
|
|
|
or AX frame length is 2430000 ticks for 32000 hz
|
|
and 1620000 ticks for 48000 hz
|
|
|
|
summary (AX frames per single video frame) :
|
|
|
|
-----------------------
|
|
| video | 32000 | 48000 |
|
|
|-----------------------|
|
|
| NTSC | 4 | 6 |
|
|
| PAL | 3.333 | 5 |
|
|
-----------------------
|
|
/*/
|