00001 #include "SoundPlayThread.h"
00002 #include "Shared/Config.h"
00003 #include "Sound/SoundManager.h"
00004
00005 #ifdef __linux__
00006 #include <alsa/asoundlib.h>
00007 #endif
00008
00009
00010 #ifdef __APPLE__
00011
00012
00013
00014 #include <AudioToolbox/AudioToolbox.h>
00015 #include <AudioUnit/AudioUnitProperties.h>
00016 #include <Carbon/Carbon.h>
00017
00018 struct SoundPlayContext {
00019 SoundPlayContext() : outputStarted(false), output(), streamDesc() {}
00020 bool outputStarted;
00021 AudioUnit output;
00022 AudioStreamBasicDescription streamDesc;
00023 };
00024
00025 void SoundPlayThread::reset() {
00026 if(!initSuccess)
00027 return;
00028 if(sndman->getNumPlaying()<=0 && context->outputStarted) {
00029 context->outputStarted=false;
00030 if(noErr != AudioOutputUnitStop(context->output)) {
00031 std::cerr << "WARNING: had error while stopping audio unit" << std::endl;
00032 }
00033 } else if(sndman->getNumPlaying()>0 && !context->outputStarted) {
00034 if(noErr != AudioOutputUnitStart(context->output)) {
00035 std::cerr << "ERROR: Could not start audio output" << std::endl;
00036 closeSystem();
00037 return;
00038 }
00039 context->outputStarted=true;
00040 }
00041 }
00042
00043 static OSStatus audioCallback(void *inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) {
00044 SoundPlayContext* context = reinterpret_cast<SoundPlayContext*>(inRefCon);
00045
00046
00047 for(typeof(ioData->mNumberBuffers) b=0; b<ioData->mNumberBuffers; ++b) {
00048 AudioBuffer& bufInfo = ioData->mBuffers[b];
00049
00050
00051 sndman->CopyTo(bufInfo.mData,bufInfo.mDataByteSize);
00052 }
00053
00054 if(sndman->getNumPlaying()<=0 && context->outputStarted) {
00055 context->outputStarted=false;
00056 if(noErr != AudioOutputUnitStop(context->output)) {
00057 std::cerr << "WARNING: had error while stopping audio unit" << std::endl;
00058 }
00059 }
00060
00061
00062 return noErr;
00063 }
00064
00065 void SoundPlayThread::openSystem() {
00066 if(context==NULL)
00067 context = new SoundPlayContext;
00068
00069 ComponentDescription cd;
00070 cd.componentType = kAudioUnitType_Output;
00071 cd.componentSubType = kAudioUnitSubType_DefaultOutput;
00072 cd.componentManufacturer = 0;
00073 cd.componentFlags = 0;
00074 cd.componentFlagsMask = 0;
00075
00076 Component comp = FindNextComponent(NULL, &cd);
00077 if(comp == NULL) {
00078 std::cerr << "ERROR: Could not find default audio output" << std::endl;
00079 return;
00080 }
00081
00082 AudioUnit& output = context->output;
00083 if(noErr != OpenAComponent(comp, &output)) {
00084 std::cerr << "ERROR: Could not open audio output component" << std::endl;
00085 return;
00086 }
00087
00088 if(noErr != AudioUnitInitialize(output)) {
00089 std::cerr << "ERROR: Could not initialize audio output" << std::endl;
00090 CloseComponent(output);
00091 return;
00092 }
00093
00094 AURenderCallbackStruct callbackData;
00095 callbackData.inputProc = audioCallback;
00096 callbackData.inputProcRefCon = context;
00097
00098 if(noErr != AudioUnitSetProperty(output, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackData, sizeof(callbackData))) {
00099 std::cerr << "ERROR: Could not set audio callback" << std::endl;
00100 closeSystem();
00101 return;
00102 }
00103
00104 AudioStreamBasicDescription& streamDesc = context->streamDesc;
00105 streamDesc.mFormatID = kAudioFormatLinearPCM;
00106 streamDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked ;
00107 streamDesc.mSampleRate = config->sound.sample_rate;
00108 streamDesc.mBitsPerChannel = config->sound.sample_bits;
00109 streamDesc.mChannelsPerFrame = 1;
00110 streamDesc.mBytesPerFrame = streamDesc.mChannelsPerFrame * streamDesc.mBitsPerChannel/8;
00111 streamDesc.mFramesPerPacket = 1;
00112 streamDesc.mBytesPerPacket = streamDesc.mFramesPerPacket * streamDesc.mBytesPerFrame;
00113
00114 if(noErr != AudioUnitSetProperty(output, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamDesc, sizeof(streamDesc))) {
00115 std::cerr << "ERROR: Could not set audio format" << std::endl;
00116 closeSystem();
00117 return;
00118 }
00119
00120 UInt32 streamDescSize = sizeof(streamDesc);
00121 if(noErr != AudioUnitGetProperty( output, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamDesc, &streamDescSize)) {
00122 std::cerr << "ERROR: Could not verify audio format" << std::endl;
00123 closeSystem();
00124 return;
00125 }
00126
00127 initSuccess=true;
00128 }
00129
00130 void SoundPlayThread::closeSystem() {
00131 initSuccess=false;
00132 if(context==NULL)
00133 return;
00134
00135 if(context->outputStarted) {
00136 if(noErr != AudioOutputUnitStop(context->output)) {
00137 std::cerr << "WARNING: had error while stopping audio unit" << std::endl;
00138 }
00139 }
00140
00141 if(noErr != AudioUnitUninitialize(context->output)) {
00142 std::cerr << "WARNING: had error while closing audio unit" << std::endl;
00143 }
00144
00145 if(noErr != CloseComponent(context->output)) {
00146 std::cerr << "WARNING: had error while closing audio component" << std::endl;
00147 }
00148
00149 delete context; context=NULL;
00150 }
00151
00152
00153
00154 #else // linux or "other"
00155
00156
00157 void SoundPlayThread::reset() {
00158
00159
00160 if(!initSuccess)
00161 return;
00162
00163 MarkScope autolock(lock);
00164
00165 if(sndman->getNumPlaying()<=0 && poller.isStarted()) {
00166 poller.stop();
00167
00168 if (snd_pcm_drain(pcm_handle) < 0) {
00169 std::cerr << "SOUND: Error stopping PCM device." << std::endl;
00170 }
00171 }
00172 else if(sndman->getNumPlaying()>0 && !poller.isStarted()) {
00173
00174
00175 if (snd_pcm_prepare(pcm_handle) < 0) {
00176 std::cerr << "SOUND: Error preparing PCM device." << std::endl;
00177 }
00178 poller.start();
00179 }
00180 }
00181
00182 void SoundPlayThread::openSystem()
00183 {
00184
00185 MarkScope autolock(lock);
00186
00187 unsigned int rate = config->sound.sample_rate;
00188 unsigned int exact_rate;
00189 int dir;
00190
00191
00192 int frame_bits;
00193
00194 unsigned int period_time = 200000;
00195 unsigned int buffer_time = 1000000;
00196
00197 snd_pcm_format_t format;
00198 if (config->sound.sample_bits == 8)
00199 format = SND_PCM_FORMAT_S8;
00200 else
00201 format = SND_PCM_FORMAT_S16_LE;
00202
00203 snd_pcm_hw_params_t *hwparams=NULL;
00204 snd_pcm_hw_params_alloca(&hwparams);
00205
00206 if (snd_pcm_open(&pcm_handle, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, 0) < 0) {
00207 if (snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) {
00208 std::cerr << "SOUND: Error opening PCM device 'plughw:0,0' and alternate 'default'" << std::endl;
00209 goto pcm_close;
00210 }
00211 }
00212
00213
00214 if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
00215 std::cerr << "SOUND: Can not configure this PCM device." << std::endl;
00216 }
00217
00218
00219 if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
00220 std::cerr << "SOUND: Error setting access." << std::endl;
00221 goto pcm_close;
00222 }
00223
00224 if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, format) < 0) {
00225 std::cerr << "SOUND: Error setting format." << std::endl;
00226 goto pcm_close;
00227 }
00228
00229
00230
00231 exact_rate = rate;
00232 if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0) < 0) {
00233 std::cerr << "SOUND: Error setting rate." << std::endl;
00234 goto pcm_close;
00235 }
00236 if (rate != exact_rate) {
00237 std::cerr << "SOUND: The rate " << rate << " Hz is not supported by your hardware." << std::endl
00238 << " ==> Using " << exact_rate << " Hz instead." << std::endl;
00239 }
00240
00241
00242
00243
00244 if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 1) < 0) {
00245 std::cerr << "SOUND: Error setting channels." << std::endl;
00246 goto pcm_close;
00247 }
00248
00249
00250 if (snd_pcm_hw_params_set_period_time_near(pcm_handle, hwparams, &period_time, &dir) < 0) {
00251 std::cerr << "SOUND: Error setting period time." << std::endl;
00252 goto pcm_close;
00253 }
00254
00255 if (snd_pcm_hw_params_get_period_size(hwparams, &period_size, &dir) < 0) {
00256 std::cerr << "SOUND: Error getting period size." << std::endl;
00257 goto pcm_close;
00258 }
00259
00260
00261
00262 if (snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams, &buffer_time, &dir) < 0) {
00263 std::cerr << "SOUND: Error getting buffer time." << std::endl;
00264 goto pcm_close;
00265 }
00266
00267 if (snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size) < 0) {
00268 std::cerr << "SOUND: Error getting buffer size." << std::endl;
00269 goto pcm_close;
00270 }
00271
00272
00273
00274
00275 if ((frame_bits = snd_pcm_format_physical_width(format)) < 0) {
00276 std::cerr << "SOUND: Error getting frame size." << std::endl;
00277 goto pcm_close;
00278 }
00279
00280 frame_size = frame_bits / 8;
00281
00282
00283
00284
00285 if (snd_pcm_hw_params(pcm_handle, hwparams) < 0) {
00286 std::cerr << "SOUND: Error setting HW params." << std::endl;
00287 goto pcm_close;
00288 }
00289
00290 initSuccess=true;
00291 return;
00292
00293 pcm_close:
00294 if (pcm_handle) {
00295
00296 if (snd_pcm_close(pcm_handle) < 0) {
00297 std::cerr << "SOUND: Error closing PCM device." << std::endl;
00298 }
00299 pcm_handle = NULL;
00300 }
00301
00302 return;
00303 }
00304
00305 void SoundPlayThread::closeSystem() {
00306 initSuccess=false;
00307 if(poller.isStarted())
00308 poller.stop().join();
00309 delete buf; buf=NULL;
00310
00311 if (pcm_handle) {
00312 MarkScope autolock(lock);
00313
00314
00315 if (snd_pcm_drop(pcm_handle) < 0) {
00316 std::cerr << "SOUND: Error stopping PCM device." << std::endl;
00317 }
00318
00319 if (snd_pcm_close(pcm_handle) < 0) {
00320 std::cerr << "SOUND: Error closing PCM device." << std::endl;
00321 }
00322 pcm_handle = NULL;
00323 }
00324
00325
00326 }
00327
00328 bool SoundPlayThread::poll() {
00329 if (!buf) {
00330 buf = new char[period_size];
00331 }
00332
00333 int left = period_size / frame_size;
00334 char *ptr = buf;
00335
00336 sndman->CopyTo(buf, period_size);
00337
00338 MarkScope autolock(lock);
00339
00340 while (left > 0) {
00341 int wrote = snd_pcm_writei(pcm_handle, ptr, left);
00342
00343
00344 if (wrote < 0) {
00345 if (wrote == -EPIPE) {
00346
00347 if (snd_pcm_prepare(pcm_handle) < 0) {
00348 std::cerr << "SOUND: Error preparing PCM device." << std::endl;
00349 return false;
00350 }
00351 return true;
00352 }
00353 }
00354
00355 ptr += wrote * frame_size;
00356 left -= wrote;
00357 }
00358
00359
00360 return sndman->getNumPlaying()>0;
00361 }
00362
00363
00364 #endif
00365
00366
00367
00368
00369
00370
00371