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 **************************************************************************/
15 #include <boost/algorithm/string.hpp>
16 #include <boost/cstdint.hpp>
17 #include <boost/noncopyable.hpp>
20 #include <vorbis/codec.h>
21 #include <vorbis/vorbisfile.h>
25 #include "resource.hh"
32 #define BUF_SIZE (4096)
35 #define NUM_SAMPLE_BITS (16)
41 thread stream_thread_
;
45 *************************************************************************/
53 if (retain_count
++ == 0)
55 al_device
= alcOpenDevice(0);
56 al_context
= alcCreateContext(al_device
, 0);
57 if (!al_device
|| !al_context
)
59 const char* error
= alcGetString(al_device
,
60 alcGetError(al_device
));
61 log_error("audio subsystem initialization failure", error
);
65 alcMakeContextCurrent(al_context
);
66 log_info
<< "opened sound device `"
67 << alcGetString(al_device
,
68 ALC_DEFAULT_DEVICE_SPECIFIER
)
75 sound_backend(const sound_backend
& backend
)
80 sound_backend
& operator=(const sound_backend
& backend
)
88 if (--retain_count
== 0)
90 alcMakeContextCurrent(0);
91 alcDestroyContext(al_context
);
92 alcCloseDevice(al_device
);
97 static int retain_count
;
98 static ALCdevice
* al_device
;
99 static ALCcontext
* al_context
;
102 int sound_backend::retain_count
= 0;
103 ALCdevice
* sound_backend::al_device
;
104 ALCcontext
* sound_backend::al_context
;
107 class sound_resource
;
108 typedef resource_handle
<sound_resource
> sound_handle
;
110 MOOF_REGISTER_RESOURCE(sound_resource
, ogg
, sounds
);
114 *************************************************************************/
116 class sound_resource
: public boost::noncopyable
120 sound_resource(const std::string
& path
) :
124 if (ov_fopen((char*)path
.c_str(), &file_
) < 0)
126 throw std::runtime_error("problem reading audio: " + path
);
133 if (buffer_
) alDeleteBuffers(1, &buffer_
);
137 ALuint
read_all() const
139 if (buffer_
) return buffer_
;
141 if (ov_pcm_seek(&file_
, 0) != 0)
143 log_warning("vorbis seek error");
147 ogg_int64_t samples
= ov_pcm_total(&file_
, 0);
149 char data
[2 * samples
* (NUM_SAMPLE_BITS
/ 8)];
152 while (size
< sizeof(data
))
155 int result
= ov_read(&file_
,
156 data
+ size
, sizeof(data
) - size
,
163 else if (result
== 0 && size
> 0)
165 vorbis_info
* info
= ov_info(&file_
, section
);
168 alGenBuffers(1, &buffer
);
169 alBufferData(buffer
, get_audio_format(info
),
170 data
, size
, info
->rate
);
177 log_warning("vorbis playback error");
185 bool read(ALuint buffer
, uint64_t& sample
) const
187 if ((sample
== sample_
&& ov_pcm_seek_lap(&file_
, sample
) != 0) ||
188 (sample
!= sample_
&& ov_pcm_seek(&file_
, sample
) != 0))
195 int result
= ov_read(&file_
, data
, sizeof(data
), 0, 2, 1, §ion
);
199 vorbis_info
* info
= ov_info(&file_
, section
);
200 alBufferData(buffer
, get_audio_format(info
),
201 data
, result
, info
->rate
);
202 sample_
= sample
= ov_pcm_tell(&file_
);
205 else if (result
< 0) log_warning("vorbis playback error");
210 bool read(ALuint buffer
, uint64_t& sample
, sound_handle other
) const
212 ov_crosslap(&other
->file_
, &file_
);
216 int result
= ov_read(&file_
, data
, sizeof(data
), 0, 2, 1, §ion
);
220 vorbis_info
* info
= ov_info(&file_
, section
);
221 alBufferData(buffer
, get_audio_format(info
),
222 data
, result
, info
->rate
);
223 sample_
= sample
= ov_pcm_tell(&file_
);
226 else if (result
< 0) log_warning("vorbis playback error");
232 // determines the correct number of fixed-size buffers needed to hold a
233 // given number of seconds of PCM audio.
234 int num_buffers(scalar seconds
) const
236 vorbis_info
* info
= ov_info(&file_
, -1);
237 int count
= scalar(info
->rate
) * // sample rate
238 scalar(info
->channels
) * // channels
239 scalar(NUM_SAMPLE_BITS
) * // sample size
241 SCALAR(0.125) / // bits to bytes
242 scalar(BUF_SIZE
); // individual buffer size
247 static ALenum
get_audio_format(const vorbis_info
* info
)
249 if (info
->channels
== 1) return AL_FORMAT_MONO16
;
250 else if (info
->channels
== 2) return AL_FORMAT_STEREO16
;
252 log_error("unsupported number of channels:", info
->channels
);
259 mutable OggVorbis_File file_
;
260 mutable ALuint buffer_
;
261 mutable uint64_t sample_
;
263 sound_backend backend_
;
268 *************************************************************************/
279 impl(const std::string
& name
)
291 buffer_size_
= SCALAR(1.0);
293 alGenSources(1, &source_
);
295 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
296 alSourcef(source_
, AL_PITCH
, 1.0f
);
297 alSourcef(source_
, AL_GAIN
, 1.0f
);
298 alSourcefv(source_
, AL_POSITION
, zero
);
299 alSourcefv(source_
, AL_VELOCITY
, zero
);
307 alDeleteSources(1, &source_
);
311 void deplete_stream()
316 alGetSourcei(source_
, AL_BUFFERS_QUEUED
, &queued
);
317 ALuint buffers
[queued
];
318 alSourceUnqueueBuffers(source_
, queued
, buffers
);
319 alDeleteBuffers(queued
, buffers
);
321 else alSourcei(source_
, AL_BUFFER
, AL_NONE
);
326 if (queue_
.empty() || is_playing_
) return;
329 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
337 start_update_timer();
338 num_buffers_
= queue_
.front()->num_buffers(buffer_size_
);
341 alSourcei(source_
, AL_LOOPING
, false);
345 ALuint buffer
= queue_
.front()->read_all();
346 alSourcei(source_
, AL_BUFFER
, buffer
);
347 alSourcei(source_
, AL_LOOPING
, is_looping_
);
355 start_update_timer();
361 alSourcePlay(source_
);
365 void start_update_timer()
367 stream_timer_
.init(boost::bind(&impl::stream_update
, this, _1
, _2
),
368 SCALAR(0.1), timer::repeat
);
371 //thread.runloop().add_timer(stream_timer_);
372 if (!stream_thread_
.is_valid())
374 stream_thread_
= thread::detach(stream_timer_
);
381 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
382 ASSERT(type
!= AL_STATIC
&& "source shouldn't be static");
386 alGetSourcei(source_
, AL_BUFFERS_QUEUED
, &queued
);
387 alGetSourcei(source_
, AL_BUFFERS_PROCESSED
, &processed
);
389 int target_diff
= num_buffers_
- queued
+ processed
;
391 ALuint bufs
[processed
];
392 alSourceUnqueueBuffers(source_
, processed
, bufs
);
393 for (int i
= 0; i
< processed
; ++i
)
395 ALuint
& buf
= bufs
[i
];
399 if (queue_buffer(buf
)) --target_diff
;
402 alDeleteBuffers(processed
- i
- 1, &bufs
[i
+1]);
406 else alDeleteBuffers(1, &buf
);
409 if (stream_timer_
.mode() != timer::invalid
&& 0 < target_diff
)
411 for (int i
= 0; i
< target_diff
; ++i
)
413 if (!queue_buffer(AL_NONE
)) break;
420 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
421 if (state
!= AL_PLAYING
)
423 // restart playing if we're stopped but supposed to be
424 // playing... this means we didn't queue enough...
425 alSourcePlay(source_
);
426 log_warning("not enough audio buffered");
431 bool queue_buffer(ALuint buffer
)
433 if (buffer
== AL_NONE
) alGenBuffers(1, &buffer
);
435 bool looped_once
= false;
437 while (0 < queue_
.size())
439 sound_handle sound
= queue_
.front();
441 bool result
= sound
->read(buffer
, sample_
);
444 alSourceQueueBuffers(source_
, 1, &buffer
);
452 if (0 < queue_
.size())
454 sound_handle new_sound
= queue_
.front();
455 result
= new_sound
->read(buffer
, sample_
, sound
);
458 queue_
.push_back(new_sound
);
459 alSourceQueueBuffers(source_
, 1, &buffer
);
460 num_buffers_
= new_sound
->num_buffers(buffer_size_
);
466 queue_
.push_front(sound
);
469 else if (is_looping_
&& !looped_once
)
471 queue_
.push_back(sound
);
478 alDeleteBuffers(1, &buffer
);
480 stream_timer_
.invalidate();
487 alSourceStop(source_
);
490 stream_timer_
.invalidate();
497 alSourcePause(source_
);
500 stream_timer_
.invalidate();
504 void sample(const std::string
& name
)
511 is_streaming_
= false;
514 void queue(const std::string
& name
)
516 sound_handle handle
= resource::load(name
, "ogg");
517 if (handle
) queue_
.push_back(handle
);
518 is_streaming_
= true;
522 bool is_playing() const
525 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
527 if (state
== AL_PLAYING
) return true;
528 else return is_playing_
;
532 void loop(bool looping
)
534 is_looping_
= looping
;
537 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
538 if (type
!= AL_STREAMING
)
540 alSourcei(source_
, AL_LOOPING
, is_looping_
);
545 void stream_update(timer
& timer
, scalar t
)
547 log_debug("blaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhrrrrrg");
558 std::deque
<sound_handle
> queue_
;
565 sound_backend backend_
;
571 impl_(new sound::impl
) {}
573 sound::sound(const std::string
& path
) :
575 impl_(new sound::impl(path
)) {}
578 void sound::sample(const std::string
& path
)
584 void sound::queue(const std::string
& path
)
611 if (is_playing()) pause();
615 bool sound::is_playing() const
618 return impl_
->is_playing();
621 void sound::buffer_size(scalar seconds
)
623 impl_
->buffer_size_
= seconds
;
627 void sound::position(const vector3
& position
)
629 float vec
[3] = {position
[0], position
[1], position
[2]};
630 alSourcefv(impl_
->source_
, AL_POSITION
, vec
);
633 void sound::velocity(const vector3
& velocity
)
635 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
636 alSourcefv(impl_
->source_
, AL_VELOCITY
, vec
);
639 void sound::gain(scalar gain
)
641 alSourcef(impl_
->source_
, AL_GAIN
, float(gain
));
644 void sound::pitch(scalar pitch
)
646 alSourcef(impl_
->source_
, AL_PITCH
, float(pitch
));
649 void sound::loop(bool looping
)
652 impl_
->loop(looping
);
656 void sound::listener_position(const vector3
& position
)
658 float vec
[] = {position
[0], position
[1], position
[2]};
659 alListenerfv(AL_POSITION
, vec
);
662 void sound::listener_velocity(const vector3
& velocity
)
664 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
665 alListenerfv(AL_VELOCITY
, vec
);
668 void sound::listener_orientation(const vector3
& forward
,
672 vec
[0] = float(forward
[0]);
673 vec
[1] = float(forward
[1]);
674 vec
[2] = float(forward
[2]);
675 vec
[3] = float(up
[0]);
676 vec
[4] = float(up
[1]);
677 vec
[5] = float(up
[2]);
678 alListenerfv(AL_ORIENTATION
, vec
);