Tekkotsu Homepage | Demos | Overview | Downloads | Dev. Resources | Reference | Credits |
SoundManager.hGo 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 |