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>
22 #include <vorbis/codec.h>
23 #include <vorbis/vorbisfile.h>
29 #include "resource.hh"
34 #define BUF_SIZE (4096)
37 #define NUM_BUFFERS (16)
49 if (retain_count
++ == 0)
51 al_device
= alcOpenDevice(0);
52 al_context
= alcCreateContext(al_device
, 0);
53 if (!al_device
|| !al_context
)
55 const char* error
= alcGetString(al_device
,
56 alcGetError(al_device
));
57 log_error("audio subsystem initialization failure", error
);
61 alcMakeContextCurrent(al_context
);
62 log_info
<< "opened sound device `"
63 << alcGetString(al_device
,
64 ALC_DEFAULT_DEVICE_SPECIFIER
)
71 sound_backend(const sound_backend
& backend
)
76 sound_backend
& operator=(const sound_backend
& backend
)
84 if (--retain_count
== 0)
86 alcMakeContextCurrent(0);
87 alcDestroyContext(al_context
);
88 alcCloseDevice(al_device
);
89 log_info("unloaded sound device ALSA");
94 static int retain_count
;
95 static ALCdevice
* al_device
;
96 static ALCcontext
* al_context
;
99 int sound_backend::retain_count
= 0;
100 ALCdevice
* sound_backend::al_device
;
101 ALCcontext
* sound_backend::al_context
;
104 class sound_resource
;
105 typedef resource_handle
<sound_resource
> sound_handle
;
108 class sound_resource_loader
112 sound_resource_loader()
114 resource::register_type
<sound_resource
>("ogg");
117 ~sound_resource_loader()
119 resource::unregister_type("ogg");
123 static sound_resource_loader loader
;
133 typedef hash
<ALuint
,int,hash_function
> retcount_lookup
;
137 buffer_((ALuint
)-1) {}
139 buffer(const void* data
,
144 alGenBuffers(1, &buffer_
);
145 alBufferData(buffer_
, format
, data
, size
, freq
);
147 retain_counts_
[buffer_
] = 1;
148 log_warning("ctor buffer:", buffer_
);
151 buffer(const buffer
& buf
)
153 buffer_
= buf
.buffer_
;
157 buffer
& operator = (const buffer
& buf
)
159 buffer_
= buf
.buffer_
;
170 void queue(ALuint source
) const
174 alSourceQueueBuffers(source
, 1, &buffer_
);
176 log_warning("queued buffer:", buffer_
);
180 static buffer
unqueue(ALuint source
)
182 ALuint buf
= (ALuint
)-1;
183 alSourceUnqueueBuffers(source
, 1, &buf
);
184 log_warning("unqueued buffer:", buf
);
188 void set(ALuint source
) const
190 log_warning("set buffer:", buffer_
);
191 if (*this) alSourcei(source
, AL_BUFFER
, buffer_
);
194 operator bool () const
196 return buffer_
!= (ALuint
)-1;
202 explicit buffer(ALuint buf
) :
208 retcount_lookup::iterator it
= retain_counts_
.find(buffer_
);
209 if (it
.valid()) ++it
->second
;
214 retcount_lookup::iterator it
= retain_counts_
.find(buffer_
);
215 if (it
.valid() && --it
->second
<= 0)
217 alDeleteBuffers(1, &buffer_
);
218 retain_counts_
.erase(it
);
219 log_warning("kill buffer:", buffer_
);
225 sound_backend backend_
;
227 static retcount_lookup retain_counts_
;
230 buffer::retcount_lookup
buffer::retain_counts_
;
240 sound_resource(const std::string
& path
)
242 log_info("audio path is", path
);
243 if (ov_fopen((char*)path
.c_str(), &file_
) < 0)
245 throw std::runtime_error("problem reading audio: " + path
);
255 bool read(buffer
& buf
)
263 if (ov_pcm_seek_lap(&file_
, 0) != 0)
265 log_warning("vorbis seek error");
269 char data
[64*BUF_SIZE
];
272 while (size
< sizeof(data
))
275 int result
= ov_read(&file_
,
276 data
+ size
, sizeof(data
) - size
,
284 else if (result
== 0 && size
> 0)
286 log_info("loaded", size
, "bytes from vorbis");
287 vorbis_info
* info
= ov_info(&file_
, section
);
288 buffer_
= buffer(data
, size
,
289 get_audio_format(info
), info
->rate
);
291 log_info("this section is", section
);
292 log_info("audio format is", get_audio_format(info
));
293 log_info("audio freq is", info
->rate
);
298 log_warning("vorbis playback error");
303 if (size
>= sizeof(data
)) log_warning("sample is too big to play");
308 bool read(buffer
& buf
, uint64_t& sample
)
310 if (ov_pcm_seek_lap(&file_
, sample
) != 0)
312 log_warning("vorbis seek error");
318 int result
= ov_read(&file_
, data
, sizeof(data
), 0, 2, 1, §ion
);
322 log_info("loaded", result
, "bytes from vorbis");
323 vorbis_info
* info
= ov_info(&file_
, section
);
324 buf
= buffer(data
, result
, get_audio_format(info
), info
->rate
);
325 sample
= ov_pcm_tell(&file_
);
326 log_info("this section is", section
);
327 log_info("next sample is", sample
);
328 log_info("audio format is", get_audio_format(info
));
329 log_info("audio freq is", info
->rate
);
333 if (result
< 0) log_warning("vorbis playback error");
338 static ALenum
get_audio_format(const vorbis_info
* info
)
340 if (info
->channels
== 1) return AL_FORMAT_MONO16
;
341 else return AL_FORMAT_STEREO16
;
347 OggVorbis_File file_
;
362 impl(const std::string
& path
)
364 log_info("sound::impl constructor");
376 alGenSources(1, &source_
);
377 log_error("alGenSources:", alGetError());
379 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
380 alSourcef(source_
, AL_PITCH
, 1.0f
);
381 alSourcef(source_
, AL_GAIN
, 1.0f
);
382 alSourcefv(source_
, AL_POSITION
, zero
);
383 alSourcefv(source_
, AL_VELOCITY
, zero
);
384 log_error("init:", alGetError());
390 alDeleteSources(1, &source_
);
396 if (queue_
.empty()) return;
398 sound_handle handle
= queue_
.front();
401 if (handle
->read(buf
))
403 log_info("playing source...");
405 alSourcei(source_
, AL_LOOPING
, is_looping_
);
406 alSourcePlay(source_
);
412 if (queue_
.empty()) return;
416 alSourcei(source_
, AL_LOOPING
, false);
417 log_error("set not looping:", alGetError());
419 sound_handle handle
= queue_
.front();
421 for (int i
= 0; i
< NUM_BUFFERS
; ++i
)
424 if (handle
->read(buf
, sample_
))
430 log_error("failed to start stream");
435 alGetSourcei(source_
, AL_BUFFERS_QUEUED
, &queued
);
436 log_info("buffers queued:", queued
);
440 if (!stream_timer_
.is_valid())
442 stream_timer_
.init(boost::bind(&impl::stream_update
, this, _1
, _2
),
443 0.01, timer::repeat
);
446 log_info("streaming source...");
447 alSourcePlay(source_
);
448 log_error("playing:", alGetError());
457 alGetSourcei(source_
, AL_BUFFERS_PROCESSED
, &finished
);
459 while (finished
-- > 0)
461 buffer::unqueue(source_
);
462 bool streamed
= false;
463 //if (handle->is_loaded())
465 //streamed = handle->read(buf);
470 sound_handle handle
= queue_
.front();
471 streamed
= handle
->read(buf
, sample_
);
480 // the buffer couldn't be streamed, so get rid of it
486 // begin the next buffer in the queue
487 handle
->read(buf
, sample_
);
489 log_info("loading new buffer");
491 else if (is_looping_
)
493 // reload the same buffer
494 log_info("looping same buffer");
496 queue_
.push_back(handle
);
497 handle
->read(buf
, sample_
);
502 // nothing more to play, stopping...
504 queue_
.push_back(handle
);
510 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
512 // restart playing if we're stopped but supposed to be playing...
513 // this means we didn't queue enough and the audio skipped :-(
514 if (is_playing_
&& state
!= AL_PLAYING
)
516 alSourcePlay(source_
);
523 alSourceStop(source_
);
526 stream_timer_
.invalidate();
528 // TODO: clear buffers if streaming
533 alSourcePause(source_
);
536 stream_timer_
.invalidate();
541 alSourceRewind(source_
);
546 void sample(const std::string
& path
)
549 alSourcei(source_
, AL_BUFFER
, AL_NONE
);
556 void enqueue(const std::string
& path
)
558 sound_handle handle
= resource::load(path
);
559 queue_
.push_back(handle
);
563 bool is_playing() const
566 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
568 if (state
== AL_PLAYING
) return true;
569 else return is_playing_
;
573 void loop(bool looping
)
575 is_looping_
= looping
;
578 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
580 if (type
!= AL_STREAMING
)
582 alSourcei(source_
, AL_LOOPING
, is_looping_
);
587 void stream_update(timer
& timer
, scalar t
)
590 // TODO: might be nice to also allow using threads for streaming
591 // rather than a timer, probably as a compile-time option
600 std::deque
<sound_handle
> queue_
;
605 sound_backend backend_
;
611 impl_(new sound::impl
) {}
613 sound::sound(const std::string
& path
) :
615 impl_(new sound::impl(path
))
617 log_info("sound constructor");
621 void sound::sample(const std::string
& path
)
627 void sound::enqueue(const std::string
& path
)
630 impl_
->enqueue(path
);
667 if (is_playing()) pause();
669 // TODO: what about streaming sources?
672 bool sound::is_playing() const
675 return impl_
->is_playing();
679 void sound::position(const vector3
& position
)
681 float vec
[3] = {position
[0], position
[1], position
[2]};
682 alSourcefv(impl_
->source_
, AL_POSITION
, vec
);
685 void sound::velocity(const vector3
& velocity
)
687 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
688 alSourcefv(impl_
->source_
, AL_VELOCITY
, vec
);
691 void sound::gain(scalar gain
)
693 alSourcef(impl_
->source_
, AL_GAIN
, float(gain
));
696 void sound::pitch(scalar pitch
)
698 alSourcef(impl_
->source_
, AL_PITCH
, float(pitch
));
701 void sound::loop(bool looping
)
704 impl_
->loop(looping
);
708 void sound::listener_position(const vector3
& position
)
710 float vec
[] = {position
[0], position
[1], position
[2]};
711 alListenerfv(AL_POSITION
, vec
);
714 void sound::listener_velocity(const vector3
& velocity
)
716 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
717 alListenerfv(AL_VELOCITY
, vec
);
720 void sound::listener_orientation(const vector3
& forward
,
724 vec
[0] = float(forward
[0]);
725 vec
[1] = float(forward
[1]);
726 vec
[2] = float(forward
[2]);
727 vec
[3] = float(up
[0]);
728 vec
[4] = float(up
[1]);
729 vec
[5] = float(up
[2]);
730 alListenerfv(AL_ORIENTATION
, vec
);