Revert previous commit, was incorrect
[amarok.git] / src / engine / helix / helix-sp / hspalsadevice.cpp
blob8060b2eab9e0f549ad63df42dc937346eb830a14
1 /******************************************************************************
2 * *
3 * This program is free software; you can redistribute it and/or modify *
4 * it under the terms of the GNU General Public License as published by *
5 * the Free Software Foundation; either version 2 of the License, or *
6 * (at your option) any later version. *
7 * *
8 * This library is distributed in the hope that it will be useful, but *
9 * WITHOUT ANY WARRANTY; without even the implied warranty of *
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
11 * Lesser General Public License for more details. *
12 * *
13 * You should have received a copy of the GNU Lesser General Public *
14 * License along with this library; if not, write to the Free Software *
15 * Foundation, Inc., 51 Franklin St, 5th fl, Boston, MA 02110-1301, *
16 * USA, or check http://www.fsf.org/about/contact.html *
17 * *
18 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. *
19 * Portions Copyright (c) 2005 Paul Cifarelli *
20 * *
21 ******************************************************************************/
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <sys/ioctl.h>
29 #include <stdio.h>
30 #include <math.h>
31 #include <time.h>
32 #include <sys/time.h>
33 #include <unistd.h>
35 #include "config-amarok.h"
37 #include "hxcomm.h"
38 #include "hxcore.h"
39 #include "hxprefs.h"
40 #include "hxstrutl.h"
41 #include "hxvsrc.h"
42 #include "hxresult.h"
43 #include "hxausvc.h"
44 #include "helix-sp.h"
46 #include "ihxpckts.h"
47 #include "hxprefs.h"
48 #include "hspalsadevice.h"
50 #ifdef HX_LOG_SUBSYSTEM
51 #include "hxtlogutil.h"
52 #include "ihxtlogsystem.h"
53 #endif
55 #include "dllpath.h"
57 #include "hxbuffer.h"
59 #ifdef USE_HELIX_ALSA
61 IHXPreferences* z_pIHXPrefs = 0;
62 #define RA_AOE_NOERR 0
63 #define RA_AOE_GENERAL -1
64 #define RA_AOE_DEVNOTOPEN -2
65 #define RA_AOE_NOTENABLED -3
66 #define RA_AOE_BADFORMAT -4
67 #define RA_AOE_NOTSUPPORTED -5
68 #define RA_AOE_DEVBUSY -6
69 #define RA_AOE_BADOPEN -7
71 #ifdef __FreeBSD__
72 #define PTHREAD_MUTEX_FAST_NP PTHREAD_MUTEX_NORMAL
73 #endif
75 #if !defined(__NetBSD__) && !defined(__OpenBSD__)
76 #include <sys/soundcard.h>
77 #else
78 #include <soundcard.h>
79 #endif
81 typedef HX_RESULT (HXEXPORT_PTR FPRMSETDLLACCESSPATH) (const char*);
84 AudioQueue::AudioQueue( const HXAudioData *buf) : fwd(0)
86 ad = *buf;
87 ad.pData->AddRef();
90 AudioQueue::~AudioQueue()
92 ad.pData->Release();
96 STDMETHODIMP
97 HSPAudioDevice::QueryInterface(REFIID riid, void**ppvObj)
99 if(IsEqualIID(riid, IID_IUnknown))
101 AddRef();
102 *ppvObj = (IUnknown*)(IHXAudioDevice *)this;
103 return HXR_OK;
105 else if(IsEqualIID(riid, IID_IHXAudioDevice))
107 AddRef();
108 *ppvObj = (IHXAudioDevice *)this;
109 return HXR_OK;
111 *ppvObj = NULL;
112 return HXR_NOINTERFACE;
115 STDMETHODIMP_(UINT32)
116 HSPAudioDevice::AddRef()
118 return InterlockedIncrement(&m_lRefCount);
121 STDMETHODIMP_(UINT32)
122 HSPAudioDevice::Release()
124 if (InterlockedDecrement(&m_lRefCount) > 0)
126 return m_lRefCount;
129 delete this;
130 return 0;
133 STDMETHODIMP
134 HSPAudioDevice::CheckFormat( const HXAudioFormat* pAudioFormat )
136 m_Player->print2stderr("########## Got to HSPAudioDevice::CheckFormat\n");
138 return (_CheckFormat(pAudioFormat));
141 STDMETHODIMP
142 HSPAudioDevice::Close( const BOOL bFlush )
144 m_Player->print2stderr("########## Got to HSPAudioDevice::Close flush %d\n", bFlush);
146 pthread_mutex_lock(&m_m);
148 if (bFlush)
150 clearQueue();
151 _Drain();
154 _Reset();
155 _CloseAudio();
156 _CloseMixer();
158 m_closed = true;
160 m_ulCurrentTime = m_ulQTime = 0;
162 if (m_pStreamResponse)
163 m_pStreamResponse->Release();
166 pthread_mutex_unlock(&m_m);
168 return 0;
171 STDMETHODIMP
172 HSPAudioDevice::Drain()
174 m_Player->print2stderr("########## Got to HSPAudioDevice::Drain\n");
175 pthread_mutex_lock(&m_m);
177 LONG32 err = _Drain();
178 clearQueue();
179 pthread_mutex_unlock(&m_m);
180 return err;
183 STDMETHODIMP
184 HSPAudioDevice::GetCurrentAudioTime( REF(ULONG32) ulCurrentTime )
186 //m_Player->print2stderr("########## Got to HSPAudioDevice::GetCurrentTime = %d\n", m_ulCurrentTime);
188 int err = 0;
189 snd_pcm_sframes_t frame_delay = 0;
191 pthread_mutex_lock(&m_m);
192 if (!m_closed)
194 err = snd_pcm_delay (m_pAlsaPCMHandle, &frame_delay);
195 if (err < 0)
197 #ifdef HX_LOG_SUBSYSTEM
198 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_status: %s", snd_strerror(err));
199 #endif
200 m_Player->print2stderr("########## HSPAudioDevice::GetCurrentAudioTime error getting frame_delay: %s\n", snd_strerror(err));
201 pthread_mutex_unlock(&m_m);
202 return -1;
205 ulCurrentTime = m_ulCurrentTime - (ULONG32)(((double)frame_delay * 1000.0) / (double)m_unSampleRate);
207 //m_Player->print2stderr("########## HSPAudioDevice::GetCurrentAudioTime %d %d\n", ulCurrentTime, m_ulCurrentTime);
209 pthread_mutex_unlock(&m_m);
211 return 0;
214 STDMETHODIMP_(UINT16)
215 HSPAudioDevice::GetVolume()
217 m_Player->print2stderr("########## Got to HSPAudioDevice::GetVolume\n");
218 return 0;
221 STDMETHODIMP_(BOOL)
222 HSPAudioDevice::InitVolume(const UINT16 /*uMinVolume*/, const UINT16 /*uMaxVolume*/)
224 m_Player->print2stderr("########## Got to HSPAudioDevice::InitVolume\n");
225 return true;
228 STDMETHODIMP
229 HSPAudioDevice::Open(const HXAudioFormat* pAudioFormat, IHXAudioDeviceResponse* pStreamResponse)
231 int err;
233 m_Player->print2stderr("########## Got to HSPAudioDevice::Open\n");
234 if (pStreamResponse)
235 pStreamResponse->AddRef();
237 pthread_mutex_lock(&m_m);
239 m_drain = false;
240 m_closed = false;
241 m_ulTotalWritten = 0;
242 m_ulCurrentTime = 0;
243 m_SWPause = false;
244 m_pStreamResponse = pStreamResponse;
245 if (!m_pAlsaPCMHandle)
247 err = _OpenAudio();
248 if (err) m_Player->print2stderr("########## HSPAudioDevice::Open error (device) %d\n", err);
249 err = SetDeviceConfig(pAudioFormat);
250 if (err) m_Player->print2stderr("########## HSPAudioDevice::Open error (config) %d\n", err);
251 m_ulCurrentTime = m_ulLastTime = m_ulQTime = 0;
254 if (m_pAlsaMixerHandle != NULL)
256 err = _OpenMixer();
257 if (err) m_Player->print2stderr("########## HSPAudioDevice::Open error (mixer) %d\n", err);
260 pthread_mutex_unlock(&m_m);
262 return 0;
265 STDMETHODIMP
266 HSPAudioDevice::Pause()
268 m_Player->print2stderr("########## Got to HSPAudioDevice::Pause %d\n", m_bHasHardwarePauseAndResume);
269 _Pause();
270 return 0;
273 STDMETHODIMP
274 HSPAudioDevice::Reset()
276 m_Player->print2stderr("########## Got to HSPAudioDevice::Reset\n");
277 return (_Reset());
280 STDMETHODIMP
281 HSPAudioDevice::Resume()
283 m_Player->print2stderr("########## Got to HSPAudioDevice::Resume\n");
284 _Resume();
286 return 0;
289 STDMETHODIMP
290 HSPAudioDevice::SetVolume( const UINT16 /*uVolume*/ )
292 m_Player->print2stderr("########## Got to HSPAudioDevice::SetVolume\n");
293 return 0;
296 STDMETHODIMP
297 HSPAudioDevice::Write( const HXAudioData* pAudioData )
299 addBuf( new AudioQueue( pAudioData ) );
300 return 0;
303 int HSPAudioDevice::sync()
305 if (m_pStreamResponse)
307 ULONG32 curtime;
308 if (!GetCurrentAudioTime(curtime) && curtime)
309 return m_pStreamResponse->OnTimeSync(curtime);
310 else
312 // probably a seek occurred
313 //clearQueue();
314 _Reset();
317 return -1;
321 HX_RESULT HSPAudioDevice::OnTimeSync()
323 HX_RESULT err;
325 if (!(err = sync()))
326 return HXR_OK;
328 return err;
332 HSPAudioDevice::_Write( const HXAudioData* pAudioData )
334 unsigned long len;
335 long bytes;
336 unsigned char *data;
337 int err = 0;
339 pAudioData->pData->Get(data, len);
341 // if the time of this buf is earlier than the last, or the time between this buf and the last is > 1 buffer's worth, this was a seek
342 if ( pAudioData->ulAudioTime < m_ulCurrentTime ||
343 pAudioData->ulAudioTime - m_ulCurrentTime > (1000 * len) / (m_unNumChannels * m_unSampleRate) + 1 )
345 m_Player->print2stderr("########## seek detected %ld %ld, len = %ld %d\n", m_ulCurrentTime, pAudioData->ulAudioTime, len,
346 abs(pAudioData->ulAudioTime - (m_ulCurrentTime + (1000 * len) / (m_unNumChannels * m_unSampleRate))));
347 //_Reset();
348 //clearQueue();
351 if (!err)
353 err = WriteBytes(data, len, bytes);
354 m_ulCurrentTime = pAudioData->ulAudioTime;
356 err = sync();
358 //m_Player->print2stderr("########## %d %d\n", m_ulCurrentTime,pAudioData->ulAudioTime);
360 //m_Player->print2stderr("########## Got to HSPAudioDevice::Write len=%d byteswriten=%d err=%d time=%d\n",
361 // len,bytes,err,m_ulCurrentTime);
363 return err;
367 //------------------------------------------
368 // Ctors and Dtors.
369 //------------------------------------------
370 HSPAudioDevice::HSPAudioDevice(HelixSimplePlayer *player, const char *device) :
371 m_pAlsaPCMHandle (NULL),
372 m_pAlsaMixerHandle (NULL),
373 m_pAlsaMixerElem (NULL),
375 m_pPCMDeviceName (NULL),
376 m_pMixerDeviceName (NULL),
377 m_pMixerElementName (NULL),
379 m_bHasHardwarePauseAndResume (false),
380 m_nBytesPlayedBeforeLastTrigger(0),
382 m_nLastBytesPlayed(0),
384 m_bGotInitialTrigger(false),
385 m_bUseMMAPTStamps(true),
386 m_lRefCount(0),
387 m_wLastError(0),
388 m_SWPause(false),
389 m_Player(player),
390 m_done(false),
391 m_drain(false),
392 m_closed(true),
393 m_head(0),
394 m_tail(0)
396 pthread_mutexattr_t ma;
398 pthread_mutexattr_init(&ma);
399 pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_FAST_NP); // note this is not portable outside linux and a few others
400 pthread_mutex_init(&m_m, &ma);
402 pthread_cond_init(&m_cv, NULL);
404 // create thread that will wait for buffers to appear to send to the device
405 pthread_create(&m_thrid, 0, writerThread, this);
407 if (device)
409 int len = strlen( device );
410 m_Player->pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &m_pPCMDeviceName);
411 if (m_pPCMDeviceName)
412 m_pPCMDeviceName->Set( (const unsigned char*) device, len + 1 );
416 HSPAudioDevice::~HSPAudioDevice()
418 pthread_mutex_lock(&m_m);
419 m_done = true;
420 pthread_mutex_unlock(&m_m);
421 pthread_cond_signal(&m_cv);
422 void *tmp;
423 pthread_join(m_thrid, &tmp);
426 if(m_pPCMDeviceName)
428 HX_RELEASE(m_pPCMDeviceName);
431 if(m_pMixerDeviceName)
433 HX_RELEASE(m_pMixerDeviceName);
436 if(m_pMixerElementName)
438 HX_RELEASE(m_pMixerElementName);
441 pthread_cond_destroy(&m_cv);
442 pthread_mutex_destroy(&m_m);
445 void HSPAudioDevice::addBuf(struct AudioQueue *item)
447 pthread_mutex_lock(&m_m);
449 m_ulQTime = item->ad.ulAudioTime;
450 if (m_tail)
452 item->fwd = 0;
453 m_tail->fwd = item;
454 m_tail = item;
456 else
458 item->fwd = 0;
459 m_head = item;
460 m_tail = item;
463 pthread_mutex_unlock(&m_m);
464 pthread_cond_signal(&m_cv);
467 AudioQueue *HSPAudioDevice::getBuf()
469 pthread_mutex_lock(&m_m);
471 AudioQueue *item = m_head;
473 if (item)
475 m_head = item->fwd;
476 if (!m_head)
477 m_tail = 0;
480 pthread_mutex_unlock(&m_m);
482 return item;
485 // NOTE THAT THIS IS NOT UNDER LOCK, AND SHOULD ONLY BE CALLED WITH THE MUTEX LOCKED
486 void HSPAudioDevice::clearQueue()
488 AudioQueue *item;
490 if (!m_tail)
491 return;
493 while (m_tail)
495 item = m_head;
496 m_head = item->fwd;
497 if (!m_head)
498 m_tail = 0;
499 delete item;
504 void *HSPAudioDevice::writerThread( void *arg )
506 HSPAudioDevice *thisObj = (HSPAudioDevice *) arg;
507 AudioQueue *item;
509 pthread_mutex_lock(&thisObj->m_m);
510 while (!thisObj->m_done)
512 pthread_mutex_unlock(&thisObj->m_m);
513 item = thisObj->getBuf();
515 if (item)
516 thisObj->_Write(&item->ad);
518 delete item;
520 pthread_mutex_lock(&thisObj->m_m);
521 if (!thisObj->m_tail)
522 pthread_cond_wait(&thisObj->m_cv, &thisObj->m_m);
524 pthread_mutex_unlock(&thisObj->m_m);
526 thisObj->m_Player->print2stderr("############ writerThread exit\n");
527 return 0;
531 // These Device Specific methods must be implemented
532 // by the platform specific sub-classes.
533 INT16 HSPAudioDevice::GetAudioFd(void)
535 //Not implemented.
536 return -1;
540 //Device specific methods to open/close the mixer and audio devices.
541 HX_RESULT HSPAudioDevice::_OpenAudio()
543 int err = 0;
544 const char* szDevice;
546 HX_ASSERT (m_pAlsaPCMHandle == NULL);
547 if (m_pAlsaPCMHandle)
549 m_wLastError = RA_AOE_BADOPEN;
550 return m_wLastError;
553 if(z_pIHXPrefs)
555 HX_RELEASE(m_pPCMDeviceName);
556 z_pIHXPrefs->ReadPref("AlsaPCMDeviceName", m_pPCMDeviceName);
559 if(!m_pPCMDeviceName)
561 const char szDefaultDevice[] = "default";
563 m_Player->pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &m_pPCMDeviceName);
564 if (m_pPCMDeviceName)
565 m_pPCMDeviceName->Set( (const unsigned char*) szDefaultDevice, sizeof(szDefaultDevice) );
568 szDevice = (const char*) m_pPCMDeviceName->GetBuffer();
569 m_Player->print2stderr("########### Opening ALSA PCM device %s\n", szDevice);
571 #ifdef HX_LOG_SUBSYSTEM
572 HXLOGL2 (HXLOG_ADEV, "Opening ALSA PCM device %s",
573 szDevice);
574 #endif
576 err = snd_pcm_open( &m_pAlsaPCMHandle,
577 szDevice,
578 SND_PCM_STREAM_PLAYBACK,
580 if(err < 0)
582 m_Player->print2stderr("########### snd_pcm_open: %s %s\n", szDevice, snd_strerror (err));
583 #ifdef HX_LOG_SUBSYSTEM
584 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_open: %s",
585 szDevice, snd_strerror (err));
586 #endif
588 m_wLastError = RA_AOE_BADOPEN;
591 if(err == 0)
593 err = snd_pcm_nonblock(m_pAlsaPCMHandle, true);
594 if(err < 0)
596 m_Player->print2stderr("########## snd_pcm_nonblock: %s\n", snd_strerror (err));
597 #ifdef HX_LOG_SUBSYSTEM
598 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_nonblock: %s",
599 snd_strerror (err));
600 #endif
601 m_wLastError = RA_AOE_BADOPEN;
605 if(err == 0)
607 m_Player->print2stderr("########## return from OpenAudio\n");
608 m_wLastError = RA_AOE_NOERR;
610 else
612 if(m_pAlsaPCMHandle)
614 snd_pcm_close(m_pAlsaPCMHandle);
615 m_pAlsaPCMHandle = NULL;
619 return m_wLastError;
623 HX_RESULT HSPAudioDevice::_CloseAudio()
625 if (!m_pAlsaPCMHandle)
627 m_wLastError = RA_AOE_DEVNOTOPEN;
628 return m_wLastError;
631 #ifdef HX_LOG_SUBSYSTEM
632 HXLOGL2 (HXLOG_ADEV, "Closing ALSA PCM device");
633 #endif
635 snd_pcm_close(m_pAlsaPCMHandle);
636 m_pAlsaPCMHandle = NULL;
637 m_wLastError = RA_AOE_NOERR;
639 return m_wLastError;
643 HX_RESULT HSPAudioDevice::_OpenMixer()
645 int err;
646 const char* szDeviceName = NULL;
647 const char* szElementName = NULL;
648 int nElementIndex = 0;
650 HX_ASSERT (m_pAlsaMixerHandle == NULL);
651 if (m_pAlsaMixerHandle != NULL)
653 m_wLastError = RA_AOE_BADOPEN;
654 return m_wLastError;
657 HX_ASSERT(m_pAlsaMixerElem == NULL);
658 if (m_pAlsaMixerElem != NULL)
660 m_wLastError = RA_AOE_BADOPEN;
661 return m_wLastError;
664 if(z_pIHXPrefs)
666 HX_RELEASE(m_pMixerDeviceName);
667 z_pIHXPrefs->ReadPref("AlsaMixerDeviceName", m_pMixerDeviceName);
670 if(!m_pMixerDeviceName)
672 const char szDefaultDevice[] = "default";
674 m_Player->pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &m_pMixerDeviceName);
675 if (m_pMixerDeviceName)
676 m_pMixerDeviceName->Set( (const unsigned char*) szDefaultDevice, sizeof(szDefaultDevice) );
679 if(z_pIHXPrefs)
681 HX_RELEASE(m_pMixerElementName);
682 z_pIHXPrefs->ReadPref("AlsaMixerElementName", m_pMixerElementName);
685 if(!m_pMixerElementName)
687 const char szDefaultElement[] = "PCM";
689 m_Player->pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &m_pMixerElementName);
690 if (m_pMixerElementName)
691 m_pMixerElementName->Set( (const unsigned char*) szDefaultElement, sizeof(szDefaultElement) );
694 if(z_pIHXPrefs)
696 IHXBuffer* pElementIndex = NULL;
697 z_pIHXPrefs->ReadPref("AlsaMixerElementIndex", pElementIndex);
698 if(pElementIndex)
700 const char* szElementIndex = (const char*) pElementIndex->GetBuffer();
701 nElementIndex = atoi(szElementIndex);
703 HX_RELEASE(pElementIndex);
707 szDeviceName = (const char*) m_pMixerDeviceName->GetBuffer();;
708 szElementName = (const char*) m_pMixerElementName->GetBuffer();
710 #ifdef HX_LOG_SUBSYSTEM
711 HXLOGL2 (HXLOG_ADEV, "Opening ALSA mixer device %s",
712 szDeviceName);
713 #endif
715 err = snd_mixer_open(&m_pAlsaMixerHandle, 0);
716 if (err < 0)
718 #ifdef HX_LOG_SUBSYSTEM
719 HXLOGL1 ( HXLOG_ADEV, "snd_mixer_open: %s",
720 snd_strerror (err));
721 #endif
723 m_wLastError = RA_AOE_BADOPEN;
726 if (err == 0)
728 err = snd_mixer_attach(m_pAlsaMixerHandle, szDeviceName);
729 if (err < 0)
731 #ifdef HX_LOG_SUBSYSTEM
732 HXLOGL1 ( HXLOG_ADEV, "snd_mixer_attach: %s",
733 snd_strerror (err));
734 #endif
736 m_wLastError = RA_AOE_BADOPEN;
740 if (err == 0)
742 err = snd_mixer_selem_register(m_pAlsaMixerHandle, NULL, NULL);
743 if (err < 0)
745 #ifdef HX_LOG_SUBSYSTEM
746 HXLOGL1 ( HXLOG_ADEV, "snd_mixer_selem_register: %s",
747 snd_strerror (err));
748 #endif
749 m_wLastError = RA_AOE_BADOPEN;
753 if (err == 0)
755 err = snd_mixer_load(m_pAlsaMixerHandle);
756 if(err < 0 )
758 #ifdef HX_LOG_SUBSYSTEM
759 HXLOGL1 ( HXLOG_ADEV, "snd_mixer_load: %s",
760 snd_strerror (err));
761 #endif
763 m_wLastError = RA_AOE_NOTENABLED;
767 if (err == 0)
769 /* Find the mixer element */
770 snd_mixer_elem_t* fallback_elem = NULL;
771 snd_mixer_elem_t* elem = snd_mixer_first_elem (m_pAlsaMixerHandle);
772 snd_mixer_elem_type_t type;
773 const char* elem_name = NULL;
774 snd_mixer_selem_id_t *sid = NULL;
775 int index;
777 snd_mixer_selem_id_alloca(&sid);
779 while (elem)
781 type = snd_mixer_elem_get_type(elem);
782 if (type == SND_MIXER_ELEM_SIMPLE)
784 snd_mixer_selem_get_id(elem, sid);
786 /* We're only interested in playback volume controls */
787 if(snd_mixer_selem_has_playback_volume(elem) &&
788 !snd_mixer_selem_has_common_volume(elem))
790 if (!fallback_elem)
792 fallback_elem = elem;
795 elem_name = snd_mixer_selem_id_get_name (sid);
796 index = snd_mixer_selem_id_get_index(sid);
797 if (strcmp(elem_name, szElementName) == 0 &&
798 index == nElementIndex)
800 break;
805 elem = snd_mixer_elem_next(elem);
808 if (!elem && fallback_elem)
810 elem = fallback_elem;
811 elem_name = NULL;
812 type = snd_mixer_elem_get_type(elem);
814 if (type == SND_MIXER_ELEM_SIMPLE)
816 snd_mixer_selem_get_id(elem, sid);
817 elem_name = snd_mixer_selem_id_get_name (sid);
820 #ifdef HX_LOG_SUBSYSTEM
821 HXLOGL1 ( HXLOG_ADEV, "Could not find element %s, using element %s instead",
822 m_pMixerElementName, elem_name? elem_name: "unknown");
823 #endif
825 else if (!elem)
827 #ifdef HX_LOG_SUBSYSTEM
828 HXLOGL1 ( HXLOG_ADEV, "Could not find a usable mixer element",
829 snd_strerror (err));
830 #endif
831 m_wLastError = RA_AOE_BADOPEN;
832 err = -1;
835 m_pAlsaMixerElem = elem;
838 if(err == 0)
840 if (m_pAlsaMixerHandle)
842 m_bMixerPresent = 1;
843 _GetVolume();
845 else
847 m_bMixerPresent = 0;
850 m_wLastError = RA_AOE_NOERR;
852 else
854 if(m_pAlsaMixerHandle)
856 snd_mixer_close(m_pAlsaMixerHandle);
857 m_pAlsaMixerHandle = NULL;
861 return m_wLastError;
864 HX_RESULT HSPAudioDevice::_CloseMixer()
866 int err;
867 const char* szMixerDeviceName = NULL;
869 if (!m_pAlsaMixerHandle)
871 m_wLastError = RA_AOE_DEVNOTOPEN;
872 return m_wLastError;
875 if (!m_pMixerDeviceName)
877 m_wLastError = RA_AOE_DEVNOTOPEN;
878 return m_wLastError;
881 szMixerDeviceName = (const char*) m_pMixerDeviceName->GetBuffer();
882 err = snd_mixer_detach(m_pAlsaMixerHandle, szMixerDeviceName);
883 if (err < 0)
885 #ifdef HX_LOG_SUBSYSTEM
886 HXLOGL1 ( HXLOG_ADEV, "snd_mixer_detach: %s",
887 snd_strerror (err));
888 #endif
889 m_wLastError = RA_AOE_GENERAL;
892 if(err == 0)
894 err = snd_mixer_close(m_pAlsaMixerHandle);
895 if(err < 0)
897 #ifdef HX_LOG_SUBSYSTEM
898 HXLOGL1 ( HXLOG_ADEV, "snd_mixer_close: %s",
899 snd_strerror (err));
900 #endif
901 m_wLastError = RA_AOE_GENERAL;
905 if(err == 0)
907 m_pAlsaMixerHandle = NULL;
908 m_pAlsaMixerElem = NULL;
909 m_wLastError = RA_AOE_NOERR;
912 return m_wLastError;
916 //Device specific method to set the audio device characteristics. Sample rate,
917 //bits-per-sample, etc.
918 //Method *must* set member vars. m_unSampleRate and m_unNumChannels.
919 HX_RESULT HSPAudioDevice::SetDeviceConfig( const HXAudioFormat* pFormat )
921 snd_pcm_state_t state;
923 HX_ASSERT(m_pAlsaPCMHandle != NULL);
924 if (!m_pAlsaPCMHandle)
926 m_wLastError = RA_AOE_DEVNOTOPEN;
927 return m_wLastError;
930 state = snd_pcm_state(m_pAlsaPCMHandle);
931 if (state != SND_PCM_STATE_OPEN)
933 #ifdef HX_LOG_SUBSYSTEM
934 HXLOGL1 ( HXLOG_ADEV, "Device is not in open state in HSPAudioDevice::SetDeviceConfig (%d)", (int) state);
935 #endif
936 m_wLastError = RA_AOE_DEVNOTOPEN;
937 return m_wLastError;
940 /* Translate from HXAudioFormat to ALSA-friendly values */
941 snd_pcm_format_t fmt;
942 unsigned int sample_rate = 0;
943 unsigned int channels = 0;
944 unsigned int buffer_time = 500000; /* 0.5 seconds */
945 unsigned int period_time = buffer_time / 4; /* 4 interrupts per buffer */
947 switch (pFormat->uBitsPerSample)
949 case 8:
950 fmt = SND_PCM_FORMAT_S8;
951 break;
953 case 16:
954 fmt = SND_PCM_FORMAT_S16_LE;
955 break;
957 case 24:
958 fmt = SND_PCM_FORMAT_S24_LE;
959 break;
961 case 32:
962 fmt = SND_PCM_FORMAT_S32_LE;
963 break;
965 default:
966 fmt = SND_PCM_FORMAT_UNKNOWN;
967 break;
970 if (fmt == SND_PCM_FORMAT_UNKNOWN)
972 #ifdef HX_LOG_SUBSYSTEM
973 HXLOGL1 ( HXLOG_ADEV, "Unknown bits per sample: %d", pFormat->uBitsPerSample);
974 #endif
975 m_wLastError = RA_AOE_NOTENABLED;
976 return m_wLastError;
978 sample_rate = pFormat->ulSamplesPerSec;
979 channels = pFormat->uChannels;
981 /* Apply to ALSA */
982 int err = 0;
983 snd_pcm_hw_params_t *hwparams;
984 snd_pcm_sw_params_t *swparams;
986 snd_pcm_hw_params_alloca(&hwparams);
987 snd_pcm_sw_params_alloca(&swparams);
989 /* Hardware parameters */
990 err = snd_pcm_hw_params_any(m_pAlsaPCMHandle, hwparams);
991 if (err < 0)
993 #ifdef HX_LOG_SUBSYSTEM
994 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_any: %s", snd_strerror(err));
995 #endif
996 m_wLastError = RA_AOE_NOTENABLED;
999 if (err == 0)
1001 err = snd_pcm_hw_params_set_access(m_pAlsaPCMHandle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
1002 if (err < 0)
1004 #ifdef HX_LOG_SUBSYSTEM
1005 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_set_access: %s", snd_strerror(err));
1006 #endif
1007 m_wLastError = RA_AOE_NOTENABLED;
1011 if (err == 0)
1013 err = snd_pcm_hw_params_set_format(m_pAlsaPCMHandle, hwparams, fmt);
1014 if (err < 0)
1016 #ifdef HX_LOG_SUBSYSTEM
1017 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_set_format: %s", snd_strerror(err));
1018 #endif
1019 m_wLastError = RA_AOE_NOTENABLED;
1023 if (err == 0)
1025 err = snd_pcm_hw_params_set_channels(m_pAlsaPCMHandle, hwparams, channels);
1026 if (err < 0)
1028 #ifdef HX_LOG_SUBSYSTEM
1029 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_set_channels: %s", snd_strerror(err));
1030 #endif
1031 m_wLastError = RA_AOE_NOTENABLED;
1035 if (err == 0)
1037 unsigned int sample_rate_out;
1038 sample_rate_out = sample_rate;
1040 err = snd_pcm_hw_params_set_rate_near(m_pAlsaPCMHandle, hwparams, &sample_rate_out, 0);
1041 if (err < 0)
1043 #ifdef HX_LOG_SUBSYSTEM
1044 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_set_channels: %s", snd_strerror(err));
1045 #endif
1046 m_wLastError = RA_AOE_NOTENABLED;
1049 if (sample_rate_out != sample_rate)
1051 #ifdef HX_LOG_SUBSYSTEM
1052 HXLOGL2 ( HXLOG_ADEV, "Requested a sample rate of %d, got a rate of %d",
1053 sample_rate, sample_rate_out);
1054 #endif
1056 sample_rate = sample_rate_out;
1060 if (err == 0)
1062 unsigned int buffer_time_out;
1063 buffer_time_out = buffer_time;
1065 err = snd_pcm_hw_params_set_buffer_time_near(m_pAlsaPCMHandle, hwparams, &buffer_time_out, 0);
1066 if (err < 0)
1068 #ifdef HX_LOG_SUBSYSTEM
1069 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_set_buffer_time_near: %s",
1070 snd_strerror(err));
1071 #endif
1072 m_wLastError = RA_AOE_NOTENABLED;
1075 if (buffer_time_out != buffer_time)
1077 #ifdef HX_LOG_SUBSYSTEM
1078 HXLOGL2 ( HXLOG_ADEV, "Requested a buffering time of %d, got a time of %d",
1079 buffer_time, buffer_time_out);
1080 #endif
1082 buffer_time = buffer_time_out;
1086 if (err == 0)
1088 unsigned int period_time_out;
1089 period_time_out = period_time;
1091 err = snd_pcm_hw_params_set_period_time_near(m_pAlsaPCMHandle, hwparams, &period_time_out, 0);
1092 if (err < 0)
1094 #ifdef HX_LOG_SUBSYSTEM
1095 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_set_period_time_near: %s",
1096 snd_strerror(err));
1097 #endif
1098 m_wLastError = RA_AOE_NOTENABLED;
1101 if (period_time_out != period_time)
1103 #ifdef HX_LOG_SUBSYSTEM
1104 HXLOGL2 ( HXLOG_ADEV, "Requested a period time of %d, got a period of %d",
1105 period_time, period_time_out);
1106 #endif
1107 period_time = period_time_out;
1111 /* Apply parameters */
1112 err = snd_pcm_hw_params(m_pAlsaPCMHandle, hwparams);
1113 if (err < 0)
1115 #ifdef HX_LOG_SUBSYSTEM
1116 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params: %s",
1117 snd_strerror(err));
1118 #endif
1119 m_wLastError = RA_AOE_NOTENABLED;
1122 /* read buffer & period sizes */
1123 snd_pcm_uframes_t buffer_size = 0;
1124 snd_pcm_uframes_t period_size = 0;
1126 if (err == 0)
1128 err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
1129 if (err < 0)
1131 #ifdef HX_LOG_SUBSYSTEM
1132 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_get_buffer_size: %s",
1133 snd_strerror(err));
1134 #endif
1135 m_wLastError = RA_AOE_NOTENABLED;
1137 else
1139 HX_ASSERT (buffer_size > 0);
1143 if (err == 0)
1145 err = snd_pcm_hw_params_get_period_size(hwparams, &period_size, 0);
1146 if (err < 0)
1148 #ifdef HX_LOG_SUBSYSTEM
1149 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_get_period_size: %s",
1150 snd_strerror(err));
1151 #endif
1152 m_wLastError = RA_AOE_NOTENABLED;
1156 /* Get hardware pause */
1157 if (err == 0)
1159 int can_pause = 0;
1160 int can_resume = 0;
1162 can_pause = snd_pcm_hw_params_can_pause(hwparams);
1163 can_resume = snd_pcm_hw_params_can_resume(hwparams);
1165 // could we really have one without the other?
1166 m_bHasHardwarePauseAndResume = (can_pause && can_resume);
1167 m_Player->print2stderr("########## can_pause %d can_resume %d\n", can_pause, can_resume);
1170 /* Software parameters */
1171 if (err == 0)
1173 err = snd_pcm_sw_params_current(m_pAlsaPCMHandle, swparams);
1174 if (err < 0)
1176 #ifdef HX_LOG_SUBSYSTEM
1177 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_current: %s",
1178 snd_strerror(err));
1179 #endif
1180 m_wLastError = RA_AOE_NOTENABLED;
1184 snd_pcm_uframes_t start_threshold = ((buffer_size - 1) / period_size) * period_size;
1186 if (err == 0)
1188 err = snd_pcm_sw_params_set_start_threshold(m_pAlsaPCMHandle, swparams, start_threshold);
1189 if (err < 0)
1191 #ifdef HX_LOG_SUBSYSTEM
1192 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_start_threshold: %s",
1193 snd_strerror(err));
1194 #endif
1195 m_wLastError = RA_AOE_NOTENABLED;
1199 if (err == 0)
1201 err = snd_pcm_sw_params_set_avail_min(m_pAlsaPCMHandle, swparams, period_size);
1202 if (err < 0)
1204 #ifdef HX_LOG_SUBSYSTEM
1205 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_avail_min: %s",
1206 snd_strerror(err));
1207 #endif
1208 m_wLastError = RA_AOE_NOTENABLED;
1212 if (err == 0)
1214 err = snd_pcm_sw_params_set_xfer_align(m_pAlsaPCMHandle, swparams, 1);
1215 if (err < 0)
1217 #ifdef HX_LOG_SUBSYSTEM
1218 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_xfer_align: %s",
1219 snd_strerror(err));
1220 #endif
1221 m_wLastError = RA_AOE_NOTENABLED;
1225 if (err == 0)
1227 err = snd_pcm_sw_params_set_tstamp_mode(m_pAlsaPCMHandle, swparams, SND_PCM_TSTAMP_MMAP);
1228 if (err < 0)
1230 #ifdef HX_LOG_SUBSYSTEM
1231 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_xfer_align: %s",
1232 snd_strerror(err));
1233 #endif
1234 m_wLastError = RA_AOE_NOTENABLED;
1238 if (err == 0)
1240 err = snd_pcm_sw_params_set_stop_threshold(m_pAlsaPCMHandle, swparams, ~0U);
1241 if (err < 0)
1243 #ifdef HX_LOG_SUBSYSTEM
1244 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_stop_threshold: %s",
1245 snd_strerror(err));
1246 #endif
1247 m_wLastError = RA_AOE_NOTENABLED;
1251 if (err == 0)
1253 err = snd_pcm_sw_params(m_pAlsaPCMHandle, swparams);
1254 if (err < 0)
1256 #ifdef HX_LOG_SUBSYSTEM
1257 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params: %s",
1258 snd_strerror(err));
1259 #endif
1260 m_wLastError = RA_AOE_NOTENABLED;
1264 /* If all the calls to this point have succeeded, move to the PREPARE state.
1265 We will enter the RUNNING state when we've buffered enough for our start theshold. */
1266 if (err == 0)
1268 err = snd_pcm_prepare (m_pAlsaPCMHandle);
1269 if (err < 0)
1271 #ifdef HX_LOG_SUBSYSTEM
1272 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_prepare: %s",
1273 snd_strerror(err));
1274 #endif
1275 m_wLastError = RA_AOE_NOTENABLED;
1279 /* Sanity check: See if we're now in the PREPARE state */
1280 if (err == 0)
1282 snd_pcm_state_t state;
1283 state = snd_pcm_state (m_pAlsaPCMHandle);
1284 if (state != SND_PCM_STATE_PREPARED)
1286 #ifdef HX_LOG_SUBSYSTEM
1287 HXLOGL1 ( HXLOG_ADEV, "Expected to be in PREPARE state, actually in state %d",
1288 (int) state);
1289 #endif
1290 m_wLastError = RA_AOE_NOTENABLED;
1294 /* Use avail to get the alsa buffer size, which is distinct from the hardware buffer
1295 size. This will match what GetRoomOnDevice uses. */
1296 int alsa_buffer_size = 0;
1297 err = snd_pcm_avail_update(m_pAlsaPCMHandle);
1298 if(err < 0)
1300 #ifdef HX_LOG_SUBSYSTEM
1301 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_avail_update: %s", snd_strerror(err));
1302 #endif
1304 else
1306 alsa_buffer_size = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, err);
1307 err = 0;
1310 if (err == 0)
1312 m_wLastError = RA_AOE_NOERR;
1314 m_unSampleRate = sample_rate;
1315 m_unNumChannels = channels;
1316 m_wBlockSize = m_ulBytesPerGran;
1317 m_ulDeviceBufferSize = alsa_buffer_size;
1318 m_uSampFrameSize = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, 1) / channels;
1320 #ifdef HX_LOG_SUBSYSTEM
1321 HXLOGL2 ( HXLOG_ADEV, "Device Configured:\n");
1322 HXLOGL2 ( HXLOG_ADEV, " Sample Rate: %d", m_unSampleRate);
1323 HXLOGL2 ( HXLOG_ADEV, " Sample Width: %d", m_uSampFrameSize);
1324 HXLOGL2 ( HXLOG_ADEV, " Num channels: %d", m_unNumChannels);
1325 HXLOGL2 ( HXLOG_ADEV, " Block size: %d", m_wBlockSize);
1326 HXLOGL2 ( HXLOG_ADEV, " Device buffer size: %lu", m_ulDeviceBufferSize);
1327 HXLOGL2 ( HXLOG_ADEV, " Supports HW Pause: %d", m_bHasHardwarePauseAndResume);
1328 HXLOGL2 ( HXLOG_ADEV, " Start threshold: %d", start_threshold);
1329 #endif
1332 else
1334 m_unSampleRate = 0;
1335 m_unNumChannels = 0;
1337 if (m_pAlsaPCMHandle)
1339 _CloseAudio();
1343 return m_wLastError;
1346 //Device specific method to write bytes out to the audiodevice and return a
1347 //count of bytes written.
1348 HX_RESULT HSPAudioDevice::WriteBytes( UCHAR* buffer, ULONG32 ulBuffLength, LONG32& lCount )
1350 int err = 0, count = 0;
1351 unsigned int frames_written = 0;
1352 snd_pcm_sframes_t num_frames = 0;
1353 ULONG32 ulBytesToWrite = ulBuffLength;
1354 ULONG32 ulBytesWrote = 0;
1356 lCount = 0;
1358 HX_ASSERT(m_pAlsaPCMHandle);
1359 if (!m_pAlsaPCMHandle)
1361 m_wLastError = RA_AOE_DEVNOTOPEN;
1362 return m_wLastError;
1365 m_wLastError = RA_AOE_NOERR;
1367 if (ulBuffLength == 0)
1369 lCount = ulBuffLength;
1370 return m_wLastError;
1376 pthread_mutex_lock(&m_m);
1377 if (!m_closed)
1379 if (!m_SWPause)
1381 num_frames = snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, ulBytesToWrite);
1382 err = snd_pcm_writei( m_pAlsaPCMHandle, buffer, num_frames );
1384 else
1385 err = -EAGAIN;
1387 else
1389 pthread_mutex_unlock(&m_m);
1390 return 0;
1392 pthread_mutex_unlock(&m_m);
1393 count++;
1394 if (err >= 0)
1396 frames_written = err;
1398 pthread_mutex_lock(&m_m);
1399 if (!m_closed)
1400 ulBytesWrote = snd_pcm_frames_to_bytes (m_pAlsaPCMHandle, frames_written);
1401 pthread_mutex_unlock(&m_m);
1402 buffer += ulBytesWrote;
1403 ulBytesToWrite -= ulBytesWrote;
1404 lCount += ulBytesWrote;
1406 m_ulTotalWritten += ulBytesWrote;
1409 else
1411 switch (err)
1413 case -EAGAIN:
1414 usleep(10000);
1415 break;
1417 case -EPIPE:
1418 HandleXRun();
1419 lCount = (LONG32) ulBuffLength;
1420 break;
1422 case -ESTRPIPE:
1423 HandleSuspend();
1424 lCount = (LONG32) ulBuffLength;
1425 break;
1427 default:
1428 m_Player->print2stderr("########### snd_pcm_writei: %s num_frames=%ld\n", snd_strerror(err), num_frames);
1429 #ifdef HX_LOG_SUBSYSTEM
1430 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_writei: %s", snd_strerror(err));
1431 #endif
1432 m_wLastError = RA_AOE_DEVBUSY;
1435 } while (err == -EAGAIN || (err>0 && ulBytesToWrite>0));
1437 //m_Player->print2stderr("############## count = %d\n", count);
1439 return m_wLastError;
1442 /* Subtract the `struct timeval' values X and Y,
1443 storing the result in RESULT.
1444 Return 1 if the difference is negative, otherwise 0. */
1447 timeval_subtract (struct timeval *result,
1448 const struct timeval *x,
1449 const struct timeval *y_orig)
1451 struct timeval y = *y_orig;
1453 /* Perform the carry for the later subtraction by updating Y. */
1454 if (x->tv_usec < y.tv_usec)
1456 int nsec = (y.tv_usec - x->tv_usec) / 1000000 + 1;
1457 y.tv_usec -= 1000000 * nsec;
1458 y.tv_sec += nsec;
1460 if ((x->tv_usec - y.tv_usec) > 1000000)
1462 int nsec = (x->tv_usec - y.tv_usec) / 1000000;
1463 y.tv_usec += 1000000 * nsec;
1464 y.tv_sec -= nsec;
1467 /* Compute the time remaining to wait.
1468 `tv_usec' is certainly positive. */
1469 result->tv_sec = x->tv_sec - y.tv_sec;
1470 result->tv_usec = x->tv_usec - y.tv_usec;
1472 /* Return 1 if result is negative. */
1473 return x->tv_sec < y.tv_sec;
1476 HX_RESULT HSPAudioDevice::GetBytesActuallyPlayedUsingTStamps(UINT64 &nBytesPlayed) const
1478 HX_RESULT retVal = HXR_FAIL;
1480 int err = 0;
1482 snd_timestamp_t trigger_tstamp, now_tstamp, diff_tstamp;
1483 snd_pcm_status_t* status;
1485 snd_pcm_status_alloca(&status);
1487 err = snd_pcm_status(m_pAlsaPCMHandle, status);
1488 if (err < 0)
1490 #ifdef HX_LOG_SUBSYSTEM
1491 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_status: %s", snd_strerror(err));
1492 #endif
1495 if (err == 0)
1497 snd_pcm_status_get_tstamp(status, &now_tstamp);
1498 snd_pcm_status_get_trigger_tstamp(status, &trigger_tstamp);
1500 if(!m_bGotInitialTrigger && now_tstamp.tv_sec == 0 && now_tstamp.tv_usec == 0)
1502 /* Our first "now" timestamp appears to be invalid (or the user is very unlucky, and
1503 happened to start playback as the timestamp rolls over). Fall back to using
1504 snd_pcm_delay.
1506 XXXRGG: Is there a better way to figure out if the driver supports mmap'd
1507 timestamps? */
1509 m_bUseMMAPTStamps = false;
1511 else
1513 /* Timestamp seems to be valid */
1514 if(!m_bGotInitialTrigger)
1516 m_bGotInitialTrigger = true;
1517 memcpy(&m_tstampLastTrigger, &trigger_tstamp, sizeof(m_tstampLastTrigger));
1519 else
1521 if(memcmp(&m_tstampLastTrigger, &trigger_tstamp, sizeof(m_tstampLastTrigger)) != 0)
1523 /* There's been a trigger since last time -- restart the timestamp counter
1524 XXXRGG: What if there's been multiple triggers? */
1525 m_nBytesPlayedBeforeLastTrigger = m_nLastBytesPlayed;
1526 memcpy(&m_tstampLastTrigger, &trigger_tstamp, sizeof(m_tstampLastTrigger));
1528 #ifdef HX_LOG_SUBSYSTEM
1529 HXLOGL1 ( HXLOG_ADEV, "Retriggered...");
1530 #endif
1534 timeval_subtract (&diff_tstamp, &now_tstamp, &m_tstampLastTrigger);
1536 double fTimePlayed = (double) diff_tstamp.tv_sec +
1537 ((double) diff_tstamp.tv_usec / 1e6);
1539 nBytesPlayed = (UINT64) ((fTimePlayed * (double) m_unSampleRate * m_uSampFrameSize * m_unNumChannels) + m_nBytesPlayedBeforeLastTrigger);
1540 retVal = HXR_OK;
1544 return retVal;
1547 HX_RESULT HSPAudioDevice::GetBytesActuallyPlayedUsingDelay (UINT64 &nBytesPlayed) const
1549 HX_RESULT retVal = HXR_FAIL;
1550 int err = 0;
1551 snd_pcm_sframes_t frame_delay = 0;
1553 err = snd_pcm_delay (m_pAlsaPCMHandle, &frame_delay);
1554 if (err < 0)
1556 #ifdef HX_LOG_SUBSYSTEM
1557 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_status: %s", snd_strerror(err));
1558 #endif
1560 else
1562 int bytes_delay;
1563 bytes_delay = snd_pcm_frames_to_bytes (m_pAlsaPCMHandle, frame_delay);
1565 nBytesPlayed = m_ulTotalWritten - bytes_delay;
1566 retVal = HXR_OK;
1569 #ifdef HX_LOG_SUBSYSTEM
1570 // HXLOGL4 ( HXLOG_ADEV, "nBytesPlayed: %llu, m_ulTotalWritten: %llu\n", nBytesPlayed, m_ulTotalWritten);
1571 #endif
1573 return retVal;
1576 HX_RESULT HSPAudioDevice::GetBytesActuallyPlayedUsingAvail(UINT64 &nBytesPlayed) const
1578 /* Try this the hwsync way. This method seems to crash & burn with dmix,
1579 as avail seems to come from the device, and varies depending on what other
1580 dmix clients are writing to the slave device. Currently not used for that reason. */
1582 HX_RESULT retVal = HXR_FAIL;
1583 int err = 0;
1585 err = snd_pcm_hwsync(m_pAlsaPCMHandle);
1586 if(err < 0)
1588 #ifdef HX_LOG_SUBSYSTEM
1589 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hwsync: %s", snd_strerror(err));
1590 #endif
1593 err = snd_pcm_avail_update(m_pAlsaPCMHandle);
1594 if(err < 0)
1596 #ifdef HX_LOG_SUBSYSTEM
1597 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_avail_update: %s", snd_strerror(err));
1598 #endif
1600 else
1602 snd_pcm_sframes_t avail = err;
1603 int bytes_avail;
1604 bytes_avail = snd_pcm_frames_to_bytes (m_pAlsaPCMHandle, avail);
1606 nBytesPlayed = m_ulTotalWritten - (m_ulDeviceBufferSize - bytes_avail);
1607 retVal = HXR_OK;
1610 return retVal;
1613 HX_RESULT HSPAudioDevice::GetBytesActuallyPlayedUsingTimer(UINT64 &/*nBytesPlayed*/) const
1615 /* Look at the alsa timer api, and how we can lock onto it as a timer source. */
1617 return HXR_FAIL;
1620 UINT64 HSPAudioDevice::GetBytesActualyPlayed(void) const
1622 HX_ASSERT(m_pAlsaPCMHandle);
1623 if (!m_pAlsaPCMHandle)
1625 return 0;
1628 HX_RESULT retVal = HXR_OK;
1629 UINT64 nBytesPlayed = 0;
1630 snd_pcm_state_t state;
1632 for(;;)
1634 state = snd_pcm_state(m_pAlsaPCMHandle);
1635 switch(state)
1637 case SND_PCM_STATE_OPEN:
1638 case SND_PCM_STATE_SETUP:
1639 case SND_PCM_STATE_PREPARED:
1640 /* If we're in one of these states, written and played should match. */
1641 m_nLastBytesPlayed = m_ulTotalWritten;
1642 return m_nLastBytesPlayed;
1644 case SND_PCM_STATE_XRUN:
1645 HandleXRun();
1646 continue;
1648 case SND_PCM_STATE_RUNNING:
1649 break;
1651 case SND_PCM_STATE_PAUSED:
1652 // return m_nLastBytesPlayed;
1653 break;
1655 case SND_PCM_STATE_DRAINING:
1656 case SND_PCM_STATE_SUSPENDED:
1657 case SND_PCM_STATE_DISCONNECTED:
1658 HX_ASSERT(!"Not reached");
1659 break;
1662 break;
1665 // XXXRGG: Always use the delay method for now.
1666 m_bUseMMAPTStamps = false;
1668 if (m_bUseMMAPTStamps)
1670 retVal = GetBytesActuallyPlayedUsingTStamps(nBytesPlayed);
1673 if (!m_bUseMMAPTStamps || FAILED(retVal))
1675 /* MMAP'd timestamps are fishy. Try using snd_pcm_delay. */
1676 retVal = GetBytesActuallyPlayedUsingDelay(nBytesPlayed);
1679 m_nLastBytesPlayed = nBytesPlayed;
1680 return nBytesPlayed;
1684 //this must return the number of bytes that can be written without blocking.
1685 HX_RESULT HSPAudioDevice::GetRoomOnDevice(ULONG32& ulBytes) const
1687 ulBytes = 0;
1689 HX_ASSERT(m_pAlsaPCMHandle);
1690 if (!m_pAlsaPCMHandle)
1692 m_wLastError = RA_AOE_DEVNOTOPEN;
1693 return m_wLastError;
1696 int err = 0;
1697 err = snd_pcm_avail_update(m_pAlsaPCMHandle);
1698 if(err > 0)
1700 ulBytes = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, err);
1702 else
1704 switch (err)
1706 case -EAGAIN:
1707 break;
1709 case -EPIPE:
1710 HandleXRun();
1711 break;
1713 case -ESTRPIPE:
1714 HandleSuspend();
1715 break;
1717 default:
1718 #ifdef HX_LOG_SUBSYSTEM
1719 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_avail_update: %s", snd_strerror(err));
1720 #endif
1721 m_wLastError = RA_AOE_DEVBUSY;
1725 #ifdef HX_LOG_SUBSYSTEM
1726 // HXLOGL4 ( HXLOG_ADEV, "RoomOnDevice: %d", ulBytes);
1727 #endif
1729 return m_wLastError;
1733 //Device specific method to get/set the devices current volume.
1734 UINT16 HSPAudioDevice::_GetVolume() const
1736 HX_ASSERT(m_pAlsaMixerElem);
1737 if (!m_pAlsaMixerElem)
1739 return 0;
1742 UINT16 nRetVolume = 0;
1744 snd_mixer_elem_type_t type;
1745 int err = 0;
1746 type = snd_mixer_elem_get_type(m_pAlsaMixerElem);
1748 if (type == SND_MIXER_ELEM_SIMPLE)
1750 long volume, min_volume, max_volume;
1752 if(snd_mixer_selem_has_playback_volume(m_pAlsaMixerElem) ||
1753 snd_mixer_selem_has_playback_volume_joined(m_pAlsaMixerElem))
1755 err = snd_mixer_selem_get_playback_volume(m_pAlsaMixerElem,
1756 SND_MIXER_SCHN_MONO,
1757 &volume);
1758 if (err < 0)
1760 #ifdef HX_LOG_SUBSYSTEM
1761 HXLOGL1 ( HXLOG_ADEV, "snd_mixer_selem_get_playback_volume: %s",
1762 snd_strerror (err));
1763 #endif
1766 if (err == 0)
1768 snd_mixer_selem_get_playback_volume_range(m_pAlsaMixerElem,
1769 &min_volume,
1770 &max_volume);
1772 if(max_volume > min_volume)
1774 nRetVolume = (UINT16) (100 * volume / (max_volume - min_volume));
1780 return nRetVolume;
1784 HX_RESULT HSPAudioDevice::_SetVolume(UINT16 unVolume)
1786 m_wLastError = RA_AOE_NOERR;
1788 HX_ASSERT(m_pAlsaMixerElem);
1789 if (!m_pAlsaMixerElem)
1791 m_wLastError = RA_AOE_DEVNOTOPEN;
1792 return m_wLastError;
1795 snd_mixer_elem_type_t type;
1796 int err = 0;
1797 type = snd_mixer_elem_get_type(m_pAlsaMixerElem);
1799 if (type == SND_MIXER_ELEM_SIMPLE)
1801 long volume, min_volume, max_volume, range;
1803 if(snd_mixer_selem_has_playback_volume(m_pAlsaMixerElem) ||
1804 snd_mixer_selem_has_playback_volume_joined(m_pAlsaMixerElem))
1806 snd_mixer_selem_get_playback_volume_range(m_pAlsaMixerElem,
1807 &min_volume,
1808 &max_volume);
1810 range = max_volume - min_volume;
1811 volume = (long) ((unVolume / 100) * range + min_volume);
1813 err = snd_mixer_selem_set_playback_volume( m_pAlsaMixerElem,
1814 SND_MIXER_SCHN_FRONT_LEFT,
1815 volume);
1816 if (err < 0)
1818 #ifdef HX_LOG_SUBSYSTEM
1819 HXLOGL1 ( HXLOG_ADEV, "snd_mixer_selem_set_playback_volume: %s",
1820 snd_strerror (err));
1821 #endif
1822 m_wLastError = RA_AOE_GENERAL;
1825 if (!snd_mixer_selem_is_playback_mono (m_pAlsaMixerElem))
1827 /* Set the right channel too */
1828 err = snd_mixer_selem_set_playback_volume( m_pAlsaMixerElem,
1829 SND_MIXER_SCHN_FRONT_RIGHT,
1830 volume);
1831 if (err < 0)
1833 #ifdef HX_LOG_SUBSYSTEM
1834 HXLOGL1 ( HXLOG_ADEV, "snd_mixer_selem_set_playback_volume: %s",
1835 snd_strerror (err));
1836 #endif
1837 m_wLastError = RA_AOE_GENERAL;
1843 return m_wLastError;
1846 //Device specific method to drain a device. This should play the remaining
1847 //bytes in the devices buffer and then return.
1848 HX_RESULT HSPAudioDevice::_Drain()
1850 m_wLastError = RA_AOE_NOERR;
1852 HX_ASSERT(m_pAlsaPCMHandle);
1853 if (!m_pAlsaPCMHandle)
1855 m_wLastError = RA_AOE_DEVNOTOPEN;
1856 return m_wLastError;
1859 int err = 0;
1861 err = snd_pcm_drain(m_pAlsaPCMHandle);
1862 if (err < 0)
1864 #ifdef HX_LOG_SUBSYSTEM
1865 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_drain: %s",
1866 snd_strerror (err));
1867 #endif
1868 m_wLastError = RA_AOE_GENERAL;
1871 err = snd_pcm_prepare(m_pAlsaPCMHandle);
1872 if (err < 0)
1874 #ifdef HX_LOG_SUBSYSTEM
1875 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_prepare: %s",
1876 snd_strerror (err));
1877 #endif
1878 m_wLastError = RA_AOE_GENERAL;
1881 return m_wLastError;
1885 //Device specific method to reset device and return it to a state that it
1886 //can accept new sample rates, num channels, etc.
1887 HX_RESULT HSPAudioDevice::_Reset()
1889 if (!m_pAlsaPCMHandle)
1891 m_wLastError = RA_AOE_DEVNOTOPEN;
1892 return m_wLastError;
1895 m_wLastError = RA_AOE_NOERR;
1897 m_nLastBytesPlayed = 0;
1899 int err = 0;
1901 err = snd_pcm_drop(m_pAlsaPCMHandle);
1902 if (err < 0)
1904 #ifdef HX_LOG_SUBSYSTEM
1905 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_drop: %s",
1906 snd_strerror (err));
1907 #endif
1908 m_wLastError = RA_AOE_GENERAL;
1911 err = snd_pcm_prepare(m_pAlsaPCMHandle);
1912 if (err < 0)
1914 #ifdef HX_LOG_SUBSYSTEM
1915 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_prepare: %s",
1916 snd_strerror (err));
1917 #endif
1918 m_wLastError = RA_AOE_GENERAL;
1921 return m_wLastError;
1924 HX_RESULT HSPAudioDevice::_CheckFormat( const HXAudioFormat* pFormat )
1926 HX_ASSERT(m_pAlsaPCMHandle == NULL);
1928 m_wLastError = _OpenAudio();
1929 if(m_wLastError != RA_AOE_NOERR)
1931 return m_wLastError;
1934 m_wLastError = RA_AOE_NOERR;
1936 snd_pcm_format_t fmt;
1937 unsigned int sample_rate = 0;
1938 unsigned int channels = 0;
1940 switch (pFormat->uBitsPerSample)
1942 case 8:
1943 fmt = SND_PCM_FORMAT_S8;
1944 break;
1946 case 16:
1947 fmt = SND_PCM_FORMAT_S16_LE;
1948 break;
1950 case 24:
1951 fmt = SND_PCM_FORMAT_S24_LE;
1952 break;
1954 case 32:
1955 fmt = SND_PCM_FORMAT_S32_LE;
1956 break;
1958 default:
1959 fmt = SND_PCM_FORMAT_UNKNOWN;
1960 break;
1963 if (fmt == SND_PCM_FORMAT_UNKNOWN)
1965 #ifdef HX_LOG_SUBSYSTEM
1966 HXLOGL1 ( HXLOG_ADEV, "Unknown bits per sample: %d", pFormat->uBitsPerSample);
1967 #endif
1968 m_wLastError = RA_AOE_NOTENABLED;
1969 return m_wLastError;
1971 sample_rate = pFormat->ulSamplesPerSec;
1972 channels = pFormat->uChannels;
1974 /* Apply to ALSA */
1975 int err = 0;
1976 snd_pcm_hw_params_t *hwparams;
1978 snd_pcm_hw_params_alloca(&hwparams);
1980 err = snd_pcm_hw_params_any(m_pAlsaPCMHandle, hwparams);
1981 if (err < 0)
1983 #ifdef HX_LOG_SUBSYSTEM
1984 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_any: %s", snd_strerror(err));
1985 #endif
1986 m_wLastError = RA_AOE_NOTENABLED;
1989 if (err == 0)
1991 err = snd_pcm_hw_params_test_rate (m_pAlsaPCMHandle, hwparams, sample_rate, 0);
1992 if (err < 0)
1994 m_wLastError = RA_AOE_BADFORMAT;
1998 if (err == 0)
2000 err = snd_pcm_hw_params_test_channels (m_pAlsaPCMHandle, hwparams, channels);
2001 if (err < 0)
2003 m_wLastError = RA_AOE_BADFORMAT;
2007 if (err == 0)
2009 err = snd_pcm_hw_params_test_format (m_pAlsaPCMHandle, hwparams, fmt);
2010 if (err < 0)
2012 m_wLastError = RA_AOE_BADFORMAT;
2016 _CloseAudio();
2018 return m_wLastError;
2022 HX_RESULT HSPAudioDevice::CheckSampleRate( ULONG32 ulSampleRate )
2024 HX_ASSERT(m_pAlsaPCMHandle == NULL);
2025 bool shouldclose = false;
2027 if (!m_pAlsaPCMHandle)
2029 m_wLastError = _OpenAudio();
2030 if(m_wLastError != RA_AOE_NOERR)
2032 return m_wLastError;
2034 shouldclose = true;
2037 int err = 0;
2038 snd_pcm_hw_params_t *hwparams;
2040 snd_pcm_hw_params_alloca(&hwparams);
2042 m_wLastError = RA_AOE_NOERR;
2044 err = snd_pcm_hw_params_any(m_pAlsaPCMHandle, hwparams);
2045 if (err < 0)
2047 #ifdef HX_LOG_SUBSYSTEM
2048 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_any: %s", snd_strerror(err));
2049 #endif
2050 m_wLastError = RA_AOE_NOTENABLED;
2053 if (err == 0)
2055 err = snd_pcm_hw_params_test_rate (m_pAlsaPCMHandle, hwparams, ulSampleRate, 0);
2056 if (err < 0)
2058 m_wLastError = RA_AOE_BADFORMAT;
2062 if (shouldclose)
2063 _CloseAudio();
2065 return m_wLastError;
2069 HX_RESULT HSPAudioDevice::_Pause()
2071 HX_ASSERT(m_pAlsaPCMHandle);
2072 if (!m_pAlsaPCMHandle)
2074 m_wLastError = RA_AOE_DEVNOTOPEN;
2075 return m_wLastError;
2078 if (m_bHasHardwarePauseAndResume)
2080 snd_pcm_state_t state;
2082 state = snd_pcm_state(m_pAlsaPCMHandle);
2083 if (state == SND_PCM_STATE_RUNNING)
2085 int err = 0;
2086 err = snd_pcm_pause(m_pAlsaPCMHandle, 1);
2087 if (err < 0)
2089 #ifdef HX_LOG_SUBSYSTEM
2090 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_pause: %s",
2091 snd_strerror (err));
2092 #endif
2094 m_wLastError = RA_AOE_NOTSUPPORTED;
2098 else
2100 pthread_mutex_lock(&m_m);
2101 m_SWPause = true;
2102 _Drain();
2103 _Reset();
2104 pthread_mutex_unlock(&m_m);
2107 return m_wLastError;
2110 HX_RESULT HSPAudioDevice::_Resume()
2112 HX_ASSERT(m_pAlsaPCMHandle);
2113 if (!m_pAlsaPCMHandle)
2115 m_wLastError = RA_AOE_DEVNOTOPEN;
2116 return m_wLastError;
2119 if (m_bHasHardwarePauseAndResume)
2121 snd_pcm_state_t state;
2123 state = snd_pcm_state(m_pAlsaPCMHandle);
2124 if (state == SND_PCM_STATE_PAUSED)
2126 int err = 0;
2127 err = snd_pcm_pause(m_pAlsaPCMHandle, 0);
2129 if (err < 0)
2131 #ifdef HX_LOG_SUBSYSTEM
2132 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_pause: %s",
2133 snd_strerror (err));
2134 #endif
2136 m_wLastError = RA_AOE_NOTSUPPORTED;
2140 else
2142 pthread_mutex_lock(&m_m);
2143 m_SWPause = false;
2144 _Reset();
2145 pthread_mutex_unlock(&m_m);
2148 return m_wLastError;
2151 BOOL HSPAudioDevice::HardwarePauseSupported() const
2153 HX_ASSERT(m_pAlsaPCMHandle != NULL);
2155 return m_bHasHardwarePauseAndResume;
2159 void HSPAudioDevice::HandleXRun(void) const
2161 int err = 0;
2163 #ifdef HX_LOG_SUBSYSTEM
2164 HXLOGL2 ( HXLOG_ADEV, "Handling XRun");
2165 #endif
2167 err = snd_pcm_prepare(m_pAlsaPCMHandle);
2168 if (err < 0)
2170 #ifdef HX_LOG_SUBSYSTEM
2171 HXLOGL1 ( HXLOG_ADEV, "snd_pcm_resume: %s (xrun)",
2172 snd_strerror (err));
2173 #endif
2176 /* Catch up to the write position of the audio device so we get new data.
2177 XXXRGG: Is there some way we, the device, can force a rewind? */
2178 m_nLastBytesPlayed = m_ulTotalWritten;
2181 void HSPAudioDevice::HandleSuspend(void) const
2183 int err = 0;
2187 err = snd_pcm_resume(m_pAlsaPCMHandle);
2188 if (err == 0)
2190 break;
2192 else if (err == -EAGAIN)
2194 usleep(1000);
2196 } while (err == -EAGAIN);
2198 if (err < 0)
2200 HandleXRun();
2204 #endif // HELIX_USE_ALSA