2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
10 **************************************************************************/
18 #include <boost/algorithm/string.hpp>
22 #include <vorbis/codec.h>
23 #include <vorbis/vorbisfile.h>
30 #define BUF_SIZE (64 * 1024)
31 //#define BUF_SIZE (5*2048)
40 static ALenum
getAudioFormat(const vorbis_info
* audioInfo
)
42 if (audioInfo
->channels
== 1) return AL_FORMAT_MONO16
;
43 else return AL_FORMAT_STEREO16
;
48 typedef boost::shared_ptr
<buffer
> buffer_ptr
;
50 class buffer
: public manager
<buffer
>
57 mOggStream
.datasource
= 0;
62 if (mOggStream
.datasource
)
64 ov_clear(&mOggStream
);
66 if (int(buffer_
) != -1) alDeleteBuffers(1, &buffer_
);
70 void init(const std::string
& name
)
72 if (mOggStream
.datasource
)
74 ov_clear(&mOggStream
);
75 mOggStream
.datasource
= 0;
78 std::string
path(name
);
79 if (!sound::find_path(path
))
81 throw std::runtime_error("cannot find resource: " + name
);
84 if (ov_fopen((char*)path
.c_str(), &mOggStream
) < 0)
86 throw std::runtime_error("problem reading audio: " + name
);
89 vorbis_info
* vorbisInfo
= ov_info(&mOggStream
, -1);
90 mFormat
= getAudioFormat(vorbisInfo
);
91 mFreq
= vorbisInfo
->rate
;
95 void load_all(ALuint source
)
97 if (!mOggStream
.datasource
) init(name());
98 if (!mOggStream
.datasource
) return;
106 int result
= ov_read(&mOggStream
, data
+ size
,
107 BUF_SIZE
- size
, 0, 2, 1, §ion
);
115 if (result
< 0) log_warning("vorbis playback error");
121 log_warning("decoded no bytes from", name());
125 alGenBuffers(1, &buffer_
);
127 alBufferData(buffer_
, mFormat
, data
, size
, mFreq
);
128 alSourcei(source
, AL_BUFFER
, buffer_
);
130 // don't need to keep this loaded
131 ov_clear(&mOggStream
);
132 mOggStream
.datasource
= 0;
135 bool stream(ALuint buffer
)
140 while (size
< BUF_SIZE
)
143 int result
= ov_read(&mOggStream
, data
+ size
,
144 BUF_SIZE
- size
, 0, 2, 1, §ion
);
152 if (result
< 0) log_warning("vorbis playback error");
157 if (size
== 0) return false;
159 alBufferData(buffer
, mFormat
, data
, size
, mFreq
);
166 if (!mOggStream
.datasource
) init(name());
167 else ov_raw_seek(&mOggStream
, 0);
173 OggVorbis_File mOggStream
;
185 impl(const std::string
& name
)
199 alGenSources(1, &source_
);
201 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
202 alSourcef(source_
, AL_PITCH
, 1.0f
);
203 alSourcef(source_
, AL_GAIN
, 1.0f
);
204 alSourcefv(source_
, AL_POSITION
, zero
);
205 alSourcefv(source_
, AL_VELOCITY
, zero
);
207 alSourcei(source_
, AL_LOOPING
, is_looping_
);
214 alDeleteSources(1, &source_
);
216 while (!buffers_
.empty())
218 alDeleteBuffers(1, &buffers_
.back());
228 if (queue_
.empty()) return;
230 if (!is_loaded_
) queue_
.front()->load_all(source_
);
232 alSourcePlay(source_
);
239 if (queue_
.empty()) return;
243 alSourcei(source_
, AL_LOOPING
, false);
247 if (!stream_timer_
.is_valid())
249 stream_timer_
.init(boost::bind(&impl::stream_update
, this, _1
, _2
),
253 alSourcePlay(source_
);
260 for (int i
= buffers_
.size(); i
<= 8; ++i
)
262 alGenBuffers(1, &buffer
);
264 if (queue_
.front()->stream(buffer
))
266 alSourceQueueBuffers(source_
, 1, &buffer
);
267 buffers_
.push_back(buffer
);
271 alDeleteBuffers(1, &buffer
);
282 alGetSourcei(source_
, AL_BUFFERS_PROCESSED
, &finished
);
284 while (finished
-- > 0)
287 alSourceUnqueueBuffers(source_
, 1, &bufferObj
);
289 buffer_ptr buffer
= queue_
.front();
290 bool streamed
= buffer
->stream(bufferObj
);
294 alSourceQueueBuffers(source_
, 1, &bufferObj
);
298 // the buffer couldn't be streamed, so get rid of it
303 // begin the next buffer in the queue
304 queue_
.front()->rewind();
305 queue_
.front()->stream(bufferObj
);
306 alSourceQueueBuffers(source_
, 1, &bufferObj
);
307 log_info("loading new buffer");
309 // queue up any unused buffers
312 else if (is_looping_
)
314 // reload the same buffer
315 queue_
.push_back(buffer
);
317 buffer
->stream(bufferObj
);
318 alSourceQueueBuffers(source_
, 1, &bufferObj
);
319 log_info("looping same buffer");
323 // nothing more to play, stopping...
325 std::remove(buffers_
.begin(), buffers_
.end(),
332 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
334 // restart playing if we're stopped but supposed to be playing...
335 // this means we didn't queue enough and the audio skipped :-(
336 if (is_playing_
&& state
!= AL_PLAYING
)
338 alSourcePlay(source_
);
345 alSourceStop(source_
);
348 stream_timer_
.invalidate();
353 alSourcePause(source_
);
356 stream_timer_
.invalidate();
360 void sample(const std::string
& name
)
363 alSourcei(source_
, AL_BUFFER
, AL_NONE
);
370 while (!buffers_
.empty())
372 alDeleteBuffers(1, &buffers_
.back());
377 void enqueue(const std::string
& name
)
379 buffer_ptr buffer
= buffer::instance(name
);
380 queue_
.push_back(buffer
);
384 bool is_playing() const
386 if (is_playing_
) return true;
389 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
391 return state
== AL_PLAYING
;
395 void loop(bool looping
)
397 is_looping_
= looping
;
400 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
402 if (type
!= AL_STREAMING
)
404 alSourcei(source_
, AL_LOOPING
, is_looping_
);
409 void stream_update(timer
& timer
, scalar t
)
412 // TODO: might be nice to also allow using threads for streaming
413 // rather than a timer, probably as a compile-time option
417 static void retain_backend()
419 if (retain_count_
++ == 0)
421 al_device_
= alcOpenDevice(0);
422 al_context_
= alcCreateContext(al_device_
, 0);
423 if (!al_device_
|| !al_context_
)
425 const char* error
= alcGetString(al_device_
,
426 alcGetError(al_device_
));
427 log_error("audio subsystem initialization failure", error
);
431 alcMakeContextCurrent(al_context_
);
432 log_info
<< "opened sound device `"
433 << alcGetString(al_device_
,
434 ALC_DEFAULT_DEVICE_SPECIFIER
)
440 static void release_backend()
442 if (--retain_count_
== 0)
444 alcMakeContextCurrent(0);
445 alcDestroyContext(al_context_
);
446 alcCloseDevice(al_device_
);
452 std::list
<ALuint
> buffers_
;
458 std::deque
<buffer_ptr
> queue_
;
462 static unsigned retain_count_
;
463 static ALCdevice
* al_device_
;
464 static ALCcontext
* al_context_
;
467 unsigned sound::impl::retain_count_
= 0;
468 ALCdevice
* sound::impl::al_device_
= 0;
469 ALCcontext
* sound::impl::al_context_
= 0;
474 impl_(new sound::impl
) {}
476 sound::sound(const std::string
& name
) :
478 impl_(new sound::impl(name
)) {}
481 void sound::sample(const std::string
& name
)
509 if (is_playing()) pause();
513 bool sound::is_playing() const
516 return impl_
->is_playing();
520 void sound::position(const vector3
& position
)
522 float vec
[3] = {position
[0], position
[1], position
[2]};
523 alSourcefv(impl_
->source_
, AL_POSITION
, vec
);
526 void sound::velocity(const vector3
& velocity
)
528 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
529 alSourcefv(impl_
->source_
, AL_VELOCITY
, vec
);
532 void sound::gain(scalar gain
)
534 alSourcef(impl_
->source_
, AL_GAIN
, float(gain
));
537 void sound::pitch(scalar pitch
)
539 alSourcef(impl_
->source_
, AL_PITCH
, float(pitch
));
542 void sound::loop(bool looping
)
545 impl_
->loop(looping
);
549 void sound::listener_position(const vector3
& position
)
551 float vec
[] = {position
[0], position
[1], position
[2]};
552 alListenerfv(AL_POSITION
, vec
);
555 void sound::listener_velocity(const vector3
& velocity
)
557 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
558 alListenerfv(AL_VELOCITY
, vec
);
561 void sound::listener_orientation(const vector3
& forward
,
565 vec
[0] = float(forward
[0]);
566 vec
[1] = float(forward
[1]);
567 vec
[2] = float(forward
[2]);
568 vec
[3] = float(up
[0]);
569 vec
[4] = float(up
[1]);
570 vec
[5] = float(up
[2]);
571 alListenerfv(AL_ORIENTATION
, vec
);
575 bool sound::find_path(std::string
& name
)
577 return resource::find_path(name
, "sounds/", "ogg");
581 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
584 void sound_stream::enqueue(const std::string
& name
)
587 impl_
->enqueue(name
);
591 void sound_stream::play()
594 impl_
->play_stream();