/*---------------------------------------------------------------------------* Project: Revolution AX stream demo File: axstream.c Copyright (C)1998-2006 Nintendo All Rights Reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Log: axstream.c,v $ Revision 1.13 2007/09/25 02:25:35 aka Added checking flag in DVDRead callbacks. Revision 1.12 2007/09/03 09:01:01 aka Changed from *.adpcm to *.dsp. Revision 1.11 2007/05/18 03:01:36 ekwon Bug fix: predictor/scale values not updated at loop point; game developers reported some pops/clicks in some streams. Not audible in our demo because our sample happens to be ok without predictor/scale update. oops. Revision 1.7.2.1 2006/12/27 07:31:56 aka for SDK2.4 patch2. Revision 1.8 2006/12/20 05:56:05 aka Added memory clear when starting stream. Changed detecting way of stream end. Revision 1.7 2006/11/20 04:22:12 aka Revised codes of getting stream data from DVD. Revision 1.6 2006/10/23 02:05:52 aka Changed from AXInit() to AXInitSpecifyMem(). Changed from MIXInit() to MIXInitSpecifyMem(). Changed from SYNInit() to SYNInitSpecifyMem(). Revision 1.5 2006/10/10 08:30:06 aka Revised AXInit(), MIXInit() and SYNInit(). Revision 1.4 2006/02/02 08:12:18 aka Modified using MEM functions instead of OSAlloc()/OSFree(). Revision 1.3 2006/02/01 05:16:03 aka Added #ifndef(#ifdef) HOLLYWOOD_REV - #else - #endif. Revision 1.2 2005/11/08 02:55:02 aka Changed suiting to Revolution's audio spec. Revision 1.1 2005/11/04 05:01:39 aka Imported from dolphin tree. 10 04/04/21 11:18a Akagi Added considering alignment of stream buffers. 9 03/04/24 8:45 Dante Removed ramBufferLenAdpcm 8 03/04/15 8:08 Dante Modified to read APDCP header instead of hard coded information & coefficients. 7 7/01/02 4:18p Billyjack changed ramBufferLenPcm16 = 827260; to ramBufferLenPcm16 = 827270; used to be off by 10 bytes at the end where it was silent 6 12/11/01 7:02p Billyjack - keep interrupts disabled during audio frame callback 5 8/03/01 4:32p Billyjack added OSEnableInterrupts() and OSRestoreInterrupts() to AX aufdio frame callback per change in AX lib. 4 7/06/01 11:50a Billyjack commented DCInvalidateRange for DVD to RAM transfers 3 5/14/01 1:39p Billyjack - reworked for DVDDATA file location and names - uses ARGetBaseAddress where applicable 2 5/09/01 6:09p Billyjack now uses the mix lib 1 3/26/01 2:32p Billyjack created $NoKeywords: $ *---------------------------------------------------------------------------*/ #include #include #include #include #include /*---------------------------------------------------------------------------* stream control flag *---------------------------------------------------------------------------*/ #define STREAM_NONE 0 #define STREAM_INITIALIZING 1 #define STREAM_STARTED 2 #define STREAM_STOPPING 3 #define STREAM_STOPPED 4 static u32 flag = STREAM_NONE; /*---------------------------------------------------------------------------* stream data *---------------------------------------------------------------------------*/ #define SAMPLE_PCM16_L "/axdemo/stream/left.pcm16" #define SAMPLE_PCM16_R "/axdemo/stream/right.pcm16" #define SAMPLE_PCM8_L "/axdemo/stream/left.pcm8" #define SAMPLE_PCM8_R "/axdemo/stream/right.pcm8" #define SAMPLE_ADPCM_L "/axdemo/stream/left.dsp" #define SAMPLE_ADPCM_R "/axdemo/stream/right.dsp" // stream info typedef struct { DVDFileInfo finfoL; DVDFileInfo finfoR; u16 format; s32 done; s32 rest; s32 offset; u32 end_addr; } STRMInfo; static STRMInfo strm; /*---------------------------------------------------------------------------* AX voice *---------------------------------------------------------------------------*/ // voice static AXVPB *streamL = NULL; static AXVPB *streamR = NULL; /*---------------------------------------------------------------------------* DVD read *---------------------------------------------------------------------------*/ // DVD control flag static BOOL active = FALSE; // write buffer offset static s32 offset; // stream buffer #define STREAMBUFFER_BYTES (32 * 1024 * 2) // 32KB x DoubleBuffer static u8 *streamBufferL = NULL; static u8 *streamBufferR = NULL; // stream pointer static u32 half_ptr; static u32 prev_ptr; /*---------------------------------------------------------------------------* Name: gottenData Description: function called when DVD read (of L&R ch) is done. Arguments: no use Returns: none *---------------------------------------------------------------------------*/ static void gottenData(s32 result, DVDFileInfo* fileInfo) { #pragma unused(result) #pragma unused(fileInfo) // if we just filled the first half of the buffer we need to set the // loop context if (offset == 0 && flag == STREAM_STARTED ) { AXPBADPCMLOOP loop; loop.loop_pred_scale = (u16)(*((u8*)(streamBufferR))); loop.loop_yn1 = 0; loop.loop_yn2 = 0; AXSetVoiceAdpcmLoop(streamR, &loop); } active = FALSE; } /*---------------------------------------------------------------------------* Name: getDataR Description: read stream data for Rch. Arguments: no use Returns: none *---------------------------------------------------------------------------*/ static void getDataR(s32 result, DVDFileInfo* fileInfo) { #pragma unused(result) #pragma unused(fileInfo) s32 size; u8 *buffer; s32 length; s32 rlength; size = STREAMBUFFER_BYTES / 2; buffer = streamBufferR + offset; length = strm.rest > size? size: strm.rest; rlength = length & ~0x1f; // memset(buffer + rlength, 0, (u32)(size - rlength)); // DCFlushRange(buffer + rlength, (u32)(size - rlength)); DVDReadAsync(&strm.finfoR, buffer, rlength, strm.offset, gottenData); strm.rest -= rlength; strm.offset += rlength; // if we just filled the first half of the buffer we need to set the // loop context if (offset == 0 && flag == STREAM_STARTED ) { AXPBADPCMLOOP loop; loop.loop_pred_scale = (u16)(*((u8*)(streamBufferL))); loop.loop_yn1 = 0; loop.loop_yn2 = 0; AXSetVoiceAdpcmLoop(streamL, &loop); } } /*---------------------------------------------------------------------------* Name: getDataL Description: read stream data for Lch. Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void getDataL(void) { s32 size; u8 *buffer; s32 length; s32 rlength; active = TRUE; size = STREAMBUFFER_BYTES / 2; buffer = streamBufferL + offset; length = strm.rest > size? size: strm.rest; rlength = length & ~0x1f; // memset(buffer + rlength, 0, (u32)(size - rlength)); // DCFlushRange(buffer + rlength, (u32)(size - rlength)); DVDReadAsync(&strm.finfoL, buffer, rlength, strm.offset, getDataR); if (!((strm.rest - rlength) & ~0x1f)) { strm.done++; if (rlength) { strm.end_addr = (u32)OSCachedToPhysical(buffer) + rlength; } } } /*---------------------------------------------------------------------------* Name: callbackAudioFrame Description: function called every audio frame. Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void callbackAudioFrame(void) { u32 curr_ptr; // tell the mixer to update settings MIXUpdateSettings(); switch (flag) { case STREAM_NONE: case STREAM_INITIALIZING: break; case STREAM_STARTED: curr_ptr = (u32)(streamL->pb.addr.currentAddressHi << 16) | (streamL->pb.addr.currentAddressLo); if (strm.format == AX_PB_FORMAT_PCM16) { curr_ptr <<= 1; } else if (strm.format == AX_PB_FORMAT_ADPCM) { curr_ptr >>= 1; } if (strm.done) { if (((curr_ptr < prev_ptr) && ((prev_ptr < strm.end_addr) || (strm.end_addr <= curr_ptr))) || ((prev_ptr < curr_ptr) && ((prev_ptr < strm.end_addr) && (strm.end_addr <= curr_ptr)))) { flag = STREAM_STOPPING; } } if (curr_ptr < prev_ptr) { // fill 2nd half if (!active) { offset = STREAMBUFFER_BYTES / 2; getDataL(); prev_ptr = curr_ptr; } } else if ((prev_ptr < half_ptr) && (curr_ptr >= half_ptr)) { // fill 1st half if (!active) { offset = 0; getDataL(); prev_ptr = curr_ptr; } } else { prev_ptr = curr_ptr; } break; case STREAM_STOPPING: // Lch MIXReleaseChannel(streamL); AXFreeVoice(streamL); streamL = NULL; // Rch MIXReleaseChannel(streamR); AXFreeVoice(streamR); streamR = NULL; flag = STREAM_STOPPED; break; case STREAM_STOPPED: if (!active) { DVDClose(&strm.finfoL); DVDClose(&strm.finfoR); flag = STREAM_NONE; } break; } } /*---------------------------------------------------------------------------* Name: startStreamPcm16 Description: play PCM16 stream. Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void startStreamPcm16(void) { s32 length; s32 rlength; AXPBADDR addr; u32 ls_addr; u32 le_addr; if (flag != STREAM_NONE) { return; } flag = STREAM_INITIALIZING; // open stream files if (!DVDOpen(SAMPLE_PCM16_L, &strm.finfoL) || !DVDOpen(SAMPLE_PCM16_R, &strm.finfoR)) { OSHalt("Cannot open stream files\n"); } strm.format = AX_PB_FORMAT_PCM16; strm.done = 0; strm.rest = (s32)DVDGetLength(&strm.finfoL); strm.offset = 0; // read stream data length = strm.rest > STREAMBUFFER_BYTES? STREAMBUFFER_BYTES: strm.rest; rlength = length & ~0x1f; memset(streamBufferL + rlength, 0, (u32)(STREAMBUFFER_BYTES - rlength)); memset(streamBufferR + rlength, 0, (u32)(STREAMBUFFER_BYTES - rlength)); DCFlushRange(streamBufferL + rlength, (u32)(STREAMBUFFER_BYTES - rlength)); DCFlushRange(streamBufferR + rlength, (u32)(STREAMBUFFER_BYTES - rlength)); DVDRead(&strm.finfoL, streamBufferL, rlength, strm.offset); DVDRead(&strm.finfoR, streamBufferR, rlength, strm.offset); strm.rest -= rlength; strm.offset += rlength; if (!(strm.rest & ~0x1f)) { strm.done++; strm.end_addr = (u32)OSCachedToPhysical(streamBufferL) + rlength; } // setup pointer half_ptr = (u32)OSCachedToPhysical(streamBufferL) + STREAMBUFFER_BYTES / 2; prev_ptr = (u32)OSCachedToPhysical(streamBufferL); // acquire voices if (!(streamL = AXAcquireVoice(15, NULL, 0)) || !(streamR = AXAcquireVoice(15, NULL, 0))) { OSHalt("Cannot acquire voice\n"); } // setup Lch ls_addr = (u32)OSCachedToPhysical(streamBufferL) >> 1; le_addr = ls_addr + (STREAMBUFFER_BYTES >> 1) - 1; MIXInitChannel(streamL, 0, 0, -904, -904, -904, 0, 127, 0); addr.loopFlag = AXPBADDR_LOOP_ON; addr.format = AX_PB_FORMAT_PCM16; addr.loopAddressHi = (u16)(ls_addr >> 16); addr.loopAddressLo = (u16)(ls_addr & 0xFFFF); addr.endAddressHi = (u16)(le_addr >> 16); addr.endAddressLo = (u16)(le_addr & 0xFFFF); addr.currentAddressHi = (u16)(ls_addr >> 16); addr.currentAddressLo = (u16)(ls_addr & 0xFFFF); AXSetVoiceAddr (streamL, &addr); AXSetVoiceSrcType(streamL, AX_SRC_TYPE_NONE); AXSetVoiceState (streamL, AX_PB_STATE_RUN); // setup Rch ls_addr = (u32)OSCachedToPhysical(streamBufferR) >> 1; le_addr = ls_addr + (STREAMBUFFER_BYTES >> 1) - 1; MIXInitChannel(streamR, 0, 0, -904, -904, -904, 127, 127, 0); addr.loopFlag = AXPBADDR_LOOP_ON; addr.format = AX_PB_FORMAT_PCM16; addr.loopAddressHi = (u16)(ls_addr >> 16); addr.loopAddressLo = (u16)(ls_addr & 0xFFFF); addr.endAddressHi = (u16)(le_addr >> 16); addr.endAddressLo = (u16)(le_addr & 0xFFFF); addr.currentAddressHi = (u16)(ls_addr >> 16); addr.currentAddressLo = (u16)(ls_addr & 0xFFFF); AXSetVoiceAddr (streamR, &addr); AXSetVoiceSrcType(streamR, AX_SRC_TYPE_NONE); AXSetVoiceState (streamR, AX_PB_STATE_RUN); flag = STREAM_STARTED; } /*---------------------------------------------------------------------------* Name: startStreamPcm8 Description: play PCM8 stream. Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void startStreamPcm8(void) { s32 length; s32 rlength; AXPBADDR addr; u32 ls_addr; u32 le_addr; if (flag != STREAM_NONE) { return; } flag = STREAM_INITIALIZING; // open stream files if (!DVDOpen(SAMPLE_PCM8_L, &strm.finfoL) || !DVDOpen(SAMPLE_PCM8_R, &strm.finfoR)) { OSHalt("Cannot open stream files\n"); } strm.format = AX_PB_FORMAT_PCM8; strm.done = 0; strm.rest = (s32)DVDGetLength(&strm.finfoL); strm.offset = 0; // read data length = strm.rest > STREAMBUFFER_BYTES? STREAMBUFFER_BYTES: strm.rest; rlength = length & ~0x1f; memset(streamBufferL + rlength, 0, (u32)(STREAMBUFFER_BYTES - rlength)); memset(streamBufferR + rlength, 0, (u32)(STREAMBUFFER_BYTES - rlength)); DCFlushRange(streamBufferL + rlength, (u32)(STREAMBUFFER_BYTES - rlength)); DCFlushRange(streamBufferR + rlength, (u32)(STREAMBUFFER_BYTES - rlength)); DVDRead(&strm.finfoL, streamBufferL, rlength, strm.offset); DVDRead(&strm.finfoR, streamBufferR, rlength, strm.offset); strm.rest -= rlength; strm.offset += rlength; if (!(strm.rest & ~0x1f)) { strm.done++; strm.end_addr = (u32)OSCachedToPhysical(streamBufferL) + rlength; } // setup pointer half_ptr = (u32)OSCachedToPhysical(streamBufferL) + STREAMBUFFER_BYTES / 2; prev_ptr = (u32)OSCachedToPhysical(streamBufferL); // acquire voices if (!(streamL = AXAcquireVoice(15, NULL, 0)) || !(streamR = AXAcquireVoice(15, NULL, 0))) { OSHalt("Cannot acquire voice\n"); } // setup Lch ls_addr = (u32)OSCachedToPhysical(streamBufferL); le_addr = ls_addr + STREAMBUFFER_BYTES - 1; MIXInitChannel(streamL, 0, 0, -904, -904, -904, 0, 127, 0); addr.loopFlag = AXPBADDR_LOOP_ON; addr.format = AX_PB_FORMAT_PCM8; addr.loopAddressHi = (u16)(ls_addr >> 16); addr.loopAddressLo = (u16)(ls_addr & 0xFFFF); addr.endAddressHi = (u16)(le_addr >> 16); addr.endAddressLo = (u16)(le_addr & 0xFFFF); addr.currentAddressHi = (u16)(ls_addr >> 16); addr.currentAddressLo = (u16)(ls_addr & 0xFFFF); AXSetVoiceAddr (streamL, &addr); AXSetVoiceSrcType(streamL, AX_SRC_TYPE_NONE); AXSetVoiceState (streamL, AX_PB_STATE_RUN); // setup Rch ls_addr = (u32)OSCachedToPhysical(streamBufferR); le_addr = ls_addr + STREAMBUFFER_BYTES - 1; MIXInitChannel(streamR, 0, 0, -904, -904, -904, 127, 127, 0); addr.loopFlag = AXPBADDR_LOOP_ON; addr.format = AX_PB_FORMAT_PCM8; addr.loopAddressHi = (u16)(ls_addr >> 16); addr.loopAddressLo = (u16)(ls_addr & 0xFFFF); addr.endAddressHi = (u16)(le_addr >> 16); addr.endAddressLo = (u16)(le_addr & 0xFFFF); addr.currentAddressHi = (u16)(ls_addr >> 16); addr.currentAddressLo = (u16)(ls_addr & 0xFFFF); AXSetVoiceAddr (streamR, &addr); AXSetVoiceSrcType(streamR, AX_SRC_TYPE_NONE); AXSetVoiceState (streamR, AX_PB_STATE_RUN); flag = STREAM_STARTED; } /*---------------------------------------------------------------------------* Name: startStreamAdpcm Description: play ADPCM stream. Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void startStreamAdpcm(void) { s32 length; s32 rlength; DSPADPCM *dspHeader; AXPBADDR addr; AXPBADPCM adpcm; u32 ls_addr; u32 le_addr; u32 cu_addr; if (flag != STREAM_NONE) { return; } flag = STREAM_INITIALIZING; // open stream files if (!DVDOpen(SAMPLE_ADPCM_L, &strm.finfoL) || !DVDOpen(SAMPLE_ADPCM_R, &strm.finfoR)) { OSHalt("Cannot open stream files\n"); } strm.format = AX_PB_FORMAT_ADPCM; strm.done = 0; strm.rest = (s32)DVDGetLength(&strm.finfoL); strm.offset = 0; // read data length = strm.rest > STREAMBUFFER_BYTES? STREAMBUFFER_BYTES: strm.rest; rlength = length & ~0x1f; memset(streamBufferL + rlength, 0, (u32)(STREAMBUFFER_BYTES - rlength)); memset(streamBufferR + rlength, 0, (u32)(STREAMBUFFER_BYTES - rlength)); DCFlushRange(streamBufferL + rlength, (u32)(STREAMBUFFER_BYTES - rlength)); DCFlushRange(streamBufferR + rlength, (u32)(STREAMBUFFER_BYTES - rlength)); DVDRead(&strm.finfoL, streamBufferL, rlength, strm.offset); DVDRead(&strm.finfoR, streamBufferR, rlength, strm.offset); strm.rest -= rlength; strm.offset += rlength; if (!(strm.rest & ~0x1f)) { strm.done++; strm.end_addr = (u32)OSCachedToPhysical(streamBufferL) + rlength; } // setup pointer half_ptr = (u32)OSCachedToPhysical(streamBufferL) + STREAMBUFFER_BYTES / 2; prev_ptr = (u32)OSCachedToPhysical(streamBufferL); // acquire voices if (!(streamL = AXAcquireVoice(15, NULL, 0)) || !(streamR = AXAcquireVoice(15, NULL, 0))) { OSHalt("Cannot acquire voice\n"); } // // - Now half of the stream buffer size is 32KB which is enough larger than size of // ADPCM header (DSPADPCM structure + initial current address (= dspHeader->ca)). // So this program applies the stream buffer to the ADPCM header temporarily. // // - Must avoid locating the loop start & end addr (ls_addr & le_addr) on the frame // header. // // setup Lch dspHeader = (DSPADPCM *)streamBufferL; ls_addr = (u32)OSCachedToPhysical(streamBufferL) << 1; le_addr = ls_addr + (STREAMBUFFER_BYTES << 1) - 1; cu_addr = ls_addr + (sizeof(DSPADPCM) << 1) + dspHeader->ca; ls_addr += 2; // skip the frame header MIXInitChannel(streamL, 0, 0, -904, -904, -904, 0, 127, 0); addr.loopFlag = AXPBADDR_LOOP_ON; addr.format = AX_PB_FORMAT_ADPCM; addr.loopAddressHi = (u16)(ls_addr >> 16); addr.loopAddressLo = (u16)(ls_addr & 0xFFFF); addr.endAddressHi = (u16)(le_addr >> 16); addr.endAddressLo = (u16)(le_addr & 0xFFFF); addr.currentAddressHi = (u16)(cu_addr >> 16); addr.currentAddressLo = (u16)(cu_addr & 0xFFFF); adpcm.a[0][0] = dspHeader->coef[0]; adpcm.a[0][1] = dspHeader->coef[1]; adpcm.a[1][0] = dspHeader->coef[2]; adpcm.a[1][1] = dspHeader->coef[3]; adpcm.a[2][0] = dspHeader->coef[4]; adpcm.a[2][1] = dspHeader->coef[5]; adpcm.a[3][0] = dspHeader->coef[6]; adpcm.a[3][1] = dspHeader->coef[7]; adpcm.a[4][0] = dspHeader->coef[8]; adpcm.a[4][1] = dspHeader->coef[9]; adpcm.a[5][0] = dspHeader->coef[10]; adpcm.a[5][1] = dspHeader->coef[11]; adpcm.a[6][0] = dspHeader->coef[12]; adpcm.a[6][1] = dspHeader->coef[13]; adpcm.a[7][0] = dspHeader->coef[14]; adpcm.a[7][1] = dspHeader->coef[15]; adpcm.gain = dspHeader->gain; adpcm.pred_scale = dspHeader->ps; adpcm.yn1 = dspHeader->yn1; adpcm.yn2 = dspHeader->yn2; AXSetVoiceType (streamL, AX_PB_TYPE_STREAM); // no loop context AXSetVoiceAdpcm (streamL, &adpcm); AXSetVoiceAddr (streamL, &addr); AXSetVoiceSrcType(streamL, AX_SRC_TYPE_NONE); AXSetVoiceState (streamL, AX_PB_STATE_RUN); // setup Rch dspHeader = (DSPADPCM *)streamBufferR; ls_addr = (u32)OSCachedToPhysical(streamBufferR) << 1; le_addr = ls_addr + (STREAMBUFFER_BYTES << 1) - 1; cu_addr = ls_addr + (sizeof(DSPADPCM) << 1) + dspHeader->ca; ls_addr += 2; // skip the frame header MIXInitChannel(streamR, 0, 0, -904, -904, -904, 127, 127, 0); addr.loopFlag = AXPBADDR_LOOP_ON; addr.format = AX_PB_FORMAT_ADPCM; addr.loopAddressHi = (u16)(ls_addr >> 16); addr.loopAddressLo = (u16)(ls_addr & 0xFFFF); addr.endAddressHi = (u16)(le_addr >> 16); addr.endAddressLo = (u16)(le_addr & 0xFFFF); addr.currentAddressHi = (u16)(cu_addr >> 16); addr.currentAddressLo = (u16)(cu_addr & 0xFFFF); adpcm.a[0][0] = dspHeader->coef[0]; adpcm.a[0][1] = dspHeader->coef[1]; adpcm.a[1][0] = dspHeader->coef[2]; adpcm.a[1][1] = dspHeader->coef[3]; adpcm.a[2][0] = dspHeader->coef[4]; adpcm.a[2][1] = dspHeader->coef[5]; adpcm.a[3][0] = dspHeader->coef[6]; adpcm.a[3][1] = dspHeader->coef[7]; adpcm.a[4][0] = dspHeader->coef[8]; adpcm.a[4][1] = dspHeader->coef[9]; adpcm.a[5][0] = dspHeader->coef[10]; adpcm.a[5][1] = dspHeader->coef[11]; adpcm.a[6][0] = dspHeader->coef[12]; adpcm.a[6][1] = dspHeader->coef[13]; adpcm.a[7][0] = dspHeader->coef[14]; adpcm.a[7][1] = dspHeader->coef[15]; adpcm.gain = dspHeader->gain; adpcm.pred_scale = dspHeader->ps; adpcm.yn1 = dspHeader->yn1; adpcm.yn2 = dspHeader->yn2; AXSetVoiceType (streamR, AX_PB_TYPE_STREAM); // no loop context AXSetVoiceAdpcm (streamR, &adpcm); AXSetVoiceAddr (streamR, &addr); AXSetVoiceSrcType(streamR, AX_SRC_TYPE_NONE); AXSetVoiceState (streamR, AX_PB_STATE_RUN); flag = STREAM_STARTED; } /*---------------------------------------------------------------------------* Name: stopStream Description: stop stream. Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void stopStream(void) { if (flag != STREAM_STARTED) { return; } MIXSetFader(streamL, -960); MIXSetFader(streamR, -960); flag = STREAM_STOPPING; } /*---------------------------------------------------------------------------* *---------------------------------------------------------------------------*/ void main(void) { void *arenaMem2Lo; void *arenaMem2Hi; void *axBuffer; void *mixBuffer; MEMHeapHandle hExpHeap; PADStatus pads[PAD_MAX_CONTROLLERS]; DEMOInit(NULL); // initialize Exp Heap on MEM2 arenaMem2Lo = OSGetMEM2ArenaLo(); arenaMem2Hi = OSGetMEM2ArenaHi(); hExpHeap = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo); // initialize AX and MIX axBuffer = MEMAllocFromExpHeapEx(hExpHeap, AXGetMemorySize(AX_MAX_VOICES), 32); mixBuffer = MEMAllocFromExpHeap(hExpHeap, MIXGetMemorySize(AX_MAX_VOICES)); AIInit(NULL); AXInitSpecifyMem(AX_MAX_VOICES, axBuffer); MIXInitSpecifyMem(mixBuffer); // prepare stream buffers streamBufferL = MEMAllocFromExpHeapEx(hExpHeap, STREAMBUFFER_BYTES, 32); streamBufferR = MEMAllocFromExpHeapEx(hExpHeap, STREAMBUFFER_BYTES, 32); // register user callback for audio frames notification AXRegisterCallback(&callbackAudioFrame); OSReport("Press the A button to play PCM16 stream.\n"); OSReport("Press the B button to play PCM8 stream.\n"); OSReport("Press the X button to play DSP ADPCM stream.\n"); OSReport("Press the Y button to stop the stream.\n"); OSReport("Press START/PAUSE button to exit program\n"); while (1) { if (streamR) { OSReport("L:%x R:%x\n", *(u32*)&streamL->pb.addr.currentAddressHi, *(u32*)&streamR->pb.addr.currentAddressHi); } // wait for retrace VIWaitForRetrace(); // check pad PADRead(pads); // see if we should quit if (pads[0].button & PAD_BUTTON_MENU) { break; } // run PCM16 stream if (pads[0].button & PAD_BUTTON_A) { startStreamPcm16(); } // run PCM8 stream if (pads[0].button & PAD_BUTTON_B) { startStreamPcm8(); } // run ADPCM stream if (pads[0].button & PAD_BUTTON_X) { startStreamAdpcm(); } // stop stream if (pads[0].button & PAD_BUTTON_Y) { stopStream(); } } MIXQuit(); AXQuit(); OSHalt("End of program\n"); }