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 **************************************************************************/
17 #include <boost/algorithm/string.hpp>
18 #include <boost/cstdint.hpp>
19 #include <boost/noncopyable.hpp>
22 #include <vorbis/codec.h>
23 #include <vorbis/vorbisfile.h>
28 #include "resource.hh"
33 #define BUF_SIZE (4096 * 64)
36 #define NUM_BUFFERS (4)
43 *************************************************************************/
51 if (retain_count
++ == 0)
53 al_device
= alcOpenDevice(0);
54 al_context
= alcCreateContext(al_device
, 0);
55 if (!al_device
|| !al_context
)
57 const char* error
= alcGetString(al_device
,
58 alcGetError(al_device
));
59 log_error("audio subsystem initialization failure", error
);
63 alcMakeContextCurrent(al_context
);
64 log_info
<< "opened sound device `"
65 << alcGetString(al_device
,
66 ALC_DEFAULT_DEVICE_SPECIFIER
)
73 sound_backend(const sound_backend
& backend
)
78 sound_backend
& operator=(const sound_backend
& backend
)
86 if (--retain_count
== 0)
88 alcMakeContextCurrent(0);
89 alcDestroyContext(al_context
);
90 alcCloseDevice(al_device
);
95 static int retain_count
;
96 static ALCdevice
* al_device
;
97 static ALCcontext
* al_context
;
100 int sound_backend::retain_count
= 0;
101 ALCdevice
* sound_backend::al_device
;
102 ALCcontext
* sound_backend::al_context
;
105 class sound_resource
;
106 typedef resource_handle
<sound_resource
> sound_handle
;
108 MOOF_REGISTER_RESOURCE(sound_resource
, ogg
, sounds
);
112 *************************************************************************/
118 typedef hash
<ALuint
,int,hash_function
> retcount_lookup
;
122 buffer_((ALuint
)-1) {}
124 buffer(const void* data
,
129 alGenBuffers(1, &buffer_
);
130 alBufferData(buffer_
, format
, data
, size
, freq
);
132 retain_counts_
[buffer_
] = 1;
135 buffer(const buffer
& buf
)
137 buffer_
= buf
.buffer_
;
141 buffer
& operator = (const buffer
& buf
)
143 buffer_
= buf
.buffer_
;
154 void queue(ALuint source
) const
158 alSourceQueueBuffers(source
, 1, &buffer_
);
163 static buffer
unqueue(ALuint source
)
165 ALuint buf
= (ALuint
)-1;
166 alSourceUnqueueBuffers(source
, 1, &buf
);
170 void set(ALuint source
) const
172 if (*this) alSourcei(source
, AL_BUFFER
, buffer_
);
175 operator bool () const
177 return buffer_
!= (ALuint
)-1;
183 explicit buffer(ALuint buf
) :
189 retcount_lookup::iterator it
= retain_counts_
.find(buffer_
);
190 if (it
.valid()) ++it
->second
;
195 retcount_lookup::iterator it
= retain_counts_
.find(buffer_
);
196 if (it
.valid() && --it
->second
<= 0)
198 alDeleteBuffers(1, &buffer_
);
199 retain_counts_
.erase(it
);
205 sound_backend backend_
;
207 static retcount_lookup retain_counts_
;
210 buffer::retcount_lookup
buffer::retain_counts_
;
215 *************************************************************************/
217 class sound_resource
: public boost::noncopyable
221 sound_resource(const std::string
& path
)
223 if (ov_fopen((char*)path
.c_str(), &file_
) < 0)
225 throw std::runtime_error("problem reading audio: " + path
);
235 bool read(buffer
& buf
) const
243 if (ov_pcm_seek_lap(&file_
, 0) != 0)
245 log_warning("vorbis seek error");
249 char data
[64*BUF_SIZE
];
252 while (size
< sizeof(data
))
255 int result
= ov_read(&file_
,
256 data
+ size
, sizeof(data
) - size
,
264 else if (result
== 0 && size
> 0)
266 vorbis_info
* info
= ov_info(&file_
, section
);
267 buffer_
= buffer(data
, size
,
268 get_audio_format(info
), info
->rate
);
274 log_warning("vorbis playback error");
279 if (size
>= sizeof(data
)) log_warning("sample is too big to play");
284 bool read(buffer
& buf
, uint64_t& sample
) const
286 if (ov_pcm_seek_lap(&file_
, sample
) != 0)
288 log_warning("vorbis seek error");
294 int result
= ov_read(&file_
, data
, sizeof(data
), 0, 2, 1, §ion
);
298 vorbis_info
* info
= ov_info(&file_
, section
);
299 buf
= buffer(data
, result
, get_audio_format(info
), info
->rate
);
300 sample
= ov_pcm_tell(&file_
);
304 if (result
< 0) log_warning("vorbis playback error");
309 static ALenum
get_audio_format(const vorbis_info
* info
)
311 if (info
->channels
== 1) return AL_FORMAT_MONO16
;
312 else return AL_FORMAT_STEREO16
;
318 mutable OggVorbis_File file_
;
319 mutable buffer buffer_
;
324 *************************************************************************/
335 impl(const std::string
& name
)
348 alGenSources(1, &source_
);
350 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
351 alSourcef(source_
, AL_PITCH
, 1.0f
);
352 alSourcef(source_
, AL_GAIN
, 1.0f
);
353 alSourcefv(source_
, AL_POSITION
, zero
);
354 alSourcefv(source_
, AL_VELOCITY
, zero
);
360 alDeleteSources(1, &source_
);
366 if (queue_
.empty()) return;
368 sound_handle handle
= queue_
.front();
371 if (handle
->read(buf
))
374 alSourcei(source_
, AL_LOOPING
, is_looping_
);
375 alSourcePlay(source_
);
381 if (queue_
.empty()) return;
385 alSourcei(source_
, AL_LOOPING
, false);
387 sound_handle handle
= queue_
.front();
389 for (int i
= 0; i
< NUM_BUFFERS
; ++i
)
392 if (handle
->read(buf
, sample_
))
398 log_error("failed to start stream");
403 alGetSourcei(source_
, AL_BUFFERS_QUEUED
, &queued
);
407 if (!stream_timer_
.is_valid())
409 stream_timer_
.init(boost::bind(&impl::stream_update
, this, _1
, _2
),
410 SCALAR(0.5), timer::repeat
);
413 alSourcePlay(source_
);
422 alGetSourcei(source_
, AL_BUFFERS_PROCESSED
, &finished
);
424 while (finished
-- > 0)
426 buffer::unqueue(source_
);
427 bool streamed
= false;
428 //if (handle->is_loaded())
430 //streamed = handle->read(buf);
435 sound_handle handle
= queue_
.front();
436 streamed
= handle
->read(buf
, sample_
);
445 // the buffer couldn't be streamed, so get rid of it
451 // begin the next buffer in the queue
452 handle
->read(buf
, sample_
);
455 else if (is_looping_
)
457 // reload the same buffer
458 queue_
.push_back(handle
);
459 handle
->read(buf
, sample_
);
464 // nothing more to play, stopping...
466 queue_
.push_back(handle
);
472 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
474 // restart playing if we're stopped but supposed to be playing...
475 // this means we didn't queue enough and the audio skipped :-(
476 if (is_playing_
&& state
!= AL_PLAYING
)
478 alSourcePlay(source_
);
485 alSourceStop(source_
);
488 stream_timer_
.invalidate();
490 // TODO: clear buffers if streaming
495 alSourcePause(source_
);
498 stream_timer_
.invalidate();
503 alSourceRewind(source_
);
508 void sample(const std::string
& name
)
511 alSourcei(source_
, AL_BUFFER
, AL_NONE
);
518 void enqueue(const std::string
& name
)
520 sound_handle handle
= resource::load(name
, "ogg");
521 queue_
.push_back(handle
);
525 bool is_playing() const
528 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
530 if (state
== AL_PLAYING
) return true;
531 else return is_playing_
;
535 void loop(bool looping
)
537 is_looping_
= looping
;
540 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
542 if (type
!= AL_STREAMING
)
544 alSourcei(source_
, AL_LOOPING
, is_looping_
);
549 void stream_update(timer
& timer
, scalar t
)
552 // TODO: might be nice to also allow using threads for streaming
553 // rather than a timer, probably as a compile-time option
562 std::deque
<sound_handle
> queue_
;
567 sound_backend backend_
;
573 impl_(new sound::impl
) {}
575 sound::sound(const std::string
& path
) :
577 impl_(new sound::impl(path
)) {}
580 void sound::sample(const std::string
& path
)
586 void sound::enqueue(const std::string
& path
)
589 impl_
->enqueue(path
);
620 if (is_playing()) pause();
624 bool sound::is_playing() const
627 return impl_
->is_playing();
631 void sound::position(const vector3
& position
)
633 float vec
[3] = {position
[0], position
[1], position
[2]};
634 alSourcefv(impl_
->source_
, AL_POSITION
, vec
);
637 void sound::velocity(const vector3
& velocity
)
639 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
640 alSourcefv(impl_
->source_
, AL_VELOCITY
, vec
);
643 void sound::gain(scalar gain
)
645 alSourcef(impl_
->source_
, AL_GAIN
, float(gain
));
648 void sound::pitch(scalar pitch
)
650 alSourcef(impl_
->source_
, AL_PITCH
, float(pitch
));
653 void sound::loop(bool looping
)
656 impl_
->loop(looping
);
660 void sound::listener_position(const vector3
& position
)
662 float vec
[] = {position
[0], position
[1], position
[2]};
663 alListenerfv(AL_POSITION
, vec
);
666 void sound::listener_velocity(const vector3
& velocity
)
668 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
669 alListenerfv(AL_VELOCITY
, vec
);
672 void sound::listener_orientation(const vector3
& forward
,
676 vec
[0] = float(forward
[0]);
677 vec
[1] = float(forward
[1]);
678 vec
[2] = float(forward
[2]);
679 vec
[3] = float(up
[0]);
680 vec
[4] = float(up
[1]);
681 vec
[5] = float(up
[2]);
682 alListenerfv(AL_ORIENTATION
, vec
);