add some hacky code to allow passing sfc files with -b
[openc2e.git] / OpenALBackend.cpp
blobbce32e7337be3406e083f9144f0f29d10297252b
1 /*
2 * OpenALBackend.cpp
3 * openc2e
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"
21 #include <alut.h>
22 #include <iostream>
23 #include <boost/format.hpp>
24 #include <boost/thread.hpp>
25 #include <boost/lambda/bind.hpp>
26 #include "SDL.h"
27 #include "exceptions.h"
28 #include "World.h"
30 #ifndef OPENAL_SUPPORT
31 #error OPENAL_SUPPORT isn't set, so this file shouldn't be being compiled
32 #endif
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) {
41 switch (err) {
42 case AL_NO_ERROR:
43 return std::string("No error");
44 case AL_INVALID_NAME:
45 return std::string("Invalid name");
46 case AL_INVALID_ENUM:
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)");
54 default:
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() {
66 alGetError();
67 alutGetError();
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)
73 ));
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");
79 if (!device) {
80 device = alcOpenDevice(NULL);
81 if (!device) {
82 al_throw_maybe();
85 ALCcontext *context = alcCreateContext(device, NULL);
86 if (!context) {
87 al_throw_maybe();
89 alcMakeContextCurrent(context);
91 setMute(false);
93 static const ALfloat init_ori[6] = {
94 0.0, 0.0, 1.0, /* at */
95 0.0, -1.0, 0.0 /* up */
97 static const ALfloat init_vel[3] = {
98 0.0, 0.0, 0.0
100 static const ALfloat init_pos[3] = {
101 0.0, 0.0, 0.0
103 memcpy((void *)ListenerOri, (void *)init_ori, sizeof init_ori);
104 memcpy((void *)ListenerVel, (void *)init_vel, sizeof init_vel);
105 memcpy((void *)ListenerPos, (void *)init_pos, sizeof init_pos);
107 ListenerPos[2] = zdist;
108 updateListener();
111 void OpenALBackend::updateListener() {
112 alListenerfv(AL_POSITION, ListenerPos);
113 alListenerfv(AL_VELOCITY, ListenerVel);
114 alListenerfv(AL_ORIENTATION, ListenerOri);
117 void OpenALBackend::setViewpointCenter(float x, float y) {
118 ListenerPos[0] = x * scale;
119 ListenerPos[1] = y * scale;
120 updateListener();
123 void OpenALBackend::shutdown() {
124 alutExit();
127 void OpenALBackend::setMute(bool m) {
128 if (m)
129 alListenerf(AL_GAIN, 0.0f);
130 else
131 alListenerf(AL_GAIN, 1.0f);
132 muted = m;
135 boost::shared_ptr<AudioSource> OpenALBackend::newSource() {
136 return boost::shared_ptr<AudioSource>(new OpenALSource(shared_from_this()));
139 AudioClip OpenALBackend::loadClip(const std::string &filename) {
140 std::string fname = world.findFile(std::string("/Sounds/") + filename + ".wav");
141 if (fname.size() == 0) return AudioClip();
143 alGetError();
144 ALuint buf = alutCreateBufferFromFile(fname.c_str());
145 if (!buf) {
146 ALenum err = alGetError();
147 throw creaturesException(boost::str(
148 boost::format("Failed to load %s: %s") % fname % alutGetErrorString(err)
152 AudioClip clip(new OpenALBuffer(shared_from_this(), buf));
153 return clip;
156 void OpenALBackend::begin() {
157 alcSuspendContext(alcGetCurrentContext());
160 void OpenALBackend::commit() {
161 alcProcessContext(alcGetCurrentContext());
164 OpenALSource::OpenALSource(boost::shared_ptr<class OpenALBackend> backend) {
165 this->backend = backend;
166 alGetError();
167 alGenSources(1, &source);
168 alSourcef(source, AL_PITCH, 1.0f);
169 alSourcef(source, AL_GAIN, 1.0f);
170 alSourcefv(source, AL_POSITION, null_vec);
171 alSourcefv(source, AL_VELOCITY, null_vec);
172 alSourcei(source, AL_LOOPING, 0);
173 al_throw_maybe();
176 OpenALBuffer::OpenALBuffer(boost::shared_ptr<class OpenALBackend> backend, ALuint handle) {
177 this->backend = backend;
178 buffer = handle;
181 unsigned int OpenALBuffer::length_samples() {
182 ALint bits;
183 ALint channels;
184 ALint size;
185 alGetBufferi(buffer, AL_BITS, &bits);
186 alGetBufferi(buffer, AL_CHANNELS, &channels);
187 alGetBufferi(buffer, AL_SIZE, &size);
188 return size / (bits / 8 * channels);
191 unsigned int OpenALBuffer::length_ms() {
192 ALint freq;
193 alGetBufferi(buffer, AL_FREQUENCY, &freq);
194 return length_samples() * 1000 / freq;
197 AudioClip OpenALSource::getClip() {
198 AudioClip clip(static_cast<AudioBuffer *>(this->clip.get()));
199 return clip;
202 void OpenALSource::setClip(AudioClip &clip_) {
203 OpenALBuffer *obp = dynamic_cast<OpenALBuffer *>(clip_.get());
204 assert(obp);
205 stop();
206 this->clip = OpenALClip(obp);
207 if (clip) {
208 alSourcei(source, AL_BUFFER, clip->buffer);
212 SourceState OpenALSource::getState() {
213 int state;
214 alGetSourcei(source, AL_SOURCE_STATE, &state);
215 switch (state) {
216 case AL_INITIAL: case AL_STOPPED: return SS_STOP;
217 case AL_PLAYING: return SS_PLAY; /* XXX: AL_INITIAL? */
218 case AL_PAUSED: return SS_PAUSE;
219 default:
221 std::cerr << "Unknown openal state, stopping stream: " << state << std::endl;
222 stop();
223 return SS_STOP;
228 void OpenALSource::play() {
229 assert(clip);
230 alSourcePlay(source);
233 void OpenALSource::stop() {
234 alSourceStop(source);
237 void OpenALSource::pause() {
238 alSourcePause(source);
241 static void fadeSource(boost::weak_ptr<AudioSource> s) {
242 int fadeout = 500; // TODO: good value?
244 /* We adjust gain every 10ms until we reach fadeout ms. */
245 const int steps = fadeout / 10;
246 const float adjust = 1.0 / (float)steps;
247 for (int i = 0; i < steps; i++) {
248 boost::shared_ptr<AudioSource> p = s.lock();
249 if (!p) return;
250 p->setVolume(1.0f - adjust * (float)i);
251 SDL_Delay(10); // XXX: do we have some other portable sleeping primitive we can use?
252 // boost::thread::sleep is a bit iffy, it seems
254 boost::shared_ptr<AudioSource> p = s.lock();
255 if (p)
256 p->stop();
259 void OpenALSource::fadeOut() {
260 boost::thread th(boost::lambda::bind<void>(fadeSource, shared_from_this()));
263 void OpenALSource::setPos(float x, float y, float plane) {
264 if (this->x == x && this->y == y && this->z == plane) return;
265 this->SkeletonAudioSource::setPos(x, y, plane);
266 alSource3f(source, AL_POSITION, x * scale, y * scale, plane * plnemul);
269 void OpenALSource::setVelocity(float x, float y) {
270 // experiment with this later
271 x = y = 0;
272 alSource3f(source, AL_VELOCITY, x * scale, y * scale, 0);
275 bool OpenALSource::isLooping() {
276 int l;
277 alGetSourcei(source, AL_LOOPING, &l);
278 return l == AL_TRUE;
281 void OpenALSource::setLooping(bool l) {
282 alSourcei(source, AL_LOOPING, l ? AL_TRUE : AL_FALSE);
285 void OpenALSource::setVolume(float v) {
286 if (v == this->volume) return;
287 this->SkeletonAudioSource::setVolume(v);
288 alSourcef(source, AL_GAIN, getEffectiveVolume());
291 void OpenALSource::setMute(bool m) {
292 if (m == muted) return;
293 this->SkeletonAudioSource::setMute(m);
294 alSourcef(source, AL_GAIN, getEffectiveVolume());
297 /* vim: set noet: */