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)
42 //log_info("registering ogg resource handler");
43 resource::register_type
<sound_stream
>("ogg");
48 //log_info("unregistering ogg resource handler");
49 resource::unregister_type("ogg");
60 static ALenum
get_audio_format(const vorbis_info
* audioInfo
)
62 if (audioInfo
->channels
== 1) return AL_FORMAT_MONO16
;
63 else return AL_FORMAT_STEREO16
;
68 typedef boost::shared_ptr
<buffer
> buffer_ptr
;
70 class buffer
: public manager
<buffer
>
77 mOggStream
.datasource
= 0;
82 if (mOggStream
.datasource
)
84 ov_clear(&mOggStream
);
86 if (int(buffer_
) != -1) alDeleteBuffers(1, &buffer_
);
90 void init(const std::string
& path
)
92 log_info("initializing audio buffer...");
93 if (mOggStream
.datasource
)
95 ov_clear(&mOggStream
);
96 mOggStream
.datasource
= 0;
99 if (ov_fopen((char*)path
.c_str(), &mOggStream
) < 0)
101 throw std::runtime_error("problem reading audio: " + path
);
104 vorbis_info
* vorbisInfo
= ov_info(&mOggStream
, -1);
105 mFormat
= get_audio_format(vorbisInfo
);
106 mFreq
= vorbisInfo
->rate
;
110 void load_all(ALuint source
)
112 if (!mOggStream
.datasource
) init(name());
113 if (!mOggStream
.datasource
) return;
121 int result
= ov_read(&mOggStream
, data
+ size
,
122 BUF_SIZE
- size
, 0, 2, 1, §ion
);
130 if (result
< 0) log_warning("vorbis playback error");
136 log_warning("decoded no bytes from", name());
140 alGenBuffers(1, &buffer_
);
142 alBufferData(buffer_
, mFormat
, data
, size
, mFreq
);
143 alSourcei(source
, AL_BUFFER
, buffer_
);
145 // don't need to keep this loaded
146 ov_clear(&mOggStream
);
147 mOggStream
.datasource
= 0;
150 bool stream(ALuint buffer
)
155 while (size
< BUF_SIZE
)
158 int result
= ov_read(&mOggStream
, data
+ size
,
159 BUF_SIZE
- size
, 0, 2, 1, §ion
);
167 if (result
< 0) log_warning("vorbis playback error");
172 if (size
== 0) return false;
174 alBufferData(buffer
, mFormat
, data
, size
, mFreq
);
181 if (!mOggStream
.datasource
) init(name());
182 else ov_raw_seek(&mOggStream
, 0);
188 OggVorbis_File mOggStream
;
200 impl(const std::string
& path
)
202 log_info("sound::impl constructor");
215 alGenSources(1, &source_
);
217 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
218 alSourcef(source_
, AL_PITCH
, 1.0f
);
219 alSourcef(source_
, AL_GAIN
, 1.0f
);
220 alSourcefv(source_
, AL_POSITION
, zero
);
221 alSourcefv(source_
, AL_VELOCITY
, zero
);
223 alSourcei(source_
, AL_LOOPING
, is_looping_
);
230 alDeleteSources(1, &source_
);
232 while (!buffers_
.empty())
234 alDeleteBuffers(1, &buffers_
.back());
244 if (queue_
.empty()) return;
246 if (!is_loaded_
) queue_
.front()->load_all(source_
);
248 alSourcePlay(source_
);
255 if (queue_
.empty()) return;
259 alSourcei(source_
, AL_LOOPING
, false);
263 if (!stream_timer_
.is_valid())
265 stream_timer_
.init(boost::bind(&impl::stream_update
, this, _1
, _2
),
269 alSourcePlay(source_
);
276 for (int i
= buffers_
.size(); i
<= 8; ++i
)
278 alGenBuffers(1, &buffer
);
280 if (queue_
.front()->stream(buffer
))
282 alSourceQueueBuffers(source_
, 1, &buffer
);
283 buffers_
.push_back(buffer
);
287 alDeleteBuffers(1, &buffer
);
298 alGetSourcei(source_
, AL_BUFFERS_PROCESSED
, &finished
);
300 while (finished
-- > 0)
303 alSourceUnqueueBuffers(source_
, 1, &bufferObj
);
305 buffer_ptr buffer
= queue_
.front();
306 bool streamed
= buffer
->stream(bufferObj
);
310 alSourceQueueBuffers(source_
, 1, &bufferObj
);
314 // the buffer couldn't be streamed, so get rid of it
319 // begin the next buffer in the queue
320 queue_
.front()->rewind();
321 queue_
.front()->stream(bufferObj
);
322 alSourceQueueBuffers(source_
, 1, &bufferObj
);
323 log_info("loading new buffer");
325 // queue up any unused buffers
328 else if (is_looping_
)
330 // reload the same buffer
331 queue_
.push_back(buffer
);
333 buffer
->stream(bufferObj
);
334 alSourceQueueBuffers(source_
, 1, &bufferObj
);
335 log_info("looping same buffer");
339 // nothing more to play, stopping...
341 std::remove(buffers_
.begin(), buffers_
.end(),
348 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
350 // restart playing if we're stopped but supposed to be playing...
351 // this means we didn't queue enough and the audio skipped :-(
352 if (is_playing_
&& state
!= AL_PLAYING
)
354 alSourcePlay(source_
);
361 alSourceStop(source_
);
364 stream_timer_
.invalidate();
369 alSourcePause(source_
);
372 stream_timer_
.invalidate();
376 void sample(const std::string
& path
)
379 alSourcei(source_
, AL_BUFFER
, AL_NONE
);
386 while (!buffers_
.empty())
388 alDeleteBuffers(1, &buffers_
.back());
393 void enqueue(const std::string
& path
)
395 buffer_ptr buffer
= buffer::instance(path
);
396 queue_
.push_back(buffer
);
400 bool is_playing() const
402 if (is_playing_
) return true;
405 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
407 return state
== AL_PLAYING
;
411 void loop(bool looping
)
413 is_looping_
= looping
;
416 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
418 if (type
!= AL_STREAMING
)
420 alSourcei(source_
, AL_LOOPING
, is_looping_
);
425 void stream_update(timer
& timer
, scalar t
)
428 // TODO: might be nice to also allow using threads for streaming
429 // rather than a timer, probably as a compile-time option
433 static void retain_backend()
435 if (retain_count_
++ == 0)
437 al_device_
= alcOpenDevice(0);
438 al_context_
= alcCreateContext(al_device_
, 0);
439 if (!al_device_
|| !al_context_
)
441 const char* error
= alcGetString(al_device_
,
442 alcGetError(al_device_
));
443 log_error("audio subsystem initialization failure", error
);
447 alcMakeContextCurrent(al_context_
);
448 log_info
<< "opened sound device `"
449 << alcGetString(al_device_
,
450 ALC_DEFAULT_DEVICE_SPECIFIER
)
456 static void release_backend()
458 if (--retain_count_
== 0)
460 alcMakeContextCurrent(0);
461 alcDestroyContext(al_context_
);
462 alcCloseDevice(al_device_
);
468 std::list
<ALuint
> buffers_
;
474 std::deque
<buffer_ptr
> queue_
;
478 static unsigned retain_count_
;
479 static ALCdevice
* al_device_
;
480 static ALCcontext
* al_context_
;
483 unsigned sound::impl::retain_count_
= 0;
484 ALCdevice
* sound::impl::al_device_
= 0;
485 ALCcontext
* sound::impl::al_context_
= 0;
490 //impl_(new sound::impl) {}
492 sound::sound(const std::string
& path
) :
494 impl_(new sound::impl(path
))
496 log_info("sound constructor");
500 void sound::sample(const std::string
& path
)
528 if (is_playing()) pause();
532 bool sound::is_playing() const
535 return impl_
->is_playing();
539 void sound::position(const vector3
& position
)
541 float vec
[3] = {position
[0], position
[1], position
[2]};
542 alSourcefv(impl_
->source_
, AL_POSITION
, vec
);
545 void sound::velocity(const vector3
& velocity
)
547 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
548 alSourcefv(impl_
->source_
, AL_VELOCITY
, vec
);
551 void sound::gain(scalar gain
)
553 alSourcef(impl_
->source_
, AL_GAIN
, float(gain
));
556 void sound::pitch(scalar pitch
)
558 alSourcef(impl_
->source_
, AL_PITCH
, float(pitch
));
561 void sound::loop(bool looping
)
564 impl_
->loop(looping
);
568 void sound::listener_position(const vector3
& position
)
570 float vec
[] = {position
[0], position
[1], position
[2]};
571 alListenerfv(AL_POSITION
, vec
);
574 void sound::listener_velocity(const vector3
& velocity
)
576 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
577 alListenerfv(AL_VELOCITY
, vec
);
580 void sound::listener_orientation(const vector3
& forward
,
584 vec
[0] = float(forward
[0]);
585 vec
[1] = float(forward
[1]);
586 vec
[2] = float(forward
[2]);
587 vec
[3] = float(up
[0]);
588 vec
[4] = float(up
[1]);
589 vec
[5] = float(up
[2]);
590 alListenerfv(AL_ORIENTATION
, vec
);
594 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
597 void sound_stream::enqueue(const std::string
& path
)
600 impl_
->enqueue(path
);
604 void sound_stream::play()
607 impl_
->play_stream();