$OpenBSD$ much like the files in FILESDIR, i believe this is applying the latest patch from chris@zdoomforums... --- src/sound/oalsound.cpp.orig Fri Dec 28 21:11:27 2012 +++ src/sound/oalsound.cpp Fri Dec 28 21:37:21 2012 @@ -41,6 +41,7 @@ #include "doomstat.h" #include "templates.h" #include "oalsound.h" +#include "oalgstreamer.h" #include "c_cvars.h" #include "c_dispatch.h" #include "i_system.h" @@ -107,37 +108,42 @@ EXTERN_CVAR (Bool, snd_pitched) (name) != (_end_##name);(name)++) -static ALenum checkALError(const char *fn, unsigned int ln) +static ALCenum checkALCError(ALCdevice *device, const char *fn, unsigned int ln) { - ALenum err = alGetError(); - if(err != AL_NO_ERROR) - { - if(strchr(fn, '/')) - fn = strrchr(fn, '/')+1; - else if(strchr(fn, '\\')) - fn = strrchr(fn, '\\')+1; - Printf(">>>>>>>>>>>> Received AL error %s (%#x), %s:%u\n", alGetString(err), err, fn, ln); - } - return err; + ALCenum err = alcGetError(device); + if(err != ALC_NO_ERROR) + { + const char *fname; + if((fname=strrchr(fn, '/')) != NULL) + fname++; + else if((fname=strrchr(fn, '\\')) != NULL) + fname++; + else + fname = fn; + Printf(">>>>>>>>>>>> Got ALC error %s (%#x), %s:%u\n", alcGetString(device, err), err, fname, ln); + } + return err; } -#define getALError() checkALError(__FILE__, __LINE__) +#define getALCError(d) checkALCError((d), __FILE__, __LINE__) -static ALCenum checkALCError(ALCdevice *device, const char *fn, unsigned int ln) +ALenum checkALError(const char *fn, unsigned int ln) { - ALCenum err = alcGetError(device); - if(err != ALC_NO_ERROR) - { - if(strchr(fn, '/')) - fn = strrchr(fn, '/')+1; - else if(strchr(fn, '\\')) - fn = strrchr(fn, '\\')+1; - Printf(">>>>>>>>>>>> Received ALC error %s (%#x), %s:%u\n", alcGetString(device, err), err, fn, ln); - } - return err; + ALenum err = alGetError(); + if(err != AL_NO_ERROR) + { + const char *fname; + if((fname=strrchr(fn, '/')) != NULL) + fname++; + else if((fname=strrchr(fn, '\\')) != NULL) + fname++; + else + fname = fn; + Printf(">>>>>>>>>>>> Got AL error %s (%#x), %s:%u\n", alGetString(err), err, fname, ln); + } + return err; } -#define getALCError(d) checkALCError((d), __FILE__, __LINE__) -static ALsizei GetBufferLength(ALuint buffer, const char *fn, unsigned int ln) +ALsizei GetBufferLength(ALuint buffer, const char *fn, unsigned int ln) { ALint bits, channels, size; alGetBufferi(buffer, AL_BITS, &bits); @@ -147,38 +153,9 @@ static ALsizei GetBufferLength(ALuint buffer, const ch return (ALsizei)(size / (channels * bits / 8)); return 0; } -#define getBufferLength(b) GetBufferLength((b), __FILE__, __LINE__) -extern ReverbContainer *ForcedEnvironment; - -#define PITCH_MULT (0.7937005f) /* Approx. 4 semitones lower; what Nash suggested */ - -#define PITCH(pitch) (snd_pitched ? (pitch)/128.f : 1.f) - - -static float GetRolloff(const FRolloffInfo *rolloff, float distance) +ALenum FormatFromDesc(int bits, int channels) { - if(distance <= rolloff->MinDistance) - return 1.f; - // Logarithmic rolloff has no max distance where it goes silent. - if(rolloff->RolloffType == ROLLOFF_Log) - return rolloff->MinDistance / - (rolloff->MinDistance + rolloff->RolloffFactor*(distance-rolloff->MinDistance)); - if(distance >= rolloff->MaxDistance) - return 0.f; - - float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance); - if(rolloff->RolloffType == ROLLOFF_Linear) - return volume; - - if(rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve != NULL) - return S_SoundCurve[int(S_SoundCurveSize * (1.f - volume))] / 127.f; - return (powf(10.f, volume) - 1.f) / 9.f; -} - - -static ALenum FormatFromDesc(int bits, int channels) -{ if(bits == 8) { if(channels == 1) return AL_FORMAT_MONO8; @@ -209,443 +186,100 @@ static ALenum FormatFromDesc(int bits, int channels) return AL_NONE; } +extern ReverbContainer *ForcedEnvironment; -#ifdef WITH_GSTREAMER -#include -#include -#include -#include -#include +#define PITCH_MULT (0.7937005f) /* Approx. 4 semitones lower; what Nash suggested */ -/* Bad GStreamer, using enums for bit fields... */ -static GstMessageType operator|(const GstMessageType &a, const GstMessageType &b) -{ return GstMessageType((unsigned)a|(unsigned)b); } -static GstSeekFlags operator|(const GstSeekFlags &a, const GstSeekFlags &b) -{ return GstSeekFlags((unsigned)a|(unsigned)b); } +#define PITCH(pitch) (snd_pitched ? (pitch)/128.f : 1.f) -static void PrintErrMsg(const char *str, GstMessage *msg) -{ - GError *error; - gchar *debug; - gst_message_parse_error(msg, &error, &debug); - Printf("%s: %s\n", str, error->message); - DPrintf("%s\n", debug); - - g_error_free(error); - g_free(debug); -} - -static GstCaps *SupportedBufferFormatCaps(int forcebits=0) +static float GetRolloff(const FRolloffInfo *rolloff, float distance) { - GstStructure *structure; - GstCaps *caps; + if(distance <= rolloff->MinDistance) + return 1.f; + // Logarithmic rolloff has no max distance where it goes silent. + if(rolloff->RolloffType == ROLLOFF_Log) + return rolloff->MinDistance / + (rolloff->MinDistance + rolloff->RolloffFactor*(distance-rolloff->MinDistance)); + if(distance >= rolloff->MaxDistance) + return 0.f; - caps = gst_caps_new_empty(); - if(alIsExtensionPresent("AL_EXT_MCFORMATS")) - { - static const struct { - gint count; - GstAudioChannelPosition pos[8]; - } chans[] = { - { 1, - { GST_AUDIO_CHANNEL_POSITION_FRONT_MONO } }, - { 2, - { GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT } }, - { 4, - { GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT } }, - { 6, - { GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - GST_AUDIO_CHANNEL_POSITION_LFE, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT } }, - { 7, - { GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - GST_AUDIO_CHANNEL_POSITION_LFE, - GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT } }, - { 8, - { GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - GST_AUDIO_CHANNEL_POSITION_LFE, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT } }, - }; - static const char *fmt32[] = { - "AL_FORMAT_MONO_FLOAT32", "AL_FORMAT_STEREO_FLOAT32", "AL_FORMAT_QUAD32", - "AL_FORMAT_51CHN32", "AL_FORMAT_61CHN32", "AL_FORMAT_71CHN32", NULL - }; - static const char *fmt16[] = { - "AL_FORMAT_MONO16", "AL_FORMAT_STEREO16", "AL_FORMAT_QUAD16", - "AL_FORMAT_51CHN16", "AL_FORMAT_61CHN16", "AL_FORMAT_71CHN16", NULL - }; - static const char *fmt8[] = { - "AL_FORMAT_MONO8", "AL_FORMAT_STEREO8", "AL_FORMAT_QUAD8", - "AL_FORMAT_51CHN8", "AL_FORMAT_61CHN8", "AL_FORMAT_71CHN8", NULL - }; + float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance); + if(rolloff->RolloffType == ROLLOFF_Linear) + return volume; - if(alIsExtensionPresent("AL_EXT_FLOAT32")) - { - for(size_t i = 0;fmt32[i];i++) - { - if(forcebits && forcebits != 32) - break; - - ALenum val = alGetEnumValue(fmt32[i]); - if(getALError() != AL_NO_ERROR || val == 0 || val == -1) - continue; - - structure = gst_structure_new("audio/x-raw-float", - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "width", G_TYPE_INT, 32, NULL); - gst_structure_set(structure, "channels", G_TYPE_INT, - chans[i].count, NULL); - if(chans[i].count > 2) - gst_audio_set_channel_positions(structure, chans[i].pos); - gst_caps_append_structure(caps, structure); - } - } - for(size_t i = 0;fmt16[i];i++) - { - if(forcebits && forcebits != 16) - break; - - ALenum val = alGetEnumValue(fmt16[i]); - if(getALError() != AL_NO_ERROR || val == 0 || val == -1) - continue; - - structure = gst_structure_new("audio/x-raw-int", - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "width", G_TYPE_INT, 16, - "depth", G_TYPE_INT, 16, - "signed", G_TYPE_BOOLEAN, TRUE, NULL); - gst_structure_set(structure, "channels", G_TYPE_INT, - chans[i].count, NULL); - if(chans[i].count > 2) - gst_audio_set_channel_positions(structure, chans[i].pos); - gst_caps_append_structure(caps, structure); - } - for(size_t i = 0;fmt8[i];i++) - { - if(forcebits && forcebits != 8) - break; - - ALenum val = alGetEnumValue(fmt8[i]); - if(getALError() != AL_NO_ERROR || val == 0 || val == -1) - continue; - - structure = gst_structure_new("audio/x-raw-int", - "width", G_TYPE_INT, 8, - "depth", G_TYPE_INT, 8, - "signed", G_TYPE_BOOLEAN, FALSE, NULL); - gst_structure_set(structure, "channels", G_TYPE_INT, - chans[i].count, NULL); - if(chans[i].count > 2) - gst_audio_set_channel_positions(structure, chans[i].pos); - gst_caps_append_structure(caps, structure); - } - } - else - { - if(alIsExtensionPresent("AL_EXT_FLOAT32") && - (!forcebits || forcebits == 32)) - { - structure = gst_structure_new("audio/x-raw-float", - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "width", G_TYPE_INT, 32, - "channels", GST_TYPE_INT_RANGE, 1, 2, NULL); - gst_caps_append_structure(caps, structure); - } - if(!forcebits || forcebits == 16) - { - structure = gst_structure_new("audio/x-raw-int", - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "width", G_TYPE_INT, 16, - "depth", G_TYPE_INT, 16, - "signed", G_TYPE_BOOLEAN, TRUE, - "channels", GST_TYPE_INT_RANGE, 1, 2, NULL); - gst_caps_append_structure(caps, structure); - } - if(!forcebits || forcebits == 8) - { - structure = gst_structure_new("audio/x-raw-int", - "width", G_TYPE_INT, 8, - "depth", G_TYPE_INT, 8, - "signed", G_TYPE_BOOLEAN, FALSE, - "channels", GST_TYPE_INT_RANGE, 1, 2, NULL); - gst_caps_append_structure(caps, structure); - } - } - return caps; + if(rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve != NULL) + return S_SoundCurve[int(S_SoundCurveSize * (1.f - volume))] / 127.f; + return (powf(10.f, volume) - 1.f) / 9.f; } + +#ifndef WITH_GSTREAMER class OpenALSoundStream : public SoundStream { OpenALSoundRenderer *Renderer; - GstElement *gstPipeline; - GstTagList *TagList; - gint64 LoopPts[2]; - ALuint Source; - bool Playing; - bool Looping; + SoundStreamCallback Callback; + void *UserData; - // Custom OpenAL sink; this is pretty crappy compared to the real - // openalsink element, but it gets the job done - static const ALsizei MaxSamplesQueued = 32768; - std::vector Buffers; - ALsizei SamplesQueued; - ALsizei SampleRate; + std::vector Data; ALenum Format; + ALsizei SampleRate; - static void sink_eos(GstAppSink *sink, gpointer user_data) - { - OpenALSoundStream *self = static_cast(user_data); + static const int BufferCount = 4; + ALuint Buffers[BufferCount]; + ALuint Source; - if(!self->Playing) - return; + bool Playing; - ALint state; - do { - g_usleep(10000); - alGetSourcei(self->Source, AL_SOURCE_STATE, &state); - } while(getALError() == AL_NO_ERROR && state == AL_PLAYING && self->Playing); - alSourceRewind(self->Source); - getALError(); - } + ALsizei (*Convert)(std::vector&); - static GstFlowReturn sink_preroll(GstAppSink *sink, gpointer user_data) - { - OpenALSoundStream *self = static_cast(user_data); + static ALsizei ConvNone(std::vector &Data) + { return Data.size(); } - // get the buffer from appsink - GstBuffer *buffer = gst_app_sink_pull_preroll(sink); - if(!buffer) return GST_FLOW_ERROR; - - GstCaps *caps = GST_BUFFER_CAPS(buffer); - gint bits = 0, channels = 0, rate = 0, i; - for(i = gst_caps_get_size(caps)-1;i >= 0;i--) - { - GstStructure *struc = gst_caps_get_structure(caps, i); - if(gst_structure_has_field(struc, "width")) - gst_structure_get_int(struc, "width", &bits); - if(gst_structure_has_field(struc, "channels")) - gst_structure_get_int(struc, "channels", &channels); - if(gst_structure_has_field(struc, "rate")) - gst_structure_get_int(struc, "rate", &rate); - } - - self->SampleRate = rate; - self->Format = FormatFromDesc(bits, channels); - - gst_buffer_unref(buffer); - if(self->Format == AL_NONE || self->SampleRate <= 0) - return GST_FLOW_ERROR; - return GST_FLOW_OK; - } - - static GstFlowReturn sink_buffer(GstAppSink *sink, gpointer user_data) + static ALsizei Conv32fToS16(std::vector &Data) { - OpenALSoundStream *self = static_cast(user_data); + union { + ALubyte *u8; + ALfloat *f32; + ALshort *s16; + } data = { &Data[0] }; + ALsizei len = Data.size() / sizeof(ALfloat); - GstBuffer *buffer = gst_app_sink_pull_buffer(sink); - if(!buffer) return GST_FLOW_ERROR; - - if(GST_BUFFER_SIZE(buffer) == 0) + for(ALsizei i = 0;i < len;i++) { - gst_buffer_unref(buffer); - return GST_FLOW_OK; + ALfloat val = data.f32[i]; + if(val >= 1.0f) + data.s16[i] = 32767; + else if(val <= -1.0f) + data.s16[i] = -32768; + else + data.s16[i] = (ALshort)(val*32767.0f); } - ALint processed, state; - next_buffer: - do { - alGetSourcei(self->Source, AL_SOURCE_STATE, &state); - alGetSourcei(self->Source, AL_BUFFERS_PROCESSED, &processed); - if(getALError() != AL_NO_ERROR) - { - gst_buffer_unref(buffer); - return GST_FLOW_ERROR; - } - if(processed > 0 || self->SamplesQueued < MaxSamplesQueued || - state != AL_PLAYING || !self->Playing) - break; - - g_usleep(10000); - } while(1); - - if(!self->Playing) - { - gst_buffer_unref(buffer); - return GST_FLOW_OK; - } - - ALuint bufID; - if(processed == 0) - { - alGenBuffers(1, &bufID); - if(getALError() != AL_NO_ERROR) - { - gst_buffer_unref(buffer); - return GST_FLOW_ERROR; - } - self->Buffers.push_back(bufID); - } - else while(1) - { - alSourceUnqueueBuffers(self->Source, 1, &bufID); - if(getALError() != AL_NO_ERROR) - { - gst_buffer_unref(buffer); - return GST_FLOW_ERROR; - } - - self->SamplesQueued -= getBufferLength(bufID); - processed--; - if(self->SamplesQueued < MaxSamplesQueued) - break; - if(processed == 0) - goto next_buffer; - self->Buffers.erase(find(self->Buffers.begin(), self->Buffers.end(), bufID)); - alDeleteBuffers(1, &bufID); - } - - alBufferData(bufID, self->Format, GST_BUFFER_DATA(buffer), - GST_BUFFER_SIZE(buffer), self->SampleRate); - alSourceQueueBuffers(self->Source, 1, &bufID); - gst_buffer_unref(buffer); - - if(getALError() != AL_NO_ERROR) - return GST_FLOW_ERROR; - - self->SamplesQueued += getBufferLength(bufID); - if(state != AL_PLAYING && processed == 0) - { - alSourcePlay(self->Source); - if(getALError() != AL_NO_ERROR) - return GST_FLOW_ERROR; - } - return GST_FLOW_OK; + return len*sizeof(ALshort); } - // Memory-based data source - std::vector MemData; - size_t MemDataPos; - - static void need_memdata(GstAppSrc *appsrc, guint size, gpointer user_data) + static ALsizei ConvS32ToS16(std::vector &Data) { - OpenALSoundStream *self = static_cast(user_data); + union { + ALubyte *u8; + ALint *s32; + ALshort *s16; + } data = { &Data[0] }; + ALsizei len = Data.size() / sizeof(ALint); - if(self->MemDataPos >= self->MemData.size()) - { - gst_app_src_end_of_stream(appsrc); - return; - } + for(ALsizei i = 0;i < len;i++) + data.s16[i] = data.s32[i]>>16; - // "read" the data it wants, up to the remaining amount - guint8 *data = &self->MemData[self->MemDataPos]; - size = std::min(size, self->MemData.size() - self->MemDataPos); - self->MemDataPos += size; - - GstBuffer *buffer = gst_buffer_new(); - GST_BUFFER_DATA(buffer) = data; - GST_BUFFER_SIZE(buffer) = size; - - // this takes ownership of the buffer; don't unref - gst_app_src_push_buffer(appsrc, buffer); + return len*sizeof(ALshort); } - static gboolean seek_memdata(GstAppSrc *appsrc, guint64 position, gpointer user_data) - { - OpenALSoundStream *self = static_cast(user_data); - if(position > self->MemData.size()) - return FALSE; - self->MemDataPos = position; - return TRUE; - } - - static void memdata_source(GObject *object, GObject *orig, GParamSpec *pspec, OpenALSoundStream *self) + bool SetupSource() { - GstElement *elem; - g_object_get(self->gstPipeline, "source", &elem, NULL); - - GstAppSrc *appsrc = GST_APP_SRC(elem); - GstAppSrcCallbacks callbacks = { - need_memdata, NULL, seek_memdata - }; - gst_app_src_set_callbacks(appsrc, &callbacks, self, NULL); - gst_app_src_set_size(appsrc, self->MemData.size()); - gst_app_src_set_stream_type(appsrc, GST_APP_STREAM_TYPE_RANDOM_ACCESS); - - gst_object_unref(appsrc); - } - - // Callback-based data source - SoundStreamCallback Callback; - void *UserData; - int BufferBytes; - GstCaps *SrcCaps; - - static void need_callback(GstAppSrc *appsrc, guint size, gpointer user_data) - { - OpenALSoundStream *self = static_cast(user_data); - - GstBuffer *buffer; - if(!self->Playing) - buffer = gst_buffer_new_and_alloc(0); - else - { - buffer = gst_buffer_new_and_alloc(size); - if(!self->Callback(self, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer), self->UserData)) - { - gst_buffer_unref(buffer); - - gst_app_src_end_of_stream(appsrc); - return; - } - } - - gst_app_src_push_buffer(appsrc, buffer); - } - - static void callback_source(GObject *object, GObject *orig, GParamSpec *pspec, OpenALSoundStream *self) - { - GstElement *elem; - g_object_get(self->gstPipeline, "source", &elem, NULL); - - GstAppSrc *appsrc = GST_APP_SRC(elem); - GstAppSrcCallbacks callbacks = { - need_callback, NULL, NULL - }; - gst_app_src_set_callbacks(appsrc, &callbacks, self, NULL); - gst_app_src_set_size(appsrc, -1); - gst_app_src_set_max_bytes(appsrc, self->BufferBytes); - gst_app_src_set_stream_type(appsrc, GST_APP_STREAM_TYPE_STREAM); - gst_app_src_set_caps(appsrc, self->SrcCaps); - - gst_object_unref(appsrc); - } - - // General methods - virtual bool SetupSource() - { - // We don't actually use this source if we have an openalsink. However, - // holding on to it helps ensure we don't overrun our allotted voice - // count. + /* Get a source, killing the farthest, lowest-priority sound if needed */ if(Renderer->FreeSfx.size() == 0) { FSoundChan *lowest = Renderer->FindLowestChannel(); @@ -657,6 +291,7 @@ class OpenALSoundStream : public SoundStream Source = Renderer->FreeSfx.back(); Renderer->FreeSfx.pop_back(); + /* Set the default properties for localized playback */ alSource3f(Source, AL_DIRECTION, 0.f, 0.f, 0.f); alSource3f(Source, AL_VELOCITY, 0.f, 0.f, 0.f); alSource3f(Source, AL_POSITION, 0.f, 0.f, 0.f); @@ -678,187 +313,19 @@ class OpenALSoundStream : public SoundStream return (getALError() == AL_NO_ERROR); } - bool PipelineSetup() - { - TagList = gst_tag_list_new(); - g_return_val_if_fail(TagList != NULL, false); - - // Flags (can be combined): - // 0x01: video - Render the video stream - // 0x02: audio - Render the audio stream - // 0x04: text - Render subtitles - // 0x08: vis - Render visualisation when no video is present - // 0x10: soft-volume - Use software volume - // 0x20: native-audio - Only use native audio formats - // 0x40: native-video - Only use native video formats - // 0x80: download - Attempt progressive download buffering - int flags = 0x02 | 0x10; - - gstPipeline = gst_element_factory_make("playbin2", NULL); - g_return_val_if_fail(gstPipeline != NULL, false); - - GstElement *sink = gst_element_factory_make("openalsink", NULL); - if(sink != NULL) - { - // Give the sink our device, so it can create its own context and - // source to play with (and not risk cross-contaminating errors) - g_object_set(sink, "device-handle", Renderer->Device, NULL); - } - else - { - static bool warned = false; - if(!warned) - g_warning("Could not create an openalsink\n"); - warned = true; - - sink = gst_element_factory_make("appsink", NULL); - g_return_val_if_fail(sink != NULL, false); - - GstAppSink *appsink = GST_APP_SINK(sink); - GstAppSinkCallbacks callbacks = { - sink_eos, sink_preroll, sink_buffer, NULL - }; - GstCaps *caps = SupportedBufferFormatCaps(); - - gst_app_sink_set_callbacks(appsink, &callbacks, this, NULL); - gst_app_sink_set_drop(appsink, FALSE); - gst_app_sink_set_caps(appsink, caps); - gst_caps_unref(caps); - } - - // This takes ownership of the element; don't unref it - g_object_set(gstPipeline, "audio-sink", sink, NULL); - g_object_set(gstPipeline, "flags", flags, NULL); - return true; - } - - void HandleLoopTags() - { - // FIXME: Sample offsets assume a 44.1khz rate. Need to find some way - // to get the actual rate of the file from GStreamer - bool looppt_is_samples; - unsigned int looppt; - gchar *valstr; - - LoopPts[0] = 0; - if(gst_tag_list_get_string(TagList, "LOOP_START", &valstr)) - { - g_print("Got LOOP_START string: %s\n", valstr); - if(!S_ParseTimeTag(valstr, &looppt_is_samples, &looppt)) - Printf("Invalid LOOP_START tag: '%s'\n", valstr); - else - LoopPts[0] = (looppt_is_samples ? ((gint64)looppt*1000000000/44100) : - ((gint64)looppt*1000000)); - g_free(valstr); - } - LoopPts[1] = -1; - if(gst_tag_list_get_string(TagList, "LOOP_END", &valstr)) - { - g_print("Got LOOP_END string: %s\n", valstr); - if(!S_ParseTimeTag(valstr, &looppt_is_samples, &looppt)) - Printf("Invalid LOOP_END tag: '%s'\n", valstr); - else - { - LoopPts[1] = (looppt_is_samples ? ((gint64)looppt*1000000000/44100) : - ((gint64)looppt*1000000)); - if(LoopPts[1] <= LoopPts[0]) - LoopPts[1] = -1; - } - g_free(valstr); - } - } - - bool PreparePipeline() - { - GstBus *bus = gst_element_get_bus(gstPipeline); - if(!bus) return false; - - GstStateChangeReturn ret = gst_element_set_state(gstPipeline, GST_STATE_PAUSED); - if(ret == GST_STATE_CHANGE_ASYNC) - { - const GstMessageType types = GST_MESSAGE_ERROR | GST_MESSAGE_TAG | GST_MESSAGE_ASYNC_DONE; - GstMessage *msg; - while((msg=gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, types)) != NULL) - { - if(GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) - { - PrintErrMsg("Prepare Error", msg); - ret = GST_STATE_CHANGE_FAILURE; - gst_message_unref(msg); - break; - } - else if(GST_MESSAGE_TYPE(msg) != GST_MESSAGE_TAG) - { - gst_message_unref(msg); - break; - } - - GstTagList *tags = NULL; - gst_message_parse_tag(msg, &tags); - - gst_tag_list_insert(TagList, tags, GST_TAG_MERGE_KEEP); - - gst_tag_list_free(tags); - gst_message_unref(msg); - } - } - else if(ret == GST_STATE_CHANGE_SUCCESS) - { - GstMessage *msg; - while((msg=gst_bus_pop(bus)) != NULL) - { - if(GST_MESSAGE_TYPE(msg) == GST_MESSAGE_TAG) - { - GstTagList *tags = NULL; - gst_message_parse_tag(msg, &tags); - - gst_tag_list_insert(TagList, tags, GST_TAG_MERGE_KEEP); - - gst_tag_list_free(tags); - } - gst_message_unref(msg); - } - } - HandleLoopTags(); - - gst_object_unref(bus); - bus = NULL; - - return (ret != GST_STATE_CHANGE_FAILURE); - } - public: - FTempFileName tmpfile; ALfloat Volume; OpenALSoundStream(OpenALSoundRenderer *renderer) - : Renderer(renderer), gstPipeline(NULL), TagList(NULL), Source(0), - Playing(false), Looping(false), SamplesQueued(0), Callback(NULL), - UserData(NULL), BufferBytes(0), SrcCaps(NULL), Volume(1.0f) + : Renderer(renderer), Source(0), Playing(false), Convert(ConvNone), + Volume(1.0f) { - LoopPts[0] = LoopPts[1] = 0; Renderer->Streams.push_back(this); + memset(Buffers, 0, sizeof(Buffers)); } virtual ~OpenALSoundStream() { - Playing = false; - - if(SrcCaps) - gst_caps_unref(SrcCaps); - SrcCaps = NULL; - - if(gstPipeline) - { - gst_element_set_state(gstPipeline, GST_STATE_NULL); - gst_object_unref(gstPipeline); - gstPipeline = NULL; - } - - if(TagList) - gst_tag_list_free(TagList); - TagList = NULL; - if(Source) { alSourceRewind(Source); @@ -868,10 +335,10 @@ class OpenALSoundStream : public SoundStream Source = 0; } - if(Buffers.size() > 0) + if(Buffers[0]) { - alDeleteBuffers(Buffers.size(), &Buffers[0]); - Buffers.clear(); + alDeleteBuffers(BufferCount, &Buffers[0]); + memset(Buffers, 0, sizeof(Buffers)); } getALError(); @@ -880,644 +347,212 @@ class OpenALSoundStream : public SoundStream Renderer = NULL; } - virtual bool Play(bool looping, float vol) + + virtual bool Play(bool loop, float vol) { + SetVolume(vol); + if(Playing) return true; - GstBus *bus = gst_element_get_bus(gstPipeline); - if(!bus) return false; - - Looping = looping; - SetVolume(vol); - - if(Looping) + /* Clear the buffer queue, then fill and queue each buffer */ + alSourcei(Source, AL_BUFFER, 0); + for(int i = 0;i < BufferCount;i++) { - const GstSeekFlags flags = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT; - gst_element_seek(gstPipeline, 1.0, GST_FORMAT_TIME, flags, - GST_SEEK_TYPE_NONE, 0, GST_SEEK_TYPE_SET, LoopPts[1]); - } - - // Start playing the stream - Playing = true; - GstStateChangeReturn ret = gst_element_set_state(gstPipeline, GST_STATE_PLAYING); - if(ret == GST_STATE_CHANGE_FAILURE) - Playing = false; - if(ret == GST_STATE_CHANGE_ASYNC) - { - const GstMessageType types = GST_MESSAGE_ERROR | GST_MESSAGE_ASYNC_DONE; - GstMessage *msg; - if((msg=gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, types)) != NULL) + if(!Callback(this, &Data[0], Data.size(), UserData)) { - if(GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) - { - PrintErrMsg("Play Error", msg); - Playing = false; - } - gst_message_unref(msg); + if(i == 0) + return false; + break; } + + ALsizei len = Convert(Data); + alBufferData(Buffers[i], Format, &Data[0], len, SampleRate); + alSourceQueueBuffers(Source, 1, &Buffers[i]); } + if(getALError() != AL_NO_ERROR) + return false; - gst_object_unref(bus); - bus = NULL; + alSourcePlay(Source); + Playing = (getALError()==AL_NO_ERROR); return Playing; } virtual void Stop() { - GstBus *bus = gst_element_get_bus(gstPipeline); - if(!bus) return; + if(!Playing) + return; - // Stop the stream - GstStateChangeReturn ret = gst_element_set_state(gstPipeline, GST_STATE_PAUSED); - if(ret == GST_STATE_CHANGE_ASYNC) - { - Playing = false; - // Wait for the state change before requesting a seek - const GstMessageType types = GST_MESSAGE_ERROR | GST_MESSAGE_ASYNC_DONE; - GstMessage *msg; - if((msg=gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, types)) != NULL) - { - if(GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) - PrintErrMsg("Stop Error", msg); - else if(GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ASYNC_DONE) - ret = GST_STATE_CHANGE_SUCCESS; - gst_message_unref(msg); - } - } - if(ret == GST_STATE_CHANGE_SUCCESS) - { - Playing = false; + alSourceStop(Source); + alSourcei(Source, AL_BUFFER, 0); + getALError(); - alSourceRewind(Source); - getALError(); - - const GstSeekFlags flags = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT; - gst_element_seek_simple(gstPipeline, GST_FORMAT_TIME, flags, 0); - } - - gst_object_unref(bus); - bus = NULL; + Playing = false; } - virtual bool SetPaused(bool paused) - { - GstBus *bus = gst_element_get_bus(gstPipeline); - if(!bus) return false; - - GstStateChangeReturn ret; - ret = gst_element_set_state(gstPipeline, (paused ? GST_STATE_PAUSED : GST_STATE_PLAYING)); - if(ret == GST_STATE_CHANGE_ASYNC) - { - const GstMessageType types = GST_MESSAGE_ERROR | GST_MESSAGE_ASYNC_DONE; - GstMessage *msg; - if((msg=gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, types)) != NULL) - { - if(GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) - { - PrintErrMsg("Pause Error", msg); - ret = GST_STATE_CHANGE_FAILURE; - } - gst_message_unref(msg); - } - } - - if(ret != GST_STATE_CHANGE_FAILURE && paused) - { - alSourcePause(Source); - getALError(); - } - - gst_object_unref(bus); - bus = NULL; - - return (ret != GST_STATE_CHANGE_FAILURE); - } - virtual void SetVolume(float vol) { Volume = vol; - g_object_set(gstPipeline, "volume", (double)(Volume*Renderer->MusicVolume), NULL); + alSourcef(Source, AL_GAIN, Renderer->MusicVolume*Volume); + getALError(); } - virtual unsigned int GetPosition() + virtual bool SetPaused(bool pause) { - GstFormat format = GST_FORMAT_TIME; - gint64 pos; - - // Position will be handled in milliseconds; GStreamer's time format is in nanoseconds - if(gst_element_query_position(gstPipeline, &format, &pos) && format == GST_FORMAT_TIME) - return (unsigned int)(pos / 1000000); - return 0; + if(pause) + alSourcePause(Source); + else + alSourcePlay(Source); + return (getALError()==AL_NO_ERROR); } - virtual bool SetPosition(unsigned int val) - { - gint64 pos = (gint64)val * 1000000; - return gst_element_seek_simple(gstPipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_ACCURATE, pos); - } + virtual unsigned int GetPosition() + { return 0; } virtual bool IsEnded() { - GstBus *bus = gst_element_get_bus(gstPipeline); - if(!bus) return true; + if(!Playing) + return true; - GstMessage *msg; - while((msg=gst_bus_pop(bus)) != NULL) - { - switch(GST_MESSAGE_TYPE(msg)) - { - case GST_MESSAGE_SEGMENT_DONE: - case GST_MESSAGE_EOS: - Playing = false; - if(Looping) - Playing = gst_element_seek(gstPipeline, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | - GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_SEGMENT, - GST_SEEK_TYPE_SET, LoopPts[0], - GST_SEEK_TYPE_SET, LoopPts[1]); - break; + ALint state, queued, processed; + alGetSourcei(Source, AL_SOURCE_STATE, &state); + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); - case GST_MESSAGE_ERROR: - PrintErrMsg("Pipeline Error", msg); - Playing = false; - break; + Playing = (getALError()==AL_NO_ERROR); + if(!Playing) + return true; - case GST_MESSAGE_WARNING: - PrintErrMsg("Pipeline Warning", msg); - break; - - default: - break; - } - - gst_message_unref(msg); - } - - gst_object_unref(bus); - bus = NULL; - - return !Playing; - } - - bool Init(const char *filename) - { - if(!SetupSource() || !PipelineSetup()) - return false; - - GError *err = NULL; - gchar *uri; - if(g_path_is_absolute(filename)) - uri = g_filename_to_uri(filename, NULL, &err); - else if(g_strrstr(filename, "://") != NULL) - uri = g_strdup(filename); - else + // For each processed buffer in the queue... + while(processed > 0) { - gchar *curdir = g_get_current_dir(); - gchar *absolute_path = g_strconcat(curdir, G_DIR_SEPARATOR_S, filename, NULL); - uri = g_filename_to_uri(absolute_path, NULL, &err); - g_free(absolute_path); - g_free(curdir); - } + ALuint bufid; - if(!uri) - { - if(err) + // Unqueue the oldest buffer, fill it with more data, and queue it + // on the end + alSourceUnqueueBuffers(Source, 1, &bufid); + processed--; queued--; + + if(Callback(this, &Data[0], Data.size(), UserData)) { - Printf("Failed to convert "TEXTCOLOR_BOLD"%s"TEXTCOLOR_NORMAL" to URI: %s\n", - filename, err->message); - g_error_free(err); + ALsizei len = Convert(Data); + alBufferData(bufid, Format, &Data[0], len, SampleRate); + alSourceQueueBuffers(Source, 1, &bufid); + queued++; } - return false; } - g_object_set(gstPipeline, "uri", uri, NULL); - g_free(uri); - - return PreparePipeline(); - } - - bool Init(const BYTE *data, unsigned int datalen) - { - // Can't keep the original pointer since the memory can apparently be - // overwritten at some point (happens with MIDI data, at least) - MemData.resize(datalen); - memcpy(&MemData[0], data, datalen); - MemDataPos = 0; - - if(!SetupSource() || !PipelineSetup()) - return false; - - g_object_set(gstPipeline, "uri", "appsrc://", NULL); - g_signal_connect(gstPipeline, "deep-notify::source", G_CALLBACK(memdata_source), this); - - return PreparePipeline(); - } - - bool Init(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) - { - Callback = callback; - UserData = userdata; - BufferBytes = buffbytes; - - GstStructure *structure; - if((flags&Float)) - structure = gst_structure_new("audio/x-raw-float", - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "width", G_TYPE_INT, 32, NULL); - else if((flags&Bits32)) - structure = gst_structure_new("audio/x-raw-int", - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "width", G_TYPE_INT, 32, - "depth", G_TYPE_INT, 32, - "signed", G_TYPE_BOOLEAN, TRUE, NULL); - else if((flags&Bits8)) - structure = gst_structure_new("audio/x-raw-int", - "width", G_TYPE_INT, 8, - "depth", G_TYPE_INT, 8, - "signed", G_TYPE_BOOLEAN, TRUE, NULL); - else - structure = gst_structure_new("audio/x-raw-int", - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "width", G_TYPE_INT, 16, - "depth", G_TYPE_INT, 16, - "signed", G_TYPE_BOOLEAN, TRUE, NULL); - gst_structure_set(structure, "channels", G_TYPE_INT, (flags&Mono)?1:2, NULL); - gst_structure_set(structure, "rate", G_TYPE_INT, samplerate, NULL); - - SrcCaps = gst_caps_new_full(structure, NULL); - - if(!SrcCaps || !SetupSource() || !PipelineSetup()) - return false; - - g_object_set(gstPipeline, "uri", "appsrc://", NULL); - g_signal_connect(gstPipeline, "deep-notify::source", G_CALLBACK(callback_source), this); - - return PreparePipeline(); - } -}; - - -class Decoder -{ - GstElement *gstPipeline, *gstSink; - GstTagList *TagList; - - const guint8 *MemData; - size_t MemDataSize; - size_t MemDataPos; - - static void need_memdata(GstAppSrc *appsrc, guint size, gpointer user_data) - { - Decoder *self = static_cast(user_data); - GstFlowReturn ret; - - if(self->MemDataPos >= self->MemDataSize) + // If the source is not playing or paused, and there are buffers queued, + // then there was an underrun. Restart the source. + Playing = (getALError()==AL_NO_ERROR) && (queued>0); + if(Playing && state != AL_PLAYING && state != AL_PAUSED) { - gst_app_src_end_of_stream(appsrc); - return; + alSourcePlay(Source); + Playing = (getALError()==AL_NO_ERROR); } - const guint8 *data = &self->MemData[self->MemDataPos]; - size = (std::min)(size, (guint)(self->MemDataSize - self->MemDataPos)); - self->MemDataPos += size; - - GstBuffer *buffer = gst_buffer_new(); - GST_BUFFER_DATA(buffer) = const_cast(data); - GST_BUFFER_SIZE(buffer) = size; - - gst_app_src_push_buffer(appsrc, buffer); + return !Playing; } - static gboolean seek_memdata(GstAppSrc *appsrc, guint64 position, gpointer user_data) - { - Decoder *self = static_cast(user_data); + bool Init(const char*, int, int) + { return false; } - if(position > self->MemDataSize) - return FALSE; - self->MemDataPos = position; - return TRUE; - } + bool Init(const BYTE*, unsigned int) + { return false; } - static void memdata_source(GObject *object, GObject *orig, GParamSpec *pspec, Decoder *self) + bool Init(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) { - GstElement *elem; - g_object_get(self->gstPipeline, "source", &elem, NULL); - - GstAppSrc *appsrc = GST_APP_SRC(elem); - GstAppSrcCallbacks callbacks = { - need_memdata, NULL, seek_memdata - }; - gst_app_src_set_callbacks(appsrc, &callbacks, self, NULL); - gst_app_src_set_size(appsrc, self->MemDataSize); - gst_app_src_set_stream_type(appsrc, GST_APP_STREAM_TYPE_RANDOM_ACCESS); - - gst_object_unref(appsrc); - } - - static GstFlowReturn sink_preroll(GstAppSink *sink, gpointer user_data) - { - Decoder *self = static_cast(user_data); - - GstBuffer *buffer = gst_app_sink_pull_preroll(sink); - if(!buffer) return GST_FLOW_ERROR; - - if(self->OutRate == 0) - { - GstCaps *caps = GST_BUFFER_CAPS(buffer); - - gint channels = 0, rate = 0, bits = 0, i; - for(i = gst_caps_get_size(caps)-1;i >= 0;i--) - { - GstStructure *struc = gst_caps_get_structure(caps, i); - if(gst_structure_has_field(struc, "channels")) - gst_structure_get_int(struc, "channels", &channels); - if(gst_structure_has_field(struc, "rate")) - gst_structure_get_int(struc, "rate", &rate); - if(gst_structure_has_field(struc, "width")) - gst_structure_get_int(struc, "width", &bits); - } - - self->OutChannels = channels; - self->OutBits = bits; - self->OutRate = rate; - } - - gst_buffer_unref(buffer); - if(self->OutRate <= 0) - return GST_FLOW_ERROR; - return GST_FLOW_OK; - } - - static GstFlowReturn sink_buffer(GstAppSink *sink, gpointer user_data) - { - Decoder *self = static_cast(user_data); - - GstBuffer *buffer = gst_app_sink_pull_buffer(sink); - if(!buffer) return GST_FLOW_ERROR; - - guint newsize = GST_BUFFER_SIZE(buffer); - size_t pos = self->OutData.size(); - self->OutData.resize(pos+newsize); - - memcpy(&self->OutData[pos], GST_BUFFER_DATA(buffer), newsize); - - gst_buffer_unref(buffer); - return GST_FLOW_OK; - } - - bool PipelineSetup(int forcebits) - { - if(forcebits && forcebits != 8 && forcebits != 16) + if(!SetupSource()) return false; - TagList = gst_tag_list_new(); - g_return_val_if_fail(TagList != NULL, false); - - gstPipeline = gst_element_factory_make("playbin2", NULL); - g_return_val_if_fail(gstPipeline != NULL, false); - - gstSink = gst_element_factory_make("appsink", NULL); - g_return_val_if_fail(gstSink != NULL, false); - - GstAppSink *appsink = GST_APP_SINK(gstSink); - GstAppSinkCallbacks callbacks = { - NULL, sink_preroll, sink_buffer, NULL - }; - - GstCaps *caps = SupportedBufferFormatCaps(forcebits); - g_object_set(appsink, "sync", FALSE, NULL); - gst_app_sink_set_callbacks(appsink, &callbacks, this, NULL); - gst_app_sink_set_drop(appsink, FALSE); - gst_app_sink_set_caps(appsink, caps); - - g_object_set(gstPipeline, "audio-sink", gst_object_ref(gstSink), NULL); - g_object_set(gstPipeline, "flags", 0x02, NULL); - - gst_caps_unref(caps); - return true; - } - - void HandleLoopTags(unsigned int looppt[2], bool looppt_is_samples[2], bool has_looppt[2]) - { - gchar *valstr; - - if(gst_tag_list_get_string(TagList, "LOOP_START", &valstr)) - { - g_print("Got LOOP_START string: %s\n", valstr); - has_looppt[0] = S_ParseTimeTag(valstr, &looppt_is_samples[0], &looppt[0]); - if(!has_looppt[0]) - Printf("Invalid LOOP_START tag: '%s'\n", valstr); - g_free(valstr); - } - if(gst_tag_list_get_string(TagList, "LOOP_END", &valstr)) - { - g_print("Got LOOP_END string: %s\n", valstr); - has_looppt[1] = S_ParseTimeTag(valstr, &looppt_is_samples[1], &looppt[1]); - if(!has_looppt[1]) - Printf("Invalid LOOP_END tag: '%s'\n", valstr); - g_free(valstr); - } - } - -public: - std::vector OutData; - ALint LoopPts[2]; - ALsizei OutRate; - ALuint OutChannels; - ALuint OutBits; - - Decoder() - : gstPipeline(NULL), gstSink(NULL), TagList(NULL), - OutRate(0), OutChannels(0), OutBits(0) - { LoopPts[0] = LoopPts[1] = 0; } - - virtual ~Decoder() - { - if(gstSink) - gst_object_unref(gstSink); - gstSink = NULL; - - if(gstPipeline) - { - gst_element_set_state(gstPipeline, GST_STATE_NULL); - gst_object_unref(gstPipeline); - gstPipeline = NULL; - } - - if(TagList) - gst_tag_list_free(TagList); - TagList = NULL; - } - - bool Decode(const void *data, unsigned int datalen, int forcebits=0) - { - MemData = static_cast(data); - MemDataSize = datalen; - MemDataPos = 0; - OutData.clear(); - - if(!PipelineSetup(forcebits)) + alGenBuffers(BufferCount, Buffers); + if(getALError() != AL_NO_ERROR) return false; - g_object_set(gstPipeline, "uri", "appsrc://", NULL); - g_signal_connect(gstPipeline, "deep-notify::source", G_CALLBACK(memdata_source), this); + Callback = callback; + UserData = userdata; + SampleRate = samplerate; - - GstBus *bus = gst_element_get_bus(gstPipeline); - if(!bus) return false; - GstMessage *msg; - - GstStateChangeReturn ret = gst_element_set_state(gstPipeline, GST_STATE_PLAYING); - if(ret == GST_STATE_CHANGE_ASYNC) + int framesize; + if((flags&Mono)) { - const GstMessageType types = GST_MESSAGE_ERROR | GST_MESSAGE_TAG | GST_MESSAGE_ASYNC_DONE; - while((msg=gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, types)) != NULL) + framesize = 1; + if((flags&Float)) { - if(GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ASYNC_DONE) + framesize *= sizeof(float); + Format = alGetEnumValue("AL_FORMAT_MONO_FLOAT32"); + if(Format == 0 || Format == -1) { - ret = GST_STATE_CHANGE_SUCCESS; - break; + Format = AL_FORMAT_MONO16; + Convert = Conv32fToS16; } - else if(GST_MESSAGE_TYPE(msg) == GST_MESSAGE_TAG) - { - GstTagList *tags = NULL; - gst_message_parse_tag(msg, &tags); - - gst_tag_list_insert(TagList, tags, GST_TAG_MERGE_KEEP); - - gst_tag_list_free(tags); - } - else if(GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) - { - ret = GST_STATE_CHANGE_FAILURE; - PrintErrMsg("Decoder Error", msg); - break; - } - gst_message_unref(msg); } + else if((flags&Bits32)) + { + /* Signed 32-bit int is not supported directly by OpenAL. + * Convert to signed 16-bit */ + framesize *= sizeof(ALint); + Format = AL_FORMAT_MONO16; + Convert = ConvS32ToS16; + } + else if((flags&Bits8)) + { + /* Signed or unsigned? We assume unsigned 8-bit... */ + Format = AL_FORMAT_MONO8; + framesize *= sizeof(ALubyte); + } + else + { + Format = AL_FORMAT_MONO16; + framesize *= sizeof(ALshort); + } } - - bool err = true; - if(ret == GST_STATE_CHANGE_SUCCESS) + else { - const GstMessageType types = GST_MESSAGE_ERROR | GST_MESSAGE_TAG | GST_MESSAGE_EOS; - while((msg=gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, types)) != NULL) + framesize = 2; + if((flags&Float)) { - if(GST_MESSAGE_TYPE(msg) == GST_MESSAGE_EOS) + framesize *= sizeof(float); + Format = alGetEnumValue("AL_FORMAT_STEREO_FLOAT32"); + if(Format == 0 || Format == -1) { - err = false; - gst_message_unref(msg); - break; + Format = AL_FORMAT_STEREO16; + Convert = Conv32fToS16; } - if(GST_MESSAGE_TYPE(msg) != GST_MESSAGE_TAG) - { - PrintErrMsg("Decoder Error", msg); - gst_message_unref(msg); - break; - } - - GstTagList *tags = NULL; - gst_message_parse_tag(msg, &tags); - - gst_tag_list_insert(TagList, tags, GST_TAG_MERGE_KEEP); - - gst_tag_list_free(tags); - gst_message_unref(msg); } - } - - if(!err) - { - ALuint FrameSize = OutChannels*OutBits/8; - if(OutData.size() >= FrameSize) + else if((flags&Bits32)) { - // HACK: Evilness abound. Seems GStreamer doesn't like (certain?) - // wave files and produces an extra sample, which can cause an - // audible click at the end. Cut it. - OutData.resize(OutData.size() - FrameSize); + framesize *= sizeof(ALint); + Format = AL_FORMAT_STEREO16; + Convert = ConvS32ToS16; } - - unsigned int looppt[2] = { 0, 0 }; - bool looppt_is_samples[2] = { true, true }; - bool has_looppt[2] = { false, false }; - - HandleLoopTags(looppt, looppt_is_samples, has_looppt); - if(has_looppt[0] || has_looppt[1]) + else if((flags&Bits8)) { - if(!has_looppt[0]) - LoopPts[0] = 0; - else if(looppt_is_samples[0]) - LoopPts[0] = (std::min)((ALint)looppt[0], - (ALint)(OutData.size() / FrameSize)); - else - LoopPts[0] = (std::min)((ALint)((gint64)looppt[0] * OutRate / 1000), - (ALint)(OutData.size() / FrameSize)); - - if(!has_looppt[1]) - LoopPts[1] = OutData.size() / FrameSize; - else if(looppt_is_samples[1]) - LoopPts[1] = (std::min)((ALint)looppt[1], - (ALint)(OutData.size() / FrameSize)); - else - LoopPts[1] = (std::min)((ALint)((gint64)looppt[1] * OutRate / 1000), - (ALint)(OutData.size() / FrameSize)); + framesize *= sizeof(ALubyte); + Format = AL_FORMAT_STEREO8; } + else + { + framesize *= sizeof(ALshort); + Format = AL_FORMAT_STEREO16; + } } - gst_object_unref(bus); - bus = NULL; + if(Format == 0 || Format == -1) + { + Printf("Unsupported format: 0x%x\n", flags); + return false; + } - return !err; - } -}; -#else /* WITH_GSTREAMER */ -class OpenALSoundStream : public SoundStream -{ - OpenALSoundRenderer *Renderer; + /* FIXME: Is buffbytes the total, or per-buffer, size? If per-buffer, + * how many buffers is it expecting? */ + buffbytes = (buffbytes/BufferCount) + framesize-1; + buffbytes -= buffbytes%framesize; + Data.resize(buffbytes); -public: - FTempFileName tmpfile; - ALfloat Volume; - - OpenALSoundStream(OpenALSoundRenderer *renderer) - : Renderer(renderer), Volume(1.0f) - { Renderer->Streams.push_back(this); } - - virtual ~OpenALSoundStream() - { - Renderer->Streams.erase(find(Renderer->Streams.begin(), - Renderer->Streams.end(), this)); - Renderer = NULL; + return true; } - - - virtual bool Play(bool, float) - { return false; } - - virtual void Stop() - { } - - virtual void SetVolume(float vol) - { Volume = vol; } - - virtual bool SetPaused(bool) - { return false; } - - virtual unsigned int GetPosition() - { return 0; } - - virtual bool IsEnded() - { return true; } - - bool Init(const char*) - { return false; } - - bool Init(const BYTE*, unsigned int) - { return false; } - - bool Init(SoundStreamCallback, int, int, int, void*) - { return false; } }; class Decoder @@ -1566,7 +601,7 @@ OpenALSoundRenderer::OpenALSoundRenderer() } #endif - if(snd_aldevice != "Default") + if(strcmp(snd_aldevice, "Default") != 0) { Device = alcOpenDevice(*snd_aldevice); if(!Device) @@ -1675,6 +710,8 @@ OpenALSoundRenderer::OpenALSoundRenderer() LastWaterAbsorb = 0.0f; if(*snd_efx && alcIsExtensionPresent(Device, "ALC_EXT_EFX")) { + alListenerf(AL_METERS_PER_UNIT, 1.0f/96.0f); + // EFX function pointers #define LOAD_FUNC(x) (LoadALFunc(#x, &x)) LOAD_FUNC(alGenEffects); @@ -1866,7 +903,7 @@ float OpenALSoundRenderer::GetOutputRate() } -SoundHandle OpenALSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart) +SoundHandle OpenALSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend) { SoundHandle retval = { NULL }; @@ -1899,10 +936,11 @@ SoundHandle OpenALSoundRenderer::LoadSoundRaw(BYTE *sf } length -= length%(channels*bits/8); - ALenum err; ALuint buffer = 0; alGenBuffers(1, &buffer); alBufferData(buffer, format, sfxdata, length, frequency); + + ALenum err; if((err=getALError()) != AL_NO_ERROR) { Printf("Failed to buffer data: %s\n", alGetString(err)); @@ -1911,17 +949,18 @@ SoundHandle OpenALSoundRenderer::LoadSoundRaw(BYTE *sf return retval; } - if(loopstart > 0 && LoopPoints) + if(LoopPoints) { - ALint loops[2] = { - loopstart, - length / (channels*bits/8) - }; - Printf("Setting loop points %d -> %d\n", loops[0], loops[1]); - alBufferiv(buffer, AL_LOOP_POINTS, loops); + if(loopstart <= 0) loopstart = 0; + if(loopend <= loopstart) + loopend = length / (channels*bits/8); + + ALint pts[2] = { loopstart, loopend }; + Printf("Setting loop points %d -> %d\n", pts[0], pts[1]); + alBufferiv(buffer, AL_LOOP_POINTS, pts); getALError(); } - else if(loopstart > 0) + else if(loopstart > 0 || loopend > 0) { static bool warned = false; if(!warned) @@ -2051,34 +1090,8 @@ SoundStream *OpenALSoundRenderer::OpenStream(const cha { std::auto_ptr stream(new OpenALSoundStream(this)); - if(offset > 0) - { - // If there's an offset to the start of the data, separate it into its - // own temp file - FILE *infile = fopen(filename, "rb"); - FILE *f = fopen(stream->tmpfile, "wb"); - if(!infile || !f || fseek(infile, offset, SEEK_SET) != 0) - { - if(infile) fclose(infile); - if(f) fclose(f); - return NULL; - } - BYTE buf[1024]; - size_t got; - do { - got = (std::min)(sizeof(buf), (size_t)length); - got = fread(buf, 1, got, infile); - if(got == 0) - break; - } while(fwrite(buf, 1, got, f) == got && (length-=got) > 0); - fclose(f); - fclose(infile); - - filename = stream->tmpfile; - } - bool ok = ((offset == -1) ? stream->Init((const BYTE*)filename, length) : - stream->Init(filename)); + stream->Init(filename, offset, length)); if(ok == false) return NULL; @@ -2567,6 +1580,11 @@ void OpenALSoundRenderer::UpdateSounds() { alcProcessContext(Context); + // HACK: For some reason, the stream's IsEnded method isn't being called by + // the engine + foreach(OpenALSoundStream*, i, Streams) + (*i)->IsEnded(); + if(DisconnectNotify) { ALCint connected = ALC_TRUE; @@ -2680,7 +1698,7 @@ void OpenALSoundRenderer::PrintDriversList() return; } - Printf("%c%s%2d. %s\n", ' ', ((snd_aldevice=="Default") ? TEXTCOLOR_BOLD : ""), 0, + Printf("%c%s%2d. %s\n", ' ', ((strcmp(*snd_aldevice, "Default")==0) ? TEXTCOLOR_BOLD : ""), 0, "Default"); for(int i = 1;*drivers;i++) { @@ -2726,12 +1744,18 @@ void OpenALSoundRenderer::LoadReverb(const ReverbConta alGenEffects(1, &envReverb); if(getALError() == AL_NO_ERROR) { - alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); - ok = (alGetError() == AL_NO_ERROR); - if(!ok) + if(alGetEnumValue("AL_EFFECT_EAXREVERB") == AL_EFFECT_EAXREVERB) { - alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); + alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); ok = (alGetError() == AL_NO_ERROR); + } + if(!ok) + { + if(alGetEnumValue("AL_EFFECT_REVERB") == AL_EFFECT_REVERB) + { + alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); + ok = (alGetError() == AL_NO_ERROR); + } } if(!ok) {