00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "OSDLSound.h"
00028
00029 #include "OSDLAudio.h"
00030 #include "OSDLFileTags.h"
00031
00032
00033 #ifdef OSDL_USES_CONFIG_H
00034 #include "OSDLConfig.h"
00035 #endif // OSDL_USES_CONFIG_H
00036
00037
00038 #if OSDL_ARCH_NINTENDO_DS
00039 #include "OSDLConfigForNintendoDS.h"
00040 #endif // OSDL_ARCH_NINTENDO_DS
00041
00042
00043
00044 #if OSDL_USES_SDL
00045 #include "SDL.h"
00046 #endif // OSDL_USES_SDL
00047
00048 #if OSDL_USES_SDL_MIXER
00049 #include "SDL_mixer.h"
00050 #endif // OSDL_USES_SDL_MIXER
00051
00052
00053
00054
00055
00056 #if OSDL_DEBUG_AUDIO_PLAYBACK
00057
00058 #define LOG_DEBUG_AUDIO(message) LogPlug::debug(message)
00059 #define LOG_TRACE_AUDIO(message) LogPlug::trace(message)
00060 #define LOG_WARNING_AUDIO(message) LogPlug::warning(message)
00061
00062 #else // OSDL_DEBUG_AUDIO_PLAYBACK
00063
00064 #define LOG_DEBUG_AUDIO(message)
00065 #define LOG_TRACE_AUDIO(message)
00066 #define LOG_WARNING_AUDIO(message)
00067
00068 #endif // OSDL_DEBUG_AUDIO_PLAYBACK
00069
00070
00071
00072 using std::string ;
00073
00074 using Ceylan::System::Millisecond ;
00075
00076
00077 using namespace Ceylan::Log ;
00078 using namespace Ceylan::System ;
00079
00080 using namespace OSDL::Audio ;
00081
00082
00083
00084
00085
00113 SoundException::SoundException( const string & reason ) :
00114 AudibleException( reason )
00115 {
00116
00117 }
00118
00119
00120
00121 SoundException::~SoundException() throw()
00122 {
00123
00124 }
00125
00126
00127
00128
00129
00130 Sound::Sound( const std::string & soundFile, bool preload ) :
00131 Audible( false ),
00132 Ceylan::LoadableWithContent<LowLevelSound>( soundFile ),
00133 _dataStream( 0 )
00134 {
00135
00136 if ( ! AudioModule::IsAudioInitialized() )
00137 throw SoundException( "Sound constructor failed: "
00138 "audio module not already initialized" ) ;
00139
00140 if ( preload )
00141 {
00142
00143 try
00144 {
00145
00146 load() ;
00147
00148 }
00149 catch( const Ceylan::LoadableException & e )
00150 {
00151
00152 throw SoundException( "Sound constructor failed while preloading: "
00153 + e.toString() ) ;
00154 }
00155
00156 }
00157
00158 }
00159
00160
00161
00162 Sound::~Sound() throw()
00163 {
00164
00165 try
00166 {
00167
00168 if ( hasContent() )
00169 unload() ;
00170
00171 }
00172 catch( const Ceylan::LoadableException & e )
00173 {
00174
00175 LogPlug::error( "Sound destructor failed while unloading: "
00176 + e.toString() ) ;
00177
00178 }
00179
00180
00181
00182 }
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192 bool Sound::load()
00193 {
00194
00195 if ( hasContent() )
00196 return false ;
00197
00198 #if OSDL_ARCH_NINTENDO_DS
00199
00200 #ifdef OSDL_RUNS_ON_ARM7
00201
00202 throw Ceylan::LoadableException( "Sound::load failed: "
00203 "not supported on the ARM7" ) ;
00204
00205 #elif defined(OSDL_RUNS_ON_ARM9)
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215 try
00216 {
00217
00218 _content = new LowLevelSound() ;
00219
00220
00221
00222 File & soundFile = File::Open( _contentPath ) ;
00223
00224
00225 FileTag readTag = soundFile.readUint16() ;
00226
00227 if ( readTag != SoundTag )
00228 throw Ceylan::LoadableException(
00229 "Sound::load: expected sound tag not found ("
00230 + Ceylan::toString( SoundTag ) + "), read instead "
00231 + Ceylan::toString( readTag ) + ", which corresponds to: "
00232 + DescribeFileTag( readTag ) ) ;
00233
00234
00235 _content->_frequency = soundFile.readUint16() ;
00236
00237
00238
00239 SampleFormat readFormat = soundFile.readUint16() ;
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266 if ( readFormat == AudioModule::LittleSint16SampleFormat )
00267 _content->_bitDepth = 16 ;
00268 else if ( readFormat == AudioModule::Sint8SampleFormat )
00269 _content->_bitDepth = 8 ;
00270 else if ( readFormat == AudioModule::IMAADPCMSampleFormat )
00271 _content->_bitDepth = 4 ;
00272 else
00273 throw Ceylan::LoadableException( "Sound::load failed: "
00274 "unexpected bit depth (" + Ceylan::toString( readFormat )
00275 + ") read from sound file '" + _contentPath + "'" ) ;
00276
00277
00278
00279 ChannelFormat readMode = soundFile.readUint16() ;
00280
00281 if ( readMode != AudioModule::Mono
00282 && readMode != AudioModule::Stereo )
00283 throw Ceylan::LoadableException( "Sound::load failed: "
00284 "unexpected channel format read from sound file '"
00285 + _contentPath + "'" ) ;
00286
00287 _content->_mode = static_cast<Ceylan::Uint8>( readMode ) ;
00288
00289
00290 Ceylan::Uint32 readSize = soundFile.size() - 4*sizeof(Ceylan::Uint16) ;
00291
00292 LOG_DEBUG_AUDIO( "Sound::load: for '" + _contentPath
00293 + "', bit depth is "
00294 + Ceylan::toNumericalString( _content->_bitDepth )
00295 + ", channel mode is "
00296 + Ceylan::toNumericalString( _content->_mode )
00297 + ", frequency is " + Ceylan::toString( _content->_frequency )
00298 + " Hz, size of all samples is " + Ceylan::toString( readSize )
00299 + " bytes." ) ;
00300
00301 _content->_size = readSize ;
00302
00303
00304 _content->_samples = CacheProtectedNew( readSize ) ;
00305
00306 soundFile.readExactLength( _content->_samples, readSize ) ;
00307
00308
00309 DC_FlushRange( (void*) _content->_samples, readSize ) ;
00310
00311 delete & soundFile ;
00312
00313 }
00314 catch( const Ceylan::System::SystemException & e )
00315 {
00316
00317 throw Ceylan::LoadableException( "Sound::load failed: "
00318 + e.toString() ) ;
00319
00320 }
00321
00322 _convertedToOutputFormat = true ;
00323
00324 return true ;
00325
00326 #endif // OSDL_RUNS_ON_ARM7
00327
00328 #else // OSDL_ARCH_NINTENDO_DS
00329
00330 #if OSDL_USES_SDL_MIXER
00331
00332
00333
00334
00335
00336
00337
00338 if ( hasContent() )
00339 return false ;
00340
00341 try
00342 {
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355 Ceylan::System::File & soundFile = File::Open( _contentPath ) ;
00356
00357
00358
00359
00360
00361
00362
00363
00364 _dataStream = & Utils::createDataStreamFrom( soundFile ) ;
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385 _content = ::Mix_LoadWAV_RW( _dataStream,
00386 true ) ;
00387
00388
00389
00390
00391
00392 }
00393 catch( const Ceylan::Exception & e )
00394 {
00395
00396 throw Ceylan::LoadableException( "Sound::load failed: '"
00397 "unable to locate '" + _contentPath + "': " + e.toString() ) ;
00398
00399 }
00400
00401 if ( _content == 0 )
00402 throw Ceylan::LoadableException( "Sound::load failed: "
00403 + string( ::Mix_GetError() ) ) ;
00404
00405 _convertedToOutputFormat = true ;
00406
00407 return true ;
00408
00409 #else // OSDL_USES_SDL_MIXER
00410
00411 throw Ceylan::LoadableException(
00412 "Sound::load failed: no SDL_mixer support available." ) ;
00413
00414 #endif // OSDL_USES_SDL_MIXER
00415
00416 #endif // OSDL_ARCH_NINTENDO_DS
00417
00418 }
00419
00420
00421
00422 bool Sound::unload()
00423 {
00424
00425 if ( ! hasContent() )
00426 return false ;
00427
00428
00429
00430 #if OSDL_ARCH_NINTENDO_DS
00431
00432 #ifdef OSDL_RUNS_ON_ARM7
00433
00434 throw Ceylan::LoadableException( "Sound::unload failed: "
00435 "not supported on the ARM7" ) ;
00436
00437 #elif defined(OSDL_RUNS_ON_ARM9)
00438
00439 if ( _content->_samples != 0 )
00440 CacheProtectedDelete( _content->_samples ) ;
00441
00442 delete _content ;
00443
00444
00445 #endif // OSDL_RUNS_ON_ARM7
00446
00447 #else // OSDL_ARCH_NINTENDO_DS
00448
00449 #if OSDL_USES_SDL_MIXER
00450
00451 ::Mix_FreeChunk( _content ) ;
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475 _dataStream = 0 ;
00476
00477 #else // OSDL_USES_SDL_MIXER
00478
00479 throw Ceylan::LoadableException(
00480 "Sound::unload failed: no SDL_mixer support available." ) ;
00481
00482 #endif // OSDL_USES_SDL_MIXER
00483
00484 #endif // OSDL_ARCH_NINTENDO_DS
00485
00486 _content = 0 ;
00487
00488 return true ;
00489
00490 }
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500 Volume Sound::getVolume() const
00501 {
00502
00503 #if OSDL_USES_SDL_MIXER
00504
00505 try
00506 {
00507
00508 if ( hasContent() )
00509 return ::Mix_VolumeChunk( const_cast<LowLevelSound *>(
00510 & getExistingContentAsConst() ), -1 ) ;
00511 else
00512 throw SoundException(
00513 "Sound::getVolume failed: no loaded sound available." ) ;
00514
00515 }
00516 catch( const Ceylan::LoadableException & e )
00517 {
00518
00519 throw SoundException( "Sound::getVolume failed: " + e.toString() ) ;
00520
00521 }
00522
00523 #else // OSDL_USES_SDL_MIXER
00524
00525 throw SoundException(
00526 "Sound::getVolume failed: no SDL_mixer support available." ) ;
00527
00528 #endif // OSDL_USES_SDL_MIXER
00529
00530 }
00531
00532
00533
00534 void Sound::setVolume( Volume newVolume )
00535 {
00536
00537 #if OSDL_USES_SDL_MIXER
00538
00539 try
00540 {
00541
00542 if ( hasContent() )
00543 ::Mix_VolumeChunk( & getExistingContent(),
00544 static_cast<int>( newVolume ) ) ;
00545 else
00546 throw SoundException(
00547 "Sound::setVolume failed: no loaded sound available." ) ;
00548
00549 }
00550 catch( const Ceylan::LoadableException & e )
00551 {
00552
00553
00554 throw SoundException( "Sound::setVolume failed: " + e.toString() ) ;
00555
00556 }
00557
00558 #else // OSDL_USES_SDL_MIXER
00559
00560 throw SoundException(
00561 "Sound::setVolume failed: no SDL_mixer support available." ) ;
00562
00563 #endif // OSDL_USES_SDL_MIXER
00564
00565 }
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578 void Sound::play( PlaybackCount playCount )
00579 {
00580
00581 #if OSDL_ARCH_NINTENDO_DS
00582
00583 #ifdef OSDL_RUNS_ON_ARM7
00584
00585 throw SoundException( "Sound::play failed: not supported on the ARM7" ) ;
00586
00587 #elif defined(OSDL_RUNS_ON_ARM9)
00588
00589
00590 try
00591 {
00592
00593 CommandManager::GetExistingCommandManager().playSound( *this ) ;
00594
00595 }
00596 catch( const CommandException & e )
00597 {
00598
00599 throw SoundException( "Sound::play failed: " + e.toString() ) ;
00600
00601 }
00602
00603
00604 #endif // OSDL_RUNS_ON_ARM7
00605
00606 #else // OSDL_ARCH_NINTENDO_DS
00607
00608
00609 playReturnChannel( playCount ) ;
00610
00611 #endif // OSDL_ARCH_NINTENDO_DS
00612
00613 }
00614
00615
00616
00617 ChannelNumber Sound::playReturnChannel( PlaybackCount playCount )
00618 {
00619
00620 #if OSDL_USES_SDL_MIXER
00621
00622 if ( ! hasContent() )
00623 throw AudibleException( "Sound::playReturnChannel failed: "
00624 "no loaded sound available." ) ;
00625
00626 int channelNumber = ::Mix_PlayChannel(
00627 -1, _content,
00628 GetLoopsForPlayCount( playCount ) ) ;
00629
00630 if ( channelNumber == -1 )
00631 throw SoundException( "Sound::playReturnChannel failed: "
00632 + string( ::Mix_GetError() ) ) ;
00633 else
00634 return static_cast<ChannelNumber>( channelNumber ) ;
00635
00636 #else // OSDL_USES_SDL_MIXER
00637
00638 throw SoundException(
00639 "Sound::playReturnChannel failed: no SDL_mixer support available." ) ;
00640
00641 #endif // OSDL_USES_SDL_MIXER
00642
00643 }
00644
00645
00646
00647 void Sound::play( ChannelNumber mixingChannelNumber, PlaybackCount playCount )
00648 {
00649
00650 #if OSDL_USES_SDL_MIXER
00651
00652 if ( ! hasContent() )
00653 throw AudibleException( "Sound::play failed: "
00654 "no loaded sound available." ) ;
00655
00656 if ( ::Mix_PlayChannel( mixingChannelNumber, _content,
00657 GetLoopsForPlayCount( playCount ) ) == -1 )
00658 throw SoundException( "Sound::play failed: "
00659 + string( ::Mix_GetError() ) ) ;
00660
00661 #else // OSDL_USES_SDL_MIXER
00662
00663 throw SoundException(
00664 "Sound::play failed: no SDL_mixer support available." ) ;
00665
00666 #endif // OSDL_USES_SDL_MIXER
00667
00668 }
00669
00670
00671
00672
00673
00674
00675
00676
00677 void Sound::playForAtMost( Ceylan::System::Millisecond maxDuration,
00678 PlaybackCount playCount )
00679 {
00680
00681
00682 playForAtMostReturnChannel( maxDuration, playCount ) ;
00683
00684 }
00685
00686
00687
00688 ChannelNumber Sound::playForAtMostReturnChannel(
00689 Ceylan::System::Millisecond maxDuration, PlaybackCount playCount )
00690 {
00691
00692 #if OSDL_USES_SDL_MIXER
00693
00694 if ( ! hasContent() )
00695 throw AudibleException( "Sound::playForAtMostReturnChannel failed: "
00696 "no loaded sound available." ) ;
00697
00698 int channelNumber = ::Mix_PlayChannelTimed(
00699 -1, _content,
00700 GetLoopsForPlayCount( playCount ), maxDuration ) ;
00701
00702 if ( channelNumber == -1 )
00703 throw SoundException( "Sound::playForAtMostReturnChannel failed: "
00704 + string( ::Mix_GetError() ) ) ;
00705 else
00706 return static_cast<ChannelNumber>( channelNumber ) ;
00707
00708 #else // OSDL_USES_SDL_MIXER
00709
00710 throw SoundException( "Sound::playForAtMostReturnChannel failed: "
00711 "no SDL_mixer support available." ) ;
00712
00713 #endif // OSDL_USES_SDL_MIXER
00714
00715 }
00716
00717
00718
00719 void Sound::playForAtMost( Ceylan::System::Millisecond maxDuration,
00720 ChannelNumber mixingChannelNumber, PlaybackCount playCount )
00721 {
00722
00723 #if OSDL_USES_SDL_MIXER
00724
00725 if ( ! hasContent() )
00726 throw AudibleException( "Sound::playForAtMost failed: "
00727 "no loaded sound available." ) ;
00728
00729 if ( ::Mix_PlayChannelTimed( mixingChannelNumber, _content,
00730 GetLoopsForPlayCount( playCount ), maxDuration ) == -1 )
00731 throw SoundException( "Sound::playForAtMost failed: "
00732 + string( ::Mix_GetError() ) ) ;
00733
00734 #else // OSDL_USES_SDL_MIXER
00735
00736 throw SoundException( "Sound::playForAtMost failed: "
00737 "no SDL_mixer support available." ) ;
00738
00739 #endif // OSDL_USES_SDL_MIXER
00740
00741 }
00742
00743
00744
00745
00746
00747
00748
00749
00750 void Sound::playWithFadeIn( Ceylan::System::Millisecond fadeInMaxDuration,
00751 PlaybackCount playCount )
00752 {
00753
00754
00755 playWithFadeInReturnChannel( fadeInMaxDuration, playCount ) ;
00756
00757
00758 }
00759
00760
00761
00762 ChannelNumber Sound::playWithFadeInReturnChannel(
00763 Ceylan::System::Millisecond fadeInMaxDuration, PlaybackCount playCount )
00764 {
00765
00766 #if OSDL_USES_SDL_MIXER
00767
00768 if ( ! hasContent() )
00769 throw AudibleException( "Sound::playWithFadeInReturnChannel failed: "
00770 "no loaded sound available." ) ;
00771
00772 int channelNumber = ::Mix_FadeInChannel(
00773 -1, _content,
00774 GetLoopsForPlayCount( playCount ), fadeInMaxDuration ) ;
00775
00776 if ( channelNumber == -1 )
00777 throw SoundException( "Sound::playWithFadeInReturnChannel failed: "
00778 + string( ::Mix_GetError() ) ) ;
00779 else
00780 return static_cast<ChannelNumber>( channelNumber ) ;
00781
00782 #else // OSDL_USES_SDL_MIXER
00783
00784 throw SoundException( "Sound::playWithFadeInReturnChannel failed: "
00785 "no SDL_mixer support available." ) ;
00786
00787 #endif // OSDL_USES_SDL_MIXER
00788
00789 }
00790
00791
00792
00793 void Sound::playWithFadeIn( Ceylan::System::Millisecond fadeInMaxDuration,
00794 ChannelNumber mixingChannelNumber, PlaybackCount playCount )
00795 {
00796
00797 #if OSDL_USES_SDL_MIXER
00798
00799 if ( ! hasContent() )
00800 throw AudibleException( "Sound::playWithFadeIn failed: "
00801 "no loaded sound available." ) ;
00802
00803 if ( ::Mix_FadeInChannel( mixingChannelNumber, _content,
00804 GetLoopsForPlayCount( playCount ), fadeInMaxDuration ) == -1 )
00805 throw SoundException( "Sound::playWithFadeIn failed: "
00806 + string( ::Mix_GetError() ) ) ;
00807
00808 #else // OSDL_USES_SDL_MIXER
00809
00810 throw SoundException( "Sound::playWithFadeIn failed: "
00811 "no SDL_mixer support available." ) ;
00812
00813 #endif // OSDL_USES_SDL_MIXER
00814
00815
00816 }
00817
00818
00819
00820
00821
00822
00823
00824 void Sound::playWithFadeInForAtMost(
00825 Ceylan::System::Millisecond playbackMaxDuration,
00826 Ceylan::System::Millisecond fadeInMaxDuration,
00827 PlaybackCount playCount )
00828 {
00829
00830
00831 playWithFadeInForAtMostReturnChannel( playbackMaxDuration,
00832 fadeInMaxDuration, playCount ) ;
00833
00834 }
00835
00836
00837
00838 ChannelNumber Sound::playWithFadeInForAtMostReturnChannel(
00839 Ceylan::System::Millisecond playbackMaxDuration,
00840 Ceylan::System::Millisecond fadeInMaxDuration,
00841 PlaybackCount playCount )
00842 {
00843
00844 #if OSDL_USES_SDL_MIXER
00845
00846 if ( ! hasContent() )
00847 throw AudibleException(
00848 "Sound::playWithFadeInForAtMostReturnChannel failed: "
00849 "no loaded sound available." ) ;
00850
00851 int channelNumber = ::Mix_FadeInChannelTimed(
00852 -1, _content,
00853 GetLoopsForPlayCount( playCount ), fadeInMaxDuration,
00854 playbackMaxDuration ) ;
00855
00856 if ( channelNumber == -1 )
00857 throw SoundException(
00858 "Sound::playWithFadeInForAtMostReturnChannel failed: "
00859 + string( ::Mix_GetError() ) ) ;
00860 else
00861 return static_cast<ChannelNumber>( channelNumber ) ;
00862
00863 #else // OSDL_USES_SDL_MIXER
00864
00865 throw SoundException( "Sound::playWithFadeInForAtMostReturnChannel failed: "
00866 "no SDL_mixer support available." ) ;
00867
00868 #endif // OSDL_USES_SDL_MIXER
00869
00870 }
00871
00872
00873
00874 void Sound::playWithFadeInForAtMost(
00875 Ceylan::System::Millisecond playbackMaxDuration,
00876 Ceylan::System::Millisecond fadeInMaxDuration,
00877 ChannelNumber mixingChannelNumber,
00878 PlaybackCount playCount )
00879 {
00880
00881 #if OSDL_USES_SDL_MIXER
00882
00883 if ( ! hasContent() )
00884 throw AudibleException( "Sound::playWithFadeInForAtMost failed: "
00885 "no loaded sound available." ) ;
00886
00887 if ( ::Mix_FadeInChannelTimed( mixingChannelNumber, _content,
00888 GetLoopsForPlayCount( playCount ), fadeInMaxDuration,
00889 playbackMaxDuration ) == -1 )
00890 throw SoundException( "Sound::playWithFadeInForAtMost failed: "
00891 + string( ::Mix_GetError() ) ) ;
00892
00893 #else // OSDL_USES_SDL_MIXER
00894
00895 throw SoundException( "Sound::playWithFadeInForAtMost failed: "
00896 "no SDL_mixer support available." ) ;
00897
00898 #endif // OSDL_USES_SDL_MIXER
00899
00900 }
00901
00902
00903
00904 const string Sound::toString( Ceylan::VerbosityLevels level ) const
00905 {
00906
00907 if ( level == Ceylan::low )
00908 return ( hasContent() ? string( "Loaded" ) : string( "Not loaded" ) )
00909 + " sound, whose content path is '" + _contentPath + "'" ;
00910
00911
00912 try
00913 {
00914
00915 if ( hasContent() )
00916 {
00917
00918 Volume v = getVolume() ;
00919
00920 return "Loaded sound whose volume is "
00921 + Ceylan::toNumericalString( v )
00922 + " (" + Ceylan::toString( 100 * v
00923 / ( AudioModule::MaxVolume - AudioModule::MinVolume ) )
00924 + "%)" ;
00925
00926 }
00927 else
00928 {
00929
00930 return "Sound currently not loaded, whose content path is '"
00931 + _contentPath + "'" ;
00932
00933 }
00934
00935 }
00936 catch( const Ceylan::Exception & e )
00937 {
00938
00939 return "Sound::toString failed (abnormal): " + e.toString() ;
00940
00941 }
00942
00943 }
00944