AaronCameron.net
I care not for your petty politics.
Not a Member? - Login or Create an Account
Thursday the 24th of May 2012 @ 03:05am
Front Page Journal Projects Your Profile About
[]

LibN2L-4 Library Code Reference

Classes
Compounds
Files
Members
Method Index
Full Reference

cAudioMixer.cpp

Go 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
©2012 Aaron Cameron