00001 00005 /* Copyright © 2010 James Legg. 00006 This program is free software: you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation, either version 3 of the License, or 00009 (at your option) any later version. 00010 */ 00011 00012 #include "Audio.h" 00013 00014 #include <iostream> 00015 #include <set> 00016 #include <algorithm> 00017 00018 #include <Debug.h> 00019 00020 namespace Engine 00021 { 00022 00023 Audio::Audio() 00024 // Scale is about 1 distance unit = 6 meters. 00025 // so speed of sound in air ~ 330 m/s / 6 = 55 units per second. 00026 // in space the speed of sound should be much lower, but it sounds weird. 00027 : m_speed_of_sound (300.0) 00028 , m_paused(false) 00029 { 00030 // Open the default audio device 00031 alutInit(0, 0); 00032 alDistanceModel(AL_NONE); 00033 // Allocate some ALSoundSources. 00034 for (unsigned int i = 0; i < 30; i++) 00035 { 00036 try 00037 { 00038 m_al_sources.push_back(boost::shared_ptr<ALSoundSource>(new ALSoundSource)); 00039 } 00040 catch (AudioError t) 00041 { 00042 // Probably hit some hardware limit. 00043 // Nothing to worry about, we will just have less 00044 // simultaneously playing sounds than normal. 00045 return; 00046 } 00047 } 00048 00049 } 00050 00051 Audio::~Audio() 00052 { 00053 alutExit(); 00054 } 00055 00056 Audio & Audio::get_instance() 00057 { 00058 static Audio * instance = new Audio(); 00059 return *instance; 00060 } 00061 00062 void Audio::pause() 00063 { 00064 m_paused = true; 00065 // pause all playing sources 00066 alcSuspendContext(alcGetCurrentContext()); 00067 for (std::vector<boost::shared_ptr<ALSoundSource> >::iterator it = m_al_sources.begin(); 00068 it != m_al_sources.end(); 00069 it++) 00070 { 00071 ALuint name = (**it).get_name(); 00072 ALint state; 00073 alGetSourcei(name, AL_SOURCE_STATE, &state); 00074 if (state == AL_PLAYING) 00075 { 00076 alSourcePause(name); 00077 } 00078 } 00079 alcProcessContext(alcGetCurrentContext()); 00080 } 00081 00082 void Audio::resume() 00083 { 00084 m_paused = false; 00085 // resume all paused sources. 00086 alcSuspendContext(alcGetCurrentContext()); 00087 for (std::vector<boost::shared_ptr<ALSoundSource> >::iterator it = m_al_sources.begin(); 00088 it != m_al_sources.end(); 00089 it++) 00090 { 00091 ALuint name = (**it).get_name(); 00092 ALint state; 00093 alGetSourcei(name, AL_SOURCE_STATE, &state); 00094 if (state == AL_PAUSED) 00095 { 00096 alSourcePlay(name); 00097 } 00098 } 00099 alcProcessContext(alcGetCurrentContext()); 00100 } 00101 00102 bool Audio::get_paused() 00103 { 00104 return m_paused; 00105 } 00106 00107 class SoundInstancePtrGainCompare 00108 { 00109 public: 00110 bool operator () (SoundInstance * sound_instance_1, 00111 SoundInstance * sound_instance_2) 00112 { 00113 return sound_instance_1->get_gain() < 00114 sound_instance_2->get_gain(); 00115 } 00116 }; 00117 00118 void Audio::commit() 00119 { 00120 // Update each ALSoundSource's values to match listener and source 00121 std::vector<boost::shared_ptr<SoundInstance> >::iterator instance_it = m_instances.begin(); 00122 std::vector<SoundListener *>::iterator listener_it; 00123 std::vector<SoundSource *>::iterator source_it; 00124 // This will store the sounds we want to be heard. 00125 std::multiset<SoundInstance *, SoundInstancePtrGainCompare> audiable_sounds; 00126 00127 // Anything at this gain or below is not mixed. 00128 ALfloat min_gain = 0.000976562; 00129 for (source_it = m_sources.begin(); source_it != m_sources.end(); source_it++) 00130 { 00131 SoundSource & source = **source_it; 00132 for (listener_it = m_listeners.begin(); listener_it != m_listeners.end(); listener_it++) 00133 { 00134 SoundListener & listener = **listener_it; 00135 SoundInstance & instance = (**instance_it); 00136 ALfloat distance = listener.m_position.distance(source.m_position); 00137 // gain has inverse attenuation. 00138 // The gain behaviour in openAL makes the inverse square rule wrong. 00139 ALfloat gain = source.m_gain/distance; 00140 instance.set_gain(gain); 00141 00142 // find the position local to the viewer. 00143 btVector3 position = listener.m_inverse_transform(source.m_position); 00144 instance.set_position(position); 00145 00146 // find the pitch from the relative velocity 00147 btVector3 relative_position = source.m_position - listener.m_position; 00148 ALfloat s_listener = relative_position.dot(source.m_velocity); 00149 ALfloat s_source = relative_position.dot(listener.m_velocity); 00150 ALfloat pitch = (m_speed_of_sound + s_listener) / (m_speed_of_sound + s_source) * source.m_pitch; 00151 if (pitch <= 0) pitch = 0.001; 00152 instance.set_pitch(pitch); 00153 00154 instance.set_looping(source.m_looping); 00155 00156 // playing state. 00157 // Non looping sounds that have to low a gain at the start are 00158 // skipped. 00159 if ( source.m_playing 00160 && !source.m_started 00161 && (source.m_looping || gain > min_gain)) 00162 { 00163 instance.play(); 00164 } 00165 else if (!(source.m_playing) || !(source.m_looping || source.m_started)) 00166 { 00167 instance.stop(); 00168 } 00169 00170 if (instance.get_playing() && gain > min_gain) 00171 { 00172 // should be audiable. 00173 audiable_sounds.insert(&instance); 00174 } 00175 00176 instance_it++; 00177 } 00178 // has been started for all listeners, it shouldn't be restarted next 00179 // time as that would cause it to play from the beginning again if it 00180 // wasn't supposed to loop. 00181 if (source.m_playing) source.m_started = true; 00182 } 00183 00184 // queue up the changes and submit them all at once 00185 // (if supported, otherwise this is a no-op.) 00186 alcSuspendContext(alcGetCurrentContext()); 00187 00188 // We want to play the loudest m_al_sources.size() sounds. 00189 unsigned int count = 0; 00190 std::vector<bool> available(m_al_sources.size(), true); 00191 std::vector<SoundInstance *> remaining; 00192 // update the loudest m_al_sources.size() sounds that have already been 00193 // assigned an ALSoundSource. 00194 std::multiset<SoundInstance *, SoundInstancePtrGainCompare>::reverse_iterator it = audiable_sounds.rbegin(); 00195 for (; 00196 it != audiable_sounds.rend() && count < m_al_sources.size(); 00197 it++, count++) 00198 { 00199 if ((**it).get_assigned()) 00200 { 00201 (**it).update_al(); 00202 available[(**it).get_index()] = false; 00203 } else { 00204 // We need to find a less audiable sound to replace. 00205 remaining.push_back(*it); 00206 } 00207 } 00208 // The remaining non looping sound instances should be stopped, so that 00209 // they don't play out of time when a ALSoundSource becomes available. 00210 for (; it != audiable_sounds.rend(); it++) 00211 { 00212 if (!(**it).get_looping()) 00213 { 00214 (**it).stop(); 00215 } 00216 } 00217 // Reassign ALSoundSources for the remaining SoundInstances. 00218 unsigned int alindex = 0; 00219 for (std::vector<SoundInstance *>::iterator it = remaining.begin(); 00220 it != remaining.end(); it++) 00221 { 00222 while (!available[alindex]) {alindex++;} 00223 assert(alindex < m_al_sources.size()); 00224 (**it).assign(*(m_al_sources[alindex]), alindex); 00225 available[alindex] = false; 00226 } 00227 // unassign any remaining sources. 00228 while (alindex < m_al_sources.size()) 00229 { 00230 m_al_sources[alindex]->unassign(); 00231 alindex++; 00232 } 00233 00234 alcProcessContext(alcGetCurrentContext()); 00235 00236 // We could do this after every call, but it costs a lot in performance. 00237 ALenum error = alGetError(); 00238 if (error != AL_NO_ERROR) 00239 { 00240 std::cerr << "OpenAL error: " 00241 << alutGetErrorString(error) << "\n"; 00242 DEBUG_MESSAGE("here."); 00243 throw AudioError(); 00244 } 00245 } 00246 00247 void Audio::attach_listner(SoundListener * listener) 00248 { 00249 // add a OpenAL source for each SoundSource. 00250 // There should be one after each listener group of the existing ones. 00251 std::size_t frequency = m_listeners.size(); 00252 m_instances.reserve((frequency+1)*m_instances.size()); 00253 std::vector<boost::shared_ptr<SoundInstance> >::iterator it = m_instances.begin(); 00254 for (std::size_t i = 0; i < m_sources.size(); i++) 00255 { 00256 it += frequency; 00257 it = m_instances.insert(it, 00258 boost::shared_ptr<SoundInstance>(new SoundInstance(m_sources[i]->get_buffer().get_al_buffer()))); 00259 it++; 00260 } 00261 m_listeners.push_back(listener); 00262 } 00263 00271 void Audio::attach_source(SoundSource * source) 00272 { 00273 // add an OpenAL source for each SoundListener 00274 // they can all go at the end of the existing vector. 00275 m_sources.push_back(source); 00276 00277 std::size_t amount = m_listeners.size(); 00278 ALint al_buffer = source->get_buffer().get_al_buffer(); 00279 for (std::size_t i = 0; i < amount; i++) 00280 { 00281 m_instances.push_back(boost::shared_ptr<SoundInstance>(new SoundInstance(al_buffer))); 00282 } 00283 assert(m_sources.size() * m_listeners.size() == m_instances.size()); 00284 } 00285 00286 void Audio::forget_listener(SoundListener * listener) 00287 { 00288 // remove an openAL source for each SoundSource 00289 // They are spaced evenly 00290 // It is more efficient to remove them backwards, since less will be moved 00291 // for the deletes near the beginning of the list. 00292 std::size_t frequency = m_listeners.size(); 00293 00294 // The index of the listner is used as offsets in m_alsources too. 00295 std::size_t index = 0; 00296 std::vector<SoundListener *>::iterator l_it; 00297 for (l_it = m_listeners.begin(); *l_it != listener; l_it++) 00298 { 00299 index++; 00300 } 00301 00302 for (std::size_t i = m_sources.size() - 1; i != std::size_t(-1) ; i--) 00303 { 00304 std::vector<boost::shared_ptr<SoundInstance> >::iterator it = 00305 m_instances.begin() + (index + frequency * i); 00306 m_instances.erase(it); 00307 } 00308 00309 m_listeners.erase(l_it); 00310 assert(m_sources.size() * m_listeners.size() == m_instances.size()); 00311 00312 // commit to this, it will stop ALSoundSources using buffers that could 00313 // be deleted. 00314 commit(); 00315 } 00316 00317 void Audio::forget_source(SoundSource * source) 00318 { 00319 // remove a SoundSource for each SoundListner 00320 std::size_t index = 0; 00321 std::vector<SoundSource *>::iterator source_it; 00322 for (source_it = m_sources.begin(); *source_it != source; source_it++) 00323 { 00324 index++; 00325 } 00326 std::size_t scale = m_listeners.size(); 00327 m_instances.erase(m_instances.begin()+scale*index, 00328 m_instances.begin()+scale*(index+1)); 00329 m_sources.erase(source_it); 00330 assert(m_sources.size() * m_listeners.size() == m_instances.size()); 00331 } 00332 00333 SoundBuffer::SoundBuffer(const char * filename) 00334 { 00335 // make sure audio has been initialised 00336 Audio::get_instance(); 00337 m_buffer = alutCreateBufferFromFile (filename); 00338 if (m_buffer == AL_NONE) 00339 { 00340 std::cerr << "Cannot create SoundBuffer:\nalutCreateBufferFromFile(" 00341 << filename << ") caused: " 00342 << alutGetErrorString(alutGetError()) << "\n"; 00343 DEBUG_MESSAGE("here."); 00344 throw AudioError(); 00345 } 00346 } 00347 00348 SoundBuffer::~SoundBuffer() 00349 { 00350 alDeleteBuffers(1, &m_buffer); 00351 ALenum error = alGetError(); 00352 if (error != AL_NO_ERROR) 00353 { 00354 std::cerr << "Cannot delete SoundBuffer:\nalDeleteBuffers caused: " 00355 << alutGetErrorString(error) << "\n"; 00356 DEBUG_MESSAGE("here."); 00357 throw AudioError(); 00358 } 00359 } 00360 00361 ALuint SoundBuffer::get_al_buffer() const 00362 { 00363 return m_buffer; 00364 } 00365 00366 SoundListener::SoundListener() 00367 : m_position(btVector3(0, 0, 0)) 00368 , m_forward(btVector3(0, 1, 0)) 00369 , m_up(btVector3(0, 0, 1)) 00370 , m_velocity(btVector3(0, 0, 0)) 00371 { 00372 Audio::get_instance().attach_listner(this); 00373 } 00374 00375 SoundListener::~SoundListener() 00376 { 00377 Audio::get_instance().forget_listener(this); 00378 } 00379 00380 void SoundListener::set_position(btVector3 position, btVector3 forward, btVector3 up) 00381 { 00382 m_position = position; 00383 m_forward = forward; 00384 m_up = up; 00385 btScalar matrix[16]; 00386 forward.normalize(); 00387 up.normalize(); 00388 btVector3 cross = forward.cross(up); 00389 matrix[0] = cross.x(); matrix[1] = up.x(); matrix[2] = -forward.x(); matrix[3] = 1.0; 00390 matrix[4] = cross.y(); matrix[5] = up.y(); matrix[6] = -forward.y(); matrix[7] = 1.0; 00391 matrix[8] = cross.z(); matrix[9] = up.z(); matrix[10] = -forward.z(); matrix[11] = 1.0; 00392 matrix[12] = position.x(); matrix[13] = position.y(); matrix[14] = position.z(); 00393 matrix[15] = 1.0; 00394 00395 m_inverse_transform.setFromOpenGLMatrix(matrix); 00396 m_inverse_transform = m_inverse_transform.inverse(); 00397 } 00398 00399 void SoundListener::set_velocity(btVector3 velocity) 00400 { 00401 m_velocity = velocity; 00402 } 00403 00404 SoundSource::SoundSource(const SoundBuffer & buffer) 00405 : m_buffer(buffer) 00406 , m_looping(false) 00407 , m_gain(1.0) 00408 , m_pitch(1.0) 00409 , m_position(btVector3(0, 0, 0)) 00410 , m_velocity(btVector3(0, 0, 0)) 00411 , m_playing(false) 00412 , m_started(false) 00413 { 00414 Audio::get_instance().attach_source(this); 00415 } 00416 00417 SoundSource::~SoundSource() 00418 { 00419 Audio::get_instance().forget_source(this); 00420 } 00421 00422 void SoundSource::set_looping(bool loop) 00423 { 00424 m_looping = loop; 00425 } 00426 00427 bool SoundInstance::get_looping() 00428 { 00429 return m_looping; 00430 } 00431 00432 void SoundSource::set_position(btVector3 position) 00433 { 00434 m_position = position; 00435 } 00436 00437 void SoundSource::set_velocity(btVector3 velocity) 00438 { 00439 m_velocity = velocity; 00440 } 00441 00442 void SoundSource::set_gain(ALfloat gain) 00443 { 00444 m_gain = gain; 00445 } 00446 00447 void SoundSource::set_pitch(ALfloat pitch) 00448 { 00449 m_pitch = pitch; 00450 } 00451 00452 void SoundSource::play() 00453 { 00454 m_playing = true; 00455 m_started = false; 00456 } 00457 00458 void SoundSource::stop() 00459 { 00460 m_playing = false; 00461 } 00462 00463 const SoundBuffer & SoundSource::get_buffer() const 00464 { 00465 return m_buffer; 00466 } 00467 00468 SoundInstance::SoundInstance(ALuint buffer) 00469 : m_source(0) 00470 , m_buffer(buffer) 00471 , m_gain(1) 00472 , m_pitch(1) 00473 , m_position(btVector3(0, 0, 0)) 00474 , m_looping(false) 00475 , m_playing(false) 00476 , m_assigning(false) 00477 { 00478 } 00479 00480 bool SoundInstance::get_assigned() 00481 { 00482 return bool(m_source); 00483 } 00484 00485 ALuint SoundInstance::get_buffer() 00486 { 00487 return m_buffer; 00488 } 00489 00490 void SoundInstance::assign(ALSoundSource & source, unsigned int index) 00491 { 00492 m_source = &source; 00493 m_source_index = index; 00494 00495 // this will call update_al. 00496 m_assigning = true; 00497 m_source->assign(*this); 00498 m_assigning = false; 00499 } 00500 00501 void SoundInstance::unassign() 00502 { 00503 m_source = 0; 00504 } 00505 00506 void SoundInstance::set_gain(ALfloat gain) 00507 { 00508 m_gain = gain; 00509 } 00510 00511 ALfloat SoundInstance::get_gain() 00512 { 00513 return m_gain; 00514 } 00515 00516 void SoundInstance::set_pitch(ALfloat pitch) 00517 { 00518 m_pitch = pitch; 00519 } 00520 00521 void SoundInstance::set_position(btVector3 position) 00522 { 00523 m_position = position; 00524 } 00525 00526 void SoundInstance::set_looping(bool looping) 00527 { 00528 m_looping = looping; 00529 } 00530 00531 00532 void SoundInstance::play() 00533 { 00534 m_playing = true; 00535 } 00536 00537 void SoundInstance::stop() 00538 { 00539 m_playing = false; 00540 } 00541 00542 bool SoundInstance::get_playing() 00543 { 00544 return m_playing; 00545 } 00546 00547 void SoundInstance::update_al() 00548 { 00549 if (!m_source) return; 00550 00551 ALuint al_source_name = m_source->get_name(); 00552 alSourcef(al_source_name, AL_GAIN, m_gain); 00553 alSource3f(al_source_name, AL_POSITION, 00554 m_position.x(), m_position.y(), m_position.z()); 00555 alSourcef(al_source_name, AL_PITCH, m_pitch); 00556 ALenum error = alGetError(); 00557 if (error != AL_NO_ERROR) 00558 { 00559 std::cerr << "OpenAL error: " 00560 << alutGetErrorString(error) << "\n"; 00561 DEBUG_MESSAGE("here."); 00562 throw AudioError(); 00563 } 00564 // Looping shouldn't change while playing. 00565 // It is actually handled by ALSoundSource when first attached. 00566 00567 if (!m_looping && !m_assigning) 00568 { 00569 // if the (not looped) sound finishes, give up the slot. 00570 ALint state; 00571 alGetSourcei(al_source_name, AL_SOURCE_STATE, &state); 00572 if (state == AL_STOPPED) 00573 { 00574 m_playing = false; 00575 unassign(); 00576 } 00577 } 00578 } 00579 00580 unsigned int SoundInstance::get_index() 00581 { 00582 return m_source_index; 00583 } 00584 00585 ALSoundSource::ALSoundSource() 00586 : m_instance(0) 00587 { 00588 alGenSources (1, &m_name); 00589 ALenum error = alGetError(); 00590 if (error != AL_NO_ERROR) 00591 { 00592 std::cerr << "Cannot create SoundSource:\nalGenSources caused: " 00593 << alutGetErrorString(error) << "\n"; 00594 DEBUG_MESSAGE("here."); 00595 throw AudioError(); 00596 } 00597 } 00598 00599 ALSoundSource::~ALSoundSource() 00600 { 00601 alDeleteSources(1, &m_name); 00602 ALenum error = alGetError(); 00603 if (error != AL_NO_ERROR) 00604 { 00605 std::cerr << "Cannot delete SoundSource:\nalDeleteSources caused: " 00606 << alutGetErrorString(error) << "\n"; 00607 DEBUG_MESSAGE("here."); 00608 throw AudioError(); 00609 } 00610 } 00611 00612 ALuint ALSoundSource::get_name() 00613 { 00614 return m_name; 00615 } 00616 00617 SoundInstance * ALSoundSource::get_instance() 00618 { 00619 return m_instance; 00620 } 00621 00622 void ALSoundSource::unassign() 00623 { 00624 if (!m_instance) 00625 { 00626 return; 00627 } 00628 else 00629 { 00630 alSourceStop(m_name); 00631 ALenum error = alGetError(); 00632 if (error != AL_NO_ERROR) 00633 { 00634 std::cerr << "OpenAL error: " 00635 << alutGetErrorString(error) << "\n"; 00636 DEBUG_MESSAGE("here."); 00637 throw AudioError(); 00638 } 00639 m_instance->unassign(); 00640 m_instance = 0; 00641 // Incase the buffer gets deleted, stop using it. 00642 alSourcei (m_name, AL_BUFFER, 0); 00643 } 00644 } 00645 00646 void ALSoundSource::assign(SoundInstance & si) 00647 { 00648 if (m_instance) 00649 { 00650 // stop using the previous assignment. 00651 unassign(); 00652 } 00653 m_instance = &si; 00654 ALenum error = alGetError(); 00655 if (error != AL_NO_ERROR) 00656 { 00657 std::cerr << "OpenAL error: " 00658 << alutGetErrorString(error) << "\n"; 00659 DEBUG_MESSAGE("here."); 00660 throw AudioError(); 00661 } 00662 alSourcei(m_name, AL_BUFFER, m_instance->get_buffer()); 00663 //DEBUG_MESSAGE("Assigned buffer " << m_instance->get_buffer() << " to source " << m_name); 00664 error = alGetError(); 00665 if (error != AL_NO_ERROR) 00666 { 00667 std::cerr << "OpenAL error: " 00668 << alutGetErrorString(error) << "\n"; 00669 DEBUG_MESSAGE("here."); 00670 throw AudioError(); 00671 } 00672 // get the instance to set the gain, pitch, and location. 00673 m_instance->update_al(); 00674 // it won't set the looping by itself though: 00675 alSourcei(m_name, AL_LOOPING, m_instance->get_looping() ? AL_TRUE : AL_FALSE); 00676 error = alGetError(); 00677 if (error != AL_NO_ERROR) 00678 { 00679 std::cerr << "OpenAL error: " 00680 << alutGetErrorString(error) << "\n"; 00681 DEBUG_MESSAGE("here."); 00682 throw AudioError(); 00683 } 00684 if (Audio::get_instance().get_paused()) 00685 { 00686 alSourcePause(m_name); 00687 } 00688 else 00689 { 00690 alSourcePlay(m_name); 00691 } 00692 error = alGetError(); 00693 if (error != AL_NO_ERROR) 00694 { 00695 std::cerr << "OpenAL error: " 00696 << alutGetErrorString(error) << "\n"; 00697 DEBUG_MESSAGE("here."); 00698 throw AudioError(); 00699 } 00700 } 00701 00702 }// namespace Engine 00703
Generated at Mon Sep 6 00:41:12 2010 by Doxygen version 1.4.7 for Racer version svn335.