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

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Fri Mar 16 05:30:35 2012 by Doxygen 1.6.3