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>
21 #include <vorbis/codec.h>
22 #include <vorbis/vorbisfile.h>
29 #define BUF_SIZE (64 * 1024)
30 //#define BUF_SIZE (5*2048)
41 resource::register_type
<sound_stream
>("ogg");
46 resource::unregister_type("ogg");
56 static ALenum
get_audio_format(const vorbis_info
* audioInfo
)
58 if (audioInfo
->channels
== 1) return AL_FORMAT_MONO16
;
59 else return AL_FORMAT_STEREO16
;
64 typedef boost::shared_ptr
<buffer
> buffer_ptr
;
66 class buffer
: public manager
<buffer
>
73 mOggStream
.datasource
= 0;
78 if (mOggStream
.datasource
)
80 ov_clear(&mOggStream
);
82 if (int(buffer_
) != -1) alDeleteBuffers(1, &buffer_
);
86 void init(const std::string
& path
)
88 log_info("initializing audio buffer...");
89 if (mOggStream
.datasource
)
91 ov_clear(&mOggStream
);
92 mOggStream
.datasource
= 0;
95 if (ov_fopen((char*)path
.c_str(), &mOggStream
) < 0)
97 throw std::runtime_error("problem reading audio: " + path
);
100 vorbis_info
* vorbisInfo
= ov_info(&mOggStream
, -1);
101 mFormat
= get_audio_format(vorbisInfo
);
102 mFreq
= vorbisInfo
->rate
;
106 void load_all(ALuint source
)
108 if (!mOggStream
.datasource
) init(name());
109 if (!mOggStream
.datasource
) return;
117 int result
= ov_read(&mOggStream
, data
+ size
,
118 BUF_SIZE
- size
, 0, 2, 1, §ion
);
126 if (result
< 0) log_warning("vorbis playback error");
132 log_warning("decoded no bytes from", name());
136 alGenBuffers(1, &buffer_
);
138 alBufferData(buffer_
, mFormat
, data
, size
, mFreq
);
139 alSourcei(source
, AL_BUFFER
, buffer_
);
141 // don't need to keep this loaded
142 ov_clear(&mOggStream
);
143 mOggStream
.datasource
= 0;
146 bool stream(ALuint buffer
)
151 while (size
< BUF_SIZE
)
154 int result
= ov_read(&mOggStream
, data
+ size
,
155 BUF_SIZE
- size
, 0, 2, 1, §ion
);
163 if (result
< 0) log_warning("vorbis playback error");
168 if (size
== 0) return false;
170 alBufferData(buffer
, mFormat
, data
, size
, mFreq
);
177 if (!mOggStream
.datasource
) init(name());
178 else ov_raw_seek(&mOggStream
, 0);
184 OggVorbis_File mOggStream
;
196 impl(const std::string
& path
)
198 log_info("sound::impl constructor");
211 alGenSources(1, &source_
);
213 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
214 alSourcef(source_
, AL_PITCH
, 1.0f
);
215 alSourcef(source_
, AL_GAIN
, 1.0f
);
216 alSourcefv(source_
, AL_POSITION
, zero
);
217 alSourcefv(source_
, AL_VELOCITY
, zero
);
219 alSourcei(source_
, AL_LOOPING
, is_looping_
);
226 alDeleteSources(1, &source_
);
228 while (!buffers_
.empty())
230 alDeleteBuffers(1, &buffers_
.back());
240 if (queue_
.empty()) return;
242 if (!is_loaded_
) queue_
.front()->load_all(source_
);
244 alSourcePlay(source_
);
251 if (queue_
.empty()) return;
255 alSourcei(source_
, AL_LOOPING
, false);
259 if (!stream_timer_
.is_valid())
261 stream_timer_
.init(boost::bind(&impl::stream_update
, this, _1
, _2
),
265 alSourcePlay(source_
);
272 for (int i
= buffers_
.size(); i
<= 8; ++i
)
274 alGenBuffers(1, &buffer
);
276 if (queue_
.front()->stream(buffer
))
278 alSourceQueueBuffers(source_
, 1, &buffer
);
279 buffers_
.push_back(buffer
);
283 alDeleteBuffers(1, &buffer
);
294 alGetSourcei(source_
, AL_BUFFERS_PROCESSED
, &finished
);
296 while (finished
-- > 0)
299 alSourceUnqueueBuffers(source_
, 1, &bufferObj
);
301 buffer_ptr buffer
= queue_
.front();
302 bool streamed
= buffer
->stream(bufferObj
);
306 alSourceQueueBuffers(source_
, 1, &bufferObj
);
310 // the buffer couldn't be streamed, so get rid of it
315 // begin the next buffer in the queue
316 queue_
.front()->rewind();
317 queue_
.front()->stream(bufferObj
);
318 alSourceQueueBuffers(source_
, 1, &bufferObj
);
319 log_info("loading new buffer");
321 // queue up any unused buffers
324 else if (is_looping_
)
326 // reload the same buffer
327 queue_
.push_back(buffer
);
329 buffer
->stream(bufferObj
);
330 alSourceQueueBuffers(source_
, 1, &bufferObj
);
331 log_info("looping same buffer");
335 // nothing more to play, stopping...
337 std::remove(buffers_
.begin(), buffers_
.end(),
344 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
346 // restart playing if we're stopped but supposed to be playing...
347 // this means we didn't queue enough and the audio skipped :-(
348 if (is_playing_
&& state
!= AL_PLAYING
)
350 alSourcePlay(source_
);
357 alSourceStop(source_
);
360 stream_timer_
.invalidate();
365 alSourcePause(source_
);
368 stream_timer_
.invalidate();
372 void sample(const std::string
& path
)
375 alSourcei(source_
, AL_BUFFER
, AL_NONE
);
382 while (!buffers_
.empty())
384 alDeleteBuffers(1, &buffers_
.back());
389 void enqueue(const std::string
& path
)
391 buffer_ptr buffer
= buffer::instance(path
);
392 queue_
.push_back(buffer
);
396 bool is_playing() const
398 if (is_playing_
) return true;
401 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
403 return state
== AL_PLAYING
;
407 void loop(bool looping
)
409 is_looping_
= looping
;
412 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
414 if (type
!= AL_STREAMING
)
416 alSourcei(source_
, AL_LOOPING
, is_looping_
);
421 void stream_update(timer
& timer
, scalar t
)
424 // TODO: might be nice to also allow using threads for streaming
425 // rather than a timer, probably as a compile-time option
429 static void retain_backend()
431 if (retain_count_
++ == 0)
433 al_device_
= alcOpenDevice(0);
434 al_context_
= alcCreateContext(al_device_
, 0);
435 if (!al_device_
|| !al_context_
)
437 const char* error
= alcGetString(al_device_
,
438 alcGetError(al_device_
));
439 log_error("audio subsystem initialization failure", error
);
443 alcMakeContextCurrent(al_context_
);
444 log_info
<< "opened sound device `"
445 << alcGetString(al_device_
,
446 ALC_DEFAULT_DEVICE_SPECIFIER
)
452 static void release_backend()
454 if (--retain_count_
== 0)
456 alcMakeContextCurrent(0);
457 alcDestroyContext(al_context_
);
458 alcCloseDevice(al_device_
);
464 std::list
<ALuint
> buffers_
;
470 std::deque
<buffer_ptr
> queue_
;
474 static unsigned retain_count_
;
475 static ALCdevice
* al_device_
;
476 static ALCcontext
* al_context_
;
479 unsigned sound::impl::retain_count_
= 0;
480 ALCdevice
* sound::impl::al_device_
= 0;
481 ALCcontext
* sound::impl::al_context_
= 0;
486 //impl_(new sound::impl) {}
488 sound::sound(const std::string
& path
) :
490 impl_(new sound::impl(path
))
492 log_info("sound constructor");
496 void sound::sample(const std::string
& path
)
524 if (is_playing()) pause();
528 bool sound::is_playing() const
531 return impl_
->is_playing();
535 void sound::position(const vector3
& position
)
537 float vec
[3] = {position
[0], position
[1], position
[2]};
538 alSourcefv(impl_
->source_
, AL_POSITION
, vec
);
541 void sound::velocity(const vector3
& velocity
)
543 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
544 alSourcefv(impl_
->source_
, AL_VELOCITY
, vec
);
547 void sound::gain(scalar gain
)
549 alSourcef(impl_
->source_
, AL_GAIN
, float(gain
));
552 void sound::pitch(scalar pitch
)
554 alSourcef(impl_
->source_
, AL_PITCH
, float(pitch
));
557 void sound::loop(bool looping
)
560 impl_
->loop(looping
);
564 void sound::listener_position(const vector3
& position
)
566 float vec
[] = {position
[0], position
[1], position
[2]};
567 alListenerfv(AL_POSITION
, vec
);
570 void sound::listener_velocity(const vector3
& velocity
)
572 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
573 alListenerfv(AL_VELOCITY
, vec
);
576 void sound::listener_orientation(const vector3
& forward
,
580 vec
[0] = float(forward
[0]);
581 vec
[1] = float(forward
[1]);
582 vec
[2] = float(forward
[2]);
583 vec
[3] = float(up
[0]);
584 vec
[4] = float(up
[1]);
585 vec
[5] = float(up
[2]);
586 alListenerfv(AL_ORIENTATION
, vec
);
590 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
593 void sound_stream::enqueue(const std::string
& path
)
596 impl_
->enqueue(path
);
600 void sound_stream::play()
603 impl_
->play_stream();