www.pudn.com > helix.src.0812.rar > macaudio.cp
/* ***** BEGIN LICENSE BLOCK ***** * Version: RCSL 1.0/RPSL 1.0 * * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. * * The contents of this file, and the files included with this file, are * subject to the current version of the RealNetworks Public Source License * Version 1.0 (the "RPSL") available at * http://www.helixcommunity.org/content/rpsl unless you have licensed * the file under the RealNetworks Community Source License Version 1.0 * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, * in which case the RCSL will apply. You may also obtain the license terms * directly from RealNetworks. You may not use this file except in * compliance with the RPSL or, if you have a valid RCSL with RealNetworks * applicable to this file, the RCSL. Please see the applicable RPSL or * RCSL for the rights, obligations and limitations governing use of the * contents of the file. * * This file is part of the Helix DNA Technology. RealNetworks is the * developer of the Original Code and owns the copyrights in the portions * it created. * * This file, and the files included with this file, is distributed and made * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * * Technology Compatibility Kit Test Suite(s) Location: * http://www.helixcommunity.org/content/tck * * Contributor(s): * * ***** END LICENSE BLOCK ***** */ // // macaudio.cp // #include#include "macaudio.h" #include "USound.h" #ifndef _MAC_MACHO #include #include #endif #include "hxtypes.h" #include "hxerrors.h" #include "hxcom.h" #include "hxausvc.h" #include "auderrs.h" #include "hxaudev.h" #include "hxslist.h" #include "hxtick.h" #include "chxpckts.h" #ifdef THREADS_SUPPORTED #include "hxthread.h" #endif #include "hxmm.h" //#define LOG_MULTIPLE_DEFERRED_TASKS 1 BOOL gSoundCallbackTime = FALSE; #if defined( _CARBON ) || defined( _MAC_UNIX ) DeferredTaskUPP CAudioOutMac::gDeferredTask = NewDeferredTaskUPP(CAudioOutMac::DeferredTaskCallback); #else DeferredTaskUPP CAudioOutMac::gDeferredTask = NewDeferredTaskProc(CAudioOutMac::DeferredTaskCallback); #endif #ifdef THREADS_SUPPORTED HXMutex* CAudioOutMac::zm_pMutex = NULL; #endif CAudioOutMac* gActiveAudioDevice = NULL; #if defined( _CARBON ) || defined( _MAC_UNIX ) typedef pascal Handle (*MacAudioNewHandleSysProcPtr)(Size); CFragConnectionID gAudioInterfaceLibConnID = kInvalidID; MacAudioNewHandleSysProcPtr gMacAudioNewHandleSysProc = nil; bool gMacAudioTriedToInitialize = false; void MacAudioInitInterfaceLibProcPtrs() { if (gMacAudioTriedToInitialize) return; gMacAudioTriedToInitialize = true; if (gAudioInterfaceLibConnID == kInvalidID) { GetSharedLibrary("\pInterfaceLib", kCompiledCFragArch, kReferenceCFrag, &gAudioInterfaceLibConnID, nil, nil); } if (gAudioInterfaceLibConnID != kInvalidID) { OSErr err = noErr; err = FindSymbol(gAudioInterfaceLibConnID, "\pNewHandleSys", (Ptr*)&gMacAudioNewHandleSysProc, nil); } } #endif /*-------------------------------------------------------------------------- | CMacWaveFormat | | Default ctor. --------------------------------------------------------------------------*/ CMacWaveFormat::CMacWaveFormat (void) { /* begin CMacWaveFormat */ SetFormatDflt (); } /* end CMacWaveFormat */ /*-------------------------------------------------------------------------- | ~CMacWaveFormat | | dtor. --------------------------------------------------------------------------*/ CMacWaveFormat::~CMacWaveFormat (void) { /* begin ~CMacWaveFormat */ } /* end ~CMacWaveFormat */ /*-------------------------------------------------------------------------- | SetUpSound | | Formats a sound handle. --------------------------------------------------------------------------*/ OSErr CMacWaveFormat::SetUpSound ( SndListHandle sndHandle, long numBytes, short *headerLen, long *headerOffset) { /* begin SetUpSound */ OSErr e = noErr; long response; if (sampleSize > 8) { if (USound::CheckSMVersion () < 3) { if ((::Gestalt(gestaltSoundAttr, &response) == noErr) && ((response & (1L << gestalt16BitSoundIO)) == 0)) return (noHardwareErr); } /* if */ else { if ((::Gestalt(gestaltSoundAttr, &response) == noErr) && ((response & (1L << gestalt16BitAudioSupport)) == 0)) return (noHardwareErr); } /* else */ } /* if */ if (noErr != (e = ::SetupSndHeader (sndHandle, numChannels, sampleRate, sampleSize, compressionType, baseFrequency, numBytes, headerLen))) goto CleanUp; if (noErr != (e = USound::GetSoundHeaderOffset (sndHandle, headerOffset))) goto CleanUp; CleanUp: return (e); } /* end SetUpSound */ /*-------------------------------------------------------------------------- | SetHeaderLength [static] | | Formats a sound header for a given byte count. --------------------------------------------------------------------------*/ OSErr CMacWaveFormat::SetHeaderLength ( SoundHeaderPtr pSoundHeader, long numBytes) { /* begin SetHeaderLength */ switch (pSoundHeader->encode) { case stdSH: /*standard sound header*/ pSoundHeader->length = numBytes; break; case extSH: /*extended sound header*/ { ExtSoundHeaderPtr eh = (ExtSoundHeaderPtr) pSoundHeader; eh->numFrames = numBytes / (eh->numChannels * (eh->sampleSize / 8)); } break; case cmpSH: /*compressed sound header*/ { CmpSoundHeaderPtr ch = (CmpSoundHeaderPtr) pSoundHeader; ch->numFrames = numBytes / (ch->numChannels * (ch->sampleSize / 8)); } break; default: return badFormat; } /* switch */ return noErr; } /* SetHeaderLength */ /*-------------------------------------------------------------------------- | SetFormatDflt | | Sets up the default Sound header information. --------------------------------------------------------------------------*/ void CMacWaveFormat::SetFormatDflt (void) { /* begin SetFormatDflt */ //mwf.wf.nAvgBytesPerSec = 22050; //mwf.wf.nBlockAlign = 2; numChannels = 1; //mwf.wf.nChannels = 1; sampleRate = Long2Fix (8000); //mwf.wf.nSamplesPerSec = 8000; //sampleRate = rate11025hz; //mwf.wf.nSamplesPerSec = 11025; sampleSize = 16; //mwf.wBitsPerSample = 16; compressionType = NoneType; //mwf.wf.wFormatTag = WAVE_FORMAT_PCM; baseFrequency = kMiddleC; } /* end SetFormatDflt */ /*-------------------------------------------------------------------------- | SetFormatDflt | | Sets up the default Sound header information. --------------------------------------------------------------------------*/ void CMacWaveFormat::SetFormat ( ULONG32 inSampleRate, UINT16 channels, UINT16 bitsPerSample) { numChannels = channels; sampleRate = inSampleRate << 16L; sampleSize = bitsPerSample; compressionType = NoneType; baseFrequency = kMiddleC; } /*-------------------------------------------------------------------------- | CWaveHeader --------------------------------------------------------------------------*/ CWaveHeader::CWaveHeader (void) : soundA5 (0) , state (kFreeState) , waveOut (NULL) , mMaxPlay (0) , sndHandle (nil) , mHeaderLen (0) , mSoundHeader (nil) { /* begin CWaveHeader */ soundA5 = SetCurrentA5 (); } /* end CWaveHeader */ /*-------------------------------------------------------------------------- | ~CWaveHeader --------------------------------------------------------------------------*/ CWaveHeader::~CWaveHeader (void) { /* begin ~CWaveHeader */ if (sndHandle) ::DisposeHandle ((Handle) sndHandle); sndHandle = nil; } /* end ~CWaveHeader */ /*-------------------------------------------------------------------------- | CWaveHeader --------------------------------------------------------------------------*/ const Size CWaveHeader::kSndHeaderSize = 512; UINT16 CWaveHeader::WAVE_BLOCKSIZE = 4096; OSErr CWaveHeader::Allocate ( UINT16 inMaxPlay, float sampleRate, UINT16 channels, UINT16 bitsPerSample) { /* begin Allocate */ OSErr e = badFormat; long offset; if (!waveOut) goto CleanUp; OSErr theError; sndHandle = (SndListHandle) ::TempNewHandle (kSndHeaderSize + inMaxPlay, &theError); if (!sndHandle) goto CleanUp; check_noerr (theError); waveOut->wf.SetFormat ((long)sampleRate,channels,bitsPerSample); if (noErr != (e = waveOut->wf.SetUpSound (sndHandle, inMaxPlay, &mHeaderLen, &offset))) goto CleanUp; ::SetHandleSize ((Handle) sndHandle, mHeaderLen + inMaxPlay); if (noErr != (e = MemError ())) goto CleanUp; mMaxPlay = inMaxPlay; ::MoveHHi ((Handle) sndHandle); ::HLock ((Handle) sndHandle); mSoundHeader = (SoundHeaderPtr) ((*(Handle) sndHandle) + offset); CleanUp: return (e); } /* end Allocate */ /*-------------------------------------------------------------------------- | Release --------------------------------------------------------------------------*/ void CWaveHeader::Release (short releaseCode, BOOL bSendTimeSync /*= TRUE*/) { /* begin Release */ if (kFreeState != state) { state = kFreeState; if ((CWaveHeader::kResetRelease != releaseCode) && (CWaveHeader::kAbortRelease != releaseCode)) //cz 5/7/96 if (waveOut) waveOut->DonePlaying (cbPlaying, timeEnd,releaseCode == kCallBackRelease, bSendTimeSync); } /* if */ if (kAbortRelease == releaseCode) { if (sndHandle) { ::DisposeHandle ((Handle) sndHandle); sndHandle=NULL; } sndHandle = nil; mMaxPlay = 0; mSoundHeader = nil; } /* if */ } /* end Release */ /*-------------------------------------------------------------------------- | PlayBuffer --------------------------------------------------------------------------*/ OSErr CWaveHeader::PlayBuffer ( char *pData, UINT16 cbPlayingArg, ULONG32 timeEndArg) { /* begin PlayBuffer */ OSErr e = noErr; SndCommand cmd; if (!waveOut) return (badFormat); if (kFreeState != state) return (badFormat); if (!sndHandle) return (nilHandleErr); if (cbPlayingArg > mMaxPlay) return (memFullErr); if (noErr != (e = waveOut->wf.SetHeaderLength (mSoundHeader, cbPlayingArg))) goto CleanUp; if(pData != NULL) ::BlockMove (pData, (*(Handle) sndHandle) + mHeaderLen, cbPlayingArg); cmd.cmd = bufferCmd; cmd.param1 = 0; cmd.param2 = (long) mSoundHeader; if (noErr != (e = ::SndDoCommand (waveOut->chan, &cmd, TRUE))) goto CleanUp; state = kQueuedState; cbPlaying = cbPlayingArg; timeEnd = timeEndArg; waveOut->StartedPlaying (cbPlaying); cmd.cmd = callBackCmd; cmd.param1 = kCallBackRelease; cmd.param2 = (long) this; if (noErr != (e = ::SndDoCommand (waveOut->chan, &cmd, TRUE))) goto CleanUp; waveOut->m_bAudioCallbacksAreAlive = TRUE; CleanUp: return (e); } /* end PlayBuffer */ /*-------------------------------------------------------------------------- | NewChannel --------------------------------------------------------------------------*/ SndChannelPtr CWaveHeader::NewChannel (void) { /* begin NewChannel */ SndChannelPtr chan = nil; #if defined( _CARBON ) || defined( _MAC_UNIX ) SndCallBackUPP userProc = NewSndCallBackUPP (Callback); #else SndCallBackUPP userProc = NewSndCallBackProc (Callback); #endif // if (noErr != ::SndNewChannel (&chan, sampledSynth, initStereo + initNoDrop + initNoInterp, userProc)) if (noErr != ::SndNewChannel (&chan, sampledSynth, initStereo, userProc)) chan = nil; return chan; } /* end NewChannel */ /*-------------------------------------------------------------------------- | DisposeChannel --------------------------------------------------------------------------*/ OSErr CWaveHeader::DisposeChannel ( SndChannelPtr chan) { /* begin DisposeChannel */ OSErr e; if(chan) { SndCallBackUPP userProc = chan->callBack; #if defined( _CARBON ) || defined( _MAC_UNIX ) if (userProc) DisposeSndCallBackUPP (userProc); #else if (userProc) DisposeRoutineDescriptor (userProc); #endif e = ::SndDisposeChannel (chan, TRUE); } return e; } /* end DisposeChannel */ /*-------------------------------------------------------------------------- | Callback --------------------------------------------------------------------------*/ pascal void CWaveHeader::Callback ( SndChannelPtr chan, SndCommand *cmd) { /* begin Callback */ #ifndef _MAC_UNIX HXMM_INTERRUPTON(); #endif // if (HXMM_RAHUL_CHECK()) // { // DebugStr("\pCWaveHeader Deferred Task ENTER;g"); // } DeferredTask* dtrec = NULL; CWaveHeader* wh = NULL; SndCommand* newCommand; if (cmd) { wh = (CWaveHeader *) cmd->param2; if ( wh ) { CAudioOutMac* theWaveOut = wh->waveOut; if ( theWaveOut ) { CMacWaveFormat wf = theWaveOut->wf; UINT32 ulBytesPerSecond = wf.numChannels * ( (ULONG32)wf.sampleRate >> 16 ) * ( wf.sampleSize / 8 ); theWaveOut->m_millisecondsIntoClip += (double)wh->cbPlaying * 1000.0 / (double)ulBytesPerSecond; } } dtrec=wh->waveOut->mDeferredTaskRec; } if (!dtrec) { goto CleanUp; } newCommand=new SndCommand; HX_ASSERT(newCommand); if (!newCommand) { delete dtrec; goto CleanUp; } if (cmd) { *newCommand=*cmd; } else { delete newCommand; delete dtrec; goto CleanUp; } wh->waveOut->AddToThePendingList(newCommand); if (!wh->waveOut->m_bDeferredTaskPending) { wh->waveOut->m_bDeferredTaskPending = TRUE; short err = 0; if ( wh->waveOut->m_bIsQuitting ) { err = -1; // just so it's nonzero } else { err = DTInstall(dtrec); } if ( err != noErr ) { wh->waveOut->m_bDeferredTaskPending = FALSE; wh->waveOut->m_bAudioCallbacksAreAlive = FALSE; delete dtrec; delete newCommand; goto CleanUp; } } /* if (wh) { long saveA5 = SetA5 (wh->soundA5); wh->Release (cmd->param1); SetA5 (saveA5); } */ CleanUp: #ifndef _MAC_UNIX HXMM_INTERRUPTOFF(); #endif // if (HXMM_RAHUL_CHECK()) // { // DebugStr("\pCWaveHeader Deferred Task LEAVE;g"); // } return; } /* end Callback */ pascal void CAudioOutMac::DeferredTaskCallback(long param) { if (!param) { return; } #ifndef _MAC_UNIX HXMM_INTERRUPTON(); #endif CAudioOutMac* pAudioOut = (CAudioOutMac*) param; pAudioOut->m_bAudioCallbacksAreAlive = FALSE; #ifdef THREADS_SUPPORTED if (zm_pMutex) { zm_pMutex->Lock(); } #endif #if defined(_DEBUG) && defined(LOG_MULTIPLE_DEFERRED_TASKS) if ( !pAudioOut->m_bAudioCallbacksAreAlive ) { DebugStr( "\pDeferredTaskCallback -- m_bAudioCallbacksAreAlive not true!;g" ); } #endif while (gActiveAudioDevice && pAudioOut->m_pPendingCallbackList && !pAudioOut->m_pPendingCallbackList->IsEmpty()) { SndCommand* cmd = (SndCommand*) pAudioOut->m_pPendingCallbackList->RemoveHead(); BOOL bIsEmpty = pAudioOut->m_pPendingCallbackList->IsEmpty(); #ifdef THREADS_SUPPORTED // we used to deadlock here; we'd grab the audio mutex before the core mutex, // which is not how the rest of this class operates if (zm_pMutex) { zm_pMutex->Unlock(); } #endif /* Send time sync for ONLY the last pending audio callback */ pAudioOut->ProcessCmd(cmd, bIsEmpty); delete cmd; #ifdef THREADS_SUPPORTED if (zm_pMutex) { zm_pMutex->Lock(); } #endif } /* It is possible that on a timesync, a renderer may issue a hyper navigate request. * This may result in leaving the currently active page (thereby unloading * the embedded player and the core. We would have destructed the CMacAudio class * in this case. */ if (!gActiveAudioDevice) { goto cleanup; } pAudioOut->m_bDeferredTaskPending = FALSE; cleanup: #ifdef THREADS_SUPPORTED if (zm_pMutex) { zm_pMutex->Unlock(); } #endif #ifndef _MAC_UNIX HXMM_INTERRUPTOFF(); #endif } void CAudioOutMac::ProcessCmd(SndCommand* cmd, BOOL bSendTimeSync /*= TRUE*/) { CWaveHeader *wh = (CWaveHeader *) cmd->param2; if (wh) { wh->Release(cmd->param1, bSendTimeSync); } } /*-------------------------------------------------------------------------- | CAudioOutMac --------------------------------------------------------------------------*/ UINT16 CAudioOutMac::NUM_WAVEHDRS = 12; // was 30 CAudioOutMac::CAudioOutMac () : chan (nil) , mlpWaveHdrs (NULL) , mcbPlaying (0) , mPaused (TRUE) , m_uNumBlocksInDevice(0) , m_bFirstPacket(TRUE) , m_ulTimeOfFirstPacket(0L) , m_pPendingCallbackList(NULL) , m_bDeferredTaskPending(FALSE) , m_bAudioCallbacksAreAlive(FALSE) , m_bIsQuitting(FALSE) , m_millisecondsIntoClip(0.0) , m_pCallback(NULL) , m_pInterruptState(NULL) { /* begin CAudioOutMac */ mDeferredTaskRec=(DeferredTask*)NewPtrClear(sizeof(DeferredTask)); mDeferredTaskRec->qType=dtQType; mDeferredTaskRec->dtAddr=CAudioOutMac::gDeferredTask; mDeferredTaskRec->dtParam= (long) this; // Gestalt-based code that remembers potential deferred tasks for emergency removal #ifndef _MAC_UNIX Handle dtGestaltHandle = nil; OSErr err = Gestalt( kLetInterruptsFinishBeforeQuittingGestalt, (long*)&dtGestaltHandle ); if ( err != noErr ) { #if defined( _CARBON ) MacAudioInitInterfaceLibProcPtrs(); if (gMacAudioNewHandleSysProc) { dtGestaltHandle = (*gMacAudioNewHandleSysProc)( 0 ); } if (!dtGestaltHandle) { dtGestaltHandle = NewHandle( 0 ); } #else dtGestaltHandle = NewHandleSys( 0 ); #endif if ( dtGestaltHandle ) { NewGestaltValue( kLetInterruptsFinishBeforeQuittingGestalt, (long)dtGestaltHandle ); } } if ( dtGestaltHandle ) { GestaltDeferredStruct gds; gds.quitting = &m_bIsQuitting; gds.pending = &m_bAudioCallbacksAreAlive; GetCurrentProcess(&(gds.psn)); PtrAndHand( &gds, dtGestaltHandle, sizeof(gds) ); } #endif #ifdef THREADS_SUPPORTED if (!zm_pMutex) { HXMutex::MakeMutex(zm_pMutex); } #endif gActiveAudioDevice = this; } /* end CAudioOutMac */ /*-------------------------------------------------------------------------- | ~CAudioOutMac --------------------------------------------------------------------------*/ CAudioOutMac::~CAudioOutMac () { /* begin ~CAudioOutMac */ Abort (); #if defined(_DEBUG) && defined(LOG_MULTIPLE_DEFERRED_TASKS) BOOL bWaitedForPending = FALSE; if ( m_bAudioCallbacksAreAlive ) { DebugStr( "\pCAudioOutMac dtor -- m_bAudioCallbacksAreAlive still true!;g" ); bWaitedForPending = TRUE; } #endif m_bIsQuitting = TRUE; // sit-n-spin awaiting interrupts to finish. for ( int i = 0; i < 10; i++ ) { unsigned long dummyTix; Delay( 6, &dummyTix ); if ( !m_bAudioCallbacksAreAlive ) { i = 10; } } #if defined(_DEBUG) && defined(LOG_MULTIPLE_DEFERRED_TASKS) if ( bWaitedForPending ) { if ( m_bAudioCallbacksAreAlive ) { DebugStr( "\ptasks STILL pending! This is gonna hurt...;g" ); } else { DebugStr( "\pSuccessfully purged pending callbacks;g" ); } } #endif if (mDeferredTaskRec) { // first ensure that the Gestalt handle doesn't think it's // here any more. #ifndef _MAC_UNIX Handle dtGestaltHandle = nil; Gestalt( kLetInterruptsFinishBeforeQuittingGestalt, (long*)&dtGestaltHandle ); if ( dtGestaltHandle ) { // XXXNH: only look at tasks for this process ProcessSerialNumber psn; GetCurrentProcess(&psn); // zip through and if an entry equals this deferred task, // simply zero it out. We won't worry about shuffling // the handle size at this juncture. long hSize = GetHandleSize( dtGestaltHandle ); GestaltDeferredStruct* currentPtr = (GestaltDeferredStruct*)*dtGestaltHandle; for ( int i = 0; i < hSize / sizeof(GestaltDeferredStruct); i++ ) { unsigned char bSameProcess = FALSE; SameProcess(&(currentPtr[i].psn), &psn, &bSameProcess); if (bSameProcess && currentPtr[i].quitting == &m_bIsQuitting && currentPtr[i].pending == &m_bAudioCallbacksAreAlive ) { currentPtr[i].quitting = NULL; currentPtr[i].pending = NULL; } } } #endif ::DisposePtr ((Ptr)mDeferredTaskRec); } CleanupPendingLists(); HX_DELETE(m_pPendingCallbackList); gActiveAudioDevice = NULL; HX_RELEASE(m_pScheduler); HX_RELEASE(m_pInterruptState); } /* end ~CAudioOutMac */ UINT16 CAudioOutMac::m_uzVolume = 50; HX_RESULT CAudioOutMac::_Imp_Open( const HXAudioFormat* pFormat ) { /* ** Open the sound channel. */ NUM_WAVEHDRS = 45; //params->numBufs; CWaveHeader::WAVE_BLOCKSIZE = pFormat->uMaxBlockSize; if (chan) CWaveHeader::DisposeChannel (chan); m_bAudioCallbacksAreAlive = FALSE; chan = CWaveHeader::NewChannel (); if (!chan) return (HX_AUDIO_DRIVER_ERROR); if (!m_pPendingCallbackList) { m_pPendingCallbackList = new CHXSimpleList; } ULONG32 nSampleRate = pFormat->ulSamplesPerSec; ULONG32 nBlockSize = pFormat->uMaxBlockSize; #if powerc && FORCE_RATE_CONVERSION // if we are on a PowerPC we will do our own sample rate conversion // since the Apple version gives us a lot of pops and aliasing ULONG32 sysSampleRate = ::USound::GetSystemSampleRate(chan); // Note: we currently have to HACK 11227 and 22254 sample rates on // old macs to 11025 and 22050 switch(sysSampleRate) { case 11227: sysSampleRate = 11025; break; case 22254: sysSampleRate = 22050; break; } // we only have a problem if the sample rate is <= 32000 Hz. (sigh!) if(nSampleRate <= 32000) { if(sysSampleRate > 0 && nSampleRate != sysSampleRate) { UINT16 bytesPerSample = pFormat->wBitsPerSample > 8 ? 2 : 1; UINT16 frameSize = bytesPerSample * pFormat->wChannels; ULONG32 bytesPerSecond = (ULONG32) (nSampleRate * frameSize); float bufDuration = (float)pFormat->uMaxBlockSize/(float)bytesPerSecond; // calculate the bytesPerSecond of the system sample rate bytesPerSecond = (ULONG32) (sysSampleRate * frameSize); // update the buffer size to handle the new sample rate nBlockSize = bytesPerSecond * bufDuration; // round the buffersize to a framesize nBlockSize = ((nBlockSize/frameSize) * frameSize) + frameSize; nSampleRate = sysSampleRate; //pFormat->ulSamplesPerSec = sysSampleRate; } } #ifndef MAC_RELEASE #if 0 char s[256]; ::sprintf(s,"sample rate %ld",sysSampleRate); ::c2pstr(s); DebugStr((UCHAR *)s); #endif #endif // MAC_RELEASE #endif // powerc && FORCE_RATE_CONVERSION // SetVolume(pFormat->volume); _Imp_SetVolume(m_uzVolume); /* ** Allocate memory for wave block headers and for wave data. */ if (NULL == mlpWaveHdrs) { if (NULL == (mlpWaveHdrs = new CWaveHeader [NUM_WAVEHDRS])) return (HX_MEMORY_ERROR); for (short iwh = 0; iwh < NUM_WAVEHDRS; iwh++) { CWaveHeader *pwh = GetWaveHeader (iwh); if (pwh) { pwh->SetOutput (this); switch (pwh->Allocate (nBlockSize, nSampleRate, pFormat->uChannels,pFormat->uBitsPerSample)) { case noErr: break; case memFullErr: return (HX_MEMORY_ERROR); default: return (HX_AUDIO_DRIVER_ERROR); } } } } /* ** Indicate that no audio blocks are currently playing. */ mcbPlaying = 0; mPaused = FALSE; if (!m_pScheduler && m_pContext) { m_pContext->QueryInterface(IID_IHXScheduler, (void**) &m_pScheduler); m_pContext->QueryInterface(IID_IHXInterruptState, (void**) &m_pInterruptState); } return (HX_NO_ERROR); } HX_RESULT CAudioOutMac::_Imp_Close( void ) { Abort(); return HXR_OK; } HX_RESULT CAudioOutMac::_Imp_Pause( void ) { mPaused = TRUE; if (OkToPauseResume(FALSE)) { // Stop playback if (chan != NULL) DoImmediate (pauseCmd); } return HXR_OK; } HX_RESULT CAudioOutMac::_Imp_Resume( void ) { mPaused = FALSE; if (OkToPauseResume(TRUE)) { if (chan != NULL) { DoImmediate (resumeCmd); } this->OnTimeSync(); } return HXR_OK; } HX_RESULT CAudioOutMac::_Imp_Write( const HXAudioData* pAudioOutHdr ) { HX_RESULT err = HX_NO_ERROR; // CWaveHeader *pwh = GetWaveHeader(0);; CWaveHeader *pwh=NULL; Boolean found = FALSE; void* pBuffer = NULL; for (short iwh = 0; iwh < NUM_WAVEHDRS && !found; iwh++) { pwh = GetWaveHeader(iwh); found = pwh && pwh->Available (); } if (found) { pBuffer = pwh->get_buffer(); } else { return playError; } if(pwh == 0) { return (playError); } char* pData = 0; pData = (char*)pAudioOutHdr->pData->GetBuffer(); ULONG32 nLen = pAudioOutHdr->pData->GetSize(); if (pBuffer) { memcpy(pBuffer, pData, nLen); if (noErr != pwh->PlayBuffer ((char*)pBuffer, nLen, pAudioOutHdr->ulAudioTime)) err = (playError); } if (!err) { m_uNumBlocksInDevice++; if(m_bFirstPacket) { m_bFirstPacket = FALSE; m_ulTimeOfFirstPacket = pAudioOutHdr->ulAudioTime; } } return err; } HX_RESULT CAudioOutMac::_Imp_Reset( void ) { // Stop playback if (chan != NULL) { DoImmediate (pauseCmd); DoImmediate (flushCmd); DoImmediate (quietCmd); } /* if */ m_ulCurrentTime = 0; m_bFirstPacket = TRUE; // Mark blocks as available. ReleaseBlocks (CWaveHeader::kResetRelease); m_uNumBlocksInDevice = 0; CleanupPendingLists(); return HX_NO_ERROR; } HX_RESULT CAudioOutMac::_Imp_Drain( void ) { return HX_NO_ERROR; } BOOL CAudioOutMac::_Imp_SupportsVolume( void ) { return TRUE; } UINT16 CAudioOutMac::_Imp_GetVolume() { #if 0 unsigned long volume=0; SndCommand theCmd; theCmd.cmd = getVolumeCmd; theCmd.param1 = 0; theCmd.param2 = volume; // queue volume command in channel ::SndDoImmediate(chan,&theCmd); return (UINT16) volume; #endif return m_uzVolume; } HX_RESULT CAudioOutMac::_Imp_SetVolume( const UINT16 uVolume ) { SndCommand theCmd; unsigned long volume; unsigned long leftVolume, rightVolume; // if(uVolume < 0) uVolume = 0; if(chan) { #if defined( _CARBON ) || defined( _MAC_UNIX ) leftVolume = rightVolume = (long) uVolume * 5; //uVolume is between 0 and 100 volume = (rightVolume << 16) + leftVolume; theCmd.cmd = volumeCmd; theCmd.param1 = 0; theCmd.param2 = volume; // queue volume command in channel ::SndDoImmediate(chan,&theCmd); #else #if OLDROUTINENAMES && !GENERATINGCFM if (USound::CheckSMVersion () < 3) ::SetSoundVol (uVolume); #else leftVolume = rightVolume = (long) uVolume * 5; //uVolume is between 0 and 100 volume = (rightVolume << 16) + leftVolume; theCmd.cmd = volumeCmd; theCmd.param1 = 0; theCmd.param2 = volume; // queue volume command in channel ::SndDoImmediate(chan,&theCmd); #endif #endif } m_uzVolume = uVolume; return HXR_OK; } HX_RESULT CAudioOutMac::_Imp_CheckFormat( const HXAudioFormat* pFormat) { return HX_NO_ERROR; } HX_RESULT CAudioOutMac::_Imp_Seek(ULONG32 ulSeekTime) { m_ulCurrentTime = 0; return HX_NO_ERROR; } /*--------------------------------------------------------------------------- | ReleaseBlocks | | Frees the blocks manually --------------------------------------------------------------------------*/ void CAudioOutMac::ReleaseBlocks ( short releaseCode) { /* begin ReleaseBlocks */ // Mark blocks as available. for (short iwh = 0; iwh < NUM_WAVEHDRS; iwh++) { CWaveHeader *pwh = GetWaveHeader (iwh); // if (pwh && !pwh->Available ()) if (pwh) pwh->Release (releaseCode); } /* for */ m_millisecondsIntoClip = 0.0; } /* end ReleaseBlocks */ /*--------------------------------------------------------------------------- | DoImmediate | | Executes a sound command --------------------------------------------------------------------------*/ OSErr CAudioOutMac::DoImmediate ( short cmd, short param1/*= 0*/, long param2/*= 0L*/) { /* begin DoImmediate */ SndCommand sndCmd; sndCmd.cmd = cmd; sndCmd.param1 = param1; sndCmd.param2 = param2; return ::SndDoImmediate (chan, &sndCmd); } /* end DoImmediate */ /*--------------------------------------------------------------------------- | Abort | | Halts playback and shuts down --------------------------------------------------------------------------*/ void CAudioOutMac::Abort (void) { /* begin Abort */ // Stop playback if (chan != NULL) { // Kludge around usual SM unreliabilityÉ // rmgw 3/12/95 DoImmediate (pauseCmd); DoImmediate (flushCmd); DoImmediate (quietCmd); CWaveHeader::DisposeChannel (chan); m_bAudioCallbacksAreAlive = FALSE; chan = NULL; mPaused = TRUE; } /* if */ // Mark blocks as available. ReleaseBlocks (CWaveHeader::kAbortRelease); /* ** Free allocated buffers. */ if (mlpWaveHdrs) delete [] mlpWaveHdrs; mlpWaveHdrs = NULL; m_uNumBlocksInDevice = 0; m_ulCurrentTime = 0; CleanupPendingLists(); // remove any pending callback if (m_pCallback && m_pCallback->m_CallbackHandle && m_pScheduler) { m_pScheduler->Remove(m_pCallback->m_CallbackHandle); HX_RELEASE(m_pCallback); } } /* end Abort */ void CAudioOutMac::CleanupPendingLists() { #ifdef THREADS_SUPPORTED if (zm_pMutex) { zm_pMutex->Lock(); } #endif while(m_pPendingCallbackList && !m_pPendingCallbackList->IsEmpty()) { SndCommand* cmd = (SndCommand*)m_pPendingCallbackList->RemoveHead(); delete cmd; } #ifdef THREADS_SUPPORTED if (zm_pMutex) { zm_pMutex->Unlock(); } #endif } /*--------------------------------------------------------------------------- | DonePlaying | --------------------------------------------------------------------------*/ void CAudioOutMac::DonePlaying ( UINT16 cbPlayedArg, ULONG32 timeEndArg, short waveTask, BOOL bSendTimeSync /*= TRUE*/) { mcbPlaying -= cbPlayedArg; // if (cbPlayedArg) { if (m_uNumBlocksInDevice > 0) { m_uNumBlocksInDevice--; } gSoundCallbackTime = TRUE; HX_ASSERT(timeEndArg >= m_ulTimeOfFirstPacket); if(timeEndArg >= m_ulTimeOfFirstPacket) { /* Never go back in time */ if (m_ulCurrentTime < m_millisecondsIntoClip) { m_ulCurrentTime = m_millisecondsIntoClip; } } else { m_ulCurrentTime = 0L; } if (bSendTimeSync) { this->OnTimeSync(); } gSoundCallbackTime = FALSE; } } /************************************************************************ * Method: * CAudioOutMac::_Imp_GetCurrentTime * Purpose: * Get the current time from the audio device. */ HX_RESULT CAudioOutMac::_Imp_GetCurrentTime ( ULONG32& ulCurrentTime ) { ulCurrentTime = m_ulCurrentTime; return HX_NO_ERROR; } UINT16 CAudioOutMac::_NumberOfBlocksRemainingToPlay(void) { return m_uNumBlocksInDevice; } void CAudioOutMac::AddToThePendingList(void* pNode) { #ifdef THREADS_SUPPORTED if (zm_pMutex) { zm_pMutex->Lock(); } #endif m_pPendingCallbackList->AddTail(pNode); #ifdef THREADS_SUPPORTED if (zm_pMutex) { zm_pMutex->Unlock(); } #endif } BOOL CAudioOutMac::OkToPauseResume(BOOL bToBeResumed/* TRUE - Resume. FALSE - Pause */) { if (m_pInterruptState && m_pInterruptState->AtInterruptTime()) { if (!m_pCallback) { m_pCallback = new HXPauseResumeCb(this); m_pCallback->AddRef(); } m_pCallback->Enter(bToBeResumed); if (m_pCallback->m_CallbackHandle == 0 && m_pScheduler) { m_pCallback->m_CallbackHandle = m_pScheduler->RelativeEnter(m_pCallback, 0); } return FALSE; } else if (m_pCallback && m_pCallback->m_CallbackHandle && m_pScheduler) { // rememebr the callback handle since it will be set to 0 in func() CallbackHandle handle = m_pCallback->m_CallbackHandle; m_pCallback->Func(); m_pScheduler->Remove(handle); m_pCallback->m_CallbackHandle = 0; } return TRUE; } // CAudioOutMac::HXPauseResumeCb CAudioOutMac::HXPauseResumeCb::HXPauseResumeCb(CAudioOutMac* pAudioObject) : m_lRefCount (0) ,m_CallbackHandle(0) ,m_pAudioDeviceObject (pAudioObject) ,m_pCommandList(NULL) { } CAudioOutMac::HXPauseResumeCb::~HXPauseResumeCb() { HX_DELETE(m_pCommandList); } /* * IUnknown methods */ ///////////////////////////////////////////////////////////////////////// // Method: // IUnknown::QueryInterface // Purpose: // Implement this to export the interfaces supported by your // object. // STDMETHODIMP CAudioOutMac::HXPauseResumeCb::QueryInterface ( REFIID riid, void** ppvObj ) { if (IsEqualIID(riid, IID_IHXCallback)) { AddRef(); *ppvObj = (IHXCallback*)this; return HXR_OK; } else if (IsEqualIID(riid, IID_IUnknown)) { AddRef(); *ppvObj = this; return HXR_OK; } *ppvObj = NULL; return HXR_NOINTERFACE; } ///////////////////////////////////////////////////////////////////////// // Method: // IUnknown::AddRef // Purpose: // Everyone usually implements this the same... feel free to use // this implementation. // STDMETHODIMP_(ULONG32) CAudioOutMac::HXPauseResumeCb::AddRef() { return InterlockedIncrement(&m_lRefCount); } ///////////////////////////////////////////////////////////////////////// // Method: // IUnknown::Release // Purpose: // Everyone usually implements this the same... feel free to use // this implementation. // STDMETHODIMP_(ULONG32) CAudioOutMac::HXPauseResumeCb::Release() { if (InterlockedDecrement(&m_lRefCount) > 0) { return m_lRefCount; } delete this; return 0; } /* * IRMAPauseResumeCb methods */ STDMETHODIMP CAudioOutMac::HXPauseResumeCb::Func(void) { m_CallbackHandle = 0; if (m_pAudioDeviceObject) { while (m_pCommandList && m_pCommandList->GetCount() > 0) { BOOL bResume = (BOOL) m_pCommandList->RemoveHead(); if(bResume) { m_pAudioDeviceObject->_Imp_Resume(); } else { m_pAudioDeviceObject->_Imp_Pause(); } } HX_DELETE(m_pCommandList); } return HXR_OK; } void CAudioOutMac::HXPauseResumeCb::Enter(BOOL bResume) { if (!m_pCommandList) { m_pCommandList = new CHXSimpleList; } m_pCommandList->AddTail((void*) bResume); }