Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

SoundManager.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_SoundManager_h_
00003 #define INCLUDED_SoundManager_h_
00004 
00005 #include "IPC/RCRegion.h"
00006 #include "Shared/ODataFormats.h"
00007 
00008 #include <string>
00009 #include <vector>
00010 #include "IPC/ListMemBuf.h"
00011 #include "IPC/MutexLock.h"
00012 #include "SoundManagerMsg.h"
00013 #include "IPC/ProcessID.h"
00014 #include "Shared/attributes.h"
00015 
00016 #ifdef PLATFORM_APERIOS
00017 class OSubject;
00018 class ONotifyEvent;
00019 #else
00020 #  include "IPC/MessageQueue.h"
00021 #endif
00022 
00023 //! Provides sound effects and caching services, as well as mixing buffers for the SoundPlay process
00024 /*! Provides easy methods for playing back sounds, either from files
00025  *  on the memory stick, or from dynamically generated buffers.  You
00026  *  can chain playback commands so that when one sound finishes,
00027  *  another picks up automatically.  This might be handy if, say,
00028  *  someone wants to write an MP3 player ;) The sounds would be too
00029  *  large to load into memory all at once, but you could load a block
00030  *  at a time and chain them so it seamlessly moves from one to the
00031  *  other.
00032  *  
00033  *  You can also preload sounds (loadFile()) before playing them (play() / playFile()) so
00034  *  there's no delay between requesting a sound and having it start playing while it is loaded from disk/memory stick.
00035  *  Just be sure to release the file (releaseFile()) again
00036  *  when you're done with it ;)
00037  *
00038  *  All functions will attempt to lock the SoundManager.  Remember,
00039  *  this is running in a shared memory region, accessible by the
00040  *  SoundPlay process and both the Main and Motion processes (so
00041  *  MotionCommands can play sounds!)
00042  *
00043  *  One could be tempted to draw parallels to the MotionManager, and
00044  *  envision a system with SoundCommands that are handed over and can
00045  *  dynamically compute sound buffers as needed.  If you have the time
00046  *  and inclination, the job's all yours... (Midi players, speech
00047  *  synthesizer, ...?)
00048  *
00049  *  @todo Volume control, variable playback speed, support more wav
00050  *  file formats (latter two are the same thing if you think about it - need
00051  *  to be able to resample on the fly)
00052  *  
00053  *  @todo Add functions to hand out regions to be filled out to avoid
00054  *  copying into the buffer.
00055  *
00056  *  @see <a href="http://www.cs.cmu.edu/~dst/Tekkotsu/Tutorial/sound.shtml">David Touretzky's "Playing Sounds" Chapter</a>
00057  */
00058 class SoundManager {
00059 public:
00060   //! destructor
00061   virtual ~SoundManager();
00062 
00063   //!constructor, should only be called by the receiving process (SoundPlay)
00064   SoundManager();
00065 #ifdef PLATFORM_APERIOS
00066   //!Each process needs to call this before it can send sounds to the SoundPlay process
00067   void InitAccess(OSubject* subj);
00068 #else //PLATFORM_LOCAL
00069   //!Each process (except SoundPlay) needs to call this before it can send sounds to the SoundPlay process
00070   void InitAccess(MessageQueueBase& sndbufq);
00071 #endif
00072   
00073   //!This is used for referring to sound data so you can start playing it or release it
00074   typedef SoundManagerMsg::Snd_ID Snd_ID;
00075   static const Snd_ID invalid_Snd_ID=(Snd_ID)-1; //!< for reporting errors
00076   static const Snd_ID MAX_SND=50; //!< the number of sounds that can be loaded at any given time
00077   
00078   //!This is for referring to instances of the play command so you can stop, pause, or monitor progress (later versions will send events upon completion)
00079   typedef unsigned short Play_ID;
00080   static const Play_ID invalid_Play_ID=(Play_ID)-1; //!< for reporting errors
00081   static const Play_ID MAX_PLAY=256; //!< the number of sounds that can be enqueued at the same time (see MixMode_t)
00082 
00083   static const unsigned int MAX_NAME_LEN=128;   //!<maximum length of a path
00084 
00085   //!Used to set the mode for mixing multiple sound channels
00086   /*!Feel free to add a higher quality mixer if you're an audiophile - I'm pretty new to sound processing*/
00087   enum MixMode_t {
00088     // there's some prototype code for a bit-shifting 'Faster' quality level, but it hasn't been finished... 'Fast' is the default for now.
00089     Fast,    //!< uses real division to maintain volume level, without increasing intermediary precision, which causes low-order bit error in exchange for less CPU usage
00090     Quality  //!< uses real division to maintain volume level, using an intermediary higher precision buffer for mixing
00091   };
00092 
00093   //! indicates how to handle channel overflow (trying to play more sounds than maximum number of mixing channels). See #queue_mode
00094   enum QueueMode_t {
00095     Enqueue,        //!< newer sounds are played when a channel opens up (when old sound finishes)
00096     Pause,          //!< newer sounds pause oldest sound, which continues when a channel opens
00097     Stop,           //!< newer sounds stop oldest sound
00098     Override,       //!< older sounds have play heads advanced, but don't get mixed until a channel opens
00099   };
00100 
00101   //!loads a wav file (if it matches Config::sound_config settings - can't do resampling yet)
00102   /*!Since the SoundManager does the loading, if the same file is being played more than once, only once copy is stored in memory 
00103    * @param name can be either a full path, or a partial path relative to Config::sound_config::root
00104    * @return ID number for future references (can also use name)
00105    * The sound data will be cached until releaseFile() or release() is called a matching number of times*/
00106   Snd_ID loadFile(std::string const &name);
00107 
00108   //!loads raw samples from a buffer (assumes matches Config::sound_config settings)
00109   /*!The sound data will be cached until release() is called a matching number of times.\n
00110    * This function is useful for dynamic sound sources.  A copy will be made. */
00111   Snd_ID loadBuffer(const char buf[], unsigned int len);
00112   
00113   //!Marks the sound buffer to be released after the last play command completes (or right now if not being played)
00114   void releaseFile(std::string const &name);
00115 
00116   //!Marks the sound buffer to be released after the last play command completes (or right now if not being played)
00117   void release(Snd_ID id);
00118   
00119   //!play a wav file (if it matches Config::sound_config settings - can't do resampling yet)
00120   /*!Will do a call to loadFile() first, and then automatically release the sound again when complete.
00121    * @param name can be either a full path, or a partial path relative to Config::sound_config::root
00122    * @return ID number for future references
00123    * The sound data will not be cached after done playing unless a call to loadFile is made*/
00124   Play_ID playFile(std::string const &name);
00125 
00126   //!loads raw samples from a buffer (assumes buffer matches Config::sound_config settings)
00127   /*!The sound data will be released after done playing*/
00128   Play_ID playBuffer(const char buf[], unsigned int len);
00129   
00130   //!plays a previously loaded buffer or file
00131   Play_ID play(Snd_ID id);
00132   
00133   //!allows automatic queuing of sounds - good for dynamic sound sources!
00134   /*!if you chain more than once to the same base, the new buffers are appended
00135    * to the end of the chain - the new buffer doesn't replace the current chain
00136    * @return @a base - just for convenience of multiple calls*/
00137   Play_ID chainFile(Play_ID base, std::string const &next);
00138 
00139   //!allows automatic queuing of sounds - good for dynamic sound sources!
00140   /*!if you chain more than once to the same base, the new buffers are appended
00141    * to the end of the chain - the new buffer doesn't replace the current chain
00142    * @return @a base - just for convenience of multiple calls*/
00143   Play_ID chainBuffer(Play_ID base, const char buf[], unsigned int len);
00144 
00145   //!allows automatic queuing of sounds - good for dynamic sound sources!
00146   /*!if you chain more than once to the same base, the new buffers are appended
00147    * to the end of the chain - the new buffer doesn't replace the current chain
00148    * @return @a base - just for convenience of multiple calls*/
00149   Play_ID chain(Play_ID base, Snd_ID next);
00150   
00151   //!Lets you stop playback of all sounds
00152   void stopPlay();
00153 
00154   //!Lets you stop playback of a sound
00155   void stopPlay(Play_ID id);
00156   
00157   //!Lets you pause playback
00158   void pausePlay(Play_ID id);
00159   
00160   //!Lets you resume playback
00161   void resumePlay(Play_ID id);
00162   
00163   //!Lets you control the maximum number of channels (currently playing sounds), method for mixing (applies when max_chan>1), and queuing method (for when overflow channels)
00164   void setMode(unsigned int max_channels, MixMode_t mixer_mode, QueueMode_t queuing_mode);
00165 
00166   //!Gives the time until the sound finishes, in milliseconds.  Subtract 32 to get guarranteed valid time for this ID.
00167   /*!You should be passing the beginning of a chain to get proper results...\n
00168    * May be slightly conservative (will report too small a time) because this
00169    * does not account for delay until SoundPlay picks up the message that a
00170    * sound has been added.\n
00171    * However, it is slighly optimistic (will report too large a time) because
00172    * it processes a buffer all at one go, so it could mark the sound as finished
00173    * (and cause the ID to go invalid) up to RobotInfo::SoundBufferTime (32 ms)
00174    * before the sound finishes.  So subtract SoundBufferTime if you want to be
00175    * absolutely sure the ID will still valid. */
00176   unsigned int getRemainTime(Play_ID id) const;
00177   
00178 #ifdef PLATFORM_APERIOS
00179   //!Copies the sound data to the OPENR buffer, ready to be passed to the system, only called by SoundPlay
00180   /*!@return the number of active sounds */
00181   unsigned int CopyTo(OSoundVectorData* data);
00182 
00183   //!updates internal data structures on the SoundPlay side - you shouldn't be calling this
00184   void ReceivedMsg(const ONotifyEvent& event);
00185 #endif
00186 
00187   //!Copies the sound data to the specified memory buffer, ready to be passed to the system
00188   /*!@return the number of active sounds */
00189   unsigned int CopyTo(void * dest, size_t destSize);
00190   
00191   //!updates internal data structures on the SoundPlay side - you shouldn't be calling this
00192   void ProcessMsg(RCRegion * rcr);
00193   
00194   //! returns number of sounds currently playing
00195   unsigned int getNumPlaying() { return chanlist.size(); }
00196   
00197   //! return the next region serial number -- doesn't actually increment it though, repeated calls will return the same value until the value is actually used
00198   virtual unsigned int getNextKey() { return sn+1; }
00199   
00200   /*! @brief These functions retain an older style first-letter capitalization derived from the OPEN-R
00201    *  sample code.  You should use the Java-style naming convention instead (first letter lowercase) */
00202   //!@name Deprecated
00203   Snd_ID LoadFile(std::string const &name) ATTR_deprecated; //!< deprecated, use loadFile() (note first letter lower case)
00204   Snd_ID LoadBuffer(const char buf[], unsigned int len) ATTR_deprecated; //!< deprecated, use loadBuffer() (note first letter lower case)
00205   void ReleaseFile(std::string const &name) ATTR_deprecated; //!< deprecated, use releaseFile() (note first letter lower case)
00206   void Release(Snd_ID id) ATTR_deprecated; //!< deprecated, use release() (note first letter lower case)
00207   Play_ID PlayFile(std::string const &name) ATTR_deprecated; //!< deprecated, use playFile() (note first letter lower case)
00208   Play_ID PlayBuffer(const char buf[], unsigned int len) ATTR_deprecated; //!< deprecated, use playBuffer() (note first letter lower case)
00209   Play_ID Play(Snd_ID id) ATTR_deprecated; //!< deprecated, use play() (note first letter lower case)
00210   Play_ID ChainFile(Play_ID base, std::string const &next) ATTR_deprecated; //!< deprecated, use chainFile() (note first letter lower case)
00211   Play_ID ChainBuffer(Play_ID base, const char buf[], unsigned int len) ATTR_deprecated; //!< deprecated, use chainBuffer() (note first letter lower case)
00212   Play_ID Chain(Play_ID base, Snd_ID next) ATTR_deprecated; //!< deprecated, use chain() (note first letter lower case)
00213   void StopPlay() ATTR_deprecated; //!< deprecated, use stopPlay() (note first letter lower case)
00214   void StopPlay(Play_ID id) ATTR_deprecated; //!< deprecated, use stopPlay() (note first letter lower case)
00215   void PausePlay(Play_ID id) ATTR_deprecated; //!< deprecated, use pausePlay() (note first letter lower case)
00216   void ResumePlay(Play_ID id) ATTR_deprecated; //!< deprecated, use resumePlay() (note first letter lower case)
00217   void SetMode(unsigned int max_channels, MixMode_t mixer_mode, QueueMode_t queuing_mode) ATTR_deprecated; //!< deprecated, use setMode() (note first letter lower case)
00218   unsigned int GetRemainTime(Play_ID id) const ATTR_deprecated; //!< deprecated, use getRemainTime() (note first letter lower case)
00219   unsigned int GetNumPlaying() ATTR_deprecated; //!< deprecated, use getNumPlaying() (note first letter lower case)
00220   //@}
00221   
00222 protected:
00223   //!Mixes the channel into the buffer
00224   void mixChannel(Play_ID channelId, void* buf, size_t size);
00225   
00226   //!Mixes the channel into the buffer additively
00227   /*!If mode is Quality, then the size of the buffer should be double the normal
00228   * size. */
00229   void mixChannelAdditively(Play_ID channelId, int bitsPerSample, MixMode_t mode, short scalingFactor, void* buf, size_t size);
00230   
00231   //!The intermediate mixer buffer used for Quality mode mixing
00232   int* mixerBuffer;
00233   
00234   //!Size (in bytes) of the intermediate mixer buffer
00235   size_t mixerBufferSize;
00236 
00237   //!Sets up a shared region to hold a sound - rounds to nearest page size
00238   RCRegion* initRegion(unsigned int size);
00239 
00240   //!Looks to see if @a name matches any of the sounds in sndlist (converts to absolute path if not already)
00241   Snd_ID lookupPath(std::string const &name) const;
00242 
00243   //!selects which of the channels are actually to be mixed together, depending on queue_mode
00244   void selectChannels(std::vector<Play_ID>& mix);
00245 
00246   //!update the offsets of sounds which weren't mixed (when needed depending on queue_mode)
00247   void updateChannels(const std::vector<Play_ID>& mixs,size_t used);
00248 
00249   //!called when a buffer end is reached, may reset buffer to next in chain, or just stopPlay()
00250   bool endPlay(Play_ID id);
00251 
00252   //!Holds data about the loaded sounds
00253   struct SoundData {
00254     SoundData();                             //!<constructor
00255     RCRegion * rcr;                          //!<shared region - don't need to share among processes, just collect in SoundPlay
00256     byte* data;                              //!<point to data in region (for convenience, only valid in SoundPlay)
00257     unsigned int len;                        //!<size of the sound
00258     unsigned int ref;                        //!<reference counter
00259     unsigned int sn;                         //!<serial number, allows us to verify that a given message buffer does indeed match this sound, and wasn't delayed in processing
00260     char name[SoundManager::MAX_NAME_LEN];   //!<stores the path to the file, empty if from a buffer
00261   private:
00262     SoundData(const SoundData&);             //!< don't call
00263     SoundData operator=(const SoundData&);   //!< don't call
00264   };
00265   //!For convenience
00266   typedef ListMemBuf<SoundData,MAX_SND,Snd_ID> sndlist_t;
00267   //!Holds a list of all currently loaded sounds
00268   sndlist_t sndlist;
00269   
00270   //!Holds data about sounds currently being played
00271   struct PlayState {
00272     PlayState();            //!<constructor
00273     Snd_ID snd_id;          //!<index of sound
00274     unsigned int offset;    //!<position in the sound
00275     unsigned int cumulative;//!<total time of playing (over queued sounds)
00276     Play_ID next_id;        //!<lets you queue for continuous sound, or loop
00277   };
00278   //!For convenience
00279   typedef ListMemBuf<PlayState,MAX_PLAY,Play_ID> playlist_t;
00280   //!Holds a list of all sounds currently enqueued
00281   playlist_t playlist;
00282   //!For convenience
00283   typedef ListMemBuf<Play_ID,MAX_PLAY,Play_ID> chanlist_t;
00284   //!Holds a list of all currently playing sounds, ordered newest (front) to oldest(back)
00285   chanlist_t chanlist;
00286   
00287   //!Current mixing mode, set by setMode();
00288   MixMode_t mix_mode;
00289 
00290   //!Current queuing mode, set by setMode();
00291   QueueMode_t queue_mode;
00292 
00293   //!Current maximum number of sounds to mix together
00294   unsigned int max_chan;
00295 
00296   //!Prevents multiple processes from accessing at the same time
00297   mutable MutexLock<ProcessID::NumProcesses> lock;
00298   
00299   //!A serial number, incremented for each sound which is created
00300   /*! This is used to verify that a sound message from a process
00301    *  refers to a current sound.  If you imaging a pathological
00302    *  process, which rapidly creates and releases sounds, it would 
00303    *  run through the sndlist ids, possibly before the sound process
00304    *  can process the incoming buffers.  So this is used to ensure
00305    *  that a given message refers to the current sound, and not one
00306    *  that was already released and then reassigned. */
00307   unsigned int sn;
00308 
00309   //!the size of a SoundManagerMsg, which is prefixed before each region sent/received by SoundManager (rounded up to nearest even word boundary)
00310   static const unsigned int MSG_SIZE=((sizeof(SoundManagerMsg)-1)/8+1)*8;
00311 
00312 #ifdef PLATFORM_APERIOS
00313   //!Storage of each process's subject object, used to internally transmit sound buffers to SoundPlay
00314   OSubject * subjs[ProcessID::NumProcesses];
00315 #else //PLATFORM_LOCAL
00316   //!Storage of each process's attachment of the message queue, used to internally transmit sound buffers to SoundPlay
00317   MessageQueueBase * subjs[ProcessID::NumProcesses];
00318 #endif
00319 
00320 private:
00321   SoundManager(const SoundManager&);           //!< don't call
00322   SoundManager operator=(const SoundManager&); //!< don't call
00323 };
00324 
00325 //! lets you play a sound from anywhere in your code - just a one liner!
00326 extern SoundManager * sndman;
00327 
00328 /*! @file
00329  * @brief Describes SoundManager, which provides sound effects and caching services, as well as mixing buffers for the SoundPlay process
00330  * @author ejt (Creator)
00331  *
00332  * $Author: ejt $
00333  * $Name: tekkotsu-4_0 $
00334  * $Revision: 1.10 $
00335  * $State: Exp $
00336  * $Date: 2006/10/03 22:52:24 $
00337  */
00338 
00339 #endif

Tekkotsu v4.0
Generated Thu Nov 22 00:54:56 2007 by Doxygen 1.5.4