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"
33 #define NUM_SAMPLE_BITS 16
39 thread stream_thread_
;
48 if (retain_count
++ == 0)
50 al_device
= alcOpenDevice(0);
51 al_context
= alcCreateContext(al_device
, 0);
52 if (!al_device
|| !al_context
)
54 const char* error
= alcGetString(al_device
,
55 alcGetError(al_device
));
56 log_error("audio subsystem initialization failure", error
);
60 alcMakeContextCurrent(al_context
);
61 log_info
<< "opened sound device `" <<
62 alcGetString(al_device
, ALC_DEFAULT_DEVICE_SPECIFIER
) <<
68 sound_backend(const sound_backend
& backend
)
73 sound_backend
& operator = (const sound_backend
& backend
)
81 if (--retain_count
== 0)
83 alcMakeContextCurrent(0);
84 alcDestroyContext(al_context
);
85 alcCloseDevice(al_device
);
89 static int retain_count
;
90 static ALCdevice
* al_device
;
91 static ALCcontext
* al_context
;
94 int sound_backend::retain_count
= 0;
95 ALCdevice
* sound_backend::al_device
;
96 ALCcontext
* sound_backend::al_context
;
100 typedef resource_handle
<sound_resource
> sound_handle
;
102 MOOF_REGISTER_RESOURCE(sound_resource
, ogg
, sounds
);
104 class sound_resource
: public boost::noncopyable
108 sound_resource(const std::string
& path
) :
112 if (ov_fopen((char*)path
.c_str(), &file_
) < 0)
113 throw std::runtime_error("problem reading audio: " +
120 if (buffer_
) alDeleteBuffers(1, &buffer_
);
123 ALuint
read_all() const
125 if (buffer_
) return buffer_
;
127 if (ov_pcm_seek(&file_
, 0) != 0)
129 log_warning("vorbis seek error");
133 ogg_int64_t samples
= ov_pcm_total(&file_
, 0);
135 char data
[2 * samples
* (NUM_SAMPLE_BITS
/ 8)];
138 while (size
< sizeof(data
))
141 int result
= ov_read(&file_
, data
+ size
,
142 sizeof(data
) - size
, 0, 2, 1, §ion
);
148 else if (result
== 0 && size
> 0)
150 vorbis_info
* info
= ov_info(&file_
, section
);
153 alGenBuffers(1, &buffer
);
154 alBufferData(buffer
, get_audio_format(info
),
155 data
, size
, info
->rate
);
162 log_warning("vorbis playback error");
169 bool read(ALuint buffer
, uint64_t& sample
) const
171 if ((sample
== sample_
&& ov_pcm_seek_lap(&file_
, sample
) != 0) ||
172 (sample
!= sample_
&& ov_pcm_seek(&file_
, sample
) != 0))
177 int result
= ov_read(&file_
, data
, sizeof(data
),
182 vorbis_info
* info
= ov_info(&file_
, section
);
183 alBufferData(buffer
, get_audio_format(info
),
184 data
, result
, info
->rate
);
185 sample_
= sample
= ov_pcm_tell(&file_
);
190 log_warning("vorbis playback error");
195 bool read(ALuint buffer
, uint64_t& sample
, sound_handle other
) const
197 ov_crosslap(&other
->file_
, &file_
);
201 int result
= ov_read(&file_
, data
, sizeof(data
),
206 vorbis_info
* info
= ov_info(&file_
, section
);
207 alBufferData(buffer
, get_audio_format(info
),
208 data
, result
, info
->rate
);
209 sample_
= sample
= ov_pcm_tell(&file_
);
214 log_warning("vorbis playback error");
219 // determines the correct number of fixed-size buffers needed to hold
220 // a given number of seconds of PCM audio.
221 int num_buffers(scalar seconds
) const
223 vorbis_info
* info
= ov_info(&file_
, -1);
224 int count
= scalar(info
->rate
) * // sample rate
225 scalar(info
->channels
) * // channels
226 scalar(NUM_SAMPLE_BITS
) * // sample size
228 SCALAR(0.125) / // bits to bytes
229 scalar(BUF_SIZE
); // individual buffer size
233 static ALenum
get_audio_format(const vorbis_info
* info
)
235 if (info
->channels
== 1)
236 return AL_FORMAT_MONO16
;
237 else if (info
->channels
== 2)
238 return AL_FORMAT_STEREO16
;
240 log_error("unsupported number of channels:", info
->channels
);
246 mutable OggVorbis_File file_
;
247 mutable ALuint buffer_
;
248 mutable uint64_t sample_
;
250 sound_backend backend_
;
263 impl(const std::string
& name
)
275 buffer_size_
= SCALAR(1.0);
277 alGenSources(1, &source_
);
279 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
280 alSourcef(source_
, AL_PITCH
, 1.0f
);
281 alSourcef(source_
, AL_GAIN
, 1.0f
);
282 alSourcefv(source_
, AL_POSITION
, zero
);
283 alSourcefv(source_
, AL_VELOCITY
, zero
);
291 alDeleteSources(1, &source_
);
295 void deplete_stream()
300 alGetSourcei(source_
, AL_BUFFERS_QUEUED
, &queued
);
301 ALuint buffers
[queued
];
302 alSourceUnqueueBuffers(source_
, queued
, buffers
);
303 alDeleteBuffers(queued
, buffers
);
307 alSourcei(source_
, AL_BUFFER
, AL_NONE
);
313 if (queue_
.empty() || is_playing_
) return;
316 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
323 start_update_timer();
324 num_buffers_
= queue_
.front()->num_buffers(buffer_size_
);
327 alSourcei(source_
, AL_LOOPING
, false);
331 ALuint buffer
= queue_
.front()->read_all();
332 alSourcei(source_
, AL_BUFFER
, buffer
);
333 alSourcei(source_
, AL_LOOPING
, is_looping_
);
340 start_update_timer();
346 alSourcePlay(source_
);
349 void start_update_timer()
351 stream_timer_
.init(boost::bind(&impl::stream_update
,
353 SCALAR(0.1), timer::repeat
);
355 thread::main_runloop().add_timer(stream_timer_
);
358 //thread.runloop().add_timer(stream_timer_);
359 //if (!stream_thread_.is_valid())
360 //stream_thread_ = thread::detach(stream_timer_);
361 // FIXME: proper locking needs to be done; this is causing crashes
362 // on shutdown when sound resources are destroyed yet the stream
363 // thread is neither shutdown or notified.
369 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
370 ASSERT(type
!= AL_STATIC
&& "source shouldn't be static");
374 alGetSourcei(source_
, AL_BUFFERS_QUEUED
, &queued
);
375 alGetSourcei(source_
, AL_BUFFERS_PROCESSED
, &processed
);
377 int target_diff
= num_buffers_
- queued
+ processed
;
379 ALuint bufs
[processed
];
380 alSourceUnqueueBuffers(source_
, processed
, bufs
);
381 for (int i
= 0; i
< processed
; ++i
)
383 ALuint
& buf
= bufs
[i
];
387 if (queue_buffer(buf
))
393 alDeleteBuffers(processed
- i
- 1, &bufs
[i
+1]);
397 else alDeleteBuffers(1, &buf
);
400 if (stream_timer_
.mode() != timer::invalid
&& 0 < target_diff
)
402 for (int i
= 0; i
< target_diff
; ++i
)
404 if (!queue_buffer(AL_NONE
)) break;
411 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
412 if (state
!= AL_PLAYING
)
414 // restart playing if we're stopped but supposed to be
415 // playing... this means we didn't queue enough...
416 alSourcePlay(source_
);
417 log_warning("not enough audio buffered");
422 // TODO: this function is ugly
423 bool queue_buffer(ALuint buffer
)
425 if (buffer
== AL_NONE
) alGenBuffers(1, &buffer
);
427 bool looped_once
= false;
429 while (0 < queue_
.size())
431 sound_handle sound
= queue_
.front();
433 bool result
= sound
->read(buffer
, sample_
);
436 alSourceQueueBuffers(source_
, 1, &buffer
);
444 if (0 < queue_
.size())
446 sound_handle new_sound
= queue_
.front();
447 result
= new_sound
->read(buffer
, sample_
, sound
);
450 queue_
.push_back(new_sound
);
451 alSourceQueueBuffers(source_
, 1, &buffer
);
452 num_buffers_
= new_sound
->num_buffers(buffer_size_
);
458 queue_
.push_front(sound
);
461 else if (is_looping_
&& !looped_once
)
463 queue_
.push_back(sound
);
473 alDeleteBuffers(1, &buffer
);
475 stream_timer_
.invalidate();
481 alSourceStop(source_
);
484 stream_timer_
.invalidate();
491 alSourcePause(source_
);
494 stream_timer_
.invalidate();
497 void sample(const std::string
& name
)
504 is_streaming_
= false;
507 void queue(const std::string
& name
)
509 sound_handle handle
= resource::load(name
, "ogg");
510 if (handle
) queue_
.push_back(handle
);
511 is_streaming_
= true;
514 bool is_playing() const
517 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
519 if (state
== AL_PLAYING
) return true;
520 else return is_playing_
;
523 void loop(bool looping
)
525 is_looping_
= looping
;
528 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
529 if (type
!= AL_STREAMING
)
530 alSourcei(source_
, AL_LOOPING
, is_looping_
);
533 void stream_update(timer
& timer
, scalar t
)
535 log_debug("blaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhrrrrrg");
545 std::deque
<sound_handle
> queue_
;
552 sound_backend backend_
;
558 impl_(new sound::impl
) {}
560 sound::sound(const std::string
& path
) :
562 impl_(new sound::impl(path
)) {}
564 void sound::sample(const std::string
& path
)
570 void sound::queue(const std::string
& path
)
596 if (is_playing()) pause();
600 bool sound::is_playing() const
603 return impl_
->is_playing();
606 void sound::buffer_size(scalar seconds
)
608 impl_
->buffer_size_
= seconds
;
611 void sound::position(const vector3
& position
)
613 float vec
[3] = {position
[0], position
[1], position
[2]};
614 alSourcefv(impl_
->source_
, AL_POSITION
, vec
);
617 void sound::velocity(const vector3
& velocity
)
619 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
620 alSourcefv(impl_
->source_
, AL_VELOCITY
, vec
);
623 void sound::gain(scalar gain
)
625 alSourcef(impl_
->source_
, AL_GAIN
, float(gain
));
628 void sound::pitch(scalar pitch
)
630 alSourcef(impl_
->source_
, AL_PITCH
, float(pitch
));
633 void sound::loop(bool looping
)
636 impl_
->loop(looping
);
639 void sound::listener_position(const vector3
& position
)
641 float vec
[] = {position
[0], position
[1], position
[2]};
642 alListenerfv(AL_POSITION
, vec
);
645 void sound::listener_velocity(const vector3
& velocity
)
647 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
648 alListenerfv(AL_VELOCITY
, vec
);
651 void sound::listener_orientation(const vector3
& forward
,
655 vec
[0] = float(forward
[0]);
656 vec
[1] = float(forward
[1]);
657 vec
[2] = float(forward
[2]);
658 vec
[3] = float(up
[0]);
659 vec
[4] = float(up
[1]);
660 vec
[5] = float(up
[2]);
661 alListenerfv(AL_ORIENTATION
, vec
);