Main Page   Modules   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

/root/src/subsynth/syn/Stream/OpenALAudioOStream.cpp

Go to the documentation of this file.
00001 
00002 /****************** <SYN heading BEGIN do not edit this line> *****************
00003  *
00004  * subsynth - modular audio synthesizer
00005  * subsynth is (C) Copyright 2001-2002 by Kevin Meinert
00006  *
00007  * Original Author: Kevin Meinert
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Library General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2 of the License, or (at your option) any later version.
00013  *
00014  * This library is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Library General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Library General Public
00020  * License along with this library; if not, write to the
00021  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00022  * Boston, MA 02111-1307, USA.
00023  *
00024  * -----------------------------------------------------------------
00025  * File:          $RCSfile: OpenALAudioOStream.cpp,v $
00026  * Date modified: $Date: 2002/04/05 16:28:28 $
00027  * Version:       $Revision: 1.9 $
00028  * -----------------------------------------------------------------
00029  *
00030  ****************** <SYN heading END do not edit this line> ******************/
00031 
00032 #ifdef _WIN32
00033    #include <windows.h>
00034 #endif
00035 
00036 #include <iostream>
00037 #include <vector>
00038 #include <string>
00039 
00040 #include <AL/al.h>
00041 #include <AL/alc.h>
00042 #include <AL/alut.h>
00043 
00044 //#include <vpr/Thread/Thread.h> // for yield 
00045 
00046 #include "syn/Util/AudioFormat.h"
00047 #include "syn/Stream/AudioContext.h"
00048 #include "syn/Stream/AudioOStream.h"
00049 #include "syn/Stream/OpenALAudioOStream.h" // my header goes last...
00050 
00051 namespace syn
00052 {
00053    OpenALAudioOStream::OpenALAudioOStream() : 
00054       mFormat( AL_FORMAT_STEREO16 ),
00055       mIsOpen( false ), 
00056       mBufferHackSwitch( 0 ),
00057       mPcount( 0 )
00058    {
00059       // hard coded to a double buffered source.
00060       mBuffers.resize( 2 ); // double buffered
00061       mSources.resize( 1 ); // one source
00062    }
00063 
00064    bool OpenALAudioOStream::isPlaying() const
00065    {
00066       
00067       if (alIsSource(mSources[0]) == AL_FALSE) 
00068       {
00069          return AL_FALSE;
00070       }
00071 
00072       ALint source_state = AL_INITIAL;
00073 #     ifdef WIN32
00074       alGetSourcei( mSources[0], AL_SOURCE_STATE, &source_state );
00075 #     else
00076       alGetSourceiv( mSources[0], AL_SOURCE_STATE, &source_state );
00077 #     endif
00078       
00079       switch (source_state) 
00080       {
00081          case AL_PLAYING:
00082          case AL_PAUSED:
00083             return true;
00084          default:
00085             break;
00086       }
00087 
00088       return false;
00089    }
00090 
00091    /* blocking call, waits until stream is done writing */
00092    void OpenALAudioOStream::flush()
00093    {
00094       // wait for the stream to finish playing...
00095       while (this->isPlaying())
00096       {
00097          //vpr::Thread::yield();
00098       }
00099    }
00100 
00101    // open the stream
00102    // specify the format of your data you'll be passing to write()
00103    bool OpenALAudioOStream::open( const syn::AudioFormat& fmt )
00104    {
00105       if (mIsOpen == true)
00106       {
00107          this->close();
00108       }
00109       
00110       ALint error;
00111 
00112       // convert the AudioFormat params to OpenAL tags...
00113       // this block is an ugly beast...
00114       mSampRate = fmt.samplingRateHz;
00115       switch (fmt.datatype)
00116       {
00117       case syn::AudioFormat::UNSIGNED8:
00118          std::cout << "[syn] WARNING: UNSIGNED8 unsupported."
00119                    << "Could not not open stream\n" << std::flush; 
00120          return false;
00121       case syn::AudioFormat::SIGNED8:
00122          if (fmt.channels == 1) 
00123             mFormat = AL_FORMAT_MONO8;
00124          else if (fmt.channels == 2)
00125             mFormat = AL_FORMAT_STEREO8;
00126          else 
00127          {
00128             std::cout << "[syn] WARNING: Only 1 or 2 channels unsupported. "
00129                       << "Could not not open stream\n" << std::flush; 
00130             return false;
00131          }
00132          break;
00133       case syn::AudioFormat::UNSIGNED16:
00134          std::cout << "[syn] WARNING: UNSIGNED16 unsupported. "
00135                    << "Could not not open stream\n" << std::flush; 
00136          return false;
00137       case syn::AudioFormat::SIGNED16:
00138          if (fmt.channels == 1) 
00139             mFormat = AL_FORMAT_MONO16;
00140          else if (fmt.channels == 2) 
00141             mFormat = AL_FORMAT_STEREO16;
00142          else 
00143          {
00144             std::cout << "[syn] WARNING: Only 1 or 2 channels unsupported. "
00145                       << "Could not not open stream\n" << std::flush; 
00146             return false;
00147          }
00148          break;
00149       case syn::AudioFormat::FLOAT32:  
00150          std::cout << "[syn] WARNING: FLOAT32 unsupported."
00151                    << "Could not not open stream\n" << std::flush; 
00152          return false;
00153       }
00154       
00155       // We are going to be queuing x buffers on our source 
00156       // (multi buffered sound)
00157       alGenBuffers( mBuffers.size(), &mBuffers.front() );
00158       if ((error = alGetError()) != AL_NO_ERROR)
00159       {
00160          std::cout << "alGenBuffers : " << error << "\n" << std::flush;
00161          return false;
00162       }
00163 
00164       // init the buffers the stream reads from
00165       std::vector<char> junk;
00166       junk.resize( 1024 );
00167       for (unsigned int x = 0; x < mBuffers.size(); x++)
00168       {
00169          alBufferData( mBuffers[x], mFormat, &junk[0], 
00170                         junk.size(), mSampRate);
00171          if ((error = alGetError()) != AL_NO_ERROR)
00172             std::cout  <<  "[syn] ERROR: alBufferData : "  <<  error << "\n"  <<  std::flush;
00173       }
00174    
00175       // create the stream:
00176 
00177       // create one sound source
00178       alGenSources( 1, &mSources[0] );
00179       if ((error = alGetError()) != AL_NO_ERROR)
00180       {
00181          std::cout  <<  "[syn] ERROR: alGenSources 1 : "  <<  error  <<  "\n"  <<  std::flush;
00182          alDeleteBuffers( mBuffers.size(), &mBuffers[0] );
00183          return false;
00184       }
00185 
00186       // Queue the multiple buffers on the source
00187       alSourceQueueBuffers( mSources[0], mBuffers.size(), &mBuffers[0] );
00188       if ((error = alGetError()) != AL_NO_ERROR)
00189          std::cout << "[syn] ERROR: alSourceQueueBuffers : " << error << "\n" << std::flush;
00190       mBufferHackSwitch = mBuffers[0];
00191       
00192       // don't loop.
00193       alSourcei( mSources[0], AL_LOOPING, AL_FALSE );
00194 
00195       // Start playing source
00196       alSourcePlay( mSources[0] );
00197       if ((error = alGetError()) != AL_NO_ERROR)
00198          std::cout << "[syn] ERROR: alSourcePlay source 0 : " << error << "\n" << std::flush;
00199 
00200       mFmt = fmt;
00201       mIsOpen = true;
00202       return true;
00203    }
00204 
00205    // SOURCE_RELATIVE
00206 
00207 
00208 
00209    // poll this function to know when to give more data to update
00210    bool OpenALAudioOStream::canAcceptMoreData() const 
00211    {
00212       if (mIsOpen == false)
00213          return false;
00214 
00215       if (alIsSource( mSources[0] ) == AL_FALSE)
00216          return false;
00217       
00218       if (this->isPlaying() == false)
00219       {
00220          // Starved Stream (bad):
00221          // OpenAL played through all queued buffers without another 
00222          // being queued in time, so the source went into a "stopped" 
00223          // state. We definately can accept more data in this starved 
00224          // state, so return true...
00225          //alSourcePlay( mSources[0] );
00226          return true;
00227       }
00228 
00229       ALuint buffers_processed;
00230 #     ifdef WIN32
00231       alGetSourcei( mSources[0], AL_BUFFERS_PROCESSED, 
00232                     (int*)&buffers_processed );
00233 #     else
00234       alGetSourceiv( mSources[0], AL_BUFFERS_PROCESSED, 
00235                     (int*)&buffers_processed );
00236 #     endif
00237 
00238       // if there is at least one buffer finished playing, 
00239       // we can requeue a new one with the given data
00240       // NOTE: we can always count on at least 2 buffers queued
00241       //       because that is always how we leave the source in 
00242       //       init() and in write()
00243       if (buffers_processed >= 1)
00244          return true;
00245       else
00246          return false;
00247    }
00248 
00249    // write data to the sound hardware. 
00250    // make sure it is still in the format you opened the steam in...
00251    bool OpenALAudioOStream::write( const void* const data, int samples )
00252    {
00253       int numBytes = samples * mFmt.sampsize();
00254       mPcount = samples;
00255       
00256       if (mIsOpen == false)
00257          return false;
00258 
00259       //ALuint count = 0;
00260       //ALuint buffersreturned = 0;
00261       //ALboolean bFinishedPlaying = AL_FALSE;
00262       //ALuint buffersinqueue = mBuffers.size();
00263       ALint error;
00264 
00265       // if there is at least one buffer finished playing, 
00266       // we can requeue a new one with the given data
00267       if (this->canAcceptMoreData() == true)
00268       {
00269          // If some buffers have been played, 
00270          // unqueue them and load new audio into them, 
00271          // then add them to the queue
00272 
00273          // Pseudo code for Streaming with Open AL
00274          // while (buffers_processed >= 1)
00275          //    Unqueue a buffer "buf"
00276          //    write audio data into "buf"
00277          //    re-Queue "buf"
00278 
00279          
00280          // unqueue a finished buffer (takes first available in 
00281          // order of appearance)
00282 
00283          ALuint buffer_id = 0;
00284          
00285 #ifdef UNIX_STREAMING_BUG 
00286          // loki function semantics... (old openal unix tree only)
00287          // NOTE: this should work under both the new and the old ways,
00288          //       but will be limited to 2 buffers...
00289          buffer_id = mBufferHackSwitch;
00290          alSourceUnqueueBuffers( mSources[0], 1, &buffer_id );
00291          if ((error = alGetError()) != AL_NO_ERROR)
00292          {
00293             std::cout << "[syn] ERROR: alSourceUnqueueBuffers: "
00294                       << error << " " << alGetString( error ) 
00295                       << "\n" << std::flush;
00296 
00297             return false;
00298          }
00299 
00300          // unqueued successfully, swap, so we unqueue the other next time.
00301          if (mBufferHackSwitch == mBuffers[0])
00302             mBufferHackSwitch = mBuffers[1];
00303          else
00304             mBufferHackSwitch = mBuffers[0];
00305 #else // ifndef UNIX_STREAMING_BUG
00306 
00307          // openal spec function semantics (win32, and new unix tree)
00308          alSourceUnqueueBuffers( mSources[0], 1, &buffer_id );
00309          if ((error = alGetError()) != AL_NO_ERROR)
00310          {
00311             std::cout << "[syn] ERROR: alSourceUnqueueBuffers 1 : "
00312                       << error << " " << alGetString( error ) << "\n" 
00313                       << std::flush;
00314             return false;
00315          }
00316 #endif // UNIX_STREAMING_BUG
00317          
00318          //std::cout<<"unqueued: "<<buffer_id<<" buf:("<<mBuffers[0]<<" "<<mBuffers[1]<<")\n"<<std::flush;
00319          
00320          // write the new data into the recently unqueued buffer.
00321          alBufferData( buffer_id, mFormat, const_cast<void*>( data ), numBytes, mSampRate );
00322          if ((error = alGetError()) != AL_NO_ERROR)
00323          {
00324             std::cout << "[syn] ERROR: alBufferData [id:"<<buffer_id<<" sz:"<<numBytes<<"] : "
00325                       <<  error << " " << alGetString( error ) << "\n" << std::flush;
00326             return false;
00327          }
00328 
00329          // re-Queue the buffer
00330          alSourceQueueBuffers( mSources[0], 1, &buffer_id );
00331          if ((error = alGetError()) != AL_NO_ERROR)
00332          {
00333             if (error == AL_INVALID_VALUE)
00334                std::cout << "[syn] ERROR: alSourceQueueBuffers 1 : "
00335                       << "AL_INVALID_VALUE\n"  << std::flush;
00336             else
00337                std::cout << "[syn] ERROR: alSourceQueueBuffers 1 : "
00338                       <<  error << " " << alGetString( error ) << "\n" << std::flush;
00339             return false;
00340          }
00341       
00342          // if our stream stopped (dropout), then restart it
00343          // now that we have something queued to play.
00344          if (this->isPlaying() == false)
00345          {
00346             std::cout << "[syn] WARNING: stream was stopped, "
00347                       << "restarting\n" << std::flush;
00348             alSourcePlay( mSources[0] );
00349          }
00350 
00351          // queued data successfully (done with it.)
00352          return true;
00353       }
00354       else
00355       {
00356          return false;
00357       }
00358    }
00359 
00360    bool OpenALAudioOStream::close()
00361    {
00362       if (mIsOpen == false)
00363          return false;
00364 
00365       ALuint error;
00366       alSourceStopv(1, &mSources[0]);
00367       if ((error = alGetError()) != AL_NO_ERROR)
00368          std::cout << "[syn] ERROR: alSourceStopv 1 : " 
00369                << error << "\n" << std::flush;
00370 
00371       alDeleteSources(1, &mSources[0]);
00372       if ((error = alGetError()) != AL_NO_ERROR)
00373          std::cout << "[syn] ERROR: alDeleteSources 1 : " 
00374                << error << "\n" << std::flush;
00375 
00376       alDeleteBuffers(mBuffers.size(), &mBuffers[0]);
00377       if ((error = alGetError()) != AL_NO_ERROR)
00378          std::cout << "[syn] ERROR: alDeleteBuffers : " 
00379                << error << "\n" << std::flush;
00380 
00381       mIsOpen = false;
00382       return true;
00383    }
00384 
00385    // set the number of audio buffers to queue...
00386    void OpenALAudioOStream::resizeBuffers( int numBuffers )
00387    {
00388       if (mIsOpen == true)
00389       {
00390          this->close();
00391          mBuffers.resize( numBuffers );
00392          this->open( mFmt );
00393       }
00394       else
00395       {
00396          mBuffers.resize( numBuffers );
00397       }
00398    }
00399    
00400 } // namespace

Generated at Mon Apr 15 09:26:08 2002 for subsynth by doxygen1.2.8.1 written by Dimitri van Heesch, © 1997-2001