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 (8)
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
);
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", "sounds");
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;
150 buffer(const buffer
& buf
)
152 buffer_
= buf
.buffer_
;
156 buffer
& operator = (const buffer
& buf
)
158 buffer_
= buf
.buffer_
;
169 void queue(ALuint source
) const
173 alSourceQueueBuffers(source
, 1, &buffer_
);
178 static buffer
unqueue(ALuint source
)
180 ALuint buf
= (ALuint
)-1;
181 alSourceUnqueueBuffers(source
, 1, &buf
);
185 void set(ALuint source
) const
187 if (*this) alSourcei(source
, AL_BUFFER
, buffer_
);
190 operator bool () const
192 return buffer_
!= (ALuint
)-1;
198 explicit buffer(ALuint buf
) :
204 retcount_lookup::iterator it
= retain_counts_
.find(buffer_
);
205 if (it
.valid()) ++it
->second
;
210 retcount_lookup::iterator it
= retain_counts_
.find(buffer_
);
211 if (it
.valid() && --it
->second
<= 0)
213 alDeleteBuffers(1, &buffer_
);
214 retain_counts_
.erase(it
);
220 sound_backend backend_
;
222 static retcount_lookup retain_counts_
;
225 buffer::retcount_lookup
buffer::retain_counts_
;
231 class sound_resource
: public boost::noncopyable
235 sound_resource(const std::string
& path
)
237 if (ov_fopen((char*)path
.c_str(), &file_
) < 0)
239 throw std::runtime_error("problem reading audio: " + path
);
249 bool read(buffer
& buf
) const
257 if (ov_pcm_seek_lap(&file_
, 0) != 0)
259 log_warning("vorbis seek error");
263 char data
[64*BUF_SIZE
];
266 while (size
< sizeof(data
))
269 int result
= ov_read(&file_
,
270 data
+ size
, sizeof(data
) - size
,
278 else if (result
== 0 && size
> 0)
280 vorbis_info
* info
= ov_info(&file_
, section
);
281 buffer_
= buffer(data
, size
,
282 get_audio_format(info
), info
->rate
);
288 log_warning("vorbis playback error");
293 if (size
>= sizeof(data
)) log_warning("sample is too big to play");
298 bool read(buffer
& buf
, uint64_t& sample
) const
300 if (ov_pcm_seek_lap(&file_
, sample
) != 0)
302 log_warning("vorbis seek error");
308 int result
= ov_read(&file_
, data
, sizeof(data
), 0, 2, 1, §ion
);
312 vorbis_info
* info
= ov_info(&file_
, section
);
313 buf
= buffer(data
, result
, get_audio_format(info
), info
->rate
);
314 sample
= ov_pcm_tell(&file_
);
318 if (result
< 0) log_warning("vorbis playback error");
323 static ALenum
get_audio_format(const vorbis_info
* info
)
325 if (info
->channels
== 1) return AL_FORMAT_MONO16
;
326 else return AL_FORMAT_STEREO16
;
332 mutable OggVorbis_File file_
;
333 mutable buffer buffer_
;
347 impl(const std::string
& name
)
360 alGenSources(1, &source_
);
362 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
363 alSourcef(source_
, AL_PITCH
, 1.0f
);
364 alSourcef(source_
, AL_GAIN
, 1.0f
);
365 alSourcefv(source_
, AL_POSITION
, zero
);
366 alSourcefv(source_
, AL_VELOCITY
, zero
);
372 alDeleteSources(1, &source_
);
378 if (queue_
.empty()) return;
380 sound_handle handle
= queue_
.front();
383 if (handle
->read(buf
))
386 alSourcei(source_
, AL_LOOPING
, is_looping_
);
387 alSourcePlay(source_
);
393 if (queue_
.empty()) return;
397 alSourcei(source_
, AL_LOOPING
, false);
399 sound_handle handle
= queue_
.front();
401 for (int i
= 0; i
< NUM_BUFFERS
; ++i
)
404 if (handle
->read(buf
, sample_
))
410 log_error("failed to start stream");
415 alGetSourcei(source_
, AL_BUFFERS_QUEUED
, &queued
);
419 if (!stream_timer_
.is_valid())
421 stream_timer_
.init(boost::bind(&impl::stream_update
, this, _1
, _2
),
422 0.01, timer::repeat
);
425 alSourcePlay(source_
);
434 alGetSourcei(source_
, AL_BUFFERS_PROCESSED
, &finished
);
436 while (finished
-- > 0)
438 buffer::unqueue(source_
);
439 bool streamed
= false;
440 //if (handle->is_loaded())
442 //streamed = handle->read(buf);
447 sound_handle handle
= queue_
.front();
448 streamed
= handle
->read(buf
, sample_
);
457 // the buffer couldn't be streamed, so get rid of it
463 // begin the next buffer in the queue
464 handle
->read(buf
, sample_
);
467 else if (is_looping_
)
469 // reload the same buffer
470 queue_
.push_back(handle
);
471 handle
->read(buf
, sample_
);
476 // nothing more to play, stopping...
478 queue_
.push_back(handle
);
484 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
486 // restart playing if we're stopped but supposed to be playing...
487 // this means we didn't queue enough and the audio skipped :-(
488 if (is_playing_
&& state
!= AL_PLAYING
)
490 alSourcePlay(source_
);
497 alSourceStop(source_
);
500 stream_timer_
.invalidate();
502 // TODO: clear buffers if streaming
507 alSourcePause(source_
);
510 stream_timer_
.invalidate();
515 alSourceRewind(source_
);
520 void sample(const std::string
& name
)
523 alSourcei(source_
, AL_BUFFER
, AL_NONE
);
530 void enqueue(const std::string
& name
)
532 sound_handle handle
= resource::load(name
, "ogg");
533 queue_
.push_back(handle
);
537 bool is_playing() const
540 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
542 if (state
== AL_PLAYING
) return true;
543 else return is_playing_
;
547 void loop(bool looping
)
549 is_looping_
= looping
;
552 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
554 if (type
!= AL_STREAMING
)
556 alSourcei(source_
, AL_LOOPING
, is_looping_
);
561 void stream_update(timer
& timer
, scalar t
)
564 // TODO: might be nice to also allow using threads for streaming
565 // rather than a timer, probably as a compile-time option
574 std::deque
<sound_handle
> queue_
;
579 sound_backend backend_
;
585 impl_(new sound::impl
) {}
587 sound::sound(const std::string
& path
) :
589 impl_(new sound::impl(path
)) {}
592 void sound::sample(const std::string
& path
)
598 void sound::enqueue(const std::string
& path
)
601 impl_
->enqueue(path
);
638 if (is_playing()) pause();
640 // TODO: what about streaming sources?
643 bool sound::is_playing() const
646 return impl_
->is_playing();
650 void sound::position(const vector3
& position
)
652 float vec
[3] = {position
[0], position
[1], position
[2]};
653 alSourcefv(impl_
->source_
, AL_POSITION
, vec
);
656 void sound::velocity(const vector3
& velocity
)
658 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
659 alSourcefv(impl_
->source_
, AL_VELOCITY
, vec
);
662 void sound::gain(scalar gain
)
664 alSourcef(impl_
->source_
, AL_GAIN
, float(gain
));
667 void sound::pitch(scalar pitch
)
669 alSourcef(impl_
->source_
, AL_PITCH
, float(pitch
));
672 void sound::loop(bool looping
)
675 impl_
->loop(looping
);
679 void sound::listener_position(const vector3
& position
)
681 float vec
[] = {position
[0], position
[1], position
[2]};
682 alListenerfv(AL_POSITION
, vec
);
685 void sound::listener_velocity(const vector3
& velocity
)
687 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
688 alListenerfv(AL_VELOCITY
, vec
);
691 void sound::listener_orientation(const vector3
& forward
,
695 vec
[0] = float(forward
[0]);
696 vec
[1] = float(forward
[1]);
697 vec
[2] = float(forward
[2]);
698 vec
[3] = float(up
[0]);
699 vec
[4] = float(up
[1]);
700 vec
[5] = float(up
[2]);
701 alListenerfv(AL_ORIENTATION
, vec
);