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 **************************************************************************/
18 #include <boost/algorithm/string.hpp>
19 #include <boost/cstdint.hpp>
20 #include <boost/noncopyable.hpp>
23 #include <vorbis/codec.h>
24 #include <vorbis/vorbisfile.h>
30 #include "resource.hh"
35 #define BUF_SIZE (4096)
38 #define NUM_BUFFERS (16)
50 if (retain_count
++ == 0)
52 al_device
= alcOpenDevice(0);
53 al_context
= alcCreateContext(al_device
, 0);
54 if (!al_device
|| !al_context
)
56 const char* error
= alcGetString(al_device
,
57 alcGetError(al_device
));
58 log_error("audio subsystem initialization failure", error
);
62 alcMakeContextCurrent(al_context
);
63 log_info
<< "opened sound device `"
64 << alcGetString(al_device
,
65 ALC_DEFAULT_DEVICE_SPECIFIER
)
72 sound_backend(const sound_backend
& backend
)
77 sound_backend
& operator=(const sound_backend
& backend
)
85 if (--retain_count
== 0)
87 alcMakeContextCurrent(0);
88 alcDestroyContext(al_context
);
89 alcCloseDevice(al_device
);
90 log_info("unloaded sound device ALSA");
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
;
109 class sound_resource_loader
113 sound_resource_loader()
115 resource::register_type
<sound_resource
>("ogg");
118 ~sound_resource_loader()
120 resource::unregister_type("ogg");
124 static sound_resource_loader loader
;
134 typedef hash
<ALuint
,int,hash_function
> retcount_lookup
;
138 buffer_((ALuint
)-1) {}
140 buffer(const void* data
,
145 alGenBuffers(1, &buffer_
);
146 alBufferData(buffer_
, format
, data
, size
, freq
);
148 retain_counts_
[buffer_
] = 1;
149 log_warning("ctor buffer:", buffer_
);
152 buffer(const buffer
& buf
)
154 buffer_
= buf
.buffer_
;
158 buffer
& operator = (const buffer
& buf
)
160 buffer_
= buf
.buffer_
;
171 void queue(ALuint source
) const
175 alSourceQueueBuffers(source
, 1, &buffer_
);
177 log_warning("queued buffer:", buffer_
);
181 static buffer
unqueue(ALuint source
)
183 ALuint buf
= (ALuint
)-1;
184 alSourceUnqueueBuffers(source
, 1, &buf
);
185 log_warning("unqueued buffer:", buf
);
189 void set(ALuint source
) const
191 log_warning("set buffer:", buffer_
);
192 if (*this) alSourcei(source
, AL_BUFFER
, buffer_
);
195 operator bool () const
197 return buffer_
!= (ALuint
)-1;
203 explicit buffer(ALuint buf
) :
209 retcount_lookup::iterator it
= retain_counts_
.find(buffer_
);
210 if (it
.valid()) ++it
->second
;
215 retcount_lookup::iterator it
= retain_counts_
.find(buffer_
);
216 if (it
.valid() && --it
->second
<= 0)
218 alDeleteBuffers(1, &buffer_
);
219 retain_counts_
.erase(it
);
220 log_warning("kill buffer:", buffer_
);
226 sound_backend backend_
;
228 static retcount_lookup retain_counts_
;
231 buffer::retcount_lookup
buffer::retain_counts_
;
237 class sound_resource
: public boost::noncopyable
241 sound_resource(const std::string
& path
)
243 log_info("audio path is", path
);
244 if (ov_fopen((char*)path
.c_str(), &file_
) < 0)
246 throw std::runtime_error("problem reading audio: " + path
);
256 bool read(buffer
& buf
) const
264 if (ov_pcm_seek_lap(&file_
, 0) != 0)
266 log_warning("vorbis seek error");
270 char data
[64*BUF_SIZE
];
273 while (size
< sizeof(data
))
276 int result
= ov_read(&file_
,
277 data
+ size
, sizeof(data
) - size
,
285 else if (result
== 0 && size
> 0)
287 log_info("loaded", size
, "bytes from vorbis");
288 vorbis_info
* info
= ov_info(&file_
, section
);
289 buffer_
= buffer(data
, size
,
290 get_audio_format(info
), info
->rate
);
292 log_info("this section is", section
);
293 log_info("audio format is", get_audio_format(info
));
294 log_info("audio freq is", info
->rate
);
299 log_warning("vorbis playback error");
304 if (size
>= sizeof(data
)) log_warning("sample is too big to play");
309 bool read(buffer
& buf
, uint64_t& sample
) const
311 if (ov_pcm_seek_lap(&file_
, sample
) != 0)
313 log_warning("vorbis seek error");
319 int result
= ov_read(&file_
, data
, sizeof(data
), 0, 2, 1, §ion
);
323 log_info("loaded", result
, "bytes from vorbis");
324 vorbis_info
* info
= ov_info(&file_
, section
);
325 buf
= buffer(data
, result
, get_audio_format(info
), info
->rate
);
326 sample
= ov_pcm_tell(&file_
);
327 log_info("this section is", section
);
328 log_info("next sample is", sample
);
329 log_info("audio format is", get_audio_format(info
));
330 log_info("audio freq is", info
->rate
);
334 if (result
< 0) log_warning("vorbis playback error");
339 static ALenum
get_audio_format(const vorbis_info
* info
)
341 if (info
->channels
== 1) return AL_FORMAT_MONO16
;
342 else return AL_FORMAT_STEREO16
;
348 mutable OggVorbis_File file_
;
349 mutable buffer buffer_
;
363 impl(const std::string
& path
)
365 log_info("sound::impl constructor");
377 alGenSources(1, &source_
);
378 log_error("alGenSources:", alGetError());
380 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
381 alSourcef(source_
, AL_PITCH
, 1.0f
);
382 alSourcef(source_
, AL_GAIN
, 1.0f
);
383 alSourcefv(source_
, AL_POSITION
, zero
);
384 alSourcefv(source_
, AL_VELOCITY
, zero
);
385 log_error("init:", alGetError());
391 alDeleteSources(1, &source_
);
397 if (queue_
.empty()) return;
399 sound_handle handle
= queue_
.front();
402 if (handle
->read(buf
))
404 log_info("playing source...");
406 alSourcei(source_
, AL_LOOPING
, is_looping_
);
407 alSourcePlay(source_
);
413 if (queue_
.empty()) return;
417 alSourcei(source_
, AL_LOOPING
, false);
418 log_error("set not looping:", alGetError());
420 sound_handle handle
= queue_
.front();
422 for (int i
= 0; i
< NUM_BUFFERS
; ++i
)
425 if (handle
->read(buf
, sample_
))
431 log_error("failed to start stream");
436 alGetSourcei(source_
, AL_BUFFERS_QUEUED
, &queued
);
437 log_info("buffers queued:", queued
);
441 if (!stream_timer_
.is_valid())
443 stream_timer_
.init(boost::bind(&impl::stream_update
, this, _1
, _2
),
444 0.01, timer::repeat
);
447 log_info("streaming source...");
448 alSourcePlay(source_
);
449 log_error("playing:", alGetError());
458 alGetSourcei(source_
, AL_BUFFERS_PROCESSED
, &finished
);
460 while (finished
-- > 0)
462 buffer::unqueue(source_
);
463 bool streamed
= false;
464 //if (handle->is_loaded())
466 //streamed = handle->read(buf);
471 sound_handle handle
= queue_
.front();
472 streamed
= handle
->read(buf
, sample_
);
481 // the buffer couldn't be streamed, so get rid of it
487 // begin the next buffer in the queue
488 handle
->read(buf
, sample_
);
490 log_info("loading new buffer");
492 else if (is_looping_
)
494 // reload the same buffer
495 log_info("looping same buffer");
497 queue_
.push_back(handle
);
498 handle
->read(buf
, sample_
);
503 // nothing more to play, stopping...
505 queue_
.push_back(handle
);
511 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
513 // restart playing if we're stopped but supposed to be playing...
514 // this means we didn't queue enough and the audio skipped :-(
515 if (is_playing_
&& state
!= AL_PLAYING
)
517 alSourcePlay(source_
);
524 alSourceStop(source_
);
527 stream_timer_
.invalidate();
529 // TODO: clear buffers if streaming
534 alSourcePause(source_
);
537 stream_timer_
.invalidate();
542 alSourceRewind(source_
);
547 void sample(const std::string
& path
)
550 alSourcei(source_
, AL_BUFFER
, AL_NONE
);
557 void enqueue(const std::string
& path
)
559 sound_handle handle
= resource::load(path
);
560 queue_
.push_back(handle
);
564 bool is_playing() const
567 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
569 if (state
== AL_PLAYING
) return true;
570 else return is_playing_
;
574 void loop(bool looping
)
576 is_looping_
= looping
;
579 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
581 if (type
!= AL_STREAMING
)
583 alSourcei(source_
, AL_LOOPING
, is_looping_
);
588 void stream_update(timer
& timer
, scalar t
)
591 // TODO: might be nice to also allow using threads for streaming
592 // rather than a timer, probably as a compile-time option
601 std::deque
<sound_handle
> queue_
;
606 sound_backend backend_
;
612 impl_(new sound::impl
) {}
614 sound::sound(const std::string
& path
) :
616 impl_(new sound::impl(path
))
618 log_info("sound constructor");
622 void sound::sample(const std::string
& path
)
628 void sound::enqueue(const std::string
& path
)
631 impl_
->enqueue(path
);
668 if (is_playing()) pause();
670 // TODO: what about streaming sources?
673 bool sound::is_playing() const
676 return impl_
->is_playing();
680 void sound::position(const vector3
& position
)
682 float vec
[3] = {position
[0], position
[1], position
[2]};
683 alSourcefv(impl_
->source_
, AL_POSITION
, vec
);
686 void sound::velocity(const vector3
& velocity
)
688 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
689 alSourcefv(impl_
->source_
, AL_VELOCITY
, vec
);
692 void sound::gain(scalar gain
)
694 alSourcef(impl_
->source_
, AL_GAIN
, float(gain
));
697 void sound::pitch(scalar pitch
)
699 alSourcef(impl_
->source_
, AL_PITCH
, float(pitch
));
702 void sound::loop(bool looping
)
705 impl_
->loop(looping
);
709 void sound::listener_position(const vector3
& position
)
711 float vec
[] = {position
[0], position
[1], position
[2]};
712 alListenerfv(AL_POSITION
, vec
);
715 void sound::listener_velocity(const vector3
& velocity
)
717 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
718 alListenerfv(AL_VELOCITY
, vec
);
721 void sound::listener_orientation(const vector3
& forward
,
725 vec
[0] = float(forward
[0]);
726 vec
[1] = float(forward
[1]);
727 vec
[2] = float(forward
[2]);
728 vec
[3] = float(up
[0]);
729 vec
[4] = float(up
[1]);
730 vec
[5] = float(up
[2]);
731 alListenerfv(AL_ORIENTATION
, vec
);