2 /*] Copyright (c) 2009-2011, Charles McGarvey [*****************************
3 **] All rights reserved.
5 * Distributable under the terms and conditions of the 2-clause BSD license;
6 * see the file COPYING for a complete text of the license.
8 *****************************************************************************/
13 #include <boost/algorithm/string.hpp>
14 #include <boost/cstdint.hpp>
15 #include <boost/noncopyable.hpp>
18 #include <vorbis/codec.h>
19 #include <vorbis/vorbisfile.h>
23 #include "resource.hh"
32 #define NUM_SAMPLE_BITS 16
38 thread stream_thread_
;
47 if (retain_count
++ == 0)
49 al_device
= alcOpenDevice(0);
50 al_context
= alcCreateContext(al_device
, 0);
51 if (!al_device
|| !al_context
)
53 const char* error
= alcGetString(al_device
,
54 alcGetError(al_device
));
55 log_error("audio subsystem initialization failure", error
);
59 alcMakeContextCurrent(al_context
);
60 log_info
<< "opened sound device `" <<
61 alcGetString(al_device
, ALC_DEFAULT_DEVICE_SPECIFIER
) <<
67 sound_backend(const sound_backend
& backend
)
72 sound_backend
& operator = (const sound_backend
& backend
)
80 if (--retain_count
== 0)
82 alcMakeContextCurrent(0);
83 alcDestroyContext(al_context
);
84 alcCloseDevice(al_device
);
88 static int retain_count
;
89 static ALCdevice
* al_device
;
90 static ALCcontext
* al_context
;
93 int sound_backend::retain_count
= 0;
94 ALCdevice
* sound_backend::al_device
;
95 ALCcontext
* sound_backend::al_context
;
98 typedef resource_handle
<sound_resource
> sound_handle
;
100 MOOF_REGISTER_RESOURCE(sound_resource
, ogg
, sounds
);
103 class sound_resource
: public boost::noncopyable
107 sound_resource(const std::string
& path
) :
111 if (ov_fopen((char*)path
.c_str(), &file_
) < 0)
112 throw std::runtime_error("problem reading audio: " +
119 if (buffer_
) alDeleteBuffers(1, &buffer_
);
122 ALuint
read_all() const
124 if (buffer_
) return buffer_
;
126 if (ov_pcm_seek(&file_
, 0) != 0)
128 log_warning("vorbis seek error");
132 ogg_int64_t samples
= ov_pcm_total(&file_
, 0);
134 char data
[2 * samples
* (NUM_SAMPLE_BITS
/ 8)];
137 while (size
< sizeof(data
))
140 int result
= ov_read(&file_
, data
+ size
,
141 sizeof(data
) - size
, 0, 2, 1, §ion
);
147 else if (result
== 0 && size
> 0)
149 vorbis_info
* info
= ov_info(&file_
, section
);
152 alGenBuffers(1, &buffer
);
153 alBufferData(buffer
, get_audio_format(info
),
154 data
, size
, info
->rate
);
161 log_warning("vorbis playback error");
168 bool read(ALuint buffer
, uint64_t& sample
) const
170 if ((sample
== sample_
&& ov_pcm_seek_lap(&file_
, sample
) != 0) ||
171 (sample
!= sample_
&& ov_pcm_seek(&file_
, sample
) != 0))
176 int result
= ov_read(&file_
, data
, sizeof(data
),
181 vorbis_info
* info
= ov_info(&file_
, section
);
182 alBufferData(buffer
, get_audio_format(info
),
183 data
, result
, info
->rate
);
184 sample_
= sample
= ov_pcm_tell(&file_
);
189 log_warning("vorbis playback error");
194 bool read(ALuint buffer
, uint64_t& sample
, sound_handle other
) const
196 ov_crosslap(&other
->file_
, &file_
);
200 int result
= ov_read(&file_
, data
, sizeof(data
),
205 vorbis_info
* info
= ov_info(&file_
, section
);
206 alBufferData(buffer
, get_audio_format(info
),
207 data
, result
, info
->rate
);
208 sample_
= sample
= ov_pcm_tell(&file_
);
213 log_warning("vorbis playback error");
218 // determines the correct number of fixed-size buffers needed to hold
219 // a given number of seconds of PCM audio.
220 int num_buffers(scalar seconds
) const
222 vorbis_info
* info
= ov_info(&file_
, -1);
223 int count
= scalar(info
->rate
) * // sample rate
224 scalar(info
->channels
) * // channels
225 scalar(NUM_SAMPLE_BITS
) * // sample size
227 SCALAR(0.125) / // bits to bytes
228 scalar(BUF_SIZE
); // individual buffer size
232 static ALenum
get_audio_format(const vorbis_info
* info
)
234 if (info
->channels
== 1)
235 return AL_FORMAT_MONO16
;
236 else if (info
->channels
== 2)
237 return AL_FORMAT_STEREO16
;
239 log_error("unsupported number of channels:", info
->channels
);
245 mutable OggVorbis_File file_
;
246 mutable ALuint buffer_
;
247 mutable uint64_t sample_
;
249 sound_backend backend_
;
262 impl(const std::string
& name
)
274 buffer_size_
= SCALAR(1.0);
276 alGenSources(1, &source_
);
278 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
279 alSourcef(source_
, AL_PITCH
, 1.0f
);
280 alSourcef(source_
, AL_GAIN
, 1.0f
);
281 alSourcefv(source_
, AL_POSITION
, zero
);
282 alSourcefv(source_
, AL_VELOCITY
, zero
);
290 alDeleteSources(1, &source_
);
294 void deplete_stream()
299 alGetSourcei(source_
, AL_BUFFERS_QUEUED
, &queued
);
300 ALuint buffers
[queued
];
301 alSourceUnqueueBuffers(source_
, queued
, buffers
);
302 alDeleteBuffers(queued
, buffers
);
306 alSourcei(source_
, AL_BUFFER
, AL_NONE
);
312 if (queue_
.empty() || is_playing_
) return;
315 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
322 start_update_timer();
323 num_buffers_
= queue_
.front()->num_buffers(buffer_size_
);
326 alSourcei(source_
, AL_LOOPING
, false);
330 ALuint buffer
= queue_
.front()->read_all();
331 alSourcei(source_
, AL_BUFFER
, buffer
);
332 alSourcei(source_
, AL_LOOPING
, is_looping_
);
339 start_update_timer();
345 alSourcePlay(source_
);
348 void start_update_timer()
350 stream_timer_
.init(boost::bind(&impl::stream_update
,
352 SCALAR(0.1), timer::repeat
);
354 thread::main_runloop().add_timer(stream_timer_
);
357 //thread.runloop().add_timer(stream_timer_);
358 //if (!stream_thread_.is_valid())
359 //stream_thread_ = thread::detach(stream_timer_);
360 // FIXME: proper locking needs to be done; this is causing crashes
361 // on shutdown when sound resources are destroyed yet the stream
362 // thread is neither shutdown or notified.
368 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
369 ASSERT(type
!= AL_STATIC
&& "source shouldn't be static");
373 alGetSourcei(source_
, AL_BUFFERS_QUEUED
, &queued
);
374 alGetSourcei(source_
, AL_BUFFERS_PROCESSED
, &processed
);
376 int target_diff
= num_buffers_
- queued
+ processed
;
378 ALuint bufs
[processed
];
379 alSourceUnqueueBuffers(source_
, processed
, bufs
);
380 for (int i
= 0; i
< processed
; ++i
)
382 ALuint
& buf
= bufs
[i
];
386 if (queue_buffer(buf
))
392 alDeleteBuffers(processed
- i
- 1, &bufs
[i
+1]);
396 else alDeleteBuffers(1, &buf
);
399 if (stream_timer_
.mode() != timer::invalid
&& 0 < target_diff
)
401 for (int i
= 0; i
< target_diff
; ++i
)
403 if (!queue_buffer(AL_NONE
)) break;
410 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
411 if (state
!= AL_PLAYING
)
413 // restart playing if we're stopped but supposed to be
414 // playing... this means we didn't queue enough...
415 alSourcePlay(source_
);
416 log_warning("not enough audio buffered");
421 // TODO: this function is ugly
422 bool queue_buffer(ALuint buffer
)
424 if (buffer
== AL_NONE
) alGenBuffers(1, &buffer
);
426 bool looped_once
= false;
428 while (0 < queue_
.size())
430 sound_handle sound
= queue_
.front();
432 bool result
= sound
->read(buffer
, sample_
);
435 alSourceQueueBuffers(source_
, 1, &buffer
);
443 if (0 < queue_
.size())
445 sound_handle new_sound
= queue_
.front();
446 result
= new_sound
->read(buffer
, sample_
, sound
);
449 queue_
.push_back(new_sound
);
450 alSourceQueueBuffers(source_
, 1, &buffer
);
451 num_buffers_
= new_sound
->num_buffers(buffer_size_
);
457 queue_
.push_front(sound
);
460 else if (is_looping_
&& !looped_once
)
462 queue_
.push_back(sound
);
472 alDeleteBuffers(1, &buffer
);
474 stream_timer_
.invalidate();
480 alSourceStop(source_
);
483 stream_timer_
.invalidate();
490 alSourcePause(source_
);
493 stream_timer_
.invalidate();
496 void sample(const std::string
& name
)
503 is_streaming_
= false;
506 void queue(const std::string
& name
)
508 sound_handle handle
= resource::load(name
, "ogg");
509 if (handle
) queue_
.push_back(handle
);
510 is_streaming_
= true;
513 bool is_playing() const
516 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
518 if (state
== AL_PLAYING
) return true;
519 else return is_playing_
;
522 void loop(bool looping
)
524 is_looping_
= looping
;
527 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
528 if (type
!= AL_STREAMING
)
529 alSourcei(source_
, AL_LOOPING
, is_looping_
);
532 void stream_update(timer
& timer
, scalar t
)
534 log_debug("blaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhrrrrrg");
544 std::deque
<sound_handle
> queue_
;
551 sound_backend backend_
;
557 impl_(new sound::impl
) {}
559 sound::sound(const std::string
& path
) :
561 impl_(new sound::impl(path
)) {}
563 void sound::sample(const std::string
& path
)
569 void sound::queue(const std::string
& path
)
595 if (is_playing()) pause();
599 bool sound::is_playing() const
602 return impl_
->is_playing();
605 void sound::buffer_size(scalar seconds
)
607 impl_
->buffer_size_
= seconds
;
610 void sound::position(const vector3
& position
)
612 float vec
[3] = {position
[0], position
[1], position
[2]};
613 alSourcefv(impl_
->source_
, AL_POSITION
, vec
);
616 void sound::velocity(const vector3
& velocity
)
618 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
619 alSourcefv(impl_
->source_
, AL_VELOCITY
, vec
);
622 void sound::gain(scalar gain
)
624 alSourcef(impl_
->source_
, AL_GAIN
, float(gain
));
627 void sound::pitch(scalar pitch
)
629 alSourcef(impl_
->source_
, AL_PITCH
, float(pitch
));
632 void sound::loop(bool looping
)
635 impl_
->loop(looping
);
638 void sound::listener_position(const vector3
& position
)
640 float vec
[] = {position
[0], position
[1], position
[2]};
641 alListenerfv(AL_POSITION
, vec
);
644 void sound::listener_velocity(const vector3
& velocity
)
646 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
647 alListenerfv(AL_VELOCITY
, vec
);
650 void sound::listener_orientation(const vector3
& forward
,
654 vec
[0] = float(forward
[0]);
655 vec
[1] = float(forward
[1]);
656 vec
[2] = float(forward
[2]);
657 vec
[3] = float(up
[0]);
658 vec
[4] = float(up
[1]);
659 vec
[5] = float(up
[2]);
660 alListenerfv(AL_ORIENTATION
, vec
);