![[]](/images/special/trans.gif)
LibN2L-4 Library Code ReferenceClassesCompounds Files Members Method Index Full Reference cAudioMixer.cppGo to the documentation of this file.00001 /************************************************************************ 00002 Nova-2 Library (libN2L, or simply n2l) Game development C++ Library 00003 Copyright (C) 2002 Aaron Cameron 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Lesser General Public 00007 License as published by the Free Software Foundation; either 00008 version 2.1 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Lesser General Public License for more details. 00014 00015 You should have received a copy of the GNU Lesser General Public 00016 License along with this library; if not, write to the Free Software 00017 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00018 00019 A copy of the GNU Lesser General Public License has been provided with 00020 this library in the file 'COPYING'. 00021 00022 Contact information for the author of this library has been provided 00023 with this library in the file 'AUTHOR'. 00024 ************************************************************************/ 00025 #include "audio/cAudioMixer.h" 00026 00027 #include "n2l/vfs.h" 00028 00029 #include "audio/cAudioSource.h" 00030 00031 #include <stdlib.h> 00032 00033 /******************************************************************************/ 00034 namespace n2l 00035 { 00036 const cAudioMixer::tFrequency cAudioMixer::DefaultFrequency = 00037 cAudioMixer::tFrequency(MIX_DEFAULT_FREQUENCY); 00038 00039 const cAudioMixer::tFormat cAudioMixer::DefaultFormat = 00040 cAudioMixer::tFormat(MIX_DEFAULT_FORMAT); 00041 00042 const cAudioMixer::tSoundChannels cAudioMixer::DefaultSoundChannels = 00043 cAudioMixer::Channels_Stereo; 00044 00045 const tSint cAudioMixer::DefaultChunkSize = 4096; 00046 00047 const cAudioMixer::tMixChannels cAudioMixer::DefaultMixingChannels = 8; 00048 00049 const cAudioMixer::tMixChannels cAudioMixer::MusicChannel = 0; 00050 00051 cAutoPtr<cAudioMixer::cSubSysHolder> cAudioMixer::smSubSysHolder = 00052 new cAudioMixer::cSubSysHolder(); 00053 00054 tBool cAudioMixer::smInited = false; 00055 cAudioMixer::tMixChannels cAudioMixer::smNumMixingChannels = 0; 00056 00057 cAudioMixer::tSoundDriver cAudioMixer::smDriver = 00058 cAudioMixer::SoundDriver_Auto; 00059 00060 cAudioMixer::tChannelSourceMap cAudioMixer::mChannelSourceMap; 00061 00062 /**************************************************************************/ 00063 cAudioMixer::cSubSysHolder::cSubSysHolder() 00064 { 00065 } 00066 00067 /**************************************************************************/ 00068 cAudioMixer::cSubSysHolder::~cSubSysHolder() 00069 { 00070 cAudioMixer::closeAudio(); 00071 } 00072 00073 /**************************************************************************/ 00074 void cAudioMixer::setDriver( const tSoundDriver &iDriver ) 00075 { 00076 boundCheckSoundDriver( iDriver ); 00077 smDriver = iDriver; 00078 } 00079 00080 /**************************************************************************/ 00081 const cAudioMixer::tSoundDriver cAudioMixer::getDriver() 00082 { 00083 return smDriver; 00084 } 00085 00086 /**************************************************************************/ 00087 void cAudioMixer::openAudio( const tFrequency iFrequency, 00088 const tFormat iFormat, const tSoundChannels iSoundChannels, 00089 const tSint iChunkSize ) 00090 { 00091 // Make sure things are as they should be. 00092 boundCheckFrequency( iFrequency ); 00093 boundCheckFormat( iFormat ); 00094 boundCheckSoundChannels( iSoundChannels ); 00095 boundCheckChunkSize( iChunkSize ); 00096 00097 // Choose a driver. 00098 if (smDriver == SoundDriver_Null) 00099 return; 00100 else if (smDriver == SoundDriver_Auto) 00101 n2lUnsetEnv( "SDL_AUDIODRIVER" ); 00102 else { 00103 if (!n2lSetEnv( "SDL_AUDIODRIVER", 00104 envStringForDriver(smDriver).c_str())) 00105 { 00106 throw cDriverLayerException( "cAudioMixer::openAudio", 00107 "Failed to select a driver", "Environment", 00108 "Environment out of space" ); 00109 } 00110 } 00111 00112 requireSDLSubSystem( SDLSubSystemInitFlag_Audio ); 00113 00114 // Set up the mixing device. 00115 if (-1==Mix_OpenAudio( tSint(iFrequency), 00116 tUint16(iFormat), 00117 tSint(iSoundChannels), 00118 tSint(iChunkSize) )) 00119 throw cDriverLayerException("cAudioMixer::openAudio", 00120 "Failed to open audio", "SDL_mixer", Mix_GetError() ); 00121 Mix_AllocateChannels( DefaultMixingChannels ); 00122 // Until Mix_loadMUS_RW is implemented, we'll reserve the 00123 // first channel for music, and play it like a regular sample. 00124 # ifdef N2L_FakeMusic 00125 Mix_ReserveChannels(1); 00126 smNumMixingChannels = DefaultMixingChannels-1; 00127 # else 00128 smNumMixingChannels = DefaultMixingChannels; 00129 # endif 00130 00131 // Set a callback to multiplex the channel finishing code. 00132 Mix_ChannelFinished( cAudioMixer::mixerSampleFinishedCallback ); 00133 00134 smInited = true; 00135 } 00136 00137 /**************************************************************************/ 00138 void cAudioMixer::closeAudio() 00139 { 00140 if (smInited) { 00141 Mix_CloseAudio(); 00142 releaseSDLSubSystem( SDLSubSystemInitFlag_Audio ); 00143 smInited = false; 00144 } 00145 } 00146 00147 /**************************************************************************/ 00148 const cAudioMixer::tFrequency cAudioMixer::frequency() 00149 { 00150 if (!smInited) 00151 throw cBadDataUseException( "cAudioMixer::frequency", 00152 "Mixer isn\'t running" ); 00153 int frequency, channels; 00154 tUint16 format; 00155 Mix_QuerySpec(&frequency, &format, &channels); 00156 return frequency; 00157 } 00158 00159 /**************************************************************************/ 00160 const cAudioMixer::tFormat cAudioMixer::format() 00161 { 00162 if (!smInited) 00163 throw cBadDataUseException( "cAudioMixer::format", 00164 "Mixer isn\'t running" ); 00165 int frequency, channels; 00166 tUint16 format; 00167 Mix_QuerySpec(&frequency, &format, &channels); 00168 switch (format) 00169 { 00170 case AUDIO_U8: return Format_U8; 00171 case AUDIO_S8: return Format_S8; 00172 case AUDIO_U16LSB: return Format_U16LSB; 00173 case AUDIO_S16LSB: return Format_S16LSB; 00174 case AUDIO_U16MSB: return Format_U16MSB; 00175 case AUDIO_S16MSB: return Format_S16MSB; 00176 default: return Format_Unknown; 00177 } 00178 } 00179 00180 /**************************************************************************/ 00181 const tUint cAudioMixer::sampleSize() 00182 { 00183 switch (format()) 00184 { 00185 case Format_U8: return 1; 00186 case Format_S8: return 1; 00187 case Format_U16LSB: return 2; 00188 case Format_S16LSB: return 2; 00189 case Format_U16MSB: return 2; 00190 case Format_S16MSB: return 2; 00191 default: return 0; 00192 } 00193 } 00194 00195 /**************************************************************************/ 00196 const tString & cAudioMixer::formatAsString() 00197 { 00198 if (!smInited) 00199 throw cBadDataUseException( "cAudioMixer::formatAsString", 00200 "Mixer isn\'t running" ); 00201 int frequency, channels; 00202 tUint16 format; 00203 Mix_QuerySpec(&frequency, &format, &channels); 00204 00205 static const tString AUDIO_U8_String("Unsigned 8-bit"); 00206 static const tString AUDIO_S8_String("Signed 8-bit"); 00207 static const tString AUDIO_U16LSB_String("Unsigned 16-bit LSB"); 00208 static const tString AUDIO_S16LSB_String("Signed 16-bit LSB"); 00209 static const tString AUDIO_U16MSB_String("Unsigned 16-bit MSB"); 00210 static const tString AUDIO_S16MSB_String("Signed 16-bit MSB"); 00211 // We don't do the system types because they just become one 00212 // of the other, already defined types. 00213 static const tString Format_Unknown_String("Unknown Format"); 00214 00215 switch (format) 00216 { 00217 case AUDIO_U8: return AUDIO_U8_String; 00218 case AUDIO_S8: return AUDIO_S8_String; 00219 case AUDIO_U16LSB: return AUDIO_U16LSB_String; 00220 case AUDIO_S16LSB: return AUDIO_S16LSB_String; 00221 case AUDIO_U16MSB: return AUDIO_U16MSB_String; 00222 case AUDIO_S16MSB: return AUDIO_S16MSB_String; 00223 default: return Format_Unknown_String; 00224 } 00225 } 00226 00227 /**************************************************************************/ 00228 const cAudioMixer::tSoundChannels cAudioMixer::soundChannels() 00229 { 00230 if (!smInited) 00231 throw cBadDataUseException( "cAudioMixer::formatAsString", 00232 "Mixer isn\'t running" ); 00233 int frequency, channels; 00234 tUint16 format; 00235 Mix_QuerySpec(&frequency, &format, &channels); 00236 if (channels==1) 00237 return Channels_Mono; 00238 else return Channels_Stereo; 00239 } 00240 00241 /**************************************************************************/ 00242 void cAudioMixer::allocateMixingChannels( const tMixChannels iNum ) 00243 { 00244 if (!smInited) 00245 throw cBadDataUseException( "cAudioMixer::allocateMixingChannels", 00246 "Mixer isn\'t running" ); 00247 // Make sure there's at least one free channel, and one channel 00248 // for music if we need it. 00249 # ifdef N2L_FakeMusic 00250 const tSint MinChannels( 2 ); 00251 smNumMixingChannels = iNum-1; 00252 # else 00253 const tSint MinChannels( 1 ); 00254 smNumMixingChannels = iNum; 00255 # endif 00256 00257 if (iNum<MinChannels) 00258 throw cOutOfBoundsException( "cAudioMixer::allocateMixingChannels", 00259 "Too few channels allocated" ); 00260 Mix_AllocateChannels( iNum ); 00261 } 00262 00263 /**************************************************************************/ 00264 const tUint cAudioMixer::numChannelsPlaying() 00265 { 00266 if (!smInited) return 0; 00267 const tUint NumChannels( Mix_Playing(-1) ); 00268 # ifdef N2L_FakeMusic 00269 if (Mix_Playing(MusicChannel)) 00270 return NumChannels-1; 00271 # endif 00272 return NumChannels; 00273 } 00274 00275 /**************************************************************************/ 00276 const tUint cAudioMixer::numFreeChannels() 00277 { 00278 if (!smInited) 00279 throw cBadDataUseException( "cAudioMixer::numFreeChannels", 00280 "Mixer isn\'t running" ); 00281 return smNumMixingChannels-numChannelsPlaying(); 00282 } 00283 00284 /**************************************************************************/ 00285 const tUint cAudioMixer::numChannels() 00286 { 00287 if (!smInited) 00288 throw cBadDataUseException( "cAudioMixer::numChannels", 00289 "Mixer isn\'t running" ); 00290 return smNumMixingChannels; 00291 } 00292 00293 /**************************************************************************/ 00294 void cAudioMixer::musicVolume( const tSint iVolume ) 00295 { 00296 if (!smInited) return; 00297 # ifdef N2L_FakeMusic 00298 Mix_Volume(MusicChannel,iVolume); 00299 # else 00300 Mix_VolumeMusic( iVolume ); 00301 # endif 00302 } 00303 00304 /**************************************************************************/ 00305 void cAudioMixer::musicVolume( const tFloat iVolume ) 00306 { 00307 if (!smInited) return; 00308 const tFloat ClampedVolume( n2l_max(n2l_min(iVolume,1.0f),0.0f) ); 00309 # ifdef N2L_FakeMusic 00310 Mix_Volume(MusicChannel, tSint( 00311 ClampedVolume*tFloat(MIX_MAX_VOLUME) ) ); 00312 # else 00313 Mix_VolumeMusic( tSint( ClampedVolume*tFloat(MIX_MAX_VOLUME) ) ); 00314 # endif 00315 } 00316 00317 /**************************************************************************/ 00318 void cAudioMixer::fadeOutMusic( const tUint iMilliseconds ) 00319 { 00320 if (!smInited) 00321 throw cBadDataUseException( "cAudioMixer::fadeOutMusic", 00322 "Mixer isn\'t running" ); 00323 # ifdef N2L_FakeMusic 00324 Mix_FadeOutChannel( MusicChannel, iMilliseconds ); 00325 # else 00326 Mix_FadeOutMusic( iMilliseconds ); 00327 # endif 00328 } 00329 00330 00331 /**************************************************************************/ 00332 const cAudioMixer::tFadeStatus cAudioMixer::musicFadeStatus() 00333 { 00334 if (!smInited) return Fading_None; 00335 # ifdef N2L_FakeMusic 00336 return tFadeStatus(Mix_FadingChannel( MusicChannel )); 00337 # else 00338 return tFadeStatus(Mix_FadingMusic()); 00339 # endif 00340 } 00341 00342 /**************************************************************************/ 00343 void cAudioMixer::channelVolume( const tAudioChannel iChannel, 00344 const tFloat &iVolume ) 00345 { 00346 if (!smInited) 00347 throw cBadDataUseException( "cAudioMixer::channelVolume", 00348 "Mixer isn\'t running" ); 00349 if (Mix_AllocateChannels(-1)<=iChannel) 00350 throw cOutOfBoundsException( "cAudioMixer::channelVolume", 00351 tString("Channel volume set on non-existent channel: ")+ 00352 asString(iChannel) ); 00353 00354 Mix_Volume( iChannel, tSint(128.0f*n2lMax(0.0f,n2lMin(1.0f,iVolume))) ); 00355 } 00356 00357 /**************************************************************************/ 00358 const tString &cAudioMixer::driverName( const tSoundDriver &iDriver ) 00359 { 00360 static const tString DrvStrings[SoundDriver_NumSoundDrivers] = 00361 { 00362 "Auto-Detect", "OpenBSD", "OSS DSP", "ALSA", 00363 "/dev/audio Audio", "AL (Irix)", "ARTS Audio Daemon", 00364 "ESound Audio Daemon", "NAS Audio Daemon", "OSS DMA", 00365 "Win32 DirectX", "Win32 WaveOut", "BeOS baudio", 00366 "MacOS SoundManager", "AIX paud", "AHI Amiga", 00367 "Disk Output", "Null" 00368 }; 00369 if (iDriver<SoundDriver_Auto || 00370 iDriver>=SoundDriver_NumSoundDrivers) 00371 throw cOutOfBoundsException( "cAudioMixer::driverName", 00372 "Driver requested does not exist" ); 00373 return DrvStrings[ iDriver ]; 00374 } 00375 00376 /**************************************************************************/ 00377 const tString &cAudioMixer::driverName() 00378 { 00379 return driverName(smDriver); 00380 } 00381 00382 /**************************************************************************/ 00383 void cAudioMixer::registerSource( cAudioSource *ioSource ) 00384 { 00385 if (!smInited) 00386 throw cBadDataUseException( "cAudioMixer::registerSource", 00387 "Mixer isn\'t running" ); 00388 mChannelSourceMap[ioSource->currentChannel()] = ioSource; 00389 } 00390 00391 /**************************************************************************/ 00392 void cAudioMixer::unregisterSource( cAudioSource *ioSource ) 00393 { 00394 if (!smInited) 00395 throw cBadDataUseException( "cAudioMixer::unregisterSource", 00396 "Mixer isn\'t running" ); 00397 tChannelSourceMap::iterator i = mChannelSourceMap.find( 00398 ioSource->currentChannel() ); 00399 if (i == mChannelSourceMap.end()) return; 00400 if (i->second == ioSource) i->second = 0; 00401 } 00402 00403 /**************************************************************************/ 00404 void cAudioMixer::boundCheckFrequency( const tFrequency iFrequency ) 00405 { 00406 if (iFrequency <= 0) 00407 throw cOutOfBoundsException( "cAudioMixer::boundCheckFrequency", 00408 "Frequency out of range ( <=0 )" ); 00409 } 00410 00411 /**************************************************************************/ 00412 void cAudioMixer::boundCheckFormat( const tFormat iFormat ) 00413 { 00414 if (iFormat == Format_U8 || 00415 iFormat == Format_S8 || 00416 iFormat == Format_U16LSB || 00417 iFormat == Format_S16LSB || 00418 iFormat == Format_U16MSB || 00419 iFormat == Format_S16MSB || 00420 iFormat == Format_U16SYS || 00421 iFormat == Format_S16SYS) return; 00422 throw cOutOfBoundsException( "cAudioMixer::boundCheckFormat", 00423 "Format out of range" ); 00424 00425 } 00426 00427 /**************************************************************************/ 00428 void cAudioMixer::boundCheckSoundChannels( const tSoundChannels iChannels ) 00429 { 00430 if (iChannels!=Channels_Mono && iChannels!=Channels_Stereo) 00431 throw cOutOfBoundsException( "cAudioMixer::boundCheckSoundChannels", 00432 "Num channels out of range" ); 00433 } 00434 00435 /**************************************************************************/ 00436 void cAudioMixer::boundCheckChunkSize( const tSint iChunkSize ) 00437 { 00438 if (iChunkSize<=0) 00439 throw cOutOfBoundsException( "cAudioMixer::boundCheckChunkSize", 00440 "Chunk size out of range" ); 00441 } 00442 00443 /**************************************************************************/ 00444 void cAudioMixer::boundCheckSoundDriver( const tSoundDriver &iDriver ) 00445 { 00446 if (iDriver<SoundDriver_Auto || iDriver>=SoundDriver_NumSoundDrivers) 00447 throw cOutOfBoundsException( "cAudioMixer::boundCheckSoundDriver", 00448 "Driver doesn\'t exist", asString(tUint(iDriver)) ); 00449 } 00450 00451 /**************************************************************************/ 00452 const tString &cAudioMixer::envStringForDriver( 00453 const tSoundDriver &iDriver ) 00454 { 00455 static const tString DrvStrings[SoundDriver_NumSoundDrivers] = 00456 { 00457 "", "openbsd", "dsp", "alsa", "audio", "AL", "artsc", "esd", 00458 "nas", "dma", "dsound", "waveout", "baudio", "sndmgr", 00459 "paud", "AHI", "disk", "null" 00460 }; 00461 if (iDriver<SoundDriver_Auto || 00462 iDriver>=SoundDriver_NumSoundDrivers) 00463 throw cOutOfBoundsException( "cAudioMixer::envStringForDriver", 00464 "Driver requested does not exist" ); 00465 return DrvStrings[ iDriver ]; 00466 } 00467 00468 /**************************************************************************/ 00469 void cAudioMixer::mixerSampleFinishedCallback( int iChannel ) 00470 { 00471 tChannelSourceMap::iterator i = mChannelSourceMap.find( iChannel ); 00472 if (i == mChannelSourceMap.end() || i->second == 0) return; 00473 if (i->second->currentChannel() == iChannel) i->second = 0; 00474 } 00475 00476 } // namespace |