| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- /*
- Copyright (c) 2013 Daniele Bartolini, Michele Rossi
- Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated documentation
- files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use,
- copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following
- conditions:
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
- #include "sound_world.h"
- #include "id_array.h"
- #include "vector3.h"
- #include "matrix4x4.h"
- #include "sound_resource.h"
- #include "temp_allocator.h"
- #include "log.h"
- #include "audio.h"
- #include <AL/al.h>
- #include <AL/alc.h>
- namespace crown
- {
- //-----------------------------------------------------------------------------
- #if defined(CROWN_DEBUG)
- static const char* al_error_to_string(ALenum error)
- {
- switch (error)
- {
- case AL_INVALID_ENUM: return "AL_INVALID_ENUM";
- case AL_INVALID_VALUE: return "AL_INVALID_VALUE";
- case AL_INVALID_OPERATION: return "AL_INVALID_OPERATION";
- case AL_OUT_OF_MEMORY: return "AL_OUT_OF_MEMORY";
- default: return "UNKNOWN_AL_ERROR";
- }
- }
- #define AL_CHECK(function)\
- function;\
- do { ALenum error; CE_ASSERT((error = alGetError()) == AL_NO_ERROR,\
- "OpenAL error: %s", al_error_to_string(error)); } while (0)
- #else
- #define AL_CHECK(function) function;
- #endif
- /// Global audio-related functions
- namespace audio_globals
- {
- static ALCdevice* s_al_device;
- static ALCcontext* s_al_context;
- void init()
- {
- s_al_device = alcOpenDevice(NULL);
- CE_ASSERT(s_al_device, "Cannot open OpenAL audio device");
- s_al_context = alcCreateContext(s_al_device, NULL);
- CE_ASSERT(s_al_context, "Cannot create OpenAL context");
- AL_CHECK(alcMakeContextCurrent(s_al_context));
- CE_LOGD("OpenAL Vendor : %s", alGetString(AL_VENDOR));
- CE_LOGD("OpenAL Version : %s", alGetString(AL_VERSION));
- CE_LOGD("OpenAL Renderer : %s", alGetString(AL_RENDERER));
- AL_CHECK(alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED));
- AL_CHECK(alDopplerFactor(1.0f));
- AL_CHECK(alDopplerVelocity(343.0f));
- }
- void shutdown()
- {
- alcDestroyContext(s_al_context);
- alcCloseDevice(s_al_device);
- }
- }
- //-----------------------------------------------------------------------------
- struct SoundInstance
- {
- void create(SoundResource* sr, const Vector3& pos)
- {
- AL_CHECK(alGenSources(1, &m_source));
- CE_ASSERT(alIsSource(m_source), "Bad OpenAL source");
- // AL_CHECK(alSourcef(m_source, AL_PITCH, 1.0f));
- // AL_CHECK(alSourcef(m_source, AL_REFERENCE_DISTANCE, 0.1f));
- // AL_CHECK(alSourcef(m_source, AL_MAX_DISTANCE, 1000.0f));
- // Generates AL buffers
- AL_CHECK(alGenBuffers(1, &m_buffer));
- CE_ASSERT(alIsBuffer(m_buffer), "Bad OpenAL buffer");
- ALenum format;
- switch (sr->bits_ps())
- {
- case 8: format = sr->channels() > 1 ? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8; break;
- case 16: format = sr->channels() > 1 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16; break;
- default: CE_FATAL("Number of bits per sample not supported."); break;
- }
- AL_CHECK(alBufferData(m_buffer, format, sr->data(), sr->size(), sr->sample_rate()));
- m_resource = sr;
- set_position(pos);
- }
- void destroy()
- {
- stop();
- AL_CHECK(alSourcei(m_source, AL_BUFFER, 0));
- AL_CHECK(alDeleteBuffers(1, &m_buffer));
- AL_CHECK(alDeleteSources(1, &m_source));
- }
- void reload(SoundResource* new_sr)
- {
- destroy();
- create(new_sr, position());
- }
- void play(bool loop, float volume)
- {
- set_volume(volume);
- AL_CHECK(alSourcei(m_source, AL_LOOPING, (loop ? AL_TRUE : AL_FALSE)));
- AL_CHECK(alSourceQueueBuffers(m_source, 1, &m_buffer));
- AL_CHECK(alSourcePlay(m_source));
- }
- void pause()
- {
- AL_CHECK(alSourcePause(m_source));
- }
- void resume()
- {
- AL_CHECK(alSourcePlay(m_source));
- }
- void stop()
- {
- AL_CHECK(alSourceStop(m_source));
- AL_CHECK(alSourceRewind(m_source)); // Workaround
- ALint processed;
- AL_CHECK(alGetSourcei(m_source, AL_BUFFERS_PROCESSED, &processed));
- if (processed > 0)
- {
- ALuint removed;
- AL_CHECK(alSourceUnqueueBuffers(m_source, 1, &removed));
- }
- }
- bool is_playing()
- {
- ALint state;
- AL_CHECK(alGetSourcei(m_source, AL_SOURCE_STATE, &state));
- return state == AL_PLAYING;
- }
- bool finished()
- {
- ALint state;
- AL_CHECK(alGetSourcei(m_source, AL_SOURCE_STATE, &state));
- return (state != AL_PLAYING && state != AL_PAUSED);
- }
- Vector3 position()
- {
- ALfloat pos[3];
- AL_CHECK(alGetSourcefv(m_source, AL_POSITION, pos));
- return Vector3(pos[0], pos[1], pos[2]);
- }
- void set_position(const Vector3& pos)
- {
- AL_CHECK(alSourcefv(m_source, AL_POSITION, vector3::to_float_ptr(pos)));
- }
- void set_range(float range)
- {
- AL_CHECK(alSourcef(m_source, AL_MAX_DISTANCE, range));
- }
- void set_volume(float volume)
- {
- AL_CHECK(alSourcef(m_source, AL_GAIN, volume));
- }
- SoundResource* resource()
- {
- return m_resource;
- }
- public:
- SoundInstanceId m_id;
- SoundResource* m_resource;
- ALuint m_buffer;
- ALuint m_source;
- };
- class ALSoundWorld : public SoundWorld
- {
- public:
- ALSoundWorld()
- {
- set_listener_pose(matrix4x4::IDENTITY);
- }
- virtual ~ALSoundWorld()
- {
- }
- virtual SoundInstanceId play(SoundResource* sr, bool loop, float volume, const Vector3& pos)
- {
- SoundInstance instance;
- instance.create(sr, pos);
- SoundInstanceId id = id_array::create(m_playing_sounds, instance);
- id_array::get(m_playing_sounds, id).m_id = id;
- instance.play(loop, volume);
- return id;
- }
- virtual void stop(SoundInstanceId id)
- {
- SoundInstance& instance = id_array::get(m_playing_sounds, id);
- instance.destroy();
- id_array::destroy(m_playing_sounds, id);
- }
- virtual bool is_playing(SoundInstanceId id)
- {
- return id_array::has(m_playing_sounds, id) && id_array::get(m_playing_sounds, id).is_playing();
- }
- virtual void stop_all()
- {
- for (uint32_t i = 0; i < id_array::size(m_playing_sounds); i++)
- {
- m_playing_sounds[i].stop();
- }
- }
- virtual void pause_all()
- {
- for (uint32_t i = 0; i < id_array::size(m_playing_sounds); i++)
- {
- m_playing_sounds[i].pause();
- }
- }
- virtual void resume_all()
- {
- for (uint32_t i = 0; i < id_array::size(m_playing_sounds); i++)
- {
- m_playing_sounds[i].resume();
- }
- }
- virtual void set_sound_positions(uint32_t num, const SoundInstanceId* ids, const Vector3* positions)
- {
- for (uint32_t i = 0; i < num; i++)
- {
- id_array::get(m_playing_sounds, ids[i]).set_position(positions[i]);
- }
- }
- virtual void set_sound_ranges(uint32_t num, const SoundInstanceId* ids, const float* ranges)
- {
- for (uint32_t i = 0; i < num; i++)
- {
- id_array::get(m_playing_sounds, ids[i]).set_range(ranges[i]);
- }
- }
- virtual void set_sound_volumes(uint32_t num, const SoundInstanceId* ids, const float* volumes)
- {
- for (uint32_t i = 0; i < num; i++)
- {
- id_array::get(m_playing_sounds, ids[i]).set_volume(volumes[i]);
- }
- }
- virtual void reload_sounds(SoundResource* old_sr, SoundResource* new_sr)
- {
- for (uint32_t i = 0; i < id_array::size(m_playing_sounds); i++)
- {
- if (m_playing_sounds[i].resource() == old_sr)
- {
- m_playing_sounds[i].reload(new_sr);
- }
- }
- }
- virtual void set_listener_pose(const Matrix4x4& pose)
- {
- const Vector3 pos = matrix4x4::translation(pose);
- const Vector3 up = matrix4x4::y(pose);
- const Vector3 at = matrix4x4::z(pose);
- AL_CHECK(alListener3f(AL_POSITION, pos.x, pos.y, pos.z));
- //AL_CHECK(alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z));
- const ALfloat orientation[] = { up.x, up.y, up.z, at.x, at.y, at.z };
- AL_CHECK(alListenerfv(AL_ORIENTATION, orientation));
- m_listener_pose = pose;
- }
- virtual void update()
- {
- TempAllocator256 alloc;
- Array<SoundInstanceId> to_delete(alloc);
- // Check what sounds finished playing
- for (uint32_t i = 0; i < id_array::size(m_playing_sounds); i++)
- {
- SoundInstance& instance = m_playing_sounds[i];
- if (instance.finished())
- {
- array::push_back(to_delete, instance.m_id);
- }
- }
- // Destroy instances which finished playing
- for (uint32_t i = 0; i < array::size(to_delete); i++)
- {
- stop(to_delete[i]);
- }
- }
- private:
- IdArray<CE_MAX_SOUND_INSTANCES, SoundInstance> m_playing_sounds;
- Matrix4x4 m_listener_pose;
- };
- SoundWorld* SoundWorld::create(Allocator& a)
- {
- return CE_NEW(a, ALSoundWorld)();
- }
- void SoundWorld::destroy(Allocator& a, SoundWorld* sw)
- {
- CE_DELETE(a, sw);
- }
- } // namespace crown
|