add wraparound support to C2 physics
[openc2e.git] / OpenALBackend.cpp
blob11ce5aaaadfd59f6c755445cbf583eb3e61a4a15
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 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);
87 if (!context) {
88 ALenum err = alGetError();
89 throw creaturesException(boost::str(boost::format("OpenAL error (%d): %s") % err % al_error_str(err)));
91 alcMakeContextCurrent(context);
93 setMute(false);
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] = {
100 0.0, 0.0, 0.0
102 static const ALfloat init_pos[3] = {
103 0.0, 0.0, 0.0
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;
110 updateListener();
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;
122 updateListener();
125 void OpenALBackend::shutdown() {
126 alutExit();
129 void OpenALBackend::setMute(bool m) {
130 if (m)
131 alListenerf(AL_GAIN, 0.0f);
132 else
133 alListenerf(AL_GAIN, 1.0f);
134 muted = m;
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();
145 alGetError();
146 ALuint buf = alutCreateBufferFromFile(fname.c_str());
147 if (!buf) {
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));
155 return clip;
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;
168 alGetError();
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);
175 al_throw_maybe();
178 OpenALBuffer::OpenALBuffer(boost::shared_ptr<class OpenALBackend> backend, ALuint handle) {
179 this->backend = backend;
180 buffer = handle;
183 unsigned int OpenALBuffer::length_samples() {
184 ALint bits;
185 ALint channels;
186 ALint size;
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() {
194 ALint freq;
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()));
201 return clip;
204 void OpenALSource::setClip(AudioClip &clip_) {
205 OpenALBuffer *obp = dynamic_cast<OpenALBuffer *>(clip_.get());
206 assert(obp);
207 stop();
208 this->clip = OpenALClip(obp);
209 if (clip) {
210 alSourcei(source, AL_BUFFER, clip->buffer);
214 SourceState OpenALSource::getState() {
215 int state;
216 alGetSourcei(source, AL_SOURCE_STATE, &state);
217 switch (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;
221 default:
223 std::cerr << "Unknown openal state, stopping stream: " << state << std::endl;
224 stop();
225 return SS_STOP;
230 void OpenALSource::play() {
231 assert(clip);
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();
251 if (!p) return;
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();
257 if (p)
258 p->stop();
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
273 x = y = 0;
274 alSource3f(source, AL_VELOCITY, x * scale, y * scale, 0);
277 bool OpenALSource::isLooping() {
278 int l;
279 alGetSourcei(source, AL_LOOPING, &l);
280 return l == AL_TRUE;
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());
299 /* vim: set noet: */