5 * Created by Bryan Donlan on Sun Aug 12 2007.
6 * Copyright (c) 2007 Bryan Donlan. All rights reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
20 #include "OpenALBackend.h"
23 #include <boost/format.hpp>
24 #include <boost/thread.hpp>
25 #include <boost/lambda/bind.hpp>
27 #include "exceptions.h"
30 #ifndef OPENAL_SUPPORT
31 #error OPENAL_SUPPORT isn't set, so this file shouldn't be being compiled
34 const ALfloat zdist
= -1.0;
35 const ALfloat plnemul
= 0.0;
36 const ALfloat scale
= 1.0/100;
38 static const ALfloat null_vec
[] = { 0.0, 0.0, 0.0 };
40 static std::string
al_error_str(ALenum err
) {
43 return std::string("No error");
45 return std::string("Invalid name");
47 return std::string("Invalid enum");
48 case AL_INVALID_VALUE
:
49 return std::string("Invalid value");
50 case AL_INVALID_OPERATION
:
51 return std::string("Invalid operation");
52 case AL_OUT_OF_MEMORY
:
53 return std::string("Out of memory (we're crashing soon)");
55 return boost::str(boost::format("Unknown error %d") % (int)err
);
59 static void al_throw_maybe() {
60 ALenum err
= alGetError();
61 if (err
== AL_NO_ERROR
) return;
62 throw creaturesException(boost::str(boost::format("OpenAL error (%d): %s") % err
% al_error_str(err
)));
65 void OpenALBackend::init() {
69 if (!alutInitWithoutContext(NULL
, NULL
)) {
70 ALenum err
= alutGetError();
71 throw creaturesException(boost::str(
72 boost::format("Failed to init OpenAL/ALUT: %s") % alutGetErrorString(err
)
76 // It seems that relying on hardware drivers is an awful idea - always use software if it's available.
77 // TODO: we should probably store this so we can (eventually) close stuff properly
78 ALCdevice
*device
= alcOpenDevice("Generic Software");
80 device
= alcOpenDevice(NULL
);
82 ALenum err
= alGetError();
83 throw creaturesException(boost::str(boost::format("OpenAL error (%d): %s") % err
% al_error_str(err
)));
86 ALCcontext
*context
= alcCreateContext(device
, NULL
);
88 ALenum err
= alGetError();
89 throw creaturesException(boost::str(boost::format("OpenAL error (%d): %s") % err
% al_error_str(err
)));
91 alcMakeContextCurrent(context
);
95 static const ALfloat init_ori
[6] = {
96 0.0, 0.0, 1.0, /* at */
97 0.0, -1.0, 0.0 /* up */
99 static const ALfloat init_vel
[3] = {
102 static const ALfloat init_pos
[3] = {
105 memcpy((void *)ListenerOri
, (void *)init_ori
, sizeof init_ori
);
106 memcpy((void *)ListenerVel
, (void *)init_vel
, sizeof init_vel
);
107 memcpy((void *)ListenerPos
, (void *)init_pos
, sizeof init_pos
);
109 ListenerPos
[2] = zdist
;
113 void OpenALBackend::updateListener() {
114 alListenerfv(AL_POSITION
, ListenerPos
);
115 alListenerfv(AL_VELOCITY
, ListenerVel
);
116 alListenerfv(AL_ORIENTATION
, ListenerOri
);
119 void OpenALBackend::setViewpointCenter(float x
, float y
) {
120 ListenerPos
[0] = x
* scale
;
121 ListenerPos
[1] = y
* scale
;
125 void OpenALBackend::shutdown() {
129 void OpenALBackend::setMute(bool m
) {
131 alListenerf(AL_GAIN
, 0.0f
);
133 alListenerf(AL_GAIN
, 1.0f
);
137 boost::shared_ptr
<AudioSource
> OpenALBackend::newSource() {
138 return boost::shared_ptr
<AudioSource
>(new OpenALSource(shared_from_this()));
141 AudioClip
OpenALBackend::loadClip(const std::string
&filename
) {
142 std::string fname
= world
.findFile(std::string("/Sounds/") + filename
+ ".wav");
143 if (fname
.size() == 0) return AudioClip();
146 ALuint buf
= alutCreateBufferFromFile(fname
.c_str());
148 ALenum err
= alGetError();
149 throw creaturesException(boost::str(
150 boost::format("Failed to load %s: %s") % fname
% alutGetErrorString(err
)
154 AudioClip
clip(new OpenALBuffer(shared_from_this(), buf
));
158 void OpenALBackend::begin() {
159 alcSuspendContext(alcGetCurrentContext());
162 void OpenALBackend::commit() {
163 alcProcessContext(alcGetCurrentContext());
166 OpenALSource::OpenALSource(boost::shared_ptr
<class OpenALBackend
> backend
) {
167 this->backend
= backend
;
169 alGenSources(1, &source
);
170 alSourcef(source
, AL_PITCH
, 1.0f
);
171 alSourcef(source
, AL_GAIN
, 1.0f
);
172 alSourcefv(source
, AL_POSITION
, null_vec
);
173 alSourcefv(source
, AL_VELOCITY
, null_vec
);
174 alSourcei(source
, AL_LOOPING
, 0);
178 OpenALBuffer::OpenALBuffer(boost::shared_ptr
<class OpenALBackend
> backend
, ALuint handle
) {
179 this->backend
= backend
;
183 unsigned int OpenALBuffer::length_samples() {
187 alGetBufferi(buffer
, AL_BITS
, &bits
);
188 alGetBufferi(buffer
, AL_CHANNELS
, &channels
);
189 alGetBufferi(buffer
, AL_SIZE
, &size
);
190 return size
/ (bits
/ 8 * channels
);
193 unsigned int OpenALBuffer::length_ms() {
195 alGetBufferi(buffer
, AL_FREQUENCY
, &freq
);
196 return length_samples() * 1000 / freq
;
199 AudioClip
OpenALSource::getClip() {
200 AudioClip
clip(static_cast<AudioBuffer
*>(this->clip
.get()));
204 void OpenALSource::setClip(AudioClip
&clip_
) {
205 OpenALBuffer
*obp
= dynamic_cast<OpenALBuffer
*>(clip_
.get());
208 this->clip
= OpenALClip(obp
);
210 alSourcei(source
, AL_BUFFER
, clip
->buffer
);
214 SourceState
OpenALSource::getState() {
216 alGetSourcei(source
, AL_SOURCE_STATE
, &state
);
218 case AL_INITIAL
: case AL_STOPPED
: return SS_STOP
;
219 case AL_PLAYING
: return SS_PLAY
; /* XXX: AL_INITIAL? */
220 case AL_PAUSED
: return SS_PAUSE
;
223 std::cerr
<< "Unknown openal state, stopping stream: " << state
<< std::endl
;
230 void OpenALSource::play() {
232 alSourcePlay(source
);
235 void OpenALSource::stop() {
236 alSourceStop(source
);
239 void OpenALSource::pause() {
240 alSourcePause(source
);
243 static void fadeSource(boost::weak_ptr
<AudioSource
> s
) {
244 int fadeout
= 500; // TODO: good value?
246 /* We adjust gain every 10ms until we reach fadeout ms. */
247 const int steps
= fadeout
/ 10;
248 const float adjust
= 1.0 / (float)steps
;
249 for (int i
= 0; i
< steps
; i
++) {
250 boost::shared_ptr
<AudioSource
> p
= s
.lock();
252 p
->setVolume(1.0f
- adjust
* (float)i
);
253 SDL_Delay(10); // XXX: do we have some other portable sleeping primitive we can use?
254 // boost::thread::sleep is a bit iffy, it seems
256 boost::shared_ptr
<AudioSource
> p
= s
.lock();
261 void OpenALSource::fadeOut() {
262 boost::thread
th(boost::lambda::bind
<void>(fadeSource
, shared_from_this()));
265 void OpenALSource::setPos(float x
, float y
, float plane
) {
266 if (this->x
== x
&& this->y
== y
&& this->z
== plane
) return;
267 this->SkeletonAudioSource::setPos(x
, y
, plane
);
268 alSource3f(source
, AL_POSITION
, x
* scale
, y
* scale
, plane
* plnemul
);
271 void OpenALSource::setVelocity(float x
, float y
) {
272 // experiment with this later
274 alSource3f(source
, AL_VELOCITY
, x
* scale
, y
* scale
, 0);
277 bool OpenALSource::isLooping() {
279 alGetSourcei(source
, AL_LOOPING
, &l
);
283 void OpenALSource::setLooping(bool l
) {
284 alSourcei(source
, AL_LOOPING
, l
? AL_TRUE
: AL_FALSE
);
287 void OpenALSource::setVolume(float v
) {
288 if (v
== this->volume
) return;
289 this->SkeletonAudioSource::setVolume(v
);
290 alSourcef(source
, AL_GAIN
, getEffectiveVolume());
293 void OpenALSource::setMute(bool m
) {
294 if (m
== muted
) return;
295 this->SkeletonAudioSource::setMute(m
);
296 alSourcef(source
, AL_GAIN
, getEffectiveVolume());