/*---------------------------------------------------------------------------* Project: Revolution AX simple demo File: axsimple.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: axsimple.c,v $ Revision 1.8 2009/04/02 01:28:39 aka Copied from SDK_3_2_DEV_BRANCH. Revision 1.7.22.1 2009/04/02 01:26:38 aka Revised miss spelling (aquire->acquire). Revision 1.7 2006/11/21 08:29:31 aka Removed the zero buffer. 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/03 12:12:08 aka Revised comment. Revision 1.3 2006/02/02 07:56:31 aka Modified using MEM functions instead of OSAlloc()/OSFree(). Revision 1.2 2006/02/01 05:42:37 aka Added #ifndef(#ifdef) HOLLYWOOD_REV - #else - #endif. Revision 1.1 2005/11/04 05:01:39 aka Imported from dolphin tree. 11 03/04/04 13:25 Suzuki remove the definition of DSPADPCM 10 12/04/02 6:23p Dante Fixed pred_scale, yn1, & yn2 refrences for AXPBADPCM structure. 9 02/11/20 7:39 Dante Added looped sample support, and fixed dropped sample callback support 8 02/11/13 11:06 Dante Bug fix: GetDSPADPCMDataSize32B's conversion from nibbles to bytes must occur after round up. Clean up (removed unused variables). 7 02/11/13 4:03 Dante Fixed GetDSPADPCMDataSize32B to return bytes not nibbles. Changed OSReports to reflect size. 6 02/11/12 9:52 Dante Removed redundant SRC commands 5 02/11/09 12:37 Dante Additions from SDK 2002-Dec-05 Patch1 4 02/11/09 12:34 Dante Added support for non-native sample rate ADPCM files and a printed intro. 3 02/10/30 6:31 Dante Set the loop address for one shot samples to the zero buffer $NoKeywords: $ *---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------* This program loads and initializes ADPCM samples. The samples were created using DSPADPCM.exe. The files are expected to have a 96 byte header. *---------------------------------------------------------------------------*/ #include #include #include // User defines #define NUM_SAMPLES 10 #define NUM_BUTTON_MAPS 11 // Filenames for samples to be played char SampleFiles [NUM_SAMPLES][256] = { "axdemo/simple/snare.dsp", "axdemo/simple/tom.dsp", "axdemo/simple/cymbal.dsp", "axdemo/simple/ride.dsp", "axdemo/simple/cowbell.dsp", "axdemo/simple/kick.dsp", "axdemo/simple/bongo1.dsp", "axdemo/simple/bongo2.dsp", "axdemo/simple/bongo3.dsp", "axdemo/simple/bongo4.dsp", }; // Button mappings for samples {Button, index in file table} u32 ButtonMap [NUM_BUTTON_MAPS][2] = { {PAD_BUTTON_A, 0}, {PAD_BUTTON_B, 1}, {PAD_BUTTON_X, 2}, {PAD_BUTTON_Y, 3}, {PAD_TRIGGER_Z, 4}, {PAD_TRIGGER_L, 5}, {PAD_TRIGGER_R, 5}, {PAD_BUTTON_DOWN, 6}, {PAD_BUTTON_UP, 7}, {PAD_BUTTON_LEFT, 8}, {PAD_BUTTON_RIGHT, 9} }; // This demo uses a very simple mixing paradigm. All sounds are played at // a volume of 1.0 (0x8000). Please see the MIX library for a more // comprehensive mixing library AXPBMIX g_mix = { 0x8000, // volume left 0x0000, // volume ramp left 0x8000, // volume right 0x0000, // volume ramp right 0x0000, // volume AUX A left 0x0000, // volume ramp AUX A left 0x0000, // volume AUX A right 0x0000, // volume ramp AUX A right 0x0000, // volume AUX B left 0x0000, // volume ramp AUX B left 0x0000, // volume AUX B right 0x0000, // volume ramp AUX B right 0x0000, // volume AUX C left 0x0000, // volume ramp AUX C left 0x0000, // volume AUX C right 0x0000, // volume ramp AUX C right 0x0000, // volume surround 0x0000, // volume ramp surround 0x0000, // volume AUX A surround 0x0000, // volume ramp AUX A surround 0x0000, // volume AUX B surround 0x0000, // volume ramp AUX B surround 0x0000, // volume AUX C surround 0x0000, // volume ramp AUX C surround }; AXPBVE g_ve = { 0x8000, // volume at start of frame, 0x8000 = 1.0 0 // signed per sample delta (160 samples per frame) }; // User Structures to keep track of data typedef struct { void *mramAddr; } SampleInfo; typedef struct { AXVPB *voice; u32 state; } VoiceInfo; // Voice Defines #define VOICE_PRIO_HIGH 31 #define VOICE_PRIO_MED 15 #define VOICE_PRIO_LOW 1 #define VOICE_STATE_STOPPED 0 #define VOICE_STATE_START 1 #define VOICE_STATE_STARTED 2 #define VOICE_STATE_PLAYING 3 #define VOICE_STATE_STOP 4 // Exp Heap static MEMHeapHandle hExpHeap; // Utility Macro Functions #define RoundUp64(x) (((u32)(x) + 64 - 1) & ~(64 - 1)) #define Bytes2Nibbles(n) (n << 1) #define Nibbles2Bytes(n) (n >> 1) #define GetDSPADPCMDataAddress(a) ((void*)((u32)a + sizeof(DSPADPCM))) #define GetDSPADPCMDataSize32B(a) (RoundUp64(((DSPADPCM*)a)->num_adpcm_nibbles) >> 1) #define GetVoiceCurrentAddr32(v) (*(u32 *)(&((v)->pb.addr.currentAddressHi))) #define GetVoiceLoopAddr32(v) (*(u32 *)(&((v)->pb.addr.loopAddressHi))) #define GetVoiceEndAddr32(v) (*(u32 *)(&((v)->pb.addr.endAddressHi))) // Sample Info static SampleInfo Samples[NUM_SAMPLES]; // AX Voice info static VoiceInfo Voices[AX_MAX_VOICES]; // function Prototypes static void * LoadFileIntoRam(char *path); static void AudioFrameCallback(void); static AXVPB* AcquireVoiceADPCM(void *pDSPADPCMData); static void LoadSamples(void); static void PlaySample(SampleInfo *sample); static void VoiceCallback(void * voiceIn); static void PrintIntro(void); /*---------------------------------------------------------------------------* *---------------------------------------------------------------------------*/ void main () { u32 i; u32 button; void *arenaMem2Lo; void *arenaMem2Hi; void *axBuffer; // Initialize OS & Audio DEMOInit(NULL); DEMOPadInit(); // initialize Exp Heap on MEM2 arenaMem2Lo = OSGetMEM2ArenaLo(); arenaMem2Hi = OSGetMEM2ArenaHi(); hExpHeap = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo); // initialize AI & AX axBuffer = MEMAllocFromExpHeapEx(hExpHeap, AXGetMemorySize(AX_MAX_VOICES), 32); AIInit(NULL); AXInitSpecifyMem(AX_MAX_VOICES, axBuffer); // Load Voice data into MRAM LoadSamples(); // Register Callback with AX for audio processing AXRegisterCallback(&AudioFrameCallback); // Print Intro PrintIntro(); // Spin while (1) { VIWaitForRetrace(); // User Input DEMOPadRead(); button = DEMOPadGetButtonDown(0); // Stop all sounds when START/PAUSE is pressed if (button & PAD_BUTTON_START) { // Stop all voices for (i = 0; i < AX_MAX_VOICES; i++) { Voices[i].state = VOICE_STATE_STOP; } continue; } // Use Button map to start sounds for (i = 0; i < NUM_BUTTON_MAPS; i++) { if (button & ButtonMap[i][0]) { PlaySample(&Samples[ButtonMap[i][1]]); } } } } /*---------------------------------------------------------------------------* Name: LoadSamples Description: Loads ADPCM files into Main Memory (Header + Data) and ARAM (Data) Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void LoadSamples(void) { u32 i; // Load samples for (i = 0; i < NUM_SAMPLES; i++) { // Load ADPCM file into MRAM (96 byte header included) Samples[i].mramAddr = LoadFileIntoRam(SampleFiles[i]); // Sanity Check if (Samples[i].mramAddr == NULL) { OSReport("WARNING! Sample %d not loaded\n", i); continue; } } } /*---------------------------------------------------------------------------* Name: PlaySample Description: Utility function that will play a sample Arguments: sample Pointer to the sample information Returns: pointer to the allocated voice *---------------------------------------------------------------------------*/ static void PlaySample(SampleInfo *sample) { AXVPB *voice; if (sample->mramAddr == NULL) { OSReport("WARNING! Sample not loaded!\n"); return; } // Acquire Voice and start voice = AcquireVoiceADPCM(sample->mramAddr); if (voice == NULL) { OSReport("WARNING: Ran out of voices!\n"); return; } Voices[voice->index].voice = voice; Voices[voice->index].state = VOICE_STATE_START; } /*---------------------------------------------------------------------------* Name: AudioFrameCallback Description: Callback that process audio data per 5ms audio frame. In this case, it stops, resets, and starts a voice. Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void AudioFrameCallback(void) { u32 i; BOOL bStopFlag = FALSE; // Monitor each voice and process states. This must be done in the // callback for (i = 0; i < AX_MAX_VOICES; i++) { // Skip NULL entries if (Voices[i].voice == NULL) continue; switch (Voices[i].state) { case VOICE_STATE_STOPPED: break; case VOICE_STATE_START: // Start the voice AXSetVoiceState(Voices[i].voice, AX_PB_STATE_RUN); Voices[i].state = VOICE_STATE_STARTED; break; case VOICE_STATE_STARTED: // Skip a frame Voices[i].state = VOICE_STATE_PLAYING; break; case VOICE_STATE_PLAYING: // Check to see if the voice is finished, if so, stop it if (Voices[i].voice->pb.state == AX_PB_STATE_STOP) bStopFlag = TRUE; break; case VOICE_STATE_STOP: // Force a voice to stop bStopFlag = TRUE; break; } // A voice must be stopped if (bStopFlag) { AXSetVoiceState(Voices[i].voice, AX_PB_STATE_STOP); AXFreeVoice(Voices[i].voice); Voices[i].voice = NULL; Voices[i].state = VOICE_STATE_STOPPED; bStopFlag = FALSE; } } } /*---------------------------------------------------------------------------* Name: VoiceCallback Description: Callback for when a voice is dropped. Arguments: unused Returns: none *---------------------------------------------------------------------------*/ static void VoiceCallback(void * voiceIn) { AXVPB *voice = (AXVPB*)voiceIn; // Note: Voice is auto-magically stopped by AX layer when dropped // Application clean-up Voices[voice->index].voice = NULL; Voices[voice->index].state = VOICE_STATE_STOPPED; } /*---------------------------------------------------------------------------* Name: AcquireVoiceADPCM Description: Parses the ADPCM header, sets voice parameters Arguments: pDSPADPCMData Pointer to the ADPCM data in MRAM pARAMStart ARAM Start address Returns: pointer to the allocated voice or NULL *---------------------------------------------------------------------------*/ static AXVPB* AcquireVoiceADPCM(void *pDSPADPCMData) { DSPADPCM *ps = (DSPADPCM*)pDSPADPCMData; AXPBADDR addr; AXPBADPCM adpcm; AXPBSRC src; AXPBADPCMLOOP adpcmLoop; AXVPB* voice; u32 srcBits; u32 mramAddress; u32 pMRAMStart; // Allocate a voice for use voice = AXAcquireVoice(VOICE_PRIO_MED, VoiceCallback, 0); if (voice == NULL) { OSReport("WARNING! Voice Acquisition failed!\n"); return NULL; } // Fill AXPBADDR structure // All the folowing addresses are in nibbles addr.loopFlag = ps->loop_flag; addr.format = ps->format; pMRAMStart = OSCachedToPhysical(GetDSPADPCMDataAddress(pDSPADPCMData)); // Support for looping if (addr.loopFlag) { adpcmLoop.loop_pred_scale = ps->lps; adpcmLoop.loop_yn1 = ps->lyn1; adpcmLoop.loop_yn2 = ps->lyn2; } mramAddress = (ps->sa + Bytes2Nibbles(pMRAMStart)); addr.loopAddressHi = (u16)(mramAddress >> 16); addr.loopAddressLo = (u16)(mramAddress & 0xFFFF); mramAddress = (ps->ea + Bytes2Nibbles(pMRAMStart)); addr.endAddressHi = (u16)(mramAddress >> 16); addr.endAddressLo = (u16)(mramAddress & 0xFFFF); mramAddress = (ps->ca + Bytes2Nibbles(pMRAMStart)); addr.currentAddressHi = (u16)(mramAddress >> 16); addr.currentAddressLo = (u16)(mramAddress & 0xFFFF); // Fill AXPBADPCM structure adpcm.a[0][0] = ps->coef[0]; adpcm.a[0][1] = ps->coef[1]; adpcm.a[1][0] = ps->coef[2]; adpcm.a[1][1] = ps->coef[3]; adpcm.a[2][0] = ps->coef[4]; adpcm.a[2][1] = ps->coef[5]; adpcm.a[3][0] = ps->coef[6]; adpcm.a[3][1] = ps->coef[7]; adpcm.a[4][0] = ps->coef[8]; adpcm.a[4][1] = ps->coef[9]; adpcm.a[5][0] = ps->coef[10]; adpcm.a[5][1] = ps->coef[11]; adpcm.a[6][0] = ps->coef[12]; adpcm.a[6][1] = ps->coef[13]; adpcm.a[7][0] = ps->coef[14]; adpcm.a[7][1] = ps->coef[15]; adpcm.gain = ps->gain; adpcm.pred_scale = ps->ps; adpcm.yn1 = ps->yn1; adpcm.yn2 = ps->yn2; // Fill AXPBSRC structure for proper sample rates srcBits = (u32)(0x00010000 * ((f32)ps->sample_rate / AX_IN_SAMPLES_PER_SEC)); src.ratioHi = (u16)(srcBits >> 16); src.ratioLo = (u16)(srcBits & 0xFFFF); src.currentAddressFrac = 0; src.last_samples[0] = 0; src.last_samples[1] = 0; src.last_samples[2] = 0; src.last_samples[3] = 0; // Set voice type AXSetVoiceType(voice, AX_PB_TYPE_NORMAL); // Set Address and ADPCM information from header AXSetVoiceAddr(voice, &addr); AXSetVoiceAdpcm(voice, &adpcm); AXSetVoiceAdpcmLoop(voice, &adpcmLoop); // Set simple volumes AXSetVoiceMix(voice, &g_mix); AXSetVoiceVe(voice, &g_ve); // Set sample rate AXSetVoiceSrcType(voice, AX_SRC_TYPE_LINEAR); AXSetVoiceSrc(voice, &src); return voice; } /*---------------------------------------------------------------------------* Name: LoadFileIntoRam Description: Loads a file into memory. Memory is allocated. Arguments: path File to load into main memory Returns: pointer to file in main memory or NULL if not opened *---------------------------------------------------------------------------*/ static void * LoadFileIntoRam(char *path) { DVDFileInfo handle; u32 round_length; s32 read_length; void *buffer; // Open File if (!DVDOpen(path, &handle)) { OSReport("WARNING! Failed to open %s\n", path); return NULL; } // Make sure file length is not 0 if (DVDGetLength(&handle) == 0) { OSReport("WARNING! File length is 0\n"); return NULL; } round_length = OSRoundUp32B(DVDGetLength(&handle)); buffer = MEMAllocFromExpHeapEx(hExpHeap, round_length, 32); // Make sure we got a buffer if (buffer == NULL) { OSReport("WARNING! Unable to allocate buffer\n"); return NULL; } // Read Files read_length = DVDRead(&handle, buffer, (s32)(round_length), 0); // Make sure we read the file correctly if (read_length <= 0) { OSReport("WARNING! File lenght is wrong\n"); return NULL; } return buffer; } /*---------------------------------------------------------------------------* Name: PrintIntro Description: Prints Intro to debug output Arguments: none Returns: none *---------------------------------------------------------------------------*/ static void PrintIntro(void) { char button[256]; u32 i; OSReport("\n\n****************************************************\n"); OSReport(" AXSimple - Plays DSPADPCM.exe files\n"); OSReport("****************************************************\n"); for (i = 0; i < NUM_BUTTON_MAPS; i++) { switch (ButtonMap[i][0]) { case PAD_BUTTON_A: sprintf(button, "A Button"); break; case PAD_BUTTON_B: sprintf(button, "B Button"); break; case PAD_BUTTON_X: sprintf(button, "X Button"); break; case PAD_BUTTON_Y: sprintf(button, "Y Button"); break; case PAD_TRIGGER_Z: sprintf(button, "Z Button"); break; case PAD_TRIGGER_L: sprintf(button, "L Button"); break; case PAD_TRIGGER_R: sprintf(button, "R Button"); break; case PAD_BUTTON_DOWN: sprintf(button, "+Control Pad Down"); break; case PAD_BUTTON_UP: sprintf(button, "+Control Pad Up"); break; case PAD_BUTTON_LEFT: sprintf(button, "+Control Pad Left"); break; case PAD_BUTTON_RIGHT: sprintf(button, "+Control Pad Right"); break; } OSReport("%s => %s\n", button, SampleFiles[ButtonMap[i][1]]); } OSReport("Start/Pause => Stop all sounds\n"); OSReport("****************************************************\n"); }