void AUDSimpleAudioChangePlayback(const u32 *playMaskArray,u32 numMasks) { u32 i,b; BOOL old = OSDisableInterrupts(); audioPlayMaskArray = playMaskArray; audioNumPlayMasks = numMasks; // Any playback mask specified? if (audioNumPlayMasks != 0) { // Yes. Count the active voices... audioNumActiveVoices = 0; for(i=0; i<numMasks; i++) { for(b=0; b<32; b++) { if (audioPlayMaskArray[i] & (1<<b)) audioNumActiveVoices++; } } // So, did we have too much? ASSERT(audioNumActiveVoices <= 2); } else audioNumActiveVoices = audioReadBufferNumChannels; // Make all active... OSRestoreInterrupts(old); }
/** * Release an Uninterruptible Spin Lock. * * Interrupts will be restored to their previous state before * the lock was acquired. * * \return Returns TRUE if the lock was released. */ BOOL OSUninterruptibleSpinLock_Release(OSSpinLock *spinlock) { if (spinReleaseLock(spinlock)) { OSRestoreInterrupts(spinlock->restoreInterruptState); } return TRUE; }
/*! ****************************************************************************** * \brief * Stop audio playback without shutting down AI etc. * ****************************************************************************** */ void AUDSimpleAudioReset(void) { BOOL old; old = OSDisableInterrupts(); audioReadBufferWritePos = 0; audioReadBufferReadPos = 0; audioPlayBufferEnabled = FALSE; OSRestoreInterrupts(old); }
/*! ****************************************************************************** * \brief * Shutdown audio decoder and free resources * ****************************************************************************** */ void AUDSimpleExitAudioDecoder(void) { // Yes. Unregister callback & stop AI DMA BOOL old = OSDisableInterrupts(); // Register our default AX callback. AXRegisterCallback( Sfx::AXUserCBack ); AXSetVoiceState(axVoice[0],AX_PB_STATE_STOP); AXSetVoiceState(axVoice[1],AX_PB_STATE_STOP); AXFreeVoice(axVoice[0]); AXFreeVoice(axVoice[1]); axVoice[0] = axVoice[1] = NULL; OSRestoreInterrupts(old); // Free allocated resources... audio_player.cbFree(audioPlayBuffer[0]); audio_player.cbFree(audioReadBuffer); }
/*! ****************************************************************************** * \brief * Main audio data decode function. * * This function will be used as a callback from the VIDAudioDecode * function or is called directely in case of PCM or ADPCM. * * \param numChannels * Number of channels present in the sample array * * \param samples * Array of s16 pointers to the sample data * * \param sampleNum * Number of samples in the array. All arrays have the same amount * of sample data * * \param userData * Some user data * * \return * FALSE if data could not be interpreted properly * ****************************************************************************** */ static BOOL audioDecode(u32 numChannels, const s16 **samples, u32 sampleNum, void* _UNUSED(userData)) { u32 freeSamples; u32 sampleSize; u32 len1; BOOL old; // we can only play mono or stereo! ASSERT(numChannels <= 2); // Disable IRQs. We must make sure we don't get interrupted by the AI callback. old = OSDisableInterrupts(); // Did the video decoder just jump back to the beginning of the stream? if (audio_player.readBuffer[audio_player.decodeIndex].frameNumber < audio_player.lastDecodedFrame) { // Yes! We have to reset the internal read buffer and disable any audio output // until we got new video data to display... // // Note: we have to reset our buffers because the stream contains more audio data than // neccessary to cover a single video frame within the first few frames to accumulate // some safety buffer. If the stream would just be allowed to loop we would get an // overflow (unless we used up the extra buffer due to read / decode delays) after a few // loops... // AUDSimpleAudioReset(); } // Calculate the read buffer's sample size sampleSize = sizeof(s16) * audioReadBufferNumChannels; // How many samples could we put into the buffer? if (audioReadBufferWritePos >= audioReadBufferReadPos) { freeSamples = audioReadBufferNumSamples - (audioReadBufferWritePos - audioReadBufferReadPos); if( freeSamples < sampleNum ) { OSRestoreInterrupts(old); #ifndef FINAL OSReport("*** audioDecode: overflow case 1\n"); #endif return FALSE; // overflow! } // We might have a two buffer update to do. Check for it... if ((len1 = (audioReadBufferNumSamples - audioReadBufferWritePos)) >= sampleNum) { // No. We got ourselfs a nice, simple single buffer update. writeChannelData((s16 *)((u32)audioReadBuffer + audioReadBufferWritePos * sampleSize),numChannels,samples,0,sampleNum); } else { // Dual buffer case writeChannelData((s16 *)((u32)audioReadBuffer + audioReadBufferWritePos * sampleSize),numChannels,samples,0,len1); writeChannelData((s16 *)audioReadBuffer,numChannels,samples,len1,sampleNum-len1); } } else { freeSamples = audioReadBufferReadPos - audioReadBufferWritePos; if (freeSamples < sampleNum) { OSRestoreInterrupts(old); #ifndef FINAL OSReport("*** audioDecode: overflow case 2\n"); #endif return FALSE; // overflow! } // We're save to assume to have a single buffer update in any case... writeChannelData((s16 *)((u32)audioReadBuffer + audioReadBufferWritePos * sampleSize),numChannels,samples,0,sampleNum); } // Advance write position... audioReadBufferWritePos += sampleNum; if (audioReadBufferWritePos >= audioReadBufferNumSamples) audioReadBufferWritePos -= audioReadBufferNumSamples; // We're done with all critical stuff. IRQs may be enabled again... OSRestoreInterrupts(old); return TRUE; }
/*! ****************************************************************************** * \brief * Initialize audio decoder * * This function allocates all neccessary memory for the audio processing * and sets the audio decoder into an idle state, waiting for first data. * A file must be opened with the VIDSimplePlayer before calling this * function. * * \return * FALSE if any problem was detected * ****************************************************************************** */ BOOL AUDSimpleInitAudioDecoder(void) { u32 i, ratio; BOOL old; AXPBMIX axMix[2]; AXPBVE axVE; AXPBSRC axSRC; AXPBADDR axAddr; AXPBADPCM axADPCM; // Calculate buffer size to allocate proper memry to keep a bit of "extra" audio data around... audioReadBufferNumSamples = (u32)((f32)AUD_AUDIO_READAHEADFRAMES * audio_player.audioInfo.vaud.frq); audioReadBufferNumChannels = audio_player.audioInfo.vaud.numChannels <= 2 ? audio_player.audioInfo.vaud.numChannels : 2; // Allocate read buffer audioReadBuffer = audio_player.cbAlloc(audioReadBufferNumSamples * sizeof(s16) * audio_player.audioInfo.vaud.numChannels); if (audioReadBuffer == NULL) return FALSE; // error // Reset ring buffer audioReadBufferReadPos = audioReadBufferWritePos = 0; // What frquency is best? audioPlayBufferFrq = audio_player.audioInfo.vaud.frq; // Allocate AI playback buffer audioPlayBuffer[0] = audio_player.cbAlloc(2 * sizeof(s16) * AUD_AUDIO_AIBUFFERSAMPLES * AUD_AUDIO_NUMAIBUFFERS); if (audioPlayBuffer[0] == NULL) return FALSE; // error for(i=1; i<AUD_AUDIO_NUMAIBUFFERS; i++) audioPlayBuffer[i] = (void *)((u32)audioPlayBuffer[i - 1] + (2 * sizeof(s16) * AUD_AUDIO_AIBUFFERSAMPLES)); // Reset buffer index audioPlayBufferWriteIndex = 0; // We disable AI output for now (logically) audioPlayBufferEnabled = FALSE; // We assume to playback all we get by default audioPlayMaskArray = NULL; audioNumPlayMasks = 0; audioNumActiveVoices = 2; // Clear out AI buffers to avoid any noise what so ever memset(audioPlayBuffer[0],0,2 * sizeof(s16) * AUD_AUDIO_AIBUFFERSAMPLES * AUD_AUDIO_NUMAIBUFFERS); DCFlushRange(audioPlayBuffer[0],2 * sizeof(s16) * AUD_AUDIO_AIBUFFERSAMPLES * AUD_AUDIO_NUMAIBUFFERS); // Init GCN audio system old = OSDisableInterrupts(); axVoice[0] = AXAcquireVoice(AX_PRIORITY_NODROP,NULL,0); ASSERT(axVoice[0] != NULL); axVoice[1] = AXAcquireVoice(AX_PRIORITY_NODROP,NULL,0); ASSERT(axVoice[1] != NULL); memset(&axMix[0],0,sizeof(axMix[0])); axMix[0].vL = 0x7FFF; memset(&axMix[1],0,sizeof(axMix[1])); axMix[1].vR = 0x7FFF; AXSetVoiceMix(axVoice[0],&axMix[0]); AXSetVoiceMix(axVoice[1],&axMix[1]); axVE.currentDelta = 0; axVE.currentVolume = 0x7FFF; AXSetVoiceVe(axVoice[0],&axVE); AXSetVoiceVe(axVoice[1],&axVE); memset(&axSRC,0,sizeof(AXPBSRC)); ratio = (u32)(65536.0f * (f32)audioPlayBufferFrq / (f32)AX_IN_SAMPLES_PER_SEC); axSRC.ratioHi = (u16)(ratio >> 16); axSRC.ratioLo = (u16)ratio; AXSetVoiceSrcType(axVoice[0],AX_SRC_TYPE_4TAP_16K); AXSetVoiceSrc(axVoice[0],&axSRC); AXSetVoiceSrcType(axVoice[1],AX_SRC_TYPE_4TAP_16K); AXSetVoiceSrc(axVoice[1],&axSRC); *(u32 *)&axAddr.currentAddressHi = AX_ARAM_LEFT_CHANNEL; *(u32 *)&axAddr.loopAddressHi = AX_ARAM_LEFT_CHANNEL; *(u32 *)&axAddr.endAddressHi = AX_ARAM_LEFT_CHANNEL + AX_ARAM_BUFFER_SIZE - 1; axAddr.format = AX_PB_FORMAT_PCM16; axAddr.loopFlag = AXPBADDR_LOOP_ON; AXSetVoiceAddr(axVoice[0],&axAddr); *(u32 *)&axAddr.currentAddressHi = AX_ARAM_RIGHT_CHANNEL; *(u32 *)&axAddr.loopAddressHi = AX_ARAM_RIGHT_CHANNEL; *(u32 *)&axAddr.endAddressHi = AX_ARAM_RIGHT_CHANNEL + AX_ARAM_BUFFER_SIZE - 1; AXSetVoiceAddr(axVoice[1],&axAddr); memset(&axADPCM,0,sizeof(axADPCM)); axADPCM.gain = 0x0800; AXSetVoiceAdpcm(axVoice[0],&axADPCM); AXSetVoiceAdpcm(axVoice[1],&axADPCM); AXSetVoiceType(axVoice[0],AX_PB_TYPE_STREAM); AXSetVoiceType(axVoice[1],AX_PB_TYPE_STREAM); AXRegisterCallback( AXCallback ); axLastAddr = AX_ARAM_LEFT_CHANNEL; axPlayedSamples = AUD_AUDIO_AIBUFFERSAMPLES * AUD_AUDIO_NUMAIBUFFERS; axPlayedSamplesTotal = 0; axPhase = AX_PHASE_STARTUP; // All is setup for the voices. We'll start them inside the AX callback as soon as we got data in the ARAM buffers OSRestoreInterrupts(old); return TRUE; }
/*! ****************************************************************************** * \brief * Decode all frame data * * This function operates on the full frame input data. It forwards this * data to the required decoder. * ****************************************************************************** */ BOOL AUDSimpleDecode(void) { BOOL enabled; u8* chunkStart; u32 chunkSize; u32 frameSize; if( audio_player.readBuffer[audio_player.decodeIndex].valid ) { // ptr to our (pre-) loaded data INSIDE (!) 'FRAM' chunk // (in other words, the 'FRAM' chunk itself is not visible here) chunkStart = audio_player.readBuffer[audio_player.decodeIndex].ptr; // usually, we read additional 32 bytes for getting info about the NEXT chunk. // We only deal with the actual 'FRAM' chunk data here and adjust the size by 32 bytes. frameSize = audio_player.readBuffer[audio_player.decodeIndex].size - 32; // loop across ALL chunks inside 'FRAM' while(frameSize >= 32) { chunkSize = VID_CHUNK_LEN(chunkStart); if( VID_CHUNK_ID(chunkStart) == VID_FCC('A','U','D','D') ) { // Get the data to the audio system... if(! AUDSimpleAudioDecode(chunkStart + VID_CHUNK_HEADER_SIZE, chunkSize - VID_CHUNK_HEADER_SIZE)) { #ifdef _DEBUG OSReport("*** AUDSimpleAudioDecode failed!\n"); #endif } } #ifdef _DEBUG else { OSReport("*** AUDSimpleDecode: unknown chunk type!\n"); } #endif // goto next chunk chunkStart += chunkSize; frameSize -= chunkSize; } audio_player.lastDecodedFrame = audio_player.readBuffer[audio_player.decodeIndex].frameNumber; audio_player.readBuffer[audio_player.decodeIndex].valid = FALSE; audio_player.decodeIndex = (audio_player.decodeIndex + 1) % AUD_NUM_READ_BUFFERS; // check if loading is still running enabled = OSDisableInterrupts(); if (!audio_player.readBuffer[audio_player.readIndex].valid && !audio_player.asyncDvdRunning) ReadFrameAsync(); OSRestoreInterrupts(enabled); return TRUE; } #ifdef _DEBUG OSReport("*** AUDSimpleDecode: No valid decode buffer found (?).\n"); #endif return FALSE; }