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"
30 #define BUF_SIZE (4096)
33 #define NUM_SAMPLE_BITS (16)
40 *************************************************************************/
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
,
63 ALC_DEFAULT_DEVICE_SPECIFIER
)
70 sound_backend(const sound_backend
& backend
)
75 sound_backend
& operator=(const sound_backend
& backend
)
83 if (--retain_count
== 0)
85 alcMakeContextCurrent(0);
86 alcDestroyContext(al_context
);
87 alcCloseDevice(al_device
);
92 static int retain_count
;
93 static ALCdevice
* al_device
;
94 static ALCcontext
* al_context
;
97 int sound_backend::retain_count
= 0;
98 ALCdevice
* sound_backend::al_device
;
99 ALCcontext
* sound_backend::al_context
;
102 class sound_resource
;
103 typedef resource_handle
<sound_resource
> sound_handle
;
105 MOOF_REGISTER_RESOURCE(sound_resource
, ogg
, sounds
);
109 *************************************************************************/
111 class sound_resource
: public boost::noncopyable
115 sound_resource(const std::string
& path
) :
119 if (ov_fopen((char*)path
.c_str(), &file_
) < 0)
121 throw std::runtime_error("problem reading audio: " + path
);
128 if (buffer_
) alDeleteBuffers(1, &buffer_
);
132 ALuint
read_all() const
134 if (buffer_
) return buffer_
;
136 if (ov_pcm_seek(&file_
, 0) != 0)
138 log_warning("vorbis seek error");
142 ogg_int64_t samples
= ov_pcm_total(&file_
, 0);
144 char data
[2 * samples
* (NUM_SAMPLE_BITS
/ 8)];
147 while (size
< sizeof(data
))
150 int result
= ov_read(&file_
,
151 data
+ size
, sizeof(data
) - size
,
158 else if (result
== 0 && size
> 0)
160 vorbis_info
* info
= ov_info(&file_
, section
);
163 alGenBuffers(1, &buffer
);
164 alBufferData(buffer
, get_audio_format(info
),
165 data
, size
, info
->rate
);
172 log_warning("vorbis playback error");
180 bool read(ALuint buffer
, uint64_t& sample
) const
182 if ((sample
== sample_
&& ov_pcm_seek_lap(&file_
, sample
) != 0) ||
183 (sample
!= sample_
&& ov_pcm_seek(&file_
, sample
) != 0))
190 int result
= ov_read(&file_
, data
, sizeof(data
), 0, 2, 1, §ion
);
194 vorbis_info
* info
= ov_info(&file_
, section
);
195 alBufferData(buffer
, get_audio_format(info
),
196 data
, result
, info
->rate
);
197 sample_
= sample
= ov_pcm_tell(&file_
);
200 else if (result
< 0) log_warning("vorbis playback error");
205 bool read(ALuint buffer
, uint64_t& sample
, sound_handle other
) const
207 ov_crosslap(&other
->file_
, &file_
);
211 int result
= ov_read(&file_
, data
, sizeof(data
), 0, 2, 1, §ion
);
215 vorbis_info
* info
= ov_info(&file_
, section
);
216 alBufferData(buffer
, get_audio_format(info
),
217 data
, result
, info
->rate
);
218 sample_
= sample
= ov_pcm_tell(&file_
);
221 else if (result
< 0) log_warning("vorbis playback error");
227 // determines the correct number of fixed-size buffers needed to hold a
228 // given number of seconds of PCM audio.
229 int num_buffers(scalar seconds
) const
231 vorbis_info
* info
= ov_info(&file_
, -1);
232 int count
= scalar(info
->rate
) * // sample rate
233 scalar(info
->channels
) * // channels
234 scalar(NUM_SAMPLE_BITS
) * // sample size
236 SCALAR(0.125) / // bits to bytes
237 scalar(BUF_SIZE
); // individual buffer size
242 static ALenum
get_audio_format(const vorbis_info
* info
)
244 if (info
->channels
== 1) return AL_FORMAT_MONO16
;
245 else if (info
->channels
== 2) return AL_FORMAT_STEREO16
;
247 log_error("unsupported number of channels:", info
->channels
);
254 mutable OggVorbis_File file_
;
255 mutable ALuint buffer_
;
256 mutable uint64_t sample_
;
258 sound_backend backend_
;
263 *************************************************************************/
274 impl(const std::string
& name
)
286 buffer_size_
= SCALAR(1.0);
288 alGenSources(1, &source_
);
290 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
291 alSourcef(source_
, AL_PITCH
, 1.0f
);
292 alSourcef(source_
, AL_GAIN
, 1.0f
);
293 alSourcefv(source_
, AL_POSITION
, zero
);
294 alSourcefv(source_
, AL_VELOCITY
, zero
);
302 alDeleteSources(1, &source_
);
306 void deplete_stream()
311 alGetSourcei(source_
, AL_BUFFERS_QUEUED
, &queued
);
312 ALuint buffers
[queued
];
313 alSourceUnqueueBuffers(source_
, queued
, buffers
);
314 alDeleteBuffers(queued
, buffers
);
316 else alSourcei(source_
, AL_BUFFER
, AL_NONE
);
321 if (queue_
.empty() || is_playing_
) return;
324 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
332 start_update_timer();
333 num_buffers_
= queue_
.front()->num_buffers(buffer_size_
);
336 alSourcei(source_
, AL_LOOPING
, false);
340 ALuint buffer
= queue_
.front()->read_all();
341 alSourcei(source_
, AL_BUFFER
, buffer
);
342 alSourcei(source_
, AL_LOOPING
, is_looping_
);
350 start_update_timer();
356 alSourcePlay(source_
);
359 void start_update_timer()
361 stream_timer_
.init(boost::bind(&impl::stream_update
, this, _1
, _2
),
362 SCALAR(0.1), timer::repeat
);
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
)) --target_diff
;
389 alDeleteBuffers(processed
- i
- 1, &bufs
[i
+1]);
393 else alDeleteBuffers(1, &buf
);
396 if (stream_timer_
.is_valid() && 0 < target_diff
)
398 for (int i
= 0; i
< target_diff
; ++i
)
400 if (!queue_buffer(AL_NONE
)) break;
407 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
408 if (state
!= AL_PLAYING
)
410 // restart playing if we're stopped but supposed to be
411 // playing... this means we didn't queue enough...
412 alSourcePlay(source_
);
413 log_warning("not enough audio buffered");
418 bool queue_buffer(ALuint buffer
)
420 if (buffer
== AL_NONE
) alGenBuffers(1, &buffer
);
422 bool looped_once
= false;
424 while (0 < queue_
.size())
426 sound_handle sound
= queue_
.front();
428 bool result
= sound
->read(buffer
, sample_
);
431 alSourceQueueBuffers(source_
, 1, &buffer
);
439 if (0 < queue_
.size())
441 sound_handle new_sound
= queue_
.front();
442 result
= new_sound
->read(buffer
, sample_
, sound
);
445 queue_
.push_back(new_sound
);
446 alSourceQueueBuffers(source_
, 1, &buffer
);
447 num_buffers_
= new_sound
->num_buffers(buffer_size_
);
453 queue_
.push_front(sound
);
456 else if (is_looping_
&& !looped_once
)
458 queue_
.push_back(sound
);
465 alDeleteBuffers(1, &buffer
);
467 stream_timer_
.invalidate();
474 alSourceStop(source_
);
477 stream_timer_
.invalidate();
484 alSourcePause(source_
);
487 stream_timer_
.invalidate();
491 void sample(const std::string
& name
)
498 is_streaming_
= false;
501 void queue(const std::string
& name
)
503 sound_handle handle
= resource::load(name
, "ogg");
504 if (handle
) queue_
.push_back(handle
);
505 is_streaming_
= true;
509 bool is_playing() const
512 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
514 if (state
== AL_PLAYING
) return true;
515 else return is_playing_
;
519 void loop(bool looping
)
521 is_looping_
= looping
;
524 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
525 if (type
!= AL_STREAMING
)
527 alSourcei(source_
, AL_LOOPING
, is_looping_
);
532 void stream_update(timer
& timer
, scalar t
)
534 //log_error("blaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhrrrrrg");
536 // TODO: might be nice to also allow using threads for streaming
537 // rather than a timer, probably as a compile-time option
547 std::deque
<sound_handle
> queue_
;
554 sound_backend backend_
;
560 impl_(new sound::impl
) {}
562 sound::sound(const std::string
& path
) :
564 impl_(new sound::impl(path
)) {}
567 void sound::sample(const std::string
& path
)
573 void sound::queue(const std::string
& path
)
600 if (is_playing()) pause();
604 bool sound::is_playing() const
607 return impl_
->is_playing();
610 void sound::buffer_size(scalar seconds
)
612 impl_
->buffer_size_
= seconds
;
616 void sound::position(const vector3
& position
)
618 float vec
[3] = {position
[0], position
[1], position
[2]};
619 alSourcefv(impl_
->source_
, AL_POSITION
, vec
);
622 void sound::velocity(const vector3
& velocity
)
624 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
625 alSourcefv(impl_
->source_
, AL_VELOCITY
, vec
);
628 void sound::gain(scalar gain
)
630 alSourcef(impl_
->source_
, AL_GAIN
, float(gain
));
633 void sound::pitch(scalar pitch
)
635 alSourcef(impl_
->source_
, AL_PITCH
, float(pitch
));
638 void sound::loop(bool looping
)
641 impl_
->loop(looping
);
645 void sound::listener_position(const vector3
& position
)
647 float vec
[] = {position
[0], position
[1], position
[2]};
648 alListenerfv(AL_POSITION
, vec
);
651 void sound::listener_velocity(const vector3
& velocity
)
653 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
654 alListenerfv(AL_VELOCITY
, vec
);
657 void sound::listener_orientation(const vector3
& forward
,
661 vec
[0] = float(forward
[0]);
662 vec
[1] = float(forward
[1]);
663 vec
[2] = float(forward
[2]);
664 vec
[3] = float(up
[0]);
665 vec
[4] = float(up
[1]);
666 vec
[5] = float(up
[2]);
667 alListenerfv(AL_ORIENTATION
, vec
);