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