-/*] Copyright (c) 2009-2010, Charles McGarvey [**************************
+/*] Copyright (c) 2009-2011, Charles McGarvey [*****************************
**] All rights reserved.
*
-* vi:ts=4 sw=4 tw=75
-*
* Distributable under the terms and conditions of the 2-clause BSD license;
* see the file COPYING for a complete text of the license.
*
-**************************************************************************/
+*****************************************************************************/
#include <deque>
#include <stdexcept>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
-#include "log.hh"
+#include "debug.hh"
#include "sound.hh"
#include "resource.hh"
+#include "runloop.hh"
+#include "thread.hh"
#include "timer.hh"
#ifndef BUF_SIZE
-#define BUF_SIZE (4096)
+#define BUF_SIZE 8192
#endif
-#define NUM_SAMPLE_BITS (16)
+#define NUM_SAMPLE_BITS 16
namespace moof {
-/*] Sound backend
- *************************************************************************/
+thread stream_thread_;
+
class sound_backend
{
if (!al_device || !al_context)
{
const char* error = alcGetString(al_device,
- alcGetError(al_device));
+ alcGetError(al_device));
log_error("audio subsystem initialization failure", error);
}
else
{
alcMakeContextCurrent(al_context);
- log_info << "opened sound device `"
- << alcGetString(al_device,
- ALC_DEFAULT_DEVICE_SPECIFIER)
- << "'" << std::endl;
+ log_info << "opened sound device `" <<
+ alcGetString(al_device, ALC_DEFAULT_DEVICE_SPECIFIER) <<
+ "'" << std::endl;
}
}
}
-
sound_backend(const sound_backend& backend)
{
++retain_count;
}
- sound_backend& operator=(const sound_backend& backend)
+ sound_backend& operator = (const sound_backend& backend)
{
++retain_count;
return *this;
}
}
-
- static int retain_count;
+ static int retain_count;
static ALCdevice* al_device;
static ALCcontext* al_context;
};
-int sound_backend::retain_count = 0;
+int sound_backend::retain_count = 0;
ALCdevice* sound_backend::al_device;
ALCcontext* sound_backend::al_context;
MOOF_REGISTER_RESOURCE(sound_resource, ogg, sounds);
-
-/*] Sound resource
- *************************************************************************/
-
class sound_resource : public boost::noncopyable
{
public:
sample_(0)
{
if (ov_fopen((char*)path.c_str(), &file_) < 0)
- {
- throw std::runtime_error("problem reading audio: " + path);
- }
+ throw std::runtime_error("problem reading audio: " +
+ path);
}
~sound_resource()
if (buffer_) alDeleteBuffers(1, &buffer_);
}
-
ALuint read_all() const
{
if (buffer_) return buffer_;
while (size < sizeof(data))
{
int section;
- int result = ov_read(&file_,
- data + size, sizeof(data) - size,
- 0, 2, 1, §ion);
+ int result = ov_read(&file_, data + size,
+ sizeof(data) - size, 0, 2, 1, §ion);
if (0 < result)
{
ALuint buffer;
alGenBuffers(1, &buffer);
alBufferData(buffer, get_audio_format(info),
- data, size, info->rate);
+ data, size, info->rate);
buffer_ = buffer;
return buffer;
break;
}
}
-
return AL_NONE;
}
{
if ((sample == sample_ && ov_pcm_seek_lap(&file_, sample) != 0) ||
(sample != sample_ && ov_pcm_seek(&file_, sample) != 0))
- {
return false;
- }
- char data[BUF_SIZE];
+ char data[BUF_SIZE];
int section;
- int result = ov_read(&file_, data, sizeof(data), 0, 2, 1, §ion);
+ int result = ov_read(&file_, data, sizeof(data),
+ 0, 2, 1, §ion);
if (0 < result)
{
sample_ = sample = ov_pcm_tell(&file_);
return true;
}
- else if (result < 0) log_warning("vorbis playback error");
-
+ else if (result < 0)
+ {
+ log_warning("vorbis playback error");
+ }
return false;
}
{
ov_crosslap(&other->file_, &file_);
- char data[BUF_SIZE];
+ char data[BUF_SIZE];
int section;
- int result = ov_read(&file_, data, sizeof(data), 0, 2, 1, §ion);
+ int result = ov_read(&file_, data, sizeof(data),
+ 0, 2, 1, §ion);
if (0 < result)
{
vorbis_info* info = ov_info(&file_, section);
alBufferData(buffer, get_audio_format(info),
- data, result, info->rate);
+ data, result, info->rate);
sample_ = sample = ov_pcm_tell(&file_);
return true;
}
- else if (result < 0) log_warning("vorbis playback error");
-
+ else if (result < 0)
+ {
+ log_warning("vorbis playback error");
+ }
return false;
}
-
- // determines the correct number of fixed-size buffers needed to hold a
- // given number of seconds of PCM audio.
+ // determines the correct number of fixed-size buffers needed to hold
+ // a given number of seconds of PCM audio.
int num_buffers(scalar seconds) const
{
vorbis_info* info = ov_info(&file_, -1);
int count = scalar(info->rate) * // sample rate
- scalar(info->channels) * // channels
- scalar(NUM_SAMPLE_BITS) * // sample size
- seconds * // time
- SCALAR(0.125) / // bits to bytes
- scalar(BUF_SIZE); // individual buffer size
+ scalar(info->channels) * // channels
+ scalar(NUM_SAMPLE_BITS) * // sample size
+ seconds * // time
+ SCALAR(0.125) / // bits to bytes
+ scalar(BUF_SIZE); // individual buffer size
return count;
}
-
static ALenum get_audio_format(const vorbis_info* info)
{
- if (info->channels == 1) return AL_FORMAT_MONO16;
- else if (info->channels == 2) return AL_FORMAT_STEREO16;
+ if (info->channels == 1)
+ return AL_FORMAT_MONO16;
+ else if (info->channels == 2)
+ return AL_FORMAT_STEREO16;
log_error("unsupported number of channels:", info->channels);
return 0;
}
-
private:
mutable OggVorbis_File file_;
- mutable ALuint buffer_;
- mutable uint64_t sample_;
+ mutable ALuint buffer_;
+ mutable uint64_t sample_;
- sound_backend backend_;
+ sound_backend backend_;
};
-/*] Sound class
- *************************************************************************/
-
class sound::impl
{
public:
alSourceUnqueueBuffers(source_, queued, buffers);
alDeleteBuffers(queued, buffers);
}
- else alSourcei(source_, AL_BUFFER, AL_NONE);
+ else
+ {
+ alSourcei(source_, AL_BUFFER, AL_NONE);
+ }
}
void play()
alGetSourcei(source_, AL_SOURCE_STATE, &state);
switch (state)
{
- case AL_INITIAL:
- case AL_STOPPED:
-
- if (is_streaming_)
- {
- start_update_timer();
- num_buffers_ = queue_.front()->num_buffers(buffer_size_);
- fill_stream();
- is_playing_ = true;
- alSourcei(source_, AL_LOOPING, false);
- }
- else
- {
- ALuint buffer = queue_.front()->read_all();
- alSourcei(source_, AL_BUFFER, buffer);
- alSourcei(source_, AL_LOOPING, is_looping_);
- }
- break;
+ case AL_INITIAL:
+ case AL_STOPPED:
+ if (is_streaming_)
+ {
+ start_update_timer();
+ num_buffers_ = queue_.front()->num_buffers(buffer_size_);
+ fill_stream();
+ is_playing_ = true;
+ alSourcei(source_, AL_LOOPING, false);
+ }
+ else
+ {
+ ALuint buffer = queue_.front()->read_all();
+ alSourcei(source_, AL_BUFFER, buffer);
+ alSourcei(source_, AL_LOOPING, is_looping_);
+ }
+ break;
- case AL_PAUSED:
-
- if (is_streaming_)
- {
- start_update_timer();
- is_playing_ = true;
- }
- break;
+ case AL_PAUSED:
+ if (is_streaming_)
+ {
+ start_update_timer();
+ is_playing_ = true;
+ }
+ break;
}
alSourcePlay(source_);
void start_update_timer()
{
- stream_timer_.init(boost::bind(&impl::stream_update, this, _1, _2),
+ stream_timer_.init(boost::bind(&impl::stream_update,
+ this, _1, _2),
SCALAR(0.1), timer::repeat);
+
+ thread::main_runloop().add_timer(stream_timer_);
+
+ //thread thread;
+ //thread.runloop().add_timer(stream_timer_);
+ //if (!stream_thread_.is_valid())
+ //stream_thread_ = thread::detach(stream_timer_);
+ // FIXME: proper locking needs to be done; this is causing crashes
+ // on shutdown when sound resources are destroyed yet the stream
+ // thread is neither shutdown or notified.
}
void fill_stream()
alGetSourcei(source_, AL_BUFFERS_QUEUED, &queued);
alGetSourcei(source_, AL_BUFFERS_PROCESSED, &processed);
- int target_diff = num_buffers_ - queued + processed;
+ int target_diff = num_buffers_ - queued + processed;
ALuint bufs[processed];
alSourceUnqueueBuffers(source_, processed, bufs);
if (0 < target_diff)
{
- if (queue_buffer(buf)) --target_diff;
+ if (queue_buffer(buf))
+ {
+ --target_diff;
+ }
else
{
alDeleteBuffers(processed - i - 1, &bufs[i+1]);
else alDeleteBuffers(1, &buf);
}
- if (stream_timer_.is_valid() && 0 < target_diff)
+ if (stream_timer_.mode() != timer::invalid && 0 < target_diff)
{
for (int i = 0; i < target_diff; ++i)
{
}
}
+ // TODO: this function is ugly
bool queue_buffer(ALuint buffer)
{
if (buffer == AL_NONE) alGenBuffers(1, &buffer);
queue_.push_back(sound);
looped_once = true;
}
- else break;
+ else
+ {
+ break;
+ }
}
}
return false;
}
-
void stop()
{
alSourceStop(source_);
stream_timer_.invalidate();
}
-
void sample(const std::string& name)
{
stop();
is_streaming_ = true;
}
-
bool is_playing() const
{
ALenum state;
alGetSourcei(source_, AL_SOURCE_STATE, &state);
if (state == AL_PLAYING) return true;
- else return is_playing_;
+ else return is_playing_;
}
-
void loop(bool looping)
{
is_looping_ = looping;
ALenum type;
alGetSourcei(source_, AL_SOURCE_TYPE, &type);
if (type != AL_STREAMING)
- {
alSourcei(source_, AL_LOOPING, is_looping_);
- }
}
-
void stream_update(timer& timer, scalar t)
{
- //log_error("blaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhrrrrrg");
+ log_debug("blaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhrrrrrg");
fill_stream();
- // TODO: might be nice to also allow using threads for streaming
- // rather than a timer, probably as a compile-time option
}
+ ALuint source_;
- ALuint source_;
-
- bool is_playing_;
- bool is_looping_;
- bool is_streaming_;
+ bool is_playing_;
+ bool is_looping_;
+ bool is_streaming_;
std::deque<sound_handle> queue_;
- uint64_t sample_;
- int num_buffers_;
- scalar buffer_size_;
+ uint64_t sample_;
+ int num_buffers_;
+ scalar buffer_size_;
- timer stream_timer_;
+ timer stream_timer_;
- sound_backend backend_;
+ sound_backend backend_;
};
// pass through
impl_(new sound::impl(path)) {}
-
void sound::sample(const std::string& path)
{
// pass through
impl_->queue(path);
}
-
void sound::play()
{
// pass through
impl_->buffer_size_ = seconds;
}
-
void sound::position(const vector3& position)
{
float vec[3] = {position[0], position[1], position[2]};
impl_->loop(looping);
}
-
void sound::listener_position(const vector3& position)
{
float vec[] = {position[0], position[1], position[2]};
}
void sound::listener_orientation(const vector3& forward,
- const vector3& up)
+ const vector3& up)
{
float vec[6];
vec[0] = float(forward[0]);