Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

SoundPlayThread.cc

Go to the documentation of this file.
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   //std::cout << sndman->getNumPlaying() << ' ' << context->outputStarted << ' ' << ' ' << inNumberFrames << ' ' << ioData->mNumberBuffers;
00045   
00046   for(typeof(ioData->mNumberBuffers) b=0; b<ioData->mNumberBuffers; ++b) {
00047     AudioBuffer& bufInfo = ioData->mBuffers[b];
00048     //std::cout << ' ' << bufInfo.mNumberChannels << ' ' << bufInfo.mDataByteSize;
00049     // beware bufInfo.mNumberChannels!=1...
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   //std::cout << '\n';
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 /* | kLinearPCMFormatFlagIsBigEndian*/;
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   //std::cout << "SOUND: reset" << std::endl;
00158 
00159   if(!initSuccess)
00160     return;
00161 
00162   MarkScope autolock(lock);
00163 
00164   if(sndman->getNumPlaying()<=0 && poller.isStarted()) {
00165     poller.stop();
00166     //std::cout << "SOUND: draining" << std::endl;
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     //buffersInFlight=0;
00173     //std::cout << "SOUND: preparing" << std::endl;
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   //std::cout << "SOUND: open" << std::endl;
00184   MarkScope autolock(lock);
00185 
00186   unsigned int rate = config->sound.sample_rate;
00187   unsigned int exact_rate;
00188   int dir;          /* exact_rate == rate --> dir = 0 */
00189   /* exact_rate < rate  --> dir = -1 */
00190   /* exact_rate > rate  --> dir = 1 */
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; // apparently this does not need a 'free'
00203   snd_pcm_hw_params_alloca(&hwparams); // (crashes if we try to 'free' afterward)
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   /* Init hwparams with full configuration space */
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   // setup basic access
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   /* Set sample rate. If the exact rate is not supported */
00229   /* by the hardware, use nearest possible rate.         */ 
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   //printf("rate is %d.\n",exact_rate);
00241 
00242   /* Set number of channels */
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   // setup period and buffer size
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   //std::cout << "SOUND: Period size is " << period_size << std::endl;
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   //std::cout << "SOUND: Buffer size is " << buffer_size << std::endl;
00272 
00273   /* get the frame size */
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   //std::cout << "SOUND: Frame size is " << frame_size << ", " << frame_bits << " bits." << std::endl;
00281 
00282   /* Apply HW parameter settings to */
00283   /* PCM device and prepare device  */
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     //std::cout << "SOUND: closing" << std::endl;
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     //std::cout << "SOUND: dropping" << std::endl;
00314     if (snd_pcm_drop(pcm_handle) < 0) {
00315       std::cerr << "SOUND: Error stopping PCM device." << std::endl;
00316     }
00317     //std::cout << "SOUND: closing" << std::endl;
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   //std::cout << "SOUND: close" << std::endl;
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     //std::cout << "SOUND: poll write with ptr " << (int)ptr << " and " << left << " bytes left wrote " << wrote << std::endl;
00342 
00343     if (wrote < 0) {
00344       if (wrote == -EPIPE) {    /* under-run */
00345         //std::cout << "SOUND: Buffer underrun." << std::endl;
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   //std::cout << "SoundPlayThread polled " << 0 << ' ' << sndman->getNumPlaying() << std::endl;
00359   return sndman->getNumPlaying()>0;
00360 }
00361 
00362 /* ******************************************************************/
00363 #endif
00364 /* ******************************************************************/
00365 
00366 
00367 /*! @file
00368  * @brief 
00369  * @author Ethan Tira-Thompson (ejt) (Creator)
00370  */

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Mon May 9 05:01:39 2016 by Doxygen 1.6.3