00001 /* 00002 libwt - Vassilis Virvilis Toolkit - a widget library 00003 Copyright (C) 2006 Vassilis Virvilis <vasvir2@fastmail.fm> 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 00017 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00018 Boston, MA 02111-1307, SA. 00019 */ 00020 00021 #include <SDL/SDL.h> 00022 00023 #include <boost/assign/list_of.hpp> // for 'map_list_of()' 00024 00025 #include "sdlmixer.h" 00026 #include <wt/trace.h> 00027 00028 namespace Wt { 00029 00030 Factory<Audio, SDLMixer> SDLMixer::factory; 00031 00032 FlagMap SDLMixer::fadingTypeMap; 00033 FlagMap SDLMixer::musicTypeMap; 00034 00035 SDLMixer::SDLMixer() 00036 : Audio() { 00037 00038 // we can't reliably initialize staticly since we have to make 00039 // sure the enumeration is initialized first and this depends 00040 // from the linking order (static initialization fiasco) 00041 fadingTypeMap = boost::assign::map_list_of 00042 (MIX_NO_FADING, FadingType::None) 00043 (MIX_FADING_OUT, FadingType::Out) 00044 (MIX_FADING_IN, FadingType::In); 00045 00046 musicTypeMap = boost::assign::map_list_of 00047 (MUS_NONE, MusicType::None) 00048 (MUS_CMD, MusicType::Cmd) 00049 (MUS_WAV, MusicType::WAV) 00050 (MUS_MOD, MusicType::MOD) 00051 (MUS_MID, MusicType::MID) 00052 (MUS_OGG, MusicType::OGG) 00053 (MUS_MP3, MusicType::MP3); 00054 00055 adjustAllocatedChannels<SDLMixer>(0, Mix_AllocateChannels(-1)); 00056 Mix_ChannelFinished(onChannelFinish); 00057 Mix_HookMusicFinished(onMusicFinish); 00058 } 00059 00060 SDLMixer::~SDLMixer() { 00061 trace("sdlmixer", "Entering Destructor for SDLMixer\n"); 00062 allocateChannels(0); 00063 Mix_CloseAudio(); 00064 } 00065 00066 Audio* SDLMixer::load() { 00067 trace("sdlmixer", "Initializing SDL audio...\n"); 00068 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { 00069 trace("sdlmixer", "Initialize SDL audio: %s\n", SDL_GetError()); 00070 goto fail; 00071 } 00072 00073 int audio_rate, audio_channels; 00074 Uint16 audio_format; 00075 int chunksize; 00076 00077 audio_format = MIX_DEFAULT_FORMAT; 00078 audio_channels = MIX_DEFAULT_CHANNELS; 00079 00080 //undef this (HIFI) to reduce CPU by a 5% 00081 audio_rate = MIX_DEFAULT_FREQUENCY; 00082 chunksize = 1024; 00083 //audio_rate = 44100; 00084 //chunksize = 2048; 00085 00086 /* Open the audio device */ 00087 if (Mix_OpenAudio(audio_rate, audio_format, 00088 audio_channels, chunksize) < 0) { 00089 trace("sdlmixer", "Initializing SDLMixer failed: %s\n", SDL_GetError()); 00090 SDL_QuitSubSystem(SDL_INIT_AUDIO); 00091 goto fail; 00092 } 00093 00094 Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels); 00095 trace("sdlmixer") << "Opened audio at "<< audio_rate << 00096 " Hz " << (audio_format & 0xFF) << "bit " << 00097 ((audio_channels > 1) ? "stereo" : "mono") << std::endl; 00098 00099 trace("audio", "SDLMixer initialization...ok\n"); 00100 return new SDLMixer(); 00101 00102 fail: 00103 trace("audio", "SDLMixer initialization...fail\n"); 00104 return 0; 00105 } 00106 00107 Audio::Chunk* SDLMixer::loadChunk(const std::string& filename) const { 00108 Mix_Chunk *chunk = Mix_LoadWAV(filename.c_str()); 00109 return (chunk) ? new SDLMixer::Chunk(chunk) : 0; 00110 } 00111 00112 int SDLMixer::allocateChannels(int num_channels) { 00113 const int oldnum = numChannels(); 00114 assert(Mix_AllocateChannels(-1) == oldnum); 00115 00116 if (num_channels == oldnum || num_channels < 0) 00117 return oldnum; 00118 00119 adjustAllocatedChannels<SDLMixer>(oldnum, 00120 Mix_AllocateChannels(num_channels)); 00121 00122 return numChannels(); 00123 } 00124 00125 void SDLMixer::pause() { 00126 Mix_Pause(-1); 00127 } 00128 00129 void SDLMixer::resume() { 00130 Mix_Resume(-1); 00131 } 00132 00133 void SDLMixer::stop(int ms) { 00134 (ms) ? Mix_ExpireChannel(-1, ms) : 00135 Mix_HaltChannel(-1); 00136 } 00137 00138 void SDLMixer::fadeOut(int ms) { 00139 Mix_FadeOutChannel(-1, ms); 00140 } 00141 00142 int SDLMixer::playingChannels() const { 00143 return Mix_Playing(-1); 00144 } 00145 00146 int SDLMixer::pausedChannels() const { 00147 return Mix_Paused(-1); 00148 } 00149 00150 bool SDLMixer::isAvailable() const { 00151 return true; 00152 } 00153 00154 // ************* SDLMixer::Music ***************** 00155 Audio::Music *SDLMixer::loadMusic(const std::string& filename) { 00156 Mix_Music *m = Mix_LoadMUS(filename.c_str()); 00157 return (m) ? new Music(m) : 0; 00158 } 00159 00160 SDLMixer::Music::Music(Mix_Music *music_p) 00161 : Audio::Music(), 00162 music_p(music_p) { 00163 trace("sdlmixer", "SDLMixer::Music::Music() constructor called\n"); 00164 } 00165 00166 SDLMixer::Music::~Music() { 00167 Mix_FreeMusic(music_p); 00168 } 00169 00170 /// it blocks until any previously music fading out is done 00171 void SDLMixer::playMusic(Audio::Music& music) { 00172 SDLMixer::Music& m = * static_cast<SDLMixer::Music *>(&music); 00173 trace("sdlmixer", "SDLMixer::playMusic() called\n"); 00174 Mix_PlayMusic(m, musicLoops - 1); 00175 } 00176 00177 void SDLMixer::fadeInMusic(Audio::Music& music, int ms) { 00178 SDLMixer::Music& m = * static_cast<SDLMixer::Music *>(&music); 00179 Mix_FadeInMusic(m, musicLoops - 1, ms); 00180 } 00181 00182 void SDLMixer::fadeOutMusic(int ms) { 00183 Mix_FadeOutMusic(ms); 00184 } 00185 00186 void SDLMixer::setMusicVolume(int volume) { 00187 SDLMixer& self = * static_cast<SDLMixer *>(instance()); 00188 int hwvol = self.HWVolume(volume); 00189 Mix_VolumeMusic(hwvol); 00190 } 00191 00192 void SDLMixer::pauseMusic() { 00193 Mix_PauseMusic(); 00194 } 00195 00196 void SDLMixer::rewindMusic() { 00197 Mix_RewindMusic(); 00198 } 00199 00200 bool SDLMixer::setMusicPosition(double start_sec) { 00201 return !Mix_SetMusicPosition(start_sec); 00202 } 00203 00204 void SDLMixer::stopMusic() { 00205 Mix_HaltMusic(); 00206 } 00207 00208 bool SDLMixer::playingMusic() const { 00209 return Mix_PlayingMusic(); 00210 } 00211 00212 bool SDLMixer::pausedMusic() const { 00213 return Mix_PausedMusic(); 00214 } 00215 00216 int SDLMixer::musicFading() const { 00217 return fadingTypeMap[Mix_FadingMusic()]; 00218 } 00219 00220 int SDLMixer::Music::type() const { 00221 return musicTypeMap[Mix_GetMusicType(music_p)]; 00222 } 00223 00224 int SDLMixer::minHWVolume() const { 00225 return 0; 00226 } 00227 00228 int SDLMixer::maxHWVolume() const { 00229 return MIX_MAX_VOLUME; 00230 } 00231 00232 void SDLMixer::onChannelFinish(int channel_id) { 00233 Audio::onChannelFinish(channel_id); 00234 // if this function is called SDLMixer is running 00235 // so all static casts are safe 00236 SDLMixer& self = * static_cast<SDLMixer *>(instance()); 00237 SDLMixer::Channel *channel = 00238 static_cast<SDLMixer::Channel *>(self.channels[channel_id]); 00239 SDLMixer::Chunk *chunk = static_cast<SDLMixer::Chunk *>(channel->chunk()); 00240 channel->chunk_p = 0; 00241 chunk->channel_id = -1; 00242 } 00243 00244 // ********* SDLMixer::Chunk *************** 00245 00246 SDLMixer::Chunk::Chunk(Mix_Chunk *mix_chunk_p) 00247 : Audio::Chunk(), 00248 chunk_p(mix_chunk_p, Deleter()), 00249 channel_id(-1) { 00250 trace("sdlmixer", 00251 "SDLMixer::Chunk Creating chunk %p (%p)\n", this, mix_chunk_p); 00252 setVolume(Audio::maxVolume); 00253 } 00254 00255 SDLMixer::Chunk::~Chunk() { 00256 trace("sdlmixer", 00257 "SDLMixer::~Chunk Stoping chunk %p (%p)\n", 00258 this, chunk_p.get()); 00259 stop(); 00260 } 00261 00262 void SDLMixer::Chunk::setVolume(int volume) { 00263 SDLMixer& self = * static_cast<SDLMixer *>(instance()); 00264 int hwvol = self.HWVolume(volume); 00265 detach(); 00266 Mix_VolumeChunk(*this, hwvol); 00267 } 00268 00269 void SDLMixer::Chunk::play(int duration) { 00270 detach(); 00271 channel_id = Mix_PlayChannelTimed(-1, *this, loops - 1, duration); 00272 if (channel_id >= 0) 00273 Audio::Chunk::channel<SDLMixer>()->chunk_p = this; 00274 } 00275 00276 void SDLMixer::Chunk::fadeIn(int ms, int duration) { 00277 detach(); 00278 channel_id = Mix_FadeInChannelTimed(-1, *this, loops - 1, ms, duration); 00279 if (channel_id >= 0) 00280 Audio::Chunk::channel<SDLMixer>()->chunk_p = this; 00281 } 00282 00283 void SDLMixer::Chunk::fadeOut(int ms) { 00284 if (channel_id < 0) 00285 return; 00286 Mix_FadeOutChannel(channel_id, ms); 00287 } 00288 00289 void SDLMixer::Chunk::pause() { 00290 if (channel_id < 0) 00291 return; 00292 Mix_Pause(channel_id); 00293 } 00294 00295 void SDLMixer::Chunk::resume() { 00296 if (channel_id < 0) 00297 return; 00298 Mix_Resume(channel_id); 00299 } 00300 00301 void SDLMixer::Chunk::stop(int ms) { 00302 if (channel_id < 0) 00303 return; 00304 (ms) ? Mix_ExpireChannel(channel_id, ms) : 00305 Mix_HaltChannel(channel_id); 00306 } 00307 00308 bool SDLMixer::Chunk::playing() const { 00309 return (channel_id < 0) ? false : Mix_Playing(channel_id); 00310 } 00311 00312 bool SDLMixer::Chunk::paused() const { 00313 return (channel_id < 0) ? false : Mix_Paused(channel_id); 00314 } 00315 00316 Audio::Channel *SDLMixer::Chunk::channel() const { 00317 SDLMixer& self = * static_cast<SDLMixer *>(instance()); 00318 return (channel_id < 0) ? 0 : self.channels[channel_id]; 00319 } 00320 00321 static Mix_Chunk *clone_chunk(const Mix_Chunk *chunk_p) { 00322 Mix_Chunk *new_chunk_p = static_cast<Mix_Chunk *>(malloc(sizeof(Mix_Chunk))); 00323 *new_chunk_p = *chunk_p; 00324 new_chunk_p->allocated = 0; 00325 trace("sdlmixer", "clone_chunk %p --> %p\n", chunk_p, new_chunk_p); 00326 return new_chunk_p; 00327 } 00328 00329 void SDLMixer::Chunk::Deleter::operator()(Mix_Chunk *c) { 00330 trace("sdlmixer", 00331 "SDLMixer::Chunk::Deleter Deleting Mix_Chunk (%p)\n", c); 00332 Mix_FreeChunk(c); 00333 } 00334 00335 Audio::Chunk *SDLMixer::Chunk::clone() const { 00336 trace("sdlmixer", "SDLMixer::Chunk::clone %p\n", this); 00337 return new Chunk(clone_chunk(*this)); 00338 } 00339 00340 void SDLMixer::Chunk::detach() { 00341 trace("sdlmixer", "SDLMixer::Chunk::detach %p\n", this); 00342 //avoid detaching if we are the only client for this Sound 00343 if (!chunk_p.unique()) { 00344 trace("sdlmixer", "SDLMixer::Chunk::detaching %p\n", this); 00345 chunk_p.reset(clone_chunk(*this), Deleter()); 00346 } 00347 } 00348 00349 // ********* SDLMixer::Channel *************** 00350 00351 SDLMixer::Channel::Channel(int id) : 00352 Audio::Channel(), 00353 chunk_p(0), 00354 channel_id(id) {} 00355 00356 void SDLMixer::Channel::setVolume(int volume) { 00357 SDLMixer& self = * static_cast<SDLMixer *>(instance()); 00358 int hwvol = self.HWVolume(volume); 00359 Mix_Volume(channel_id, hwvol); 00360 } 00361 00362 void SDLMixer::Channel::play(Audio::Chunk& chunk, int duration) { 00363 trace("sdlmixer", "SDLMixer::Channel::play()\n"); 00364 SDLMixer::Chunk& c = * static_cast<SDLMixer::Chunk *>(&chunk); 00365 int status = Mix_PlayChannelTimed(channel_id, c, 00366 loops - 1, duration); 00367 if (!status) 00368 chunk_p = &c; 00369 } 00370 00371 void SDLMixer::Channel::fadeIn(Audio::Chunk& chunk, int ms, int duration) { 00372 SDLMixer::Chunk& c = * static_cast<SDLMixer::Chunk *>(&chunk); 00373 int status = Mix_FadeInChannelTimed(channel_id, c, 00374 loops - 1, ms, duration); 00375 if (!status) 00376 chunk_p = &c; 00377 } 00378 00379 void SDLMixer::Channel::fadeOut(int ms) { 00380 Mix_FadeOutChannel(channel_id, ms); 00381 } 00382 00383 void SDLMixer::Channel::pause() { 00384 Mix_Pause(channel_id); 00385 } 00386 00387 void SDLMixer::Channel::resume() { 00388 Mix_Resume(channel_id); 00389 } 00390 00391 void SDLMixer::Channel::stop(int ms) { 00392 (ms) ? Mix_ExpireChannel(channel_id, ms) : 00393 Mix_HaltChannel(channel_id); 00394 } 00395 00396 bool SDLMixer::Channel::playing() const { 00397 return Mix_Playing(channel_id); 00398 } 00399 00400 bool SDLMixer::Channel::paused() const { 00401 return Mix_Paused(channel_id); 00402 } 00403 00404 int SDLMixer::Channel::fading() const { 00405 return fadingTypeMap[Mix_FadingChannel(channel_id)]; 00406 } 00407 00408 Audio::Chunk *SDLMixer::Channel::chunk() const { 00409 return chunk_p; 00410 } 00411 00412 } // namespace Wt
This document is licensed under the terms of the GNU Free Documentation License and may be freely distributed under the conditions given by this license.