Audio.cpp

Go to the documentation of this file.
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 

Get Racer at SourceForge.net. Fast, secure and Free Open Source software downloads

Generated at Mon Sep 6 00:41:12 2010 by Doxygen version 1.4.7 for Racer version svn335.