00001 #include <math.h>
00002 #include "MercurySoundDriverALSA.h"
00003 #include "MercuryLog.h"
00004
00005 MercurySoundDriverALSA::MercurySoundDriverALSA()
00006 :MercurySoundDriver(), m_pcmHandle(NULL)
00007 {
00008 }
00009
00010 MercurySoundDriverALSA::~MercurySoundDriverALSA()
00011 {
00012 }
00013
00014 void MercurySoundDriverALSA::ALSACallback(snd_async_handler_t *pcm_callback)
00015 {
00016 MercurySoundDriverALSA* sd = (MercurySoundDriverALSA*)snd_async_handler_get_callback_private(pcm_callback);
00017 sd->WriteSoundFrames();
00018 #if defined( NO_THREADS )
00019 sd->ProcessPlayingSounds();
00020 #endif
00021 }
00022
00023 void MercurySoundDriverALSA::Init()
00024 {
00025 MercurySoundDriver::Init();
00026 m_name = "default";
00027
00028 int err = snd_pcm_open(&m_pcmHandle, m_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
00029 if (err < 0)
00030 printf("can not open audio device %s (%s)\n", m_name.c_str(), snd_strerror(err) );
00031
00032 SetupHWParams();
00033 SetupSWParams();
00034
00035 m_buffer = new int16_t[m_bufferSize*m_channels];
00036 memset(m_buffer, 0, m_bufferSize*m_channels*sizeof(int16_t));
00037 m_buffer2 = new int16_t[m_bufferSize*m_channels];
00038 memset(m_buffer2, 0, m_bufferSize*m_channels*sizeof(int16_t));
00039
00040 snd_pcm_prepare (m_pcmHandle);
00041 Start();
00042
00043 ProcessPlayingSounds();
00044
00045 snd_async_handler_t *pcm_callback;
00046 snd_async_add_pcm_handler(&pcm_callback, m_pcmHandle, MercurySoundDriverALSA::ALSACallback, this);
00047 void *private_data = snd_async_handler_get_callback_private(pcm_callback);
00048 }
00049
00050 void MercurySoundDriverALSA::WriteSoundFrames()
00051 {
00052 m_mutex.Wait();
00053
00054 snd_pcm_sframes_t avail( snd_pcm_avail_update(m_pcmHandle) );
00055 while ( (avail >= m_writeSize) && (m_sentFrames < m_bufferSize) )
00056 {
00057 unsigned long numFrames = avail<(m_bufferSize-m_sentFrames)?avail:(m_bufferSize-m_sentFrames);
00058 unsigned long byteOffset = m_sentFrames*m_channels;
00059 int wrote = snd_pcm_writei(m_pcmHandle, (*m_front)+byteOffset, numFrames);
00060
00061
00062 if (wrote < 0)
00063 {
00064 Recover(wrote);
00065
00066 Start();
00067 m_mutex.UnLock();
00068 return;
00069 }
00070
00071 m_sentFrames += wrote;
00072 avail = snd_pcm_avail_update(m_pcmHandle);
00073 }
00074
00075 if (m_sentFrames >= m_bufferSize)
00076 {
00077 m_sentFrames = 0;
00078 m_needData = true;
00079 }
00080
00081 m_mutex.UnLock();
00082 }
00083
00084 void MercurySoundDriverALSA::Recover(int error)
00085 {
00086 if( error == -EPIPE )
00087 {
00088 LOG.Warn("ALSA buffer underrun");
00089 snd_pcm_prepare(m_pcmHandle);
00090 }
00091 if( error == -ESTRPIPE )
00092 {
00093 LOG.Warn("ALSA suspend");
00094 while (snd_pcm_resume(m_pcmHandle) == -EAGAIN)
00095 usleep(10000);
00096 }
00097 }
00098
00099 void MercurySoundDriverALSA::SetupHWParams()
00100 {
00101 snd_pcm_hw_params_t *hw_params;
00102 snd_pcm_hw_params_malloc(&hw_params);
00103
00104 snd_pcm_hw_params_any(m_pcmHandle, hw_params);
00105
00106 snd_pcm_hw_params_set_access(m_pcmHandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
00107 snd_pcm_hw_params_set_format(m_pcmHandle, hw_params, SND_PCM_FORMAT_S16_LE);
00108 snd_pcm_hw_params_set_rate_near(m_pcmHandle, hw_params, &m_rate, NULL);
00109 snd_pcm_hw_params_set_channels(m_pcmHandle, hw_params, m_channels);
00110 snd_pcm_hw_params_set_buffer_size_near(m_pcmHandle, hw_params, &m_bufferSize);
00111 snd_pcm_hw_params_set_period_size_near(m_pcmHandle, hw_params, &m_writeSize, NULL);
00112
00113 snd_pcm_hw_params(m_pcmHandle, hw_params);
00114 snd_pcm_hw_params_free(hw_params);
00115 }
00116
00117 void MercurySoundDriverALSA::SetupSWParams()
00118 {
00119 snd_pcm_sw_params_t *sw_params;
00120
00121 snd_pcm_sw_params_malloc(&sw_params);
00122 snd_pcm_sw_params_current(m_pcmHandle, sw_params);
00123 snd_pcm_sw_params_set_start_threshold(m_pcmHandle, sw_params, m_bufferSize - m_writeSize);
00124 snd_pcm_sw_params_set_avail_min(m_pcmHandle, sw_params, m_writeSize);
00125 snd_pcm_sw_params(m_pcmHandle, sw_params);
00126 snd_pcm_sw_params_free (sw_params);
00127 }
00128
00129 void MercurySoundDriverALSA::Cleanup()
00130 {
00131 snd_pcm_close(m_pcmHandle);
00132 }
00133
00134 void MercurySoundDriverALSA::Start()
00135 {
00136 int wrote = snd_pcm_writei (m_pcmHandle, *m_front, m_bufferSize);
00137 snd_pcm_start(m_pcmHandle);
00138 }
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166