www.pudn.com > helix.src.0812.rar > audaix.cpp
/* ***** 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 ***** */ /******************************************************************* * audaix.cpp * * CLASS: CAudioOutAIX * * DESCRIPTION: AIX & UMS specific audio class implementation * *******************************************************************/ #include// for getenv() #include #include // for getenv() #include #include #include // for I_FLUSH ioctl #include #include #include #include #include #include #include #include #include #include #include "hxcom.h" #include "hxresult.h" #include "hxengin.h" #include "ihxpckts.h" // for IHXBuffer #include "hxslist.h" #include "timeval.h" #include "audaix.h" #include "hxaudses.h" #include "hxtick.h" #include "chxpckts.h" #include "debug.h" #include "hxstrutl.h" #include #include #include struct IHXCallback; // forward declaration needed for callback. // One and ONLY one of the following must be uncommented. They determine the // strategy used to determine the elaped time. The first is the preferred. #define AIX_TIME_BYTES_WRITTEN // function of bytes written vrs bytes in buffer. //********************************************************************** // constants. // // in UMS, balance is really an initial pan setting. // -100 = hard left, 0 is centered, 100 = hard right static const LONG32 lDefaultBalance = 0; static const LONG32 lDefaultVolume = 50; // UMS range is 0..100 static LONG32 zlCurrentVolume = 50; // UMS range is 0..100 // this is sufficient for PCI devices, for microchannel devices, set the // envar AUDIODEV to "/dev/maud0". static const char * szDefaultPortFilename = "/dev/paud0"; static const char * moduleName = "CAudioOutAIX"; // this will point to the SOM environment needed by the UMS subsystem static Environment* gpSomEnvironment = NULL; // static local routines, used to translate UMS message code to RMA types. static const char * getUMSAudioDeviceError( UMSAudioDevice_ReturnCode ); static audio_error UMSErrorCodeToRACode( UMSAudioDevice_ReturnCode ); CAudioOutAIX::CAudioOutAIX() : mixm_wID( -1 ), m_wState( RA_AOS_CLOSED ), m_wLastError( RA_AOE_NOERR ), m_bMixerPresent(FALSE), m_wBlockSize(0), m_ulLastNumBytes (0), m_ulBytesRemaining(0), m_ulTotalWritten(0), m_bFirstWrite (TRUE), m_pPlaybackCountCBTime(NULL), m_PendingCallbackID (0), m_bCallbackPending(FALSE), m_paused(FALSE), m_pWriteList(NULL), m_lLeftGain(100), m_lRightGain(100) { // set up UMS environment. gpSomEnvironment = somGetGlobalEnvironment(); HX_ASSERT( gpSomEnvironment ); m_pAudioDevice = new UMSBAUDDevice(); // Get AUDIODEV environment var to find audio device of choice char *adev = (char*)getenv("AUDIODEV"); /* Flawfinder: ignore */ char *mdev = (char*)getenv("MIXERDEV"); /* Flawfinder: ignore */ // Use defaults if no environment variable is set. m_DevName[26] = NULL; m_DevCtlName[26] = NULL; if (adev && strlen(adev) > 0) SafeStrCpy(m_DevName, adev, 26); else SafeStrCpy(m_DevName, szDefaultPortFilename, 26); m_pPlaybackCountCBTime = new Timeval; if (mdev && strlen(mdev) > 0) SafeStrCpy(m_DevCtlName, mdev, 26); else SafeStrCpy(m_DevCtlName, szDefaultPortFilename, 26); m_pWriteList = new CHXSimpleList(); // now configure our device. m_pAudioDevice->set_audio_format_type ( gpSomEnvironment, "PCM" ); m_pAudioDevice->set_number_format ( gpSomEnvironment, "TWOS_COMPLEMENT" ); m_pAudioDevice->set_byte_order ( gpSomEnvironment, "MSB" ); m_pAudioDevice->set_time_format ( gpSomEnvironment, UMSAudioTypes_Msecs ); m_pAudioDevice->set_balance ( gpSomEnvironment, lDefaultBalance ); m_pAudioDevice->set_volume ( gpSomEnvironment, zlCurrentVolume ); m_pAudioDevice->enable_output ( gpSomEnvironment, "LINE_OUT", &m_lLeftGain, &m_lRightGain ); } CAudioOutAIX::~CAudioOutAIX() { if ( m_wState != RA_AOS_CLOSED ) { _Imp_Close(); m_wState = RA_AOS_CLOSED; } mixm_wID = -1; m_wState = RA_AOS_CLOSED; m_wLastError = RA_AOE_NOERR; m_bMixerPresent = FALSE; m_wBlockSize = 0; m_ulLastNumBytes = 0; m_ulBytesRemaining = 0; m_ulTotalWritten = 0; if (m_pScheduler) m_pScheduler->Release(); m_PendingCallbackID = 0; m_bCallbackPending = TRUE; while(!m_pWriteList->IsEmpty()) { IHXBuffer* pBuffer = (IHXBuffer*)m_pWriteList->RemoveHead(); HX_RELEASE(pBuffer); } HX_DELETE(m_pWriteList); delete m_pAudioDevice; } /* * I do not open /dev/mixer to control volume, the volume is scaled * by the UMS device. * The device's volume has a range of 0..100. */ UINT16 CAudioOutAIX::_Imp_GetVolume() { long volume; m_pAudioDevice->get_volume( gpSomEnvironment, &volume ); return zlCurrentVolume; return (UINT16)volume; } HX_RESULT CAudioOutAIX::_Imp_SetVolume( const UINT16 uVolume ) { m_pAudioDevice->set_volume( gpSomEnvironment, (long)uVolume ); zlCurrentVolume = uVolume; return RA_AOE_NOERR; } /* * All UMS audio devices support volume. */ BOOL CAudioOutAIX::_Imp_SupportsVolume() { return TRUE; } HX_RESULT CAudioOutAIX:: _Imp_Open( const HXAudioFormat* pFormatSupplied ) { if( pFormatSupplied != NULL ) m_pAudioFormat = pFormatSupplied; HX_ASSERT( m_pAudioFormat != NULL ); // Get the core scheduler interface; Use this to schedule polling // the audio device for number of bytes played. if ( m_pContext ) { m_pContext->QueryInterface(IID_IHXScheduler, (void**) &m_pScheduler ); } // Check state. Could already be opened. if ( m_wState == RA_AOS_OPEN_PAUSED || m_wState == RA_AOS_OPEN_PLAYING || m_wState == RA_AOS_OPENING ) return RA_AOE_NOERR; m_ulBlocksProcessed = 0; UMSAudioDevice_ReturnCode retCode = m_pAudioDevice->open( gpSomEnvironment, m_DevName, "PLAY", 0 ); if( retCode != UMSAudioDevice_Success ) { return HXR_FAILED; } m_wBlockSize = m_ulBytesPerGran; //m_pAudioFormat->uMaxBlockSize; m_uSampFrameSize = m_pAudioFormat->uBitsPerSample / 8; // Set device state m_wState = RA_AOS_OPENING; // Configure the audio device. AUDIOERROR iVal = SetDeviceConfig( m_pAudioFormat ); return RA_AOE_NOERR; } HX_RESULT CAudioOutAIX::_Imp_Close() { m_wState = RA_AOS_CLOSED; // this will force the player to play all remaining data. If passed TRUE, // the call will block until finished playing. Do we want to call this? // m_pAudioDevice->play_remaining_data( gpSomEnvironment, TRUE ); _Imp_Write(NULL); m_pAudioDevice->stop( gpSomEnvironment ); m_pAudioDevice->close( gpSomEnvironment ); // Remove callback from scheduler if (m_bCallbackPending) { m_pScheduler->Remove(m_PendingCallbackID); m_bCallbackPending = FALSE; } return RA_AOE_NOERR; } HX_RESULT CAudioOutAIX::_Imp_Write( const HXAudioData* pAudioOutHdr ) { IHXBuffer* pBuffer = NULL; UCHAR* pData = NULL; UMSAudioTypes_Buffer adBuffer; UINT32* pTimeStamp = NULL; if ( m_bFirstWrite && pAudioOutHdr) { /* Initialize the playback callback time. */ HXTimeval lTime = m_pScheduler->GetCurrentSchedulerTime(); m_pPlaybackCountCBTime->tv_sec = lTime.tv_sec; m_pPlaybackCountCBTime->tv_usec = lTime.tv_usec; m_bFirstWrite = FALSE; ReschedPlaybackCheck(); } if (pAudioOutHdr) { ++m_ulBlocksProcessed ; DPRINTF(D_INFO, ("_Imp_Write: buf len in milli-seconds: %lu\n", ((ULONG32) (( 1000.0 / (m_num_channels * m_uSampFrameSize * m_sample_rate)) * pAudioOutHdr->pData->GetSize())))); } // If we are paused, just add the block to the end of the WriteList. if (m_paused) { if (!pAudioOutHdr) return m_wLastError = RA_AOE_NOERR; IHXBuffer* pNewBuffer = new CHXBuffer(); pNewBuffer->Set(pAudioOutHdr->pData->GetBuffer(), pAudioOutHdr->pData->GetSize()); pNewBuffer->AddRef(); m_pWriteList->AddTail(pNewBuffer); return RA_AOE_NOERR; } BOOL bWroteSomething; do { bWroteSomething = FALSE; if(m_pWriteList->GetCount() <= 0) { if(!pAudioOutHdr) return RA_AOE_NOERR; pData = (UCHAR*)pAudioOutHdr->pData->GetBuffer(); adBuffer._length = pAudioOutHdr->pData->GetSize(); } else { if (pAudioOutHdr) { IHXBuffer* pNewBuffer = new CHXBuffer(); pNewBuffer->AddRef(); pNewBuffer->Set(pAudioOutHdr->pData->GetBuffer(), pAudioOutHdr->pData->GetSize()); m_pWriteList->AddTail(pNewBuffer); } pBuffer = (IHXBuffer*)m_pWriteList->RemoveHead(); HX_ASSERT(pBuffer); pData = pBuffer->GetBuffer(); adBuffer._length = pBuffer->GetSize(); } // Write audio data to device. long count = 0; int wrote; adBuffer._buffer = pData; adBuffer._maximum = adBuffer._length; int nBytesPerSample = (m_bits_per_sample/8)* m_num_channels; long lSamplesToWrite = adBuffer._length / nBytesPerSample; long lSamplesWritten =0; int transferredCount = adBuffer._length; m_wLastError = RA_AOE_NOERR; // less work this way. UMSAudioDevice_ReturnCode retCode = m_pAudioDevice->write( gpSomEnvironment, &adBuffer, lSamplesToWrite, &lSamplesWritten ); switch(retCode) { case UMSAudioDevice_Success : { int nBytesWritten = lSamplesWritten * nBytesPerSample; m_ulTotalWritten += nBytesWritten; HX_ASSERT(lSamplesWritten <= lSamplesToWrite); if( lSamplesWritten == lSamplesToWrite ) { bWroteSomething = TRUE; } else { /* requeue the balance. */ long lSamplesToRequeue = lSamplesToWrite - lSamplesWritten; IHXBuffer* pNewBuffer = new CHXBuffer(); pNewBuffer->AddRef(); pNewBuffer->Set( adBuffer._buffer + nBytesWritten, adBuffer._length - nBytesWritten ); m_pWriteList->AddHead(pNewBuffer); } } break; case UMSAudioDevice_DeviceError : // indicates EWOULDBLOCK case UMSAudioDevice_Preempted : case UMSAudioDevice_Interrupted : { // just requeue the data IHXBuffer* pNewBuffer = new CHXBuffer(); pNewBuffer->AddRef(); pNewBuffer->Set( adBuffer._buffer, adBuffer._length ); m_pWriteList->AddHead(pNewBuffer); break; } default : // failure! HX_ASSERT(FALSE); m_wLastError = UMSErrorCodeToRACode( retCode ); } INT32 newRefCount = 0; if( pBuffer ) newRefCount = pBuffer->Release(); HX_ASSERT(newRefCount == 0); pBuffer = NULL; pAudioOutHdr = NULL; // don't add the same buffer again... } while (bWroteSomething); return m_wLastError; } // Seek() is never called. HX_RESULT CAudioOutAIX::_Imp_Seek(ULONG32 ulSeekTime) { return HXR_OK; } HX_RESULT CAudioOutAIX::_Imp_Pause() { m_paused = TRUE; return RA_AOE_NOERR; } HX_RESULT CAudioOutAIX::_Imp_Resume() { m_paused = FALSE; return RA_AOE_NOERR; } ///////////////////////////////////////////////////////////////////////// // Imp_Reset() // Reset the device. It is important to note that this call results // from a seek, so we must act accordingly. // HX_RESULT CAudioOutAIX::_Imp_Reset() { _Imp_Close(); _Imp_Open(NULL); while(!m_pWriteList->IsEmpty()) { IHXBuffer* pBuffer = (IHXBuffer*)m_pWriteList->RemoveHead(); HX_RELEASE(pBuffer); } HX_ASSERT( m_pWriteList->IsEmpty() ); // these are vital! If they are not set this way, playback after a seek // will just stop after a few frames. m_bPaused = FALSE; m_bFirstWrite = TRUE; // and these are fairly obvious, but necessary after a seek. It should be // noted here that the audio timeline after a seek is relative to the // internal timeline of the core scheduler; all our playback is based from // our start. m_ulTotalWritten =0; m_ulCurrentTime = 0; return RA_AOE_NOERR; } // this will force the player to play all remaining data. The call // will block until finished playing - if this is not what is desired, // change the second parameter of the call to FALSE. HX_RESULT CAudioOutAIX::_Imp_Drain() { // -pjg need to drain the WriteList also. // this will force the player to play all remaining data. If passed TRUE, // the call will block until finished playing. while(!m_pWriteList->IsEmpty()) { IHXBuffer* pBuffer = (IHXBuffer*)m_pWriteList->RemoveHead(); HX_RELEASE(pBuffer); } m_pAudioDevice->play_remaining_data( gpSomEnvironment, TRUE ); return RA_AOE_NOERR; } AUDIOERROR CAudioOutAIX::SetDeviceConfig( const HXAudioFormat* pFormat ) { m_pAudioDevice->set_sample_rate ( gpSomEnvironment, pFormat->ulSamplesPerSec, &m_oSamples ); m_pAudioDevice->set_number_of_channels ( gpSomEnvironment, pFormat->uChannels ); m_pAudioDevice->set_bits_per_sample ( gpSomEnvironment, pFormat->uBitsPerSample ); m_pAudioDevice->set_audio_format_type ( gpSomEnvironment, "PCM" ); m_pAudioDevice->set_number_format ( gpSomEnvironment, "TWOS_COMPLEMENT" ); m_pAudioDevice->set_byte_order ( gpSomEnvironment, "MSB" ); m_pAudioDevice->set_time_format ( gpSomEnvironment, UMSAudioTypes_Msecs ); m_pAudioDevice->set_balance ( gpSomEnvironment, lDefaultBalance ); m_pAudioDevice->set_volume ( gpSomEnvironment, zlCurrentVolume ); m_pAudioDevice->enable_output ( gpSomEnvironment, "LINE_OUT", &m_lLeftGain, &m_lRightGain ); m_pAudioDevice->initialize ( gpSomEnvironment ); m_pAudioDevice->start ( gpSomEnvironment ); m_num_channels = pFormat->uChannels; m_sample_rate = pFormat->ulSamplesPerSec; m_bits_per_sample = pFormat->uBitsPerSample; return RA_AOE_NOERR; } HX_RESULT CAudioOutAIX::_Imp_CheckFormat( const HXAudioFormat* pFormat ) { // Check for valid format inputs. if ( pFormat->uChannels != 1 && pFormat->uChannels != 2 ) { return HXR_FAIL; } if ( pFormat->uBitsPerSample != 8 && pFormat->uBitsPerSample != 16 ) { return HXR_FAIL; } return HXR_OK; } /************************************************************************ * Method: * CAudioOutAIX::_Imp_GetCurrentTime * Purpose: * Get the current time from the audio device. * We added this to support the clock available in the * Window's audio driver. * * Current time is simply a function of the total number of * bytes written to the device minus the number still in * the device queue. This is expressed as playbackbytes. * * Ported to Aix 4.3.1 by fhutchinson, Jan 1999. * * The minitimer below is a simple timer employed to avoid excessive * calls to the UMS audio driver to get the current time, the rationale * being that a gettimeofday call and a little arithmetic is less expensive * than a call to the driver. The threshold value is the number of * microseconds (1/1,000,000 sec) that must pass before the time goes off. */ #include // for strerror() HX_RESULT CAudioOutAIX::_Imp_GetCurrentTime( ULONG32& ulCurrentTime ) { long lBytesPerSample = m_bits_per_sample/8; long lBytesStillInDeviceBuffer; ulCurrentTime = m_ulCurrentTime; UMSAudioDevice_ReturnCode retCode; // set the device to return the amount of data still in the device buffer as a // number of bytes retCode = m_pAudioDevice->set_time_format( gpSomEnvironment, UMSAudioTypes_Bytes ); if( retCode != UMSAudioDevice_Success ) { m_wLastError = UMSErrorCodeToRACode( retCode ); return (m_wLastError = UMSErrorCodeToRACode( retCode ) ); } retCode = m_pAudioDevice->write_buff_used( gpSomEnvironment, &lBytesStillInDeviceBuffer ); if( retCode != UMSAudioDevice_Success ) { // almost certainly because the device is not opened. return (m_wLastError = UMSErrorCodeToRACode( retCode ) ); } long lBytesPlayed = m_ulTotalWritten - lBytesStillInDeviceBuffer; m_ulCurrentTime = ulCurrentTime = ( (lBytesPlayed /lBytesPerSample) / (float)m_sample_rate * 1000 / m_num_channels ); return HXR_OK; } /************************************************************************ * Method: * CAudioOutAIX::_Imp_GetAudioFd * Purpose: Intended to return the file descriptor of the device. * However, UMS is not file descriptor-based, so we * return a -1, generally accepted as a closed device fd. */ INT16 CAudioOutAIX::_Imp_GetAudioFd() { return -1; } /************************************************************************ * Method: * CAudioOutAIX::DoTimeSyncs * Purpose: * Manual time syncs! Fork! */ void CAudioOutAIX::DoTimeSyncs() { ReschedPlaybackCheck(); OnTimeSync(); // XXXDMB // hxaudev.cpp::CHXAudioDevice::OnTimeSync() return; } /************************************************************************ * Method: * CAudioOutAIX::ReschedPlaybackCheck() * Purpose: * Reschedule playback callback. */ HX_RESULT CAudioOutAIX::ReschedPlaybackCheck() { HX_RESULT theErr = HXR_OK; if (m_bCallbackPending) return theErr; *m_pPlaybackCountCBTime += (int) (m_ulGranularity*1000) / 2; // Put this back in the scheduler. HXPlaybackCountCb* pCallback = 0; pCallback = new HXPlaybackCountCb(TRUE); if (pCallback) { pCallback->m_pAudioDeviceObject = this; m_bCallbackPending = TRUE; m_PendingCallbackID = m_pScheduler->AbsoluteEnter(pCallback,*((HXTimeval*)m_pPlaybackCountCBTime)) ; } else theErr = HXR_OUTOFMEMORY; // but ignored, why? return theErr; } UINT16 CAudioOutAIX::_NumberOfBlocksRemainingToPlay(void) { ULONG32 ulCurTime = 0; GetCurrentAudioTime(ulCurTime); UINT32 bytesBuffered = 0; LISTPOSITION i = m_pWriteList->GetHeadPosition(); while (i) { bytesBuffered += ((IHXBuffer *)m_pWriteList->GetAt(i)) -> GetSize(); m_pWriteList->GetNext(i); } m_pAudioDevice->set_time_format( gpSomEnvironment, UMSAudioTypes_Bytes ); long lBytesStillInDeviceBuffer; UINT32 tries = 100; UMSAudioDevice_ReturnCode retCode; while (--tries && (m_pAudioDevice->write_buff_used( gpSomEnvironment, &lBytesStillInDeviceBuffer )) != UMSAudioDevice_Success); if (!tries) { HX_ASSERT(FALSE); // don't know what to do! } UINT16 blocks = (bytesBuffered + lBytesStillInDeviceBuffer) / m_wBlockSize + 1; //UINT16 blocks = (int) (((double)bytesBuffered + lBytesStillInDeviceBuffer) / // m_ulBytesPerGran) + 1; return blocks; } // CAudioOutAIX::HXPlaybackCountCb CAudioOutAIX::HXPlaybackCountCb::HXPlaybackCountCb(BOOL timed) : m_lRefCount (0), m_pAudioDeviceObject (0), m_timed(timed) { } CAudioOutAIX::HXPlaybackCountCb::~HXPlaybackCountCb() { } /* * IUnknown methods */ ///////////////////////////////////////////////////////////////////////// // Method: // IUnknown::QueryInterface // Purpose: // Implement this to export the interfaces supported by your // object. // STDMETHODIMP CAudioOutAIX::HXPlaybackCountCb::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) CAudioOutAIX::HXPlaybackCountCb::AddRef() { return InterlockedIncrement(&m_lRefCount); } ///////////////////////////////////////////////////////////////////////// // Method: // IUnknown::Release // Purpose: // Everyone usually implements this the same... feel free to use // this implementation. // STDMETHODIMP_(ULONG32) CAudioOutAIX::HXPlaybackCountCb::Release() { if (InterlockedDecrement(&m_lRefCount) > 0) { return m_lRefCount; } delete this; return 0; } /* * IHXPlaybackCountCb methods */ STDMETHODIMP CAudioOutAIX::HXPlaybackCountCb::Func(void) { if (m_pAudioDeviceObject) { if(!m_timed) { m_pAudioDeviceObject->_Imp_Write(NULL); } else { m_pAudioDeviceObject->_Imp_Write(NULL); m_pAudioDeviceObject->m_bCallbackPending = FALSE; m_pAudioDeviceObject->DoTimeSyncs(); } } return HXR_OK; } // // getUMSAudioDeviceError() // // returns the string description of the given UMS error code // static const char * getUMSAudioDeviceError( UMSAudioDevice_ReturnCode code ) { switch( code ) { case UMSAudioDevice_Success : return "Success"; case UMSAudioDevice_InvalidParam : return "Invalid parameter"; case UMSAudioDevice_MemoryError : return "Success"; case UMSAudioDevice_DeviceNotAvail : return "Device is not available"; case UMSAudioDevice_Preempted : return "Preempted"; case UMSAudioDevice_Interrupted : return "Inerrupted"; case UMSAudioDevice_DeviceError : return "Device error"; // make sure this is last before default case. case UMSAudioDevice_Failure : return "undescribed error"; default : break; } return "Unknown error"; } // // UMSErrorCodeToRACode() // // Converts the UMS return code to a best-guess RMA return code equivalent. // audio_error UMSErrorCodeToRACode( UMSAudioDevice_ReturnCode retCode ) { switch( retCode ) { case UMSAudioDevice_Success : return RA_AOE_NOERR; case UMSAudioDevice_InvalidParam : return RA_AOE_INVALPARAM; case UMSAudioDevice_DeviceNotAvail : return RA_AOE_BADDEVICEID; case UMSAudioDevice_NoDevice : return RA_AOE_BADDEVICEID; case UMSAudioDevice_IncompatibleSettings : return RA_AOE_BADFORMAT ; case UMSAudioDevice_NotOpen : return RA_AOE_DEVNOTOPEN ; case UMSAudioDevice_NotReady : return RA_AOE_DEVBUSY ; case UMSAudioDevice_SettingsChanged : return RA_AOE_BADFORMAT ; // this is a stretch... case UMSAudioDevice_DeviceError : return RA_AOE_BADWRITE ; case UMSAudioDevice_NotSupported : return RA_AOE_NOTSUPPORTED ; // and these we will classify under general for now. case UMSAudioDevice_Interrupted : ; case UMSAudioDevice_Preempted : ; case UMSAudioDevice_MemoryError : ; case UMSAudioDevice_Failure : ; case UMSAudioDevice_UnderRun : ; case UMSAudioDevice_OverRun : ; default : break; } return RA_AOE_GENERAL; }