mirror of
https://github.com/love2d/love-android.git
synced 2026-08-16 08:11:44 +02:00
Upgraded OpenAL-Soft to 0.18.2
This commit is contained in:
@@ -21,8 +21,8 @@ LOCAL_C_INCLUDES := \
|
||||
${LOCAL_PATH}/../libmng-1.0.10/ \
|
||||
${LOCAL_PATH}/../lcms2-2.5/include \
|
||||
${LOCAL_PATH}/../tiff-3.9.5/libtiff \
|
||||
${LOCAL_PATH}/../openal-soft-1.17.0/include \
|
||||
${LOCAL_PATH}/../openal-soft-1.17.0/OpenAL32/Include \
|
||||
${LOCAL_PATH}/../openal-soft-1.18.2/include \
|
||||
${LOCAL_PATH}/../openal-soft-1.18.2/OpenAL32/Include \
|
||||
${LOCAL_PATH}/../freetype2-android/include \
|
||||
${LOCAL_PATH}/../freetype2-android/src \
|
||||
${LOCAL_PATH}/../physfs-3.0.1/src \
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,129 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
|
||||
struct RingBuffer {
|
||||
ALubyte *mem;
|
||||
|
||||
ALsizei frame_size;
|
||||
ALsizei length;
|
||||
ALint read_pos;
|
||||
ALint write_pos;
|
||||
|
||||
almtx_t mtx;
|
||||
};
|
||||
|
||||
|
||||
RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length)
|
||||
{
|
||||
RingBuffer *ring = calloc(1, sizeof(*ring) + ((length+1) * frame_size));
|
||||
if(ring)
|
||||
{
|
||||
ring->mem = (ALubyte*)(ring+1);
|
||||
|
||||
ring->frame_size = frame_size;
|
||||
ring->length = length+1;
|
||||
ring->read_pos = 0;
|
||||
ring->write_pos = 0;
|
||||
|
||||
almtx_init(&ring->mtx, almtx_plain);
|
||||
}
|
||||
return ring;
|
||||
}
|
||||
|
||||
void DestroyRingBuffer(RingBuffer *ring)
|
||||
{
|
||||
if(ring)
|
||||
{
|
||||
almtx_destroy(&ring->mtx);
|
||||
free(ring);
|
||||
}
|
||||
}
|
||||
|
||||
ALsizei RingBufferSize(RingBuffer *ring)
|
||||
{
|
||||
ALsizei s;
|
||||
|
||||
almtx_lock(&ring->mtx);
|
||||
s = (ring->write_pos-ring->read_pos+ring->length) % ring->length;
|
||||
almtx_unlock(&ring->mtx);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len)
|
||||
{
|
||||
int remain;
|
||||
|
||||
almtx_lock(&ring->mtx);
|
||||
|
||||
remain = (ring->read_pos-ring->write_pos-1+ring->length) % ring->length;
|
||||
if(remain < len) len = remain;
|
||||
|
||||
if(len > 0)
|
||||
{
|
||||
remain = ring->length - ring->write_pos;
|
||||
if(remain < len)
|
||||
{
|
||||
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
|
||||
remain*ring->frame_size);
|
||||
memcpy(ring->mem, data+(remain*ring->frame_size),
|
||||
(len-remain)*ring->frame_size);
|
||||
}
|
||||
else
|
||||
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
|
||||
len*ring->frame_size);
|
||||
|
||||
ring->write_pos += len;
|
||||
ring->write_pos %= ring->length;
|
||||
}
|
||||
|
||||
almtx_unlock(&ring->mtx);
|
||||
}
|
||||
|
||||
void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len)
|
||||
{
|
||||
int remain;
|
||||
|
||||
almtx_lock(&ring->mtx);
|
||||
|
||||
remain = ring->length - ring->read_pos;
|
||||
if(remain < len)
|
||||
{
|
||||
memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), remain*ring->frame_size);
|
||||
memcpy(data+(remain*ring->frame_size), ring->mem, (len-remain)*ring->frame_size);
|
||||
}
|
||||
else
|
||||
memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), len*ring->frame_size);
|
||||
|
||||
ring->read_pos += len;
|
||||
ring->read_pos %= ring->length;
|
||||
|
||||
almtx_unlock(&ring->mtx);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#ifndef ALSTRING_H
|
||||
#define ALSTRING_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
|
||||
typedef char al_string_char_type;
|
||||
TYPEDEF_VECTOR(al_string_char_type, al_string)
|
||||
|
||||
inline void al_string_deinit(al_string *str)
|
||||
{ VECTOR_DEINIT(*str); }
|
||||
#define AL_STRING_INIT(_x) do { (_x) = (al_string)NULL; } while(0)
|
||||
#define AL_STRING_INIT_STATIC() ((al_string)NULL)
|
||||
#define AL_STRING_DEINIT(_x) al_string_deinit(&(_x))
|
||||
|
||||
inline ALsizei al_string_length(const_al_string str)
|
||||
{ return VECTOR_SIZE(str); }
|
||||
|
||||
inline ALboolean al_string_empty(const_al_string str)
|
||||
{ return al_string_length(str) == 0; }
|
||||
|
||||
inline const al_string_char_type *al_string_get_cstr(const_al_string str)
|
||||
{ return str ? &VECTOR_FRONT(str) : ""; }
|
||||
|
||||
void al_string_clear(al_string *str);
|
||||
|
||||
int al_string_cmp(const_al_string str1, const_al_string str2);
|
||||
int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2);
|
||||
|
||||
void al_string_copy(al_string *str, const_al_string from);
|
||||
void al_string_copy_cstr(al_string *str, const al_string_char_type *from);
|
||||
|
||||
void al_string_append_char(al_string *str, const al_string_char_type c);
|
||||
void al_string_append_cstr(al_string *str, const al_string_char_type *from);
|
||||
void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <wchar.h>
|
||||
/* Windows-only methods to deal with WideChar strings. */
|
||||
void al_string_copy_wcstr(al_string *str, const wchar_t *from);
|
||||
#endif
|
||||
|
||||
#endif /* ALSTRING_H */
|
||||
@@ -1,232 +0,0 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
|
||||
/* Base ALCbackend method implementations. */
|
||||
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)
|
||||
{
|
||||
int ret;
|
||||
self->mDevice = device;
|
||||
ret = almtx_init(&self->mMutex, almtx_recursive);
|
||||
assert(ret == althrd_success);
|
||||
}
|
||||
|
||||
void ALCbackend_Destruct(ALCbackend *self)
|
||||
{
|
||||
almtx_destroy(&self->mMutex);
|
||||
}
|
||||
|
||||
ALCboolean ALCbackend_reset(ALCbackend* UNUSED(self))
|
||||
{
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
ALCenum ALCbackend_captureSamples(ALCbackend* UNUSED(self), void* UNUSED(buffer), ALCuint UNUSED(samples))
|
||||
{
|
||||
return ALC_INVALID_DEVICE;
|
||||
}
|
||||
|
||||
ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ALint64 ALCbackend_getLatency(ALCbackend* UNUSED(self))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ALCbackend_lock(ALCbackend *self)
|
||||
{
|
||||
int ret = almtx_lock(&self->mMutex);
|
||||
assert(ret == althrd_success);
|
||||
}
|
||||
|
||||
void ALCbackend_unlock(ALCbackend *self)
|
||||
{
|
||||
int ret = almtx_unlock(&self->mMutex);
|
||||
assert(ret == althrd_success);
|
||||
}
|
||||
|
||||
|
||||
/* Base ALCbackendFactory method implementations. */
|
||||
void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/* Wrappers to use an old-style backend with the new interface. */
|
||||
typedef struct PlaybackWrapper {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
const BackendFuncs *Funcs;
|
||||
} PlaybackWrapper;
|
||||
|
||||
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct)
|
||||
static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
|
||||
static void PlaybackWrapper_close(PlaybackWrapper *self);
|
||||
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
|
||||
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
|
||||
static void PlaybackWrapper_stop(PlaybackWrapper *self);
|
||||
static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
|
||||
static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self);
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
|
||||
DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
|
||||
|
||||
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
|
||||
|
||||
self->Funcs = funcs;
|
||||
}
|
||||
|
||||
static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->OpenPlayback(device, name);
|
||||
}
|
||||
|
||||
static void PlaybackWrapper_close(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->ClosePlayback(device);
|
||||
}
|
||||
|
||||
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->ResetPlayback(device);
|
||||
}
|
||||
|
||||
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->StartPlayback(device);
|
||||
}
|
||||
|
||||
static void PlaybackWrapper_stop(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->StopPlayback(device);
|
||||
}
|
||||
|
||||
static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->GetLatency(device);
|
||||
}
|
||||
|
||||
|
||||
typedef struct CaptureWrapper {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
const BackendFuncs *Funcs;
|
||||
} CaptureWrapper;
|
||||
|
||||
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct)
|
||||
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
|
||||
static void CaptureWrapper_close(CaptureWrapper *self);
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
|
||||
static void CaptureWrapper_stop(CaptureWrapper *self);
|
||||
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
|
||||
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
|
||||
static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self);
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
|
||||
DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
|
||||
|
||||
|
||||
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(CaptureWrapper, ALCbackend, self);
|
||||
|
||||
self->Funcs = funcs;
|
||||
}
|
||||
|
||||
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->OpenCapture(device, name);
|
||||
}
|
||||
|
||||
static void CaptureWrapper_close(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->CloseCapture(device);
|
||||
}
|
||||
|
||||
static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->StartCapture(device);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void CaptureWrapper_stop(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->StopCapture(device);
|
||||
}
|
||||
|
||||
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->CaptureSamples(device, buffer, samples);
|
||||
}
|
||||
|
||||
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->AvailableSamples(device);
|
||||
}
|
||||
|
||||
static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->GetLatency(device);
|
||||
}
|
||||
|
||||
|
||||
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
PlaybackWrapper *backend;
|
||||
|
||||
backend = PlaybackWrapper_New(sizeof(*backend));
|
||||
if(!backend) return NULL;
|
||||
|
||||
PlaybackWrapper_Construct(backend, device, funcs);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
CaptureWrapper *backend;
|
||||
|
||||
backend = CaptureWrapper_New(sizeof(*backend));
|
||||
if(!backend) return NULL;
|
||||
|
||||
CaptureWrapper_Construct(backend, device, funcs);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,424 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* This is an OpenAL backend for Android using the native audio APIs based on
|
||||
* OpenSL ES 1.0.1. It is based on source code for the native-audio sample app
|
||||
* bundled with NDK.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
/* Helper macros */
|
||||
#define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
|
||||
#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* engine interfaces */
|
||||
SLObjectItf engineObject;
|
||||
SLEngineItf engine;
|
||||
|
||||
/* output mix interfaces */
|
||||
SLObjectItf outputMix;
|
||||
|
||||
/* buffer queue player interfaces */
|
||||
SLObjectItf bufferQueueObject;
|
||||
|
||||
void *buffer;
|
||||
ALuint bufferSize;
|
||||
ALuint curBuffer;
|
||||
|
||||
ALuint frameSize;
|
||||
} osl_data;
|
||||
|
||||
|
||||
static const ALCchar opensl_device[] = "OpenSL";
|
||||
|
||||
|
||||
static SLuint32 GetChannelMask(enum DevFmtChannels chans)
|
||||
{
|
||||
switch(chans)
|
||||
{
|
||||
case DevFmtMono: return SL_SPEAKER_FRONT_CENTER;
|
||||
case DevFmtStereo: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT;
|
||||
case DevFmtQuad: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
|
||||
case DevFmtX51: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
|
||||
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
|
||||
case DevFmtX61: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
|
||||
SL_SPEAKER_BACK_CENTER|
|
||||
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
|
||||
case DevFmtX71: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
|
||||
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT|
|
||||
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
|
||||
case DevFmtX51Side: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
|
||||
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *res_str(SLresult result)
|
||||
{
|
||||
switch(result)
|
||||
{
|
||||
case SL_RESULT_SUCCESS: return "Success";
|
||||
case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated";
|
||||
case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid";
|
||||
case SL_RESULT_MEMORY_FAILURE: return "Memory failure";
|
||||
case SL_RESULT_RESOURCE_ERROR: return "Resource error";
|
||||
case SL_RESULT_RESOURCE_LOST: return "Resource lost";
|
||||
case SL_RESULT_IO_ERROR: return "I/O error";
|
||||
case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient";
|
||||
case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted";
|
||||
case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported";
|
||||
case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found";
|
||||
case SL_RESULT_PERMISSION_DENIED: return "Permission denied";
|
||||
case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported";
|
||||
case SL_RESULT_INTERNAL_ERROR: return "Internal error";
|
||||
case SL_RESULT_UNKNOWN_ERROR: return "Unknown error";
|
||||
case SL_RESULT_OPERATION_ABORTED: return "Operation aborted";
|
||||
case SL_RESULT_CONTROL_LOST: return "Control lost";
|
||||
#ifdef SL_RESULT_READONLY
|
||||
case SL_RESULT_READONLY: return "ReadOnly";
|
||||
#endif
|
||||
#ifdef SL_RESULT_ENGINEOPTION_UNSUPPORTED
|
||||
case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported";
|
||||
#endif
|
||||
#ifdef SL_RESULT_SOURCE_SINK_INCOMPATIBLE
|
||||
case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible";
|
||||
#endif
|
||||
}
|
||||
return "Unknown error code";
|
||||
}
|
||||
|
||||
#define PRINTERR(x, s) do { \
|
||||
if((x) != SL_RESULT_SUCCESS) \
|
||||
ERR("%s: %s\n", (s), res_str((x))); \
|
||||
} while(0)
|
||||
|
||||
/* this callback handler is called every time a buffer finishes playing */
|
||||
static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context)
|
||||
{
|
||||
ALCdevice *Device = context;
|
||||
osl_data *data = Device->ExtraData;
|
||||
ALvoid *buf;
|
||||
SLresult result;
|
||||
|
||||
buf = (ALbyte*)data->buffer + data->curBuffer*data->bufferSize;
|
||||
aluMixData(Device, buf, data->bufferSize/data->frameSize);
|
||||
|
||||
result = VCALL(bq,Enqueue)(buf, data->bufferSize);
|
||||
PRINTERR(result, "bq->Enqueue");
|
||||
|
||||
data->curBuffer = (data->curBuffer+1) % Device->NumUpdates;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName)
|
||||
{
|
||||
osl_data *data = NULL;
|
||||
SLresult result;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = opensl_device;
|
||||
else if(strcmp(deviceName, opensl_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
if(!data)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
|
||||
// create engine
|
||||
result = slCreateEngine(&data->engineObject, 0, NULL, 0, NULL, NULL);
|
||||
PRINTERR(result, "slCreateEngine");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(data->engineObject,Realize)(SL_BOOLEAN_FALSE);
|
||||
PRINTERR(result, "engine->Realize");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(data->engineObject,GetInterface)(SL_IID_ENGINE, &data->engine);
|
||||
PRINTERR(result, "engine->GetInterface");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(data->engine,CreateOutputMix)(&data->outputMix, 0, NULL, NULL);
|
||||
PRINTERR(result, "engine->CreateOutputMix");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(data->outputMix,Realize)(SL_BOOLEAN_FALSE);
|
||||
PRINTERR(result, "outputMix->Realize");
|
||||
}
|
||||
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
if(data->outputMix != NULL)
|
||||
VCALL0(data->outputMix,Destroy)();
|
||||
data->outputMix = NULL;
|
||||
|
||||
if(data->engineObject != NULL)
|
||||
VCALL0(data->engineObject,Destroy)();
|
||||
data->engineObject = NULL;
|
||||
data->engine = NULL;
|
||||
|
||||
free(data);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&Device->DeviceName, deviceName);
|
||||
Device->ExtraData = data;
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static void opensl_close_playback(ALCdevice *Device)
|
||||
{
|
||||
osl_data *data = Device->ExtraData;
|
||||
|
||||
if(data->bufferQueueObject != NULL)
|
||||
VCALL0(data->bufferQueueObject,Destroy)();
|
||||
data->bufferQueueObject = NULL;
|
||||
|
||||
VCALL0(data->outputMix,Destroy)();
|
||||
data->outputMix = NULL;
|
||||
|
||||
VCALL0(data->engineObject,Destroy)();
|
||||
data->engineObject = NULL;
|
||||
data->engine = NULL;
|
||||
|
||||
free(data);
|
||||
Device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean opensl_reset_playback(ALCdevice *Device)
|
||||
{
|
||||
osl_data *data = Device->ExtraData;
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
|
||||
SLDataLocator_OutputMix loc_outmix;
|
||||
SLDataFormat_PCM format_pcm;
|
||||
SLDataSource audioSrc;
|
||||
SLDataSink audioSnk;
|
||||
SLInterfaceID id;
|
||||
SLboolean req;
|
||||
SLresult result;
|
||||
|
||||
|
||||
Device->UpdateSize = (ALuint64)Device->UpdateSize * 44100 / Device->Frequency;
|
||||
Device->UpdateSize = Device->UpdateSize * Device->NumUpdates / 2;
|
||||
Device->NumUpdates = 2;
|
||||
|
||||
Device->Frequency = 44100;
|
||||
Device->FmtChans = DevFmtStereo;
|
||||
Device->FmtType = DevFmtShort;
|
||||
|
||||
SetDefaultWFXChannelOrder(Device);
|
||||
|
||||
|
||||
id = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
|
||||
req = SL_BOOLEAN_TRUE;
|
||||
|
||||
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
|
||||
loc_bufq.numBuffers = Device->NumUpdates;
|
||||
|
||||
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
||||
format_pcm.numChannels = ChannelsFromDevFmt(Device->FmtChans);
|
||||
format_pcm.samplesPerSec = Device->Frequency * 1000;
|
||||
format_pcm.bitsPerSample = BytesFromDevFmt(Device->FmtType) * 8;
|
||||
format_pcm.containerSize = format_pcm.bitsPerSample;
|
||||
format_pcm.channelMask = GetChannelMask(Device->FmtChans);
|
||||
format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
|
||||
SL_BYTEORDER_BIGENDIAN;
|
||||
|
||||
audioSrc.pLocator = &loc_bufq;
|
||||
audioSrc.pFormat = &format_pcm;
|
||||
|
||||
loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
||||
loc_outmix.outputMix = data->outputMix;
|
||||
audioSnk.pLocator = &loc_outmix;
|
||||
audioSnk.pFormat = NULL;
|
||||
|
||||
|
||||
if(data->bufferQueueObject != NULL)
|
||||
VCALL0(data->bufferQueueObject,Destroy)();
|
||||
data->bufferQueueObject = NULL;
|
||||
|
||||
result = VCALL(data->engine,CreateAudioPlayer)(&data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req);
|
||||
PRINTERR(result, "engine->CreateAudioPlayer");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(data->bufferQueueObject,Realize)(SL_BOOLEAN_FALSE);
|
||||
PRINTERR(result, "bufferQueue->Realize");
|
||||
}
|
||||
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
if(data->bufferQueueObject != NULL)
|
||||
VCALL0(data->bufferQueueObject,Destroy)();
|
||||
data->bufferQueueObject = NULL;
|
||||
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean opensl_start_playback(ALCdevice *Device)
|
||||
{
|
||||
osl_data *data = Device->ExtraData;
|
||||
SLAndroidSimpleBufferQueueItf bufferQueue;
|
||||
SLPlayItf player;
|
||||
SLresult result;
|
||||
ALuint i;
|
||||
|
||||
result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
|
||||
PRINTERR(result, "bufferQueue->GetInterface");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(bufferQueue,RegisterCallback)(opensl_callback, Device);
|
||||
PRINTERR(result, "bufferQueue->RegisterCallback");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
|
||||
data->bufferSize = Device->UpdateSize * data->frameSize;
|
||||
data->buffer = calloc(Device->NumUpdates, data->bufferSize);
|
||||
if(!data->buffer)
|
||||
{
|
||||
result = SL_RESULT_MEMORY_FAILURE;
|
||||
PRINTERR(result, "calloc");
|
||||
}
|
||||
}
|
||||
/* enqueue the first buffer to kick off the callbacks */
|
||||
for(i = 0;i < Device->NumUpdates;i++)
|
||||
{
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
ALvoid *buf = (ALbyte*)data->buffer + i*data->bufferSize;
|
||||
result = VCALL(bufferQueue,Enqueue)(buf, data->bufferSize);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
}
|
||||
}
|
||||
data->curBuffer = 0;
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
|
||||
PRINTERR(result, "bufferQueue->GetInterface");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
|
||||
PRINTERR(result, "player->SetPlayState");
|
||||
}
|
||||
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
if(data->bufferQueueObject != NULL)
|
||||
VCALL0(data->bufferQueueObject,Destroy)();
|
||||
data->bufferQueueObject = NULL;
|
||||
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
data->bufferSize = 0;
|
||||
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void opensl_stop_playback(ALCdevice *Device)
|
||||
{
|
||||
osl_data *data = Device->ExtraData;
|
||||
SLPlayItf player;
|
||||
SLAndroidSimpleBufferQueueItf bufferQueue;
|
||||
SLresult result;
|
||||
|
||||
result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
|
||||
PRINTERR(result, "bufferQueue->GetInterface");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
|
||||
PRINTERR(result, "player->SetPlayState");
|
||||
}
|
||||
|
||||
result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
|
||||
PRINTERR(result, "bufferQueue->GetInterface");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL0(bufferQueue,Clear)();
|
||||
PRINTERR(result, "bufferQueue->Clear");
|
||||
}
|
||||
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
data->bufferSize = 0;
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs opensl_funcs = {
|
||||
opensl_open_playback,
|
||||
opensl_close_playback,
|
||||
opensl_reset_playback,
|
||||
opensl_start_playback,
|
||||
opensl_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
|
||||
ALCboolean alc_opensl_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = opensl_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_opensl_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_opensl_probe(enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(opensl_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,469 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include <portaudio.h>
|
||||
|
||||
|
||||
static const ALCchar pa_device[] = "PortAudio Default";
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
static void *pa_handle;
|
||||
#define MAKE_FUNC(x) static __typeof(x) * p##x
|
||||
MAKE_FUNC(Pa_Initialize);
|
||||
MAKE_FUNC(Pa_Terminate);
|
||||
MAKE_FUNC(Pa_GetErrorText);
|
||||
MAKE_FUNC(Pa_StartStream);
|
||||
MAKE_FUNC(Pa_StopStream);
|
||||
MAKE_FUNC(Pa_OpenStream);
|
||||
MAKE_FUNC(Pa_CloseStream);
|
||||
MAKE_FUNC(Pa_GetDefaultOutputDevice);
|
||||
MAKE_FUNC(Pa_GetDefaultInputDevice);
|
||||
MAKE_FUNC(Pa_GetStreamInfo);
|
||||
#undef MAKE_FUNC
|
||||
|
||||
#define Pa_Initialize pPa_Initialize
|
||||
#define Pa_Terminate pPa_Terminate
|
||||
#define Pa_GetErrorText pPa_GetErrorText
|
||||
#define Pa_StartStream pPa_StartStream
|
||||
#define Pa_StopStream pPa_StopStream
|
||||
#define Pa_OpenStream pPa_OpenStream
|
||||
#define Pa_CloseStream pPa_CloseStream
|
||||
#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
|
||||
#define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
|
||||
#define Pa_GetStreamInfo pPa_GetStreamInfo
|
||||
#endif
|
||||
|
||||
static ALCboolean pa_load(void)
|
||||
{
|
||||
PaError err;
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(!pa_handle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
# define PALIB "portaudio.dll"
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
# define PALIB "libportaudio.2.dylib"
|
||||
#elif defined(__OpenBSD__)
|
||||
# define PALIB "libportaudio.so"
|
||||
#else
|
||||
# define PALIB "libportaudio.so.2"
|
||||
#endif
|
||||
|
||||
pa_handle = LoadLib(PALIB);
|
||||
if(!pa_handle)
|
||||
return ALC_FALSE;
|
||||
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = GetSymbol(pa_handle, #f); \
|
||||
if(p##f == NULL) \
|
||||
{ \
|
||||
CloseLib(pa_handle); \
|
||||
pa_handle = NULL; \
|
||||
return ALC_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
LOAD_FUNC(Pa_Initialize);
|
||||
LOAD_FUNC(Pa_Terminate);
|
||||
LOAD_FUNC(Pa_GetErrorText);
|
||||
LOAD_FUNC(Pa_StartStream);
|
||||
LOAD_FUNC(Pa_StopStream);
|
||||
LOAD_FUNC(Pa_OpenStream);
|
||||
LOAD_FUNC(Pa_CloseStream);
|
||||
LOAD_FUNC(Pa_GetDefaultOutputDevice);
|
||||
LOAD_FUNC(Pa_GetDefaultInputDevice);
|
||||
LOAD_FUNC(Pa_GetStreamInfo);
|
||||
#undef LOAD_FUNC
|
||||
|
||||
if((err=Pa_Initialize()) != paNoError)
|
||||
{
|
||||
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
CloseLib(pa_handle);
|
||||
pa_handle = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if((err=Pa_Initialize()) != paNoError)
|
||||
{
|
||||
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
#endif
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
PaStream *stream;
|
||||
PaStreamParameters params;
|
||||
ALuint update_size;
|
||||
|
||||
RingBuffer *ring;
|
||||
} pa_data;
|
||||
|
||||
|
||||
static int pa_callback(const void *UNUSED(inputBuffer), void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
|
||||
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)userData;
|
||||
|
||||
aluMixData(device, outputBuffer, framesPerBuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pa_capture_cb(const void *inputBuffer, void *UNUSED(outputBuffer),
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
|
||||
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)userData;
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
|
||||
WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
pa_data *data;
|
||||
PaError err;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = pa_device;
|
||||
else if(strcmp(deviceName, pa_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (pa_data*)calloc(1, sizeof(pa_data));
|
||||
data->update_size = device->UpdateSize;
|
||||
|
||||
data->params.device = -1;
|
||||
if(!ConfigValueInt("port", "device", &data->params.device) ||
|
||||
data->params.device < 0)
|
||||
data->params.device = Pa_GetDefaultOutputDevice();
|
||||
data->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
|
||||
(float)device->Frequency;
|
||||
data->params.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
data->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
data->params.sampleFormat = paInt8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
data->params.sampleFormat = paUInt8;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
data->params.sampleFormat = paInt16;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
/* fall-through */
|
||||
case DevFmtInt:
|
||||
data->params.sampleFormat = paInt32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
data->params.sampleFormat = paFloat32;
|
||||
break;
|
||||
}
|
||||
|
||||
retry_open:
|
||||
err = Pa_OpenStream(&data->stream, NULL, &data->params, device->Frequency,
|
||||
device->UpdateSize, paNoFlag, pa_callback, device);
|
||||
if(err != paNoError)
|
||||
{
|
||||
if(data->params.sampleFormat == paFloat32)
|
||||
{
|
||||
data->params.sampleFormat = paInt16;
|
||||
goto retry_open;
|
||||
}
|
||||
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
free(data);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
device->ExtraData = data;
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void pa_close_playback(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_CloseStream(data->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean pa_reset_playback(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
const PaStreamInfo *streamInfo;
|
||||
|
||||
streamInfo = Pa_GetStreamInfo(data->stream);
|
||||
device->Frequency = streamInfo->sampleRate;
|
||||
device->UpdateSize = data->update_size;
|
||||
|
||||
if(data->params.sampleFormat == paInt8)
|
||||
device->FmtType = DevFmtByte;
|
||||
else if(data->params.sampleFormat == paUInt8)
|
||||
device->FmtType = DevFmtUByte;
|
||||
else if(data->params.sampleFormat == paInt16)
|
||||
device->FmtType = DevFmtShort;
|
||||
else if(data->params.sampleFormat == paInt32)
|
||||
device->FmtType = DevFmtInt;
|
||||
else if(data->params.sampleFormat == paFloat32)
|
||||
device->FmtType = DevFmtFloat;
|
||||
else
|
||||
{
|
||||
ERR("Unexpected sample format: 0x%lx\n", data->params.sampleFormat);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(data->params.channelCount == 2)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
else if(data->params.channelCount == 1)
|
||||
device->FmtChans = DevFmtMono;
|
||||
else
|
||||
{
|
||||
ERR("Unexpected channel count: %u\n", data->params.channelCount);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean pa_start_playback(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StartStream(data->stream);
|
||||
if(err != paNoError)
|
||||
{
|
||||
ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void pa_stop_playback(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StopStream(data->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
|
||||
static ALCenum pa_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
ALuint frame_size;
|
||||
pa_data *data;
|
||||
PaError err;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = pa_device;
|
||||
else if(strcmp(deviceName, pa_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (pa_data*)calloc(1, sizeof(pa_data));
|
||||
if(data == NULL)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
|
||||
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);
|
||||
if(data->ring == NULL)
|
||||
goto error;
|
||||
|
||||
data->params.device = -1;
|
||||
if(!ConfigValueInt("port", "capture", &data->params.device) ||
|
||||
data->params.device < 0)
|
||||
data->params.device = Pa_GetDefaultInputDevice();
|
||||
data->params.suggestedLatency = 0.0f;
|
||||
data->params.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
data->params.sampleFormat = paInt8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
data->params.sampleFormat = paUInt8;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
data->params.sampleFormat = paInt16;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
data->params.sampleFormat = paInt32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
data->params.sampleFormat = paFloat32;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
case DevFmtUShort:
|
||||
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
|
||||
goto error;
|
||||
}
|
||||
data->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
|
||||
|
||||
err = Pa_OpenStream(&data->stream, &data->params, NULL, device->Frequency,
|
||||
paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device);
|
||||
if(err != paNoError)
|
||||
{
|
||||
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
goto error;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
|
||||
device->ExtraData = data;
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
error:
|
||||
DestroyRingBuffer(data->ring);
|
||||
free(data);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void pa_close_capture(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_CloseStream(data->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
|
||||
DestroyRingBuffer(data->ring);
|
||||
data->ring = NULL;
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void pa_start_capture(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StartStream(data->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
static void pa_stop_capture(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StopStream(data->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
static ALCenum pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
ReadRingBuffer(data->ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCuint pa_available_samples(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
return RingBufferSize(data->ring);
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs pa_funcs = {
|
||||
pa_open_playback,
|
||||
pa_close_playback,
|
||||
pa_reset_playback,
|
||||
pa_start_playback,
|
||||
pa_stop_playback,
|
||||
pa_open_capture,
|
||||
pa_close_capture,
|
||||
pa_start_capture,
|
||||
pa_stop_capture,
|
||||
pa_capture_samples,
|
||||
pa_available_samples,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
ALCboolean alc_pa_init(BackendFuncs *func_list)
|
||||
{
|
||||
if(!pa_load())
|
||||
return ALC_FALSE;
|
||||
*func_list = pa_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_pa_deinit(void)
|
||||
{
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(pa_handle)
|
||||
{
|
||||
Pa_Terminate();
|
||||
CloseLib(pa_handle);
|
||||
pa_handle = NULL;
|
||||
}
|
||||
#else
|
||||
Pa_Terminate();
|
||||
#endif
|
||||
}
|
||||
|
||||
void alc_pa_probe(enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(pa_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
AppendCaptureDeviceList(pa_device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
|
||||
#include <sndio.h>
|
||||
|
||||
|
||||
static const ALCchar sndio_device[] = "SndIO Default";
|
||||
|
||||
|
||||
static ALCboolean sndio_load(void)
|
||||
{
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct sio_hdl *sndHandle;
|
||||
|
||||
ALvoid *mix_data;
|
||||
ALsizei data_size;
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} sndio_data;
|
||||
|
||||
|
||||
static int sndio_proc(void *ptr)
|
||||
{
|
||||
ALCdevice *device = ptr;
|
||||
sndio_data *data = device->ExtraData;
|
||||
ALsizei frameSize;
|
||||
size_t wrote;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
|
||||
while(!data->killNow && device->Connected)
|
||||
{
|
||||
ALsizei len = data->data_size;
|
||||
ALubyte *WritePtr = data->mix_data;
|
||||
|
||||
aluMixData(device, WritePtr, len/frameSize);
|
||||
while(len > 0 && !data->killNow)
|
||||
{
|
||||
wrote = sio_write(data->sndHandle, WritePtr, len);
|
||||
if(wrote == 0)
|
||||
{
|
||||
ERR("sio_write failed\n");
|
||||
ALCdevice_Lock(device);
|
||||
aluHandleDisconnect(device);
|
||||
ALCdevice_Unlock(device);
|
||||
break;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ALCenum sndio_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
sndio_data *data;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = sndio_device;
|
||||
else if(strcmp(deviceName, sndio_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
data->killNow = 0;
|
||||
|
||||
data->sndHandle = sio_open(NULL, SIO_PLAY, 0);
|
||||
if(data->sndHandle == NULL)
|
||||
{
|
||||
free(data);
|
||||
ERR("Could not open device\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
device->ExtraData = data;
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void sndio_close_playback(ALCdevice *device)
|
||||
{
|
||||
sndio_data *data = device->ExtraData;
|
||||
|
||||
sio_close(data->sndHandle);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean sndio_reset_playback(ALCdevice *device)
|
||||
{
|
||||
sndio_data *data = device->ExtraData;
|
||||
struct sio_par par;
|
||||
|
||||
sio_initpar(&par);
|
||||
|
||||
par.rate = device->Frequency;
|
||||
par.pchan = ((device->FmtChans != DevFmtMono) ? 2 : 1);
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
par.bits = 8;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
par.bits = 8;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
case DevFmtShort:
|
||||
par.bits = 16;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
par.bits = 16;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
par.bits = 32;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
par.bits = 32;
|
||||
par.sig = 0;
|
||||
break;
|
||||
}
|
||||
par.le = SIO_LE_NATIVE;
|
||||
|
||||
par.round = device->UpdateSize;
|
||||
par.appbufsz = device->UpdateSize * (device->NumUpdates-1);
|
||||
if(!par.appbufsz) par.appbufsz = device->UpdateSize;
|
||||
|
||||
if(!sio_setpar(data->sndHandle, &par) || !sio_getpar(data->sndHandle, &par))
|
||||
{
|
||||
ERR("Failed to set device parameters\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(par.bits != par.bps*8)
|
||||
{
|
||||
ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->Frequency = par.rate;
|
||||
device->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
|
||||
|
||||
if(par.bits == 8 && par.sig == 1)
|
||||
device->FmtType = DevFmtByte;
|
||||
else if(par.bits == 8 && par.sig == 0)
|
||||
device->FmtType = DevFmtUByte;
|
||||
else if(par.bits == 16 && par.sig == 1)
|
||||
device->FmtType = DevFmtShort;
|
||||
else if(par.bits == 16 && par.sig == 0)
|
||||
device->FmtType = DevFmtUShort;
|
||||
else if(par.bits == 32 && par.sig == 1)
|
||||
device->FmtType = DevFmtInt;
|
||||
else if(par.bits == 32 && par.sig == 0)
|
||||
device->FmtType = DevFmtUInt;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->UpdateSize = par.round;
|
||||
device->NumUpdates = (par.bufsz/par.round) + 1;
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean sndio_start_playback(ALCdevice *device)
|
||||
{
|
||||
sndio_data *data = device->ExtraData;
|
||||
|
||||
if(!sio_start(data->sndHandle))
|
||||
{
|
||||
ERR("Error starting playback\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->mix_data = calloc(1, data->data_size);
|
||||
|
||||
data->killNow = 0;
|
||||
if(althrd_create(&data->thread, sndio_proc, device) != althrd_success)
|
||||
{
|
||||
sio_stop(data->sndHandle);
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void sndio_stop_playback(ALCdevice *device)
|
||||
{
|
||||
sndio_data *data = device->ExtraData;
|
||||
int res;
|
||||
|
||||
if(data->killNow)
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
althrd_join(data->thread, &res);
|
||||
|
||||
if(!sio_stop(data->sndHandle))
|
||||
ERR("Error stopping device\n");
|
||||
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs sndio_funcs = {
|
||||
sndio_open_playback,
|
||||
sndio_close_playback,
|
||||
sndio_reset_playback,
|
||||
sndio_start_playback,
|
||||
sndio_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
ALCboolean alc_sndio_init(BackendFuncs *func_list)
|
||||
{
|
||||
if(!sndio_load())
|
||||
return ALC_FALSE;
|
||||
*func_list = sndio_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_sndio_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_sndio_probe(enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(sndio_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include <sys/audioio.h>
|
||||
|
||||
|
||||
static const ALCchar solaris_device[] = "Solaris Default";
|
||||
|
||||
static const char *solaris_driver = "/dev/audio";
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
|
||||
ALubyte *mix_data;
|
||||
int data_size;
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} solaris_data;
|
||||
|
||||
|
||||
static int SolarisProc(void *ptr)
|
||||
{
|
||||
ALCdevice *Device = (ALCdevice*)ptr;
|
||||
solaris_data *data = (solaris_data*)Device->ExtraData;
|
||||
ALint frameSize;
|
||||
int wrote;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
|
||||
|
||||
while(!data->killNow && Device->Connected)
|
||||
{
|
||||
ALint len = data->data_size;
|
||||
ALubyte *WritePtr = data->mix_data;
|
||||
|
||||
aluMixData(Device, WritePtr, len/frameSize);
|
||||
while(len > 0 && !data->killNow)
|
||||
{
|
||||
wrote = write(data->fd, WritePtr, len);
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
|
||||
{
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
ALCdevice_Lock(Device);
|
||||
aluHandleDisconnect(Device);
|
||||
ALCdevice_Unlock(Device);
|
||||
break;
|
||||
}
|
||||
|
||||
al_nssleep(0, 1000000);
|
||||
continue;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum solaris_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
solaris_data *data;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = solaris_device;
|
||||
else if(strcmp(deviceName, solaris_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (solaris_data*)calloc(1, sizeof(solaris_data));
|
||||
data->killNow = 0;
|
||||
|
||||
data->fd = open(solaris_driver, O_WRONLY);
|
||||
if(data->fd == -1)
|
||||
{
|
||||
free(data);
|
||||
ERR("Could not open %s: %s\n", solaris_driver, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void solaris_close_playback(ALCdevice *device)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
|
||||
close(data->fd);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean solaris_reset_playback(ALCdevice *device)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
audio_info_t info;
|
||||
ALuint frameSize;
|
||||
int numChannels;
|
||||
|
||||
AUDIO_INITINFO(&info);
|
||||
|
||||
info.play.sample_rate = device->Frequency;
|
||||
|
||||
if(device->FmtChans != DevFmtMono)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
numChannels = ChannelsFromDevFmt(device->FmtChans);
|
||||
info.play.channels = numChannels;
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
info.play.precision = 8;
|
||||
info.play.encoding = AUDIO_ENCODING_LINEAR;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
info.play.precision = 8;
|
||||
info.play.encoding = AUDIO_ENCODING_LINEAR8;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtUInt:
|
||||
case DevFmtFloat:
|
||||
device->FmtType = DevFmtShort;
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
info.play.precision = 16;
|
||||
info.play.encoding = AUDIO_ENCODING_LINEAR;
|
||||
break;
|
||||
}
|
||||
|
||||
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
|
||||
info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize;
|
||||
|
||||
if(ioctl(data->fd, AUDIO_SETINFO, &info) < 0)
|
||||
{
|
||||
ERR("ioctl failed: %s\n", strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(ChannelsFromDevFmt(device->FmtChans) != info.play.channels)
|
||||
{
|
||||
ERR("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device->FmtChans), info.play.channels);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && device->FmtType == DevFmtUByte) ||
|
||||
(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtByte) ||
|
||||
(info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtShort) ||
|
||||
(info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtInt)))
|
||||
{
|
||||
ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(device->FmtType),
|
||||
info.play.precision, info.play.encoding);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->Frequency = info.play.sample_rate;
|
||||
device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1;
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean solaris_start_playback(ALCdevice *device)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
|
||||
data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->mix_data = calloc(1, data->data_size);
|
||||
|
||||
data->killNow = 0;
|
||||
if(althrd_create(&data->thread, SolarisProc, device) != althrd_success)
|
||||
{
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void solaris_stop_playback(ALCdevice *device)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
int res;
|
||||
|
||||
if(data->killNow)
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
althrd_join(data->thread, &res);
|
||||
|
||||
if(ioctl(data->fd, AUDIO_DRAIN) < 0)
|
||||
ERR("Error draining device: %s\n", strerror(errno));
|
||||
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs solaris_funcs = {
|
||||
solaris_open_playback,
|
||||
solaris_close_playback,
|
||||
solaris_reset_playback,
|
||||
solaris_start_playback,
|
||||
solaris_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
ALCboolean alc_solaris_init(BackendFuncs *func_list)
|
||||
{
|
||||
ConfigValueStr("solaris", "device", &solaris_driver);
|
||||
|
||||
*func_list = solaris_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_solaris_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_solaris_probe(enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(solaris_driver, &buf) == 0)
|
||||
#endif
|
||||
AppendAllDevicesList(solaris_device);
|
||||
}
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,377 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
FILE *f;
|
||||
long DataStart;
|
||||
|
||||
ALvoid *buffer;
|
||||
ALuint size;
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} wave_data;
|
||||
|
||||
|
||||
static const ALCchar waveDevice[] = "Wave File Writer";
|
||||
|
||||
static const ALubyte SUBTYPE_PCM[] = {
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
|
||||
0x00, 0x38, 0x9b, 0x71
|
||||
};
|
||||
static const ALubyte SUBTYPE_FLOAT[] = {
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
|
||||
0x00, 0x38, 0x9b, 0x71
|
||||
};
|
||||
|
||||
static const ALuint channel_masks[] = {
|
||||
0, /* invalid */
|
||||
0x4, /* Mono */
|
||||
0x1 | 0x2, /* Stereo */
|
||||
0, /* 3 channel */
|
||||
0x1 | 0x2 | 0x10 | 0x20, /* Quad */
|
||||
0, /* 5 channel */
|
||||
0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20, /* 5.1 */
|
||||
0x1 | 0x2 | 0x4 | 0x8 | 0x100 | 0x200 | 0x400, /* 6.1 */
|
||||
0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x200 | 0x400, /* 7.1 */
|
||||
};
|
||||
|
||||
|
||||
static void fwrite16le(ALushort val, FILE *f)
|
||||
{
|
||||
fputc(val&0xff, f);
|
||||
fputc((val>>8)&0xff, f);
|
||||
}
|
||||
|
||||
static void fwrite32le(ALuint val, FILE *f)
|
||||
{
|
||||
fputc(val&0xff, f);
|
||||
fputc((val>>8)&0xff, f);
|
||||
fputc((val>>16)&0xff, f);
|
||||
fputc((val>>24)&0xff, f);
|
||||
}
|
||||
|
||||
|
||||
static int WaveProc(void *ptr)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)ptr;
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
struct timespec now, start;
|
||||
ALint64 avail, done;
|
||||
ALuint frameSize;
|
||||
size_t fs;
|
||||
const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
|
||||
device->Frequency / 2);
|
||||
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
|
||||
done = 0;
|
||||
if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
|
||||
{
|
||||
ERR("Failed to get starting time\n");
|
||||
return 1;
|
||||
}
|
||||
while(!data->killNow && device->Connected)
|
||||
{
|
||||
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
|
||||
{
|
||||
ERR("Failed to get current time\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
avail = (now.tv_sec - start.tv_sec) * device->Frequency;
|
||||
avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000;
|
||||
if(avail < done)
|
||||
{
|
||||
/* Oops, time skipped backwards. Reset the number of samples done
|
||||
* with one update available since we (likely) just came back from
|
||||
* sleeping. */
|
||||
done = avail - device->UpdateSize;
|
||||
}
|
||||
|
||||
if(avail-done < device->UpdateSize)
|
||||
al_nssleep(0, restTime);
|
||||
else while(avail-done >= device->UpdateSize)
|
||||
{
|
||||
aluMixData(device, data->buffer, device->UpdateSize);
|
||||
done += device->UpdateSize;
|
||||
|
||||
if(!IS_LITTLE_ENDIAN)
|
||||
{
|
||||
ALuint bytesize = BytesFromDevFmt(device->FmtType);
|
||||
ALubyte *bytes = data->buffer;
|
||||
ALuint i;
|
||||
|
||||
if(bytesize == 1)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i], data->f);
|
||||
}
|
||||
else if(bytesize == 2)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i^1], data->f);
|
||||
}
|
||||
else if(bytesize == 4)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i^3], data->f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fs = fwrite(data->buffer, frameSize, device->UpdateSize,
|
||||
data->f);
|
||||
(void)fs;
|
||||
}
|
||||
if(ferror(data->f))
|
||||
{
|
||||
ERR("Error writing to file\n");
|
||||
ALCdevice_Lock(device);
|
||||
aluHandleDisconnect(device);
|
||||
ALCdevice_Unlock(device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALCenum wave_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
wave_data *data;
|
||||
const char *fname;
|
||||
|
||||
fname = GetConfigValue("wave", "file", "");
|
||||
if(!fname[0])
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = waveDevice;
|
||||
else if(strcmp(deviceName, waveDevice) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (wave_data*)calloc(1, sizeof(wave_data));
|
||||
|
||||
data->f = al_fopen(fname, "wb");
|
||||
if(!data->f)
|
||||
{
|
||||
free(data);
|
||||
ERR("Could not open file '%s': %s\n", fname, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void wave_close_playback(ALCdevice *device)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
|
||||
fclose(data->f);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean wave_reset_playback(ALCdevice *device)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
ALuint channels=0, bits=0;
|
||||
size_t val;
|
||||
|
||||
fseek(data->f, 0, SEEK_SET);
|
||||
clearerr(data->f);
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
device->FmtType = DevFmtUByte;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
device->FmtType = DevFmtShort;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
device->FmtType = DevFmtInt;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
case DevFmtShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtFloat:
|
||||
break;
|
||||
}
|
||||
bits = BytesFromDevFmt(device->FmtType) * 8;
|
||||
channels = ChannelsFromDevFmt(device->FmtChans);
|
||||
|
||||
fprintf(data->f, "RIFF");
|
||||
fwrite32le(0xFFFFFFFF, data->f); // 'RIFF' header len; filled in at close
|
||||
|
||||
fprintf(data->f, "WAVE");
|
||||
|
||||
fprintf(data->f, "fmt ");
|
||||
fwrite32le(40, data->f); // 'fmt ' header len; 40 bytes for EXTENSIBLE
|
||||
|
||||
// 16-bit val, format type id (extensible: 0xFFFE)
|
||||
fwrite16le(0xFFFE, data->f);
|
||||
// 16-bit val, channel count
|
||||
fwrite16le(channels, data->f);
|
||||
// 32-bit val, frequency
|
||||
fwrite32le(device->Frequency, data->f);
|
||||
// 32-bit val, bytes per second
|
||||
fwrite32le(device->Frequency * channels * bits / 8, data->f);
|
||||
// 16-bit val, frame size
|
||||
fwrite16le(channels * bits / 8, data->f);
|
||||
// 16-bit val, bits per sample
|
||||
fwrite16le(bits, data->f);
|
||||
// 16-bit val, extra byte count
|
||||
fwrite16le(22, data->f);
|
||||
// 16-bit val, valid bits per sample
|
||||
fwrite16le(bits, data->f);
|
||||
// 32-bit val, channel mask
|
||||
fwrite32le(channel_masks[channels], data->f);
|
||||
// 16 byte GUID, sub-type format
|
||||
val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f);
|
||||
(void)val;
|
||||
|
||||
fprintf(data->f, "data");
|
||||
fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close
|
||||
|
||||
if(ferror(data->f))
|
||||
{
|
||||
ERR("Error writing header: %s\n", strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
data->DataStart = ftell(data->f);
|
||||
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean wave_start_playback(ALCdevice *device)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
|
||||
data->size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->buffer = malloc(data->size);
|
||||
if(!data->buffer)
|
||||
{
|
||||
ERR("Buffer malloc failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->killNow = 0;
|
||||
if(althrd_create(&data->thread, WaveProc, device) != althrd_success)
|
||||
{
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void wave_stop_playback(ALCdevice *device)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
ALuint dataLen;
|
||||
long size;
|
||||
int res;
|
||||
|
||||
if(data->killNow)
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
althrd_join(data->thread, &res);
|
||||
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
|
||||
size = ftell(data->f);
|
||||
if(size > 0)
|
||||
{
|
||||
dataLen = size - data->DataStart;
|
||||
if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0)
|
||||
fwrite32le(dataLen, data->f); // 'data' header len
|
||||
if(fseek(data->f, 4, SEEK_SET) == 0)
|
||||
fwrite32le(size-8, data->f); // 'WAVE' header len
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs wave_funcs = {
|
||||
wave_open_playback,
|
||||
wave_close_playback,
|
||||
wave_reset_playback,
|
||||
wave_start_playback,
|
||||
wave_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
ALCboolean alc_wave_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = wave_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_wave_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_wave_probe(enum DevProbe type)
|
||||
{
|
||||
if(!ConfigValueExists("wave", "file"))
|
||||
return;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(waveDevice);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,716 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
|
||||
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
||||
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
// MMSYSTEM Device
|
||||
volatile ALboolean killNow;
|
||||
althrd_t thread;
|
||||
|
||||
RefCount WaveBuffersCommitted;
|
||||
WAVEHDR WaveBuffer[4];
|
||||
|
||||
union {
|
||||
HWAVEIN In;
|
||||
HWAVEOUT Out;
|
||||
} WaveHandle;
|
||||
|
||||
WAVEFORMATEX Format;
|
||||
|
||||
RingBuffer *Ring;
|
||||
} WinMMData;
|
||||
|
||||
|
||||
TYPEDEF_VECTOR(al_string, vector_al_string)
|
||||
static vector_al_string PlaybackDevices;
|
||||
static vector_al_string CaptureDevices;
|
||||
|
||||
static void clear_devlist(vector_al_string *list)
|
||||
{
|
||||
VECTOR_FOR_EACH(al_string, *list, al_string_deinit);
|
||||
VECTOR_RESIZE(*list, 0);
|
||||
}
|
||||
|
||||
|
||||
static void ProbePlaybackDevices(void)
|
||||
{
|
||||
al_string *iter, *end;
|
||||
ALuint numdevs;
|
||||
ALuint i;
|
||||
|
||||
clear_devlist(&PlaybackDevices);
|
||||
|
||||
numdevs = waveOutGetNumDevs();
|
||||
VECTOR_RESERVE(PlaybackDevices, numdevs);
|
||||
for(i = 0;i < numdevs;i++)
|
||||
{
|
||||
WAVEOUTCAPSW WaveCaps;
|
||||
al_string dname;
|
||||
|
||||
AL_STRING_INIT(dname);
|
||||
if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
||||
{
|
||||
ALuint count = 0;
|
||||
do {
|
||||
al_string_copy_wcstr(&dname, WaveCaps.szPname);
|
||||
if(count != 0)
|
||||
{
|
||||
char str[64];
|
||||
snprintf(str, sizeof(str), " #%d", count+1);
|
||||
al_string_append_cstr(&dname, str);
|
||||
}
|
||||
count++;
|
||||
|
||||
iter = VECTOR_ITER_BEGIN(PlaybackDevices);
|
||||
end = VECTOR_ITER_END(PlaybackDevices);
|
||||
for(;iter != end;iter++)
|
||||
{
|
||||
if(al_string_cmp(*iter, dname) == 0)
|
||||
break;
|
||||
}
|
||||
} while(iter != end);
|
||||
|
||||
TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
|
||||
}
|
||||
VECTOR_PUSH_BACK(PlaybackDevices, dname);
|
||||
}
|
||||
}
|
||||
|
||||
static void ProbeCaptureDevices(void)
|
||||
{
|
||||
al_string *iter, *end;
|
||||
ALuint numdevs;
|
||||
ALuint i;
|
||||
|
||||
clear_devlist(&CaptureDevices);
|
||||
|
||||
numdevs = waveInGetNumDevs();
|
||||
VECTOR_RESERVE(CaptureDevices, numdevs);
|
||||
for(i = 0;i < numdevs;i++)
|
||||
{
|
||||
WAVEINCAPSW WaveCaps;
|
||||
al_string dname;
|
||||
|
||||
AL_STRING_INIT(dname);
|
||||
if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
||||
{
|
||||
ALuint count = 0;
|
||||
do {
|
||||
al_string_copy_wcstr(&dname, WaveCaps.szPname);
|
||||
if(count != 0)
|
||||
{
|
||||
char str[64];
|
||||
snprintf(str, sizeof(str), " #%d", count+1);
|
||||
al_string_append_cstr(&dname, str);
|
||||
}
|
||||
count++;
|
||||
|
||||
iter = VECTOR_ITER_BEGIN(CaptureDevices);
|
||||
end = VECTOR_ITER_END(CaptureDevices);
|
||||
for(;iter != end;iter++)
|
||||
{
|
||||
if(al_string_cmp(*iter, dname) == 0)
|
||||
break;
|
||||
}
|
||||
} while(iter != end);
|
||||
|
||||
TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
|
||||
}
|
||||
VECTOR_PUSH_BACK(CaptureDevices, dname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
WaveOutProc
|
||||
|
||||
Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and
|
||||
returns to the application (for more data)
|
||||
*/
|
||||
static void CALLBACK WaveOutProc(HWAVEOUT UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
|
||||
{
|
||||
ALCdevice *Device = (ALCdevice*)instance;
|
||||
WinMMData *data = Device->ExtraData;
|
||||
|
||||
if(msg != WOM_DONE)
|
||||
return;
|
||||
|
||||
DecrementRef(&data->WaveBuffersCommitted);
|
||||
PostThreadMessage(data->thread, msg, 0, param1);
|
||||
}
|
||||
|
||||
FORCE_ALIGN static int PlaybackThreadProc(void *arg)
|
||||
{
|
||||
ALCdevice *Device = (ALCdevice*)arg;
|
||||
WinMMData *data = Device->ExtraData;
|
||||
WAVEHDR *WaveHdr;
|
||||
MSG msg;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
while(GetMessage(&msg, NULL, 0, 0))
|
||||
{
|
||||
if(msg.message != WOM_DONE)
|
||||
continue;
|
||||
|
||||
if(data->killNow)
|
||||
{
|
||||
if(ReadRef(&data->WaveBuffersCommitted) == 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
WaveHdr = ((WAVEHDR*)msg.lParam);
|
||||
aluMixData(Device, WaveHdr->lpData, WaveHdr->dwBufferLength /
|
||||
data->Format.nBlockAlign);
|
||||
|
||||
// Send buffer back to play more data
|
||||
waveOutWrite(data->WaveHandle.Out, WaveHdr, sizeof(WAVEHDR));
|
||||
IncrementRef(&data->WaveBuffersCommitted);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
WaveInProc
|
||||
|
||||
Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
|
||||
returns to the application (with more data)
|
||||
*/
|
||||
static void CALLBACK WaveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
|
||||
{
|
||||
ALCdevice *Device = (ALCdevice*)instance;
|
||||
WinMMData *data = Device->ExtraData;
|
||||
|
||||
if(msg != WIM_DATA)
|
||||
return;
|
||||
|
||||
DecrementRef(&data->WaveBuffersCommitted);
|
||||
PostThreadMessage(data->thread, msg, 0, param1);
|
||||
}
|
||||
|
||||
static int CaptureThreadProc(void *arg)
|
||||
{
|
||||
ALCdevice *Device = (ALCdevice*)arg;
|
||||
WinMMData *data = Device->ExtraData;
|
||||
WAVEHDR *WaveHdr;
|
||||
MSG msg;
|
||||
|
||||
althrd_setname(althrd_current(), "alsoft-record");
|
||||
|
||||
while(GetMessage(&msg, NULL, 0, 0))
|
||||
{
|
||||
if(msg.message != WIM_DATA)
|
||||
continue;
|
||||
/* Don't wait for other buffers to finish before quitting. We're
|
||||
* closing so we don't need them. */
|
||||
if(data->killNow)
|
||||
break;
|
||||
|
||||
WaveHdr = ((WAVEHDR*)msg.lParam);
|
||||
WriteRingBuffer(data->Ring, (ALubyte*)WaveHdr->lpData,
|
||||
WaveHdr->dwBytesRecorded/data->Format.nBlockAlign);
|
||||
|
||||
// Send buffer back to capture more data
|
||||
waveInAddBuffer(data->WaveHandle.In, WaveHdr, sizeof(WAVEHDR));
|
||||
IncrementRef(&data->WaveBuffersCommitted);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum WinMMOpenPlayback(ALCdevice *Device, const ALCchar *deviceName)
|
||||
{
|
||||
WinMMData *data = NULL;
|
||||
const al_string *iter, *end;
|
||||
UINT DeviceID;
|
||||
MMRESULT res;
|
||||
|
||||
if(VECTOR_SIZE(PlaybackDevices) == 0)
|
||||
ProbePlaybackDevices();
|
||||
|
||||
// Find the Device ID matching the deviceName if valid
|
||||
iter = VECTOR_ITER_BEGIN(PlaybackDevices);
|
||||
end = VECTOR_ITER_END(PlaybackDevices);
|
||||
for(;iter != end;iter++)
|
||||
{
|
||||
if(!al_string_empty(*iter) &&
|
||||
(!deviceName || al_string_cmp_cstr(*iter, deviceName) == 0))
|
||||
{
|
||||
DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(PlaybackDevices));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(iter == end)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
if(!data)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
Device->ExtraData = data;
|
||||
|
||||
retry_open:
|
||||
memset(&data->Format, 0, sizeof(WAVEFORMATEX));
|
||||
if(Device->FmtType == DevFmtFloat)
|
||||
{
|
||||
data->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
data->Format.wBitsPerSample = 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
data->Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
if(Device->FmtType == DevFmtUByte || Device->FmtType == DevFmtByte)
|
||||
data->Format.wBitsPerSample = 8;
|
||||
else
|
||||
data->Format.wBitsPerSample = 16;
|
||||
}
|
||||
data->Format.nChannels = ((Device->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
data->Format.nBlockAlign = data->Format.wBitsPerSample *
|
||||
data->Format.nChannels / 8;
|
||||
data->Format.nSamplesPerSec = Device->Frequency;
|
||||
data->Format.nAvgBytesPerSec = data->Format.nSamplesPerSec *
|
||||
data->Format.nBlockAlign;
|
||||
data->Format.cbSize = 0;
|
||||
|
||||
if((res=waveOutOpen(&data->WaveHandle.Out, DeviceID, &data->Format, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
|
||||
{
|
||||
if(Device->FmtType == DevFmtFloat)
|
||||
{
|
||||
Device->FmtType = DevFmtShort;
|
||||
goto retry_open;
|
||||
}
|
||||
ERR("waveOutOpen failed: %u\n", res);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
al_string_copy(&Device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
failure:
|
||||
if(data->WaveHandle.Out)
|
||||
waveOutClose(data->WaveHandle.Out);
|
||||
|
||||
free(data);
|
||||
Device->ExtraData = NULL;
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void WinMMClosePlayback(ALCdevice *device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)device->ExtraData;
|
||||
|
||||
// Close the Wave device
|
||||
waveOutClose(data->WaveHandle.Out);
|
||||
data->WaveHandle.Out = 0;
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean WinMMResetPlayback(ALCdevice *device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)device->ExtraData;
|
||||
|
||||
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
|
||||
data->Format.nSamplesPerSec /
|
||||
device->Frequency);
|
||||
device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4;
|
||||
device->NumUpdates = 4;
|
||||
device->Frequency = data->Format.nSamplesPerSec;
|
||||
|
||||
if(data->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
|
||||
{
|
||||
if(data->Format.wBitsPerSample == 32)
|
||||
device->FmtType = DevFmtFloat;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled IEEE float sample depth: %d\n", data->Format.wBitsPerSample);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
}
|
||||
else if(data->Format.wFormatTag == WAVE_FORMAT_PCM)
|
||||
{
|
||||
if(data->Format.wBitsPerSample == 16)
|
||||
device->FmtType = DevFmtShort;
|
||||
else if(data->Format.wBitsPerSample == 8)
|
||||
device->FmtType = DevFmtUByte;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled PCM sample depth: %d\n", data->Format.wBitsPerSample);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unhandled format tag: 0x%04x\n", data->Format.wFormatTag);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(data->Format.nChannels == 2)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
else if(data->Format.nChannels == 1)
|
||||
device->FmtChans = DevFmtMono;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled channel count: %d\n", data->Format.nChannels);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean WinMMStartPlayback(ALCdevice *device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)device->ExtraData;
|
||||
ALbyte *BufferData;
|
||||
ALint BufferSize;
|
||||
ALuint i;
|
||||
|
||||
data->killNow = AL_FALSE;
|
||||
if(althrd_create(&data->thread, PlaybackThreadProc, device) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
|
||||
InitRef(&data->WaveBuffersCommitted, 0);
|
||||
|
||||
// Create 4 Buffers
|
||||
BufferSize = device->UpdateSize*device->NumUpdates / 4;
|
||||
BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
|
||||
BufferData = calloc(4, BufferSize);
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR));
|
||||
data->WaveBuffer[i].dwBufferLength = BufferSize;
|
||||
data->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
|
||||
(data->WaveBuffer[i-1].lpData +
|
||||
data->WaveBuffer[i-1].dwBufferLength));
|
||||
waveOutPrepareHeader(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
waveOutWrite(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
IncrementRef(&data->WaveBuffersCommitted);
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void WinMMStopPlayback(ALCdevice *device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)device->ExtraData;
|
||||
void *buffer = NULL;
|
||||
int i;
|
||||
|
||||
if(data->killNow)
|
||||
return;
|
||||
|
||||
// Set flag to stop processing headers
|
||||
data->killNow = AL_TRUE;
|
||||
althrd_join(data->thread, &i);
|
||||
|
||||
// Release the wave buffers
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
waveOutUnprepareHeader(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
if(i == 0) buffer = data->WaveBuffer[i].lpData;
|
||||
data->WaveBuffer[i].lpData = NULL;
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
|
||||
static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName)
|
||||
{
|
||||
const al_string *iter, *end;
|
||||
ALbyte *BufferData = NULL;
|
||||
DWORD CapturedDataSize;
|
||||
WinMMData *data = NULL;
|
||||
ALint BufferSize;
|
||||
UINT DeviceID;
|
||||
MMRESULT res;
|
||||
ALuint i;
|
||||
|
||||
if(VECTOR_SIZE(CaptureDevices) == 0)
|
||||
ProbeCaptureDevices();
|
||||
|
||||
// Find the Device ID matching the deviceName if valid
|
||||
iter = VECTOR_ITER_BEGIN(CaptureDevices);
|
||||
end = VECTOR_ITER_END(CaptureDevices);
|
||||
for(;iter != end;iter++)
|
||||
{
|
||||
if(!al_string_empty(*iter) &&
|
||||
(!deviceName || al_string_cmp_cstr(*iter, deviceName) == 0))
|
||||
{
|
||||
DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(CaptureDevices));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(iter == end)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
switch(Device->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
case DevFmtStereo:
|
||||
break;
|
||||
|
||||
case DevFmtQuad:
|
||||
case DevFmtX51:
|
||||
case DevFmtX51Side:
|
||||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
return ALC_INVALID_ENUM;
|
||||
}
|
||||
|
||||
switch(Device->FmtType)
|
||||
{
|
||||
case DevFmtUByte:
|
||||
case DevFmtShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtFloat:
|
||||
break;
|
||||
|
||||
case DevFmtByte:
|
||||
case DevFmtUShort:
|
||||
case DevFmtUInt:
|
||||
return ALC_INVALID_ENUM;
|
||||
}
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
if(!data)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
Device->ExtraData = data;
|
||||
|
||||
memset(&data->Format, 0, sizeof(WAVEFORMATEX));
|
||||
data->Format.wFormatTag = ((Device->FmtType == DevFmtFloat) ?
|
||||
WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
|
||||
data->Format.nChannels = ChannelsFromDevFmt(Device->FmtChans);
|
||||
data->Format.wBitsPerSample = BytesFromDevFmt(Device->FmtType) * 8;
|
||||
data->Format.nBlockAlign = data->Format.wBitsPerSample *
|
||||
data->Format.nChannels / 8;
|
||||
data->Format.nSamplesPerSec = Device->Frequency;
|
||||
data->Format.nAvgBytesPerSec = data->Format.nSamplesPerSec *
|
||||
data->Format.nBlockAlign;
|
||||
data->Format.cbSize = 0;
|
||||
|
||||
if((res=waveInOpen(&data->WaveHandle.In, DeviceID, &data->Format, (DWORD_PTR)&WaveInProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
|
||||
{
|
||||
ERR("waveInOpen failed: %u\n", res);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
// Allocate circular memory buffer for the captured audio
|
||||
CapturedDataSize = Device->UpdateSize*Device->NumUpdates;
|
||||
|
||||
// Make sure circular buffer is at least 100ms in size
|
||||
if(CapturedDataSize < (data->Format.nSamplesPerSec / 10))
|
||||
CapturedDataSize = data->Format.nSamplesPerSec / 10;
|
||||
|
||||
data->Ring = CreateRingBuffer(data->Format.nBlockAlign, CapturedDataSize);
|
||||
if(!data->Ring)
|
||||
goto failure;
|
||||
|
||||
InitRef(&data->WaveBuffersCommitted, 0);
|
||||
|
||||
// Create 4 Buffers of 50ms each
|
||||
BufferSize = data->Format.nAvgBytesPerSec / 20;
|
||||
BufferSize -= (BufferSize % data->Format.nBlockAlign);
|
||||
|
||||
BufferData = calloc(4, BufferSize);
|
||||
if(!BufferData)
|
||||
goto failure;
|
||||
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR));
|
||||
data->WaveBuffer[i].dwBufferLength = BufferSize;
|
||||
data->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
|
||||
(data->WaveBuffer[i-1].lpData +
|
||||
data->WaveBuffer[i-1].dwBufferLength));
|
||||
data->WaveBuffer[i].dwFlags = 0;
|
||||
data->WaveBuffer[i].dwLoops = 0;
|
||||
waveInPrepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
waveInAddBuffer(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
IncrementRef(&data->WaveBuffersCommitted);
|
||||
}
|
||||
|
||||
if(althrd_create(&data->thread, CaptureThreadProc, Device) != althrd_success)
|
||||
goto failure;
|
||||
|
||||
al_string_copy(&Device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
failure:
|
||||
if(BufferData)
|
||||
{
|
||||
for(i = 0;i < 4;i++)
|
||||
waveInUnprepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
free(BufferData);
|
||||
}
|
||||
|
||||
if(data->Ring)
|
||||
DestroyRingBuffer(data->Ring);
|
||||
|
||||
if(data->WaveHandle.In)
|
||||
waveInClose(data->WaveHandle.In);
|
||||
|
||||
free(data);
|
||||
Device->ExtraData = NULL;
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void WinMMCloseCapture(ALCdevice *Device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)Device->ExtraData;
|
||||
void *buffer = NULL;
|
||||
int i;
|
||||
|
||||
/* Tell the processing thread to quit and wait for it to do so. */
|
||||
data->killNow = AL_TRUE;
|
||||
PostThreadMessage(data->thread, WM_QUIT, 0, 0);
|
||||
|
||||
althrd_join(data->thread, &i);
|
||||
|
||||
/* Make sure capture is stopped and all pending buffers are flushed. */
|
||||
waveInReset(data->WaveHandle.In);
|
||||
|
||||
// Release the wave buffers
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
waveInUnprepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
if(i == 0) buffer = data->WaveBuffer[i].lpData;
|
||||
data->WaveBuffer[i].lpData = NULL;
|
||||
}
|
||||
free(buffer);
|
||||
|
||||
DestroyRingBuffer(data->Ring);
|
||||
data->Ring = NULL;
|
||||
|
||||
// Close the Wave device
|
||||
waveInClose(data->WaveHandle.In);
|
||||
data->WaveHandle.In = 0;
|
||||
|
||||
free(data);
|
||||
Device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void WinMMStartCapture(ALCdevice *Device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)Device->ExtraData;
|
||||
waveInStart(data->WaveHandle.In);
|
||||
}
|
||||
|
||||
static void WinMMStopCapture(ALCdevice *Device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)Device->ExtraData;
|
||||
waveInStop(data->WaveHandle.In);
|
||||
}
|
||||
|
||||
static ALCenum WinMMCaptureSamples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)Device->ExtraData;
|
||||
ReadRingBuffer(data->Ring, Buffer, Samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCuint WinMMAvailableSamples(ALCdevice *Device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)Device->ExtraData;
|
||||
return RingBufferSize(data->Ring);
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const al_string *name)
|
||||
{
|
||||
if(!al_string_empty(*name))
|
||||
AppendAllDevicesList(al_string_get_cstr(*name));
|
||||
}
|
||||
static inline void AppendCaptureDeviceList2(const al_string *name)
|
||||
{
|
||||
if(!al_string_empty(*name))
|
||||
AppendCaptureDeviceList(al_string_get_cstr(*name));
|
||||
}
|
||||
|
||||
static const BackendFuncs WinMMFuncs = {
|
||||
WinMMOpenPlayback,
|
||||
WinMMClosePlayback,
|
||||
WinMMResetPlayback,
|
||||
WinMMStartPlayback,
|
||||
WinMMStopPlayback,
|
||||
WinMMOpenCapture,
|
||||
WinMMCloseCapture,
|
||||
WinMMStartCapture,
|
||||
WinMMStopCapture,
|
||||
WinMMCaptureSamples,
|
||||
WinMMAvailableSamples,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
ALCboolean alcWinMMInit(BackendFuncs *FuncList)
|
||||
{
|
||||
VECTOR_INIT(PlaybackDevices);
|
||||
VECTOR_INIT(CaptureDevices);
|
||||
|
||||
*FuncList = WinMMFuncs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alcWinMMDeinit()
|
||||
{
|
||||
clear_devlist(&PlaybackDevices);
|
||||
VECTOR_DEINIT(PlaybackDevices);
|
||||
|
||||
clear_devlist(&CaptureDevices);
|
||||
VECTOR_DEINIT(CaptureDevices);
|
||||
}
|
||||
|
||||
void alcWinMMProbe(enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
ProbePlaybackDevices();
|
||||
VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
ProbeCaptureDevices();
|
||||
VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,272 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2013 by Anis A. Hireche, Nasca Octavian Paul
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "alu.h"
|
||||
#include "alFilter.h"
|
||||
#include "alError.h"
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
|
||||
|
||||
/* Auto-wah is simply a low-pass filter with a cutoff frequency that shifts up
|
||||
* or down depending on the input signal, and a resonant peak at the cutoff.
|
||||
*
|
||||
* Currently, we assume a cutoff frequency range of 500hz (no amplitude) to
|
||||
* 3khz (peak gain). Peak gain is assumed to be in normalized scale.
|
||||
*/
|
||||
|
||||
typedef struct ALautowahState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MaxChannels];
|
||||
|
||||
/* Effect parameters */
|
||||
ALfloat AttackRate;
|
||||
ALfloat ReleaseRate;
|
||||
ALfloat Resonance;
|
||||
ALfloat PeakGain;
|
||||
ALfloat GainCtrl;
|
||||
ALfloat Frequency;
|
||||
|
||||
/* Samples processing */
|
||||
ALfilterState LowPass;
|
||||
} ALautowahState;
|
||||
|
||||
static ALvoid ALautowahState_Destruct(ALautowahState *UNUSED(state))
|
||||
{
|
||||
}
|
||||
|
||||
static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device)
|
||||
{
|
||||
state->Frequency = (ALfloat)device->Frequency;
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, const ALeffectslot *slot)
|
||||
{
|
||||
ALfloat attackTime, releaseTime;
|
||||
ALfloat gain;
|
||||
|
||||
attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency;
|
||||
releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency;
|
||||
|
||||
state->AttackRate = powf(1.0f/GAIN_SILENCE_THRESHOLD, 1.0f/attackTime);
|
||||
state->ReleaseRate = powf(GAIN_SILENCE_THRESHOLD/1.0f, 1.0f/releaseTime);
|
||||
state->PeakGain = slot->EffectProps.Autowah.PeakGain;
|
||||
state->Resonance = slot->EffectProps.Autowah.Resonance;
|
||||
|
||||
gain = sqrtf(1.0f / device->NumChan) * slot->Gain;
|
||||
SetGains(device, gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE])
|
||||
{
|
||||
ALuint it, kt;
|
||||
ALuint base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
ALfloat gain = state->GainCtrl;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
{
|
||||
ALfloat smp = SamplesIn[it+base];
|
||||
ALfloat alpha, w0;
|
||||
ALfloat amplitude;
|
||||
ALfloat cutoff;
|
||||
|
||||
/* Similar to compressor, we get the current amplitude of the
|
||||
* incoming signal, and attack or release to reach it. */
|
||||
amplitude = fabsf(smp);
|
||||
if(amplitude > gain)
|
||||
gain = minf(gain*state->AttackRate, amplitude);
|
||||
else if(amplitude < gain)
|
||||
gain = maxf(gain*state->ReleaseRate, amplitude);
|
||||
gain = maxf(gain, GAIN_SILENCE_THRESHOLD);
|
||||
|
||||
/* FIXME: What range does the filter cover? */
|
||||
cutoff = lerp(20.0f, 20000.0f, minf(gain/state->PeakGain, 1.0f));
|
||||
|
||||
/* The code below is like calling ALfilterState_setParams with
|
||||
* ALfilterType_LowPass. However, instead of passing a bandwidth,
|
||||
* we use the resonance property for Q. This also inlines the call.
|
||||
*/
|
||||
w0 = F_2PI * cutoff / state->Frequency;
|
||||
|
||||
/* FIXME: Resonance controls the resonant peak, or Q. How? Not sure
|
||||
* that Q = resonance*0.1. */
|
||||
alpha = sinf(w0) / (2.0f * state->Resonance*0.1f);
|
||||
state->LowPass.b[0] = (1.0f - cosf(w0)) / 2.0f;
|
||||
state->LowPass.b[1] = 1.0f - cosf(w0);
|
||||
state->LowPass.b[2] = (1.0f - cosf(w0)) / 2.0f;
|
||||
state->LowPass.a[0] = 1.0f + alpha;
|
||||
state->LowPass.a[1] = -2.0f * cosf(w0);
|
||||
state->LowPass.a[2] = 1.0f - alpha;
|
||||
|
||||
state->LowPass.b[2] /= state->LowPass.a[0];
|
||||
state->LowPass.b[1] /= state->LowPass.a[0];
|
||||
state->LowPass.b[0] /= state->LowPass.a[0];
|
||||
state->LowPass.a[2] /= state->LowPass.a[0];
|
||||
state->LowPass.a[1] /= state->LowPass.a[0];
|
||||
state->LowPass.a[0] /= state->LowPass.a[0];
|
||||
|
||||
temps[it] = ALfilterState_processSingle(&state->LowPass, smp);
|
||||
}
|
||||
state->GainCtrl = gain;
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[kt];
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * temps[it];
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
|
||||
|
||||
|
||||
typedef struct ALautowahStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALautowahStateFactory;
|
||||
|
||||
static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALautowahState *state;
|
||||
|
||||
state = ALautowahState_New(sizeof(*state));
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALautowahState, ALeffectState, state);
|
||||
|
||||
state->AttackRate = 1.0f;
|
||||
state->ReleaseRate = 1.0f;
|
||||
state->Resonance = 2.0f;
|
||||
state->PeakGain = 1.0f;
|
||||
state->GainCtrl = 1.0f;
|
||||
|
||||
ALfilterState_clear(&state->LowPass);
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALautowahStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALautowahStateFactory_getFactory(void)
|
||||
{
|
||||
static ALautowahStateFactory AutowahFactory = { { GET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &AutowahFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALautowah_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALautowah_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Autowah.AttackTime = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RELEASE_TIME:
|
||||
if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Autowah.ReleaseTime = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RESONANCE:
|
||||
if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Autowah.Resonance = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_PEAK_GAIN:
|
||||
if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Autowah.PeakGain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALautowah_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALautowah_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALautowah_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
*val = props->Autowah.AttackTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RELEASE_TIME:
|
||||
*val = props->Autowah.ReleaseTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RESONANCE:
|
||||
*val = props->Autowah.Resonance;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_PEAK_GAIN:
|
||||
*val = props->Autowah.PeakGain;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALautowah_getParamf(effect, context, param, vals);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALautowah);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,31 +0,0 @@
|
||||
#ifndef AL_EVTQUEUE_H
|
||||
#define AL_EVTQUEUE_H
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
typedef struct MidiEvent {
|
||||
ALuint64 time;
|
||||
ALuint event;
|
||||
union {
|
||||
ALuint val[2];
|
||||
struct {
|
||||
ALvoid *data;
|
||||
ALsizei size;
|
||||
} sysex;
|
||||
} param;
|
||||
} MidiEvent;
|
||||
|
||||
typedef struct EvtQueue {
|
||||
MidiEvent *events;
|
||||
ALsizei pos;
|
||||
ALsizei size;
|
||||
ALsizei maxsize;
|
||||
} EvtQueue;
|
||||
|
||||
void InitEvtQueue(EvtQueue *queue);
|
||||
void ResetEvtQueue(EvtQueue *queue);
|
||||
ALenum InsertEvtQueue(EvtQueue *queue, const MidiEvent *evt);
|
||||
|
||||
#endif /* AL_EVTQUEUE_H */
|
||||
@@ -1,814 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2011 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef __MINGW32__
|
||||
#define _WIN32_IE 0x501
|
||||
#else
|
||||
#define _WIN32_IE 0x400
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#ifdef HAVE_MALLOC_H
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#ifndef AL_NO_UID_DEFS
|
||||
#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
|
||||
#define INITGUID
|
||||
#include <windows.h>
|
||||
#ifdef HAVE_GUIDDEF_H
|
||||
#include <guiddef.h>
|
||||
#else
|
||||
#include <initguid.h>
|
||||
#endif
|
||||
|
||||
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
|
||||
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
|
||||
|
||||
DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16);
|
||||
|
||||
DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e);
|
||||
DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
|
||||
DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
|
||||
DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
|
||||
|
||||
#ifdef HAVE_MMDEVAPI
|
||||
#include <devpropdef.h>
|
||||
DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
|
||||
#endif
|
||||
#endif
|
||||
#endif /* AL_NO_UID_DEFS */
|
||||
|
||||
#ifdef HAVE_DLFCN_H
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#ifdef HAVE_INTRIN_H
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
#ifdef HAVE_CPUID_H
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_SYSCONF_H
|
||||
#include <sys/sysconf.h>
|
||||
#endif
|
||||
#ifdef HAVE_FLOAT_H
|
||||
#include <float.h>
|
||||
#endif
|
||||
#ifdef HAVE_IEEEFP_H
|
||||
#include <ieeefp.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32_IE
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "atomic.h"
|
||||
#include "uintmap.h"
|
||||
#include "vector.h"
|
||||
#include "alstring.h"
|
||||
#include "compat.h"
|
||||
#include "threads.h"
|
||||
|
||||
|
||||
extern inline ALuint NextPowerOf2(ALuint value);
|
||||
extern inline ALint fastf2i(ALfloat f);
|
||||
extern inline ALuint fastf2u(ALfloat f);
|
||||
|
||||
|
||||
ALuint CPUCapFlags = 0;
|
||||
|
||||
|
||||
void FillCPUCaps(ALuint capfilter)
|
||||
{
|
||||
ALuint caps = 0;
|
||||
|
||||
/* FIXME: We really should get this for all available CPUs in case different
|
||||
* CPUs have different caps (is that possible on one machine?). */
|
||||
#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
|
||||
defined(_M_IX86) || defined(_M_X64))
|
||||
union {
|
||||
unsigned int regs[4];
|
||||
char str[sizeof(unsigned int[4])];
|
||||
} cpuinf[3];
|
||||
|
||||
if(!__get_cpuid(0, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
|
||||
ERR("Failed to get CPUID\n");
|
||||
else
|
||||
{
|
||||
unsigned int maxfunc = cpuinf[0].regs[0];
|
||||
unsigned int maxextfunc = 0;
|
||||
|
||||
if(__get_cpuid(0x80000000, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
|
||||
maxextfunc = cpuinf[0].regs[0];
|
||||
TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
|
||||
|
||||
TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
|
||||
if(maxextfunc >= 0x80000004 &&
|
||||
__get_cpuid(0x80000002, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]) &&
|
||||
__get_cpuid(0x80000003, &cpuinf[1].regs[0], &cpuinf[1].regs[1], &cpuinf[1].regs[2], &cpuinf[1].regs[3]) &&
|
||||
__get_cpuid(0x80000004, &cpuinf[2].regs[0], &cpuinf[2].regs[1], &cpuinf[2].regs[2], &cpuinf[2].regs[3]))
|
||||
TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
|
||||
|
||||
if(maxfunc >= 1 &&
|
||||
__get_cpuid(1, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
|
||||
{
|
||||
if((cpuinf[0].regs[3]&(1<<25)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE;
|
||||
if((cpuinf[0].regs[3]&(1<<26)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE2;
|
||||
if((cpuinf[0].regs[2]&(1<<19)))
|
||||
caps |= CPU_CAP_SSE4_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
|
||||
defined(_M_IX86) || defined(_M_X64))
|
||||
union {
|
||||
int regs[4];
|
||||
char str[sizeof(int[4])];
|
||||
} cpuinf[3];
|
||||
|
||||
(__cpuid)(cpuinf[0].regs, 0);
|
||||
if(cpuinf[0].regs[0] == 0)
|
||||
ERR("Failed to get CPUID\n");
|
||||
else
|
||||
{
|
||||
unsigned int maxfunc = cpuinf[0].regs[0];
|
||||
unsigned int maxextfunc;
|
||||
|
||||
(__cpuid)(cpuinf[0].regs, 0x80000000);
|
||||
maxextfunc = cpuinf[0].regs[0];
|
||||
|
||||
TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
|
||||
|
||||
TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
|
||||
if(maxextfunc >= 0x80000004)
|
||||
{
|
||||
(__cpuid)(cpuinf[0].regs, 0x80000002);
|
||||
(__cpuid)(cpuinf[1].regs, 0x80000003);
|
||||
(__cpuid)(cpuinf[2].regs, 0x80000004);
|
||||
TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
|
||||
}
|
||||
|
||||
if(maxfunc >= 1)
|
||||
{
|
||||
(__cpuid)(cpuinf[0].regs, 1);
|
||||
if((cpuinf[0].regs[3]&(1<<25)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE;
|
||||
if((cpuinf[0].regs[3]&(1<<26)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE2;
|
||||
if((cpuinf[0].regs[2]&(1<<19)))
|
||||
caps |= CPU_CAP_SSE4_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Assume support for whatever's supported if we can't check for it */
|
||||
#if defined(HAVE_SSE4_1)
|
||||
#warning "Assuming SSE 4.1 run-time support!"
|
||||
capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE4_1;
|
||||
#elif defined(HAVE_SSE2)
|
||||
#warning "Assuming SSE 2 run-time support!"
|
||||
capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2;
|
||||
#elif defined(HAVE_SSE)
|
||||
#warning "Assuming SSE run-time support!"
|
||||
capfilter |= CPU_CAP_SSE;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef HAVE_NEON
|
||||
/* Assume Neon support if compiled with it */
|
||||
caps |= CPU_CAP_NEON;
|
||||
#endif
|
||||
|
||||
TRACE("Extensions:%s%s%s%s%s\n",
|
||||
((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""),
|
||||
((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""),
|
||||
((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""),
|
||||
((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +Neon" : " -Neon") : ""),
|
||||
((!capfilter) ? " -none-" : "")
|
||||
);
|
||||
CPUCapFlags = caps & capfilter;
|
||||
}
|
||||
|
||||
|
||||
void *al_malloc(size_t alignment, size_t size)
|
||||
{
|
||||
#if defined(HAVE_ALIGNED_ALLOC)
|
||||
size = (size+(alignment-1))&~(alignment-1);
|
||||
return aligned_alloc(alignment, size);
|
||||
#elif defined(HAVE_POSIX_MEMALIGN)
|
||||
void *ret;
|
||||
if(posix_memalign(&ret, alignment, size) == 0)
|
||||
return ret;
|
||||
return NULL;
|
||||
#elif defined(HAVE__ALIGNED_MALLOC)
|
||||
return _aligned_malloc(size, alignment);
|
||||
#else
|
||||
char *ret = malloc(size+alignment);
|
||||
if(ret != NULL)
|
||||
{
|
||||
*(ret++) = 0x00;
|
||||
while(((ALintptrEXT)ret&(alignment-1)) != 0)
|
||||
*(ret++) = 0x55;
|
||||
}
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *al_calloc(size_t alignment, size_t size)
|
||||
{
|
||||
void *ret = al_malloc(alignment, size);
|
||||
if(ret) memset(ret, 0, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void al_free(void *ptr)
|
||||
{
|
||||
#if defined(HAVE_ALIGNED_ALLOC) || defined(HAVE_POSIX_MEMALIGN)
|
||||
free(ptr);
|
||||
#elif defined(HAVE__ALIGNED_MALLOC)
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
if(ptr != NULL)
|
||||
{
|
||||
char *finder = ptr;
|
||||
do {
|
||||
--finder;
|
||||
} while(*finder == 0x55);
|
||||
free(finder);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void SetMixerFPUMode(FPUCtl *ctl)
|
||||
{
|
||||
#ifdef HAVE_FENV_H
|
||||
fegetenv(STATIC_CAST(fenv_t, ctl));
|
||||
#if defined(__GNUC__) && defined(HAVE_SSE)
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
__asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state));
|
||||
#endif
|
||||
|
||||
#ifdef FE_TOWARDZERO
|
||||
fesetround(FE_TOWARDZERO);
|
||||
#endif
|
||||
#if defined(__GNUC__) && defined(HAVE_SSE)
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
{
|
||||
int sseState = ctl->sse_state;
|
||||
sseState |= 0x6000; /* set round-to-zero */
|
||||
sseState |= 0x8000; /* set flush-to-zero */
|
||||
if((CPUCapFlags&CPU_CAP_SSE2))
|
||||
sseState |= 0x0040; /* set denormals-are-zero */
|
||||
__asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState));
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE___CONTROL87_2)
|
||||
|
||||
int mode;
|
||||
__control87_2(0, 0, &ctl->state, NULL);
|
||||
__control87_2(_RC_CHOP, _MCW_RC, &mode, NULL);
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
{
|
||||
__control87_2(0, 0, NULL, &ctl->sse_state);
|
||||
__control87_2(_RC_CHOP|_DN_FLUSH, _MCW_RC|_MCW_DN, NULL, &mode);
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE__CONTROLFP)
|
||||
|
||||
ctl->state = _controlfp(0, 0);
|
||||
(void)_controlfp(_RC_CHOP, _MCW_RC);
|
||||
#endif
|
||||
}
|
||||
|
||||
void RestoreFPUMode(const FPUCtl *ctl)
|
||||
{
|
||||
#ifdef HAVE_FENV_H
|
||||
fesetenv(STATIC_CAST(fenv_t, ctl));
|
||||
#if defined(__GNUC__) && defined(HAVE_SSE)
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
__asm__ __volatile__("ldmxcsr %0" : : "m" (*&ctl->sse_state));
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE___CONTROL87_2)
|
||||
|
||||
int mode;
|
||||
__control87_2(ctl->state, _MCW_RC, &mode, NULL);
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
__control87_2(ctl->sse_state, _MCW_RC|_MCW_DN, NULL, &mode);
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE__CONTROLFP)
|
||||
|
||||
_controlfp(ctl->state, _MCW_RC);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static WCHAR *FromUTF8(const char *str)
|
||||
{
|
||||
WCHAR *out = NULL;
|
||||
int len;
|
||||
|
||||
if((len=MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)) > 0)
|
||||
{
|
||||
out = calloc(sizeof(WCHAR), len);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str, -1, out, len);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
void *LoadLib(const char *name)
|
||||
{
|
||||
HANDLE hdl = NULL;
|
||||
WCHAR *wname;
|
||||
|
||||
wname = FromUTF8(name);
|
||||
if(!wname)
|
||||
ERR("Failed to convert UTF-8 filename: \"%s\"\n", name);
|
||||
else
|
||||
{
|
||||
hdl = LoadLibraryW(wname);
|
||||
free(wname);
|
||||
}
|
||||
return hdl;
|
||||
}
|
||||
void CloseLib(void *handle)
|
||||
{ FreeLibrary((HANDLE)handle); }
|
||||
void *GetSymbol(void *handle, const char *name)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
ret = (void*)GetProcAddress((HANDLE)handle, name);
|
||||
if(ret == NULL)
|
||||
ERR("Failed to load %s\n", name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
WCHAR *strdupW(const WCHAR *str)
|
||||
{
|
||||
const WCHAR *n;
|
||||
WCHAR *ret;
|
||||
size_t len;
|
||||
|
||||
n = str;
|
||||
while(*n) n++;
|
||||
len = n - str;
|
||||
|
||||
ret = calloc(sizeof(WCHAR), len+1);
|
||||
if(ret != NULL)
|
||||
memcpy(ret, str, sizeof(WCHAR)*len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
FILE *al_fopen(const char *fname, const char *mode)
|
||||
{
|
||||
WCHAR *wname=NULL, *wmode=NULL;
|
||||
FILE *file = NULL;
|
||||
|
||||
wname = FromUTF8(fname);
|
||||
wmode = FromUTF8(mode);
|
||||
if(!wname)
|
||||
ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname);
|
||||
else if(!wmode)
|
||||
ERR("Failed to convert UTF-8 mode: \"%s\"\n", mode);
|
||||
else
|
||||
file = _wfopen(wname, wmode);
|
||||
|
||||
free(wname);
|
||||
free(wmode);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifdef HAVE_DLFCN_H
|
||||
|
||||
void *LoadLib(const char *name)
|
||||
{
|
||||
const char *err;
|
||||
void *handle;
|
||||
|
||||
dlerror();
|
||||
handle = dlopen(name, RTLD_NOW);
|
||||
if((err=dlerror()) != NULL)
|
||||
handle = NULL;
|
||||
return handle;
|
||||
}
|
||||
void CloseLib(void *handle)
|
||||
{ dlclose(handle); }
|
||||
void *GetSymbol(void *handle, const char *name)
|
||||
{
|
||||
const char *err;
|
||||
void *sym;
|
||||
|
||||
dlerror();
|
||||
sym = dlsym(handle, name);
|
||||
if((err=dlerror()) != NULL)
|
||||
{
|
||||
WARN("Failed to load %s: %s\n", name, err);
|
||||
sym = NULL;
|
||||
}
|
||||
return sym;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
void al_print(const char *type, const char *func, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
fprintf(LogFile, "AL lib: %s %s: ", type, func);
|
||||
vfprintf(LogFile, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fflush(LogFile);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static inline int is_slash(int c)
|
||||
{ return (c == '\\' || c == '/'); }
|
||||
|
||||
FILE *OpenDataFile(const char *fname, const char *subdir)
|
||||
{
|
||||
static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
|
||||
WCHAR *wname=NULL, *wsubdir=NULL;
|
||||
FILE *f;
|
||||
int i;
|
||||
|
||||
/* If the path is absolute, open it directly. */
|
||||
if(fname[0] != '\0' && fname[1] == ':' && is_slash(fname[2]))
|
||||
{
|
||||
if((f=al_fopen(fname, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", fname);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", fname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If it's relative, try the current directory first before the data directories. */
|
||||
if((f=al_fopen(fname, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", fname);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", fname);
|
||||
|
||||
wname = FromUTF8(fname);
|
||||
wsubdir = FromUTF8(subdir);
|
||||
if(!wname)
|
||||
ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname);
|
||||
else if(!wsubdir)
|
||||
ERR("Failed to convert UTF-8 subdir: \"%s\"\n", subdir);
|
||||
else for(i = 0;i < 2;i++)
|
||||
{
|
||||
WCHAR buffer[PATH_MAX];
|
||||
size_t len;
|
||||
|
||||
if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) == FALSE)
|
||||
continue;
|
||||
|
||||
len = lstrlenW(buffer);
|
||||
if(len > 0 && is_slash(buffer[len-1]))
|
||||
buffer[--len] = '\0';
|
||||
_snwprintf(buffer+len, PATH_MAX-len, L"/%ls/%ls", wsubdir, wname);
|
||||
len = lstrlenW(buffer);
|
||||
while(len > 0)
|
||||
{
|
||||
--len;
|
||||
if(buffer[len] == '/')
|
||||
buffer[len] = '\\';
|
||||
}
|
||||
|
||||
if((f=_wfopen(buffer, L"rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %ls\n", buffer);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %ls\n", buffer);
|
||||
}
|
||||
free(wname);
|
||||
free(wsubdir);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
FILE *OpenDataFile(const char *fname, const char *subdir)
|
||||
{
|
||||
char buffer[PATH_MAX] = "";
|
||||
const char *str, *next;
|
||||
FILE *f;
|
||||
|
||||
if(fname[0] == '/')
|
||||
{
|
||||
if((f=al_fopen(fname, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", fname);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", fname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if((f=al_fopen(fname, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", fname);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", fname);
|
||||
|
||||
if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0')
|
||||
snprintf(buffer, sizeof(buffer), "%s/%s/%s", str, subdir, fname);
|
||||
else if((str=getenv("HOME")) != NULL && str[0] != '\0')
|
||||
snprintf(buffer, sizeof(buffer), "%s/.local/share/%s/%s", str, subdir, fname);
|
||||
if(buffer[0])
|
||||
{
|
||||
if((f=al_fopen(buffer, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", buffer);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", buffer);
|
||||
}
|
||||
|
||||
if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0')
|
||||
str = "/usr/local/share/:/usr/share/";
|
||||
|
||||
next = str;
|
||||
while((str=next) != NULL && str[0] != '\0')
|
||||
{
|
||||
size_t len;
|
||||
next = strchr(str, ':');
|
||||
|
||||
if(!next)
|
||||
len = strlen(str);
|
||||
else
|
||||
{
|
||||
len = next - str;
|
||||
next++;
|
||||
}
|
||||
|
||||
if(len > sizeof(buffer)-1)
|
||||
len = sizeof(buffer)-1;
|
||||
strncpy(buffer, str, len);
|
||||
buffer[len] = '\0';
|
||||
snprintf(buffer+len, sizeof(buffer)-len, "/%s/%s", subdir, fname);
|
||||
|
||||
if((f=al_fopen(buffer, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", buffer);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", buffer);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void SetRTPriority(void)
|
||||
{
|
||||
ALboolean failed = AL_FALSE;
|
||||
|
||||
#ifdef _WIN32
|
||||
if(RTPrioLevel > 0)
|
||||
failed = !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
||||
#elif defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
|
||||
if(RTPrioLevel > 0)
|
||||
{
|
||||
struct sched_param param;
|
||||
/* Use the minimum real-time priority possible for now (on Linux this
|
||||
* should be 1 for SCHED_RR) */
|
||||
param.sched_priority = sched_get_priority_min(SCHED_RR);
|
||||
failed = !!pthread_setschedparam(pthread_self(), SCHED_RR, ¶m);
|
||||
}
|
||||
#else
|
||||
/* Real-time priority not available */
|
||||
failed = (RTPrioLevel>0);
|
||||
#endif
|
||||
if(failed)
|
||||
ERR("Failed to set priority level for thread\n");
|
||||
}
|
||||
|
||||
|
||||
ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count, ALboolean exact)
|
||||
{
|
||||
vector_ *vecptr = (vector_*)ptr;
|
||||
if(obj_count < 0)
|
||||
return AL_FALSE;
|
||||
if((*vecptr ? (*vecptr)->Capacity : 0) < obj_count)
|
||||
{
|
||||
ALsizei old_size = (*vecptr ? (*vecptr)->Size : 0);
|
||||
void *temp;
|
||||
|
||||
/* Use the next power-of-2 size if we don't need to allocate the exact
|
||||
* amount. This is preferred when regularly increasing the vector since
|
||||
* it means fewer reallocations. Though it means it also wastes some
|
||||
* memory. */
|
||||
if(exact == AL_FALSE)
|
||||
{
|
||||
obj_count = NextPowerOf2((ALuint)obj_count);
|
||||
if(obj_count < 0) return AL_FALSE;
|
||||
}
|
||||
|
||||
/* Need to be explicit with the caller type's base size, because it
|
||||
* could have extra padding before the start of the array (that is,
|
||||
* sizeof(*vector_) may not equal base_size). */
|
||||
temp = realloc(*vecptr, base_size + obj_size*obj_count);
|
||||
if(temp == NULL) return AL_FALSE;
|
||||
|
||||
*vecptr = temp;
|
||||
(*vecptr)->Capacity = obj_count;
|
||||
(*vecptr)->Size = old_size;
|
||||
}
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count)
|
||||
{
|
||||
vector_ *vecptr = (vector_*)ptr;
|
||||
if(obj_count < 0)
|
||||
return AL_FALSE;
|
||||
if(*vecptr || obj_count > 0)
|
||||
{
|
||||
if(!vector_reserve((char*)vecptr, base_size, obj_size, obj_count, AL_TRUE))
|
||||
return AL_FALSE;
|
||||
(*vecptr)->Size = obj_count;
|
||||
}
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend)
|
||||
{
|
||||
vector_ *vecptr = (vector_*)ptr;
|
||||
if(datstart != datend)
|
||||
{
|
||||
ptrdiff_t ins_elem = (*vecptr ? ((char*)ins_pos - ((char*)(*vecptr) + base_size)) :
|
||||
((char*)ins_pos - (char*)NULL)) /
|
||||
obj_size;
|
||||
ptrdiff_t numins = ((const char*)datend - (const char*)datstart) / obj_size;
|
||||
|
||||
assert(numins > 0);
|
||||
if(INT_MAX-VECTOR_SIZE(*vecptr) <= numins ||
|
||||
!vector_reserve((char*)vecptr, base_size, obj_size, VECTOR_SIZE(*vecptr)+numins, AL_TRUE))
|
||||
return AL_FALSE;
|
||||
|
||||
/* NOTE: ins_pos may have been invalidated if *vecptr moved. Use ins_elem instead. */
|
||||
if(ins_elem < (*vecptr)->Size)
|
||||
{
|
||||
memmove((char*)(*vecptr) + base_size + ((ins_elem+numins)*obj_size),
|
||||
(char*)(*vecptr) + base_size + ((ins_elem )*obj_size),
|
||||
((*vecptr)->Size-ins_elem)*obj_size);
|
||||
}
|
||||
memcpy((char*)(*vecptr) + base_size + (ins_elem*obj_size),
|
||||
datstart, numins*obj_size);
|
||||
(*vecptr)->Size += (ALsizei)numins;
|
||||
}
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
extern inline void al_string_deinit(al_string *str);
|
||||
extern inline ALsizei al_string_length(const_al_string str);
|
||||
extern inline ALboolean al_string_empty(const_al_string str);
|
||||
extern inline const al_string_char_type *al_string_get_cstr(const_al_string str);
|
||||
|
||||
void al_string_clear(al_string *str)
|
||||
{
|
||||
/* Reserve one more character than the total size of the string. This is to
|
||||
* ensure we have space to add a null terminator in the string data so it
|
||||
* can be used as a C-style string. */
|
||||
VECTOR_RESERVE(*str, 1);
|
||||
VECTOR_RESIZE(*str, 0);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
|
||||
static inline int al_string_compare(const al_string_char_type *str1, ALsizei str1len,
|
||||
const al_string_char_type *str2, ALsizei str2len)
|
||||
{
|
||||
ALsizei complen = mini(str1len, str2len);
|
||||
int ret = memcmp(str1, str2, complen);
|
||||
if(ret == 0)
|
||||
{
|
||||
if(str1len > str2len) return 1;
|
||||
if(str1len < str2len) return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
int al_string_cmp(const_al_string str1, const_al_string str2)
|
||||
{
|
||||
return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1),
|
||||
&VECTOR_FRONT(str2), al_string_length(str2));
|
||||
}
|
||||
int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2)
|
||||
{
|
||||
return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1),
|
||||
str2, (ALsizei)strlen(str2));
|
||||
}
|
||||
|
||||
void al_string_copy(al_string *str, const_al_string from)
|
||||
{
|
||||
ALsizei len = VECTOR_SIZE(from);
|
||||
VECTOR_RESERVE(*str, len+1);
|
||||
VECTOR_RESIZE(*str, 0);
|
||||
VECTOR_INSERT(*str, VECTOR_ITER_END(*str), VECTOR_ITER_BEGIN(from), VECTOR_ITER_BEGIN(from)+len);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
|
||||
void al_string_copy_cstr(al_string *str, const al_string_char_type *from)
|
||||
{
|
||||
size_t len = strlen(from);
|
||||
VECTOR_RESERVE(*str, len+1);
|
||||
VECTOR_RESIZE(*str, 0);
|
||||
VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
|
||||
void al_string_append_char(al_string *str, const al_string_char_type c)
|
||||
{
|
||||
VECTOR_RESERVE(*str, al_string_length(*str)+2);
|
||||
VECTOR_PUSH_BACK(*str, c);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
|
||||
void al_string_append_cstr(al_string *str, const al_string_char_type *from)
|
||||
{
|
||||
size_t len = strlen(from);
|
||||
if(len != 0)
|
||||
{
|
||||
VECTOR_RESERVE(*str, al_string_length(*str)+len+1);
|
||||
VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to)
|
||||
{
|
||||
if(to != from)
|
||||
{
|
||||
VECTOR_RESERVE(*str, al_string_length(*str)+(to-from)+1);
|
||||
VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, to);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void al_string_copy_wcstr(al_string *str, const wchar_t *from)
|
||||
{
|
||||
int len;
|
||||
if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0)
|
||||
{
|
||||
VECTOR_RESERVE(*str, len);
|
||||
VECTOR_RESIZE(*str, len-1);
|
||||
WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str), len, NULL, NULL);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,820 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2011 by Chris Robinson
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alSource.h"
|
||||
#include "alu.h"
|
||||
#include "hrtf.h"
|
||||
|
||||
|
||||
/* Current data set limits defined by the makehrtf utility. */
|
||||
#define MIN_IR_SIZE (8)
|
||||
#define MAX_IR_SIZE (128)
|
||||
#define MOD_IR_SIZE (8)
|
||||
|
||||
#define MIN_EV_COUNT (5)
|
||||
#define MAX_EV_COUNT (128)
|
||||
|
||||
#define MIN_AZ_COUNT (1)
|
||||
#define MAX_AZ_COUNT (128)
|
||||
|
||||
struct Hrtf {
|
||||
ALuint sampleRate;
|
||||
ALuint irSize;
|
||||
ALubyte evCount;
|
||||
|
||||
const ALubyte *azCount;
|
||||
const ALushort *evOffset;
|
||||
const ALshort *coeffs;
|
||||
const ALubyte *delays;
|
||||
|
||||
struct Hrtf *next;
|
||||
};
|
||||
|
||||
static const ALchar magicMarker00[8] = "MinPHR00";
|
||||
static const ALchar magicMarker01[8] = "MinPHR01";
|
||||
|
||||
/* First value for pass-through coefficients (remaining are 0), used for omni-
|
||||
* directional sounds. */
|
||||
static const ALfloat PassthruCoeff = 32767.0f * 0.707106781187f/*sqrt(0.5)*/;
|
||||
|
||||
static struct Hrtf *LoadedHrtfs = NULL;
|
||||
|
||||
/* Calculate the elevation indices given the polar elevation in radians.
|
||||
* This will return two indices between 0 and (evcount - 1) and an
|
||||
* interpolation factor between 0.0 and 1.0.
|
||||
*/
|
||||
static void CalcEvIndices(ALuint evcount, ALfloat ev, ALuint *evidx, ALfloat *evmu)
|
||||
{
|
||||
ev = (F_PI_2 + ev) * (evcount-1) / F_PI;
|
||||
evidx[0] = fastf2u(ev);
|
||||
evidx[1] = minu(evidx[0] + 1, evcount-1);
|
||||
*evmu = ev - evidx[0];
|
||||
}
|
||||
|
||||
/* Calculate the azimuth indices given the polar azimuth in radians. This
|
||||
* will return two indices between 0 and (azcount - 1) and an interpolation
|
||||
* factor between 0.0 and 1.0.
|
||||
*/
|
||||
static void CalcAzIndices(ALuint azcount, ALfloat az, ALuint *azidx, ALfloat *azmu)
|
||||
{
|
||||
az = (F_2PI + az) * azcount / (F_2PI);
|
||||
azidx[0] = fastf2u(az) % azcount;
|
||||
azidx[1] = (azidx[0] + 1) % azcount;
|
||||
*azmu = az - floorf(az);
|
||||
}
|
||||
|
||||
/* Calculates the normalized HRTF transition factor (delta) from the changes
|
||||
* in gain and listener to source angle between updates. The result is a
|
||||
* normalized delta factor that can be used to calculate moving HRIR stepping
|
||||
* values.
|
||||
*/
|
||||
ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3])
|
||||
{
|
||||
ALfloat gainChange, angleChange, change;
|
||||
|
||||
// Calculate the normalized dB gain change.
|
||||
newGain = maxf(newGain, 0.0001f);
|
||||
oldGain = maxf(oldGain, 0.0001f);
|
||||
gainChange = fabsf(log10f(newGain / oldGain) / log10f(0.0001f));
|
||||
|
||||
// Calculate the angle change only when there is enough gain to notice it.
|
||||
angleChange = 0.0f;
|
||||
if(gainChange > 0.0001f || newGain > 0.0001f)
|
||||
{
|
||||
// No angle change when the directions are equal or degenerate (when
|
||||
// both have zero length).
|
||||
if(newdir[0] != olddir[0] || newdir[1] != olddir[1] || newdir[2] != olddir[2])
|
||||
{
|
||||
ALfloat dotp = olddir[0]*newdir[0] + olddir[1]*newdir[1] + olddir[2]*newdir[2];
|
||||
angleChange = acosf(clampf(dotp, -1.0f, 1.0f)) / F_PI;
|
||||
}
|
||||
}
|
||||
|
||||
// Use the largest of the two changes for the delta factor, and apply a
|
||||
// significance shaping function to it.
|
||||
change = maxf(angleChange * 25.0f, gainChange) * 2.0f;
|
||||
return minf(change, 1.0f);
|
||||
}
|
||||
|
||||
/* Calculates static HRIR coefficients and delays for the given polar
|
||||
* elevation and azimuth in radians. Linear interpolation is used to
|
||||
* increase the apparent resolution of the HRIR data set. The coefficients
|
||||
* are also normalized and attenuated by the specified gain.
|
||||
*/
|
||||
void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays)
|
||||
{
|
||||
ALuint evidx[2], lidx[4], ridx[4];
|
||||
ALfloat mu[3], blend[4];
|
||||
ALuint i;
|
||||
|
||||
/* Claculate elevation indices and interpolation factor. */
|
||||
CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]);
|
||||
|
||||
for(i = 0;i < 2;i++)
|
||||
{
|
||||
ALuint azcount = Hrtf->azCount[evidx[i]];
|
||||
ALuint evoffset = Hrtf->evOffset[evidx[i]];
|
||||
ALuint azidx[2];
|
||||
|
||||
/* Calculate azimuth indices and interpolation factor for this elevation. */
|
||||
CalcAzIndices(azcount, azimuth, azidx, &mu[i]);
|
||||
|
||||
/* Calculate a set of linear HRIR indices for left and right channels. */
|
||||
lidx[i*2 + 0] = evoffset + azidx[0];
|
||||
lidx[i*2 + 1] = evoffset + azidx[1];
|
||||
ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount);
|
||||
ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount);
|
||||
}
|
||||
|
||||
/* Calculate 4 blending weights for 2D bilinear interpolation. */
|
||||
blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]);
|
||||
blend[1] = ( mu[0]) * (1.0f-mu[2]);
|
||||
blend[2] = (1.0f-mu[1]) * ( mu[2]);
|
||||
blend[3] = ( mu[1]) * ( mu[2]);
|
||||
|
||||
/* Calculate the HRIR delays using linear interpolation. */
|
||||
delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] +
|
||||
Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) *
|
||||
dirfact + 0.5f) << HRTFDELAY_BITS;
|
||||
delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] +
|
||||
Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) *
|
||||
dirfact + 0.5f) << HRTFDELAY_BITS;
|
||||
|
||||
/* Calculate the sample offsets for the HRIR indices. */
|
||||
lidx[0] *= Hrtf->irSize;
|
||||
lidx[1] *= Hrtf->irSize;
|
||||
lidx[2] *= Hrtf->irSize;
|
||||
lidx[3] *= Hrtf->irSize;
|
||||
ridx[0] *= Hrtf->irSize;
|
||||
ridx[1] *= Hrtf->irSize;
|
||||
ridx[2] *= Hrtf->irSize;
|
||||
ridx[3] *= Hrtf->irSize;
|
||||
|
||||
/* Calculate the normalized and attenuated HRIR coefficients using linear
|
||||
* interpolation when there is enough gain to warrant it. Zero the
|
||||
* coefficients if gain is too low.
|
||||
*/
|
||||
if(gain > 0.0001f)
|
||||
{
|
||||
ALfloat c;
|
||||
|
||||
gain *= 1.0f/32767.0f;
|
||||
|
||||
i = 0;
|
||||
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
|
||||
coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain;
|
||||
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
|
||||
coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain;
|
||||
|
||||
for(i = 1;i < Hrtf->irSize;i++)
|
||||
{
|
||||
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
|
||||
coeffs[i][0] = lerp(0.0f, c, dirfact) * gain;
|
||||
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
|
||||
coeffs[i][1] = lerp(0.0f, c, dirfact) * gain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0;i < Hrtf->irSize;i++)
|
||||
{
|
||||
coeffs[i][0] = 0.0f;
|
||||
coeffs[i][1] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculates the moving HRIR target coefficients, target delays, and
|
||||
* stepping values for the given polar elevation and azimuth in radians.
|
||||
* Linear interpolation is used to increase the apparent resolution of the
|
||||
* HRIR data set. The coefficients are also normalized and attenuated by the
|
||||
* specified gain. Stepping resolution and count is determined using the
|
||||
* given delta factor between 0.0 and 1.0.
|
||||
*/
|
||||
ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep)
|
||||
{
|
||||
ALuint evidx[2], lidx[4], ridx[4];
|
||||
ALfloat mu[3], blend[4];
|
||||
ALfloat left, right;
|
||||
ALfloat step;
|
||||
ALuint i;
|
||||
|
||||
/* Claculate elevation indices and interpolation factor. */
|
||||
CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]);
|
||||
|
||||
for(i = 0;i < 2;i++)
|
||||
{
|
||||
ALuint azcount = Hrtf->azCount[evidx[i]];
|
||||
ALuint evoffset = Hrtf->evOffset[evidx[i]];
|
||||
ALuint azidx[2];
|
||||
|
||||
/* Calculate azimuth indices and interpolation factor for this elevation. */
|
||||
CalcAzIndices(azcount, azimuth, azidx, &mu[i]);
|
||||
|
||||
/* Calculate a set of linear HRIR indices for left and right channels. */
|
||||
lidx[i*2 + 0] = evoffset + azidx[0];
|
||||
lidx[i*2 + 1] = evoffset + azidx[1];
|
||||
ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount);
|
||||
ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount);
|
||||
}
|
||||
|
||||
// Calculate the stepping parameters.
|
||||
delta = maxf(floorf(delta*(Hrtf->sampleRate*0.015f) + 0.5f), 1.0f);
|
||||
step = 1.0f / delta;
|
||||
|
||||
/* Calculate 4 blending weights for 2D bilinear interpolation. */
|
||||
blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]);
|
||||
blend[1] = ( mu[0]) * (1.0f-mu[2]);
|
||||
blend[2] = (1.0f-mu[1]) * ( mu[2]);
|
||||
blend[3] = ( mu[1]) * ( mu[2]);
|
||||
|
||||
/* Calculate the HRIR delays using linear interpolation. Then calculate
|
||||
* the delay stepping values using the target and previous running
|
||||
* delays.
|
||||
*/
|
||||
left = (ALfloat)(delays[0] - (delayStep[0] * counter));
|
||||
right = (ALfloat)(delays[1] - (delayStep[1] * counter));
|
||||
|
||||
delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] +
|
||||
Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) *
|
||||
dirfact + 0.5f) << HRTFDELAY_BITS;
|
||||
delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] +
|
||||
Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) *
|
||||
dirfact + 0.5f) << HRTFDELAY_BITS;
|
||||
|
||||
delayStep[0] = fastf2i(step * (delays[0] - left));
|
||||
delayStep[1] = fastf2i(step * (delays[1] - right));
|
||||
|
||||
/* Calculate the sample offsets for the HRIR indices. */
|
||||
lidx[0] *= Hrtf->irSize;
|
||||
lidx[1] *= Hrtf->irSize;
|
||||
lidx[2] *= Hrtf->irSize;
|
||||
lidx[3] *= Hrtf->irSize;
|
||||
ridx[0] *= Hrtf->irSize;
|
||||
ridx[1] *= Hrtf->irSize;
|
||||
ridx[2] *= Hrtf->irSize;
|
||||
ridx[3] *= Hrtf->irSize;
|
||||
|
||||
/* Calculate the normalized and attenuated target HRIR coefficients using
|
||||
* linear interpolation when there is enough gain to warrant it. Zero
|
||||
* the target coefficients if gain is too low. Then calculate the
|
||||
* coefficient stepping values using the target and previous running
|
||||
* coefficients.
|
||||
*/
|
||||
if(gain > 0.0001f)
|
||||
{
|
||||
ALfloat c;
|
||||
|
||||
gain *= 1.0f/32767.0f;
|
||||
|
||||
i = 0;
|
||||
left = coeffs[i][0] - (coeffStep[i][0] * counter);
|
||||
right = coeffs[i][1] - (coeffStep[i][1] * counter);
|
||||
|
||||
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
|
||||
coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain;
|
||||
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
|
||||
coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain;
|
||||
|
||||
coeffStep[i][0] = step * (coeffs[i][0] - left);
|
||||
coeffStep[i][1] = step * (coeffs[i][1] - right);
|
||||
|
||||
for(i = 1;i < Hrtf->irSize;i++)
|
||||
{
|
||||
left = coeffs[i][0] - (coeffStep[i][0] * counter);
|
||||
right = coeffs[i][1] - (coeffStep[i][1] * counter);
|
||||
|
||||
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
|
||||
coeffs[i][0] = lerp(0.0f, c, dirfact) * gain;
|
||||
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
|
||||
coeffs[i][1] = lerp(0.0f, c, dirfact) * gain;
|
||||
|
||||
coeffStep[i][0] = step * (coeffs[i][0] - left);
|
||||
coeffStep[i][1] = step * (coeffs[i][1] - right);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0;i < Hrtf->irSize;i++)
|
||||
{
|
||||
left = coeffs[i][0] - (coeffStep[i][0] * counter);
|
||||
right = coeffs[i][1] - (coeffStep[i][1] * counter);
|
||||
|
||||
coeffs[i][0] = 0.0f;
|
||||
coeffs[i][1] = 0.0f;
|
||||
|
||||
coeffStep[i][0] = step * -left;
|
||||
coeffStep[i][1] = step * -right;
|
||||
}
|
||||
}
|
||||
|
||||
/* The stepping count is the number of samples necessary for the HRIR to
|
||||
* complete its transition. The mixer will only apply stepping for this
|
||||
* many samples.
|
||||
*/
|
||||
return fastf2u(delta);
|
||||
}
|
||||
|
||||
|
||||
static struct Hrtf *LoadHrtf00(FILE *f, ALuint deviceRate)
|
||||
{
|
||||
const ALubyte maxDelay = SRC_HISTORY_LENGTH-1;
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
ALboolean failed = AL_FALSE;
|
||||
ALuint rate = 0, irCount = 0;
|
||||
ALushort irSize = 0;
|
||||
ALubyte evCount = 0;
|
||||
ALubyte *azCount = NULL;
|
||||
ALushort *evOffset = NULL;
|
||||
ALshort *coeffs = NULL;
|
||||
ALubyte *delays = NULL;
|
||||
ALuint i, j;
|
||||
|
||||
rate = fgetc(f);
|
||||
rate |= fgetc(f)<<8;
|
||||
rate |= fgetc(f)<<16;
|
||||
rate |= fgetc(f)<<24;
|
||||
|
||||
irCount = fgetc(f);
|
||||
irCount |= fgetc(f)<<8;
|
||||
|
||||
irSize = fgetc(f);
|
||||
irSize |= fgetc(f)<<8;
|
||||
|
||||
evCount = fgetc(f);
|
||||
|
||||
if(rate != deviceRate)
|
||||
{
|
||||
ERR("HRIR rate does not match device rate: rate=%d (%d)\n",
|
||||
rate, deviceRate);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
|
||||
{
|
||||
ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
|
||||
irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT)
|
||||
{
|
||||
ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
|
||||
evCount, MIN_EV_COUNT, MAX_EV_COUNT);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
if(failed)
|
||||
return NULL;
|
||||
|
||||
azCount = malloc(sizeof(azCount[0])*evCount);
|
||||
evOffset = malloc(sizeof(evOffset[0])*evCount);
|
||||
if(azCount == NULL || evOffset == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
evOffset[0] = fgetc(f);
|
||||
evOffset[0] |= fgetc(f)<<8;
|
||||
for(i = 1;i < evCount;i++)
|
||||
{
|
||||
evOffset[i] = fgetc(f);
|
||||
evOffset[i] |= fgetc(f)<<8;
|
||||
if(evOffset[i] <= evOffset[i-1])
|
||||
{
|
||||
ERR("Invalid evOffset: evOffset[%d]=%d (last=%d)\n",
|
||||
i, evOffset[i], evOffset[i-1]);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
azCount[i-1] = evOffset[i] - evOffset[i-1];
|
||||
if(azCount[i-1] < MIN_AZ_COUNT || azCount[i-1] > MAX_AZ_COUNT)
|
||||
{
|
||||
ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
|
||||
i-1, azCount[i-1], MIN_AZ_COUNT, MAX_AZ_COUNT);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
if(irCount <= evOffset[i-1])
|
||||
{
|
||||
ERR("Invalid evOffset: evOffset[%d]=%d (irCount=%d)\n",
|
||||
i-1, evOffset[i-1], irCount);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
azCount[i-1] = irCount - evOffset[i-1];
|
||||
if(azCount[i-1] < MIN_AZ_COUNT || azCount[i-1] > MAX_AZ_COUNT)
|
||||
{
|
||||
ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
|
||||
i-1, azCount[i-1], MIN_AZ_COUNT, MAX_AZ_COUNT);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
coeffs = malloc(sizeof(coeffs[0])*irSize*irCount);
|
||||
delays = malloc(sizeof(delays[0])*irCount);
|
||||
if(coeffs == NULL || delays == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
for(i = 0;i < irCount*irSize;i+=irSize)
|
||||
{
|
||||
for(j = 0;j < irSize;j++)
|
||||
{
|
||||
ALshort coeff;
|
||||
coeff = fgetc(f);
|
||||
coeff |= fgetc(f)<<8;
|
||||
coeffs[i+j] = coeff;
|
||||
}
|
||||
}
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
delays[i] = fgetc(f);
|
||||
if(delays[i] > maxDelay)
|
||||
{
|
||||
ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(feof(f))
|
||||
{
|
||||
ERR("Premature end of data\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
Hrtf = malloc(sizeof(struct Hrtf));
|
||||
if(Hrtf == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
Hrtf->sampleRate = rate;
|
||||
Hrtf->irSize = irSize;
|
||||
Hrtf->evCount = evCount;
|
||||
Hrtf->azCount = azCount;
|
||||
Hrtf->evOffset = evOffset;
|
||||
Hrtf->coeffs = coeffs;
|
||||
Hrtf->delays = delays;
|
||||
Hrtf->next = NULL;
|
||||
return Hrtf;
|
||||
}
|
||||
|
||||
free(azCount);
|
||||
free(evOffset);
|
||||
free(coeffs);
|
||||
free(delays);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static struct Hrtf *LoadHrtf01(FILE *f, ALuint deviceRate)
|
||||
{
|
||||
const ALubyte maxDelay = SRC_HISTORY_LENGTH-1;
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
ALboolean failed = AL_FALSE;
|
||||
ALuint rate = 0, irCount = 0;
|
||||
ALubyte irSize = 0, evCount = 0;
|
||||
ALubyte *azCount = NULL;
|
||||
ALushort *evOffset = NULL;
|
||||
ALshort *coeffs = NULL;
|
||||
ALubyte *delays = NULL;
|
||||
ALuint i, j;
|
||||
|
||||
rate = fgetc(f);
|
||||
rate |= fgetc(f)<<8;
|
||||
rate |= fgetc(f)<<16;
|
||||
rate |= fgetc(f)<<24;
|
||||
|
||||
irSize = fgetc(f);
|
||||
|
||||
evCount = fgetc(f);
|
||||
|
||||
if(rate != deviceRate)
|
||||
{
|
||||
ERR("HRIR rate does not match device rate: rate=%d (%d)\n",
|
||||
rate, deviceRate);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
|
||||
{
|
||||
ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
|
||||
irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT)
|
||||
{
|
||||
ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
|
||||
evCount, MIN_EV_COUNT, MAX_EV_COUNT);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
if(failed)
|
||||
return NULL;
|
||||
|
||||
azCount = malloc(sizeof(azCount[0])*evCount);
|
||||
evOffset = malloc(sizeof(evOffset[0])*evCount);
|
||||
if(azCount == NULL || evOffset == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
for(i = 0;i < evCount;i++)
|
||||
{
|
||||
azCount[i] = fgetc(f);
|
||||
if(azCount[i] < MIN_AZ_COUNT || azCount[i] > MAX_AZ_COUNT)
|
||||
{
|
||||
ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
|
||||
i, azCount[i], MIN_AZ_COUNT, MAX_AZ_COUNT);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
evOffset[0] = 0;
|
||||
irCount = azCount[0];
|
||||
for(i = 1;i < evCount;i++)
|
||||
{
|
||||
evOffset[i] = evOffset[i-1] + azCount[i-1];
|
||||
irCount += azCount[i];
|
||||
}
|
||||
|
||||
coeffs = malloc(sizeof(coeffs[0])*irSize*irCount);
|
||||
delays = malloc(sizeof(delays[0])*irCount);
|
||||
if(coeffs == NULL || delays == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
for(i = 0;i < irCount*irSize;i+=irSize)
|
||||
{
|
||||
for(j = 0;j < irSize;j++)
|
||||
{
|
||||
ALshort coeff;
|
||||
coeff = fgetc(f);
|
||||
coeff |= fgetc(f)<<8;
|
||||
coeffs[i+j] = coeff;
|
||||
}
|
||||
}
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
delays[i] = fgetc(f);
|
||||
if(delays[i] > maxDelay)
|
||||
{
|
||||
ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(feof(f))
|
||||
{
|
||||
ERR("Premature end of data\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
Hrtf = malloc(sizeof(struct Hrtf));
|
||||
if(Hrtf == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
Hrtf->sampleRate = rate;
|
||||
Hrtf->irSize = irSize;
|
||||
Hrtf->evCount = evCount;
|
||||
Hrtf->azCount = azCount;
|
||||
Hrtf->evOffset = evOffset;
|
||||
Hrtf->coeffs = coeffs;
|
||||
Hrtf->delays = delays;
|
||||
Hrtf->next = NULL;
|
||||
return Hrtf;
|
||||
}
|
||||
|
||||
free(azCount);
|
||||
free(evOffset);
|
||||
free(coeffs);
|
||||
free(delays);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static struct Hrtf *LoadHrtf(ALuint deviceRate)
|
||||
{
|
||||
const char *fnamelist = "default-%r.mhr";
|
||||
|
||||
ConfigValueStr(NULL, "hrtf_tables", &fnamelist);
|
||||
while(*fnamelist != '\0')
|
||||
{
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
char fname[PATH_MAX];
|
||||
const char *next;
|
||||
ALchar magic[8];
|
||||
ALuint i;
|
||||
FILE *f;
|
||||
|
||||
i = 0;
|
||||
while(isspace(*fnamelist) || *fnamelist == ',')
|
||||
fnamelist++;
|
||||
next = fnamelist;
|
||||
while(*(fnamelist=next) != '\0' && *fnamelist != ',')
|
||||
{
|
||||
next = strpbrk(fnamelist, "%,");
|
||||
while(fnamelist != next && *fnamelist && i < sizeof(fname))
|
||||
fname[i++] = *(fnamelist++);
|
||||
|
||||
if(!next || *next == ',')
|
||||
break;
|
||||
|
||||
/* *next == '%' */
|
||||
next++;
|
||||
if(*next == 'r')
|
||||
{
|
||||
int wrote = snprintf(&fname[i], sizeof(fname)-i, "%u", deviceRate);
|
||||
i += minu(wrote, sizeof(fname)-i);
|
||||
next++;
|
||||
}
|
||||
else if(*next == '%')
|
||||
{
|
||||
if(i < sizeof(fname))
|
||||
fname[i++] = '%';
|
||||
next++;
|
||||
}
|
||||
else
|
||||
ERR("Invalid marker '%%%c'\n", *next);
|
||||
}
|
||||
i = minu(i, sizeof(fname)-1);
|
||||
fname[i] = '\0';
|
||||
while(i > 0 && isspace(fname[i-1]))
|
||||
i--;
|
||||
fname[i] = '\0';
|
||||
|
||||
if(fname[0] == '\0')
|
||||
continue;
|
||||
|
||||
TRACE("Loading %s...\n", fname);
|
||||
f = OpenDataFile(fname, "openal/hrtf");
|
||||
if(f == NULL)
|
||||
{
|
||||
ERR("Could not open %s\n", fname);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(fread(magic, 1, sizeof(magic), f) != sizeof(magic))
|
||||
ERR("Failed to read header from %s\n", fname);
|
||||
else
|
||||
{
|
||||
if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0)
|
||||
{
|
||||
TRACE("Detected data set format v0\n");
|
||||
Hrtf = LoadHrtf00(f, deviceRate);
|
||||
}
|
||||
else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0)
|
||||
{
|
||||
TRACE("Detected data set format v1\n");
|
||||
Hrtf = LoadHrtf01(f, deviceRate);
|
||||
}
|
||||
else
|
||||
ERR("Invalid header in %s: \"%.8s\"\n", fname, magic);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
f = NULL;
|
||||
|
||||
if(Hrtf)
|
||||
{
|
||||
Hrtf->next = LoadedHrtfs;
|
||||
LoadedHrtfs = Hrtf;
|
||||
TRACE("Loaded HRTF support for format: %s %uhz\n",
|
||||
DevFmtChannelsString(DevFmtStereo), Hrtf->sampleRate);
|
||||
return Hrtf;
|
||||
}
|
||||
|
||||
ERR("Failed to load %s\n", fname);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct Hrtf *GetHrtf(enum DevFmtChannels chans, ALCuint srate)
|
||||
{
|
||||
if(chans == DevFmtStereo)
|
||||
{
|
||||
struct Hrtf *Hrtf = LoadedHrtfs;
|
||||
while(Hrtf != NULL)
|
||||
{
|
||||
if(srate == Hrtf->sampleRate)
|
||||
return Hrtf;
|
||||
Hrtf = Hrtf->next;
|
||||
}
|
||||
|
||||
Hrtf = LoadHrtf(srate);
|
||||
if(Hrtf != NULL)
|
||||
return Hrtf;
|
||||
}
|
||||
ERR("Incompatible format: %s %uhz\n", DevFmtChannelsString(chans), srate);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALCboolean FindHrtfFormat(enum DevFmtChannels *chans, ALCuint *srate)
|
||||
{
|
||||
const struct Hrtf *hrtf = LoadedHrtfs;
|
||||
while(hrtf != NULL)
|
||||
{
|
||||
if(*srate == hrtf->sampleRate)
|
||||
break;
|
||||
hrtf = hrtf->next;
|
||||
}
|
||||
|
||||
if(hrtf == NULL)
|
||||
{
|
||||
hrtf = LoadHrtf(*srate);
|
||||
if(hrtf == NULL) return ALC_FALSE;
|
||||
}
|
||||
|
||||
*chans = DevFmtStereo;
|
||||
*srate = hrtf->sampleRate;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void FreeHrtfs(void)
|
||||
{
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
|
||||
while((Hrtf=LoadedHrtfs) != NULL)
|
||||
{
|
||||
LoadedHrtfs = Hrtf->next;
|
||||
free((void*)Hrtf->azCount);
|
||||
free((void*)Hrtf->evOffset);
|
||||
free((void*)Hrtf->coeffs);
|
||||
free((void*)Hrtf->delays);
|
||||
free(Hrtf);
|
||||
}
|
||||
}
|
||||
|
||||
ALuint GetHrtfIrSize (const struct Hrtf *Hrtf)
|
||||
{
|
||||
return Hrtf->irSize;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifndef ALC_HRTF_H
|
||||
#define ALC_HRTF_H
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
enum DevFmtChannels;
|
||||
|
||||
struct Hrtf;
|
||||
|
||||
#define HRIR_BITS (7)
|
||||
#define HRIR_LENGTH (1<<HRIR_BITS)
|
||||
#define HRIR_MASK (HRIR_LENGTH-1)
|
||||
#define HRTFDELAY_BITS (20)
|
||||
#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS)
|
||||
#define HRTFDELAY_MASK (HRTFDELAY_FRACONE-1)
|
||||
|
||||
const struct Hrtf *GetHrtf(enum DevFmtChannels chans, ALCuint srate);
|
||||
ALCboolean FindHrtfFormat(enum DevFmtChannels *chans, ALCuint *srate);
|
||||
|
||||
void FreeHrtfs(void);
|
||||
|
||||
ALuint GetHrtfIrSize(const struct Hrtf *Hrtf);
|
||||
ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3]);
|
||||
void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays);
|
||||
ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep);
|
||||
|
||||
#endif /* ALC_HRTF_H */
|
||||
@@ -1,244 +0,0 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
#include "alMidi.h"
|
||||
#include "alMain.h"
|
||||
#include "alError.h"
|
||||
#include "alThunk.h"
|
||||
#include "evtqueue.h"
|
||||
#include "rwlock.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
extern inline ALboolean IsValidCtrlInput(int cc);
|
||||
|
||||
extern inline size_t Reader_read(Reader *self, void *buf, size_t len);
|
||||
|
||||
|
||||
/* MIDI events */
|
||||
#define SYSEX_EVENT (0xF0)
|
||||
|
||||
|
||||
void InitEvtQueue(EvtQueue *queue)
|
||||
{
|
||||
queue->events = NULL;
|
||||
queue->maxsize = 0;
|
||||
queue->size = 0;
|
||||
queue->pos = 0;
|
||||
}
|
||||
|
||||
void ResetEvtQueue(EvtQueue *queue)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < queue->size;i++)
|
||||
{
|
||||
if(queue->events[i].event == SYSEX_EVENT)
|
||||
{
|
||||
free(queue->events[i].param.sysex.data);
|
||||
queue->events[i].param.sysex.data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
free(queue->events);
|
||||
queue->events = NULL;
|
||||
queue->maxsize = 0;
|
||||
queue->size = 0;
|
||||
queue->pos = 0;
|
||||
}
|
||||
|
||||
ALenum InsertEvtQueue(EvtQueue *queue, const MidiEvent *evt)
|
||||
{
|
||||
ALsizei pos;
|
||||
|
||||
if(queue->maxsize == queue->size)
|
||||
{
|
||||
if(queue->pos > 0)
|
||||
{
|
||||
/* Queue has some stale entries, remove them to make space for more
|
||||
* events. */
|
||||
for(pos = 0;pos < queue->pos;pos++)
|
||||
{
|
||||
if(queue->events[pos].event == SYSEX_EVENT)
|
||||
{
|
||||
free(queue->events[pos].param.sysex.data);
|
||||
queue->events[pos].param.sysex.data = NULL;
|
||||
}
|
||||
}
|
||||
memmove(&queue->events[0], &queue->events[queue->pos],
|
||||
(queue->size-queue->pos)*sizeof(queue->events[0]));
|
||||
queue->size -= queue->pos;
|
||||
queue->pos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Queue is full, double the allocated space. */
|
||||
void *temp = NULL;
|
||||
ALsizei newsize;
|
||||
|
||||
newsize = (queue->maxsize ? (queue->maxsize<<1) : 16);
|
||||
if(newsize > queue->maxsize)
|
||||
temp = realloc(queue->events, newsize * sizeof(queue->events[0]));
|
||||
if(!temp)
|
||||
return AL_OUT_OF_MEMORY;
|
||||
|
||||
queue->events = temp;
|
||||
queue->maxsize = newsize;
|
||||
}
|
||||
}
|
||||
|
||||
pos = queue->pos;
|
||||
if(queue->size > 0)
|
||||
{
|
||||
ALsizei high = queue->size - 1;
|
||||
while(pos < high)
|
||||
{
|
||||
ALsizei mid = pos + (high-pos)/2;
|
||||
if(queue->events[mid].time < evt->time)
|
||||
pos = mid + 1;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
while(pos < queue->size && queue->events[pos].time <= evt->time)
|
||||
pos++;
|
||||
|
||||
if(pos < queue->size)
|
||||
memmove(&queue->events[pos+1], &queue->events[pos],
|
||||
(queue->size-pos)*sizeof(queue->events[0]));
|
||||
}
|
||||
|
||||
queue->events[pos] = *evt;
|
||||
queue->size++;
|
||||
|
||||
return AL_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void MidiSynth_Construct(MidiSynth *self, ALCdevice *device)
|
||||
{
|
||||
InitEvtQueue(&self->EventQueue);
|
||||
|
||||
RWLockInit(&self->Lock);
|
||||
|
||||
self->Soundfonts = NULL;
|
||||
self->NumSoundfonts = 0;
|
||||
|
||||
self->Gain = 1.0f;
|
||||
self->State = AL_INITIAL;
|
||||
|
||||
self->ClockBase = 0;
|
||||
self->SamplesDone = 0;
|
||||
self->SampleRate = device->Frequency;
|
||||
}
|
||||
|
||||
void MidiSynth_Destruct(MidiSynth *self)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumSoundfonts;i++)
|
||||
DecrementRef(&self->Soundfonts[i]->ref);
|
||||
free(self->Soundfonts);
|
||||
self->Soundfonts = NULL;
|
||||
self->NumSoundfonts = 0;
|
||||
|
||||
ResetEvtQueue(&self->EventQueue);
|
||||
}
|
||||
|
||||
|
||||
ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids)
|
||||
{
|
||||
ALCdevice *device = context->Device;
|
||||
ALsoundfont **sfonts;
|
||||
ALsizei i;
|
||||
|
||||
if(self->State != AL_INITIAL && self->State != AL_STOPPED)
|
||||
return AL_INVALID_OPERATION;
|
||||
|
||||
sfonts = calloc(1, count * sizeof(sfonts[0]));
|
||||
if(!sfonts) return AL_OUT_OF_MEMORY;
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
if(ids[i] == 0)
|
||||
sfonts[i] = ALsoundfont_getDefSoundfont(context);
|
||||
else if(!(sfonts[i]=LookupSfont(device, ids[i])))
|
||||
{
|
||||
free(sfonts);
|
||||
return AL_INVALID_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
IncrementRef(&sfonts[i]->ref);
|
||||
sfonts = ExchangePtr((XchgPtr*)&self->Soundfonts, sfonts);
|
||||
count = ExchangeInt(&self->NumSoundfonts, count);
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
DecrementRef(&sfonts[i]->ref);
|
||||
free(sfonts);
|
||||
|
||||
return AL_NO_ERROR;
|
||||
}
|
||||
|
||||
extern inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain);
|
||||
extern inline ALfloat MidiSynth_getGain(const MidiSynth *self);
|
||||
extern inline void MidiSynth_setState(MidiSynth *self, ALenum state);
|
||||
extern inline ALenum MidiSynth_getState(const MidiSynth *self);
|
||||
|
||||
void MidiSynth_stop(MidiSynth *self)
|
||||
{
|
||||
ResetEvtQueue(&self->EventQueue);
|
||||
|
||||
self->ClockBase = 0;
|
||||
self->SamplesDone = 0;
|
||||
}
|
||||
|
||||
extern inline void MidiSynth_reset(MidiSynth *self);
|
||||
extern inline ALuint64 MidiSynth_getTime(const MidiSynth *self);
|
||||
extern inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self);
|
||||
|
||||
void MidiSynth_setSampleRate(MidiSynth *self, ALuint srate)
|
||||
{
|
||||
if(self->SampleRate != srate)
|
||||
{
|
||||
self->ClockBase += self->SamplesDone * MIDI_CLOCK_RES / self->SampleRate;
|
||||
self->SamplesDone = 0;
|
||||
self->SampleRate = srate;
|
||||
}
|
||||
}
|
||||
|
||||
extern inline void MidiSynth_update(MidiSynth *self, ALCdevice *device);
|
||||
|
||||
ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2)
|
||||
{
|
||||
MidiEvent entry;
|
||||
entry.time = time;
|
||||
entry.event = event;
|
||||
entry.param.val[0] = param1;
|
||||
entry.param.val[1] = param2;
|
||||
return InsertEvtQueue(&self->EventQueue, &entry);
|
||||
}
|
||||
|
||||
ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size)
|
||||
{
|
||||
MidiEvent entry;
|
||||
ALenum err;
|
||||
|
||||
entry.time = time;
|
||||
entry.event = SYSEX_EVENT;
|
||||
entry.param.sysex.size = size;
|
||||
entry.param.sysex.data = malloc(size);
|
||||
if(!entry.param.sysex.data)
|
||||
return AL_OUT_OF_MEMORY;
|
||||
memcpy(entry.param.sysex.data, data, size);
|
||||
|
||||
err = InsertEvtQueue(&self->EventQueue, &entry);
|
||||
if(err != AL_NO_ERROR)
|
||||
free(entry.param.sysex.data);
|
||||
return err;
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
#ifndef AL_MIDI_BASE_H
|
||||
#define AL_MIDI_BASE_H
|
||||
|
||||
#include "alMain.h"
|
||||
#include "atomic.h"
|
||||
#include "evtqueue.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ALsoundfont;
|
||||
|
||||
typedef size_t (*ReaderCb)(void *ptr, size_t size, void *stream);
|
||||
typedef struct Reader {
|
||||
ReaderCb cb;
|
||||
void *ptr;
|
||||
int error;
|
||||
} Reader;
|
||||
inline size_t Reader_read(Reader *self, void *buf, size_t len)
|
||||
{
|
||||
size_t got = (!self->error) ? self->cb(buf, len, self->ptr) : 0;
|
||||
if(got < len) self->error = 1;
|
||||
return got;
|
||||
}
|
||||
#define READERR(x_) ((x_)->error)
|
||||
|
||||
ALboolean loadSf2(Reader *stream, struct ALsoundfont *sfont, ALCcontext *context);
|
||||
|
||||
|
||||
#define MIDI_CLOCK_RES U64(1000000000)
|
||||
|
||||
|
||||
struct MidiSynthVtable;
|
||||
|
||||
typedef struct MidiSynth {
|
||||
EvtQueue EventQueue;
|
||||
|
||||
ALuint64 ClockBase;
|
||||
ALuint SamplesDone;
|
||||
ALuint SampleRate;
|
||||
|
||||
/* NOTE: This rwlock is for the state and soundfont. The EventQueue and
|
||||
* related must instead use the device lock as they're used in the mixer
|
||||
* thread.
|
||||
*/
|
||||
RWLock Lock;
|
||||
|
||||
struct ALsoundfont **Soundfonts;
|
||||
ALsizei NumSoundfonts;
|
||||
|
||||
volatile ALfloat Gain;
|
||||
volatile ALenum State;
|
||||
|
||||
const struct MidiSynthVtable *vtbl;
|
||||
} MidiSynth;
|
||||
|
||||
void MidiSynth_Construct(MidiSynth *self, ALCdevice *device);
|
||||
void MidiSynth_Destruct(MidiSynth *self);
|
||||
ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
|
||||
inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain) { self->Gain = gain; }
|
||||
inline ALfloat MidiSynth_getGain(const MidiSynth *self) { return self->Gain; }
|
||||
inline void MidiSynth_setState(MidiSynth *self, ALenum state) { ExchangeInt(&self->State, state); }
|
||||
inline ALenum MidiSynth_getState(const MidiSynth *self) { return self->State; }
|
||||
void MidiSynth_stop(MidiSynth *self);
|
||||
inline void MidiSynth_reset(MidiSynth *self) { MidiSynth_stop(self); }
|
||||
inline ALuint64 MidiSynth_getTime(const MidiSynth *self)
|
||||
{ return self->ClockBase + (self->SamplesDone*MIDI_CLOCK_RES/self->SampleRate); }
|
||||
inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self)
|
||||
{
|
||||
if(self->EventQueue.pos == self->EventQueue.size)
|
||||
return UINT64_MAX;
|
||||
return self->EventQueue.events[self->EventQueue.pos].time;
|
||||
}
|
||||
void MidiSynth_setSampleRate(MidiSynth *self, ALuint srate);
|
||||
inline void MidiSynth_update(MidiSynth *self, ALCdevice *device)
|
||||
{ MidiSynth_setSampleRate(self, device->Frequency); }
|
||||
ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2);
|
||||
ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size);
|
||||
|
||||
|
||||
struct MidiSynthVtable {
|
||||
void (*const Destruct)(MidiSynth *self);
|
||||
|
||||
ALenum (*const selectSoundfonts)(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
|
||||
|
||||
void (*const setGain)(MidiSynth *self, ALfloat gain);
|
||||
|
||||
void (*const stop)(MidiSynth *self);
|
||||
void (*const reset)(MidiSynth *self);
|
||||
|
||||
void (*const update)(MidiSynth *self, ALCdevice *device);
|
||||
void (*const process)(MidiSynth *self, ALuint samples, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
|
||||
|
||||
void (*const Delete)(void *ptr);
|
||||
};
|
||||
|
||||
#define DEFINE_MIDISYNTH_VTABLE(T) \
|
||||
DECLARE_THUNK(T, MidiSynth, void, Destruct) \
|
||||
DECLARE_THUNK3(T, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*) \
|
||||
DECLARE_THUNK1(T, MidiSynth, void, setGain, ALfloat) \
|
||||
DECLARE_THUNK(T, MidiSynth, void, stop) \
|
||||
DECLARE_THUNK(T, MidiSynth, void, reset) \
|
||||
DECLARE_THUNK1(T, MidiSynth, void, update, ALCdevice*) \
|
||||
DECLARE_THUNK2(T, MidiSynth, void, process, ALuint, ALfloatBUFFERSIZE*restrict) \
|
||||
static void T##_MidiSynth_Delete(void *ptr) \
|
||||
{ T##_Delete(STATIC_UPCAST(T, MidiSynth, (MidiSynth*)ptr)); } \
|
||||
\
|
||||
static const struct MidiSynthVtable T##_MidiSynth_vtable = { \
|
||||
T##_MidiSynth_Destruct, \
|
||||
\
|
||||
T##_MidiSynth_selectSoundfonts, \
|
||||
T##_MidiSynth_setGain, \
|
||||
T##_MidiSynth_stop, \
|
||||
T##_MidiSynth_reset, \
|
||||
T##_MidiSynth_update, \
|
||||
T##_MidiSynth_process, \
|
||||
\
|
||||
T##_MidiSynth_Delete, \
|
||||
}
|
||||
|
||||
|
||||
MidiSynth *SSynth_create(ALCdevice *device);
|
||||
MidiSynth *FSynth_create(ALCdevice *device);
|
||||
MidiSynth *DSynth_create(ALCdevice *device);
|
||||
|
||||
MidiSynth *SynthCreate(ALCdevice *device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* AL_MIDI_BASE_H */
|
||||
@@ -1,76 +0,0 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alError.h"
|
||||
#include "evtqueue.h"
|
||||
#include "rwlock.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
typedef struct DSynth {
|
||||
DERIVE_FROM_TYPE(MidiSynth);
|
||||
} DSynth;
|
||||
|
||||
static void DSynth_Construct(DSynth *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(DSynth, MidiSynth, void, Destruct)
|
||||
static DECLARE_FORWARD3(DSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*)
|
||||
static DECLARE_FORWARD1(DSynth, MidiSynth, void, setGain, ALfloat)
|
||||
static DECLARE_FORWARD(DSynth, MidiSynth, void, stop)
|
||||
static DECLARE_FORWARD(DSynth, MidiSynth, void, reset)
|
||||
static DECLARE_FORWARD1(DSynth, MidiSynth, void, update, ALCdevice*)
|
||||
static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
|
||||
DECLARE_DEFAULT_ALLOCATORS(DSynth)
|
||||
DEFINE_MIDISYNTH_VTABLE(DSynth);
|
||||
|
||||
|
||||
static void DSynth_Construct(DSynth *self, ALCdevice *device)
|
||||
{
|
||||
MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
|
||||
SET_VTABLE2(DSynth, MidiSynth, self);
|
||||
}
|
||||
|
||||
|
||||
static void DSynth_processQueue(DSynth *self, ALuint64 time)
|
||||
{
|
||||
EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
|
||||
|
||||
while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
|
||||
queue->pos++;
|
||||
}
|
||||
|
||||
static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloatBUFFERSIZE*restrict UNUSED(DryBuffer))
|
||||
{
|
||||
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
|
||||
ALuint64 curtime;
|
||||
|
||||
if(synth->State != AL_PLAYING)
|
||||
return;
|
||||
|
||||
synth->SamplesDone += SamplesToDo;
|
||||
synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
|
||||
synth->SamplesDone %= synth->SampleRate;
|
||||
|
||||
curtime = MidiSynth_getTime(synth);
|
||||
DSynth_processQueue(self, maxi64(curtime-1, 0));
|
||||
}
|
||||
|
||||
|
||||
MidiSynth *DSynth_create(ALCdevice *device)
|
||||
{
|
||||
DSynth *synth = DSynth_New(sizeof(*synth));
|
||||
if(!synth)
|
||||
{
|
||||
ERR("Failed to allocate DSynth\n");
|
||||
return NULL;
|
||||
}
|
||||
memset(synth, 0, sizeof(*synth));
|
||||
DSynth_Construct(synth, device);
|
||||
return STATIC_CAST(MidiSynth, synth);
|
||||
}
|
||||
@@ -1,930 +0,0 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alError.h"
|
||||
#include "alMidi.h"
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
#include "evtqueue.h"
|
||||
#include "rwlock.h"
|
||||
|
||||
#ifdef HAVE_FLUIDSYNTH
|
||||
|
||||
#include <fluidsynth.h>
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
#define FLUID_FUNCS(MAGIC) \
|
||||
MAGIC(new_fluid_synth); \
|
||||
MAGIC(delete_fluid_synth); \
|
||||
MAGIC(new_fluid_settings); \
|
||||
MAGIC(delete_fluid_settings); \
|
||||
MAGIC(fluid_settings_setint); \
|
||||
MAGIC(fluid_settings_setnum); \
|
||||
MAGIC(fluid_synth_noteon); \
|
||||
MAGIC(fluid_synth_noteoff); \
|
||||
MAGIC(fluid_synth_program_change); \
|
||||
MAGIC(fluid_synth_pitch_bend); \
|
||||
MAGIC(fluid_synth_channel_pressure); \
|
||||
MAGIC(fluid_synth_cc); \
|
||||
MAGIC(fluid_synth_sysex); \
|
||||
MAGIC(fluid_synth_bank_select); \
|
||||
MAGIC(fluid_synth_set_channel_type); \
|
||||
MAGIC(fluid_synth_all_sounds_off); \
|
||||
MAGIC(fluid_synth_system_reset); \
|
||||
MAGIC(fluid_synth_set_gain); \
|
||||
MAGIC(fluid_synth_set_sample_rate); \
|
||||
MAGIC(fluid_synth_write_float); \
|
||||
MAGIC(fluid_synth_add_sfloader); \
|
||||
MAGIC(fluid_synth_sfload); \
|
||||
MAGIC(fluid_synth_sfunload); \
|
||||
MAGIC(fluid_synth_alloc_voice); \
|
||||
MAGIC(fluid_synth_start_voice); \
|
||||
MAGIC(fluid_voice_gen_set); \
|
||||
MAGIC(fluid_voice_add_mod); \
|
||||
MAGIC(fluid_mod_set_source1); \
|
||||
MAGIC(fluid_mod_set_source2); \
|
||||
MAGIC(fluid_mod_set_amount); \
|
||||
MAGIC(fluid_mod_set_dest);
|
||||
|
||||
void *fsynth_handle = NULL;
|
||||
#define DECL_FUNC(x) __typeof(x) *p##x
|
||||
FLUID_FUNCS(DECL_FUNC)
|
||||
#undef DECL_FUNC
|
||||
|
||||
#define new_fluid_synth pnew_fluid_synth
|
||||
#define delete_fluid_synth pdelete_fluid_synth
|
||||
#define new_fluid_settings pnew_fluid_settings
|
||||
#define delete_fluid_settings pdelete_fluid_settings
|
||||
#define fluid_settings_setint pfluid_settings_setint
|
||||
#define fluid_settings_setnum pfluid_settings_setnum
|
||||
#define fluid_synth_noteon pfluid_synth_noteon
|
||||
#define fluid_synth_noteoff pfluid_synth_noteoff
|
||||
#define fluid_synth_program_change pfluid_synth_program_change
|
||||
#define fluid_synth_pitch_bend pfluid_synth_pitch_bend
|
||||
#define fluid_synth_channel_pressure pfluid_synth_channel_pressure
|
||||
#define fluid_synth_cc pfluid_synth_cc
|
||||
#define fluid_synth_sysex pfluid_synth_sysex
|
||||
#define fluid_synth_bank_select pfluid_synth_bank_select
|
||||
#define fluid_synth_set_channel_type pfluid_synth_set_channel_type
|
||||
#define fluid_synth_all_sounds_off pfluid_synth_all_sounds_off
|
||||
#define fluid_synth_system_reset pfluid_synth_system_reset
|
||||
#define fluid_synth_set_gain pfluid_synth_set_gain
|
||||
#define fluid_synth_set_sample_rate pfluid_synth_set_sample_rate
|
||||
#define fluid_synth_write_float pfluid_synth_write_float
|
||||
#define fluid_synth_add_sfloader pfluid_synth_add_sfloader
|
||||
#define fluid_synth_sfload pfluid_synth_sfload
|
||||
#define fluid_synth_sfunload pfluid_synth_sfunload
|
||||
#define fluid_synth_alloc_voice pfluid_synth_alloc_voice
|
||||
#define fluid_synth_start_voice pfluid_synth_start_voice
|
||||
#define fluid_voice_gen_set pfluid_voice_gen_set
|
||||
#define fluid_voice_add_mod pfluid_voice_add_mod
|
||||
#define fluid_mod_set_source1 pfluid_mod_set_source1
|
||||
#define fluid_mod_set_source2 pfluid_mod_set_source2
|
||||
#define fluid_mod_set_amount pfluid_mod_set_amount
|
||||
#define fluid_mod_set_dest pfluid_mod_set_dest
|
||||
|
||||
static ALboolean LoadFSynth(void)
|
||||
{
|
||||
ALboolean ret = AL_TRUE;
|
||||
if(!fsynth_handle)
|
||||
{
|
||||
fsynth_handle = LoadLib("libfluidsynth.so.1");
|
||||
if(!fsynth_handle) return AL_FALSE;
|
||||
|
||||
#define LOAD_FUNC(x) do { \
|
||||
p##x = GetSymbol(fsynth_handle, #x); \
|
||||
if(!p##x) ret = AL_FALSE; \
|
||||
} while(0)
|
||||
FLUID_FUNCS(LOAD_FUNC)
|
||||
#undef LOAD_FUNC
|
||||
|
||||
if(ret == AL_FALSE)
|
||||
{
|
||||
CloseLib(fsynth_handle);
|
||||
fsynth_handle = NULL;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static inline ALboolean LoadFSynth(void) { return AL_TRUE; }
|
||||
#endif
|
||||
|
||||
|
||||
/* MIDI events */
|
||||
#define SYSEX_EVENT (0xF0)
|
||||
|
||||
/* MIDI controllers */
|
||||
#define CTRL_BANKSELECT_MSB (0)
|
||||
#define CTRL_BANKSELECT_LSB (32)
|
||||
#define CTRL_ALLNOTESOFF (123)
|
||||
|
||||
|
||||
static int getModInput(ALenum input)
|
||||
{
|
||||
switch(input)
|
||||
{
|
||||
case AL_ONE_SOFT: return FLUID_MOD_NONE;
|
||||
case AL_NOTEON_VELOCITY_SOFT: return FLUID_MOD_VELOCITY;
|
||||
case AL_NOTEON_KEY_SOFT: return FLUID_MOD_KEY;
|
||||
case AL_KEYPRESSURE_SOFT: return FLUID_MOD_KEYPRESSURE;
|
||||
case AL_CHANNELPRESSURE_SOFT: return FLUID_MOD_CHANNELPRESSURE;
|
||||
case AL_PITCHBEND_SOFT: return FLUID_MOD_PITCHWHEEL;
|
||||
case AL_PITCHBEND_SENSITIVITY_SOFT: return FLUID_MOD_PITCHWHEELSENS;
|
||||
}
|
||||
return input&0x7F;
|
||||
}
|
||||
|
||||
static int getModFlags(ALenum input, ALenum type, ALenum form)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case AL_UNORM_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE; break;
|
||||
case AL_UNORM_REV_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE; break;
|
||||
case AL_SNORM_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE; break;
|
||||
case AL_SNORM_REV_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE; break;
|
||||
}
|
||||
switch(form)
|
||||
{
|
||||
case AL_LINEAR_SOFT: ret |= FLUID_MOD_LINEAR; break;
|
||||
case AL_CONCAVE_SOFT: ret |= FLUID_MOD_CONCAVE; break;
|
||||
case AL_CONVEX_SOFT: ret |= FLUID_MOD_CONVEX; break;
|
||||
case AL_SWITCH_SOFT: ret |= FLUID_MOD_SWITCH; break;
|
||||
}
|
||||
/* Source input values less than 128 correspond to a MIDI continuous
|
||||
* controller. Otherwise, it's a general controller. */
|
||||
if(input < 128) ret |= FLUID_MOD_CC;
|
||||
else ret |= FLUID_MOD_GC;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum fluid_gen_type getModDest(ALenum gen)
|
||||
{
|
||||
switch(gen)
|
||||
{
|
||||
case AL_MOD_LFO_TO_PITCH_SOFT: return GEN_MODLFOTOPITCH;
|
||||
case AL_VIBRATO_LFO_TO_PITCH_SOFT: return GEN_VIBLFOTOPITCH;
|
||||
case AL_MOD_ENV_TO_PITCH_SOFT: return GEN_MODENVTOPITCH;
|
||||
case AL_FILTER_CUTOFF_SOFT: return GEN_FILTERFC;
|
||||
case AL_FILTER_RESONANCE_SOFT: return GEN_FILTERQ;
|
||||
case AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT: return GEN_MODLFOTOFILTERFC;
|
||||
case AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT: return GEN_MODENVTOFILTERFC;
|
||||
case AL_MOD_LFO_TO_VOLUME_SOFT: return GEN_MODLFOTOVOL;
|
||||
case AL_CHORUS_SEND_SOFT: return GEN_CHORUSSEND;
|
||||
case AL_REVERB_SEND_SOFT: return GEN_REVERBSEND;
|
||||
case AL_PAN_SOFT: return GEN_PAN;
|
||||
case AL_MOD_LFO_DELAY_SOFT: return GEN_MODLFODELAY;
|
||||
case AL_MOD_LFO_FREQUENCY_SOFT: return GEN_MODLFOFREQ;
|
||||
case AL_VIBRATO_LFO_DELAY_SOFT: return GEN_VIBLFODELAY;
|
||||
case AL_VIBRATO_LFO_FREQUENCY_SOFT: return GEN_VIBLFOFREQ;
|
||||
case AL_MOD_ENV_DELAYTIME_SOFT: return GEN_MODENVDELAY;
|
||||
case AL_MOD_ENV_ATTACKTIME_SOFT: return GEN_MODENVATTACK;
|
||||
case AL_MOD_ENV_HOLDTIME_SOFT: return GEN_MODENVHOLD;
|
||||
case AL_MOD_ENV_DECAYTIME_SOFT: return GEN_MODENVDECAY;
|
||||
case AL_MOD_ENV_SUSTAINVOLUME_SOFT: return GEN_MODENVSUSTAIN;
|
||||
case AL_MOD_ENV_RELEASETIME_SOFT: return GEN_MODENVRELEASE;
|
||||
case AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOMODENVHOLD;
|
||||
case AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOMODENVDECAY;
|
||||
case AL_VOLUME_ENV_DELAYTIME_SOFT: return GEN_VOLENVDELAY;
|
||||
case AL_VOLUME_ENV_ATTACKTIME_SOFT: return GEN_VOLENVATTACK;
|
||||
case AL_VOLUME_ENV_HOLDTIME_SOFT: return GEN_VOLENVHOLD;
|
||||
case AL_VOLUME_ENV_DECAYTIME_SOFT: return GEN_VOLENVDECAY;
|
||||
case AL_VOLUME_ENV_SUSTAINVOLUME_SOFT: return GEN_VOLENVSUSTAIN;
|
||||
case AL_VOLUME_ENV_RELEASETIME_SOFT: return GEN_VOLENVRELEASE;
|
||||
case AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOVOLENVHOLD;
|
||||
case AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOVOLENVDECAY;
|
||||
case AL_ATTENUATION_SOFT: return GEN_ATTENUATION;
|
||||
case AL_TUNING_COARSE_SOFT: return GEN_COARSETUNE;
|
||||
case AL_TUNING_FINE_SOFT: return GEN_FINETUNE;
|
||||
case AL_TUNING_SCALE_SOFT: return GEN_SCALETUNE;
|
||||
}
|
||||
ERR("Unhandled generator: 0x%04x\n", gen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getSf2LoopMode(ALenum mode)
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
case AL_NONE: return 0;
|
||||
case AL_LOOP_CONTINUOUS_SOFT: return 1;
|
||||
case AL_LOOP_UNTIL_RELEASE_SOFT: return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getSampleType(ALenum type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case AL_MONO_SOFT: return FLUID_SAMPLETYPE_MONO;
|
||||
case AL_RIGHT_SOFT: return FLUID_SAMPLETYPE_RIGHT;
|
||||
case AL_LEFT_SOFT: return FLUID_SAMPLETYPE_LEFT;
|
||||
}
|
||||
return FLUID_SAMPLETYPE_MONO;
|
||||
}
|
||||
|
||||
typedef struct FSample {
|
||||
DERIVE_FROM_TYPE(fluid_sample_t);
|
||||
|
||||
ALfontsound *Sound;
|
||||
|
||||
fluid_mod_t *Mods;
|
||||
ALsizei NumMods;
|
||||
} FSample;
|
||||
|
||||
static void FSample_Construct(FSample *self, ALfontsound *sound)
|
||||
{
|
||||
fluid_sample_t *sample = STATIC_CAST(fluid_sample_t, self);
|
||||
memset(sample->name, 0, sizeof(sample->name));
|
||||
sample->start = sound->Start;
|
||||
sample->end = sound->End;
|
||||
sample->loopstart = sound->LoopStart;
|
||||
sample->loopend = sound->LoopEnd;
|
||||
sample->samplerate = sound->SampleRate;
|
||||
sample->origpitch = sound->PitchKey;
|
||||
sample->pitchadj = sound->PitchCorrection;
|
||||
sample->sampletype = getSampleType(sound->SampleType);
|
||||
sample->valid = !!sound->Buffer;
|
||||
sample->data = sound->Buffer ? sound->Buffer->data : NULL;
|
||||
|
||||
sample->amplitude_that_reaches_noise_floor_is_valid = 0;
|
||||
sample->amplitude_that_reaches_noise_floor = 0.0;
|
||||
|
||||
sample->refcount = 0;
|
||||
|
||||
sample->notify = NULL;
|
||||
|
||||
sample->userdata = self;
|
||||
|
||||
self->Sound = sound;
|
||||
|
||||
self->NumMods = 0;
|
||||
self->Mods = calloc(sound->ModulatorMap.size*4, sizeof(fluid_mod_t[4]));
|
||||
if(self->Mods)
|
||||
{
|
||||
ALsizei i, j, k;
|
||||
|
||||
for(i = j = 0;i < sound->ModulatorMap.size;i++)
|
||||
{
|
||||
ALsfmodulator *mod = sound->ModulatorMap.array[i].value;
|
||||
for(k = 0;k < 4;k++,mod++)
|
||||
{
|
||||
if(mod->Dest == AL_NONE)
|
||||
continue;
|
||||
fluid_mod_set_source1(&self->Mods[j], getModInput(mod->Source[0].Input),
|
||||
getModFlags(mod->Source[0].Input, mod->Source[0].Type,
|
||||
mod->Source[0].Form));
|
||||
fluid_mod_set_source2(&self->Mods[j], getModInput(mod->Source[1].Input),
|
||||
getModFlags(mod->Source[1].Input, mod->Source[1].Type,
|
||||
mod->Source[1].Form));
|
||||
fluid_mod_set_amount(&self->Mods[j], mod->Amount);
|
||||
fluid_mod_set_dest(&self->Mods[j], getModDest(mod->Dest));
|
||||
self->Mods[j++].next = NULL;
|
||||
}
|
||||
}
|
||||
self->NumMods = j;
|
||||
}
|
||||
}
|
||||
|
||||
static void FSample_Destruct(FSample *self)
|
||||
{
|
||||
free(self->Mods);
|
||||
self->Mods = NULL;
|
||||
self->NumMods = 0;
|
||||
}
|
||||
|
||||
|
||||
typedef struct FPreset {
|
||||
DERIVE_FROM_TYPE(fluid_preset_t);
|
||||
|
||||
char Name[16];
|
||||
|
||||
int Preset;
|
||||
int Bank;
|
||||
|
||||
FSample *Samples;
|
||||
ALsizei NumSamples;
|
||||
} FPreset;
|
||||
|
||||
static char* FPreset_getName(fluid_preset_t *preset);
|
||||
static int FPreset_getPreset(fluid_preset_t *preset);
|
||||
static int FPreset_getBank(fluid_preset_t *preset);
|
||||
static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int velocity);
|
||||
|
||||
static void FPreset_Construct(FPreset *self, ALsfpreset *preset, fluid_sfont_t *parent)
|
||||
{
|
||||
STATIC_CAST(fluid_preset_t, self)->data = self;
|
||||
STATIC_CAST(fluid_preset_t, self)->sfont = parent;
|
||||
STATIC_CAST(fluid_preset_t, self)->free = NULL;
|
||||
STATIC_CAST(fluid_preset_t, self)->get_name = FPreset_getName;
|
||||
STATIC_CAST(fluid_preset_t, self)->get_banknum = FPreset_getBank;
|
||||
STATIC_CAST(fluid_preset_t, self)->get_num = FPreset_getPreset;
|
||||
STATIC_CAST(fluid_preset_t, self)->noteon = FPreset_noteOn;
|
||||
STATIC_CAST(fluid_preset_t, self)->notify = NULL;
|
||||
|
||||
memset(self->Name, 0, sizeof(self->Name));
|
||||
self->Preset = preset->Preset;
|
||||
self->Bank = preset->Bank;
|
||||
|
||||
self->NumSamples = 0;
|
||||
self->Samples = calloc(1, preset->NumSounds * sizeof(self->Samples[0]));
|
||||
if(self->Samples)
|
||||
{
|
||||
ALsizei i;
|
||||
self->NumSamples = preset->NumSounds;
|
||||
for(i = 0;i < self->NumSamples;i++)
|
||||
FSample_Construct(&self->Samples[i], preset->Sounds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void FPreset_Destruct(FPreset *self)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumSamples;i++)
|
||||
FSample_Destruct(&self->Samples[i]);
|
||||
free(self->Samples);
|
||||
self->Samples = NULL;
|
||||
self->NumSamples = 0;
|
||||
}
|
||||
|
||||
static ALboolean FPreset_canDelete(FPreset *self)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < self->NumSamples;i++)
|
||||
{
|
||||
if(fluid_sample_refcount(STATIC_CAST(fluid_sample_t, &self->Samples[i])) != 0)
|
||||
return AL_FALSE;
|
||||
}
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static char* FPreset_getName(fluid_preset_t *preset)
|
||||
{
|
||||
return ((FPreset*)preset->data)->Name;
|
||||
}
|
||||
|
||||
static int FPreset_getPreset(fluid_preset_t *preset)
|
||||
{
|
||||
return ((FPreset*)preset->data)->Preset;
|
||||
}
|
||||
|
||||
static int FPreset_getBank(fluid_preset_t *preset)
|
||||
{
|
||||
return ((FPreset*)preset->data)->Bank;
|
||||
}
|
||||
|
||||
static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int vel)
|
||||
{
|
||||
FPreset *self = ((FPreset*)preset->data);
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumSamples;i++)
|
||||
{
|
||||
FSample *sample = &self->Samples[i];
|
||||
ALfontsound *sound = sample->Sound;
|
||||
fluid_voice_t *voice;
|
||||
ALsizei m;
|
||||
|
||||
if(!(key >= sound->MinKey && key <= sound->MaxKey && vel >= sound->MinVelocity && vel <= sound->MaxVelocity))
|
||||
continue;
|
||||
|
||||
voice = fluid_synth_alloc_voice(synth, STATIC_CAST(fluid_sample_t, sample), channel, key, vel);
|
||||
if(voice == NULL) return FLUID_FAILED;
|
||||
|
||||
fluid_voice_gen_set(voice, GEN_MODLFOTOPITCH, sound->ModLfoToPitch);
|
||||
fluid_voice_gen_set(voice, GEN_VIBLFOTOPITCH, sound->VibratoLfoToPitch);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVTOPITCH, sound->ModEnvToPitch);
|
||||
fluid_voice_gen_set(voice, GEN_FILTERFC, sound->FilterCutoff);
|
||||
fluid_voice_gen_set(voice, GEN_FILTERQ, sound->FilterQ);
|
||||
fluid_voice_gen_set(voice, GEN_MODLFOTOFILTERFC, sound->ModLfoToFilterCutoff);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVTOFILTERFC, sound->ModEnvToFilterCutoff);
|
||||
fluid_voice_gen_set(voice, GEN_MODLFOTOVOL, sound->ModLfoToVolume);
|
||||
fluid_voice_gen_set(voice, GEN_CHORUSSEND, sound->ChorusSend);
|
||||
fluid_voice_gen_set(voice, GEN_REVERBSEND, sound->ReverbSend);
|
||||
fluid_voice_gen_set(voice, GEN_PAN, sound->Pan);
|
||||
fluid_voice_gen_set(voice, GEN_MODLFODELAY, sound->ModLfo.Delay);
|
||||
fluid_voice_gen_set(voice, GEN_MODLFOFREQ, sound->ModLfo.Frequency);
|
||||
fluid_voice_gen_set(voice, GEN_VIBLFODELAY, sound->VibratoLfo.Delay);
|
||||
fluid_voice_gen_set(voice, GEN_VIBLFOFREQ, sound->VibratoLfo.Frequency);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVDELAY, sound->ModEnv.DelayTime);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVATTACK, sound->ModEnv.AttackTime);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVHOLD, sound->ModEnv.HoldTime);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVDECAY, sound->ModEnv.DecayTime);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVSUSTAIN, sound->ModEnv.SustainAttn);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVRELEASE, sound->ModEnv.ReleaseTime);
|
||||
fluid_voice_gen_set(voice, GEN_KEYTOMODENVHOLD, sound->ModEnv.KeyToHoldTime);
|
||||
fluid_voice_gen_set(voice, GEN_KEYTOMODENVDECAY, sound->ModEnv.KeyToDecayTime);
|
||||
fluid_voice_gen_set(voice, GEN_VOLENVDELAY, sound->VolEnv.DelayTime);
|
||||
fluid_voice_gen_set(voice, GEN_VOLENVATTACK, sound->VolEnv.AttackTime);
|
||||
fluid_voice_gen_set(voice, GEN_VOLENVHOLD, sound->VolEnv.HoldTime);
|
||||
fluid_voice_gen_set(voice, GEN_VOLENVDECAY, sound->VolEnv.DecayTime);
|
||||
fluid_voice_gen_set(voice, GEN_VOLENVSUSTAIN, sound->VolEnv.SustainAttn);
|
||||
fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, sound->VolEnv.ReleaseTime);
|
||||
fluid_voice_gen_set(voice, GEN_KEYTOVOLENVHOLD, sound->VolEnv.KeyToHoldTime);
|
||||
fluid_voice_gen_set(voice, GEN_KEYTOVOLENVDECAY, sound->VolEnv.KeyToDecayTime);
|
||||
fluid_voice_gen_set(voice, GEN_ATTENUATION, sound->Attenuation);
|
||||
fluid_voice_gen_set(voice, GEN_COARSETUNE, sound->CoarseTuning);
|
||||
fluid_voice_gen_set(voice, GEN_FINETUNE, sound->FineTuning);
|
||||
fluid_voice_gen_set(voice, GEN_SAMPLEMODE, getSf2LoopMode(sound->LoopMode));
|
||||
fluid_voice_gen_set(voice, GEN_SCALETUNE, sound->TuningScale);
|
||||
fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, sound->ExclusiveClass);
|
||||
for(m = 0;m < sample->NumMods;m++)
|
||||
fluid_voice_add_mod(voice, &sample->Mods[m], FLUID_VOICE_OVERWRITE);
|
||||
|
||||
fluid_synth_start_voice(synth, voice);
|
||||
}
|
||||
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
||||
|
||||
typedef struct FSfont {
|
||||
DERIVE_FROM_TYPE(fluid_sfont_t);
|
||||
|
||||
char Name[16];
|
||||
|
||||
FPreset *Presets;
|
||||
ALsizei NumPresets;
|
||||
|
||||
ALsizei CurrentPos;
|
||||
} FSfont;
|
||||
|
||||
static int FSfont_free(fluid_sfont_t *sfont);
|
||||
static char* FSfont_getName(fluid_sfont_t *sfont);
|
||||
static fluid_preset_t* FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum);
|
||||
static void FSfont_iterStart(fluid_sfont_t *sfont);
|
||||
static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset);
|
||||
|
||||
static void FSfont_Construct(FSfont *self, ALsoundfont *sfont)
|
||||
{
|
||||
STATIC_CAST(fluid_sfont_t, self)->data = self;
|
||||
STATIC_CAST(fluid_sfont_t, self)->id = FLUID_FAILED;
|
||||
STATIC_CAST(fluid_sfont_t, self)->free = FSfont_free;
|
||||
STATIC_CAST(fluid_sfont_t, self)->get_name = FSfont_getName;
|
||||
STATIC_CAST(fluid_sfont_t, self)->get_preset = FSfont_getPreset;
|
||||
STATIC_CAST(fluid_sfont_t, self)->iteration_start = FSfont_iterStart;
|
||||
STATIC_CAST(fluid_sfont_t, self)->iteration_next = FSfont_iterNext;
|
||||
|
||||
memset(self->Name, 0, sizeof(self->Name));
|
||||
self->CurrentPos = 0;
|
||||
self->NumPresets = 0;
|
||||
self->Presets = calloc(1, sfont->NumPresets * sizeof(self->Presets[0]));
|
||||
if(self->Presets)
|
||||
{
|
||||
ALsizei i;
|
||||
self->NumPresets = sfont->NumPresets;
|
||||
for(i = 0;i < self->NumPresets;i++)
|
||||
FPreset_Construct(&self->Presets[i], sfont->Presets[i], STATIC_CAST(fluid_sfont_t, self));
|
||||
}
|
||||
}
|
||||
|
||||
static void FSfont_Destruct(FSfont *self)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumPresets;i++)
|
||||
FPreset_Destruct(&self->Presets[i]);
|
||||
free(self->Presets);
|
||||
self->Presets = NULL;
|
||||
self->NumPresets = 0;
|
||||
self->CurrentPos = 0;
|
||||
}
|
||||
|
||||
static int FSfont_free(fluid_sfont_t *sfont)
|
||||
{
|
||||
FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumPresets;i++)
|
||||
{
|
||||
if(!FPreset_canDelete(&self->Presets[i]))
|
||||
return 1;
|
||||
}
|
||||
|
||||
FSfont_Destruct(self);
|
||||
free(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char* FSfont_getName(fluid_sfont_t *sfont)
|
||||
{
|
||||
return STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->Name;
|
||||
}
|
||||
|
||||
static fluid_preset_t *FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum)
|
||||
{
|
||||
FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumPresets;i++)
|
||||
{
|
||||
FPreset *preset = &self->Presets[i];
|
||||
if(preset->Bank == (int)bank && preset->Preset == (int)prenum)
|
||||
return STATIC_CAST(fluid_preset_t, preset);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void FSfont_iterStart(fluid_sfont_t *sfont)
|
||||
{
|
||||
STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->CurrentPos = 0;
|
||||
}
|
||||
|
||||
static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset)
|
||||
{
|
||||
FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
|
||||
if(self->CurrentPos >= self->NumPresets)
|
||||
return 0;
|
||||
*preset = *STATIC_CAST(fluid_preset_t, &self->Presets[self->CurrentPos++]);
|
||||
preset->free = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
typedef struct FSynth {
|
||||
DERIVE_FROM_TYPE(MidiSynth);
|
||||
DERIVE_FROM_TYPE(fluid_sfloader_t);
|
||||
|
||||
fluid_settings_t *Settings;
|
||||
fluid_synth_t *Synth;
|
||||
int *FontIDs;
|
||||
ALsizei NumFontIDs;
|
||||
|
||||
ALboolean ForceGM2BankSelect;
|
||||
ALfloat GainScale;
|
||||
} FSynth;
|
||||
|
||||
static void FSynth_Construct(FSynth *self, ALCdevice *device);
|
||||
static void FSynth_Destruct(FSynth *self);
|
||||
static ALboolean FSynth_init(FSynth *self, ALCdevice *device);
|
||||
static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
|
||||
static void FSynth_setGain(FSynth *self, ALfloat gain);
|
||||
static void FSynth_stop(FSynth *self);
|
||||
static void FSynth_reset(FSynth *self);
|
||||
static void FSynth_update(FSynth *self, ALCdevice *device);
|
||||
static void FSynth_processQueue(FSynth *self, ALuint64 time);
|
||||
static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
|
||||
DECLARE_DEFAULT_ALLOCATORS(FSynth)
|
||||
DEFINE_MIDISYNTH_VTABLE(FSynth);
|
||||
|
||||
static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename);
|
||||
|
||||
|
||||
static void FSynth_Construct(FSynth *self, ALCdevice *device)
|
||||
{
|
||||
MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
|
||||
SET_VTABLE2(FSynth, MidiSynth, self);
|
||||
|
||||
STATIC_CAST(fluid_sfloader_t, self)->data = self;
|
||||
STATIC_CAST(fluid_sfloader_t, self)->free = NULL;
|
||||
STATIC_CAST(fluid_sfloader_t, self)->load = FSynth_loadSfont;
|
||||
|
||||
self->Settings = NULL;
|
||||
self->Synth = NULL;
|
||||
self->FontIDs = NULL;
|
||||
self->NumFontIDs = 0;
|
||||
self->ForceGM2BankSelect = AL_FALSE;
|
||||
self->GainScale = 0.2f;
|
||||
}
|
||||
|
||||
static void FSynth_Destruct(FSynth *self)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumFontIDs;i++)
|
||||
fluid_synth_sfunload(self->Synth, self->FontIDs[i], 0);
|
||||
free(self->FontIDs);
|
||||
self->FontIDs = NULL;
|
||||
self->NumFontIDs = 0;
|
||||
|
||||
if(self->Synth != NULL)
|
||||
delete_fluid_synth(self->Synth);
|
||||
self->Synth = NULL;
|
||||
|
||||
if(self->Settings != NULL)
|
||||
delete_fluid_settings(self->Settings);
|
||||
self->Settings = NULL;
|
||||
|
||||
MidiSynth_Destruct(STATIC_CAST(MidiSynth, self));
|
||||
}
|
||||
|
||||
static ALboolean FSynth_init(FSynth *self, ALCdevice *device)
|
||||
{
|
||||
ALfloat vol;
|
||||
|
||||
if(ConfigValueFloat("midi", "volume", &vol))
|
||||
{
|
||||
if(!(vol <= 0.0f))
|
||||
{
|
||||
ERR("MIDI volume %f clamped to 0\n", vol);
|
||||
vol = 0.0f;
|
||||
}
|
||||
self->GainScale = powf(10.0f, vol / 20.0f);
|
||||
}
|
||||
|
||||
self->Settings = new_fluid_settings();
|
||||
if(!self->Settings)
|
||||
{
|
||||
ERR("Failed to create FluidSettings\n");
|
||||
return AL_FALSE;
|
||||
}
|
||||
|
||||
fluid_settings_setint(self->Settings, "synth.polyphony", 256);
|
||||
fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale);
|
||||
fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
|
||||
|
||||
self->Synth = new_fluid_synth(self->Settings);
|
||||
if(!self->Synth)
|
||||
{
|
||||
ERR("Failed to create FluidSynth\n");
|
||||
return AL_FALSE;
|
||||
}
|
||||
|
||||
fluid_synth_add_sfloader(self->Synth, STATIC_CAST(fluid_sfloader_t, self));
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename)
|
||||
{
|
||||
FSynth *self = STATIC_UPCAST(FSynth, fluid_sfloader_t, loader);
|
||||
FSfont *sfont;
|
||||
int idx;
|
||||
|
||||
if(!filename || sscanf(filename, "_al_internal %d", &idx) != 1)
|
||||
return NULL;
|
||||
if(idx < 0 || idx >= STATIC_CAST(MidiSynth, self)->NumSoundfonts)
|
||||
{
|
||||
ERR("Received invalid soundfont index %d (max: %d)\n", idx, STATIC_CAST(MidiSynth, self)->NumSoundfonts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sfont = calloc(1, sizeof(sfont[0]));
|
||||
if(!sfont) return NULL;
|
||||
|
||||
FSfont_Construct(sfont, STATIC_CAST(MidiSynth, self)->Soundfonts[idx]);
|
||||
return STATIC_CAST(fluid_sfont_t, sfont);
|
||||
}
|
||||
|
||||
static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids)
|
||||
{
|
||||
int *fontid;
|
||||
ALenum ret;
|
||||
ALsizei i;
|
||||
|
||||
ret = MidiSynth_selectSoundfonts(STATIC_CAST(MidiSynth, self), context, count, ids);
|
||||
if(ret != AL_NO_ERROR) return ret;
|
||||
|
||||
ALCdevice_Lock(context->Device);
|
||||
for(i = 0;i < 16;i++)
|
||||
fluid_synth_all_sounds_off(self->Synth, i);
|
||||
ALCdevice_Unlock(context->Device);
|
||||
|
||||
fontid = malloc(count * sizeof(fontid[0]));
|
||||
if(fontid)
|
||||
{
|
||||
for(i = 0;i < STATIC_CAST(MidiSynth, self)->NumSoundfonts;i++)
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, sizeof(name), "_al_internal %d", i);
|
||||
|
||||
fontid[i] = fluid_synth_sfload(self->Synth, name, 0);
|
||||
if(fontid[i] == FLUID_FAILED)
|
||||
ERR("Failed to load selected soundfont %d\n", i);
|
||||
}
|
||||
|
||||
fontid = ExchangePtr((XchgPtr*)&self->FontIDs, fontid);
|
||||
count = ExchangeInt(&self->NumFontIDs, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Failed to allocate space for %d font IDs!\n", count);
|
||||
fontid = ExchangePtr((XchgPtr*)&self->FontIDs, NULL);
|
||||
count = ExchangeInt(&self->NumFontIDs, 0);
|
||||
}
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
fluid_synth_sfunload(self->Synth, fontid[i], 0);
|
||||
free(fontid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void FSynth_setGain(FSynth *self, ALfloat gain)
|
||||
{
|
||||
fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale * gain);
|
||||
fluid_synth_set_gain(self->Synth, self->GainScale * gain);
|
||||
MidiSynth_setGain(STATIC_CAST(MidiSynth, self), gain);
|
||||
}
|
||||
|
||||
|
||||
static void FSynth_stop(FSynth *self)
|
||||
{
|
||||
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
|
||||
ALuint64 curtime;
|
||||
ALsizei chan;
|
||||
|
||||
/* Make sure all pending events are processed. */
|
||||
curtime = MidiSynth_getTime(synth);
|
||||
FSynth_processQueue(self, curtime);
|
||||
|
||||
/* All notes off */
|
||||
for(chan = 0;chan < 16;chan++)
|
||||
fluid_synth_cc(self->Synth, chan, CTRL_ALLNOTESOFF, 0);
|
||||
|
||||
MidiSynth_stop(STATIC_CAST(MidiSynth, self));
|
||||
}
|
||||
|
||||
static void FSynth_reset(FSynth *self)
|
||||
{
|
||||
/* Reset to power-up status. */
|
||||
fluid_synth_system_reset(self->Synth);
|
||||
|
||||
MidiSynth_reset(STATIC_CAST(MidiSynth, self));
|
||||
}
|
||||
|
||||
|
||||
static void FSynth_update(FSynth *self, ALCdevice *device)
|
||||
{
|
||||
fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
|
||||
fluid_synth_set_sample_rate(self->Synth, device->Frequency);
|
||||
MidiSynth_update(STATIC_CAST(MidiSynth, self), device);
|
||||
}
|
||||
|
||||
|
||||
static void FSynth_processQueue(FSynth *self, ALuint64 time)
|
||||
{
|
||||
EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
|
||||
|
||||
while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
|
||||
{
|
||||
const MidiEvent *evt = &queue->events[queue->pos];
|
||||
|
||||
if(evt->event == SYSEX_EVENT)
|
||||
{
|
||||
static const ALbyte gm2_on[] = { 0x7E, 0x7F, 0x09, 0x03 };
|
||||
static const ALbyte gm2_off[] = { 0x7E, 0x7F, 0x09, 0x02 };
|
||||
int handled = 0;
|
||||
|
||||
fluid_synth_sysex(self->Synth, evt->param.sysex.data, evt->param.sysex.size, NULL, NULL, &handled, 0);
|
||||
if(!handled && evt->param.sysex.size >= (ALsizei)sizeof(gm2_on))
|
||||
{
|
||||
if(memcmp(evt->param.sysex.data, gm2_on, sizeof(gm2_on)) == 0)
|
||||
self->ForceGM2BankSelect = AL_TRUE;
|
||||
else if(memcmp(evt->param.sysex.data, gm2_off, sizeof(gm2_off)) == 0)
|
||||
self->ForceGM2BankSelect = AL_FALSE;
|
||||
}
|
||||
}
|
||||
else switch((evt->event&0xF0))
|
||||
{
|
||||
case AL_NOTEOFF_SOFT:
|
||||
fluid_synth_noteoff(self->Synth, (evt->event&0x0F), evt->param.val[0]);
|
||||
break;
|
||||
case AL_NOTEON_SOFT:
|
||||
fluid_synth_noteon(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]);
|
||||
break;
|
||||
case AL_KEYPRESSURE_SOFT:
|
||||
break;
|
||||
|
||||
case AL_CONTROLLERCHANGE_SOFT:
|
||||
if(self->ForceGM2BankSelect)
|
||||
{
|
||||
int chan = (evt->event&0x0F);
|
||||
if(evt->param.val[0] == CTRL_BANKSELECT_MSB)
|
||||
{
|
||||
if(evt->param.val[1] == 120 && (chan == 9 || chan == 10))
|
||||
fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_DRUM);
|
||||
else if(evt->param.val[1] == 121)
|
||||
fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_MELODIC);
|
||||
break;
|
||||
}
|
||||
if(evt->param.val[0] == CTRL_BANKSELECT_LSB)
|
||||
{
|
||||
fluid_synth_bank_select(self->Synth, chan, evt->param.val[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fluid_synth_cc(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]);
|
||||
break;
|
||||
case AL_PROGRAMCHANGE_SOFT:
|
||||
fluid_synth_program_change(self->Synth, (evt->event&0x0F), evt->param.val[0]);
|
||||
break;
|
||||
|
||||
case AL_CHANNELPRESSURE_SOFT:
|
||||
fluid_synth_channel_pressure(self->Synth, (evt->event&0x0F), evt->param.val[0]);
|
||||
break;
|
||||
|
||||
case AL_PITCHBEND_SOFT:
|
||||
fluid_synth_pitch_bend(self->Synth, (evt->event&0x0F), (evt->param.val[0]&0x7F) |
|
||||
((evt->param.val[1]&0x7F)<<7));
|
||||
break;
|
||||
}
|
||||
|
||||
queue->pos++;
|
||||
}
|
||||
}
|
||||
|
||||
static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE])
|
||||
{
|
||||
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
|
||||
ALenum state = synth->State;
|
||||
ALuint64 curtime;
|
||||
ALuint total = 0;
|
||||
|
||||
if(state == AL_INITIAL)
|
||||
return;
|
||||
if(state != AL_PLAYING)
|
||||
{
|
||||
fluid_synth_write_float(self->Synth, SamplesToDo, DryBuffer[FrontLeft], 0, 1,
|
||||
DryBuffer[FrontRight], 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
curtime = MidiSynth_getTime(synth);
|
||||
while(total < SamplesToDo)
|
||||
{
|
||||
ALuint64 time, diff;
|
||||
ALint tonext;
|
||||
|
||||
time = MidiSynth_getNextEvtTime(synth);
|
||||
diff = maxu64(time, curtime) - curtime;
|
||||
if(diff >= MIDI_CLOCK_RES || time == UINT64_MAX)
|
||||
{
|
||||
/* If there's no pending event, or if it's more than 1 second
|
||||
* away, do as many samples as we can. */
|
||||
tonext = INT_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Figure out how many samples until the next event. */
|
||||
tonext = (ALint)((diff*synth->SampleRate + (MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES);
|
||||
tonext -= total;
|
||||
}
|
||||
|
||||
if(tonext > 0)
|
||||
{
|
||||
ALuint todo = minu(tonext, SamplesToDo-total);
|
||||
fluid_synth_write_float(self->Synth, todo, DryBuffer[FrontLeft], total, 1,
|
||||
DryBuffer[FrontRight], total, 1);
|
||||
total += todo;
|
||||
tonext -= todo;
|
||||
}
|
||||
if(total < SamplesToDo && tonext <= 0)
|
||||
FSynth_processQueue(self, time);
|
||||
}
|
||||
|
||||
synth->SamplesDone += SamplesToDo;
|
||||
synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
|
||||
synth->SamplesDone %= synth->SampleRate;
|
||||
}
|
||||
|
||||
|
||||
MidiSynth *FSynth_create(ALCdevice *device)
|
||||
{
|
||||
FSynth *synth;
|
||||
|
||||
if(!LoadFSynth())
|
||||
return NULL;
|
||||
|
||||
synth = FSynth_New(sizeof(*synth));
|
||||
if(!synth)
|
||||
{
|
||||
ERR("Failed to allocate FSynth\n");
|
||||
return NULL;
|
||||
}
|
||||
memset(synth, 0, sizeof(*synth));
|
||||
FSynth_Construct(synth, device);
|
||||
|
||||
if(FSynth_init(synth, device) == AL_FALSE)
|
||||
{
|
||||
DELETE_OBJ(STATIC_CAST(MidiSynth, synth));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return STATIC_CAST(MidiSynth, synth);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
MidiSynth *FSynth_create(ALCdevice* UNUSED(device))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,140 +0,0 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alError.h"
|
||||
#include "evtqueue.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
|
||||
typedef struct SSynth {
|
||||
DERIVE_FROM_TYPE(MidiSynth);
|
||||
} SSynth;
|
||||
|
||||
static void SSynth_mixSamples(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
|
||||
|
||||
static void SSynth_Construct(SSynth *self, ALCdevice *device);
|
||||
static void SSynth_Destruct(SSynth *self);
|
||||
static DECLARE_FORWARD3(SSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*)
|
||||
static DECLARE_FORWARD1(SSynth, MidiSynth, void, setGain, ALfloat)
|
||||
static DECLARE_FORWARD(SSynth, MidiSynth, void, stop)
|
||||
static DECLARE_FORWARD(SSynth, MidiSynth, void, reset)
|
||||
static void SSynth_update(SSynth *self, ALCdevice *device);
|
||||
static void SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
|
||||
DECLARE_DEFAULT_ALLOCATORS(SSynth)
|
||||
DEFINE_MIDISYNTH_VTABLE(SSynth);
|
||||
|
||||
|
||||
static void SSynth_Construct(SSynth *self, ALCdevice *device)
|
||||
{
|
||||
MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
|
||||
SET_VTABLE2(SSynth, MidiSynth, self);
|
||||
}
|
||||
|
||||
static void SSynth_Destruct(SSynth* UNUSED(self))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static void SSynth_update(SSynth* UNUSED(self), ALCdevice* UNUSED(device))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static void SSynth_mixSamples(SSynth* UNUSED(self), ALuint UNUSED(SamplesToDo), ALfloatBUFFERSIZE *restrict UNUSED(DryBuffer))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static void SSynth_processQueue(SSynth *self, ALuint64 time)
|
||||
{
|
||||
EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
|
||||
|
||||
while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
|
||||
queue->pos++;
|
||||
}
|
||||
|
||||
static void SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE])
|
||||
{
|
||||
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
|
||||
ALenum state = synth->State;
|
||||
ALuint64 curtime;
|
||||
ALuint total = 0;
|
||||
|
||||
if(state == AL_INITIAL)
|
||||
return;
|
||||
if(state != AL_PLAYING)
|
||||
{
|
||||
SSynth_mixSamples(self, SamplesToDo, DryBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
curtime = MidiSynth_getTime(synth);
|
||||
while(total < SamplesToDo)
|
||||
{
|
||||
ALuint64 time, diff;
|
||||
ALint tonext;
|
||||
|
||||
time = MidiSynth_getNextEvtTime(synth);
|
||||
diff = maxu64(time, curtime) - curtime;
|
||||
if(diff >= MIDI_CLOCK_RES || time == UINT64_MAX)
|
||||
{
|
||||
/* If there's no pending event, or if it's more than 1 second
|
||||
* away, do as many samples as we can. */
|
||||
tonext = INT_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Figure out how many samples until the next event. */
|
||||
tonext = (ALint)((diff*synth->SampleRate + (MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES);
|
||||
tonext -= total;
|
||||
/* For efficiency reasons, try to mix a multiple of 64 samples
|
||||
* (~1ms @ 44.1khz) before processing the next event. */
|
||||
tonext = (tonext+63) & ~63;
|
||||
}
|
||||
|
||||
if(tonext > 0)
|
||||
{
|
||||
ALuint todo = mini(tonext, SamplesToDo-total);
|
||||
SSynth_mixSamples(self, todo, DryBuffer);
|
||||
total += todo;
|
||||
tonext -= todo;
|
||||
}
|
||||
if(total < SamplesToDo && tonext <= 0)
|
||||
SSynth_processQueue(self, time);
|
||||
}
|
||||
|
||||
synth->SamplesDone += SamplesToDo;
|
||||
synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
|
||||
synth->SamplesDone %= synth->SampleRate;
|
||||
}
|
||||
|
||||
|
||||
MidiSynth *SSynth_create(ALCdevice *device)
|
||||
{
|
||||
SSynth *synth;
|
||||
|
||||
/* This option is temporary. Once this synth is in a more usable state, a
|
||||
* more generic selector should be used. */
|
||||
if(!GetConfigValueBool("midi", "internal-synth", 0))
|
||||
{
|
||||
TRACE("Not using internal MIDI synth\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
synth = SSynth_New(sizeof(*synth));
|
||||
if(!synth)
|
||||
{
|
||||
ERR("Failed to allocate SSynth\n");
|
||||
return NULL;
|
||||
}
|
||||
SSynth_Construct(synth, device);
|
||||
return STATIC_CAST(MidiSynth, synth);
|
||||
}
|
||||
@@ -1,510 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alSource.h"
|
||||
#include "alBuffer.h"
|
||||
#include "alListener.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "mixer_defs.h"
|
||||
|
||||
|
||||
extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size);
|
||||
|
||||
|
||||
static inline HrtfMixerFunc SelectHrtfMixer(void)
|
||||
{
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return MixHrtf_SSE;
|
||||
#endif
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return MixHrtf_Neon;
|
||||
#endif
|
||||
|
||||
return MixHrtf_C;
|
||||
}
|
||||
|
||||
static inline MixerFunc SelectMixer(void)
|
||||
{
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return Mix_SSE;
|
||||
#endif
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return Mix_Neon;
|
||||
#endif
|
||||
|
||||
return Mix_C;
|
||||
}
|
||||
|
||||
static inline ResamplerFunc SelectResampler(enum Resampler Resampler, ALuint increment)
|
||||
{
|
||||
if(increment == FRACTIONONE)
|
||||
return Resample_copy32_C;
|
||||
switch(Resampler)
|
||||
{
|
||||
case PointResampler:
|
||||
return Resample_point32_C;
|
||||
case LinearResampler:
|
||||
#ifdef HAVE_SSE4_1
|
||||
if((CPUCapFlags&CPU_CAP_SSE4_1))
|
||||
return Resample_lerp32_SSE41;
|
||||
#endif
|
||||
#ifdef HAVE_SSE2
|
||||
if((CPUCapFlags&CPU_CAP_SSE2))
|
||||
return Resample_lerp32_SSE2;
|
||||
#endif
|
||||
return Resample_lerp32_C;
|
||||
case CubicResampler:
|
||||
return Resample_cubic32_C;
|
||||
case ResamplerMax:
|
||||
/* Shouldn't happen */
|
||||
break;
|
||||
}
|
||||
|
||||
return Resample_point32_C;
|
||||
}
|
||||
|
||||
|
||||
static inline ALfloat Sample_ALbyte(ALbyte val)
|
||||
{ return val * (1.0f/127.0f); }
|
||||
|
||||
static inline ALfloat Sample_ALshort(ALshort val)
|
||||
{ return val * (1.0f/32767.0f); }
|
||||
|
||||
static inline ALfloat Sample_ALfloat(ALfloat val)
|
||||
{ return val; }
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
static void Load_##T(ALfloat *dst, const T *src, ALuint srcstep, ALuint samples)\
|
||||
{ \
|
||||
ALuint i; \
|
||||
for(i = 0;i < samples;i++) \
|
||||
dst[i] = Sample_##T(src[i*srcstep]); \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALbyte)
|
||||
DECL_TEMPLATE(ALshort)
|
||||
DECL_TEMPLATE(ALfloat)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum FmtType srctype, ALuint samples)
|
||||
{
|
||||
switch(srctype)
|
||||
{
|
||||
case FmtByte:
|
||||
Load_ALbyte(dst, src, srcstep, samples);
|
||||
break;
|
||||
case FmtShort:
|
||||
Load_ALshort(dst, src, srcstep, samples);
|
||||
break;
|
||||
case FmtFloat:
|
||||
Load_ALfloat(dst, src, srcstep, samples);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void SilenceSamples(ALfloat *dst, ALuint samples)
|
||||
{
|
||||
ALuint i;
|
||||
for(i = 0;i < samples;i++)
|
||||
dst[i] = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter,
|
||||
ALfloat *restrict dst, const ALfloat *restrict src,
|
||||
ALuint numsamples, enum ActiveFilters type)
|
||||
{
|
||||
ALuint i;
|
||||
switch(type)
|
||||
{
|
||||
case AF_None:
|
||||
break;
|
||||
|
||||
case AF_LowPass:
|
||||
ALfilterState_process(lpfilter, dst, src, numsamples);
|
||||
return dst;
|
||||
case AF_HighPass:
|
||||
ALfilterState_process(hpfilter, dst, src, numsamples);
|
||||
return dst;
|
||||
|
||||
case AF_BandPass:
|
||||
for(i = 0;i < numsamples;)
|
||||
{
|
||||
ALfloat temp[64];
|
||||
ALuint todo = minu(64, numsamples-i);
|
||||
|
||||
ALfilterState_process(lpfilter, temp, src+i, todo);
|
||||
ALfilterState_process(hpfilter, dst+i, temp, todo);
|
||||
i += todo;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
|
||||
ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
{
|
||||
MixerFunc Mix;
|
||||
HrtfMixerFunc HrtfMix;
|
||||
ResamplerFunc Resample;
|
||||
ALsource *Source = src->Source;
|
||||
ALbufferlistitem *BufferListItem;
|
||||
ALuint DataPosInt, DataPosFrac;
|
||||
ALboolean Looping;
|
||||
ALuint increment;
|
||||
enum Resampler Resampler;
|
||||
ALenum State;
|
||||
ALuint OutPos;
|
||||
ALuint NumChannels;
|
||||
ALuint SampleSize;
|
||||
ALint64 DataSize64;
|
||||
ALuint chan, j;
|
||||
|
||||
/* Get source info */
|
||||
State = Source->state;
|
||||
BufferListItem = ATOMIC_LOAD(&Source->current_buffer);
|
||||
DataPosInt = Source->position;
|
||||
DataPosFrac = Source->position_fraction;
|
||||
Looping = Source->Looping;
|
||||
increment = src->Step;
|
||||
Resampler = (increment==FRACTIONONE) ? PointResampler : Source->Resampler;
|
||||
NumChannels = Source->NumChannels;
|
||||
SampleSize = Source->SampleSize;
|
||||
|
||||
Mix = SelectMixer();
|
||||
HrtfMix = SelectHrtfMixer();
|
||||
Resample = SelectResampler(Resampler, increment);
|
||||
|
||||
OutPos = 0;
|
||||
do {
|
||||
const ALuint BufferPrePadding = ResamplerPrePadding[Resampler];
|
||||
const ALuint BufferPadding = ResamplerPadding[Resampler];
|
||||
ALuint SrcBufferSize, DstBufferSize;
|
||||
|
||||
/* Figure out how many buffer samples will be needed */
|
||||
DataSize64 = SamplesToDo-OutPos;
|
||||
DataSize64 *= increment;
|
||||
DataSize64 += DataPosFrac+FRACTIONMASK;
|
||||
DataSize64 >>= FRACTIONBITS;
|
||||
DataSize64 += BufferPadding+BufferPrePadding;
|
||||
|
||||
SrcBufferSize = (ALuint)mini64(DataSize64, BUFFERSIZE);
|
||||
|
||||
/* Figure out how many samples we can actually mix from this. */
|
||||
DataSize64 = SrcBufferSize;
|
||||
DataSize64 -= BufferPadding+BufferPrePadding;
|
||||
DataSize64 <<= FRACTIONBITS;
|
||||
DataSize64 -= DataPosFrac;
|
||||
|
||||
DstBufferSize = (ALuint)((DataSize64+(increment-1)) / increment);
|
||||
DstBufferSize = minu(DstBufferSize, (SamplesToDo-OutPos));
|
||||
|
||||
/* Some mixers like having a multiple of 4, so try to give that unless
|
||||
* this is the last update. */
|
||||
if(OutPos+DstBufferSize < SamplesToDo)
|
||||
DstBufferSize &= ~3;
|
||||
|
||||
for(chan = 0;chan < NumChannels;chan++)
|
||||
{
|
||||
const ALfloat *ResampledData;
|
||||
ALfloat *SrcData = Device->SourceData;
|
||||
ALuint SrcDataSize = 0;
|
||||
|
||||
if(Source->SourceType == AL_STATIC)
|
||||
{
|
||||
const ALbuffer *ALBuffer = BufferListItem->buffer;
|
||||
const ALubyte *Data = ALBuffer->data;
|
||||
ALuint DataSize;
|
||||
ALuint pos;
|
||||
|
||||
/* If current pos is beyond the loop range, do not loop */
|
||||
if(Looping == AL_FALSE || DataPosInt >= (ALuint)ALBuffer->LoopEnd)
|
||||
{
|
||||
Looping = AL_FALSE;
|
||||
|
||||
if(DataPosInt >= BufferPrePadding)
|
||||
pos = DataPosInt - BufferPrePadding;
|
||||
else
|
||||
{
|
||||
DataSize = BufferPrePadding - DataPosInt;
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
|
||||
SilenceSamples(&SrcData[SrcDataSize], DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
/* Copy what's left to play in the source buffer, and clear the
|
||||
* rest of the temp buffer */
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, ALBuffer->SampleLen - pos);
|
||||
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize],
|
||||
NumChannels, ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
|
||||
SrcDataSize += SrcBufferSize - SrcDataSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALuint LoopStart = ALBuffer->LoopStart;
|
||||
ALuint LoopEnd = ALBuffer->LoopEnd;
|
||||
|
||||
if(DataPosInt >= LoopStart)
|
||||
{
|
||||
pos = DataPosInt-LoopStart;
|
||||
while(pos < BufferPrePadding)
|
||||
pos += LoopEnd-LoopStart;
|
||||
pos -= BufferPrePadding;
|
||||
pos += LoopStart;
|
||||
}
|
||||
else if(DataPosInt >= BufferPrePadding)
|
||||
pos = DataPosInt - BufferPrePadding;
|
||||
else
|
||||
{
|
||||
DataSize = BufferPrePadding - DataPosInt;
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
|
||||
SilenceSamples(&SrcData[SrcDataSize], DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
/* Copy what's left of this loop iteration, then copy repeats
|
||||
* of the loop section */
|
||||
DataSize = LoopEnd - pos;
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize],
|
||||
NumChannels, ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
DataSize = LoopEnd-LoopStart;
|
||||
while(SrcBufferSize > SrcDataSize)
|
||||
{
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[(LoopStart*NumChannels + chan)*SampleSize],
|
||||
NumChannels, ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Crawl the buffer queue to fill in the temp buffer */
|
||||
ALbufferlistitem *tmpiter = BufferListItem;
|
||||
ALuint pos;
|
||||
|
||||
if(DataPosInt >= BufferPrePadding)
|
||||
pos = DataPosInt - BufferPrePadding;
|
||||
else
|
||||
{
|
||||
pos = BufferPrePadding - DataPosInt;
|
||||
while(pos > 0)
|
||||
{
|
||||
ALbufferlistitem *prev;
|
||||
if((prev=tmpiter->prev) != NULL)
|
||||
tmpiter = prev;
|
||||
else if(Looping)
|
||||
{
|
||||
while(tmpiter->next)
|
||||
tmpiter = tmpiter->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALuint DataSize = minu(SrcBufferSize - SrcDataSize, pos);
|
||||
|
||||
SilenceSamples(&SrcData[SrcDataSize], DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
pos = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if(tmpiter->buffer)
|
||||
{
|
||||
if((ALuint)tmpiter->buffer->SampleLen > pos)
|
||||
{
|
||||
pos = tmpiter->buffer->SampleLen - pos;
|
||||
break;
|
||||
}
|
||||
pos -= tmpiter->buffer->SampleLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while(tmpiter && SrcBufferSize > SrcDataSize)
|
||||
{
|
||||
const ALbuffer *ALBuffer;
|
||||
if((ALBuffer=tmpiter->buffer) != NULL)
|
||||
{
|
||||
const ALubyte *Data = ALBuffer->data;
|
||||
ALuint DataSize = ALBuffer->SampleLen;
|
||||
|
||||
/* Skip the data already played */
|
||||
if(DataSize <= pos)
|
||||
pos -= DataSize;
|
||||
else
|
||||
{
|
||||
Data += (pos*NumChannels + chan)*SampleSize;
|
||||
DataSize -= pos;
|
||||
pos -= pos;
|
||||
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
LoadSamples(&SrcData[SrcDataSize], Data, NumChannels,
|
||||
ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
}
|
||||
}
|
||||
tmpiter = tmpiter->next;
|
||||
if(!tmpiter && Looping)
|
||||
tmpiter = ATOMIC_LOAD(&Source->queue);
|
||||
else if(!tmpiter)
|
||||
{
|
||||
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
|
||||
SrcDataSize += SrcBufferSize - SrcDataSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now resample, then filter and mix to the appropriate outputs. */
|
||||
ResampledData = Resample(
|
||||
&SrcData[BufferPrePadding], DataPosFrac, increment,
|
||||
Device->ResampledData, DstBufferSize
|
||||
);
|
||||
{
|
||||
DirectParams *parms = &src->Direct;
|
||||
const ALfloat *samples;
|
||||
|
||||
samples = DoFilters(
|
||||
&parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
|
||||
Device->FilteredData, ResampledData, DstBufferSize,
|
||||
parms->Filters[chan].ActiveType
|
||||
);
|
||||
if(!src->IsHrtf)
|
||||
Mix(samples, MaxChannels, parms->OutBuffer, parms->Mix.Gains[chan],
|
||||
parms->Counter, OutPos, DstBufferSize);
|
||||
else
|
||||
HrtfMix(parms->OutBuffer, samples, parms->Counter, src->Offset,
|
||||
OutPos, parms->Mix.Hrtf.IrSize, &parms->Mix.Hrtf.Params[chan],
|
||||
&parms->Mix.Hrtf.State[chan], DstBufferSize);
|
||||
}
|
||||
|
||||
for(j = 0;j < Device->NumAuxSends;j++)
|
||||
{
|
||||
SendParams *parms = &src->Send[j];
|
||||
const ALfloat *samples;
|
||||
|
||||
if(!parms->OutBuffer)
|
||||
continue;
|
||||
|
||||
samples = DoFilters(
|
||||
&parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
|
||||
Device->FilteredData, ResampledData, DstBufferSize,
|
||||
parms->Filters[chan].ActiveType
|
||||
);
|
||||
Mix(samples, 1, parms->OutBuffer, &parms->Gain,
|
||||
parms->Counter, OutPos, DstBufferSize);
|
||||
}
|
||||
}
|
||||
/* Update positions */
|
||||
DataPosFrac += increment*DstBufferSize;
|
||||
DataPosInt += DataPosFrac>>FRACTIONBITS;
|
||||
DataPosFrac &= FRACTIONMASK;
|
||||
|
||||
OutPos += DstBufferSize;
|
||||
src->Offset += DstBufferSize;
|
||||
src->Direct.Counter = maxu(src->Direct.Counter, DstBufferSize) - DstBufferSize;
|
||||
for(j = 0;j < Device->NumAuxSends;j++)
|
||||
src->Send[j].Counter = maxu(src->Send[j].Counter, DstBufferSize) - DstBufferSize;
|
||||
|
||||
/* Handle looping sources */
|
||||
while(1)
|
||||
{
|
||||
const ALbuffer *ALBuffer;
|
||||
ALuint DataSize = 0;
|
||||
ALuint LoopStart = 0;
|
||||
ALuint LoopEnd = 0;
|
||||
|
||||
if((ALBuffer=BufferListItem->buffer) != NULL)
|
||||
{
|
||||
DataSize = ALBuffer->SampleLen;
|
||||
LoopStart = ALBuffer->LoopStart;
|
||||
LoopEnd = ALBuffer->LoopEnd;
|
||||
if(LoopEnd > DataPosInt)
|
||||
break;
|
||||
}
|
||||
|
||||
if(Looping && Source->SourceType == AL_STATIC)
|
||||
{
|
||||
assert(LoopEnd > LoopStart);
|
||||
DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
|
||||
break;
|
||||
}
|
||||
|
||||
if(DataSize > DataPosInt)
|
||||
break;
|
||||
|
||||
if(!(BufferListItem=BufferListItem->next))
|
||||
{
|
||||
if(Looping)
|
||||
BufferListItem = ATOMIC_LOAD(&Source->queue);
|
||||
else
|
||||
{
|
||||
State = AL_STOPPED;
|
||||
BufferListItem = NULL;
|
||||
DataPosInt = 0;
|
||||
DataPosFrac = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DataPosInt -= DataSize;
|
||||
}
|
||||
} while(State == AL_PLAYING && OutPos < SamplesToDo);
|
||||
|
||||
/* Update source info */
|
||||
Source->state = State;
|
||||
ATOMIC_STORE(&Source->current_buffer, BufferListItem);
|
||||
Source->position = DataPosInt;
|
||||
Source->position_fraction = DataPosFrac;
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "alSource.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
|
||||
|
||||
static inline ALfloat point32(const ALfloat *vals, ALuint UNUSED(frac))
|
||||
{ return vals[0]; }
|
||||
static inline ALfloat lerp32(const ALfloat *vals, ALuint frac)
|
||||
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
|
||||
static inline ALfloat cubic32(const ALfloat *vals, ALuint frac)
|
||||
{ return cubic(vals[-1], vals[0], vals[1], vals[2], frac * (1.0f/FRACTIONONE)); }
|
||||
|
||||
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint UNUSED(frac),
|
||||
ALuint increment, ALfloat *restrict dst, ALuint numsamples)
|
||||
{
|
||||
assert(increment==FRACTIONONE);
|
||||
#if defined(HAVE_SSE) || defined(HAVE_NEON)
|
||||
/* Avoid copying the source data if it's aligned like the destination. */
|
||||
if((((intptr_t)src)&15) == (((intptr_t)dst)&15))
|
||||
return src;
|
||||
#endif
|
||||
memcpy(dst, src, numsamples*sizeof(ALfloat));
|
||||
return dst;
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Sampler) \
|
||||
const ALfloat *Resample_##Sampler##_C(const ALfloat *src, ALuint frac, \
|
||||
ALuint increment, ALfloat *restrict dst, ALuint numsamples) \
|
||||
{ \
|
||||
ALuint i; \
|
||||
for(i = 0;i < numsamples;i++) \
|
||||
{ \
|
||||
dst[i] = Sampler(src, frac); \
|
||||
\
|
||||
frac += increment; \
|
||||
src += frac>>FRACTIONBITS; \
|
||||
frac &= FRACTIONMASK; \
|
||||
} \
|
||||
return dst; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(point32)
|
||||
DECL_TEMPLATE(lerp32)
|
||||
DECL_TEMPLATE(cubic32)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
|
||||
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples)
|
||||
{
|
||||
ALuint i;
|
||||
for(i = 0;i < numsamples;i++)
|
||||
*(dst++) = ALfilterState_processSingle(filter, *(src++));
|
||||
}
|
||||
|
||||
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
const ALfloat (*restrict CoeffStep)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALuint c;
|
||||
for(c = 0;c < IrSize;c++)
|
||||
{
|
||||
const ALuint off = (Offset+c)&HRIR_MASK;
|
||||
Values[off][0] += Coeffs[c][0] * left;
|
||||
Values[off][1] += Coeffs[c][1] * right;
|
||||
Coeffs[c][0] += CoeffStep[c][0];
|
||||
Coeffs[c][1] += CoeffStep[c][1];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALuint c;
|
||||
for(c = 0;c < IrSize;c++)
|
||||
{
|
||||
const ALuint off = (Offset+c)&HRIR_MASK;
|
||||
Values[off][0] += Coeffs[c][0] * left;
|
||||
Values[off][1] += Coeffs[c][1] * right;
|
||||
}
|
||||
}
|
||||
|
||||
#define SUFFIX C
|
||||
#include "mixer_inc.c"
|
||||
#undef SUFFIX
|
||||
|
||||
|
||||
void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
|
||||
{
|
||||
ALfloat gain, step;
|
||||
ALuint c;
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALuint pos = 0;
|
||||
gain = Gains[c].Current;
|
||||
step = Gains[c].Step;
|
||||
if(step != 1.0f && Counter > 0)
|
||||
{
|
||||
for(;pos < BufferSize && pos < Counter;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain *= step;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = Gains[c].Target;
|
||||
Gains[c].Current = gain;
|
||||
}
|
||||
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
#ifndef MIXER_DEFS_H
|
||||
#define MIXER_DEFS_H
|
||||
|
||||
#include "AL/alc.h"
|
||||
#include "AL/al.h"
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
struct MixGains;
|
||||
|
||||
struct HrtfParams;
|
||||
struct HrtfState;
|
||||
|
||||
/* C resamplers */
|
||||
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_point32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_lerp32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_cubic32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
|
||||
|
||||
/* C mixers */
|
||||
void MixHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
|
||||
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
|
||||
ALuint BufferSize);
|
||||
void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
|
||||
|
||||
/* SSE mixers */
|
||||
void MixHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
|
||||
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
|
||||
ALuint BufferSize);
|
||||
void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
|
||||
|
||||
/* SSE resamplers */
|
||||
inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
pos_arr[0] = 0;
|
||||
frac_arr[0] = frac;
|
||||
for(i = 1;i < size;i++)
|
||||
{
|
||||
ALuint frac_tmp = frac_arr[i-1] + increment;
|
||||
pos_arr[i] = pos_arr[i-1] + (frac_tmp>>FRACTIONBITS);
|
||||
frac_arr[i] = frac_tmp&FRACTIONMASK;
|
||||
}
|
||||
}
|
||||
|
||||
const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples);
|
||||
const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples);
|
||||
|
||||
/* Neon mixers */
|
||||
void MixHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
|
||||
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
|
||||
ALuint BufferSize);
|
||||
void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
|
||||
|
||||
#endif /* MIXER_DEFS_H */
|
||||
@@ -1,93 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alSource.h"
|
||||
|
||||
#include "hrtf.h"
|
||||
#include "mixer_defs.h"
|
||||
#include "align.h"
|
||||
|
||||
|
||||
#define REAL_MERGE(a,b) a##b
|
||||
#define MERGE(a,b) REAL_MERGE(a,b)
|
||||
|
||||
#define MixHrtf MERGE(MixHrtf_,SUFFIX)
|
||||
|
||||
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint irSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
const ALfloat (*restrict CoeffStep)[2],
|
||||
ALfloat left, ALfloat right);
|
||||
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint irSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right);
|
||||
|
||||
|
||||
void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
|
||||
const HrtfParams *hrtfparams, HrtfState *hrtfstate, ALuint BufferSize)
|
||||
{
|
||||
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
|
||||
ALuint Delay[2];
|
||||
ALfloat left, right;
|
||||
ALuint pos;
|
||||
ALuint c;
|
||||
|
||||
for(c = 0;c < IrSize;c++)
|
||||
{
|
||||
Coeffs[c][0] = hrtfparams->Coeffs[c][0] - (hrtfparams->CoeffStep[c][0]*Counter);
|
||||
Coeffs[c][1] = hrtfparams->Coeffs[c][1] - (hrtfparams->CoeffStep[c][1]*Counter);
|
||||
}
|
||||
Delay[0] = hrtfparams->Delay[0] - (hrtfparams->DelayStep[0]*Counter);
|
||||
Delay[1] = hrtfparams->Delay[1] - (hrtfparams->DelayStep[1]*Counter);
|
||||
|
||||
for(pos = 0;pos < BufferSize && pos < Counter;pos++)
|
||||
{
|
||||
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos];
|
||||
left = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK],
|
||||
hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK],
|
||||
(Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
|
||||
right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK],
|
||||
hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK],
|
||||
(Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
|
||||
|
||||
Delay[0] += hrtfparams->DelayStep[0];
|
||||
Delay[1] += hrtfparams->DelayStep[1];
|
||||
|
||||
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
|
||||
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
|
||||
Offset++;
|
||||
|
||||
ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->CoeffStep, left, right);
|
||||
OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
OutPos++;
|
||||
}
|
||||
|
||||
Delay[0] >>= HRTFDELAY_BITS;
|
||||
Delay[1] >>= HRTFDELAY_BITS;
|
||||
for(;pos < BufferSize;pos++)
|
||||
{
|
||||
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos];
|
||||
left = hrtfstate->History[(Offset-Delay[0])&SRC_HISTORY_MASK];
|
||||
right = hrtfstate->History[(Offset-Delay[1])&SRC_HISTORY_MASK];
|
||||
|
||||
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
|
||||
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
|
||||
Offset++;
|
||||
|
||||
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right);
|
||||
OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
|
||||
OutPos++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#undef MixHrtf
|
||||
|
||||
#undef MERGE
|
||||
#undef REAL_MERGE
|
||||
@@ -1,118 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <arm_neon.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "hrtf.h"
|
||||
|
||||
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
const ALfloat (*restrict CoeffStep)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALuint c;
|
||||
float32x4_t leftright4;
|
||||
{
|
||||
float32x2_t leftright2 = vdup_n_f32(0.0);
|
||||
leftright2 = vset_lane_f32(left, leftright2, 0);
|
||||
leftright2 = vset_lane_f32(right, leftright2, 1);
|
||||
leftright4 = vcombine_f32(leftright2, leftright2);
|
||||
}
|
||||
for(c = 0;c < IrSize;c += 2)
|
||||
{
|
||||
const ALuint o0 = (Offset+c)&HRIR_MASK;
|
||||
const ALuint o1 = (o0+1)&HRIR_MASK;
|
||||
float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
|
||||
vld1_f32((float32_t*)&Values[o1][0]));
|
||||
float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
|
||||
float32x4_t deltas = vld1q_f32(&CoeffStep[c][0]);
|
||||
|
||||
vals = vmlaq_f32(vals, coefs, leftright4);
|
||||
coefs = vaddq_f32(coefs, deltas);
|
||||
|
||||
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
|
||||
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
|
||||
vst1q_f32(&Coeffs[c][0], coefs);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALuint c;
|
||||
float32x4_t leftright4;
|
||||
{
|
||||
float32x2_t leftright2 = vdup_n_f32(0.0);
|
||||
leftright2 = vset_lane_f32(left, leftright2, 0);
|
||||
leftright2 = vset_lane_f32(right, leftright2, 1);
|
||||
leftright4 = vcombine_f32(leftright2, leftright2);
|
||||
}
|
||||
for(c = 0;c < IrSize;c += 2)
|
||||
{
|
||||
const ALuint o0 = (Offset+c)&HRIR_MASK;
|
||||
const ALuint o1 = (o0+1)&HRIR_MASK;
|
||||
float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
|
||||
vld1_f32((float32_t*)&Values[o1][0]));
|
||||
float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
|
||||
|
||||
vals = vmlaq_f32(vals, coefs, leftright4);
|
||||
|
||||
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
|
||||
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define SUFFIX Neon
|
||||
#include "mixer_inc.c"
|
||||
#undef SUFFIX
|
||||
|
||||
|
||||
void MixDirect_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
|
||||
{
|
||||
ALfloat gain, step;
|
||||
float32x4_t gain4;
|
||||
ALuint c;
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALuint pos = 0;
|
||||
gain = Gains[c].Current;
|
||||
step = Gains[c].Step;
|
||||
if(step != 1.0f && Counter > 0)
|
||||
{
|
||||
for(;pos < BufferSize && pos < Counter;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain *= step;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = Gains[c].Target;
|
||||
Gains[c].Current = gain;
|
||||
/* Mix until pos is aligned with 4 or the mix is done. */
|
||||
for(;pos < BufferSize && (pos&3) != 0;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
gain4 = vdupq_n_f32(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
{
|
||||
const float32x4_t val4 = vld1q_f32(&data[pos]);
|
||||
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
|
||||
dry4 = vaddq_f32(dry4, vmulq_f32(val4, gain4));
|
||||
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef IN_IDE_PARSER
|
||||
/* KDevelop's parser won't recognize these defines that get added by the -msse
|
||||
* switch used to compile this source. Without them, xmmintrin.h fails to
|
||||
* declare anything. */
|
||||
#define __MMX__
|
||||
#define __SSE__
|
||||
#endif
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "alSource.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "mixer_defs.h"
|
||||
|
||||
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
const ALfloat (*restrict CoeffStep)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
const __m128 lrlr = _mm_setr_ps(left, right, left, right);
|
||||
__m128 coeffs, deltas, imp0, imp1;
|
||||
__m128 vals = _mm_setzero_ps();
|
||||
ALuint i;
|
||||
|
||||
if((Offset&1))
|
||||
{
|
||||
const ALuint o0 = Offset&HRIR_MASK;
|
||||
const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[0][0]);
|
||||
deltas = _mm_load_ps(&CoeffStep[0][0]);
|
||||
vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
|
||||
imp0 = _mm_mul_ps(lrlr, coeffs);
|
||||
coeffs = _mm_add_ps(coeffs, deltas);
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_store_ps(&Coeffs[0][0], coeffs);
|
||||
_mm_storel_pi((__m64*)&Values[o0][0], vals);
|
||||
for(i = 1;i < IrSize-1;i += 2)
|
||||
{
|
||||
const ALuint o2 = (Offset+i)&HRIR_MASK;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[i+1][0]);
|
||||
deltas = _mm_load_ps(&CoeffStep[i+1][0]);
|
||||
vals = _mm_load_ps(&Values[o2][0]);
|
||||
imp1 = _mm_mul_ps(lrlr, coeffs);
|
||||
coeffs = _mm_add_ps(coeffs, deltas);
|
||||
imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_store_ps(&Coeffs[i+1][0], coeffs);
|
||||
_mm_store_ps(&Values[o2][0], vals);
|
||||
imp0 = imp1;
|
||||
}
|
||||
vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
|
||||
imp0 = _mm_movehl_ps(imp0, imp0);
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_storel_pi((__m64*)&Values[o1][0], vals);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0;i < IrSize;i += 2)
|
||||
{
|
||||
const ALuint o = (Offset + i)&HRIR_MASK;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[i][0]);
|
||||
deltas = _mm_load_ps(&CoeffStep[i][0]);
|
||||
vals = _mm_load_ps(&Values[o][0]);
|
||||
imp0 = _mm_mul_ps(lrlr, coeffs);
|
||||
coeffs = _mm_add_ps(coeffs, deltas);
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_store_ps(&Coeffs[i][0], coeffs);
|
||||
_mm_store_ps(&Values[o][0], vals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
const __m128 lrlr = _mm_setr_ps(left, right, left, right);
|
||||
__m128 vals = _mm_setzero_ps();
|
||||
__m128 coeffs;
|
||||
ALuint i;
|
||||
|
||||
if((Offset&1))
|
||||
{
|
||||
const ALuint o0 = Offset&HRIR_MASK;
|
||||
const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
|
||||
__m128 imp0, imp1;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[0][0]);
|
||||
vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
|
||||
imp0 = _mm_mul_ps(lrlr, coeffs);
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_storel_pi((__m64*)&Values[o0][0], vals);
|
||||
for(i = 1;i < IrSize-1;i += 2)
|
||||
{
|
||||
const ALuint o2 = (Offset+i)&HRIR_MASK;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[i+1][0]);
|
||||
vals = _mm_load_ps(&Values[o2][0]);
|
||||
imp1 = _mm_mul_ps(lrlr, coeffs);
|
||||
imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_store_ps(&Values[o2][0], vals);
|
||||
imp0 = imp1;
|
||||
}
|
||||
vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
|
||||
imp0 = _mm_movehl_ps(imp0, imp0);
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_storel_pi((__m64*)&Values[o1][0], vals);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0;i < IrSize;i += 2)
|
||||
{
|
||||
const ALuint o = (Offset + i)&HRIR_MASK;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[i][0]);
|
||||
vals = _mm_load_ps(&Values[o][0]);
|
||||
vals = _mm_add_ps(vals, _mm_mul_ps(lrlr, coeffs));
|
||||
_mm_store_ps(&Values[o][0], vals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define SUFFIX SSE
|
||||
#include "mixer_inc.c"
|
||||
#undef SUFFIX
|
||||
|
||||
|
||||
void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
|
||||
{
|
||||
ALfloat gain, step;
|
||||
__m128 gain4, step4;
|
||||
ALuint c;
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALuint pos = 0;
|
||||
gain = Gains[c].Current;
|
||||
step = Gains[c].Step;
|
||||
if(step != 1.0f && Counter > 0)
|
||||
{
|
||||
/* Mix with applying gain steps in aligned multiples of 4. */
|
||||
if(BufferSize-pos > 3 && Counter-pos > 3)
|
||||
{
|
||||
gain4 = _mm_setr_ps(
|
||||
gain,
|
||||
gain * step,
|
||||
gain * step * step,
|
||||
gain * step * step * step
|
||||
);
|
||||
step4 = _mm_set1_ps(step * step * step * step);
|
||||
do {
|
||||
const __m128 val4 = _mm_load_ps(&data[pos]);
|
||||
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
|
||||
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
|
||||
gain4 = _mm_mul_ps(gain4, step4);
|
||||
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
|
||||
pos += 4;
|
||||
} while(BufferSize-pos > 3 && Counter-pos > 3);
|
||||
gain = _mm_cvtss_f32(gain4);
|
||||
}
|
||||
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
|
||||
for(;pos < BufferSize && pos < Counter;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain *= step;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = Gains[c].Target;
|
||||
Gains[c].Current = gain;
|
||||
/* Mix until pos is aligned with 4 or the mix is done. */
|
||||
for(;pos < BufferSize && (pos&3) != 0;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
gain4 = _mm_set1_ps(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
{
|
||||
const __m128 val4 = _mm_load_ps(&data[pos]);
|
||||
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
|
||||
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
|
||||
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2014 by Timothy Arceri <t_arceri@yahoo.com.au>.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
#include <smmintrin.h>
|
||||
|
||||
#include "alu.h"
|
||||
#include "mixer_defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples)
|
||||
{
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
|
||||
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
|
||||
alignas(16) union { ALuint i[4]; float f[4]; } pos_;
|
||||
alignas(16) union { ALuint i[4]; float f[4]; } frac_;
|
||||
__m128i frac4, pos4;
|
||||
ALuint pos;
|
||||
ALuint i;
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
|
||||
|
||||
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
|
||||
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
|
||||
|
||||
for(i = 0;numsamples-i > 3;i += 4)
|
||||
{
|
||||
const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
|
||||
const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
|
||||
|
||||
/* val1 + (val2-val1)*mu */
|
||||
const __m128 r0 = _mm_sub_ps(val2, val1);
|
||||
const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
|
||||
const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
|
||||
|
||||
_mm_store_ps(&dst[i], out);
|
||||
|
||||
frac4 = _mm_add_epi32(frac4, increment4);
|
||||
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
|
||||
frac4 = _mm_and_si128(frac4, fracMask4);
|
||||
|
||||
pos_.i[0] = _mm_extract_epi32(pos4, 0);
|
||||
pos_.i[1] = _mm_extract_epi32(pos4, 1);
|
||||
pos_.i[2] = _mm_extract_epi32(pos4, 2);
|
||||
pos_.i[3] = _mm_extract_epi32(pos4, 3);
|
||||
}
|
||||
|
||||
pos = pos_.i[0];
|
||||
frac = _mm_cvtsi128_si32(frac4);
|
||||
|
||||
for(;i < numsamples;i++)
|
||||
{
|
||||
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
@@ -1,450 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2010 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alu.h"
|
||||
|
||||
extern inline void SetGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MaxChannels]);
|
||||
|
||||
static void SetSpeakerArrangement(const char *name, ALfloat SpeakerAngle[MaxChannels],
|
||||
enum Channel Speaker2Chan[MaxChannels], ALint chans)
|
||||
{
|
||||
char *confkey, *next;
|
||||
char *layout_str;
|
||||
char *sep, *end;
|
||||
enum Channel val;
|
||||
const char *str;
|
||||
int i;
|
||||
|
||||
if(!ConfigValueStr(NULL, name, &str) && !ConfigValueStr(NULL, "layout", &str))
|
||||
return;
|
||||
|
||||
layout_str = strdup(str);
|
||||
next = confkey = layout_str;
|
||||
while(next && *next)
|
||||
{
|
||||
confkey = next;
|
||||
next = strchr(confkey, ',');
|
||||
if(next)
|
||||
{
|
||||
*next = 0;
|
||||
do {
|
||||
next++;
|
||||
} while(isspace(*next) || *next == ',');
|
||||
}
|
||||
|
||||
sep = strchr(confkey, '=');
|
||||
if(!sep || confkey == sep)
|
||||
{
|
||||
ERR("Malformed speaker key: %s\n", confkey);
|
||||
continue;
|
||||
}
|
||||
|
||||
end = sep - 1;
|
||||
while(isspace(*end) && end != confkey)
|
||||
end--;
|
||||
*(++end) = 0;
|
||||
|
||||
if(strcmp(confkey, "fl") == 0 || strcmp(confkey, "front-left") == 0)
|
||||
val = FrontLeft;
|
||||
else if(strcmp(confkey, "fr") == 0 || strcmp(confkey, "front-right") == 0)
|
||||
val = FrontRight;
|
||||
else if(strcmp(confkey, "fc") == 0 || strcmp(confkey, "front-center") == 0)
|
||||
val = FrontCenter;
|
||||
else if(strcmp(confkey, "bl") == 0 || strcmp(confkey, "back-left") == 0)
|
||||
val = BackLeft;
|
||||
else if(strcmp(confkey, "br") == 0 || strcmp(confkey, "back-right") == 0)
|
||||
val = BackRight;
|
||||
else if(strcmp(confkey, "bc") == 0 || strcmp(confkey, "back-center") == 0)
|
||||
val = BackCenter;
|
||||
else if(strcmp(confkey, "sl") == 0 || strcmp(confkey, "side-left") == 0)
|
||||
val = SideLeft;
|
||||
else if(strcmp(confkey, "sr") == 0 || strcmp(confkey, "side-right") == 0)
|
||||
val = SideRight;
|
||||
else
|
||||
{
|
||||
ERR("Unknown speaker for %s: \"%s\"\n", name, confkey);
|
||||
continue;
|
||||
}
|
||||
|
||||
*(sep++) = 0;
|
||||
while(isspace(*sep))
|
||||
sep++;
|
||||
|
||||
for(i = 0;i < chans;i++)
|
||||
{
|
||||
if(Speaker2Chan[i] == val)
|
||||
{
|
||||
long angle = strtol(sep, NULL, 10);
|
||||
if(angle >= -180 && angle <= 180)
|
||||
SpeakerAngle[i] = DEG2RAD(angle);
|
||||
else
|
||||
ERR("Invalid angle for speaker \"%s\": %ld\n", confkey, angle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(layout_str);
|
||||
layout_str = NULL;
|
||||
|
||||
for(i = 0;i < chans;i++)
|
||||
{
|
||||
int min = i;
|
||||
int i2;
|
||||
|
||||
for(i2 = i+1;i2 < chans;i2++)
|
||||
{
|
||||
if(SpeakerAngle[i2] < SpeakerAngle[min])
|
||||
min = i2;
|
||||
}
|
||||
|
||||
if(min != i)
|
||||
{
|
||||
ALfloat tmpf;
|
||||
enum Channel tmpc;
|
||||
|
||||
tmpf = SpeakerAngle[i];
|
||||
SpeakerAngle[i] = SpeakerAngle[min];
|
||||
SpeakerAngle[min] = tmpf;
|
||||
|
||||
tmpc = Speaker2Chan[i];
|
||||
Speaker2Chan[i] = Speaker2Chan[min];
|
||||
Speaker2Chan[min] = tmpc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat gains[MaxChannels])
|
||||
{
|
||||
ALfloat tmpgains[MaxChannels] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
enum Channel Speaker2Chan[MaxChannels];
|
||||
ALfloat SpeakerAngle[MaxChannels];
|
||||
ALfloat langle, rangle;
|
||||
ALfloat a;
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < device->NumChan;i++)
|
||||
Speaker2Chan[i] = device->Speaker2Chan[i];
|
||||
for(i = 0;i < device->NumChan;i++)
|
||||
SpeakerAngle[i] = device->SpeakerAngle[i];
|
||||
|
||||
/* Some easy special-cases first... */
|
||||
if(device->NumChan <= 1 || hwidth >= F_PI)
|
||||
{
|
||||
/* Full coverage for all speakers. */
|
||||
for(i = 0;i < MaxChannels;i++)
|
||||
gains[i] = 0.0f;
|
||||
for(i = 0;i < device->NumChan;i++)
|
||||
{
|
||||
enum Channel chan = Speaker2Chan[i];
|
||||
gains[chan] = ingain;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(hwidth <= 0.0f)
|
||||
{
|
||||
/* Infinitely small sound point. */
|
||||
for(i = 0;i < MaxChannels;i++)
|
||||
gains[i] = 0.0f;
|
||||
for(i = 0;i < device->NumChan-1;i++)
|
||||
{
|
||||
if(angle >= SpeakerAngle[i] && angle < SpeakerAngle[i+1])
|
||||
{
|
||||
/* Sound is between speakers i and i+1 */
|
||||
a = (angle-SpeakerAngle[i]) /
|
||||
(SpeakerAngle[i+1]-SpeakerAngle[i]);
|
||||
gains[Speaker2Chan[i]] = sqrtf(1.0f-a) * ingain;
|
||||
gains[Speaker2Chan[i+1]] = sqrtf( a) * ingain;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Sound is between last and first speakers */
|
||||
if(angle < SpeakerAngle[0])
|
||||
angle += F_2PI;
|
||||
a = (angle-SpeakerAngle[i]) /
|
||||
(F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
|
||||
gains[Speaker2Chan[i]] = sqrtf(1.0f-a) * ingain;
|
||||
gains[Speaker2Chan[0]] = sqrtf( a) * ingain;
|
||||
return;
|
||||
}
|
||||
|
||||
if(fabsf(angle)+hwidth > F_PI)
|
||||
{
|
||||
/* The coverage area would go outside of -pi...+pi. Instead, rotate the
|
||||
* speaker angles so it would be as if angle=0, and keep them wrapped
|
||||
* within -pi...+pi. */
|
||||
if(angle > 0.0f)
|
||||
{
|
||||
ALuint done;
|
||||
ALuint i = 0;
|
||||
while(i < device->NumChan && device->SpeakerAngle[i]-angle < -F_PI)
|
||||
i++;
|
||||
for(done = 0;i < device->NumChan;done++)
|
||||
{
|
||||
SpeakerAngle[done] = device->SpeakerAngle[i]-angle;
|
||||
Speaker2Chan[done] = device->Speaker2Chan[i];
|
||||
i++;
|
||||
}
|
||||
for(i = 0;done < device->NumChan;i++)
|
||||
{
|
||||
SpeakerAngle[done] = device->SpeakerAngle[i]-angle + F_2PI;
|
||||
Speaker2Chan[done] = device->Speaker2Chan[i];
|
||||
done++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* NOTE: '< device->NumChan' on the iterators is correct here since
|
||||
* we need to handle index 0. Because the iterators are unsigned,
|
||||
* they'll underflow and wrap to become 0xFFFFFFFF, which will
|
||||
* break as expected. */
|
||||
ALuint done;
|
||||
ALuint i = device->NumChan-1;
|
||||
while(i < device->NumChan && device->SpeakerAngle[i]-angle > F_PI)
|
||||
i--;
|
||||
for(done = device->NumChan-1;i < device->NumChan;done--)
|
||||
{
|
||||
SpeakerAngle[done] = device->SpeakerAngle[i]-angle;
|
||||
Speaker2Chan[done] = device->Speaker2Chan[i];
|
||||
i--;
|
||||
}
|
||||
for(i = device->NumChan-1;done < device->NumChan;i--)
|
||||
{
|
||||
SpeakerAngle[done] = device->SpeakerAngle[i]-angle - F_2PI;
|
||||
Speaker2Chan[done] = device->Speaker2Chan[i];
|
||||
done--;
|
||||
}
|
||||
}
|
||||
angle = 0.0f;
|
||||
}
|
||||
langle = angle - hwidth;
|
||||
rangle = angle + hwidth;
|
||||
|
||||
/* First speaker */
|
||||
i = 0;
|
||||
do {
|
||||
ALuint last = device->NumChan-1;
|
||||
enum Channel chan = Speaker2Chan[i];
|
||||
|
||||
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
|
||||
{
|
||||
tmpgains[chan] = 1.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(SpeakerAngle[i] < langle && SpeakerAngle[i+1] > langle)
|
||||
{
|
||||
a = (langle-SpeakerAngle[i]) /
|
||||
(SpeakerAngle[i+1]-SpeakerAngle[i]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
|
||||
}
|
||||
if(SpeakerAngle[i] > rangle)
|
||||
{
|
||||
a = (F_2PI + rangle-SpeakerAngle[last]) /
|
||||
(F_2PI + SpeakerAngle[i]-SpeakerAngle[last]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
|
||||
}
|
||||
else if(SpeakerAngle[last] < rangle)
|
||||
{
|
||||
a = (rangle-SpeakerAngle[last]) /
|
||||
(F_2PI + SpeakerAngle[i]-SpeakerAngle[last]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
|
||||
}
|
||||
} while(0);
|
||||
|
||||
for(i = 1;i < device->NumChan-1;i++)
|
||||
{
|
||||
enum Channel chan = Speaker2Chan[i];
|
||||
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
|
||||
{
|
||||
tmpgains[chan] = 1.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(SpeakerAngle[i] < langle && SpeakerAngle[i+1] > langle)
|
||||
{
|
||||
a = (langle-SpeakerAngle[i]) /
|
||||
(SpeakerAngle[i+1]-SpeakerAngle[i]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
|
||||
}
|
||||
if(SpeakerAngle[i] > rangle && SpeakerAngle[i-1] < rangle)
|
||||
{
|
||||
a = (rangle-SpeakerAngle[i-1]) /
|
||||
(SpeakerAngle[i]-SpeakerAngle[i-1]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
|
||||
}
|
||||
}
|
||||
|
||||
/* Last speaker */
|
||||
i = device->NumChan-1;
|
||||
do {
|
||||
enum Channel chan = Speaker2Chan[i];
|
||||
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
|
||||
{
|
||||
tmpgains[Speaker2Chan[i]] = 1.0f;
|
||||
continue;
|
||||
}
|
||||
if(SpeakerAngle[i] > rangle && SpeakerAngle[i-1] < rangle)
|
||||
{
|
||||
a = (rangle-SpeakerAngle[i-1]) /
|
||||
(SpeakerAngle[i]-SpeakerAngle[i-1]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
|
||||
}
|
||||
if(SpeakerAngle[i] < langle)
|
||||
{
|
||||
a = (langle-SpeakerAngle[i]) /
|
||||
(F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
|
||||
}
|
||||
else if(SpeakerAngle[0] > langle)
|
||||
{
|
||||
a = (F_2PI + langle-SpeakerAngle[i]) /
|
||||
(F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
|
||||
}
|
||||
} while(0);
|
||||
|
||||
for(i = 0;i < device->NumChan;i++)
|
||||
{
|
||||
enum Channel chan = device->Speaker2Chan[i];
|
||||
gains[chan] = sqrtf(tmpgains[chan]) * ingain;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ALvoid aluInitPanning(ALCdevice *Device)
|
||||
{
|
||||
const char *layoutname = NULL;
|
||||
enum Channel *Speaker2Chan;
|
||||
ALfloat *SpeakerAngle;
|
||||
|
||||
Speaker2Chan = Device->Speaker2Chan;
|
||||
SpeakerAngle = Device->SpeakerAngle;
|
||||
switch(Device->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
Device->NumChan = 1;
|
||||
Speaker2Chan[0] = FrontCenter;
|
||||
SpeakerAngle[0] = DEG2RAD(0.0f);
|
||||
layoutname = NULL;
|
||||
break;
|
||||
|
||||
case DevFmtStereo:
|
||||
Device->NumChan = 2;
|
||||
Speaker2Chan[0] = FrontLeft;
|
||||
Speaker2Chan[1] = FrontRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-90.0f);
|
||||
SpeakerAngle[1] = DEG2RAD( 90.0f);
|
||||
layoutname = "layout_stereo";
|
||||
break;
|
||||
|
||||
case DevFmtQuad:
|
||||
Device->NumChan = 4;
|
||||
Speaker2Chan[0] = BackLeft;
|
||||
Speaker2Chan[1] = FrontLeft;
|
||||
Speaker2Chan[2] = FrontRight;
|
||||
Speaker2Chan[3] = BackRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-135.0f);
|
||||
SpeakerAngle[1] = DEG2RAD( -45.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( 45.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 135.0f);
|
||||
layoutname = "layout_quad";
|
||||
break;
|
||||
|
||||
case DevFmtX51:
|
||||
Device->NumChan = 5;
|
||||
Speaker2Chan[0] = BackLeft;
|
||||
Speaker2Chan[1] = FrontLeft;
|
||||
Speaker2Chan[2] = FrontCenter;
|
||||
Speaker2Chan[3] = FrontRight;
|
||||
Speaker2Chan[4] = BackRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-110.0f);
|
||||
SpeakerAngle[1] = DEG2RAD( -30.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( 0.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 30.0f);
|
||||
SpeakerAngle[4] = DEG2RAD( 110.0f);
|
||||
layoutname = "layout_surround51";
|
||||
break;
|
||||
|
||||
case DevFmtX51Side:
|
||||
Device->NumChan = 5;
|
||||
Speaker2Chan[0] = SideLeft;
|
||||
Speaker2Chan[1] = FrontLeft;
|
||||
Speaker2Chan[2] = FrontCenter;
|
||||
Speaker2Chan[3] = FrontRight;
|
||||
Speaker2Chan[4] = SideRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-90.0f);
|
||||
SpeakerAngle[1] = DEG2RAD(-30.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( 0.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 30.0f);
|
||||
SpeakerAngle[4] = DEG2RAD( 90.0f);
|
||||
layoutname = "layout_side51";
|
||||
break;
|
||||
|
||||
case DevFmtX61:
|
||||
Device->NumChan = 6;
|
||||
Speaker2Chan[0] = SideLeft;
|
||||
Speaker2Chan[1] = FrontLeft;
|
||||
Speaker2Chan[2] = FrontCenter;
|
||||
Speaker2Chan[3] = FrontRight;
|
||||
Speaker2Chan[4] = SideRight;
|
||||
Speaker2Chan[5] = BackCenter;
|
||||
SpeakerAngle[0] = DEG2RAD(-90.0f);
|
||||
SpeakerAngle[1] = DEG2RAD(-30.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( 0.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 30.0f);
|
||||
SpeakerAngle[4] = DEG2RAD( 90.0f);
|
||||
SpeakerAngle[5] = DEG2RAD(180.0f);
|
||||
layoutname = "layout_surround61";
|
||||
break;
|
||||
|
||||
case DevFmtX71:
|
||||
Device->NumChan = 7;
|
||||
Speaker2Chan[0] = BackLeft;
|
||||
Speaker2Chan[1] = SideLeft;
|
||||
Speaker2Chan[2] = FrontLeft;
|
||||
Speaker2Chan[3] = FrontCenter;
|
||||
Speaker2Chan[4] = FrontRight;
|
||||
Speaker2Chan[5] = SideRight;
|
||||
Speaker2Chan[6] = BackRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-150.0f);
|
||||
SpeakerAngle[1] = DEG2RAD( -90.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( -30.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 0.0f);
|
||||
SpeakerAngle[4] = DEG2RAD( 30.0f);
|
||||
SpeakerAngle[5] = DEG2RAD( 90.0f);
|
||||
SpeakerAngle[6] = DEG2RAD( 150.0f);
|
||||
layoutname = "layout_surround71";
|
||||
break;
|
||||
}
|
||||
if(layoutname && Device->Type != Loopback)
|
||||
SetSpeakerArrangement(layoutname, SpeakerAngle, Speaker2Chan, Device->NumChan);
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
#ifndef AL_VECTOR_H
|
||||
#define AL_VECTOR_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <AL/al.h>
|
||||
|
||||
/* "Base" vector type, designed to alias with the actual vector types. */
|
||||
typedef struct vector__s {
|
||||
ALsizei Capacity;
|
||||
ALsizei Size;
|
||||
} *vector_;
|
||||
|
||||
#define TYPEDEF_VECTOR(T, N) typedef struct { \
|
||||
ALsizei Capacity; \
|
||||
ALsizei Size; \
|
||||
T Data[]; \
|
||||
} _##N; \
|
||||
typedef _##N* N; \
|
||||
typedef const _##N* const_##N;
|
||||
|
||||
#define VECTOR(T) struct { \
|
||||
ALsizei Capacity; \
|
||||
ALsizei Size; \
|
||||
T Data[]; \
|
||||
}*
|
||||
|
||||
#define VECTOR_INIT(_x) do { (_x) = NULL; } while(0)
|
||||
#define VECTOR_INIT_STATIC() NULL
|
||||
#define VECTOR_DEINIT(_x) do { free((_x)); (_x) = NULL; } while(0)
|
||||
|
||||
/* Helper to increase a vector's reserve. Do not call directly. */
|
||||
ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count, ALboolean exact);
|
||||
#define VECTOR_RESERVE(_x, _c) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c), AL_TRUE))
|
||||
|
||||
ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count);
|
||||
#define VECTOR_RESIZE(_x, _c) (vector_resize((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c)))
|
||||
|
||||
#define VECTOR_CAPACITY(_x) ((_x) ? (_x)->Capacity : 0)
|
||||
#define VECTOR_SIZE(_x) ((_x) ? (_x)->Size : 0)
|
||||
|
||||
#define VECTOR_ITER_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL)
|
||||
#define VECTOR_ITER_END(_x) ((_x) ? (_x)->Data + (_x)->Size : NULL)
|
||||
|
||||
ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend);
|
||||
#ifdef __GNUC__
|
||||
#define TYPE_CHECK(T1, T2) __builtin_types_compatible_p(T1, T2)
|
||||
#define VECTOR_INSERT(_x, _i, _s, _e) __extension__({ \
|
||||
ALboolean _r; \
|
||||
static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_i))), "Incompatible insertion iterator"); \
|
||||
static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_s))), "Incompatible insertion source type"); \
|
||||
static_assert(TYPE_CHECK(__typeof(*(_s)), __typeof(*(_e))), "Incompatible iterator sources"); \
|
||||
_r = vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e)); \
|
||||
_r; \
|
||||
})
|
||||
#else
|
||||
#define VECTOR_INSERT(_x, _i, _s, _e) (vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e)))
|
||||
#endif
|
||||
|
||||
#define VECTOR_PUSH_BACK(_x, _obj) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), VECTOR_SIZE(_x)+1, AL_FALSE) && \
|
||||
(((_x)->Data[(_x)->Size++] = (_obj)),AL_TRUE))
|
||||
#define VECTOR_POP_BACK(_x) ((void)((_x)->Size--))
|
||||
|
||||
#define VECTOR_BACK(_x) ((_x)->Data[(_x)->Size-1])
|
||||
#define VECTOR_FRONT(_x) ((_x)->Data[0])
|
||||
|
||||
#define VECTOR_ELEM(_x, _o) ((_x)->Data[(_o)])
|
||||
|
||||
#define VECTOR_FOR_EACH(_t, _x, _f) do { \
|
||||
_t *_iter = VECTOR_ITER_BEGIN((_x)); \
|
||||
_t *_end = VECTOR_ITER_END((_x)); \
|
||||
for(;_iter != _end;++_iter) \
|
||||
_f(_iter); \
|
||||
} while(0)
|
||||
|
||||
#define VECTOR_FIND_IF(_i, _t, _x, _f) do { \
|
||||
_t *_iter = VECTOR_ITER_BEGIN((_x)); \
|
||||
_t *_end = VECTOR_ITER_END((_x)); \
|
||||
for(;_iter != _end;++_iter) \
|
||||
{ \
|
||||
if(_f(_iter)) \
|
||||
break; \
|
||||
} \
|
||||
(_i) = _iter; \
|
||||
} while(0)
|
||||
|
||||
#endif /* AL_VECTOR_H */
|
||||
@@ -1,112 +0,0 @@
|
||||
#ifndef _AL_FILTER_H_
|
||||
#define _AL_FILTER_H_
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LOWPASSFREQREF (5000.0f)
|
||||
#define HIGHPASSFREQREF (250.0f)
|
||||
|
||||
|
||||
/* Filters implementation is based on the "Cookbook formulae for audio *
|
||||
* EQ biquad filter coefficients" by Robert Bristow-Johnson *
|
||||
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
|
||||
|
||||
typedef enum ALfilterType {
|
||||
/** EFX-style low-pass filter, specifying a gain and reference frequency. */
|
||||
ALfilterType_HighShelf,
|
||||
/** EFX-style high-pass filter, specifying a gain and reference frequency. */
|
||||
ALfilterType_LowShelf,
|
||||
/** Peaking filter, specifying a gain, reference frequency, and bandwidth. */
|
||||
ALfilterType_Peaking,
|
||||
|
||||
/** Low-pass cut-off filter, specifying a cut-off frequency and bandwidth. */
|
||||
ALfilterType_LowPass,
|
||||
/** High-pass cut-off filter, specifying a cut-off frequency and bandwidth. */
|
||||
ALfilterType_HighPass,
|
||||
/** Band-pass filter, specifying a center frequency and bandwidth. */
|
||||
ALfilterType_BandPass,
|
||||
} ALfilterType;
|
||||
|
||||
typedef struct ALfilterState {
|
||||
ALfloat x[2]; /* History of two last input samples */
|
||||
ALfloat y[2]; /* History of two last output samples */
|
||||
ALfloat a[3]; /* Transfer function coefficients "a" */
|
||||
ALfloat b[3]; /* Transfer function coefficients "b" */
|
||||
|
||||
void (*process)(struct ALfilterState *self, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples);
|
||||
} ALfilterState;
|
||||
#define ALfilterState_process(a, ...) ((a)->process((a), __VA_ARGS__))
|
||||
|
||||
void ALfilterState_clear(ALfilterState *filter);
|
||||
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat bandwidth);
|
||||
|
||||
inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample)
|
||||
{
|
||||
ALfloat outsmp;
|
||||
|
||||
outsmp = filter->b[0] * sample +
|
||||
filter->b[1] * filter->x[0] +
|
||||
filter->b[2] * filter->x[1] -
|
||||
filter->a[1] * filter->y[0] -
|
||||
filter->a[2] * filter->y[1];
|
||||
filter->x[1] = filter->x[0];
|
||||
filter->x[0] = sample;
|
||||
filter->y[1] = filter->y[0];
|
||||
filter->y[0] = outsmp;
|
||||
|
||||
return outsmp;
|
||||
}
|
||||
|
||||
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples);
|
||||
|
||||
|
||||
typedef struct ALfilter {
|
||||
// Filter type (AL_FILTER_NULL, ...)
|
||||
ALenum type;
|
||||
|
||||
ALfloat Gain;
|
||||
ALfloat GainHF;
|
||||
ALfloat HFReference;
|
||||
ALfloat GainLF;
|
||||
ALfloat LFReference;
|
||||
|
||||
void (*SetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint val);
|
||||
void (*SetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALint *vals);
|
||||
void (*SetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val);
|
||||
void (*SetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals);
|
||||
|
||||
void (*GetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *val);
|
||||
void (*GetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *vals);
|
||||
void (*GetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val);
|
||||
void (*GetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals);
|
||||
|
||||
/* Self ID */
|
||||
ALuint id;
|
||||
} ALfilter;
|
||||
|
||||
#define ALfilter_SetParami(x, c, p, v) ((x)->SetParami((x),(c),(p),(v)))
|
||||
#define ALfilter_SetParamiv(x, c, p, v) ((x)->SetParamiv((x),(c),(p),(v)))
|
||||
#define ALfilter_SetParamf(x, c, p, v) ((x)->SetParamf((x),(c),(p),(v)))
|
||||
#define ALfilter_SetParamfv(x, c, p, v) ((x)->SetParamfv((x),(c),(p),(v)))
|
||||
|
||||
#define ALfilter_GetParami(x, c, p, v) ((x)->GetParami((x),(c),(p),(v)))
|
||||
#define ALfilter_GetParamiv(x, c, p, v) ((x)->GetParamiv((x),(c),(p),(v)))
|
||||
#define ALfilter_GetParamf(x, c, p, v) ((x)->GetParamf((x),(c),(p),(v)))
|
||||
#define ALfilter_GetParamfv(x, c, p, v) ((x)->GetParamfv((x),(c),(p),(v)))
|
||||
|
||||
inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALfilter*)LookupUIntMapKey(&device->FilterMap, id); }
|
||||
inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALfilter*)RemoveUIntMapKey(&device->FilterMap, id); }
|
||||
|
||||
ALvoid ReleaseALFilters(ALCdevice *device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifndef _AL_LISTENER_H_
|
||||
#define _AL_LISTENER_H_
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ALlistener {
|
||||
volatile ALfloat Position[3];
|
||||
volatile ALfloat Velocity[3];
|
||||
volatile ALfloat Forward[3];
|
||||
volatile ALfloat Up[3];
|
||||
volatile ALfloat Gain;
|
||||
volatile ALfloat MetersPerUnit;
|
||||
|
||||
struct {
|
||||
ALfloat Matrix[4][4];
|
||||
ALfloat Velocity[3];
|
||||
} Params;
|
||||
} ALlistener;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,895 +0,0 @@
|
||||
#ifndef AL_MAIN_H
|
||||
#define AL_MAIN_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FENV_H
|
||||
#include <fenv.h>
|
||||
#endif
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
|
||||
#if defined(_WIN64)
|
||||
#define SZFMT "%I64u"
|
||||
#elif defined(_WIN32)
|
||||
#define SZFMT "%u"
|
||||
#else
|
||||
#define SZFMT "%zu"
|
||||
#endif
|
||||
|
||||
|
||||
#include "static_assert.h"
|
||||
#include "align.h"
|
||||
#include "atomic.h"
|
||||
#include "uintmap.h"
|
||||
#include "vector.h"
|
||||
#include "alstring.h"
|
||||
|
||||
#ifndef ALC_SOFT_HRTF
|
||||
#define ALC_SOFT_HRTF 1
|
||||
#define ALC_HRTF_SOFT 0x1992
|
||||
#endif
|
||||
|
||||
#ifndef ALC_SOFT_midi_interface
|
||||
#define ALC_SOFT_midi_interface 1
|
||||
/* Global properties */
|
||||
#define AL_MIDI_CLOCK_SOFT 0x9999
|
||||
#define AL_MIDI_STATE_SOFT 0x9986
|
||||
#define AL_MIDI_GAIN_SOFT 0x9998
|
||||
#define AL_SOUNDFONTS_SIZE_SOFT 0x9995
|
||||
#define AL_SOUNDFONTS_SOFT 0x9994
|
||||
|
||||
/* Soundfont properties */
|
||||
#define AL_PRESETS_SIZE_SOFT 0x9993
|
||||
#define AL_PRESETS_SOFT 0x9992
|
||||
|
||||
/* Preset properties */
|
||||
#define AL_MIDI_PRESET_SOFT 0x9997
|
||||
#define AL_MIDI_BANK_SOFT 0x9996
|
||||
#define AL_FONTSOUNDS_SIZE_SOFT 0x9991
|
||||
#define AL_FONTSOUNDS_SOFT 0x9990
|
||||
|
||||
/* Fontsound properties */
|
||||
/* AL_BUFFER */
|
||||
#define AL_SAMPLE_START_SOFT 0x2000
|
||||
#define AL_SAMPLE_END_SOFT 0x2001
|
||||
#define AL_SAMPLE_LOOP_START_SOFT 0x2002
|
||||
#define AL_SAMPLE_LOOP_END_SOFT 0x2003
|
||||
#define AL_SAMPLE_RATE_SOFT 0x2004
|
||||
#define AL_BASE_KEY_SOFT 0x2005
|
||||
#define AL_KEY_CORRECTION_SOFT 0x2006
|
||||
#define AL_SAMPLE_TYPE_SOFT 0x2007
|
||||
#define AL_FONTSOUND_LINK_SOFT 0x2008
|
||||
#define AL_MOD_LFO_TO_PITCH_SOFT 0x0005
|
||||
#define AL_VIBRATO_LFO_TO_PITCH_SOFT 0x0006
|
||||
#define AL_MOD_ENV_TO_PITCH_SOFT 0x0007
|
||||
#define AL_FILTER_CUTOFF_SOFT 0x0008
|
||||
#define AL_FILTER_RESONANCE_SOFT 0x0009
|
||||
#define AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT 0x000A
|
||||
#define AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT 0x000B
|
||||
#define AL_MOD_LFO_TO_VOLUME_SOFT 0x000D
|
||||
#define AL_CHORUS_SEND_SOFT 0x000F
|
||||
#define AL_REVERB_SEND_SOFT 0x0010
|
||||
#define AL_PAN_SOFT 0x0011
|
||||
#define AL_MOD_LFO_DELAY_SOFT 0x0015
|
||||
#define AL_MOD_LFO_FREQUENCY_SOFT 0x0016
|
||||
#define AL_VIBRATO_LFO_DELAY_SOFT 0x0017
|
||||
#define AL_VIBRATO_LFO_FREQUENCY_SOFT 0x0018
|
||||
#define AL_MOD_ENV_DELAYTIME_SOFT 0x0019
|
||||
#define AL_MOD_ENV_ATTACKTIME_SOFT 0x001A
|
||||
#define AL_MOD_ENV_HOLDTIME_SOFT 0x001B
|
||||
#define AL_MOD_ENV_DECAYTIME_SOFT 0x001C
|
||||
#define AL_MOD_ENV_SUSTAINVOLUME_SOFT 0x001D
|
||||
#define AL_MOD_ENV_RELEASETIME_SOFT 0x002E
|
||||
#define AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT 0x001F
|
||||
#define AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT 0x0020
|
||||
#define AL_VOLUME_ENV_DELAYTIME_SOFT 0x0021
|
||||
#define AL_VOLUME_ENV_ATTACKTIME_SOFT 0x0022
|
||||
#define AL_VOLUME_ENV_HOLDTIME_SOFT 0x0023
|
||||
#define AL_VOLUME_ENV_DECAYTIME_SOFT 0x0024
|
||||
#define AL_VOLUME_ENV_SUSTAINVOLUME_SOFT 0x0025
|
||||
#define AL_VOLUME_ENV_RELEASETIME_SOFT 0x0026
|
||||
#define AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT 0x0027
|
||||
#define AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT 0x0028
|
||||
#define AL_KEY_RANGE_SOFT 0x002B
|
||||
#define AL_VELOCITY_RANGE_SOFT 0x002C
|
||||
#define AL_ATTENUATION_SOFT 0x0030
|
||||
#define AL_TUNING_COARSE_SOFT 0x0033
|
||||
#define AL_TUNING_FINE_SOFT 0x0034
|
||||
#define AL_LOOP_MODE_SOFT 0x0036
|
||||
#define AL_TUNING_SCALE_SOFT 0x0038
|
||||
#define AL_EXCLUSIVE_CLASS_SOFT 0x0039
|
||||
|
||||
/* Sample Types */
|
||||
/* AL_MONO_SOFT */
|
||||
#define AL_RIGHT_SOFT 0x0002
|
||||
#define AL_LEFT_SOFT 0x0004
|
||||
|
||||
/* Loop Modes */
|
||||
/* AL_NONE */
|
||||
#define AL_LOOP_CONTINUOUS_SOFT 0x0001
|
||||
#define AL_LOOP_UNTIL_RELEASE_SOFT 0x0003
|
||||
|
||||
/* Fontsound modulator stage properties */
|
||||
#define AL_SOURCE0_INPUT_SOFT 0x998F
|
||||
#define AL_SOURCE0_TYPE_SOFT 0x998E
|
||||
#define AL_SOURCE0_FORM_SOFT 0x998D
|
||||
#define AL_SOURCE1_INPUT_SOFT 0x998C
|
||||
#define AL_SOURCE1_TYPE_SOFT 0x998B
|
||||
#define AL_SOURCE1_FORM_SOFT 0x998A
|
||||
#define AL_AMOUNT_SOFT 0x9989
|
||||
#define AL_TRANSFORM_OP_SOFT 0x9988
|
||||
#define AL_DESTINATION_SOFT 0x9987
|
||||
|
||||
/* Sounce Inputs */
|
||||
#define AL_ONE_SOFT 0x0080
|
||||
#define AL_NOTEON_VELOCITY_SOFT 0x0082
|
||||
#define AL_NOTEON_KEY_SOFT 0x0083
|
||||
/* AL_KEYPRESSURE_SOFT */
|
||||
/* AL_CHANNELPRESSURE_SOFT */
|
||||
/* AL_PITCHBEND_SOFT */
|
||||
#define AL_PITCHBEND_SENSITIVITY_SOFT 0x0090
|
||||
/* CC 0...127 */
|
||||
|
||||
/* Source Types */
|
||||
#define AL_UNORM_SOFT 0x0000
|
||||
#define AL_UNORM_REV_SOFT 0x0100
|
||||
#define AL_SNORM_SOFT 0x0200
|
||||
#define AL_SNORM_REV_SOFT 0x0300
|
||||
|
||||
/* Source Forms */
|
||||
#define AL_LINEAR_SOFT 0x0000
|
||||
#define AL_CONCAVE_SOFT 0x0400
|
||||
#define AL_CONVEX_SOFT 0x0800
|
||||
#define AL_SWITCH_SOFT 0x0C00
|
||||
|
||||
/* Transform Ops */
|
||||
/* AL_LINEAR_SOFT */
|
||||
#define AL_ABSOLUTE_SOFT 0x0002
|
||||
|
||||
/* Events */
|
||||
#define AL_NOTEOFF_SOFT 0x0080
|
||||
#define AL_NOTEON_SOFT 0x0090
|
||||
#define AL_KEYPRESSURE_SOFT 0x00A0
|
||||
#define AL_CONTROLLERCHANGE_SOFT 0x00B0
|
||||
#define AL_PROGRAMCHANGE_SOFT 0x00C0
|
||||
#define AL_CHANNELPRESSURE_SOFT 0x00D0
|
||||
#define AL_PITCHBEND_SOFT 0x00E0
|
||||
|
||||
typedef void (AL_APIENTRY*LPALGENSOUNDFONTSSOFT)(ALsizei n, ALuint *ids);
|
||||
typedef void (AL_APIENTRY*LPALDELETESOUNDFONTSSOFT)(ALsizei n, const ALuint *ids);
|
||||
typedef ALboolean (AL_APIENTRY*LPALISSOUNDFONTSOFT)(ALuint id);
|
||||
typedef void (AL_APIENTRY*LPALGETSOUNDFONTIVSOFT)(ALuint id, ALenum param, ALint *values);
|
||||
typedef void (AL_APIENTRY*LPALSOUNDFONTPRESETSSOFT)(ALuint id, ALsizei count, const ALuint *pids);
|
||||
typedef void (AL_APIENTRY*LPALGENPRESETSSOFT)(ALsizei n, ALuint *ids);
|
||||
typedef void (AL_APIENTRY*LPALDELETEPRESETSSOFT)(ALsizei n, const ALuint *ids);
|
||||
typedef ALboolean (AL_APIENTRY*LPALISPRESETSOFT)(ALuint id);
|
||||
typedef void (AL_APIENTRY*LPALPRESETISOFT)(ALuint id, ALenum param, ALint value);
|
||||
typedef void (AL_APIENTRY*LPALPRESETIVSOFT)(ALuint id, ALenum param, const ALint *values);
|
||||
typedef void (AL_APIENTRY*LPALPRESETFONTSOUNDSSOFT)(ALuint id, ALsizei count, const ALuint *fsids);
|
||||
typedef void (AL_APIENTRY*LPALGETPRESETIVSOFT)(ALuint id, ALenum param, ALint *values);
|
||||
typedef void (AL_APIENTRY*LPALGENFONTSOUNDSSOFT)(ALsizei n, ALuint *ids);
|
||||
typedef void (AL_APIENTRY*LPALDELETEFONTSOUNDSSOFT)(ALsizei n, const ALuint *ids);
|
||||
typedef ALboolean (AL_APIENTRY*LPALISFONTSOUNDSOFT)(ALuint id);
|
||||
typedef void (AL_APIENTRY*LPALFONTSOUNDISOFT)(ALuint id, ALenum param, ALint value);
|
||||
typedef void (AL_APIENTRY*LPALFONTSOUND2ISOFT)(ALuint id, ALenum param, ALint value1, ALint value2);
|
||||
typedef void (AL_APIENTRY*LPALFONTSOUNDIVSOFT)(ALuint id, ALenum param, const ALint *values);
|
||||
typedef void (AL_APIENTRY*LPALGETFONTSOUNDIVSOFT)(ALuint id, ALenum param, ALint *values);
|
||||
typedef void (AL_APIENTRY*LPALFONTSOUNDMOFULATORISOFT)(ALuint id, ALsizei stage, ALenum param, ALint value);
|
||||
typedef void (AL_APIENTRY*LPALGETFONTSOUNDMODULATORIVSOFT)(ALuint id, ALsizei stage, ALenum param, ALint *values);
|
||||
typedef void (AL_APIENTRY*LPALMIDISOUNDFONTSOFT)(ALuint id);
|
||||
typedef void (AL_APIENTRY*LPALMIDISOUNDFONTVSOFT)(ALsizei count, const ALuint *ids);
|
||||
typedef void (AL_APIENTRY*LPALMIDIEVENTSOFT)(ALuint64SOFT time, ALenum event, ALsizei channel, ALsizei param1, ALsizei param2);
|
||||
typedef void (AL_APIENTRY*LPALMIDISYSEXSOFT)(ALuint64SOFT time, const ALbyte *data, ALsizei size);
|
||||
typedef void (AL_APIENTRY*LPALMIDIPLAYSOFT)(void);
|
||||
typedef void (AL_APIENTRY*LPALMIDIPAUSESOFT)(void);
|
||||
typedef void (AL_APIENTRY*LPALMIDISTOPSOFT)(void);
|
||||
typedef void (AL_APIENTRY*LPALMIDIRESETSOFT)(void);
|
||||
typedef void (AL_APIENTRY*LPALMIDIGAINSOFT)(ALfloat value);
|
||||
typedef ALint64SOFT (AL_APIENTRY*LPALGETINTEGER64SOFT)(ALenum pname);
|
||||
typedef void (AL_APIENTRY*LPALGETINTEGER64VSOFT)(ALenum pname, ALint64SOFT *values);
|
||||
typedef void (AL_APIENTRY*LPALLOADSOUNDFONTSOFT)(ALuint id, size_t(*cb)(ALvoid*,size_t,ALvoid*), ALvoid *user);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
AL_API void AL_APIENTRY alGenSoundfontsSOFT(ALsizei n, ALuint *ids);
|
||||
AL_API void AL_APIENTRY alDeleteSoundfontsSOFT(ALsizei n, const ALuint *ids);
|
||||
AL_API ALboolean AL_APIENTRY alIsSoundfontSOFT(ALuint id);
|
||||
AL_API void AL_APIENTRY alGetSoundfontivSOFT(ALuint id, ALenum param, ALint *values);
|
||||
AL_API void AL_APIENTRY alSoundfontPresetsSOFT(ALuint id, ALsizei count, const ALuint *pids);
|
||||
|
||||
AL_API void AL_APIENTRY alGenPresetsSOFT(ALsizei n, ALuint *ids);
|
||||
AL_API void AL_APIENTRY alDeletePresetsSOFT(ALsizei n, const ALuint *ids);
|
||||
AL_API ALboolean AL_APIENTRY alIsPresetSOFT(ALuint id);
|
||||
AL_API void AL_APIENTRY alPresetiSOFT(ALuint id, ALenum param, ALint value);
|
||||
AL_API void AL_APIENTRY alPresetivSOFT(ALuint id, ALenum param, const ALint *values);
|
||||
AL_API void AL_APIENTRY alGetPresetivSOFT(ALuint id, ALenum param, ALint *values);
|
||||
AL_API void AL_APIENTRY alPresetFontsoundsSOFT(ALuint id, ALsizei count, const ALuint *fsids);
|
||||
|
||||
AL_API void AL_APIENTRY alGenFontsoundsSOFT(ALsizei n, ALuint *ids);
|
||||
AL_API void AL_APIENTRY alDeleteFontsoundsSOFT(ALsizei n, const ALuint *ids);
|
||||
AL_API ALboolean AL_APIENTRY alIsFontsoundSOFT(ALuint id);
|
||||
AL_API void AL_APIENTRY alFontsoundiSOFT(ALuint id, ALenum param, ALint value);
|
||||
AL_API void AL_APIENTRY alFontsound2iSOFT(ALuint id, ALenum param, ALint value1, ALint value2);
|
||||
AL_API void AL_APIENTRY alFontsoundivSOFT(ALuint id, ALenum param, const ALint *values);
|
||||
AL_API void AL_APIENTRY alGetFontsoundivSOFT(ALuint id, ALenum param, ALint *values);
|
||||
AL_API void AL_APIENTRY alFontsoundModulatoriSOFT(ALuint id, ALsizei stage, ALenum param, ALint value);
|
||||
AL_API void AL_APIENTRY alGetFontsoundModulatorivSOFT(ALuint id, ALsizei stage, ALenum param, ALint *values);
|
||||
|
||||
AL_API void AL_APIENTRY alMidiSoundfontSOFT(ALuint id);
|
||||
AL_API void AL_APIENTRY alMidiSoundfontvSOFT(ALsizei count, const ALuint *ids);
|
||||
AL_API void AL_APIENTRY alMidiEventSOFT(ALuint64SOFT time, ALenum event, ALsizei channel, ALsizei param1, ALsizei param2);
|
||||
AL_API void AL_APIENTRY alMidiSysExSOFT(ALuint64SOFT time, const ALbyte *data, ALsizei size);
|
||||
AL_API void AL_APIENTRY alMidiPlaySOFT(void);
|
||||
AL_API void AL_APIENTRY alMidiPauseSOFT(void);
|
||||
AL_API void AL_APIENTRY alMidiStopSOFT(void);
|
||||
AL_API void AL_APIENTRY alMidiResetSOFT(void);
|
||||
AL_API void AL_APIENTRY alMidiGainSOFT(ALfloat value);
|
||||
AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname);
|
||||
AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values);
|
||||
AL_API void AL_APIENTRY alLoadSoundfontSOFT(ALuint id, size_t(*cb)(ALvoid*,size_t,ALvoid*), ALvoid *user);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ALC_SOFT_device_clock
|
||||
#define ALC_SOFT_device_clock 1
|
||||
typedef int64_t ALCint64SOFT;
|
||||
typedef uint64_t ALCuint64SOFT;
|
||||
#define ALC_DEVICE_CLOCK_SOFT 0x1600
|
||||
typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef IN_IDE_PARSER
|
||||
/* KDevelop's parser doesn't recognize the C99-standard restrict keyword, but
|
||||
* recent versions (at least 4.5.1) do recognize GCC's __restrict. */
|
||||
#define restrict __restrict
|
||||
#endif
|
||||
|
||||
|
||||
typedef ALint64SOFT ALint64;
|
||||
typedef ALuint64SOFT ALuint64;
|
||||
|
||||
typedef ptrdiff_t ALintptrEXT;
|
||||
typedef ptrdiff_t ALsizeiptrEXT;
|
||||
|
||||
#ifndef U64
|
||||
#if defined(_MSC_VER)
|
||||
#define U64(x) ((ALuint64)(x##ui64))
|
||||
#elif SIZEOF_LONG == 8
|
||||
#define U64(x) ((ALuint64)(x##ul))
|
||||
#elif SIZEOF_LONG_LONG == 8
|
||||
#define U64(x) ((ALuint64)(x##ull))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef UINT64_MAX
|
||||
#define UINT64_MAX U64(18446744073709551615)
|
||||
#endif
|
||||
|
||||
#ifndef UNUSED
|
||||
#if defined(__cplusplus)
|
||||
#define UNUSED(x)
|
||||
#elif defined(__GNUC__)
|
||||
#define UNUSED(x) UNUSED_##x __attribute__((unused))
|
||||
#elif defined(__LCLINT__)
|
||||
#define UNUSED(x) /*@unused@*/ x
|
||||
#else
|
||||
#define UNUSED(x) x
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define DECL_CONST __attribute__((const))
|
||||
#define DECL_FORMAT(x, y, z) __attribute__((format(x, (y), (z))))
|
||||
#else
|
||||
#define DECL_CONST
|
||||
#define DECL_FORMAT(x, y, z)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && defined(__i386__)
|
||||
/* force_align_arg_pointer is required for proper function arguments aligning
|
||||
* when SSE code is used. Some systems (Windows, QNX) do not guarantee our
|
||||
* thread functions will be properly aligned on the stack, even though GCC may
|
||||
* generate code with the assumption that it is. */
|
||||
#define FORCE_ALIGN __attribute__((force_align_arg_pointer))
|
||||
#else
|
||||
#define FORCE_ALIGN
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_C99_VLA
|
||||
#define DECL_VLA(T, _name, _size) T _name[(_size)]
|
||||
#else
|
||||
#define DECL_VLA(T, _name, _size) T *_name = alloca((_size) * sizeof(T))
|
||||
#endif
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#ifdef MAX_PATH
|
||||
#define PATH_MAX MAX_PATH
|
||||
#else
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
static const union {
|
||||
ALuint u;
|
||||
ALubyte b[sizeof(ALuint)];
|
||||
} EndianTest = { 1 };
|
||||
#define IS_LITTLE_ENDIAN (EndianTest.b[0] == 1)
|
||||
|
||||
#define COUNTOF(x) (sizeof((x))/sizeof((x)[0]))
|
||||
|
||||
|
||||
#define DERIVE_FROM_TYPE(t) t t##_parent
|
||||
#define STATIC_CAST(to, obj) (&(obj)->to##_parent)
|
||||
#ifdef __GNUC__
|
||||
#define STATIC_UPCAST(to, from, obj) __extension__({ \
|
||||
static_assert(__builtin_types_compatible_p(from, __typeof(*(obj))), \
|
||||
"Invalid upcast object from type"); \
|
||||
(to*)((char*)(obj) - offsetof(to, from##_parent)); \
|
||||
})
|
||||
#else
|
||||
#define STATIC_UPCAST(to, from, obj) ((to*)((char*)(obj) - offsetof(to, from##_parent)))
|
||||
#endif
|
||||
|
||||
#define DECLARE_FORWARD(T1, T2, rettype, func) \
|
||||
rettype T1##_##func(T1 *obj) \
|
||||
{ return T2##_##func(STATIC_CAST(T2, obj)); }
|
||||
|
||||
#define DECLARE_FORWARD1(T1, T2, rettype, func, argtype1) \
|
||||
rettype T1##_##func(T1 *obj, argtype1 a) \
|
||||
{ return T2##_##func(STATIC_CAST(T2, obj), a); }
|
||||
|
||||
#define DECLARE_FORWARD2(T1, T2, rettype, func, argtype1, argtype2) \
|
||||
rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b) \
|
||||
{ return T2##_##func(STATIC_CAST(T2, obj), a, b); }
|
||||
|
||||
#define DECLARE_FORWARD3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \
|
||||
rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b, argtype3 c) \
|
||||
{ return T2##_##func(STATIC_CAST(T2, obj), a, b, c); }
|
||||
|
||||
|
||||
#define GET_VTABLE1(T1) (&(T1##_vtable))
|
||||
#define GET_VTABLE2(T1, T2) (&(T1##_##T2##_vtable))
|
||||
|
||||
#define SET_VTABLE1(T1, obj) ((obj)->vtbl = GET_VTABLE1(T1))
|
||||
#define SET_VTABLE2(T1, T2, obj) (STATIC_CAST(T2, obj)->vtbl = GET_VTABLE2(T1, T2))
|
||||
|
||||
#define DECLARE_THUNK(T1, T2, rettype, func) \
|
||||
static rettype T1##_##T2##_##func(T2 *obj) \
|
||||
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj)); }
|
||||
|
||||
#define DECLARE_THUNK1(T1, T2, rettype, func, argtype1) \
|
||||
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a) \
|
||||
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a); }
|
||||
|
||||
#define DECLARE_THUNK2(T1, T2, rettype, func, argtype1, argtype2) \
|
||||
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b) \
|
||||
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b); }
|
||||
|
||||
#define DECLARE_THUNK3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \
|
||||
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c) \
|
||||
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c); }
|
||||
|
||||
#define DECLARE_DEFAULT_ALLOCATORS(T) \
|
||||
static void* T##_New(size_t size) { return malloc(size); } \
|
||||
static void T##_Delete(void *ptr) { free(ptr); }
|
||||
|
||||
/* Helper to extract an argument list for VCALL. Not used directly. */
|
||||
#define EXTRACT_VCALL_ARGS(...) __VA_ARGS__))
|
||||
|
||||
/* Call a "virtual" method on an object, with arguments. */
|
||||
#define V(obj, func) ((obj)->vtbl->func((obj), EXTRACT_VCALL_ARGS
|
||||
/* Call a "virtual" method on an object, with no arguments. */
|
||||
#define V0(obj, func) ((obj)->vtbl->func((obj) EXTRACT_VCALL_ARGS
|
||||
|
||||
#define DELETE_OBJ(obj) do { \
|
||||
if((obj) != NULL) \
|
||||
{ \
|
||||
V0((obj),Destruct)(); \
|
||||
V0((obj),Delete)(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Hrtf;
|
||||
|
||||
|
||||
#define DEFAULT_OUTPUT_RATE (44100)
|
||||
#define MIN_OUTPUT_RATE (8000)
|
||||
|
||||
|
||||
/* Find the next power-of-2 for non-power-of-2 numbers. */
|
||||
inline ALuint NextPowerOf2(ALuint value)
|
||||
{
|
||||
if(value > 0)
|
||||
{
|
||||
value--;
|
||||
value |= value>>1;
|
||||
value |= value>>2;
|
||||
value |= value>>4;
|
||||
value |= value>>8;
|
||||
value |= value>>16;
|
||||
}
|
||||
return value+1;
|
||||
}
|
||||
|
||||
/* Fast float-to-int conversion. Assumes the FPU is already in round-to-zero
|
||||
* mode. */
|
||||
inline ALint fastf2i(ALfloat f)
|
||||
{
|
||||
#ifdef HAVE_LRINTF
|
||||
return lrintf(f);
|
||||
#elif defined(_MSC_VER) && defined(_M_IX86)
|
||||
ALint i;
|
||||
__asm fld f
|
||||
__asm fistp i
|
||||
return i;
|
||||
#else
|
||||
return (ALint)f;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Fast float-to-uint conversion. Assumes the FPU is already in round-to-zero
|
||||
* mode. */
|
||||
inline ALuint fastf2u(ALfloat f)
|
||||
{ return fastf2i(f); }
|
||||
|
||||
|
||||
enum DevProbe {
|
||||
ALL_DEVICE_PROBE,
|
||||
CAPTURE_DEVICE_PROBE
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ALCenum (*OpenPlayback)(ALCdevice*, const ALCchar*);
|
||||
void (*ClosePlayback)(ALCdevice*);
|
||||
ALCboolean (*ResetPlayback)(ALCdevice*);
|
||||
ALCboolean (*StartPlayback)(ALCdevice*);
|
||||
void (*StopPlayback)(ALCdevice*);
|
||||
|
||||
ALCenum (*OpenCapture)(ALCdevice*, const ALCchar*);
|
||||
void (*CloseCapture)(ALCdevice*);
|
||||
void (*StartCapture)(ALCdevice*);
|
||||
void (*StopCapture)(ALCdevice*);
|
||||
ALCenum (*CaptureSamples)(ALCdevice*, void*, ALCuint);
|
||||
ALCuint (*AvailableSamples)(ALCdevice*);
|
||||
|
||||
ALint64 (*GetLatency)(ALCdevice*);
|
||||
} BackendFuncs;
|
||||
|
||||
ALCboolean alc_solaris_init(BackendFuncs *func_list);
|
||||
void alc_solaris_deinit(void);
|
||||
void alc_solaris_probe(enum DevProbe type);
|
||||
ALCboolean alc_sndio_init(BackendFuncs *func_list);
|
||||
void alc_sndio_deinit(void);
|
||||
void alc_sndio_probe(enum DevProbe type);
|
||||
ALCboolean alcWinMMInit(BackendFuncs *FuncList);
|
||||
void alcWinMMDeinit(void);
|
||||
void alcWinMMProbe(enum DevProbe type);
|
||||
ALCboolean alc_pa_init(BackendFuncs *func_list);
|
||||
void alc_pa_deinit(void);
|
||||
void alc_pa_probe(enum DevProbe type);
|
||||
ALCboolean alc_wave_init(BackendFuncs *func_list);
|
||||
void alc_wave_deinit(void);
|
||||
void alc_wave_probe(enum DevProbe type);
|
||||
ALCboolean alc_ca_init(BackendFuncs *func_list);
|
||||
void alc_ca_deinit(void);
|
||||
void alc_ca_probe(enum DevProbe type);
|
||||
ALCboolean alc_opensl_init(BackendFuncs *func_list);
|
||||
void alc_opensl_deinit(void);
|
||||
void alc_opensl_probe(enum DevProbe type);
|
||||
ALCboolean alc_qsa_init(BackendFuncs *func_list);
|
||||
void alc_qsa_deinit(void);
|
||||
void alc_qsa_probe(enum DevProbe type);
|
||||
|
||||
struct ALCbackend;
|
||||
|
||||
|
||||
enum DistanceModel {
|
||||
InverseDistanceClamped = AL_INVERSE_DISTANCE_CLAMPED,
|
||||
LinearDistanceClamped = AL_LINEAR_DISTANCE_CLAMPED,
|
||||
ExponentDistanceClamped = AL_EXPONENT_DISTANCE_CLAMPED,
|
||||
InverseDistance = AL_INVERSE_DISTANCE,
|
||||
LinearDistance = AL_LINEAR_DISTANCE,
|
||||
ExponentDistance = AL_EXPONENT_DISTANCE,
|
||||
DisableDistance = AL_NONE,
|
||||
|
||||
DefaultDistanceModel = InverseDistanceClamped
|
||||
};
|
||||
|
||||
enum Resampler {
|
||||
PointResampler,
|
||||
LinearResampler,
|
||||
CubicResampler,
|
||||
|
||||
ResamplerMax,
|
||||
};
|
||||
|
||||
enum Channel {
|
||||
FrontLeft = 0,
|
||||
FrontRight,
|
||||
FrontCenter,
|
||||
LFE,
|
||||
BackLeft,
|
||||
BackRight,
|
||||
BackCenter,
|
||||
SideLeft,
|
||||
SideRight,
|
||||
|
||||
MaxChannels,
|
||||
};
|
||||
|
||||
|
||||
/* Device formats */
|
||||
enum DevFmtType {
|
||||
DevFmtByte = ALC_BYTE_SOFT,
|
||||
DevFmtUByte = ALC_UNSIGNED_BYTE_SOFT,
|
||||
DevFmtShort = ALC_SHORT_SOFT,
|
||||
DevFmtUShort = ALC_UNSIGNED_SHORT_SOFT,
|
||||
DevFmtInt = ALC_INT_SOFT,
|
||||
DevFmtUInt = ALC_UNSIGNED_INT_SOFT,
|
||||
DevFmtFloat = ALC_FLOAT_SOFT,
|
||||
|
||||
DevFmtTypeDefault = DevFmtFloat
|
||||
};
|
||||
enum DevFmtChannels {
|
||||
DevFmtMono = ALC_MONO_SOFT,
|
||||
DevFmtStereo = ALC_STEREO_SOFT,
|
||||
DevFmtQuad = ALC_QUAD_SOFT,
|
||||
DevFmtX51 = ALC_5POINT1_SOFT,
|
||||
DevFmtX61 = ALC_6POINT1_SOFT,
|
||||
DevFmtX71 = ALC_7POINT1_SOFT,
|
||||
|
||||
/* Similar to 5.1, except using the side channels instead of back */
|
||||
DevFmtX51Side = 0x80000000,
|
||||
|
||||
DevFmtChannelsDefault = DevFmtStereo
|
||||
};
|
||||
|
||||
ALuint BytesFromDevFmt(enum DevFmtType type) DECL_CONST;
|
||||
ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) DECL_CONST;
|
||||
inline ALuint FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type)
|
||||
{
|
||||
return ChannelsFromDevFmt(chans) * BytesFromDevFmt(type);
|
||||
}
|
||||
|
||||
|
||||
extern const struct EffectList {
|
||||
const char *name;
|
||||
int type;
|
||||
const char *ename;
|
||||
ALenum val;
|
||||
} EffectList[];
|
||||
|
||||
|
||||
enum DeviceType {
|
||||
Playback,
|
||||
Capture,
|
||||
Loopback
|
||||
};
|
||||
|
||||
|
||||
/* Size for temporary storage of buffer data, in ALfloats. Larger values need
|
||||
* more memory, while smaller values may need more iterations. The value needs
|
||||
* to be a sensible size, however, as it constrains the max stepping value used
|
||||
* for mixing, as well as the maximum number of samples per mixing iteration.
|
||||
*/
|
||||
#define BUFFERSIZE (2048u)
|
||||
|
||||
|
||||
struct ALCdevice_struct
|
||||
{
|
||||
RefCount ref;
|
||||
|
||||
ALCboolean Connected;
|
||||
enum DeviceType Type;
|
||||
|
||||
ALuint Frequency;
|
||||
ALuint UpdateSize;
|
||||
ALuint NumUpdates;
|
||||
enum DevFmtChannels FmtChans;
|
||||
enum DevFmtType FmtType;
|
||||
|
||||
al_string DeviceName;
|
||||
|
||||
ATOMIC(ALCenum) LastError;
|
||||
|
||||
// Maximum number of sources that can be created
|
||||
ALuint MaxNoOfSources;
|
||||
// Maximum number of slots that can be created
|
||||
ALuint AuxiliaryEffectSlotMax;
|
||||
|
||||
ALCuint NumMonoSources;
|
||||
ALCuint NumStereoSources;
|
||||
ALuint NumAuxSends;
|
||||
|
||||
// Map of Buffers for this device
|
||||
UIntMap BufferMap;
|
||||
|
||||
// Map of Effects for this device
|
||||
UIntMap EffectMap;
|
||||
|
||||
// Map of Filters for this device
|
||||
UIntMap FilterMap;
|
||||
|
||||
// Map of Soundfonts for this device
|
||||
UIntMap SfontMap;
|
||||
|
||||
// Map of Presets for this device
|
||||
UIntMap PresetMap;
|
||||
|
||||
// Map of Fontsounds for this device
|
||||
UIntMap FontsoundMap;
|
||||
|
||||
/* Default soundfont (accessible as ID 0) */
|
||||
struct ALsoundfont *DefaultSfont;
|
||||
|
||||
/* MIDI synth engine */
|
||||
struct MidiSynth *Synth;
|
||||
|
||||
/* HRTF filter tables */
|
||||
const struct Hrtf *Hrtf;
|
||||
|
||||
// Stereo-to-binaural filter
|
||||
struct bs2b *Bs2b;
|
||||
ALCint Bs2bLevel;
|
||||
|
||||
// Device flags
|
||||
ALuint Flags;
|
||||
|
||||
ALuint ChannelOffsets[MaxChannels];
|
||||
|
||||
enum Channel Speaker2Chan[MaxChannels];
|
||||
ALfloat SpeakerAngle[MaxChannels];
|
||||
ALuint NumChan;
|
||||
|
||||
ALuint64 ClockBase;
|
||||
ALuint SamplesDone;
|
||||
|
||||
/* Temp storage used for each source when mixing. */
|
||||
alignas(16) ALfloat SourceData[BUFFERSIZE];
|
||||
alignas(16) ALfloat ResampledData[BUFFERSIZE];
|
||||
alignas(16) ALfloat FilteredData[BUFFERSIZE];
|
||||
|
||||
// Dry path buffer mix
|
||||
alignas(16) ALfloat DryBuffer[MaxChannels][BUFFERSIZE];
|
||||
|
||||
/* Running count of the mixer invocations, in 31.1 fixed point. This
|
||||
* actually increments *twice* when mixing, first at the start and then at
|
||||
* the end, so the bottom bit indicates if the device is currently mixing
|
||||
* and the upper bits indicates how many mixes have been done.
|
||||
*/
|
||||
RefCount MixCount;
|
||||
|
||||
/* Default effect slot */
|
||||
struct ALeffectslot *DefaultSlot;
|
||||
|
||||
// Contexts created on this device
|
||||
ATOMIC(ALCcontext*) ContextList;
|
||||
|
||||
struct ALCbackend *Backend;
|
||||
|
||||
void *ExtraData; // For the backend's use
|
||||
|
||||
ALCdevice *volatile next;
|
||||
|
||||
/* Memory space used by the default slot (Playback devices only) */
|
||||
alignas(16) ALCbyte _slot_mem[];
|
||||
};
|
||||
|
||||
// Frequency was requested by the app or config file
|
||||
#define DEVICE_FREQUENCY_REQUEST (1<<1)
|
||||
// Channel configuration was requested by the config file
|
||||
#define DEVICE_CHANNELS_REQUEST (1<<2)
|
||||
// Sample type was requested by the config file
|
||||
#define DEVICE_SAMPLE_TYPE_REQUEST (1<<3)
|
||||
// HRTF was requested by the app
|
||||
#define DEVICE_HRTF_REQUEST (1<<4)
|
||||
|
||||
// Stereo sources cover 120-degree angles around +/-90
|
||||
#define DEVICE_WIDE_STEREO (1<<16)
|
||||
|
||||
// Specifies if the DSP is paused at user request
|
||||
#define DEVICE_PAUSED (1<<30)
|
||||
|
||||
// Specifies if the device is currently running
|
||||
#define DEVICE_RUNNING (1<<31)
|
||||
|
||||
/* Invalid channel offset */
|
||||
#define INVALID_OFFSET (~0u)
|
||||
|
||||
|
||||
/* Nanosecond resolution for the device clock time. */
|
||||
#define DEVICE_CLOCK_RES U64(1000000000)
|
||||
|
||||
|
||||
/* Must be less than 15 characters (16 including terminating null) for
|
||||
* compatibility with pthread_setname_np limitations. */
|
||||
#define MIXER_THREAD_NAME "alsoft-mixer"
|
||||
|
||||
|
||||
struct ALCcontext_struct
|
||||
{
|
||||
RefCount ref;
|
||||
|
||||
struct ALlistener *Listener;
|
||||
|
||||
UIntMap SourceMap;
|
||||
UIntMap EffectSlotMap;
|
||||
|
||||
ATOMIC(ALenum) LastError;
|
||||
|
||||
ATOMIC(ALenum) UpdateSources;
|
||||
|
||||
volatile enum DistanceModel DistanceModel;
|
||||
volatile ALboolean SourceDistanceModel;
|
||||
|
||||
volatile ALfloat DopplerFactor;
|
||||
volatile ALfloat DopplerVelocity;
|
||||
volatile ALfloat SpeedOfSound;
|
||||
volatile ALenum DeferUpdates;
|
||||
|
||||
struct ALactivesource **ActiveSources;
|
||||
ALsizei ActiveSourceCount;
|
||||
ALsizei MaxActiveSources;
|
||||
|
||||
VECTOR(struct ALeffectslot*) ActiveAuxSlots;
|
||||
|
||||
ALCdevice *Device;
|
||||
const ALCchar *ExtensionList;
|
||||
|
||||
ALCcontext *volatile next;
|
||||
|
||||
/* Memory space used by the listener */
|
||||
alignas(16) ALCbyte _listener_mem[];
|
||||
};
|
||||
|
||||
ALCcontext *GetContextRef(void);
|
||||
|
||||
void ALCcontext_IncRef(ALCcontext *context);
|
||||
void ALCcontext_DecRef(ALCcontext *context);
|
||||
|
||||
void AppendAllDevicesList(const ALCchar *name);
|
||||
void AppendCaptureDeviceList(const ALCchar *name);
|
||||
|
||||
ALint64 ALCdevice_GetLatencyDefault(ALCdevice *device);
|
||||
|
||||
void ALCdevice_Lock(ALCdevice *device);
|
||||
void ALCdevice_Unlock(ALCdevice *device);
|
||||
ALint64 ALCdevice_GetLatency(ALCdevice *device);
|
||||
|
||||
inline void LockContext(ALCcontext *context)
|
||||
{ ALCdevice_Lock(context->Device); }
|
||||
|
||||
inline void UnlockContext(ALCcontext *context)
|
||||
{ ALCdevice_Unlock(context->Device); }
|
||||
|
||||
|
||||
void *al_malloc(size_t alignment, size_t size);
|
||||
void *al_calloc(size_t alignment, size_t size);
|
||||
void al_free(void *ptr);
|
||||
|
||||
|
||||
typedef struct {
|
||||
#ifdef HAVE_FENV_H
|
||||
DERIVE_FROM_TYPE(fenv_t);
|
||||
#else
|
||||
int state;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
int sse_state;
|
||||
#endif
|
||||
} FPUCtl;
|
||||
void SetMixerFPUMode(FPUCtl *ctl);
|
||||
void RestoreFPUMode(const FPUCtl *ctl);
|
||||
|
||||
|
||||
typedef struct RingBuffer RingBuffer;
|
||||
RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length);
|
||||
void DestroyRingBuffer(RingBuffer *ring);
|
||||
ALsizei RingBufferSize(RingBuffer *ring);
|
||||
void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len);
|
||||
void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len);
|
||||
|
||||
void ReadALConfig(void);
|
||||
void FreeALConfig(void);
|
||||
int ConfigValueExists(const char *blockName, const char *keyName);
|
||||
const char *GetConfigValue(const char *blockName, const char *keyName, const char *def);
|
||||
int GetConfigValueBool(const char *blockName, const char *keyName, int def);
|
||||
int ConfigValueStr(const char *blockName, const char *keyName, const char **ret);
|
||||
int ConfigValueInt(const char *blockName, const char *keyName, int *ret);
|
||||
int ConfigValueUInt(const char *blockName, const char *keyName, unsigned int *ret);
|
||||
int ConfigValueFloat(const char *blockName, const char *keyName, float *ret);
|
||||
|
||||
void SetRTPriority(void);
|
||||
|
||||
void SetDefaultChannelOrder(ALCdevice *device);
|
||||
void SetDefaultWFXChannelOrder(ALCdevice *device);
|
||||
|
||||
const ALCchar *DevFmtTypeString(enum DevFmtType type) DECL_CONST;
|
||||
const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans) DECL_CONST;
|
||||
|
||||
|
||||
extern FILE *LogFile;
|
||||
|
||||
#if defined(__GNUC__) && !defined(IN_IDE_PARSER)
|
||||
#define AL_PRINT(T, MSG, ...) fprintf(LogFile, "AL lib: %s %s: "MSG, T, __FUNCTION__ , ## __VA_ARGS__)
|
||||
#else
|
||||
void al_print(const char *type, const char *func, const char *fmt, ...) DECL_FORMAT(printf, 3,4);
|
||||
#define AL_PRINT(T, ...) al_print((T), __FUNCTION__, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
enum LogLevel {
|
||||
NoLog,
|
||||
LogError,
|
||||
LogWarning,
|
||||
LogTrace,
|
||||
LogRef
|
||||
};
|
||||
extern enum LogLevel LogLevel;
|
||||
|
||||
#define TRACEREF(...) do { \
|
||||
if(LogLevel >= LogRef) \
|
||||
AL_PRINT("(--)", __VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define TRACE(...) do { \
|
||||
if(LogLevel >= LogTrace) \
|
||||
AL_PRINT("(II)", __VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define WARN(...) do { \
|
||||
if(LogLevel >= LogWarning) \
|
||||
AL_PRINT("(WW)", __VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define ERR(...) do { \
|
||||
if(LogLevel >= LogError) \
|
||||
AL_PRINT("(EE)", __VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
|
||||
extern ALint RTPrioLevel;
|
||||
|
||||
|
||||
extern ALuint CPUCapFlags;
|
||||
enum {
|
||||
CPU_CAP_SSE = 1<<0,
|
||||
CPU_CAP_SSE2 = 1<<1,
|
||||
CPU_CAP_SSE4_1 = 1<<2,
|
||||
CPU_CAP_NEON = 1<<3,
|
||||
};
|
||||
|
||||
void FillCPUCaps(ALuint capfilter);
|
||||
|
||||
FILE *OpenDataFile(const char *fname, const char *subdir);
|
||||
|
||||
/* Small hack to use a pointer-to-array type as a normal argument type.
|
||||
* Shouldn't be used directly. */
|
||||
typedef ALfloat ALfloatBUFFERSIZE[BUFFERSIZE];
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,172 +0,0 @@
|
||||
#ifndef ALMIDI_H
|
||||
#define ALMIDI_H
|
||||
|
||||
#include "alMain.h"
|
||||
#include "atomic.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ALsfmodulator {
|
||||
struct {
|
||||
ALenum Input;
|
||||
ALenum Type;
|
||||
ALenum Form;
|
||||
} Source[2];
|
||||
ALint Amount;
|
||||
ALenum TransformOp;
|
||||
ALenum Dest;
|
||||
} ALsfmodulator;
|
||||
|
||||
typedef struct ALenvelope {
|
||||
ALint DelayTime;
|
||||
ALint AttackTime;
|
||||
ALint HoldTime;
|
||||
ALint DecayTime;
|
||||
ALint SustainAttn;
|
||||
ALint ReleaseTime;
|
||||
ALint KeyToHoldTime;
|
||||
ALint KeyToDecayTime;
|
||||
} ALenvelope;
|
||||
|
||||
|
||||
typedef struct ALfontsound {
|
||||
RefCount ref;
|
||||
|
||||
struct ALbuffer *Buffer;
|
||||
|
||||
ALint MinKey, MaxKey;
|
||||
ALint MinVelocity, MaxVelocity;
|
||||
|
||||
ALint ModLfoToPitch;
|
||||
ALint VibratoLfoToPitch;
|
||||
ALint ModEnvToPitch;
|
||||
|
||||
ALint FilterCutoff;
|
||||
ALint FilterQ;
|
||||
ALint ModLfoToFilterCutoff;
|
||||
ALint ModEnvToFilterCutoff;
|
||||
ALint ModLfoToVolume;
|
||||
|
||||
ALint ChorusSend;
|
||||
ALint ReverbSend;
|
||||
|
||||
ALint Pan;
|
||||
|
||||
struct {
|
||||
ALint Delay;
|
||||
ALint Frequency;
|
||||
} ModLfo;
|
||||
struct {
|
||||
ALint Delay;
|
||||
ALint Frequency;
|
||||
} VibratoLfo;
|
||||
|
||||
ALenvelope ModEnv;
|
||||
ALenvelope VolEnv;
|
||||
|
||||
ALint Attenuation;
|
||||
|
||||
ALint CoarseTuning;
|
||||
ALint FineTuning;
|
||||
|
||||
ALenum LoopMode;
|
||||
|
||||
ALint TuningScale;
|
||||
|
||||
ALint ExclusiveClass;
|
||||
|
||||
ALuint Start;
|
||||
ALuint End;
|
||||
ALuint LoopStart;
|
||||
ALuint LoopEnd;
|
||||
ALuint SampleRate;
|
||||
ALubyte PitchKey;
|
||||
ALbyte PitchCorrection;
|
||||
ALenum SampleType;
|
||||
struct ALfontsound *Link;
|
||||
|
||||
/* NOTE: Each map entry contains *four* (4) ALsfmodulator objects. */
|
||||
UIntMap ModulatorMap;
|
||||
|
||||
ALuint id;
|
||||
} ALfontsound;
|
||||
|
||||
void ALfontsound_setPropi(ALfontsound *self, ALCcontext *context, ALenum param, ALint value);
|
||||
void ALfontsound_setModStagei(ALfontsound *self, ALCcontext *context, ALsizei stage, ALenum param, ALint value);
|
||||
|
||||
ALfontsound *NewFontsound(ALCcontext *context);
|
||||
void DeleteFontsound(ALCdevice *device, ALfontsound *sound);
|
||||
|
||||
inline struct ALfontsound *LookupFontsound(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALfontsound*)LookupUIntMapKey(&device->FontsoundMap, id); }
|
||||
inline struct ALfontsound *RemoveFontsound(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALfontsound*)RemoveUIntMapKey(&device->FontsoundMap, id); }
|
||||
|
||||
void ReleaseALFontsounds(ALCdevice *device);
|
||||
|
||||
|
||||
typedef struct ALsfpreset {
|
||||
RefCount ref;
|
||||
|
||||
ALint Preset; /* a.k.a. MIDI program number */
|
||||
ALint Bank; /* MIDI bank 0...127, or percussion (bank 128) */
|
||||
|
||||
ALfontsound **Sounds;
|
||||
ALsizei NumSounds;
|
||||
|
||||
ALuint id;
|
||||
} ALsfpreset;
|
||||
|
||||
ALsfpreset *NewPreset(ALCcontext *context);
|
||||
void DeletePreset(ALCdevice *device, ALsfpreset *preset);
|
||||
|
||||
inline struct ALsfpreset *LookupPreset(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALsfpreset*)LookupUIntMapKey(&device->PresetMap, id); }
|
||||
inline struct ALsfpreset *RemovePreset(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALsfpreset*)RemoveUIntMapKey(&device->PresetMap, id); }
|
||||
|
||||
void ReleaseALPresets(ALCdevice *device);
|
||||
|
||||
|
||||
typedef struct ALsoundfont {
|
||||
RefCount ref;
|
||||
|
||||
ALsfpreset **Presets;
|
||||
ALsizei NumPresets;
|
||||
|
||||
RWLock Lock;
|
||||
|
||||
ALuint id;
|
||||
} ALsoundfont;
|
||||
|
||||
ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context);
|
||||
void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device);
|
||||
|
||||
inline struct ALsoundfont *LookupSfont(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALsoundfont*)LookupUIntMapKey(&device->SfontMap, id); }
|
||||
inline struct ALsoundfont *RemoveSfont(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALsoundfont*)RemoveUIntMapKey(&device->SfontMap, id); }
|
||||
|
||||
void ReleaseALSoundfonts(ALCdevice *device);
|
||||
|
||||
|
||||
inline ALboolean IsValidCtrlInput(int cc)
|
||||
{
|
||||
/* These correspond to MIDI functions, not real controller values. */
|
||||
if(cc == 0 || cc == 6 || cc == 32 || cc == 38 || (cc >= 98 && cc <= 101) || cc >= 120)
|
||||
return AL_FALSE;
|
||||
/* These are the LSB components of CC0...CC31, which are automatically used when
|
||||
* reading the MSB controller value. */
|
||||
if(cc >= 32 && cc <= 63)
|
||||
return AL_FALSE;
|
||||
/* All the rest are okay! */
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ALMIDI_H */
|
||||
@@ -1,147 +0,0 @@
|
||||
#ifndef _AL_SOURCE_H_
|
||||
#define _AL_SOURCE_H_
|
||||
|
||||
#define MAX_SENDS 4
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "hrtf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern enum Resampler DefaultResampler;
|
||||
|
||||
extern const ALsizei ResamplerPadding[ResamplerMax];
|
||||
extern const ALsizei ResamplerPrePadding[ResamplerMax];
|
||||
|
||||
|
||||
typedef struct ALbufferlistitem {
|
||||
struct ALbuffer *buffer;
|
||||
struct ALbufferlistitem *volatile next;
|
||||
struct ALbufferlistitem *volatile prev;
|
||||
} ALbufferlistitem;
|
||||
|
||||
|
||||
typedef struct ALactivesource {
|
||||
struct ALsource *Source;
|
||||
|
||||
/** Method to update mixing parameters. */
|
||||
ALvoid (*Update)(struct ALactivesource *self, const ALCcontext *context);
|
||||
|
||||
/** Current target parameters used for mixing. */
|
||||
ALint Step;
|
||||
|
||||
ALboolean IsHrtf;
|
||||
|
||||
ALuint Offset; /* Number of output samples mixed since starting. */
|
||||
|
||||
DirectParams Direct;
|
||||
SendParams Send[MAX_SENDS];
|
||||
} ALactivesource;
|
||||
|
||||
|
||||
typedef struct ALsource {
|
||||
/** Source properties. */
|
||||
volatile ALfloat Pitch;
|
||||
volatile ALfloat Gain;
|
||||
volatile ALfloat OuterGain;
|
||||
volatile ALfloat MinGain;
|
||||
volatile ALfloat MaxGain;
|
||||
volatile ALfloat InnerAngle;
|
||||
volatile ALfloat OuterAngle;
|
||||
volatile ALfloat RefDistance;
|
||||
volatile ALfloat MaxDistance;
|
||||
volatile ALfloat RollOffFactor;
|
||||
volatile ALfloat Position[3];
|
||||
volatile ALfloat Velocity[3];
|
||||
volatile ALfloat Orientation[3];
|
||||
volatile ALboolean HeadRelative;
|
||||
volatile ALboolean Looping;
|
||||
volatile enum DistanceModel DistanceModel;
|
||||
volatile ALboolean DirectChannels;
|
||||
|
||||
volatile ALboolean DryGainHFAuto;
|
||||
volatile ALboolean WetGainAuto;
|
||||
volatile ALboolean WetGainHFAuto;
|
||||
volatile ALfloat OuterGainHF;
|
||||
|
||||
volatile ALfloat AirAbsorptionFactor;
|
||||
volatile ALfloat RoomRolloffFactor;
|
||||
volatile ALfloat DopplerFactor;
|
||||
|
||||
volatile ALfloat Radius;
|
||||
|
||||
enum Resampler Resampler;
|
||||
|
||||
/**
|
||||
* Last user-specified offset, and the offset type (bytes, samples, or
|
||||
* seconds).
|
||||
*/
|
||||
ALdouble Offset;
|
||||
ALenum OffsetType;
|
||||
|
||||
/** Source type (static, streaming, or undetermined) */
|
||||
volatile ALint SourceType;
|
||||
|
||||
/** Source state (initial, playing, paused, or stopped) */
|
||||
volatile ALenum state;
|
||||
ALenum new_state;
|
||||
|
||||
/**
|
||||
* Source offset in samples, relative to the currently playing buffer, NOT
|
||||
* the whole queue, and the fractional (fixed-point) offset to the next
|
||||
* sample.
|
||||
*/
|
||||
ALuint position;
|
||||
ALuint position_fraction;
|
||||
|
||||
/** Source Buffer Queue info. */
|
||||
ATOMIC(ALbufferlistitem*) queue;
|
||||
ATOMIC(ALbufferlistitem*) current_buffer;
|
||||
RWLock queue_lock;
|
||||
|
||||
/** Current buffer sample info. */
|
||||
ALuint NumChannels;
|
||||
ALuint SampleSize;
|
||||
|
||||
/** Direct filter and auxiliary send info. */
|
||||
struct {
|
||||
ALfloat Gain;
|
||||
ALfloat GainHF;
|
||||
ALfloat HFReference;
|
||||
ALfloat GainLF;
|
||||
ALfloat LFReference;
|
||||
} Direct;
|
||||
struct {
|
||||
struct ALeffectslot *Slot;
|
||||
ALfloat Gain;
|
||||
ALfloat GainHF;
|
||||
ALfloat HFReference;
|
||||
ALfloat GainLF;
|
||||
ALfloat LFReference;
|
||||
} Send[MAX_SENDS];
|
||||
|
||||
/** Source needs to update its mixing parameters. */
|
||||
ATOMIC(ALenum) NeedsUpdate;
|
||||
|
||||
/** Self ID */
|
||||
ALuint id;
|
||||
} ALsource;
|
||||
|
||||
inline struct ALsource *LookupSource(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALsource*)LookupUIntMapKey(&context->SourceMap, id); }
|
||||
inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALsource*)RemoveUIntMapKey(&context->SourceMap, id); }
|
||||
|
||||
ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state);
|
||||
ALboolean ApplyOffset(ALsource *Source);
|
||||
|
||||
ALvoid ReleaseALSources(ALCcontext *Context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,236 +0,0 @@
|
||||
#ifndef _ALU_H_
|
||||
#define _ALU_H_
|
||||
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#ifdef HAVE_FLOAT_H
|
||||
#include <float.h>
|
||||
#endif
|
||||
#ifdef HAVE_IEEEFP_H
|
||||
#include <ieeefp.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alBuffer.h"
|
||||
#include "alFilter.h"
|
||||
|
||||
#include "hrtf.h"
|
||||
#include "align.h"
|
||||
|
||||
|
||||
#define F_PI (3.14159265358979323846f)
|
||||
#define F_PI_2 (1.57079632679489661923f)
|
||||
#define F_2PI (6.28318530717958647692f)
|
||||
|
||||
#ifndef FLT_EPSILON
|
||||
#define FLT_EPSILON (1.19209290e-07f)
|
||||
#endif
|
||||
|
||||
#define DEG2RAD(x) ((ALfloat)(x) * (F_PI/180.0f))
|
||||
#define RAD2DEG(x) ((ALfloat)(x) * (180.0f/F_PI))
|
||||
|
||||
|
||||
#define SRC_HISTORY_BITS (6)
|
||||
#define SRC_HISTORY_LENGTH (1<<SRC_HISTORY_BITS)
|
||||
#define SRC_HISTORY_MASK (SRC_HISTORY_LENGTH-1)
|
||||
|
||||
#define MAX_PITCH (10)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum ActiveFilters {
|
||||
AF_None = 0,
|
||||
AF_LowPass = 1,
|
||||
AF_HighPass = 2,
|
||||
AF_BandPass = AF_LowPass | AF_HighPass
|
||||
};
|
||||
|
||||
|
||||
typedef struct HrtfState {
|
||||
alignas(16) ALfloat History[SRC_HISTORY_LENGTH];
|
||||
alignas(16) ALfloat Values[HRIR_LENGTH][2];
|
||||
} HrtfState;
|
||||
|
||||
typedef struct HrtfParams {
|
||||
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
|
||||
alignas(16) ALfloat CoeffStep[HRIR_LENGTH][2];
|
||||
ALuint Delay[2];
|
||||
ALint DelayStep[2];
|
||||
} HrtfParams;
|
||||
|
||||
|
||||
typedef struct MixGains {
|
||||
ALfloat Current;
|
||||
ALfloat Step;
|
||||
ALfloat Target;
|
||||
} MixGains;
|
||||
|
||||
|
||||
typedef struct DirectParams {
|
||||
ALfloat (*OutBuffer)[BUFFERSIZE];
|
||||
|
||||
/* If not 'moving', gain/coefficients are set directly without fading. */
|
||||
ALboolean Moving;
|
||||
/* Stepping counter for gain/coefficient fading. */
|
||||
ALuint Counter;
|
||||
|
||||
struct {
|
||||
enum ActiveFilters ActiveType;
|
||||
ALfilterState LowPass;
|
||||
ALfilterState HighPass;
|
||||
} Filters[MAX_INPUT_CHANNELS];
|
||||
|
||||
union {
|
||||
struct {
|
||||
HrtfParams Params[MAX_INPUT_CHANNELS];
|
||||
HrtfState State[MAX_INPUT_CHANNELS];
|
||||
ALuint IrSize;
|
||||
ALfloat Gain;
|
||||
ALfloat Dir[3];
|
||||
} Hrtf;
|
||||
|
||||
MixGains Gains[MAX_INPUT_CHANNELS][MaxChannels];
|
||||
} Mix;
|
||||
} DirectParams;
|
||||
|
||||
typedef struct SendParams {
|
||||
ALfloat (*OutBuffer)[BUFFERSIZE];
|
||||
|
||||
ALboolean Moving;
|
||||
ALuint Counter;
|
||||
|
||||
struct {
|
||||
enum ActiveFilters ActiveType;
|
||||
ALfilterState LowPass;
|
||||
ALfilterState HighPass;
|
||||
} Filters[MAX_INPUT_CHANNELS];
|
||||
|
||||
/* Gain control, which applies to all input channels to a single (mono)
|
||||
* output buffer. */
|
||||
MixGains Gain;
|
||||
} SendParams;
|
||||
|
||||
|
||||
typedef const ALfloat* (*ResamplerFunc)(const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint dstlen);
|
||||
|
||||
typedef void (*MixerFunc)(const ALfloat *data, ALuint OutChans,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE], struct MixGains *Gains,
|
||||
ALuint Counter, ALuint OutPos, ALuint BufferSize);
|
||||
typedef void (*HrtfMixerFunc)(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
ALuint Counter, ALuint Offset, ALuint OutPos,
|
||||
const ALuint IrSize, const HrtfParams *hrtfparams,
|
||||
HrtfState *hrtfstate, ALuint BufferSize);
|
||||
|
||||
|
||||
#define GAIN_SILENCE_THRESHOLD (0.00001f) /* -100dB */
|
||||
|
||||
#define SPEEDOFSOUNDMETRESPERSEC (343.3f)
|
||||
#define AIRABSORBGAINHF (0.99426f) /* -0.05dB */
|
||||
|
||||
#define FRACTIONBITS (14)
|
||||
#define FRACTIONONE (1<<FRACTIONBITS)
|
||||
#define FRACTIONMASK (FRACTIONONE-1)
|
||||
|
||||
|
||||
inline ALfloat minf(ALfloat a, ALfloat b)
|
||||
{ return ((a > b) ? b : a); }
|
||||
inline ALfloat maxf(ALfloat a, ALfloat b)
|
||||
{ return ((a > b) ? a : b); }
|
||||
inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max)
|
||||
{ return minf(max, maxf(min, val)); }
|
||||
|
||||
inline ALdouble mind(ALdouble a, ALdouble b)
|
||||
{ return ((a > b) ? b : a); }
|
||||
inline ALdouble maxd(ALdouble a, ALdouble b)
|
||||
{ return ((a > b) ? a : b); }
|
||||
inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max)
|
||||
{ return mind(max, maxd(min, val)); }
|
||||
|
||||
inline ALuint minu(ALuint a, ALuint b)
|
||||
{ return ((a > b) ? b : a); }
|
||||
inline ALuint maxu(ALuint a, ALuint b)
|
||||
{ return ((a > b) ? a : b); }
|
||||
inline ALuint clampu(ALuint val, ALuint min, ALuint max)
|
||||
{ return minu(max, maxu(min, val)); }
|
||||
|
||||
inline ALint mini(ALint a, ALint b)
|
||||
{ return ((a > b) ? b : a); }
|
||||
inline ALint maxi(ALint a, ALint b)
|
||||
{ return ((a > b) ? a : b); }
|
||||
inline ALint clampi(ALint val, ALint min, ALint max)
|
||||
{ return mini(max, maxi(min, val)); }
|
||||
|
||||
inline ALint64 mini64(ALint64 a, ALint64 b)
|
||||
{ return ((a > b) ? b : a); }
|
||||
inline ALint64 maxi64(ALint64 a, ALint64 b)
|
||||
{ return ((a > b) ? a : b); }
|
||||
inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max)
|
||||
{ return mini64(max, maxi64(min, val)); }
|
||||
|
||||
inline ALuint64 minu64(ALuint64 a, ALuint64 b)
|
||||
{ return ((a > b) ? b : a); }
|
||||
inline ALuint64 maxu64(ALuint64 a, ALuint64 b)
|
||||
{ return ((a > b) ? a : b); }
|
||||
inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max)
|
||||
{ return minu64(max, maxu64(min, val)); }
|
||||
|
||||
|
||||
inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu)
|
||||
{
|
||||
return val1 + (val2-val1)*mu;
|
||||
}
|
||||
inline ALfloat cubic(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat mu)
|
||||
{
|
||||
ALfloat mu2 = mu*mu;
|
||||
ALfloat a0 = -0.5f*val0 + 1.5f*val1 + -1.5f*val2 + 0.5f*val3;
|
||||
ALfloat a1 = val0 + -2.5f*val1 + 2.0f*val2 + -0.5f*val3;
|
||||
ALfloat a2 = -0.5f*val0 + 0.5f*val2;
|
||||
ALfloat a3 = val1;
|
||||
|
||||
return a0*mu*mu2 + a1*mu2 + a2*mu + a3;
|
||||
}
|
||||
|
||||
|
||||
ALvoid aluInitPanning(ALCdevice *Device);
|
||||
|
||||
/**
|
||||
* ComputeAngleGains
|
||||
*
|
||||
* Sets channel gains based on a given source's angle and its half-width. The
|
||||
* angle and hwidth parameters are in radians.
|
||||
*/
|
||||
void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat gains[MaxChannels]);
|
||||
|
||||
/**
|
||||
* SetGains
|
||||
*
|
||||
* Helper to set the appropriate channels to the specified gain.
|
||||
*/
|
||||
inline void SetGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MaxChannels])
|
||||
{
|
||||
ComputeAngleGains(device, 0.0f, F_PI, ingain, gains);
|
||||
}
|
||||
|
||||
|
||||
ALvoid CalcSourceParams(struct ALactivesource *src, const ALCcontext *ALContext);
|
||||
ALvoid CalcNonAttnSourceParams(struct ALactivesource *src, const ALCcontext *ALContext);
|
||||
|
||||
ALvoid MixSource(struct ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo);
|
||||
|
||||
ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size);
|
||||
/* Caller must lock the device. */
|
||||
ALvoid aluHandleDisconnect(ALCdevice *device);
|
||||
|
||||
extern ALfloat ConeScale;
|
||||
extern ALfloat ZScale;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,217 +0,0 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alMidi.h"
|
||||
#include "alError.h"
|
||||
#include "alThunk.h"
|
||||
#include "evtqueue.h"
|
||||
#include "rwlock.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
|
||||
MidiSynth *SynthCreate(ALCdevice *device)
|
||||
{
|
||||
MidiSynth *synth = NULL;
|
||||
if(!synth) synth = SSynth_create(device);
|
||||
if(!synth) synth = FSynth_create(device);
|
||||
if(!synth) synth = DSynth_create(device);
|
||||
return synth;
|
||||
}
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alMidiSoundfontSOFT(ALuint id)
|
||||
{
|
||||
alMidiSoundfontvSOFT(1, &id);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alMidiSoundfontvSOFT(ALsizei count, const ALuint *ids)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
MidiSynth *synth;
|
||||
ALenum err;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(count < 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
synth = device->Synth;
|
||||
|
||||
WriteLock(&synth->Lock);
|
||||
if(synth->State == AL_PLAYING || synth->State == AL_PAUSED)
|
||||
alSetError(context, AL_INVALID_OPERATION);
|
||||
else
|
||||
{
|
||||
err = V(synth,selectSoundfonts)(context, count, ids);
|
||||
if(err != AL_NO_ERROR)
|
||||
alSetError(context, err);
|
||||
}
|
||||
WriteUnlock(&synth->Lock);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alMidiEventSOFT(ALuint64SOFT time, ALenum event, ALsizei channel, ALsizei param1, ALsizei param2)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALenum err;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(event == AL_NOTEOFF_SOFT || event == AL_NOTEON_SOFT ||
|
||||
event == AL_KEYPRESSURE_SOFT || event == AL_CONTROLLERCHANGE_SOFT ||
|
||||
event == AL_PROGRAMCHANGE_SOFT || event == AL_CHANNELPRESSURE_SOFT ||
|
||||
event == AL_PITCHBEND_SOFT))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
if(!(channel >= 0 && channel <= 15))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
if(!(param1 >= 0 && param1 <= 127))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
if(!(param2 >= 0 && param2 <= 127))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
ALCdevice_Lock(device);
|
||||
err = MidiSynth_insertEvent(device->Synth, time, event|channel, param1, param2);
|
||||
ALCdevice_Unlock(device);
|
||||
if(err != AL_NO_ERROR)
|
||||
alSetError(context, err);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alMidiSysExSOFT(ALuint64SOFT time, const ALbyte *data, ALsizei size)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALenum err;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!data || size < 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
ALCdevice_Lock(device);
|
||||
err = MidiSynth_insertSysExEvent(device->Synth, time, data, size);
|
||||
ALCdevice_Unlock(device);
|
||||
if(err != AL_NO_ERROR)
|
||||
alSetError(context, err);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alMidiPlaySOFT(void)
|
||||
{
|
||||
ALCcontext *context;
|
||||
MidiSynth *synth;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
synth = context->Device->Synth;
|
||||
WriteLock(&synth->Lock);
|
||||
MidiSynth_setState(synth, AL_PLAYING);
|
||||
WriteUnlock(&synth->Lock);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alMidiPauseSOFT(void)
|
||||
{
|
||||
ALCcontext *context;
|
||||
MidiSynth *synth;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
synth = context->Device->Synth;
|
||||
WriteLock(&synth->Lock);
|
||||
MidiSynth_setState(synth, AL_PAUSED);
|
||||
WriteUnlock(&synth->Lock);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alMidiStopSOFT(void)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
MidiSynth *synth;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
synth = device->Synth;
|
||||
|
||||
WriteLock(&synth->Lock);
|
||||
MidiSynth_setState(synth, AL_STOPPED);
|
||||
|
||||
ALCdevice_Lock(device);
|
||||
V0(synth,stop)();
|
||||
ALCdevice_Unlock(device);
|
||||
WriteUnlock(&synth->Lock);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alMidiResetSOFT(void)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
MidiSynth *synth;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
synth = device->Synth;
|
||||
|
||||
WriteLock(&synth->Lock);
|
||||
MidiSynth_setState(synth, AL_INITIAL);
|
||||
|
||||
ALCdevice_Lock(device);
|
||||
V0(synth,reset)();
|
||||
ALCdevice_Unlock(device);
|
||||
WriteUnlock(&synth->Lock);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alMidiGainSOFT(ALfloat value)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(value >= 0.0f && isfinite(value)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
V(device->Synth,setGain)(value);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
@@ -1,339 +0,0 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alMidi.h"
|
||||
#include "alError.h"
|
||||
#include "alThunk.h"
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
|
||||
extern inline struct ALsfpreset *LookupPreset(ALCdevice *device, ALuint id);
|
||||
extern inline struct ALsfpreset *RemovePreset(ALCdevice *device, ALuint id);
|
||||
|
||||
static void ALsfpreset_Construct(ALsfpreset *self);
|
||||
static void ALsfpreset_Destruct(ALsfpreset *self);
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alGenPresetsSOFT(ALsizei n, ALuint *ids)
|
||||
{
|
||||
ALCcontext *context;
|
||||
ALsizei cur = 0;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
for(cur = 0;cur < n;cur++)
|
||||
{
|
||||
ALsfpreset *preset = NewPreset(context);
|
||||
if(!preset)
|
||||
{
|
||||
alDeletePresetsSOFT(cur, ids);
|
||||
break;
|
||||
}
|
||||
|
||||
ids[cur] = preset->id;
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alDeletePresetsSOFT(ALsizei n, const ALuint *ids)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsfpreset *preset;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
/* Check for valid ID */
|
||||
if((preset=LookupPreset(device, ids[i])) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
if(ReadRef(&preset->ref) != 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
}
|
||||
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
if((preset=LookupPreset(device, ids[i])) != NULL)
|
||||
DeletePreset(device, preset);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALboolean AL_APIENTRY alIsPresetSOFT(ALuint id)
|
||||
{
|
||||
ALCcontext *context;
|
||||
ALboolean ret;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return AL_FALSE;
|
||||
|
||||
ret = LookupPreset(context->Device, id) ? AL_TRUE : AL_FALSE;
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alPresetiSOFT(ALuint id, ALenum param, ALint value)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsfpreset *preset;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if((preset=LookupPreset(device, id)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
if(ReadRef(&preset->ref) != 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
switch(param)
|
||||
{
|
||||
case AL_MIDI_PRESET_SOFT:
|
||||
if(!(value >= 0 && value <= 127))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
preset->Preset = value;
|
||||
break;
|
||||
|
||||
case AL_MIDI_BANK_SOFT:
|
||||
if(!(value >= 0 && value <= 128))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
preset->Bank = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alPresetivSOFT(ALuint id, ALenum param, const ALint *values)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsfpreset *preset;
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_MIDI_PRESET_SOFT:
|
||||
case AL_MIDI_BANK_SOFT:
|
||||
alPresetiSOFT(id, param, values[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if((preset=LookupPreset(device, id)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
if(ReadRef(&preset->ref) != 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alGetPresetivSOFT(ALuint id, ALenum param, ALint *values)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsfpreset *preset;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if((preset=LookupPreset(device, id)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
switch(param)
|
||||
{
|
||||
case AL_MIDI_PRESET_SOFT:
|
||||
values[0] = preset->Preset;
|
||||
break;
|
||||
|
||||
case AL_MIDI_BANK_SOFT:
|
||||
values[0] = preset->Bank;
|
||||
break;
|
||||
|
||||
case AL_FONTSOUNDS_SIZE_SOFT:
|
||||
values[0] = preset->NumSounds;
|
||||
break;
|
||||
|
||||
case AL_FONTSOUNDS_SOFT:
|
||||
for(i = 0;i < preset->NumSounds;i++)
|
||||
values[i] = preset->Sounds[i]->id;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alPresetFontsoundsSOFT(ALuint id, ALsizei count, const ALuint *fsids)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsfpreset *preset;
|
||||
ALfontsound **sounds;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if(!(preset=LookupPreset(device, id)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
if(count < 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
if(ReadRef(&preset->ref) != 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
|
||||
if(count == 0)
|
||||
sounds = NULL;
|
||||
else
|
||||
{
|
||||
sounds = calloc(count, sizeof(sounds[0]));
|
||||
if(!sounds)
|
||||
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
if(!(sounds[i]=LookupFontsound(device, fsids[i])))
|
||||
{
|
||||
free(sounds);
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
IncrementRef(&sounds[i]->ref);
|
||||
|
||||
sounds = ExchangePtr((XchgPtr*)&preset->Sounds, sounds);
|
||||
count = ExchangeInt(&preset->NumSounds, count);
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
DecrementRef(&sounds[i]->ref);
|
||||
free(sounds);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
ALsfpreset *NewPreset(ALCcontext *context)
|
||||
{
|
||||
ALCdevice *device = context->Device;
|
||||
ALsfpreset *preset;
|
||||
ALenum err;
|
||||
|
||||
preset = calloc(1, sizeof(*preset));
|
||||
if(!preset)
|
||||
SET_ERROR_AND_RETURN_VALUE(context, AL_OUT_OF_MEMORY, NULL);
|
||||
ALsfpreset_Construct(preset);
|
||||
|
||||
err = NewThunkEntry(&preset->id);
|
||||
if(err == AL_NO_ERROR)
|
||||
err = InsertUIntMapEntry(&device->PresetMap, preset->id, preset);
|
||||
if(err != AL_NO_ERROR)
|
||||
{
|
||||
ALsfpreset_Destruct(preset);
|
||||
memset(preset, 0, sizeof(*preset));
|
||||
free(preset);
|
||||
|
||||
SET_ERROR_AND_RETURN_VALUE(context, err, NULL);
|
||||
}
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
void DeletePreset(ALCdevice *device, ALsfpreset *preset)
|
||||
{
|
||||
RemovePreset(device, preset->id);
|
||||
|
||||
ALsfpreset_Destruct(preset);
|
||||
memset(preset, 0, sizeof(*preset));
|
||||
free(preset);
|
||||
}
|
||||
|
||||
|
||||
static void ALsfpreset_Construct(ALsfpreset *self)
|
||||
{
|
||||
InitRef(&self->ref, 0);
|
||||
|
||||
self->Preset = 0;
|
||||
self->Bank = 0;
|
||||
|
||||
self->Sounds = NULL;
|
||||
self->NumSounds = 0;
|
||||
|
||||
self->id = 0;
|
||||
}
|
||||
|
||||
static void ALsfpreset_Destruct(ALsfpreset *self)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
FreeThunkEntry(self->id);
|
||||
self->id = 0;
|
||||
|
||||
for(i = 0;i < self->NumSounds;i++)
|
||||
DecrementRef(&self->Sounds[i]->ref);
|
||||
free(self->Sounds);
|
||||
self->Sounds = NULL;
|
||||
self->NumSounds = 0;
|
||||
}
|
||||
|
||||
|
||||
/* ReleaseALPresets
|
||||
*
|
||||
* Called to destroy any presets that still exist on the device
|
||||
*/
|
||||
void ReleaseALPresets(ALCdevice *device)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < device->PresetMap.size;i++)
|
||||
{
|
||||
ALsfpreset *temp = device->PresetMap.array[i].value;
|
||||
device->PresetMap.array[i].value = NULL;
|
||||
|
||||
ALsfpreset_Destruct(temp);
|
||||
|
||||
memset(temp, 0, sizeof(*temp));
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
@@ -1,454 +0,0 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alMidi.h"
|
||||
#include "alThunk.h"
|
||||
#include "alError.h"
|
||||
#include <alBuffer.h>
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
|
||||
extern inline struct ALsoundfont *LookupSfont(ALCdevice *device, ALuint id);
|
||||
extern inline struct ALsoundfont *RemoveSfont(ALCdevice *device, ALuint id);
|
||||
|
||||
static void ALsoundfont_Construct(ALsoundfont *self);
|
||||
static void ALsoundfont_Destruct(ALsoundfont *self);
|
||||
void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device);
|
||||
ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context);
|
||||
static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr);
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alGenSoundfontsSOFT(ALsizei n, ALuint *ids)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsizei cur = 0;
|
||||
ALenum err;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
for(cur = 0;cur < n;cur++)
|
||||
{
|
||||
ALsoundfont *sfont = calloc(1, sizeof(ALsoundfont));
|
||||
if(!sfont)
|
||||
{
|
||||
alDeleteSoundfontsSOFT(cur, ids);
|
||||
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
|
||||
}
|
||||
ALsoundfont_Construct(sfont);
|
||||
|
||||
err = NewThunkEntry(&sfont->id);
|
||||
if(err == AL_NO_ERROR)
|
||||
err = InsertUIntMapEntry(&device->SfontMap, sfont->id, sfont);
|
||||
if(err != AL_NO_ERROR)
|
||||
{
|
||||
ALsoundfont_Destruct(sfont);
|
||||
memset(sfont, 0, sizeof(ALsoundfont));
|
||||
free(sfont);
|
||||
|
||||
alDeleteSoundfontsSOFT(cur, ids);
|
||||
SET_ERROR_AND_GOTO(context, err, done);
|
||||
}
|
||||
|
||||
ids[cur] = sfont->id;
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alDeleteSoundfontsSOFT(ALsizei n, const ALuint *ids)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsoundfont *sfont;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
/* Check for valid soundfont ID */
|
||||
if(ids[i] == 0)
|
||||
{
|
||||
if(!(sfont=device->DefaultSfont))
|
||||
continue;
|
||||
}
|
||||
else if((sfont=LookupSfont(device, ids[i])) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
if(ReadRef(&sfont->ref) != 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
}
|
||||
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
if(ids[i] == 0)
|
||||
{
|
||||
MidiSynth *synth = device->Synth;
|
||||
WriteLock(&synth->Lock);
|
||||
if(device->DefaultSfont != NULL)
|
||||
ALsoundfont_deleteSoundfont(device->DefaultSfont, device);
|
||||
device->DefaultSfont = NULL;
|
||||
WriteUnlock(&synth->Lock);
|
||||
continue;
|
||||
}
|
||||
else if((sfont=RemoveSfont(device, ids[i])) == NULL)
|
||||
continue;
|
||||
|
||||
ALsoundfont_Destruct(sfont);
|
||||
|
||||
memset(sfont, 0, sizeof(*sfont));
|
||||
free(sfont);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALboolean AL_APIENTRY alIsSoundfontSOFT(ALuint id)
|
||||
{
|
||||
ALCcontext *context;
|
||||
ALboolean ret;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return AL_FALSE;
|
||||
|
||||
ret = ((!id || LookupSfont(context->Device, id)) ?
|
||||
AL_TRUE : AL_FALSE);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alGetSoundfontivSOFT(ALuint id, ALenum param, ALint *values)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsoundfont *sfont;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if(id == 0)
|
||||
sfont = ALsoundfont_getDefSoundfont(context);
|
||||
else if(!(sfont=LookupSfont(device, id)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
switch(param)
|
||||
{
|
||||
case AL_PRESETS_SIZE_SOFT:
|
||||
values[0] = sfont->NumPresets;
|
||||
break;
|
||||
|
||||
case AL_PRESETS_SOFT:
|
||||
for(i = 0;i < sfont->NumPresets;i++)
|
||||
values[i] = sfont->Presets[i]->id;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alSoundfontPresetsSOFT(ALuint id, ALsizei count, const ALuint *pids)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsoundfont *sfont;
|
||||
ALsfpreset **presets;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if(id == 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
if(!(sfont=LookupSfont(device, id)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
if(count < 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
WriteLock(&sfont->Lock);
|
||||
if(ReadRef(&sfont->ref) != 0)
|
||||
{
|
||||
WriteUnlock(&sfont->Lock);
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
}
|
||||
|
||||
if(count == 0)
|
||||
presets = NULL;
|
||||
else
|
||||
{
|
||||
presets = calloc(count, sizeof(presets[0]));
|
||||
if(!presets)
|
||||
{
|
||||
WriteUnlock(&sfont->Lock);
|
||||
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
|
||||
}
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
if(!(presets[i]=LookupPreset(device, pids[i])))
|
||||
{
|
||||
free(presets);
|
||||
WriteUnlock(&sfont->Lock);
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
IncrementRef(&presets[i]->ref);
|
||||
|
||||
presets = ExchangePtr((XchgPtr*)&sfont->Presets, presets);
|
||||
count = ExchangeInt(&sfont->NumPresets, count);
|
||||
WriteUnlock(&sfont->Lock);
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
DecrementRef(&presets[i]->ref);
|
||||
free(presets);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alLoadSoundfontSOFT(ALuint id, size_t(*cb)(ALvoid*,size_t,ALvoid*), ALvoid *user)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsoundfont *sfont;
|
||||
Reader reader;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if(id == 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
if(!(sfont=LookupSfont(device, id)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
|
||||
WriteLock(&sfont->Lock);
|
||||
if(ReadRef(&sfont->ref) != 0)
|
||||
{
|
||||
WriteUnlock(&sfont->Lock);
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
}
|
||||
if(sfont->NumPresets > 0)
|
||||
{
|
||||
WriteUnlock(&sfont->Lock);
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
}
|
||||
|
||||
reader.cb = cb;
|
||||
reader.ptr = user;
|
||||
reader.error = 0;
|
||||
loadSf2(&reader, sfont, context);
|
||||
WriteUnlock(&sfont->Lock);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
static void ALsoundfont_Construct(ALsoundfont *self)
|
||||
{
|
||||
InitRef(&self->ref, 0);
|
||||
|
||||
self->Presets = NULL;
|
||||
self->NumPresets = 0;
|
||||
|
||||
RWLockInit(&self->Lock);
|
||||
|
||||
self->id = 0;
|
||||
}
|
||||
|
||||
static void ALsoundfont_Destruct(ALsoundfont *self)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
FreeThunkEntry(self->id);
|
||||
self->id = 0;
|
||||
|
||||
for(i = 0;i < self->NumPresets;i++)
|
||||
{
|
||||
DecrementRef(&self->Presets[i]->ref);
|
||||
self->Presets[i] = NULL;
|
||||
}
|
||||
free(self->Presets);
|
||||
self->Presets = NULL;
|
||||
self->NumPresets = 0;
|
||||
}
|
||||
|
||||
ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context)
|
||||
{
|
||||
ALCdevice *device = context->Device;
|
||||
al_string fname = AL_STRING_INIT_STATIC();
|
||||
const char *namelist;
|
||||
|
||||
if(device->DefaultSfont)
|
||||
return device->DefaultSfont;
|
||||
|
||||
device->DefaultSfont = calloc(1, sizeof(device->DefaultSfont[0]));
|
||||
ALsoundfont_Construct(device->DefaultSfont);
|
||||
|
||||
namelist = getenv("ALSOFT_SOUNDFONT");
|
||||
if(!namelist || !namelist[0])
|
||||
ConfigValueStr("midi", "soundfont", &namelist);
|
||||
while(namelist && namelist[0])
|
||||
{
|
||||
const char *next, *end;
|
||||
FILE *f;
|
||||
|
||||
while(*namelist && (isspace(*namelist) || *namelist == ','))
|
||||
namelist++;
|
||||
if(!*namelist)
|
||||
break;
|
||||
next = strchr(namelist, ',');
|
||||
end = next ? next++ : (namelist+strlen(namelist));
|
||||
while(--end != namelist && isspace(*end)) {
|
||||
}
|
||||
if(end == namelist)
|
||||
continue;
|
||||
al_string_append_range(&fname, namelist, end+1);
|
||||
namelist = next;
|
||||
|
||||
f = OpenDataFile(al_string_get_cstr(fname), "openal/soundfonts");
|
||||
if(f == NULL)
|
||||
ERR("Failed to open %s\n", al_string_get_cstr(fname));
|
||||
else
|
||||
{
|
||||
Reader reader;
|
||||
reader.cb = ALsoundfont_read;
|
||||
reader.ptr = f;
|
||||
reader.error = 0;
|
||||
TRACE("Loading %s\n", al_string_get_cstr(fname));
|
||||
loadSf2(&reader, device->DefaultSfont, context);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
al_string_clear(&fname);
|
||||
}
|
||||
AL_STRING_DEINIT(fname);
|
||||
|
||||
return device->DefaultSfont;
|
||||
}
|
||||
|
||||
void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device)
|
||||
{
|
||||
ALsfpreset **presets;
|
||||
ALsizei num_presets;
|
||||
VECTOR(ALbuffer*) buffers;
|
||||
ALsizei i;
|
||||
|
||||
VECTOR_INIT(buffers);
|
||||
presets = ExchangePtr((XchgPtr*)&self->Presets, NULL);
|
||||
num_presets = ExchangeInt(&self->NumPresets, 0);
|
||||
|
||||
for(i = 0;i < num_presets;i++)
|
||||
{
|
||||
ALsfpreset *preset = presets[i];
|
||||
ALfontsound **sounds;
|
||||
ALsizei num_sounds;
|
||||
ALboolean deleting;
|
||||
ALsizei j;
|
||||
|
||||
sounds = ExchangePtr((XchgPtr*)&preset->Sounds, NULL);
|
||||
num_sounds = ExchangeInt(&preset->NumSounds, 0);
|
||||
|
||||
DeletePreset(device, preset);
|
||||
preset = NULL;
|
||||
|
||||
for(j = 0;j < num_sounds;j++)
|
||||
DecrementRef(&sounds[j]->ref);
|
||||
/* Some fontsounds may not be immediately deletable because they're
|
||||
* linked to another fontsound. When those fontsounds are deleted
|
||||
* they should become deletable, so use a loop until all fontsounds
|
||||
* are deleted. */
|
||||
do {
|
||||
deleting = AL_FALSE;
|
||||
for(j = 0;j < num_sounds;j++)
|
||||
{
|
||||
if(sounds[j] && ReadRef(&sounds[j]->ref) == 0)
|
||||
{
|
||||
deleting = AL_TRUE;
|
||||
if(sounds[j]->Buffer)
|
||||
{
|
||||
ALbuffer *buffer = sounds[j]->Buffer;
|
||||
ALbuffer **iter;
|
||||
|
||||
#define MATCH_BUFFER(_i) (buffer == *(_i))
|
||||
VECTOR_FIND_IF(iter, ALbuffer*, buffers, MATCH_BUFFER);
|
||||
if(iter == VECTOR_ITER_END(buffers))
|
||||
VECTOR_PUSH_BACK(buffers, buffer);
|
||||
#undef MATCH_BUFFER
|
||||
}
|
||||
DeleteFontsound(device, sounds[j]);
|
||||
sounds[j] = NULL;
|
||||
}
|
||||
}
|
||||
} while(deleting);
|
||||
free(sounds);
|
||||
}
|
||||
|
||||
ALsoundfont_Destruct(self);
|
||||
free(self);
|
||||
|
||||
#define DELETE_BUFFER(iter) do { \
|
||||
assert(ReadRef(&(*(iter))->ref) == 0); \
|
||||
DeleteBuffer(device, *(iter)); \
|
||||
} while(0)
|
||||
VECTOR_FOR_EACH(ALbuffer*, buffers, DELETE_BUFFER);
|
||||
VECTOR_DEINIT(buffers);
|
||||
#undef DELETE_BUFFER
|
||||
}
|
||||
|
||||
|
||||
static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr)
|
||||
{
|
||||
return fread(buf, 1, bytes, (FILE*)ptr);
|
||||
}
|
||||
|
||||
|
||||
/* ReleaseALSoundfonts
|
||||
*
|
||||
* Called to destroy any soundfonts that still exist on the device
|
||||
*/
|
||||
void ReleaseALSoundfonts(ALCdevice *device)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < device->SfontMap.size;i++)
|
||||
{
|
||||
ALsoundfont *temp = device->SfontMap.array[i].value;
|
||||
device->SfontMap.array[i].value = NULL;
|
||||
|
||||
ALsoundfont_Destruct(temp);
|
||||
|
||||
memset(temp, 0, sizeof(*temp));
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,33 +0,0 @@
|
||||
# - Find DirectSound includes and libraries
|
||||
#
|
||||
# DSOUND_FOUND - True if DSOUND_INCLUDE_DIR & DSOUND_LIBRARY are found
|
||||
# DSOUND_LIBRARIES - Set when DSOUND_LIBRARY is found
|
||||
# DSOUND_INCLUDE_DIRS - Set when DSOUND_INCLUDE_DIR is found
|
||||
#
|
||||
# DSOUND_INCLUDE_DIR - where to find dsound.h, etc.
|
||||
# DSOUND_LIBRARY - the dsound library
|
||||
#
|
||||
|
||||
find_path(DSOUND_INCLUDE_DIR
|
||||
PATHS "${DXSDK_DIR}/include"
|
||||
NAMES dsound.h
|
||||
DOC "The DirectSound include directory"
|
||||
)
|
||||
|
||||
find_library(DSOUND_LIBRARY
|
||||
PATHS "${DXSDK_DIR}/lib"
|
||||
NAMES dsound
|
||||
DOC "The DirectSound library"
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(DSound
|
||||
REQUIRED_VARS DSOUND_LIBRARY DSOUND_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(DSOUND_FOUND)
|
||||
set(DSOUND_LIBRARIES ${DSOUND_LIBRARY})
|
||||
set(DSOUND_INCLUDE_DIRS ${DSOUND_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
mark_as_advanced(DSOUND_INCLUDE_DIR DSOUND_LIBRARY)
|
||||
@@ -1,19 +0,0 @@
|
||||
# - Find fluidsynth
|
||||
# Find the native fluidsynth includes and library
|
||||
#
|
||||
# FLUIDSYNTH_INCLUDE_DIR - where to find fluidsynth.h
|
||||
# FLUIDSYNTH_LIBRARIES - List of libraries when using fluidsynth.
|
||||
# FLUIDSYNTH_FOUND - True if fluidsynth found.
|
||||
|
||||
|
||||
FIND_PATH(FLUIDSYNTH_INCLUDE_DIR fluidsynth.h)
|
||||
|
||||
FIND_LIBRARY(FLUIDSYNTH_LIBRARIES NAMES fluidsynth )
|
||||
MARK_AS_ADVANCED( FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR )
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments and set FLUIDSYNTH_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(FluidSynth
|
||||
REQUIRED_VARS FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR)
|
||||
|
||||
@@ -1,380 +0,0 @@
|
||||
# - Locates the SDL_sound library
|
||||
#
|
||||
# This module depends on SDL being found and
|
||||
# must be called AFTER FindSDL.cmake or FindSDL2.cmake is called.
|
||||
#
|
||||
# This module defines
|
||||
# SDL_SOUND_INCLUDE_DIR, where to find SDL_sound.h
|
||||
# SDL_SOUND_FOUND, if false, do not try to link to SDL_sound
|
||||
# SDL_SOUND_LIBRARIES, this contains the list of libraries that you need
|
||||
# to link against. This is a read-only variable and is marked INTERNAL.
|
||||
# SDL_SOUND_EXTRAS, this is an optional variable for you to add your own
|
||||
# flags to SDL_SOUND_LIBRARIES. This is prepended to SDL_SOUND_LIBRARIES.
|
||||
# This is available mostly for cases this module failed to anticipate for
|
||||
# and you must add additional flags. This is marked as ADVANCED.
|
||||
# SDL_SOUND_VERSION_STRING, human-readable string containing the version of SDL_sound
|
||||
#
|
||||
# This module also defines (but you shouldn't need to use directly)
|
||||
# SDL_SOUND_LIBRARY, the name of just the SDL_sound library you would link
|
||||
# against. Use SDL_SOUND_LIBRARIES for you link instructions and not this one.
|
||||
# And might define the following as needed
|
||||
# MIKMOD_LIBRARY
|
||||
# MODPLUG_LIBRARY
|
||||
# OGG_LIBRARY
|
||||
# VORBIS_LIBRARY
|
||||
# SMPEG_LIBRARY
|
||||
# FLAC_LIBRARY
|
||||
# SPEEX_LIBRARY
|
||||
#
|
||||
# Typically, you should not use these variables directly, and you should use
|
||||
# SDL_SOUND_LIBRARIES which contains SDL_SOUND_LIBRARY and the other audio libraries
|
||||
# (if needed) to successfully compile on your system.
|
||||
#
|
||||
# Created by Eric Wing.
|
||||
# This module is a bit more complicated than the other FindSDL* family modules.
|
||||
# The reason is that SDL_sound can be compiled in a large variety of different ways
|
||||
# which are independent of platform. SDL_sound may dynamically link against other 3rd
|
||||
# party libraries to get additional codec support, such as Ogg Vorbis, SMPEG, ModPlug,
|
||||
# MikMod, FLAC, Speex, and potentially others.
|
||||
# Under some circumstances which I don't fully understand,
|
||||
# there seems to be a requirement
|
||||
# that dependent libraries of libraries you use must also be explicitly
|
||||
# linked against in order to successfully compile. SDL_sound does not currently
|
||||
# have any system in place to know how it was compiled.
|
||||
# So this CMake module does the hard work in trying to discover which 3rd party
|
||||
# libraries are required for building (if any).
|
||||
# This module uses a brute force approach to create a test program that uses SDL_sound,
|
||||
# and then tries to build it. If the build fails, it parses the error output for
|
||||
# known symbol names to figure out which libraries are needed.
|
||||
#
|
||||
# Responds to the $SDLDIR and $SDLSOUNDDIR environmental variable that would
|
||||
# correspond to the ./configure --prefix=$SDLDIR used in building SDL.
|
||||
#
|
||||
# On OSX, this will prefer the Framework version (if found) over others.
|
||||
# People will have to manually change the cache values of
|
||||
# SDL_LIBRARY or SDL2_LIBRARY to override this selection or set the CMake
|
||||
# environment CMAKE_INCLUDE_PATH to modify the search paths.
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2005-2009 Kitware, Inc.
|
||||
# Copyright 2012 Benjamin Eikel
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License (the "License");
|
||||
# see accompanying file Copyright.txt for details.
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
#=============================================================================
|
||||
# (To distribute this file outside of CMake, substitute the full
|
||||
# License text for the above reference.)
|
||||
|
||||
set(SDL_SOUND_EXTRAS "" CACHE STRING "SDL_sound extra flags")
|
||||
mark_as_advanced(SDL_SOUND_EXTRAS)
|
||||
|
||||
# Find SDL_sound.h
|
||||
find_path(SDL_SOUND_INCLUDE_DIR SDL_sound.h
|
||||
HINTS
|
||||
ENV SDLSOUNDDIR
|
||||
ENV SDLDIR
|
||||
PATH_SUFFIXES SDL SDL12 SDL11
|
||||
)
|
||||
|
||||
find_library(SDL_SOUND_LIBRARY
|
||||
NAMES SDL_sound
|
||||
HINTS
|
||||
ENV SDLSOUNDDIR
|
||||
ENV SDLDIR
|
||||
)
|
||||
|
||||
if(SDL2_FOUND OR SDL_FOUND)
|
||||
if(SDL_SOUND_INCLUDE_DIR AND SDL_SOUND_LIBRARY)
|
||||
# CMake is giving me problems using TRY_COMPILE with the CMAKE_FLAGS
|
||||
# for the :STRING syntax if I have multiple values contained in a
|
||||
# single variable. This is a problem for the SDL2_LIBRARY variable
|
||||
# because it does just that. When I feed this variable to the command,
|
||||
# only the first value gets the appropriate modifier (e.g. -I) and
|
||||
# the rest get dropped.
|
||||
# To get multiple single variables to work, I must separate them with a "\;"
|
||||
# I could go back and modify the FindSDL2.cmake module, but that's kind of painful.
|
||||
# The solution would be to try something like:
|
||||
# set(SDL2_TRY_COMPILE_LIBRARY_LIST "${SDL2_TRY_COMPILE_LIBRARY_LIST}\;${CMAKE_THREAD_LIBS_INIT}")
|
||||
# Instead, it was suggested on the mailing list to write a temporary CMakeLists.txt
|
||||
# with a temporary test project and invoke that with TRY_COMPILE.
|
||||
# See message thread "Figuring out dependencies for a library in order to build"
|
||||
# 2005-07-16
|
||||
# try_compile(
|
||||
# MY_RESULT
|
||||
# ${CMAKE_BINARY_DIR}
|
||||
# ${PROJECT_SOURCE_DIR}/DetermineSoundLibs.c
|
||||
# CMAKE_FLAGS
|
||||
# -DINCLUDE_DIRECTORIES:STRING=${SDL2_INCLUDE_DIR}\;${SDL_SOUND_INCLUDE_DIR}
|
||||
# -DLINK_LIBRARIES:STRING=${SDL_SOUND_LIBRARY}\;${SDL2_LIBRARY}
|
||||
# OUTPUT_VARIABLE MY_OUTPUT
|
||||
# )
|
||||
|
||||
# To minimize external dependencies, create a sdlsound test program
|
||||
# which will be used to figure out if additional link dependencies are
|
||||
# required for the link phase.
|
||||
file(WRITE ${PROJECT_BINARY_DIR}/CMakeTmp/DetermineSoundLibs.c
|
||||
"#include \"SDL_sound.h\"
|
||||
#include \"SDL.h\"
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
Sound_AudioInfo desired;
|
||||
Sound_Sample* sample;
|
||||
|
||||
SDL_Init(0);
|
||||
Sound_Init();
|
||||
|
||||
/* This doesn't actually have to work, but Init() is a no-op
|
||||
* for some of the decoders, so this should force more symbols
|
||||
* to be pulled in.
|
||||
*/
|
||||
sample = Sound_NewSampleFromFile(argv[1], &desired, 4096);
|
||||
|
||||
Sound_Quit();
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
}"
|
||||
)
|
||||
|
||||
# Calling
|
||||
# target_link_libraries(DetermineSoundLibs "${SDL_SOUND_LIBRARY} ${SDL2_LIBRARY})
|
||||
# causes problems when SDL2_LIBRARY looks like
|
||||
# /Library/Frameworks/SDL2.framework;-framework Cocoa
|
||||
# The ;-framework Cocoa seems to be confusing CMake once the OS X
|
||||
# framework support was added. I was told that breaking up the list
|
||||
# would fix the problem.
|
||||
set(TMP_TRY_LIBS)
|
||||
if(SDL2_FOUND)
|
||||
foreach(lib ${SDL_SOUND_LIBRARY} ${SDL2_LIBRARY})
|
||||
set(TMP_TRY_LIBS "${TMP_TRY_LIBS} \"${lib}\"")
|
||||
endforeach()
|
||||
set(TMP_INCLUDE_DIRS ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR})
|
||||
else()
|
||||
foreach(lib ${SDL_SOUND_LIBRARY} ${SDL_LIBRARY})
|
||||
set(TMP_TRY_LIBS "${TMP_TRY_LIBS} \"${lib}\"")
|
||||
endforeach()
|
||||
set(TMP_INCLUDE_DIRS ${SDL_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
# message("TMP_TRY_LIBS ${TMP_TRY_LIBS}")
|
||||
|
||||
# Write the CMakeLists.txt and test project
|
||||
# Weird, this is still sketchy. If I don't quote the variables
|
||||
# in the TARGET_LINK_LIBRARIES, I seem to loose everything
|
||||
# in the SDL2_LIBRARY string after the "-framework".
|
||||
# But if I quote the stuff in INCLUDE_DIRECTORIES, it doesn't work.
|
||||
file(WRITE ${PROJECT_BINARY_DIR}/CMakeTmp/CMakeLists.txt
|
||||
"cmake_minimum_required(VERSION 2.8)
|
||||
project(DetermineSoundLibs C)
|
||||
include_directories(${TMP_INCLUDE_DIRS})
|
||||
add_executable(DetermineSoundLibs DetermineSoundLibs.c)
|
||||
target_link_libraries(DetermineSoundLibs ${TMP_TRY_LIBS})"
|
||||
)
|
||||
unset(TMP_INCLUDE_DIRS)
|
||||
unset(TMP_TRY_LIBS)
|
||||
|
||||
try_compile(
|
||||
MY_RESULT
|
||||
${PROJECT_BINARY_DIR}/CMakeTmp
|
||||
${PROJECT_BINARY_DIR}/CMakeTmp
|
||||
DetermineSoundLibs
|
||||
OUTPUT_VARIABLE MY_OUTPUT
|
||||
)
|
||||
# message("${MY_RESULT}")
|
||||
# message(${MY_OUTPUT})
|
||||
|
||||
if(NOT MY_RESULT)
|
||||
# I expect that MPGLIB, VOC, WAV, AIFF, and SHN are compiled in statically.
|
||||
# I think Timidity is also compiled in statically.
|
||||
# I've never had to explcitly link against Quicktime, so I'll skip that for now.
|
||||
|
||||
set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARY})
|
||||
|
||||
# Find MikMod
|
||||
if("${MY_OUTPUT}" MATCHES "MikMod_")
|
||||
find_library(MIKMOD_LIBRARY
|
||||
NAMES libmikmod-coreaudio mikmod
|
||||
PATHS
|
||||
ENV MIKMODDIR
|
||||
ENV SDLSOUNDDIR
|
||||
ENV SDLDIR
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
PATH_SUFFIXES lib
|
||||
)
|
||||
if(MIKMOD_LIBRARY)
|
||||
set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${MIKMOD_LIBRARY})
|
||||
endif(MIKMOD_LIBRARY)
|
||||
endif("${MY_OUTPUT}" MATCHES "MikMod_")
|
||||
|
||||
# Find ModPlug
|
||||
if("${MY_OUTPUT}" MATCHES "MODPLUG_")
|
||||
find_library(MODPLUG_LIBRARY
|
||||
NAMES modplug
|
||||
PATHS
|
||||
ENV MODPLUGDIR
|
||||
ENV SDLSOUNDDIR
|
||||
ENV SDLDIR
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
PATH_SUFFIXES lib
|
||||
)
|
||||
if(MODPLUG_LIBRARY)
|
||||
set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${MODPLUG_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Find Ogg and Vorbis
|
||||
if("${MY_OUTPUT}" MATCHES "ov_")
|
||||
find_library(VORBIS_LIBRARY
|
||||
NAMES vorbis Vorbis VORBIS
|
||||
PATHS
|
||||
ENV VORBISDIR
|
||||
ENV OGGDIR
|
||||
ENV SDLSOUNDDIR
|
||||
ENV SDLDIR
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
PATH_SUFFIXES lib
|
||||
)
|
||||
if(VORBIS_LIBRARY)
|
||||
set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${VORBIS_LIBRARY})
|
||||
endif()
|
||||
find_library(OGG_LIBRARY
|
||||
NAMES ogg Ogg OGG
|
||||
PATHS
|
||||
ENV OGGDIR
|
||||
ENV VORBISDIR
|
||||
ENV SDLSOUNDDIR
|
||||
ENV SDLDIR
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
PATH_SUFFIXES lib
|
||||
)
|
||||
if(OGG_LIBRARY)
|
||||
set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${OGG_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Find SMPEG
|
||||
if("${MY_OUTPUT}" MATCHES "SMPEG_")
|
||||
find_library(SMPEG_LIBRARY
|
||||
NAMES smpeg SMPEG Smpeg SMpeg
|
||||
PATHS
|
||||
ENV SMPEGDIR
|
||||
ENV SDLSOUNDDIR
|
||||
ENV SDLDIR
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
PATH_SUFFIXES lib
|
||||
)
|
||||
if(SMPEG_LIBRARY)
|
||||
set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${SMPEG_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Find FLAC
|
||||
if("${MY_OUTPUT}" MATCHES "FLAC_")
|
||||
find_library(FLAC_LIBRARY
|
||||
NAMES flac FLAC
|
||||
PATHS
|
||||
ENV FLACDIR
|
||||
ENV SDLSOUNDDIR
|
||||
ENV SDLDIR
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
PATH_SUFFIXES lib
|
||||
)
|
||||
if(FLAC_LIBRARY)
|
||||
set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${FLAC_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Hmmm...Speex seems to depend on Ogg. This might be a problem if
|
||||
# the TRY_COMPILE attempt gets blocked at SPEEX before it can pull
|
||||
# in the Ogg symbols. I'm not sure if I should duplicate the ogg stuff
|
||||
# above for here or if two ogg entries will screw up things.
|
||||
if("${MY_OUTPUT}" MATCHES "speex_")
|
||||
find_library(SPEEX_LIBRARY
|
||||
NAMES speex SPEEX
|
||||
PATHS
|
||||
ENV SPEEXDIR
|
||||
ENV SDLSOUNDDIR
|
||||
ENV SDLDIR
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
PATH_SUFFIXES lib
|
||||
)
|
||||
if(SPEEX_LIBRARY)
|
||||
set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${SPEEX_LIBRARY})
|
||||
endif()
|
||||
|
||||
# Find OGG (needed for Speex)
|
||||
# We might have already found Ogg for Vorbis, so skip it if so.
|
||||
if(NOT OGG_LIBRARY)
|
||||
find_library(OGG_LIBRARY
|
||||
NAMES ogg Ogg OGG
|
||||
PATHS
|
||||
ENV OGGDIR
|
||||
ENV VORBISDIR
|
||||
ENV SPEEXDIR
|
||||
ENV SDLSOUNDDIR
|
||||
ENV SDLDIR
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
PATH_SUFFIXES lib
|
||||
)
|
||||
if(OGG_LIBRARY)
|
||||
set(SDL_SOUND_LIBRARIES_TMP ${SDL_SOUND_LIBRARIES_TMP} ${OGG_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(SDL_SOUND_LIBRARIES ${SDL_SOUND_EXTRAS} ${SDL_SOUND_LIBRARIES_TMP} CACHE INTERNAL "SDL_sound and dependent libraries")
|
||||
else()
|
||||
set(SDL_SOUND_LIBRARIES ${SDL_SOUND_EXTRAS} ${SDL_SOUND_LIBRARY} CACHE INTERNAL "SDL_sound and dependent libraries")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(SDL_SOUND_INCLUDE_DIR AND EXISTS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h")
|
||||
file(STRINGS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h" SDL_SOUND_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SOUND_VER_MAJOR[ \t]+[0-9]+$")
|
||||
file(STRINGS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h" SDL_SOUND_VERSION_MINOR_LINE REGEX "^#define[ \t]+SOUND_VER_MINOR[ \t]+[0-9]+$")
|
||||
file(STRINGS "${SDL_SOUND_INCLUDE_DIR}/SDL_sound.h" SDL_SOUND_VERSION_PATCH_LINE REGEX "^#define[ \t]+SOUND_VER_PATCH[ \t]+[0-9]+$")
|
||||
string(REGEX REPLACE "^#define[ \t]+SOUND_VER_MAJOR[ \t]+([0-9]+)$" "\\1" SDL_SOUND_VERSION_MAJOR "${SDL_SOUND_VERSION_MAJOR_LINE}")
|
||||
string(REGEX REPLACE "^#define[ \t]+SOUND_VER_MINOR[ \t]+([0-9]+)$" "\\1" SDL_SOUND_VERSION_MINOR "${SDL_SOUND_VERSION_MINOR_LINE}")
|
||||
string(REGEX REPLACE "^#define[ \t]+SOUND_VER_PATCH[ \t]+([0-9]+)$" "\\1" SDL_SOUND_VERSION_PATCH "${SDL_SOUND_VERSION_PATCH_LINE}")
|
||||
set(SDL_SOUND_VERSION_STRING ${SDL_SOUND_VERSION_MAJOR}.${SDL_SOUND_VERSION_MINOR}.${SDL_SOUND_VERSION_PATCH})
|
||||
unset(SDL_SOUND_VERSION_MAJOR_LINE)
|
||||
unset(SDL_SOUND_VERSION_MINOR_LINE)
|
||||
unset(SDL_SOUND_VERSION_PATCH_LINE)
|
||||
unset(SDL_SOUND_VERSION_MAJOR)
|
||||
unset(SDL_SOUND_VERSION_MINOR)
|
||||
unset(SDL_SOUND_VERSION_PATCH)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL_sound
|
||||
REQUIRED_VARS SDL_SOUND_LIBRARIES SDL_SOUND_INCLUDE_DIR
|
||||
VERSION_VAR SDL_SOUND_VERSION_STRING)
|
||||
@@ -1,144 +0,0 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "uintmap.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
extern inline void LockUIntMapRead(UIntMap *map);
|
||||
extern inline void UnlockUIntMapRead(UIntMap *map);
|
||||
extern inline void LockUIntMapWrite(UIntMap *map);
|
||||
extern inline void UnlockUIntMapWrite(UIntMap *map);
|
||||
|
||||
|
||||
void InitUIntMap(UIntMap *map, ALsizei limit)
|
||||
{
|
||||
map->array = NULL;
|
||||
map->size = 0;
|
||||
map->maxsize = 0;
|
||||
map->limit = limit;
|
||||
RWLockInit(&map->lock);
|
||||
}
|
||||
|
||||
void ResetUIntMap(UIntMap *map)
|
||||
{
|
||||
WriteLock(&map->lock);
|
||||
free(map->array);
|
||||
map->array = NULL;
|
||||
map->size = 0;
|
||||
map->maxsize = 0;
|
||||
WriteUnlock(&map->lock);
|
||||
}
|
||||
|
||||
ALenum InsertUIntMapEntry(UIntMap *map, ALuint key, ALvoid *value)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
|
||||
WriteLock(&map->lock);
|
||||
if(map->size > 0)
|
||||
{
|
||||
ALsizei low = 0;
|
||||
ALsizei high = map->size - 1;
|
||||
while(low < high)
|
||||
{
|
||||
ALsizei mid = low + (high-low)/2;
|
||||
if(map->array[mid].key < key)
|
||||
low = mid + 1;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
if(map->array[low].key < key)
|
||||
low++;
|
||||
pos = low;
|
||||
}
|
||||
|
||||
if(pos == map->size || map->array[pos].key != key)
|
||||
{
|
||||
if(map->size == map->limit)
|
||||
{
|
||||
WriteUnlock(&map->lock);
|
||||
return AL_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if(map->size == map->maxsize)
|
||||
{
|
||||
ALvoid *temp = NULL;
|
||||
ALsizei newsize;
|
||||
|
||||
newsize = (map->maxsize ? (map->maxsize<<1) : 4);
|
||||
if(newsize >= map->maxsize)
|
||||
temp = realloc(map->array, newsize*sizeof(map->array[0]));
|
||||
if(!temp)
|
||||
{
|
||||
WriteUnlock(&map->lock);
|
||||
return AL_OUT_OF_MEMORY;
|
||||
}
|
||||
map->array = temp;
|
||||
map->maxsize = newsize;
|
||||
}
|
||||
|
||||
if(pos < map->size)
|
||||
memmove(&map->array[pos+1], &map->array[pos],
|
||||
(map->size-pos)*sizeof(map->array[0]));
|
||||
map->size++;
|
||||
}
|
||||
map->array[pos].key = key;
|
||||
map->array[pos].value = value;
|
||||
WriteUnlock(&map->lock);
|
||||
|
||||
return AL_NO_ERROR;
|
||||
}
|
||||
|
||||
ALvoid *RemoveUIntMapKey(UIntMap *map, ALuint key)
|
||||
{
|
||||
ALvoid *ptr = NULL;
|
||||
WriteLock(&map->lock);
|
||||
if(map->size > 0)
|
||||
{
|
||||
ALsizei low = 0;
|
||||
ALsizei high = map->size - 1;
|
||||
while(low < high)
|
||||
{
|
||||
ALsizei mid = low + (high-low)/2;
|
||||
if(map->array[mid].key < key)
|
||||
low = mid + 1;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
if(map->array[low].key == key)
|
||||
{
|
||||
ptr = map->array[low].value;
|
||||
if(low < map->size-1)
|
||||
memmove(&map->array[low], &map->array[low+1],
|
||||
(map->size-1-low)*sizeof(map->array[0]));
|
||||
map->size--;
|
||||
}
|
||||
}
|
||||
WriteUnlock(&map->lock);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
ALvoid *LookupUIntMapKey(UIntMap *map, ALuint key)
|
||||
{
|
||||
ALvoid *ptr = NULL;
|
||||
ReadLock(&map->lock);
|
||||
if(map->size > 0)
|
||||
{
|
||||
ALsizei low = 0;
|
||||
ALsizei high = map->size - 1;
|
||||
while(low < high)
|
||||
{
|
||||
ALsizei mid = low + (high-low)/2;
|
||||
if(map->array[mid].key < key)
|
||||
low = mid + 1;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
if(map->array[low].key == key)
|
||||
ptr = map->array[low].value;
|
||||
}
|
||||
ReadUnlock(&map->lock);
|
||||
return ptr;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,327 +0,0 @@
|
||||
/*
|
||||
* OpenAL Helpers
|
||||
*
|
||||
* Copyright (c) 2011 by Chris Robinson <chris.kcat@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* This file contains routines to help with some menial OpenAL-related tasks,
|
||||
* such as opening a device and setting up a context, closing the device and
|
||||
* destroying its context, converting between frame counts and byte lengths,
|
||||
* finding an appropriate buffer format, and getting readable strings for
|
||||
* channel configs and sample types. */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "alhelpers.h"
|
||||
|
||||
|
||||
/* InitAL opens the default device and sets up a context using default
|
||||
* attributes, making the program ready to call OpenAL functions. */
|
||||
int InitAL(void)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *ctx;
|
||||
|
||||
/* Open and initialize a device with default settings */
|
||||
device = alcOpenDevice(NULL);
|
||||
if(!device)
|
||||
{
|
||||
fprintf(stderr, "Could not open a device!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx = alcCreateContext(device, NULL);
|
||||
if(ctx == NULL || alcMakeContextCurrent(ctx) == ALC_FALSE)
|
||||
{
|
||||
if(ctx != NULL)
|
||||
alcDestroyContext(ctx);
|
||||
alcCloseDevice(device);
|
||||
fprintf(stderr, "Could not set a context!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Opened \"%s\"\n", alcGetString(device, ALC_DEVICE_SPECIFIER));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* CloseAL closes the device belonging to the current context, and destroys the
|
||||
* context. */
|
||||
void CloseAL(void)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *ctx;
|
||||
|
||||
ctx = alcGetCurrentContext();
|
||||
if(ctx == NULL)
|
||||
return;
|
||||
|
||||
device = alcGetContextsDevice(ctx);
|
||||
|
||||
alcMakeContextCurrent(NULL);
|
||||
alcDestroyContext(ctx);
|
||||
alcCloseDevice(device);
|
||||
}
|
||||
|
||||
|
||||
/* GetFormat retrieves a compatible buffer format given the channel config and
|
||||
* sample type. If an alIsBufferFormatSupportedSOFT-compatible function is
|
||||
* provided, it will be called to find the closest-matching format from
|
||||
* AL_SOFT_buffer_samples. Returns AL_NONE (0) if no supported format can be
|
||||
* found. */
|
||||
ALenum GetFormat(ALenum channels, ALenum type, LPALISBUFFERFORMATSUPPORTEDSOFT palIsBufferFormatSupportedSOFT)
|
||||
{
|
||||
ALenum format = AL_NONE;
|
||||
|
||||
/* If using AL_SOFT_buffer_samples, try looking through its formats */
|
||||
if(palIsBufferFormatSupportedSOFT)
|
||||
{
|
||||
/* AL_SOFT_buffer_samples is more lenient with matching formats. The
|
||||
* specified sample type does not need to match the returned format,
|
||||
* but it is nice to try to get something close. */
|
||||
if(type == AL_UNSIGNED_BYTE_SOFT || type == AL_BYTE_SOFT)
|
||||
{
|
||||
if(channels == AL_MONO_SOFT) format = AL_MONO8_SOFT;
|
||||
else if(channels == AL_STEREO_SOFT) format = AL_STEREO8_SOFT;
|
||||
else if(channels == AL_QUAD_SOFT) format = AL_QUAD8_SOFT;
|
||||
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_8_SOFT;
|
||||
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_8_SOFT;
|
||||
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_8_SOFT;
|
||||
}
|
||||
else if(type == AL_UNSIGNED_SHORT_SOFT || type == AL_SHORT_SOFT)
|
||||
{
|
||||
if(channels == AL_MONO_SOFT) format = AL_MONO16_SOFT;
|
||||
else if(channels == AL_STEREO_SOFT) format = AL_STEREO16_SOFT;
|
||||
else if(channels == AL_QUAD_SOFT) format = AL_QUAD16_SOFT;
|
||||
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_16_SOFT;
|
||||
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_16_SOFT;
|
||||
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_16_SOFT;
|
||||
}
|
||||
else if(type == AL_UNSIGNED_BYTE3_SOFT || type == AL_BYTE3_SOFT ||
|
||||
type == AL_UNSIGNED_INT_SOFT || type == AL_INT_SOFT ||
|
||||
type == AL_FLOAT_SOFT || type == AL_DOUBLE_SOFT)
|
||||
{
|
||||
if(channels == AL_MONO_SOFT) format = AL_MONO32F_SOFT;
|
||||
else if(channels == AL_STEREO_SOFT) format = AL_STEREO32F_SOFT;
|
||||
else if(channels == AL_QUAD_SOFT) format = AL_QUAD32F_SOFT;
|
||||
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_32F_SOFT;
|
||||
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_32F_SOFT;
|
||||
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_32F_SOFT;
|
||||
}
|
||||
|
||||
if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format))
|
||||
format = AL_NONE;
|
||||
|
||||
/* A matching format was not found or supported. Try 32-bit float. */
|
||||
if(format == AL_NONE)
|
||||
{
|
||||
if(channels == AL_MONO_SOFT) format = AL_MONO32F_SOFT;
|
||||
else if(channels == AL_STEREO_SOFT) format = AL_STEREO32F_SOFT;
|
||||
else if(channels == AL_QUAD_SOFT) format = AL_QUAD32F_SOFT;
|
||||
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_32F_SOFT;
|
||||
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_32F_SOFT;
|
||||
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_32F_SOFT;
|
||||
|
||||
if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format))
|
||||
format = AL_NONE;
|
||||
}
|
||||
/* 32-bit float not supported. Try 16-bit int. */
|
||||
if(format == AL_NONE)
|
||||
{
|
||||
if(channels == AL_MONO_SOFT) format = AL_MONO16_SOFT;
|
||||
else if(channels == AL_STEREO_SOFT) format = AL_STEREO16_SOFT;
|
||||
else if(channels == AL_QUAD_SOFT) format = AL_QUAD16_SOFT;
|
||||
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_16_SOFT;
|
||||
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_16_SOFT;
|
||||
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_16_SOFT;
|
||||
|
||||
if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format))
|
||||
format = AL_NONE;
|
||||
}
|
||||
/* 16-bit int not supported. Try 8-bit int. */
|
||||
if(format == AL_NONE)
|
||||
{
|
||||
if(channels == AL_MONO_SOFT) format = AL_MONO8_SOFT;
|
||||
else if(channels == AL_STEREO_SOFT) format = AL_STEREO8_SOFT;
|
||||
else if(channels == AL_QUAD_SOFT) format = AL_QUAD8_SOFT;
|
||||
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_8_SOFT;
|
||||
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_8_SOFT;
|
||||
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_8_SOFT;
|
||||
|
||||
if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format))
|
||||
format = AL_NONE;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
/* We use the AL_EXT_MCFORMATS extension to provide output of Quad, 5.1,
|
||||
* and 7.1 channel configs, AL_EXT_FLOAT32 for 32-bit float samples, and
|
||||
* AL_EXT_DOUBLE for 64-bit float samples. */
|
||||
if(type == AL_UNSIGNED_BYTE_SOFT)
|
||||
{
|
||||
if(channels == AL_MONO_SOFT)
|
||||
format = AL_FORMAT_MONO8;
|
||||
else if(channels == AL_STEREO_SOFT)
|
||||
format = AL_FORMAT_STEREO8;
|
||||
else if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
|
||||
{
|
||||
if(channels == AL_QUAD_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_QUAD8");
|
||||
else if(channels == AL_5POINT1_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_51CHN8");
|
||||
else if(channels == AL_6POINT1_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_61CHN8");
|
||||
else if(channels == AL_7POINT1_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_71CHN8");
|
||||
}
|
||||
}
|
||||
else if(type == AL_SHORT_SOFT)
|
||||
{
|
||||
if(channels == AL_MONO_SOFT)
|
||||
format = AL_FORMAT_MONO16;
|
||||
else if(channels == AL_STEREO_SOFT)
|
||||
format = AL_FORMAT_STEREO16;
|
||||
else if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
|
||||
{
|
||||
if(channels == AL_QUAD_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_QUAD16");
|
||||
else if(channels == AL_5POINT1_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_51CHN16");
|
||||
else if(channels == AL_6POINT1_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_61CHN16");
|
||||
else if(channels == AL_7POINT1_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_71CHN16");
|
||||
}
|
||||
}
|
||||
else if(type == AL_FLOAT_SOFT && alIsExtensionPresent("AL_EXT_FLOAT32"))
|
||||
{
|
||||
if(channels == AL_MONO_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_MONO_FLOAT32");
|
||||
else if(channels == AL_STEREO_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_STEREO_FLOAT32");
|
||||
else if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
|
||||
{
|
||||
if(channels == AL_QUAD_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_QUAD32");
|
||||
else if(channels == AL_5POINT1_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_51CHN32");
|
||||
else if(channels == AL_6POINT1_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_61CHN32");
|
||||
else if(channels == AL_7POINT1_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_71CHN32");
|
||||
}
|
||||
}
|
||||
else if(type == AL_DOUBLE_SOFT && alIsExtensionPresent("AL_EXT_DOUBLE"))
|
||||
{
|
||||
if(channels == AL_MONO_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_MONO_DOUBLE");
|
||||
else if(channels == AL_STEREO_SOFT)
|
||||
format = alGetEnumValue("AL_FORMAT_STEREO_DOUBLE");
|
||||
}
|
||||
|
||||
/* NOTE: It seems OSX returns -1 from alGetEnumValue for unknown enums, as
|
||||
* opposed to 0. Correct it. */
|
||||
if(format == -1)
|
||||
format = 0;
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
|
||||
void AL_APIENTRY wrap_BufferSamples(ALuint buffer, ALuint samplerate,
|
||||
ALenum internalformat, ALsizei samples,
|
||||
ALenum channels, ALenum type,
|
||||
const ALvoid *data)
|
||||
{
|
||||
alBufferData(buffer, internalformat, data,
|
||||
FramesToBytes(samples, channels, type),
|
||||
samplerate);
|
||||
}
|
||||
|
||||
|
||||
const char *ChannelsName(ALenum chans)
|
||||
{
|
||||
switch(chans)
|
||||
{
|
||||
case AL_MONO_SOFT: return "Mono";
|
||||
case AL_STEREO_SOFT: return "Stereo";
|
||||
case AL_REAR_SOFT: return "Rear";
|
||||
case AL_QUAD_SOFT: return "Quadraphonic";
|
||||
case AL_5POINT1_SOFT: return "5.1 Surround";
|
||||
case AL_6POINT1_SOFT: return "6.1 Surround";
|
||||
case AL_7POINT1_SOFT: return "7.1 Surround";
|
||||
}
|
||||
return "Unknown Channels";
|
||||
}
|
||||
|
||||
const char *TypeName(ALenum type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case AL_BYTE_SOFT: return "S8";
|
||||
case AL_UNSIGNED_BYTE_SOFT: return "U8";
|
||||
case AL_SHORT_SOFT: return "S16";
|
||||
case AL_UNSIGNED_SHORT_SOFT: return "U16";
|
||||
case AL_INT_SOFT: return "S32";
|
||||
case AL_UNSIGNED_INT_SOFT: return "U32";
|
||||
case AL_FLOAT_SOFT: return "Float32";
|
||||
case AL_DOUBLE_SOFT: return "Float64";
|
||||
}
|
||||
return "Unknown Type";
|
||||
}
|
||||
|
||||
|
||||
ALsizei FramesToBytes(ALsizei size, ALenum channels, ALenum type)
|
||||
{
|
||||
switch(channels)
|
||||
{
|
||||
case AL_MONO_SOFT: size *= 1; break;
|
||||
case AL_STEREO_SOFT: size *= 2; break;
|
||||
case AL_REAR_SOFT: size *= 2; break;
|
||||
case AL_QUAD_SOFT: size *= 4; break;
|
||||
case AL_5POINT1_SOFT: size *= 6; break;
|
||||
case AL_6POINT1_SOFT: size *= 7; break;
|
||||
case AL_7POINT1_SOFT: size *= 8; break;
|
||||
}
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case AL_BYTE_SOFT: size *= sizeof(ALbyte); break;
|
||||
case AL_UNSIGNED_BYTE_SOFT: size *= sizeof(ALubyte); break;
|
||||
case AL_SHORT_SOFT: size *= sizeof(ALshort); break;
|
||||
case AL_UNSIGNED_SHORT_SOFT: size *= sizeof(ALushort); break;
|
||||
case AL_INT_SOFT: size *= sizeof(ALint); break;
|
||||
case AL_UNSIGNED_INT_SOFT: size *= sizeof(ALuint); break;
|
||||
case AL_FLOAT_SOFT: size *= sizeof(ALfloat); break;
|
||||
case AL_DOUBLE_SOFT: size *= sizeof(ALdouble); break;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
ALsizei BytesToFrames(ALsizei size, ALenum channels, ALenum type)
|
||||
{
|
||||
return size / FramesToBytes(1, channels, type);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
#ifndef ALHELPERS_H
|
||||
#define ALHELPERS_H
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#define Sleep(x) usleep((x)*1000)
|
||||
#else
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "AL/alc.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* Some helper functions to get the name from the channel and type enums. */
|
||||
const char *ChannelsName(ALenum chans);
|
||||
const char *TypeName(ALenum type);
|
||||
|
||||
/* Helpers to convert frame counts and byte lengths. */
|
||||
ALsizei FramesToBytes(ALsizei size, ALenum channels, ALenum type);
|
||||
ALsizei BytesToFrames(ALsizei size, ALenum channels, ALenum type);
|
||||
|
||||
/* Retrieves a compatible buffer format given the channel configuration and
|
||||
* sample type. If an alIsBufferFormatSupportedSOFT-compatible function is
|
||||
* provided, it will be called to find the closest-matching format from
|
||||
* AL_SOFT_buffer_samples. Returns AL_NONE (0) if no supported format can be
|
||||
* found. */
|
||||
ALenum GetFormat(ALenum channels, ALenum type, LPALISBUFFERFORMATSUPPORTEDSOFT palIsBufferFormatSupportedSOFT);
|
||||
|
||||
/* Loads samples into a buffer using the standard alBufferData call, but with a
|
||||
* LPALBUFFERSAMPLESSOFT-compatible prototype. Assumes internalformat is valid
|
||||
* for alBufferData, and that channels and type match it. */
|
||||
void AL_APIENTRY wrap_BufferSamples(ALuint buffer, ALuint samplerate,
|
||||
ALenum internalformat, ALsizei samples,
|
||||
ALenum channels, ALenum type,
|
||||
const ALvoid *data);
|
||||
|
||||
/* Easy device init/deinit functions. InitAL returns 0 on success. */
|
||||
int InitAL(void);
|
||||
void CloseAL(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* ALHELPERS_H */
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* SDL_sound Decoder Helpers
|
||||
*
|
||||
* Copyright (c) 2013 by Chris Robinson <chris.kcat@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* This file contains routines for helping to decode audio using SDL_sound.
|
||||
* There's very little OpenAL-specific code here.
|
||||
*/
|
||||
#include "sdl_sound.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <SDL_sound.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "alhelpers.h"
|
||||
|
||||
|
||||
static int done_init = 0;
|
||||
|
||||
FilePtr openAudioFile(const char *fname, size_t buftime_ms)
|
||||
{
|
||||
FilePtr file;
|
||||
ALuint rate;
|
||||
Uint32 bufsize;
|
||||
ALenum chans, type;
|
||||
|
||||
/* We need to make sure SDL_sound is initialized. */
|
||||
if(!done_init)
|
||||
{
|
||||
Sound_Init();
|
||||
done_init = 1;
|
||||
}
|
||||
|
||||
file = Sound_NewSampleFromFile(fname, NULL, 0);
|
||||
if(!file)
|
||||
{
|
||||
fprintf(stderr, "Failed to open %s: %s\n", fname, Sound_GetError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(getAudioInfo(file, &rate, &chans, &type) != 0)
|
||||
{
|
||||
Sound_FreeSample(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bufsize = FramesToBytes((ALsizei)(buftime_ms/1000.0*rate), chans, type);
|
||||
if(Sound_SetBufferSize(file, bufsize) == 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to set buffer size to %u bytes: %s\n", bufsize, Sound_GetError());
|
||||
Sound_FreeSample(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
void closeAudioFile(FilePtr file)
|
||||
{
|
||||
if(file)
|
||||
Sound_FreeSample(file);
|
||||
}
|
||||
|
||||
|
||||
int getAudioInfo(FilePtr file, ALuint *rate, ALenum *channels, ALenum *type)
|
||||
{
|
||||
if(file->actual.channels == 1)
|
||||
*channels = AL_MONO_SOFT;
|
||||
else if(file->actual.channels == 2)
|
||||
*channels = AL_STEREO_SOFT;
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Unsupported channel count: %d\n", file->actual.channels);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(file->actual.format == AUDIO_U8)
|
||||
*type = AL_UNSIGNED_BYTE_SOFT;
|
||||
else if(file->actual.format == AUDIO_S8)
|
||||
*type = AL_BYTE_SOFT;
|
||||
else if(file->actual.format == AUDIO_U16LSB || file->actual.format == AUDIO_U16MSB)
|
||||
*type = AL_UNSIGNED_SHORT_SOFT;
|
||||
else if(file->actual.format == AUDIO_S16LSB || file->actual.format == AUDIO_S16MSB)
|
||||
*type = AL_SHORT_SOFT;
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Unsupported sample format: 0x%04x\n", file->actual.format);
|
||||
return 1;
|
||||
}
|
||||
|
||||
*rate = file->actual.rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint8_t *getAudioData(FilePtr file, size_t *length)
|
||||
{
|
||||
*length = Sound_Decode(file);
|
||||
if(*length == 0)
|
||||
return NULL;
|
||||
if((file->actual.format == AUDIO_U16LSB && AUDIO_U16LSB != AUDIO_U16SYS) ||
|
||||
(file->actual.format == AUDIO_U16MSB && AUDIO_U16MSB != AUDIO_U16SYS) ||
|
||||
(file->actual.format == AUDIO_S16LSB && AUDIO_S16LSB != AUDIO_S16SYS) ||
|
||||
(file->actual.format == AUDIO_S16MSB && AUDIO_S16MSB != AUDIO_S16SYS))
|
||||
{
|
||||
/* Swap bytes if the decoded endianness doesn't match the system. */
|
||||
char *buffer = file->buffer;
|
||||
size_t i;
|
||||
for(i = 0;i < *length;i+=2)
|
||||
{
|
||||
char b = buffer[i];
|
||||
buffer[i] = buffer[i+1];
|
||||
buffer[i+1] = b;
|
||||
}
|
||||
}
|
||||
return file->buffer;
|
||||
}
|
||||
|
||||
void *decodeAudioStream(FilePtr file, size_t *length)
|
||||
{
|
||||
Uint32 got;
|
||||
char *mem;
|
||||
|
||||
got = Sound_DecodeAll(file);
|
||||
if(got == 0)
|
||||
{
|
||||
*length = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mem = malloc(got);
|
||||
memcpy(mem, file->buffer, got);
|
||||
|
||||
*length = got;
|
||||
return mem;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
#ifndef EXAMPLES_SDL_SOUND_H
|
||||
#define EXAMPLES_SDL_SOUND_H
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#include <SDL_sound.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* Opaque handles to files and streams. Apps don't need to concern themselves
|
||||
* with the internals */
|
||||
typedef Sound_Sample *FilePtr;
|
||||
|
||||
/* Opens a file with SDL_sound, and specifies the size of the sample buffer in
|
||||
* milliseconds. */
|
||||
FilePtr openAudioFile(const char *fname, size_t buftime_ms);
|
||||
|
||||
/* Closes/frees an opened file */
|
||||
void closeAudioFile(FilePtr file);
|
||||
|
||||
/* Returns information about the given audio stream. Returns 0 on success. */
|
||||
int getAudioInfo(FilePtr file, ALuint *rate, ALenum *channels, ALenum *type);
|
||||
|
||||
/* Returns a pointer to the next available chunk of decoded audio. The size (in
|
||||
* bytes) of the returned data buffer is stored in 'length', and the returned
|
||||
* pointer is only valid until the next call to getAudioData. */
|
||||
uint8_t *getAudioData(FilePtr file, size_t *length);
|
||||
|
||||
/* Decodes all remaining data from the stream and returns a buffer containing
|
||||
* the audio data, with the size stored in 'length'. The returned pointer must
|
||||
* be freed with a call to free(). Note that since this decodes the whole
|
||||
* stream, using it on lengthy streams (eg, music) will use a lot of memory.
|
||||
* Such streams are better handled using getAudioData to keep smaller chunks in
|
||||
* memory at any given time. */
|
||||
void *decodeAudioStream(FilePtr, size_t *length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* EXAMPLES_SDL_SOUND_H */
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,313 +0,0 @@
|
||||
#ifndef AL_ATOMIC_H
|
||||
#define AL_ATOMIC_H
|
||||
|
||||
#include "static_assert.h"
|
||||
#include "bool.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void *volatile XchgPtr;
|
||||
|
||||
/* Atomics using C11 */
|
||||
#ifdef HAVE_C11_ATOMIC
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
inline int ExchangeInt(volatile int *ptr, int newval)
|
||||
{ return atomic_exchange(ptr, newval); }
|
||||
inline void *ExchangePtr(XchgPtr *ptr, void *newval)
|
||||
{ return atomic_exchange(ptr, newval); }
|
||||
|
||||
|
||||
#define ATOMIC(T) struct { T _Atomic value; }
|
||||
|
||||
#define ATOMIC_INIT(_val, _newval) atomic_init(&(_val)->value, (_newval))
|
||||
#define ATOMIC_INIT_STATIC(_newval) {ATOMIC_VAR_INIT(_newval)}
|
||||
|
||||
#define ATOMIC_LOAD(_val) atomic_load(&(_val)->value)
|
||||
#define ATOMIC_STORE(_val, _newval) atomic_store(&(_val)->value, (_newval))
|
||||
|
||||
#define ATOMIC_ADD(T, _val, _incr) atomic_fetch_add(&(_val)->value, (_incr))
|
||||
#define ATOMIC_SUB(T, _val, _decr) atomic_fetch_sub(&(_val)->value, (_decr))
|
||||
|
||||
#define ATOMIC_EXCHANGE(T, _val, _newval) atomic_exchange(&(_val)->value, (_newval))
|
||||
#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval) \
|
||||
atomic_compare_exchange_strong(&(_val)->value, (_oldval), (_newval))
|
||||
#define ATOMIC_COMPARE_EXCHANGE_WEAK(T, _val, _oldval, _newval) \
|
||||
atomic_compare_exchange_weak(&(_val)->value, (_oldval), (_newval))
|
||||
|
||||
/* Atomics using GCC intrinsics */
|
||||
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && !defined(__QNXNTO__)
|
||||
|
||||
inline int ExchangeInt(volatile int *ptr, int newval)
|
||||
{ return __sync_lock_test_and_set(ptr, newval); }
|
||||
inline void *ExchangePtr(XchgPtr *ptr, void *newval)
|
||||
{ return __sync_lock_test_and_set(ptr, newval); }
|
||||
|
||||
|
||||
#define ATOMIC(T) struct { T volatile value; }
|
||||
|
||||
#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0)
|
||||
#define ATOMIC_INIT_STATIC(_newval) {(_newval)}
|
||||
|
||||
#define ATOMIC_LOAD(_val) __extension__({ \
|
||||
__typeof((_val)->value) _r = (_val)->value; \
|
||||
__asm__ __volatile__("" ::: "memory"); \
|
||||
_r; \
|
||||
})
|
||||
#define ATOMIC_STORE(_val, _newval) do { \
|
||||
__asm__ __volatile__("" ::: "memory"); \
|
||||
(_val)->value = (_newval); \
|
||||
} while(0)
|
||||
|
||||
#define ATOMIC_ADD(T, _val, _incr) __extension__({ \
|
||||
static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \
|
||||
__sync_fetch_and_add(&(_val)->value, (_incr)); \
|
||||
})
|
||||
#define ATOMIC_SUB(T, _val, _decr) __extension__({ \
|
||||
static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \
|
||||
__sync_fetch_and_sub(&(_val)->value, (_decr)); \
|
||||
})
|
||||
|
||||
#define ATOMIC_EXCHANGE(T, _val, _newval) __extension__({ \
|
||||
static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \
|
||||
__sync_lock_test_and_set(&(_val)->value, (_newval)); \
|
||||
})
|
||||
#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval) __extension__({ \
|
||||
static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \
|
||||
T _o = *(_oldval); \
|
||||
*(_oldval) = __sync_val_compare_and_swap(&(_val)->value, _o, (_newval)); \
|
||||
*(_oldval) == _o; \
|
||||
})
|
||||
|
||||
/* Atomics using x86/x86-64 GCC inline assembly */
|
||||
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
|
||||
|
||||
#define WRAP_ADD(ret, dest, incr) __asm__ __volatile__( \
|
||||
"lock; xaddl %0,(%1)" \
|
||||
: "=r" (ret) \
|
||||
: "r" (dest), "0" (incr) \
|
||||
: "memory" \
|
||||
)
|
||||
#define WRAP_SUB(ret, dest, decr) __asm__ __volatile__( \
|
||||
"lock; xaddl %0,(%1)" \
|
||||
: "=r" (ret) \
|
||||
: "r" (dest), "0" (-(decr)) \
|
||||
: "memory" \
|
||||
)
|
||||
|
||||
#define WRAP_XCHG(S, ret, dest, newval) __asm__ __volatile__( \
|
||||
"lock; xchg"S" %0,(%1)" \
|
||||
: "=r" (ret) \
|
||||
: "r" (dest), "0" (newval) \
|
||||
: "memory" \
|
||||
)
|
||||
#define WRAP_CMPXCHG(S, ret, dest, oldval, newval) __asm__ __volatile__( \
|
||||
"lock; cmpxchg"S" %2,(%1)" \
|
||||
: "=a" (ret) \
|
||||
: "r" (dest), "r" (newval), "0" (oldval) \
|
||||
: "memory" \
|
||||
)
|
||||
|
||||
|
||||
inline int ExchangeInt(volatile int *dest, int newval)
|
||||
{ int ret; WRAP_XCHG("l", ret, dest, newval); return ret; }
|
||||
|
||||
#ifdef __i386__
|
||||
inline void *ExchangePtr(XchgPtr *dest, void *newval)
|
||||
{ void *ret; WRAP_XCHG("l", ret, dest, newval); return ret; }
|
||||
#else
|
||||
inline void *ExchangePtr(XchgPtr *dest, void *newval)
|
||||
{ void *ret; WRAP_XCHG("q", ret, dest, newval); return ret; }
|
||||
#endif
|
||||
|
||||
|
||||
#define ATOMIC(T) struct { T volatile value; }
|
||||
|
||||
#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0)
|
||||
#define ATOMIC_INIT_STATIC(_newval) {(_newval)}
|
||||
|
||||
#define ATOMIC_LOAD(_val) __extension__({ \
|
||||
__typeof((_val)->value) _r = (_val)->value; \
|
||||
__asm__ __volatile__("" ::: "memory"); \
|
||||
_r; \
|
||||
})
|
||||
#define ATOMIC_STORE(_val, _newval) do { \
|
||||
__asm__ __volatile__("" ::: "memory"); \
|
||||
(_val)->value = (_newval); \
|
||||
} while(0)
|
||||
|
||||
#define ATOMIC_ADD(T, _val, _incr) __extension__({ \
|
||||
static_assert(sizeof(T)==4, "Type "#T" has incorrect size!"); \
|
||||
static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \
|
||||
T _r; \
|
||||
WRAP_ADD(_r, &(_val)->value, (T)(_incr)); \
|
||||
_r; \
|
||||
})
|
||||
#define ATOMIC_SUB(T, _val, _decr) __extension__({ \
|
||||
static_assert(sizeof(T)==4, "Type "#T" has incorrect size!"); \
|
||||
static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \
|
||||
T _r; \
|
||||
WRAP_SUB(_r, &(_val)->value, (T)(_decr)); \
|
||||
_r; \
|
||||
})
|
||||
|
||||
#define ATOMIC_EXCHANGE(T, _val, _newval) __extension__({ \
|
||||
static_assert(sizeof(T)==4 || sizeof(T)==8, "Type "#T" has incorrect size!"); \
|
||||
static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \
|
||||
T _r; \
|
||||
if(sizeof(T) == 4) WRAP_XCHG("l", _r, &(_val)->value, (T)(_newval)); \
|
||||
else if(sizeof(T) == 8) WRAP_XCHG("q", _r, &(_val)->value, (T)(_newval)); \
|
||||
_r; \
|
||||
})
|
||||
#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval) __extension__({ \
|
||||
static_assert(sizeof(T)==4 || sizeof(T)==8, "Type "#T" has incorrect size!"); \
|
||||
static_assert(sizeof(T)==sizeof((_val)->value), "Type "#T" has incorrect size!"); \
|
||||
T _old = *(_oldval); \
|
||||
if(sizeof(T) == 4) WRAP_CMPXCHG("l", *(_oldval), &(_val)->value, _old, (T)(_newval)); \
|
||||
else if(sizeof(T) == 8) WRAP_CMPXCHG("q", *(_oldval), &(_val)->value, _old, (T)(_newval)); \
|
||||
*(_oldval) == _old; \
|
||||
})
|
||||
|
||||
/* Atomics using Windows methods */
|
||||
#elif defined(_WIN32)
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
/* NOTE: This mess is *extremely* noisy, at least on GCC. It works by wrapping
|
||||
* Windows' 32-bit and 64-bit atomic methods, which are then casted to use the
|
||||
* given type based on its size (e.g. int and float use 32-bit atomics). This
|
||||
* is fine for the swap and compare-and-swap methods, although the add and
|
||||
* subtract methods only work properly for integer types.
|
||||
*
|
||||
* Despite how noisy it is, it's unfortunately the only way that doesn't rely
|
||||
* on C99 (damn MSVC).
|
||||
*/
|
||||
|
||||
inline LONG AtomicAdd32(volatile LONG *dest, LONG incr)
|
||||
{
|
||||
return InterlockedExchangeAdd(dest, incr);
|
||||
}
|
||||
inline LONG AtomicSub32(volatile LONG *dest, LONG decr)
|
||||
{
|
||||
return InterlockedExchangeAdd(dest, -decr);
|
||||
}
|
||||
|
||||
inline LONG AtomicSwap32(volatile LONG *dest, LONG newval)
|
||||
{
|
||||
return InterlockedExchange(dest, newval);
|
||||
}
|
||||
inline LONGLONG AtomicSwap64(volatile LONGLONG *dest, LONGLONG newval)
|
||||
{
|
||||
return InterlockedExchange64(dest, newval);
|
||||
}
|
||||
|
||||
inline bool CompareAndSwap32(volatile LONG *dest, LONG newval, LONG *oldval)
|
||||
{
|
||||
LONG old = *oldval;
|
||||
*oldval = InterlockedCompareExchange(dest, newval, *oldval);
|
||||
return old == *oldval;
|
||||
}
|
||||
inline bool CompareAndSwap64(volatile LONGLONG *dest, LONGLONG newval, LONGLONG *oldval)
|
||||
{
|
||||
LONGLONG old = *oldval;
|
||||
*oldval = InterlockedCompareExchange64(dest, newval, *oldval);
|
||||
return old == *oldval;
|
||||
}
|
||||
|
||||
#define WRAP_ADDSUB(T, _func, _ptr, _amnt) ((T(*)(T volatile*,T))_func)((_ptr), (_amnt))
|
||||
#define WRAP_XCHG(T, _func, _ptr, _newval) ((T(*)(T volatile*,T))_func)((_ptr), (_newval))
|
||||
#define WRAP_CMPXCHG(T, _func, _ptr, _newval, _oldval) ((bool(*)(T volatile*,T,T*))_func)((_ptr), (_newval), (_oldval))
|
||||
|
||||
inline int ExchangeInt(volatile int *ptr, int newval)
|
||||
{ return WRAP_XCHG(int,AtomicSwap32,ptr,newval); }
|
||||
|
||||
#ifdef _WIN64
|
||||
inline void *ExchangePtr(XchgPtr *ptr, void *newval)
|
||||
{ return WRAP_XCHG(void*,AtomicSwap64,ptr,newval); }
|
||||
#else
|
||||
inline void *ExchangePtr(XchgPtr *ptr, void *newval)
|
||||
{ return WRAP_XCHG(void*,AtomicSwap32,ptr,newval); }
|
||||
#endif
|
||||
|
||||
|
||||
#define ATOMIC(T) struct { T volatile value; }
|
||||
|
||||
#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0)
|
||||
#define ATOMIC_INIT_STATIC(_newval) {(_newval)}
|
||||
|
||||
#define ATOMIC_LOAD(_val) ((_val)->value)
|
||||
#define ATOMIC_STORE(_val, _newval) do { \
|
||||
(_val)->value = (_newval); \
|
||||
} while(0)
|
||||
|
||||
int _al_invalid_atomic_size(); /* not defined */
|
||||
|
||||
#define ATOMIC_ADD(T, _val, _incr) \
|
||||
((sizeof(T)==4) ? WRAP_ADDSUB(T, AtomicAdd32, &(_val)->value, (_incr)) : \
|
||||
(T)_al_invalid_atomic_size())
|
||||
#define ATOMIC_SUB(T, _val, _decr) \
|
||||
((sizeof(T)==4) ? WRAP_ADDSUB(T, AtomicSub32, &(_val)->value, (_decr)) : \
|
||||
(T)_al_invalid_atomic_size())
|
||||
|
||||
#define ATOMIC_EXCHANGE(T, _val, _newval) \
|
||||
((sizeof(T)==4) ? WRAP_XCHG(T, AtomicSwap32, &(_val)->value, (_newval)) : \
|
||||
(sizeof(T)==8) ? WRAP_XCHG(T, AtomicSwap64, &(_val)->value, (_newval)) : \
|
||||
(T)_al_invalid_atomic_size())
|
||||
#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval) \
|
||||
((sizeof(T)==4) ? WRAP_CMPXCHG(T, CompareAndSwap32, &(_val)->value, (_newval), (_oldval)) : \
|
||||
(sizeof(T)==8) ? WRAP_CMPXCHG(T, CompareAndSwap64, &(_val)->value, (_newval), (_oldval)) : \
|
||||
(bool)_al_invalid_atomic_size())
|
||||
|
||||
#else
|
||||
|
||||
#error "No atomic functions available on this platform!"
|
||||
|
||||
#define ATOMIC(T) T
|
||||
|
||||
#define ATOMIC_INIT_STATIC(_newval) (0)
|
||||
|
||||
#define ATOMIC_LOAD_UNSAFE(_val) (0)
|
||||
#define ATOMIC_STORE_UNSAFE(_val, _newval) ((void)0)
|
||||
|
||||
#define ATOMIC_LOAD(_val) (0)
|
||||
#define ATOMIC_STORE(_val, _newval) ((void)0)
|
||||
|
||||
#define ATOMIC_ADD(T, _val, _incr) (0)
|
||||
#define ATOMIC_SUB(T, _val, _decr) (0)
|
||||
|
||||
#define ATOMIC_EXCHANGE(T, _val, _newval) (0)
|
||||
#define ATOMIC_COMPARE_EXCHANGE_STRONG(T, _val, _oldval, _newval) (0)
|
||||
#endif
|
||||
|
||||
/* If no weak cmpxchg is provided (not all systems will have one), substitute a
|
||||
* strong cmpxchg. */
|
||||
#ifndef ATOMIC_COMPARE_EXCHANGE_WEAK
|
||||
#define ATOMIC_COMPARE_EXCHANGE_WEAK(a, b, c, d) ATOMIC_COMPARE_EXCHANGE_STRONG(a, b, c, d)
|
||||
#endif
|
||||
|
||||
/* This is *NOT* atomic, but is a handy utility macro to compare-and-swap non-
|
||||
* atomic variables. */
|
||||
#define COMPARE_EXCHANGE(_val, _oldval, _newval) ((*(_val) == *(_oldval)) ? ((*(_val)=(_newval)),true) : ((*(_oldval)=*(_val)),false))
|
||||
|
||||
|
||||
typedef unsigned int uint;
|
||||
typedef ATOMIC(uint) RefCount;
|
||||
|
||||
inline void InitRef(RefCount *ptr, uint value)
|
||||
{ ATOMIC_INIT(ptr, value); }
|
||||
inline uint ReadRef(RefCount *ptr)
|
||||
{ return ATOMIC_LOAD(ptr); }
|
||||
inline uint IncrementRef(RefCount *ptr)
|
||||
{ return ATOMIC_ADD(uint, ptr, 1)+1; }
|
||||
inline uint DecrementRef(RefCount *ptr)
|
||||
{ return ATOMIC_SUB(uint, ptr, 1)-1; }
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* AL_ATOMIC_H */
|
||||
@@ -1,29 +0,0 @@
|
||||
project(alsoft-config)
|
||||
|
||||
include_directories("${alsoft-config_BINARY_DIR}")
|
||||
|
||||
# Need Qt 4.8.0 or newer for the iconset theme attribute to work
|
||||
find_package(Qt4 4.8.0 COMPONENTS QtCore QtGui)
|
||||
if(QT4_FOUND)
|
||||
include(${QT_USE_FILE})
|
||||
|
||||
set(alsoft-config_SRCS main.cpp
|
||||
mainwindow.cpp
|
||||
)
|
||||
|
||||
set(alsoft-config_UIS mainwindow.ui)
|
||||
QT4_WRAP_UI(UIS ${alsoft-config_UIS})
|
||||
|
||||
set(alsoft-config_MOCS mainwindow.h)
|
||||
QT4_WRAP_CPP(MOCS ${alsoft-config_MOCS})
|
||||
|
||||
add_executable(alsoft-config ${alsoft-config_SRCS} ${UIS} ${RSCS} ${TRS} ${MOCS})
|
||||
target_link_libraries(alsoft-config ${QT_LIBRARIES})
|
||||
set_target_properties(alsoft-config PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${OpenAL_BINARY_DIR})
|
||||
|
||||
install(TARGETS alsoft-config
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION "lib${LIB_SUFFIX}"
|
||||
ARCHIVE DESTINATION "lib${LIB_SUFFIX}"
|
||||
)
|
||||
endif()
|
||||
@@ -1,660 +0,0 @@
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
#include <QtGlobal>
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
namespace {
|
||||
static const struct {
|
||||
char backend_name[16];
|
||||
char menu_string[32];
|
||||
} backendMenuList[] = {
|
||||
#ifdef Q_OS_WIN32
|
||||
{ "mmdevapi", "Add MMDevAPI" },
|
||||
{ "dsound", "Add DirectSound" },
|
||||
{ "winmm", "Add Windows Multimedia" },
|
||||
#endif
|
||||
#ifdef Q_OS_MAC
|
||||
{ "core", "Add CoreAudio" },
|
||||
#endif
|
||||
{ "pulse", "Add PulseAudio" },
|
||||
#ifdef Q_OS_UNIX
|
||||
{ "alsa", "Add ALSA" },
|
||||
{ "oss", "Add OSS" },
|
||||
{ "solaris", "Add Solaris" },
|
||||
{ "sndio", "Add SndIO" },
|
||||
{ "qsa", "Add QSA" },
|
||||
#endif
|
||||
{ "port", "Add PortAudio" },
|
||||
{ "opensl", "Add OpenSL" },
|
||||
{ "null", "Add Null Output" },
|
||||
{ "wave", "Add Wave Writer" },
|
||||
{ "", "" }
|
||||
};
|
||||
|
||||
static QString getDefaultConfigName()
|
||||
{
|
||||
#ifdef Q_OS_WIN32
|
||||
static const char fname[] = "alsoft.ini";
|
||||
QByteArray base = qgetenv("AppData");
|
||||
#else
|
||||
static const char fname[] = "alsoft.conf";
|
||||
QByteArray base = qgetenv("XDG_CONFIG_HOME");
|
||||
if(base.isEmpty())
|
||||
{
|
||||
base = qgetenv("HOME");
|
||||
if(base.isEmpty() == false)
|
||||
base += "/.config";
|
||||
}
|
||||
#endif
|
||||
if(base.isEmpty() == false)
|
||||
return base +'/'+ fname;
|
||||
return fname;
|
||||
}
|
||||
|
||||
static QString getBaseDataPath()
|
||||
{
|
||||
#ifdef Q_OS_WIN32
|
||||
QByteArray base = qgetenv("AppData");
|
||||
#else
|
||||
QByteArray base = qgetenv("XDG_DATA_HOME");
|
||||
if(base.isEmpty())
|
||||
{
|
||||
base = qgetenv("HOME");
|
||||
if(!base.isEmpty())
|
||||
base += "/.local/share";
|
||||
}
|
||||
#endif
|
||||
return base;
|
||||
}
|
||||
|
||||
static QStringList getAllDataPaths(QString append=QString())
|
||||
{
|
||||
QStringList list;
|
||||
list.append(getBaseDataPath());
|
||||
#ifdef Q_OS_WIN32
|
||||
// TODO: Common AppData path
|
||||
#else
|
||||
QString paths = qgetenv("XDG_DATA_DIRS");
|
||||
if(paths.isEmpty())
|
||||
paths = "/usr/local/share/:/usr/share/";
|
||||
list += paths.split(QChar(':'), QString::SkipEmptyParts);
|
||||
#endif
|
||||
QStringList::iterator iter = list.begin();
|
||||
while(iter != list.end())
|
||||
{
|
||||
if(iter->isEmpty())
|
||||
iter = list.erase(iter);
|
||||
else
|
||||
{
|
||||
iter->append(append);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow),
|
||||
mPeriodSizeValidator(NULL),
|
||||
mPeriodCountValidator(NULL),
|
||||
mSourceCountValidator(NULL),
|
||||
mEffectSlotValidator(NULL),
|
||||
mSourceSendValidator(NULL),
|
||||
mSampleRateValidator(NULL),
|
||||
mReverbBoostValidator(NULL)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
mPeriodSizeValidator = new QIntValidator(64, 8192, this);
|
||||
ui->periodSizeEdit->setValidator(mPeriodSizeValidator);
|
||||
mPeriodCountValidator = new QIntValidator(2, 16, this);
|
||||
ui->periodCountEdit->setValidator(mPeriodCountValidator);
|
||||
|
||||
mSourceCountValidator = new QIntValidator(0, 256, this);
|
||||
ui->srcCountLineEdit->setValidator(mSourceCountValidator);
|
||||
mEffectSlotValidator = new QIntValidator(0, 16, this);
|
||||
ui->effectSlotLineEdit->setValidator(mEffectSlotValidator);
|
||||
mSourceSendValidator = new QIntValidator(0, 4, this);
|
||||
ui->srcSendLineEdit->setValidator(mSourceSendValidator);
|
||||
mSampleRateValidator = new QIntValidator(8000, 192000, this);
|
||||
ui->sampleRateCombo->lineEdit()->setValidator(mSampleRateValidator);
|
||||
|
||||
mReverbBoostValidator = new QDoubleValidator(-12.0, +12.0, 1, this);
|
||||
ui->reverbBoostEdit->setValidator(mReverbBoostValidator);
|
||||
|
||||
connect(ui->actionLoad, SIGNAL(triggered()), this, SLOT(loadConfigFromFile()));
|
||||
connect(ui->actionSave_As, SIGNAL(triggered()), this, SLOT(saveConfigAsFile()));
|
||||
|
||||
connect(ui->applyButton, SIGNAL(clicked()), this, SLOT(saveCurrentConfig()));
|
||||
|
||||
connect(ui->periodSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(updatePeriodSizeEdit(int)));
|
||||
connect(ui->periodSizeEdit, SIGNAL(editingFinished()), this, SLOT(updatePeriodSizeSlider()));
|
||||
connect(ui->periodCountSlider, SIGNAL(valueChanged(int)), this, SLOT(updatePeriodCountEdit(int)));
|
||||
connect(ui->periodCountEdit, SIGNAL(editingFinished()), this, SLOT(updatePeriodCountSlider()));
|
||||
|
||||
connect(ui->hrtfAddButton, SIGNAL(clicked()), this, SLOT(addHrtfFile()));
|
||||
connect(ui->hrtfRemoveButton, SIGNAL(clicked()), this, SLOT(removeHrtfFile()));
|
||||
connect(ui->hrtfFileList, SIGNAL(itemSelectionChanged()), this, SLOT(updateHrtfRemoveButton()));
|
||||
|
||||
ui->enabledBackendList->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->enabledBackendList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showEnabledBackendMenu(QPoint)));
|
||||
|
||||
ui->disabledBackendList->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->disabledBackendList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showDisabledBackendMenu(QPoint)));
|
||||
|
||||
connect(ui->reverbBoostSlider, SIGNAL(valueChanged(int)), this, SLOT(updateReverbBoostEdit(int)));
|
||||
connect(ui->reverbBoostEdit, SIGNAL(textEdited(QString)), this, SLOT(updateReverbBoostSlider(QString)));
|
||||
|
||||
loadConfig(getDefaultConfigName());
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
delete mPeriodSizeValidator;
|
||||
delete mPeriodCountValidator;
|
||||
delete mSourceCountValidator;
|
||||
delete mEffectSlotValidator;
|
||||
delete mSourceSendValidator;
|
||||
delete mSampleRateValidator;
|
||||
delete mReverbBoostValidator;
|
||||
}
|
||||
|
||||
void MainWindow::loadConfigFromFile()
|
||||
{
|
||||
QString fname = QFileDialog::getOpenFileName(this, tr("Select Files"));
|
||||
if(fname.isEmpty() == false)
|
||||
loadConfig(fname);
|
||||
}
|
||||
|
||||
void MainWindow::loadConfig(const QString &fname)
|
||||
{
|
||||
QSettings settings(fname, QSettings::IniFormat);
|
||||
|
||||
QString sampletype = settings.value("sample-type").toString();
|
||||
ui->sampleFormatCombo->setCurrentIndex(0);
|
||||
if(sampletype.isEmpty() == false)
|
||||
{
|
||||
for(int i = 1;i < ui->sampleFormatCombo->count();i++)
|
||||
{
|
||||
QString item = ui->sampleFormatCombo->itemText(i);
|
||||
if(item.startsWith(sampletype))
|
||||
{
|
||||
ui->sampleFormatCombo->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString channelconfig = settings.value("channels").toString();
|
||||
ui->channelConfigCombo->setCurrentIndex(0);
|
||||
if(channelconfig.isEmpty() == false)
|
||||
{
|
||||
for(int i = 1;i < ui->channelConfigCombo->count();i++)
|
||||
{
|
||||
QString item = ui->channelConfigCombo->itemText(i);
|
||||
if(item.startsWith(channelconfig))
|
||||
{
|
||||
ui->channelConfigCombo->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString srate = settings.value("frequency").toString();
|
||||
if(srate.isEmpty())
|
||||
ui->sampleRateCombo->setCurrentIndex(0);
|
||||
else
|
||||
{
|
||||
ui->sampleRateCombo->lineEdit()->clear();
|
||||
ui->sampleRateCombo->lineEdit()->insert(srate);
|
||||
}
|
||||
|
||||
ui->srcCountLineEdit->clear();
|
||||
ui->srcCountLineEdit->insert(settings.value("sources").toString());
|
||||
ui->effectSlotLineEdit->clear();
|
||||
ui->effectSlotLineEdit->insert(settings.value("slots").toString());
|
||||
ui->srcSendLineEdit->clear();
|
||||
ui->srcSendLineEdit->insert(settings.value("sends").toString());
|
||||
|
||||
QString resampler = settings.value("resampler").toString().trimmed();
|
||||
if(resampler.isEmpty())
|
||||
ui->resamplerComboBox->setCurrentIndex(0);
|
||||
else
|
||||
{
|
||||
for(int i = 1;i < ui->resamplerComboBox->count();i++)
|
||||
{
|
||||
QString item = ui->resamplerComboBox->itemText(i);
|
||||
int end = item.indexOf(' ');
|
||||
if(end < 0) end = item.size();
|
||||
if(resampler.size() == end && resampler.compare(item.leftRef(end), Qt::CaseInsensitive) == 0)
|
||||
{
|
||||
ui->resamplerComboBox->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int periodsize = settings.value("period_size").toInt();
|
||||
ui->periodSizeEdit->clear();
|
||||
if(periodsize >= 64)
|
||||
{
|
||||
ui->periodSizeEdit->insert(QString::number(periodsize));
|
||||
updatePeriodSizeSlider();
|
||||
}
|
||||
|
||||
int periodcount = settings.value("periods").toInt();
|
||||
ui->periodCountEdit->clear();
|
||||
if(periodcount >= 2)
|
||||
{
|
||||
ui->periodCountEdit->insert(QString::number(periodcount));
|
||||
updatePeriodCountSlider();
|
||||
}
|
||||
|
||||
QStringList disabledCpuExts = settings.value("disable-cpu-exts").toStringList();
|
||||
if(disabledCpuExts.size() == 1)
|
||||
disabledCpuExts = disabledCpuExts[0].split(QChar(','));
|
||||
std::transform(disabledCpuExts.begin(), disabledCpuExts.end(),
|
||||
disabledCpuExts.begin(), std::mem_fun_ref(&QString::trimmed));
|
||||
ui->enableSSECheckBox->setChecked(!disabledCpuExts.contains("sse", Qt::CaseInsensitive));
|
||||
ui->enableSSE2CheckBox->setChecked(!disabledCpuExts.contains("sse2", Qt::CaseInsensitive));
|
||||
ui->enableSSE41CheckBox->setChecked(!disabledCpuExts.contains("sse4.1", Qt::CaseInsensitive));
|
||||
ui->enableNeonCheckBox->setChecked(!disabledCpuExts.contains("neon", Qt::CaseInsensitive));
|
||||
|
||||
if(settings.value("hrtf").toString() == QString())
|
||||
ui->hrtfEnableButton->setChecked(true);
|
||||
else
|
||||
{
|
||||
if(settings.value("hrtf", true).toBool())
|
||||
ui->hrtfForceButton->setChecked(true);
|
||||
else
|
||||
ui->hrtfDisableButton->setChecked(true);
|
||||
}
|
||||
|
||||
QStringList hrtf_tables = settings.value("hrtf_tables").toStringList();
|
||||
if(hrtf_tables.size() == 1)
|
||||
hrtf_tables = hrtf_tables[0].split(QChar(','));
|
||||
std::transform(hrtf_tables.begin(), hrtf_tables.end(),
|
||||
hrtf_tables.begin(), std::mem_fun_ref(&QString::trimmed));
|
||||
ui->hrtfFileList->clear();
|
||||
ui->hrtfFileList->addItems(hrtf_tables);
|
||||
updateHrtfRemoveButton();
|
||||
|
||||
ui->enabledBackendList->clear();
|
||||
ui->disabledBackendList->clear();
|
||||
QStringList drivers = settings.value("drivers").toStringList();
|
||||
if(drivers.size() == 0)
|
||||
ui->backendCheckBox->setChecked(true);
|
||||
else
|
||||
{
|
||||
if(drivers.size() == 1)
|
||||
drivers = drivers[0].split(QChar(','));
|
||||
std::transform(drivers.begin(), drivers.end(),
|
||||
drivers.begin(), std::mem_fun_ref(&QString::trimmed));
|
||||
|
||||
bool lastWasEmpty = false;
|
||||
foreach(const QString &backend, drivers)
|
||||
{
|
||||
lastWasEmpty = backend.isEmpty();
|
||||
if(!backend.startsWith(QChar('-')) && !lastWasEmpty)
|
||||
ui->enabledBackendList->addItem(backend);
|
||||
else if(backend.size() > 1)
|
||||
ui->disabledBackendList->addItem(backend.right(backend.size()-1));
|
||||
}
|
||||
ui->backendCheckBox->setChecked(lastWasEmpty);
|
||||
}
|
||||
|
||||
QString defaultreverb = settings.value("default-reverb").toString().toLower();
|
||||
ui->defaultReverbComboBox->setCurrentIndex(0);
|
||||
if(defaultreverb.isEmpty() == false)
|
||||
{
|
||||
for(int i = 0;i < ui->defaultReverbComboBox->count();i++)
|
||||
{
|
||||
if(defaultreverb.compare(ui->defaultReverbComboBox->itemText(i).toLower()) == 0)
|
||||
{
|
||||
ui->defaultReverbComboBox->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui->emulateEaxCheckBox->setChecked(settings.value("reverb/emulate-eax", false).toBool());
|
||||
ui->reverbBoostEdit->clear();
|
||||
ui->reverbBoostEdit->insert(settings.value("reverb/boost").toString());
|
||||
|
||||
QStringList excludefx = settings.value("excludefx").toStringList();
|
||||
if(excludefx.size() == 1)
|
||||
excludefx = excludefx[0].split(QChar(','));
|
||||
std::transform(excludefx.begin(), excludefx.end(),
|
||||
excludefx.begin(), std::mem_fun_ref(&QString::trimmed));
|
||||
ui->enableEaxReverbCheck->setChecked(!excludefx.contains("eaxreverb", Qt::CaseInsensitive));
|
||||
ui->enableStdReverbCheck->setChecked(!excludefx.contains("reverb", Qt::CaseInsensitive));
|
||||
ui->enableChorusCheck->setChecked(!excludefx.contains("chorus", Qt::CaseInsensitive));
|
||||
ui->enableCompressorCheck->setChecked(!excludefx.contains("compressor", Qt::CaseInsensitive));
|
||||
ui->enableDistortionCheck->setChecked(!excludefx.contains("distortion", Qt::CaseInsensitive));
|
||||
ui->enableEchoCheck->setChecked(!excludefx.contains("echo", Qt::CaseInsensitive));
|
||||
ui->enableEqualizerCheck->setChecked(!excludefx.contains("equalizer", Qt::CaseInsensitive));
|
||||
ui->enableFlangerCheck->setChecked(!excludefx.contains("flanger", Qt::CaseInsensitive));
|
||||
ui->enableModulatorCheck->setChecked(!excludefx.contains("modulator", Qt::CaseInsensitive));
|
||||
ui->enableDedicatedCheck->setChecked(!excludefx.contains("dedicated", Qt::CaseInsensitive));
|
||||
}
|
||||
|
||||
void MainWindow::saveCurrentConfig()
|
||||
{
|
||||
saveConfig(getDefaultConfigName());
|
||||
QMessageBox::information(this, tr("Information"),
|
||||
tr("Applications using OpenAL need to be restarted for changes to take effect."));
|
||||
}
|
||||
|
||||
void MainWindow::saveConfigAsFile()
|
||||
{
|
||||
QString fname = QFileDialog::getOpenFileName(this, tr("Select Files"));
|
||||
if(fname.isEmpty() == false)
|
||||
saveConfig(fname);
|
||||
}
|
||||
|
||||
void MainWindow::saveConfig(const QString &fname) const
|
||||
{
|
||||
QSettings settings(fname, QSettings::IniFormat);
|
||||
|
||||
/* HACK: Compound any stringlist values into a comma-separated string. */
|
||||
QStringList allkeys = settings.allKeys();
|
||||
foreach(const QString &key, allkeys)
|
||||
{
|
||||
QStringList vals = settings.value(key).toStringList();
|
||||
if(vals.size() > 1)
|
||||
settings.setValue(key, vals.join(QChar(',')));
|
||||
}
|
||||
|
||||
QString str = ui->sampleFormatCombo->currentText();
|
||||
str.truncate(str.indexOf('-'));
|
||||
settings.setValue("sample-type", str.trimmed());
|
||||
|
||||
str = ui->channelConfigCombo->currentText();
|
||||
str.truncate(str.indexOf('-'));
|
||||
settings.setValue("channels", str.trimmed());
|
||||
|
||||
uint rate = ui->sampleRateCombo->currentText().toUInt();
|
||||
if(rate == 0)
|
||||
settings.setValue("frequency", QString());
|
||||
else
|
||||
settings.setValue("frequency", rate);
|
||||
|
||||
settings.setValue("period_size", ui->periodSizeEdit->text());
|
||||
settings.setValue("periods", ui->periodCountEdit->text());
|
||||
|
||||
settings.setValue("sources", ui->srcCountLineEdit->text());
|
||||
settings.setValue("slots", ui->effectSlotLineEdit->text());
|
||||
|
||||
if(ui->resamplerComboBox->currentIndex() == 0)
|
||||
settings.setValue("resampler", QString());
|
||||
else
|
||||
{
|
||||
str = ui->resamplerComboBox->currentText();
|
||||
settings.setValue("resampler", str.split(' ').first().toLower());
|
||||
}
|
||||
|
||||
QStringList strlist;
|
||||
if(!ui->enableSSECheckBox->isChecked())
|
||||
strlist.append("sse");
|
||||
if(!ui->enableSSE2CheckBox->isChecked())
|
||||
strlist.append("sse2");
|
||||
if(!ui->enableSSE41CheckBox->isChecked())
|
||||
strlist.append("sse4.1");
|
||||
if(!ui->enableNeonCheckBox->isChecked())
|
||||
strlist.append("neon");
|
||||
settings.setValue("disable-cpu-exts", strlist.join(QChar(',')));
|
||||
|
||||
if(ui->hrtfForceButton->isChecked())
|
||||
settings.setValue("hrtf", "true");
|
||||
else if(ui->hrtfDisableButton->isChecked())
|
||||
settings.setValue("hrtf", "false");
|
||||
else
|
||||
settings.setValue("hrtf", QString());
|
||||
|
||||
strlist.clear();
|
||||
QList<QListWidgetItem*> items = ui->hrtfFileList->findItems("*", Qt::MatchWildcard);
|
||||
foreach(const QListWidgetItem *item, items)
|
||||
strlist.append(item->text());
|
||||
settings.setValue("hrtf_tables", strlist.join(QChar(',')));
|
||||
|
||||
strlist.clear();
|
||||
items = ui->enabledBackendList->findItems("*", Qt::MatchWildcard);
|
||||
foreach(const QListWidgetItem *item, items)
|
||||
strlist.append(item->text());
|
||||
items = ui->disabledBackendList->findItems("*", Qt::MatchWildcard);
|
||||
foreach(const QListWidgetItem *item, items)
|
||||
strlist.append(QChar('-')+item->text());
|
||||
if(strlist.size() == 0 && !ui->backendCheckBox->isChecked())
|
||||
strlist.append("-all");
|
||||
else if(ui->backendCheckBox->isChecked())
|
||||
strlist.append(QString());
|
||||
settings.setValue("drivers", strlist.join(QChar(',')));
|
||||
|
||||
// TODO: Remove check when we can properly match global values.
|
||||
if(ui->defaultReverbComboBox->currentIndex() == 0)
|
||||
settings.setValue("default-reverb", QString());
|
||||
else
|
||||
{
|
||||
str = ui->defaultReverbComboBox->currentText().toLower();
|
||||
settings.setValue("default-reverb", str);
|
||||
}
|
||||
|
||||
if(ui->emulateEaxCheckBox->isChecked())
|
||||
settings.setValue("reverb/emulate-eax", "true");
|
||||
else
|
||||
settings.setValue("reverb/emulate-eax", QString()/*"false"*/);
|
||||
|
||||
// TODO: Remove check when we can properly match global values.
|
||||
if(ui->reverbBoostSlider->sliderPosition() == 0)
|
||||
settings.setValue("reverb/boost", QString());
|
||||
else
|
||||
settings.setValue("reverb/boost", ui->reverbBoostEdit->text());
|
||||
|
||||
strlist.clear();
|
||||
if(!ui->enableEaxReverbCheck->isChecked())
|
||||
strlist.append("eaxreverb");
|
||||
if(!ui->enableStdReverbCheck->isChecked())
|
||||
strlist.append("reverb");
|
||||
if(!ui->enableChorusCheck->isChecked())
|
||||
strlist.append("chorus");
|
||||
if(!ui->enableDistortionCheck->isChecked())
|
||||
strlist.append("distortion");
|
||||
if(!ui->enableCompressorCheck->isChecked())
|
||||
strlist.append("compressor");
|
||||
if(!ui->enableEchoCheck->isChecked())
|
||||
strlist.append("echo");
|
||||
if(!ui->enableEqualizerCheck->isChecked())
|
||||
strlist.append("equalizer");
|
||||
if(!ui->enableFlangerCheck->isChecked())
|
||||
strlist.append("flanger");
|
||||
if(!ui->enableModulatorCheck->isChecked())
|
||||
strlist.append("modulator");
|
||||
if(!ui->enableDedicatedCheck->isChecked())
|
||||
strlist.append("dedicated");
|
||||
settings.setValue("excludefx", strlist.join(QChar(',')));
|
||||
|
||||
/* Remove empty keys
|
||||
* FIXME: Should only remove keys whose value matches the globally-specified value.
|
||||
*/
|
||||
allkeys = settings.allKeys();
|
||||
foreach(const QString &key, allkeys)
|
||||
{
|
||||
str = settings.value(key).toString();
|
||||
if(str == QString())
|
||||
settings.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::updatePeriodSizeEdit(int size)
|
||||
{
|
||||
ui->periodSizeEdit->clear();
|
||||
if(size >= 64)
|
||||
{
|
||||
size = (size+32)&~0x3f;
|
||||
ui->periodSizeEdit->insert(QString::number(size));
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::updatePeriodSizeSlider()
|
||||
{
|
||||
int pos = ui->periodSizeEdit->text().toInt();
|
||||
if(pos >= 64)
|
||||
{
|
||||
if(pos > 8192)
|
||||
pos = 8192;
|
||||
ui->periodSizeSlider->setSliderPosition(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::updatePeriodCountEdit(int count)
|
||||
{
|
||||
ui->periodCountEdit->clear();
|
||||
if(count >= 2)
|
||||
ui->periodCountEdit->insert(QString::number(count));
|
||||
}
|
||||
|
||||
void MainWindow::updatePeriodCountSlider()
|
||||
{
|
||||
int pos = ui->periodCountEdit->text().toInt();
|
||||
if(pos < 2)
|
||||
pos = 0;
|
||||
else if(pos > 16)
|
||||
pos = 16;
|
||||
ui->periodCountSlider->setSliderPosition(pos);
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::addHrtfFile()
|
||||
{
|
||||
const QStringList datapaths = getAllDataPaths("/openal/hrtf");
|
||||
QStringList fnames = QFileDialog::getOpenFileNames(this, tr("Select Files"),
|
||||
datapaths.empty() ? QString() : datapaths[0],
|
||||
"HRTF Datasets(*.mhr);;All Files(*.*)");
|
||||
if(fnames.isEmpty() == false)
|
||||
{
|
||||
for(QStringList::iterator iter = fnames.begin();iter != fnames.end();iter++)
|
||||
{
|
||||
QStringList::const_iterator path = datapaths.constBegin();
|
||||
for(;path != datapaths.constEnd();path++)
|
||||
{
|
||||
QDir hrtfdir(*path);
|
||||
if(!hrtfdir.isAbsolute())
|
||||
continue;
|
||||
|
||||
const QString relname = hrtfdir.relativeFilePath(*iter);
|
||||
if(!relname.startsWith(".."))
|
||||
{
|
||||
// If filename is within this path, use the relative pathname
|
||||
ui->hrtfFileList->addItem(relname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(path == datapaths.constEnd())
|
||||
{
|
||||
// Filename is not within any data path, use the absolute pathname
|
||||
ui->hrtfFileList->addItem(*iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::removeHrtfFile()
|
||||
{
|
||||
QList<QListWidgetItem*> selected = ui->hrtfFileList->selectedItems();
|
||||
foreach(QListWidgetItem *item, selected)
|
||||
delete item;
|
||||
}
|
||||
|
||||
void MainWindow::updateHrtfRemoveButton()
|
||||
{
|
||||
ui->hrtfRemoveButton->setEnabled(ui->hrtfFileList->selectedItems().size() != 0);
|
||||
}
|
||||
|
||||
void MainWindow::showEnabledBackendMenu(QPoint pt)
|
||||
{
|
||||
QMap<QAction*,QString> actionMap;
|
||||
|
||||
pt = ui->enabledBackendList->mapToGlobal(pt);
|
||||
|
||||
QMenu ctxmenu;
|
||||
QAction *removeAction = ctxmenu.addAction(QIcon::fromTheme("list-remove"), "Remove");
|
||||
if(ui->enabledBackendList->selectedItems().size() == 0)
|
||||
removeAction->setEnabled(false);
|
||||
ctxmenu.addSeparator();
|
||||
for(size_t i = 0;backendMenuList[i].backend_name[0];i++)
|
||||
{
|
||||
QAction *action = ctxmenu.addAction(backendMenuList[i].menu_string);
|
||||
actionMap[action] = backendMenuList[i].backend_name;
|
||||
if(ui->enabledBackendList->findItems(backendMenuList[i].backend_name, Qt::MatchFixedString).size() != 0 ||
|
||||
ui->disabledBackendList->findItems(backendMenuList[i].backend_name, Qt::MatchFixedString).size() != 0)
|
||||
action->setEnabled(false);
|
||||
}
|
||||
|
||||
QAction *gotAction = ctxmenu.exec(pt);
|
||||
if(gotAction == removeAction)
|
||||
{
|
||||
QList<QListWidgetItem*> selected = ui->enabledBackendList->selectedItems();
|
||||
foreach(QListWidgetItem *item, selected)
|
||||
delete item;
|
||||
}
|
||||
else if(gotAction != NULL)
|
||||
{
|
||||
QMap<QAction*,QString>::const_iterator iter = actionMap.find(gotAction);
|
||||
if(iter != actionMap.end())
|
||||
ui->enabledBackendList->addItem(iter.value());
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::showDisabledBackendMenu(QPoint pt)
|
||||
{
|
||||
QMap<QAction*,QString> actionMap;
|
||||
|
||||
pt = ui->disabledBackendList->mapToGlobal(pt);
|
||||
|
||||
QMenu ctxmenu;
|
||||
QAction *removeAction = ctxmenu.addAction(QIcon::fromTheme("list-remove"), "Remove");
|
||||
if(ui->disabledBackendList->selectedItems().size() == 0)
|
||||
removeAction->setEnabled(false);
|
||||
ctxmenu.addSeparator();
|
||||
for(size_t i = 0;backendMenuList[i].backend_name[0];i++)
|
||||
{
|
||||
QAction *action = ctxmenu.addAction(backendMenuList[i].menu_string);
|
||||
actionMap[action] = backendMenuList[i].backend_name;
|
||||
if(ui->disabledBackendList->findItems(backendMenuList[i].backend_name, Qt::MatchFixedString).size() != 0 ||
|
||||
ui->enabledBackendList->findItems(backendMenuList[i].backend_name, Qt::MatchFixedString).size() != 0)
|
||||
action->setEnabled(false);
|
||||
}
|
||||
|
||||
QAction *gotAction = ctxmenu.exec(pt);
|
||||
if(gotAction == removeAction)
|
||||
{
|
||||
QList<QListWidgetItem*> selected = ui->disabledBackendList->selectedItems();
|
||||
foreach(QListWidgetItem *item, selected)
|
||||
delete item;
|
||||
}
|
||||
else if(gotAction != NULL)
|
||||
{
|
||||
QMap<QAction*,QString>::const_iterator iter = actionMap.find(gotAction);
|
||||
if(iter != actionMap.end())
|
||||
ui->disabledBackendList->addItem(iter.value());
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::updateReverbBoostEdit(int value)
|
||||
{
|
||||
ui->reverbBoostEdit->clear();
|
||||
if(value != 0)
|
||||
ui->reverbBoostEdit->insert(QString::number(value/10.0, 'f', 1));
|
||||
}
|
||||
|
||||
void MainWindow::updateReverbBoostSlider(QString value)
|
||||
{
|
||||
int pos = int(value.toFloat()*10.0f);
|
||||
ui->reverbBoostSlider->setSliderPosition(pos);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
build
|
||||
winbuild
|
||||
win64build
|
||||
include/SLES
|
||||
include/sndio.h
|
||||
include/sys
|
||||
openal-soft.kdev4
|
||||
@@ -0,0 +1,70 @@
|
||||
language: c
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
- os: linux
|
||||
dist: trusty
|
||||
env:
|
||||
- BUILD_ANDROID=true
|
||||
- os: osx
|
||||
sudo: required
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/android-ndk-r14
|
||||
install:
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" && -z "${BUILD_ANDROID}" ]]; then
|
||||
# Install pulseaudio, portaudio, ALSA, JACK dependencies for
|
||||
# corresponding backends.
|
||||
# Install Qt5 dependency for alsoft-config.
|
||||
sudo apt-get install -qq \
|
||||
libpulse-dev \
|
||||
portaudio19-dev \
|
||||
libasound2-dev \
|
||||
libjack-dev \
|
||||
qtbase5-dev
|
||||
fi
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" && "${BUILD_ANDROID}" == "true" ]]; then
|
||||
if [[ ! -d ~/android-ndk-r14 || -z "$(ls -A ~/android-ndk-r14)" ]]; then
|
||||
curl -o ~/android-ndk.zip https://dl.google.com/android/repository/android-ndk-r14-linux-x86_64.zip
|
||||
unzip -q ~/android-ndk.zip -d ~ \
|
||||
'android-ndk-r14/build/cmake/*' \
|
||||
'android-ndk-r14/platforms/android-9/arch-arm/*' \
|
||||
'android-ndk-r14/source.properties' \
|
||||
'android-ndk-r14/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/*' \
|
||||
'android-ndk-r14/sysroot/*' \
|
||||
'android-ndk-r14/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/*' \
|
||||
'android-ndk-r14/toolchains/llvm/prebuilt/linux-x86_64/*'
|
||||
sed -i -e 's/VERSION 3.6.0/VERSION 3.2/' ~/android-ndk-r14/build/cmake/android.toolchain.cmake
|
||||
fi
|
||||
fi
|
||||
script:
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" && -z "${BUILD_ANDROID}" ]]; then
|
||||
cmake \
|
||||
-DALSOFT_REQUIRE_ALSA=ON \
|
||||
-DALSOFT_REQUIRE_OSS=ON \
|
||||
-DALSOFT_REQUIRE_PORTAUDIO=ON \
|
||||
-DALSOFT_REQUIRE_PULSEAUDIO=ON \
|
||||
-DALSOFT_REQUIRE_JACK=ON \
|
||||
-DALSOFT_EMBED_HRTF_DATA=YES \
|
||||
.
|
||||
fi
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" && "${BUILD_ANDROID}" == "true" ]]; then
|
||||
cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=~/android-ndk-r14/build/cmake/android.toolchain.cmake \
|
||||
-DALSOFT_REQUIRE_OPENSL=ON \
|
||||
-DALSOFT_EMBED_HRTF_DATA=YES \
|
||||
.
|
||||
fi
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
|
||||
cmake \
|
||||
-DALSOFT_REQUIRE_COREAUDIO=ON \
|
||||
-DALSOFT_EMBED_HRTF_DATA=YES \
|
||||
.
|
||||
fi
|
||||
- make -j2
|
||||
+1862
-959
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+173
-36
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -33,11 +33,13 @@
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32_IE
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "compat.h"
|
||||
#include "bool.h"
|
||||
|
||||
|
||||
typedef struct ConfigEntry {
|
||||
@@ -137,13 +139,21 @@ static char *expdup(const char *str)
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasbraces;
|
||||
char envname[1024];
|
||||
size_t k = 0;
|
||||
|
||||
hasbraces = (*str == '{');
|
||||
if(hasbraces) str++;
|
||||
|
||||
while((isalnum(*str) || *str == '_') && k < sizeof(envname)-1)
|
||||
envname[k++] = *(str++);
|
||||
envname[k++] = '\0';
|
||||
|
||||
if(hasbraces && *str != '}')
|
||||
continue;
|
||||
|
||||
if(hasbraces) str++;
|
||||
if((addstr=getenv(envname)) == NULL)
|
||||
continue;
|
||||
addstrlen = strlen(addstr);
|
||||
@@ -192,12 +202,8 @@ static void LoadConfigFromFile(FILE *f)
|
||||
char key[256] = "";
|
||||
char value[256] = "";
|
||||
|
||||
comment = strchr(buffer, '#');
|
||||
if(comment) *(comment++) = 0;
|
||||
|
||||
line = rstrip(lstrip(buffer));
|
||||
if(!line[0])
|
||||
continue;
|
||||
if(!line[0]) continue;
|
||||
|
||||
if(line[0] == '[')
|
||||
{
|
||||
@@ -205,10 +211,21 @@ static void LoadConfigFromFile(FILE *f)
|
||||
char *endsection;
|
||||
|
||||
endsection = strchr(section, ']');
|
||||
if(!endsection || section == endsection || endsection[1] != 0)
|
||||
if(!endsection || section == endsection)
|
||||
{
|
||||
ERR("config parse error: bad line \"%s\"\n", line);
|
||||
continue;
|
||||
ERR("config parse error: bad line \"%s\"\n", line);
|
||||
continue;
|
||||
}
|
||||
if(endsection[1] != 0)
|
||||
{
|
||||
char *end = endsection+1;
|
||||
while(isspace(*end))
|
||||
++end;
|
||||
if(*end != 0 && *end != '#')
|
||||
{
|
||||
ERR("config parse error: bad line \"%s\"\n", line);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*endsection = 0;
|
||||
|
||||
@@ -216,13 +233,71 @@ static void LoadConfigFromFile(FILE *f)
|
||||
curSection[0] = 0;
|
||||
else
|
||||
{
|
||||
strncpy(curSection, section, sizeof(curSection)-1);
|
||||
size_t len, p = 0;
|
||||
do {
|
||||
char *nextp = strchr(section, '%');
|
||||
if(!nextp)
|
||||
{
|
||||
strncpy(curSection+p, section, sizeof(curSection)-1-p);
|
||||
break;
|
||||
}
|
||||
|
||||
len = nextp - section;
|
||||
if(len > sizeof(curSection)-1-p)
|
||||
len = sizeof(curSection)-1-p;
|
||||
strncpy(curSection+p, section, len);
|
||||
p += len;
|
||||
section = nextp;
|
||||
|
||||
if(((section[1] >= '0' && section[1] <= '9') ||
|
||||
(section[1] >= 'a' && section[1] <= 'f') ||
|
||||
(section[1] >= 'A' && section[1] <= 'F')) &&
|
||||
((section[2] >= '0' && section[2] <= '9') ||
|
||||
(section[2] >= 'a' && section[2] <= 'f') ||
|
||||
(section[2] >= 'A' && section[2] <= 'F')))
|
||||
{
|
||||
unsigned char b = 0;
|
||||
if(section[1] >= '0' && section[1] <= '9')
|
||||
b = (section[1]-'0') << 4;
|
||||
else if(section[1] >= 'a' && section[1] <= 'f')
|
||||
b = (section[1]-'a'+0xa) << 4;
|
||||
else if(section[1] >= 'A' && section[1] <= 'F')
|
||||
b = (section[1]-'A'+0x0a) << 4;
|
||||
if(section[2] >= '0' && section[2] <= '9')
|
||||
b |= (section[2]-'0');
|
||||
else if(section[2] >= 'a' && section[2] <= 'f')
|
||||
b |= (section[2]-'a'+0xa);
|
||||
else if(section[2] >= 'A' && section[2] <= 'F')
|
||||
b |= (section[2]-'A'+0x0a);
|
||||
if(p < sizeof(curSection)-1)
|
||||
curSection[p++] = b;
|
||||
section += 3;
|
||||
}
|
||||
else if(section[1] == '%')
|
||||
{
|
||||
if(p < sizeof(curSection)-1)
|
||||
curSection[p++] = '%';
|
||||
section += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(p < sizeof(curSection)-1)
|
||||
curSection[p++] = '%';
|
||||
section += 1;
|
||||
}
|
||||
if(p < sizeof(curSection)-1)
|
||||
curSection[p] = 0;
|
||||
} while(p < sizeof(curSection)-1 && *section != 0);
|
||||
curSection[sizeof(curSection)-1] = 0;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
comment = strchr(line, '#');
|
||||
if(comment) *(comment++) = 0;
|
||||
if(!line[0]) continue;
|
||||
|
||||
if(sscanf(line, "%255[^=] = \"%255[^\"]\"", key, value) == 2 ||
|
||||
sscanf(line, "%255[^=] = '%255[^\']'", key, value) == 2 ||
|
||||
sscanf(line, "%255[^=] = %255[^\n]", key, value) == 2)
|
||||
@@ -292,15 +367,31 @@ void ReadALConfig(void)
|
||||
{
|
||||
WCHAR buffer[PATH_MAX];
|
||||
const WCHAR *str;
|
||||
al_string ppath;
|
||||
FILE *f;
|
||||
|
||||
if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
|
||||
{
|
||||
size_t p = lstrlenW(buffer);
|
||||
_snwprintf(buffer+p, PATH_MAX-p, L"\\alsoft.ini");
|
||||
al_string filepath = AL_STRING_INIT_STATIC();
|
||||
alstr_copy_wcstr(&filepath, buffer);
|
||||
alstr_append_cstr(&filepath, "\\alsoft.ini");
|
||||
|
||||
TRACE("Loading config %ls...\n", buffer);
|
||||
f = _wfopen(buffer, L"rt");
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(filepath));
|
||||
f = al_fopen(alstr_get_cstr(filepath), "rt");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
alstr_reset(&filepath);
|
||||
}
|
||||
|
||||
ppath = GetProcPath();
|
||||
if(!alstr_empty(ppath))
|
||||
{
|
||||
alstr_append_cstr(&ppath, "\\alsoft.ini");
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(ppath));
|
||||
f = al_fopen(alstr_get_cstr(ppath), "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
@@ -310,20 +401,27 @@ void ReadALConfig(void)
|
||||
|
||||
if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str)
|
||||
{
|
||||
TRACE("Loading config %ls...\n", str);
|
||||
f = _wfopen(str, L"rt");
|
||||
al_string filepath = AL_STRING_INIT_STATIC();
|
||||
alstr_copy_wcstr(&filepath, str);
|
||||
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(filepath));
|
||||
f = al_fopen(alstr_get_cstr(filepath), "rt");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
alstr_reset(&filepath);
|
||||
}
|
||||
|
||||
alstr_reset(&ppath);
|
||||
}
|
||||
#else
|
||||
void ReadALConfig(void)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
const char *str;
|
||||
al_string ppath;
|
||||
FILE *f;
|
||||
|
||||
str = "/etc/openal/alsoft.conf";
|
||||
@@ -403,6 +501,19 @@ void ReadALConfig(void)
|
||||
}
|
||||
}
|
||||
|
||||
ppath = GetProcPath();
|
||||
if(!alstr_empty(ppath))
|
||||
{
|
||||
alstr_append_cstr(&ppath, "/alsoft.conf");
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(ppath));
|
||||
f = al_fopen(alstr_get_cstr(ppath), "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
if((str=getenv("ALSOFT_CONF")) != NULL && *str)
|
||||
{
|
||||
TRACE("Loading config %s...\n", str);
|
||||
@@ -413,6 +524,8 @@ void ReadALConfig(void)
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
alstr_reset(&ppath);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -428,7 +541,7 @@ void FreeALConfig(void)
|
||||
free(cfgBlock.entries);
|
||||
}
|
||||
|
||||
const char *GetConfigValue(const char *blockName, const char *keyName, const char *def)
|
||||
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def)
|
||||
{
|
||||
unsigned int i;
|
||||
char key[256];
|
||||
@@ -437,16 +550,26 @@ const char *GetConfigValue(const char *blockName, const char *keyName, const cha
|
||||
return def;
|
||||
|
||||
if(blockName && strcasecmp(blockName, "general") != 0)
|
||||
snprintf(key, sizeof(key), "%s/%s", blockName, keyName);
|
||||
{
|
||||
if(devName)
|
||||
snprintf(key, sizeof(key), "%s/%s/%s", blockName, devName, keyName);
|
||||
else
|
||||
snprintf(key, sizeof(key), "%s/%s", blockName, keyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(key, keyName, sizeof(key)-1);
|
||||
key[sizeof(key)-1] = 0;
|
||||
if(devName)
|
||||
snprintf(key, sizeof(key), "%s/%s", devName, keyName);
|
||||
else
|
||||
{
|
||||
strncpy(key, keyName, sizeof(key)-1);
|
||||
key[sizeof(key)-1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0;i < cfgBlock.entryCount;i++)
|
||||
{
|
||||
if(strcasecmp(cfgBlock.entries[i].key, key) == 0)
|
||||
if(strcmp(cfgBlock.entries[i].key, key) == 0)
|
||||
{
|
||||
TRACE("Found %s = \"%s\"\n", key, cfgBlock.entries[i].value);
|
||||
if(cfgBlock.entries[i].value[0])
|
||||
@@ -455,46 +578,50 @@ const char *GetConfigValue(const char *blockName, const char *keyName, const cha
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("Key %s not found\n", key);
|
||||
return def;
|
||||
if(!devName)
|
||||
{
|
||||
TRACE("Key %s not found\n", key);
|
||||
return def;
|
||||
}
|
||||
return GetConfigValue(NULL, blockName, keyName, def);
|
||||
}
|
||||
|
||||
int ConfigValueExists(const char *blockName, const char *keyName)
|
||||
int ConfigValueExists(const char *devName, const char *blockName, const char *keyName)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
return !!val[0];
|
||||
}
|
||||
|
||||
int ConfigValueStr(const char *blockName, const char *keyName, const char **ret)
|
||||
int ConfigValueStr(const char *devName, const char *blockName, const char *keyName, const char **ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueInt(const char *blockName, const char *keyName, int *ret)
|
||||
int ConfigValueInt(const char *devName, const char *blockName, const char *keyName, int *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = strtol(val, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueUInt(const char *blockName, const char *keyName, unsigned int *ret)
|
||||
int ConfigValueUInt(const char *devName, const char *blockName, const char *keyName, unsigned int *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = strtoul(val, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueFloat(const char *blockName, const char *keyName, float *ret)
|
||||
int ConfigValueFloat(const char *devName, const char *blockName, const char *keyName, float *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
#ifdef HAVE_STRTOF
|
||||
@@ -505,9 +632,19 @@ int ConfigValueFloat(const char *blockName, const char *keyName, float *ret)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int GetConfigValueBool(const char *blockName, const char *keyName, int def)
|
||||
int ConfigValueBool(const char *devName, const char *blockName, const char *keyName, int *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
|
||||
strcasecmp(val, "on") == 0 || atoi(val) != 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, int def)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
|
||||
if(!val[0]) return !!def;
|
||||
return (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
|
||||
@@ -0,0 +1,317 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "threads.h"
|
||||
#include "almalloc.h"
|
||||
#include "compat.h"
|
||||
|
||||
|
||||
/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended
|
||||
* to include an element size. Consequently, parameters and return values for a
|
||||
* size or count is in 'elements', not bytes. Additionally, it only supports
|
||||
* single-consumer/single-provider operation. */
|
||||
struct ll_ringbuffer {
|
||||
ATOMIC(size_t) write_ptr;
|
||||
ATOMIC(size_t) read_ptr;
|
||||
size_t size;
|
||||
size_t size_mask;
|
||||
size_t elem_size;
|
||||
int mlocked;
|
||||
|
||||
alignas(16) char buf[];
|
||||
};
|
||||
|
||||
/* Create a new ringbuffer to hold at least `sz' elements of `elem_sz' bytes.
|
||||
* The number of elements is rounded up to the next power of two. */
|
||||
ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz)
|
||||
{
|
||||
ll_ringbuffer_t *rb;
|
||||
ALuint power_of_two;
|
||||
|
||||
power_of_two = NextPowerOf2(sz);
|
||||
if(power_of_two < sz)
|
||||
return NULL;
|
||||
|
||||
rb = al_malloc(16, sizeof(*rb) + power_of_two*elem_sz);
|
||||
if(!rb) return NULL;
|
||||
|
||||
ATOMIC_INIT(&rb->write_ptr, 0);
|
||||
ATOMIC_INIT(&rb->read_ptr, 0);
|
||||
rb->size = power_of_two;
|
||||
rb->size_mask = rb->size - 1;
|
||||
rb->elem_size = elem_sz;
|
||||
rb->mlocked = 0;
|
||||
return rb;
|
||||
}
|
||||
|
||||
/* Free all data associated with the ringbuffer `rb'. */
|
||||
void ll_ringbuffer_free(ll_ringbuffer_t *rb)
|
||||
{
|
||||
if(rb)
|
||||
{
|
||||
#ifdef USE_MLOCK
|
||||
if(rb->mlocked)
|
||||
munlock(rb, sizeof(*rb) + rb->size*rb->elem_size);
|
||||
#endif /* USE_MLOCK */
|
||||
al_free(rb);
|
||||
}
|
||||
}
|
||||
|
||||
/* Lock the data block of `rb' using the system call 'mlock'. */
|
||||
int ll_ringbuffer_mlock(ll_ringbuffer_t *rb)
|
||||
{
|
||||
#ifdef USE_MLOCK
|
||||
if(!rb->mlocked && mlock(rb, sizeof(*rb) + rb->size*rb->elem_size))
|
||||
return -1;
|
||||
#endif /* USE_MLOCK */
|
||||
rb->mlocked = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reset the read and write pointers to zero. This is not thread safe. */
|
||||
void ll_ringbuffer_reset(ll_ringbuffer_t *rb)
|
||||
{
|
||||
ATOMIC_STORE(&rb->write_ptr, 0, almemory_order_release);
|
||||
ATOMIC_STORE(&rb->read_ptr, 0, almemory_order_release);
|
||||
memset(rb->buf, 0, rb->size*rb->elem_size);
|
||||
}
|
||||
|
||||
/* Return the number of elements available for reading. This is the number of
|
||||
* elements in front of the read pointer and behind the write pointer. */
|
||||
size_t ll_ringbuffer_read_space(const ll_ringbuffer_t *rb)
|
||||
{
|
||||
size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
|
||||
size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
|
||||
return (w-r) & rb->size_mask;
|
||||
}
|
||||
/* Return the number of elements available for writing. This is the number of
|
||||
* elements in front of the write pointer and behind the read pointer. */
|
||||
size_t ll_ringbuffer_write_space(const ll_ringbuffer_t *rb)
|
||||
{
|
||||
size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
|
||||
size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
|
||||
return (r-w-1) & rb->size_mask;
|
||||
}
|
||||
|
||||
/* The copying data reader. Copy at most `cnt' elements from `rb' to `dest'.
|
||||
* Returns the actual number of elements copied. */
|
||||
size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt)
|
||||
{
|
||||
size_t read_ptr;
|
||||
size_t free_cnt;
|
||||
size_t cnt2;
|
||||
size_t to_read;
|
||||
size_t n1, n2;
|
||||
|
||||
free_cnt = ll_ringbuffer_read_space(rb);
|
||||
if(free_cnt == 0) return 0;
|
||||
|
||||
to_read = (cnt > free_cnt) ? free_cnt : cnt;
|
||||
read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask;
|
||||
|
||||
cnt2 = read_ptr + to_read;
|
||||
if(cnt2 > rb->size)
|
||||
{
|
||||
n1 = rb->size - read_ptr;
|
||||
n2 = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = to_read;
|
||||
n2 = 0;
|
||||
}
|
||||
|
||||
memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size);
|
||||
read_ptr += n1;
|
||||
if(n2)
|
||||
{
|
||||
memcpy(dest + n1*rb->elem_size, &rb->buf[(read_ptr&rb->size_mask)*rb->elem_size],
|
||||
n2*rb->elem_size);
|
||||
read_ptr += n2;
|
||||
}
|
||||
ATOMIC_STORE(&rb->read_ptr, read_ptr, almemory_order_release);
|
||||
return to_read;
|
||||
}
|
||||
|
||||
/* The copying data reader w/o read pointer advance. Copy at most `cnt'
|
||||
* elements from `rb' to `dest'. Returns the actual number of elements copied.
|
||||
*/
|
||||
size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
|
||||
{
|
||||
size_t free_cnt;
|
||||
size_t cnt2;
|
||||
size_t to_read;
|
||||
size_t n1, n2;
|
||||
size_t read_ptr;
|
||||
|
||||
free_cnt = ll_ringbuffer_read_space(rb);
|
||||
if(free_cnt == 0) return 0;
|
||||
|
||||
to_read = (cnt > free_cnt) ? free_cnt : cnt;
|
||||
read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask;
|
||||
|
||||
cnt2 = read_ptr + to_read;
|
||||
if(cnt2 > rb->size)
|
||||
{
|
||||
n1 = rb->size - read_ptr;
|
||||
n2 = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = to_read;
|
||||
n2 = 0;
|
||||
}
|
||||
|
||||
memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size);
|
||||
if(n2)
|
||||
{
|
||||
read_ptr += n1;
|
||||
memcpy(dest + n1*rb->elem_size, &rb->buf[(read_ptr&rb->size_mask)*rb->elem_size],
|
||||
n2*rb->elem_size);
|
||||
}
|
||||
return to_read;
|
||||
}
|
||||
|
||||
/* The copying data writer. Copy at most `cnt' elements to `rb' from `src'.
|
||||
* Returns the actual number of elements copied. */
|
||||
size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
|
||||
{
|
||||
size_t write_ptr;
|
||||
size_t free_cnt;
|
||||
size_t cnt2;
|
||||
size_t to_write;
|
||||
size_t n1, n2;
|
||||
|
||||
free_cnt = ll_ringbuffer_write_space(rb);
|
||||
if(free_cnt == 0) return 0;
|
||||
|
||||
to_write = (cnt > free_cnt) ? free_cnt : cnt;
|
||||
write_ptr = ATOMIC_LOAD(&rb->write_ptr, almemory_order_relaxed) & rb->size_mask;
|
||||
|
||||
cnt2 = write_ptr + to_write;
|
||||
if(cnt2 > rb->size)
|
||||
{
|
||||
n1 = rb->size - write_ptr;
|
||||
n2 = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = to_write;
|
||||
n2 = 0;
|
||||
}
|
||||
|
||||
memcpy(&rb->buf[write_ptr*rb->elem_size], src, n1*rb->elem_size);
|
||||
write_ptr += n1;
|
||||
if(n2)
|
||||
{
|
||||
memcpy(&rb->buf[(write_ptr&rb->size_mask)*rb->elem_size], src + n1*rb->elem_size,
|
||||
n2*rb->elem_size);
|
||||
write_ptr += n2;
|
||||
}
|
||||
ATOMIC_STORE(&rb->write_ptr, write_ptr, almemory_order_release);
|
||||
return to_write;
|
||||
}
|
||||
|
||||
/* Advance the read pointer `cnt' places. */
|
||||
void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt)
|
||||
{
|
||||
ATOMIC_ADD(&rb->read_ptr, cnt, almemory_order_acq_rel);
|
||||
}
|
||||
|
||||
/* Advance the write pointer `cnt' places. */
|
||||
void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt)
|
||||
{
|
||||
ATOMIC_ADD(&rb->write_ptr, cnt, almemory_order_acq_rel);
|
||||
}
|
||||
|
||||
/* The non-copying data reader. `vec' is an array of two places. Set the values
|
||||
* at `vec' to hold the current readable data at `rb'. If the readable data is
|
||||
* in one segment the second segment has zero length. */
|
||||
void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t * vec)
|
||||
{
|
||||
size_t free_cnt;
|
||||
size_t cnt2;
|
||||
size_t w, r;
|
||||
|
||||
w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
|
||||
r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
|
||||
w &= rb->size_mask;
|
||||
r &= rb->size_mask;
|
||||
free_cnt = (w-r) & rb->size_mask;
|
||||
|
||||
cnt2 = r + free_cnt;
|
||||
if(cnt2 > rb->size)
|
||||
{
|
||||
/* Two part vector: the rest of the buffer after the current write ptr,
|
||||
* plus some from the start of the buffer. */
|
||||
vec[0].buf = (char*)&rb->buf[r*rb->elem_size];
|
||||
vec[0].len = rb->size - r;
|
||||
vec[1].buf = (char*)rb->buf;
|
||||
vec[1].len = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Single part vector: just the rest of the buffer */
|
||||
vec[0].buf = (char*)&rb->buf[r*rb->elem_size];
|
||||
vec[0].len = free_cnt;
|
||||
vec[1].buf = NULL;
|
||||
vec[1].len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* The non-copying data writer. `vec' is an array of two places. Set the values
|
||||
* at `vec' to hold the current writeable data at `rb'. If the writeable data
|
||||
* is in one segment the second segment has zero length. */
|
||||
void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t *vec)
|
||||
{
|
||||
size_t free_cnt;
|
||||
size_t cnt2;
|
||||
size_t w, r;
|
||||
|
||||
w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
|
||||
r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
|
||||
w &= rb->size_mask;
|
||||
r &= rb->size_mask;
|
||||
free_cnt = (r-w-1) & rb->size_mask;
|
||||
|
||||
cnt2 = w + free_cnt;
|
||||
if(cnt2 > rb->size)
|
||||
{
|
||||
/* Two part vector: the rest of the buffer after the current write ptr,
|
||||
* plus some from the start of the buffer. */
|
||||
vec[0].buf = (char*)&rb->buf[w*rb->elem_size];
|
||||
vec[0].len = rb->size - w;
|
||||
vec[1].buf = (char*)rb->buf;
|
||||
vec[1].len = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
vec[0].buf = (char*)&rb->buf[w*rb->elem_size];
|
||||
vec[0].len = free_cnt;
|
||||
vec[1].buf = NULL;
|
||||
vec[1].len = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef ALSTRING_H
|
||||
#define ALSTRING_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
|
||||
typedef char al_string_char_type;
|
||||
TYPEDEF_VECTOR(al_string_char_type, al_string)
|
||||
TYPEDEF_VECTOR(al_string, vector_al_string)
|
||||
|
||||
inline void alstr_reset(al_string *str)
|
||||
{ VECTOR_DEINIT(*str); }
|
||||
#define AL_STRING_INIT(_x) do { (_x) = (al_string)NULL; } while(0)
|
||||
#define AL_STRING_INIT_STATIC() ((al_string)NULL)
|
||||
#define AL_STRING_DEINIT(_x) alstr_reset(&(_x))
|
||||
|
||||
inline size_t alstr_length(const_al_string str)
|
||||
{ return VECTOR_SIZE(str); }
|
||||
|
||||
inline ALboolean alstr_empty(const_al_string str)
|
||||
{ return alstr_length(str) == 0; }
|
||||
|
||||
inline const al_string_char_type *alstr_get_cstr(const_al_string str)
|
||||
{ return str ? &VECTOR_FRONT(str) : ""; }
|
||||
|
||||
void alstr_clear(al_string *str);
|
||||
|
||||
int alstr_cmp(const_al_string str1, const_al_string str2);
|
||||
int alstr_cmp_cstr(const_al_string str1, const al_string_char_type *str2);
|
||||
|
||||
void alstr_copy(al_string *str, const_al_string from);
|
||||
void alstr_copy_cstr(al_string *str, const al_string_char_type *from);
|
||||
void alstr_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
|
||||
|
||||
void alstr_append_char(al_string *str, const al_string_char_type c);
|
||||
void alstr_append_cstr(al_string *str, const al_string_char_type *from);
|
||||
void alstr_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <wchar.h>
|
||||
/* Windows-only methods to deal with WideChar strings. */
|
||||
void alstr_copy_wcstr(al_string *str, const wchar_t *from);
|
||||
void alstr_append_wcstr(al_string *str, const wchar_t *from);
|
||||
void alstr_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
|
||||
#endif
|
||||
|
||||
#endif /* ALSTRING_H */
|
||||
@@ -0,0 +1,566 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ambdec.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
|
||||
static char *lstrip(char *line)
|
||||
{
|
||||
while(isspace(line[0]))
|
||||
line++;
|
||||
return line;
|
||||
}
|
||||
|
||||
static char *rstrip(char *line)
|
||||
{
|
||||
size_t len = strlen(line);
|
||||
while(len > 0 && isspace(line[len-1]))
|
||||
len--;
|
||||
line[len] = 0;
|
||||
return line;
|
||||
}
|
||||
|
||||
static int readline(FILE *f, char **output, size_t *maxlen)
|
||||
{
|
||||
size_t len = 0;
|
||||
int c;
|
||||
|
||||
while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
|
||||
;
|
||||
if(c == EOF)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
if(len+1 >= *maxlen)
|
||||
{
|
||||
void *temp = NULL;
|
||||
size_t newmax;
|
||||
|
||||
newmax = (*maxlen ? (*maxlen)<<1 : 32);
|
||||
if(newmax > *maxlen)
|
||||
temp = realloc(*output, newmax);
|
||||
if(!temp)
|
||||
{
|
||||
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*output = temp;
|
||||
*maxlen = newmax;
|
||||
}
|
||||
(*output)[len++] = c;
|
||||
(*output)[len] = '\0';
|
||||
} while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Custom strtok_r, since we can't rely on it existing. */
|
||||
static char *my_strtok_r(char *str, const char *delim, char **saveptr)
|
||||
{
|
||||
/* Sanity check and update internal pointer. */
|
||||
if(!saveptr || !delim) return NULL;
|
||||
if(str) *saveptr = str;
|
||||
str = *saveptr;
|
||||
|
||||
/* Nothing more to do with this string. */
|
||||
if(!str) return NULL;
|
||||
|
||||
/* Find the first non-delimiter character. */
|
||||
while(*str != '\0' && strchr(delim, *str) != NULL)
|
||||
str++;
|
||||
if(*str == '\0')
|
||||
{
|
||||
/* End of string. */
|
||||
*saveptr = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find the next delimiter character. */
|
||||
*saveptr = strpbrk(str, delim);
|
||||
if(*saveptr) *((*saveptr)++) = '\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *read_int(ALint *num, const char *line, int base)
|
||||
{
|
||||
char *end;
|
||||
*num = strtol(line, &end, base);
|
||||
if(end && *end != '\0')
|
||||
end = lstrip(end);
|
||||
return end;
|
||||
}
|
||||
|
||||
static char *read_uint(ALuint *num, const char *line, int base)
|
||||
{
|
||||
char *end;
|
||||
*num = strtoul(line, &end, base);
|
||||
if(end && *end != '\0')
|
||||
end = lstrip(end);
|
||||
return end;
|
||||
}
|
||||
|
||||
static char *read_float(ALfloat *num, const char *line)
|
||||
{
|
||||
char *end;
|
||||
#ifdef HAVE_STRTOF
|
||||
*num = strtof(line, &end);
|
||||
#else
|
||||
*num = (ALfloat)strtod(line, &end);
|
||||
#endif
|
||||
if(end && *end != '\0')
|
||||
end = lstrip(end);
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
char *read_clipped_line(FILE *f, char **buffer, size_t *maxlen)
|
||||
{
|
||||
while(readline(f, buffer, maxlen))
|
||||
{
|
||||
char *line, *comment;
|
||||
|
||||
line = lstrip(*buffer);
|
||||
comment = strchr(line, '#');
|
||||
if(comment) *(comment++) = 0;
|
||||
|
||||
line = rstrip(line);
|
||||
if(line[0]) return line;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int load_ambdec_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
|
||||
{
|
||||
ALsizei cur = 0;
|
||||
while(cur < conf->NumSpeakers)
|
||||
{
|
||||
const char *cmd = my_strtok_r(NULL, " \t", saveptr);
|
||||
if(!cmd)
|
||||
{
|
||||
char *line = read_clipped_line(f, buffer, maxlen);
|
||||
if(!line)
|
||||
{
|
||||
ERR("Unexpected end of file\n");
|
||||
return 0;
|
||||
}
|
||||
cmd = my_strtok_r(line, " \t", saveptr);
|
||||
}
|
||||
|
||||
if(strcmp(cmd, "add_spkr") == 0)
|
||||
{
|
||||
const char *name = my_strtok_r(NULL, " \t", saveptr);
|
||||
const char *dist = my_strtok_r(NULL, " \t", saveptr);
|
||||
const char *az = my_strtok_r(NULL, " \t", saveptr);
|
||||
const char *elev = my_strtok_r(NULL, " \t", saveptr);
|
||||
const char *conn = my_strtok_r(NULL, " \t", saveptr);
|
||||
|
||||
if(!name) WARN("Name not specified for speaker %u\n", cur+1);
|
||||
else alstr_copy_cstr(&conf->Speakers[cur].Name, name);
|
||||
if(!dist) WARN("Distance not specified for speaker %u\n", cur+1);
|
||||
else read_float(&conf->Speakers[cur].Distance, dist);
|
||||
if(!az) WARN("Azimuth not specified for speaker %u\n", cur+1);
|
||||
else read_float(&conf->Speakers[cur].Azimuth, az);
|
||||
if(!elev) WARN("Elevation not specified for speaker %u\n", cur+1);
|
||||
else read_float(&conf->Speakers[cur].Elevation, elev);
|
||||
if(!conn) TRACE("Connection not specified for speaker %u\n", cur+1);
|
||||
else alstr_copy_cstr(&conf->Speakers[cur].Connection, conn);
|
||||
|
||||
cur++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unexpected speakers command: %s\n", cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmd = my_strtok_r(NULL, " \t", saveptr);
|
||||
if(cmd)
|
||||
{
|
||||
ERR("Unexpected junk on line: %s\n", cmd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALsizei maxrow, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
|
||||
{
|
||||
int gotgains = 0;
|
||||
ALsizei cur = 0;
|
||||
while(cur < maxrow)
|
||||
{
|
||||
const char *cmd = my_strtok_r(NULL, " \t", saveptr);
|
||||
if(!cmd)
|
||||
{
|
||||
char *line = read_clipped_line(f, buffer, maxlen);
|
||||
if(!line)
|
||||
{
|
||||
ERR("Unexpected end of file\n");
|
||||
return 0;
|
||||
}
|
||||
cmd = my_strtok_r(line, " \t", saveptr);
|
||||
}
|
||||
|
||||
if(strcmp(cmd, "order_gain") == 0)
|
||||
{
|
||||
ALuint curgain = 0;
|
||||
char *line;
|
||||
while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
|
||||
{
|
||||
ALfloat value;
|
||||
line = read_float(&value, line);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk on gain %u: %s\n", curgain+1, line);
|
||||
return 0;
|
||||
}
|
||||
if(curgain < MAX_AMBI_ORDER+1)
|
||||
gains[curgain] = value;
|
||||
curgain++;
|
||||
}
|
||||
while(curgain < MAX_AMBI_ORDER+1)
|
||||
gains[curgain++] = 0.0f;
|
||||
gotgains = 1;
|
||||
}
|
||||
else if(strcmp(cmd, "add_row") == 0)
|
||||
{
|
||||
ALuint curidx = 0;
|
||||
char *line;
|
||||
while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
|
||||
{
|
||||
ALfloat value;
|
||||
line = read_float(&value, line);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk on matrix element %ux%u: %s\n", cur, curidx, line);
|
||||
return 0;
|
||||
}
|
||||
if(curidx < MAX_AMBI_COEFFS)
|
||||
matrix[cur][curidx] = value;
|
||||
curidx++;
|
||||
}
|
||||
while(curidx < MAX_AMBI_COEFFS)
|
||||
matrix[cur][curidx++] = 0.0f;
|
||||
cur++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unexpected speakers command: %s\n", cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmd = my_strtok_r(NULL, " \t", saveptr);
|
||||
if(cmd)
|
||||
{
|
||||
ERR("Unexpected junk on line: %s\n", cmd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(!gotgains)
|
||||
{
|
||||
ERR("Matrix order_gain not specified\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ambdec_init(AmbDecConf *conf)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
memset(conf, 0, sizeof(*conf));
|
||||
AL_STRING_INIT(conf->Description);
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
{
|
||||
AL_STRING_INIT(conf->Speakers[i].Name);
|
||||
AL_STRING_INIT(conf->Speakers[i].Connection);
|
||||
}
|
||||
}
|
||||
|
||||
void ambdec_deinit(AmbDecConf *conf)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
alstr_reset(&conf->Description);
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
{
|
||||
alstr_reset(&conf->Speakers[i].Name);
|
||||
alstr_reset(&conf->Speakers[i].Connection);
|
||||
}
|
||||
memset(conf, 0, sizeof(*conf));
|
||||
}
|
||||
|
||||
int ambdec_load(AmbDecConf *conf, const char *fname)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
size_t maxlen = 0;
|
||||
char *line;
|
||||
FILE *f;
|
||||
|
||||
f = al_fopen(fname, "r");
|
||||
if(!f)
|
||||
{
|
||||
ERR("Failed to open: %s\n", fname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while((line=read_clipped_line(f, &buffer, &maxlen)) != NULL)
|
||||
{
|
||||
char *saveptr;
|
||||
char *command;
|
||||
|
||||
command = my_strtok_r(line, "/ \t", &saveptr);
|
||||
if(!command)
|
||||
{
|
||||
ERR("Malformed line: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if(strcmp(command, "description") == 0)
|
||||
{
|
||||
char *value = my_strtok_r(NULL, "", &saveptr);
|
||||
alstr_copy_cstr(&conf->Description, lstrip(value));
|
||||
}
|
||||
else if(strcmp(command, "version") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "", &saveptr);
|
||||
line = read_uint(&conf->Version, line, 10);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk after version: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
if(conf->Version != 3)
|
||||
{
|
||||
ERR("Unsupported version: %u\n", conf->Version);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(command, "dec") == 0)
|
||||
{
|
||||
const char *dec = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(strcmp(dec, "chan_mask") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "", &saveptr);
|
||||
line = read_uint(&conf->ChanMask, line, 16);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk after mask: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(dec, "freq_bands") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "", &saveptr);
|
||||
line = read_uint(&conf->FreqBands, line, 10);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk after freq_bands: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
if(conf->FreqBands != 1 && conf->FreqBands != 2)
|
||||
{
|
||||
ERR("Invalid freq_bands value: %u\n", conf->FreqBands);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(dec, "speakers") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "", &saveptr);
|
||||
line = read_int(&conf->NumSpeakers, line, 10);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk after speakers: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
if(conf->NumSpeakers > MAX_OUTPUT_CHANNELS)
|
||||
{
|
||||
ERR("Unsupported speaker count: %u\n", conf->NumSpeakers);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(dec, "coeff_scale") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, " \t", &saveptr);
|
||||
if(strcmp(line, "n3d") == 0)
|
||||
conf->CoeffScale = ADS_N3D;
|
||||
else if(strcmp(line, "sn3d") == 0)
|
||||
conf->CoeffScale = ADS_SN3D;
|
||||
else if(strcmp(line, "fuma") == 0)
|
||||
conf->CoeffScale = ADS_FuMa;
|
||||
else
|
||||
{
|
||||
ERR("Unsupported coeff scale: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unexpected /dec option: %s\n", dec);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(command, "opt") == 0)
|
||||
{
|
||||
const char *opt = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(strcmp(opt, "xover_freq") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "", &saveptr);
|
||||
line = read_float(&conf->XOverFreq, line);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk after xover_freq: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(opt, "xover_ratio") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "", &saveptr);
|
||||
line = read_float(&conf->XOverRatio, line);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk after xover_ratio: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(opt, "input_scale") == 0 || strcmp(opt, "nfeff_comp") == 0 ||
|
||||
strcmp(opt, "delay_comp") == 0 || strcmp(opt, "level_comp") == 0)
|
||||
{
|
||||
/* Unused */
|
||||
my_strtok_r(NULL, " \t", &saveptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unexpected /opt option: %s\n", opt);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(command, "speakers") == 0)
|
||||
{
|
||||
const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(strcmp(value, "{") != 0)
|
||||
{
|
||||
ERR("Expected { after %s command, got %s\n", command, value);
|
||||
goto fail;
|
||||
}
|
||||
if(!load_ambdec_speakers(conf, f, &buffer, &maxlen, &saveptr))
|
||||
goto fail;
|
||||
value = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(!value)
|
||||
{
|
||||
line = read_clipped_line(f, &buffer, &maxlen);
|
||||
if(!line)
|
||||
{
|
||||
ERR("Unexpected end of file\n");
|
||||
goto fail;
|
||||
}
|
||||
value = my_strtok_r(line, "/ \t", &saveptr);
|
||||
}
|
||||
if(strcmp(value, "}") != 0)
|
||||
{
|
||||
ERR("Expected } after speaker definitions, got %s\n", value);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(command, "lfmatrix") == 0 || strcmp(command, "hfmatrix") == 0 ||
|
||||
strcmp(command, "matrix") == 0)
|
||||
{
|
||||
const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(strcmp(value, "{") != 0)
|
||||
{
|
||||
ERR("Expected { after %s command, got %s\n", command, value);
|
||||
goto fail;
|
||||
}
|
||||
if(conf->FreqBands == 1)
|
||||
{
|
||||
if(strcmp(command, "matrix") != 0)
|
||||
{
|
||||
ERR("Unexpected \"%s\" type for a single-band decoder\n", command);
|
||||
goto fail;
|
||||
}
|
||||
if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
|
||||
f, &buffer, &maxlen, &saveptr))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(strcmp(command, "lfmatrix") == 0)
|
||||
{
|
||||
if(!load_ambdec_matrix(conf->LFOrderGain, conf->LFMatrix, conf->NumSpeakers,
|
||||
f, &buffer, &maxlen, &saveptr))
|
||||
goto fail;
|
||||
}
|
||||
else if(strcmp(command, "hfmatrix") == 0)
|
||||
{
|
||||
if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
|
||||
f, &buffer, &maxlen, &saveptr))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unexpected \"%s\" type for a dual-band decoder\n", command);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
value = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(!value)
|
||||
{
|
||||
line = read_clipped_line(f, &buffer, &maxlen);
|
||||
if(!line)
|
||||
{
|
||||
ERR("Unexpected end of file\n");
|
||||
goto fail;
|
||||
}
|
||||
value = my_strtok_r(line, "/ \t", &saveptr);
|
||||
}
|
||||
if(strcmp(value, "}") != 0)
|
||||
{
|
||||
ERR("Expected } after matrix definitions, got %s\n", value);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(command, "end") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(line)
|
||||
{
|
||||
ERR("Unexpected junk on end: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unexpected command: %s\n", command);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
line = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(line)
|
||||
{
|
||||
ERR("Unexpected junk on line: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
ERR("Unexpected end of file\n");
|
||||
|
||||
fail:
|
||||
fclose(f);
|
||||
free(buffer);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef AMBDEC_H
|
||||
#define AMBDEC_H
|
||||
|
||||
#include "alstring.h"
|
||||
#include "alMain.h"
|
||||
|
||||
/* Helpers to read .ambdec configuration files. */
|
||||
|
||||
enum AmbDecScaleType {
|
||||
ADS_N3D,
|
||||
ADS_SN3D,
|
||||
ADS_FuMa,
|
||||
};
|
||||
typedef struct AmbDecConf {
|
||||
al_string Description;
|
||||
ALuint Version; /* Must be 3 */
|
||||
|
||||
ALuint ChanMask;
|
||||
ALuint FreqBands; /* Must be 1 or 2 */
|
||||
ALsizei NumSpeakers;
|
||||
enum AmbDecScaleType CoeffScale;
|
||||
|
||||
ALfloat XOverFreq;
|
||||
ALfloat XOverRatio;
|
||||
|
||||
struct {
|
||||
al_string Name;
|
||||
ALfloat Distance;
|
||||
ALfloat Azimuth;
|
||||
ALfloat Elevation;
|
||||
al_string Connection;
|
||||
} Speakers[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Unused when FreqBands == 1 */
|
||||
ALfloat LFOrderGain[MAX_AMBI_ORDER+1];
|
||||
ALfloat LFMatrix[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
|
||||
|
||||
ALfloat HFOrderGain[MAX_AMBI_ORDER+1];
|
||||
ALfloat HFMatrix[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
|
||||
} AmbDecConf;
|
||||
|
||||
void ambdec_init(AmbDecConf *conf);
|
||||
void ambdec_deinit(AmbDecConf *conf);
|
||||
int ambdec_load(AmbDecConf *conf, const char *fname);
|
||||
|
||||
#endif /* AMBDEC_H */
|
||||
+142
-92
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -199,15 +199,21 @@ static ALCboolean alsa_load(void)
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(!alsa_handle)
|
||||
{
|
||||
al_string missing_funcs = AL_STRING_INIT_STATIC();
|
||||
|
||||
alsa_handle = LoadLib("libasound.so.2");
|
||||
if(!alsa_handle)
|
||||
{
|
||||
WARN("Failed to load %s\n", "libasound.so.2");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
error = ALC_FALSE;
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = GetSymbol(alsa_handle, #f); \
|
||||
if(p##f == NULL) { \
|
||||
error = ALC_TRUE; \
|
||||
alstr_append_cstr(&missing_funcs, "\n" #f); \
|
||||
} \
|
||||
} while(0)
|
||||
ALSA_FUNCS(LOAD_FUNC);
|
||||
@@ -215,10 +221,11 @@ static ALCboolean alsa_load(void)
|
||||
|
||||
if(error)
|
||||
{
|
||||
WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
|
||||
CloseLib(alsa_handle);
|
||||
alsa_handle = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
alstr_reset(&missing_funcs);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -237,16 +244,13 @@ static vector_DevMap CaptureDevices;
|
||||
|
||||
static void clear_devlist(vector_DevMap *devlist)
|
||||
{
|
||||
DevMap *iter, *end;
|
||||
|
||||
iter = VECTOR_ITER_BEGIN(*devlist);
|
||||
end = VECTOR_ITER_END(*devlist);
|
||||
for(;iter != end;iter++)
|
||||
{
|
||||
AL_STRING_DEINIT(iter->name);
|
||||
AL_STRING_DEINIT(iter->device_name);
|
||||
}
|
||||
VECTOR_RESIZE(*devlist, 0);
|
||||
#define FREE_DEV(i) do { \
|
||||
AL_STRING_DEINIT((i)->name); \
|
||||
AL_STRING_DEINIT((i)->device_name); \
|
||||
} while(0)
|
||||
VECTOR_FOR_EACH(DevMap, *devlist, FREE_DEV);
|
||||
VECTOR_RESIZE(*devlist, 0, 0);
|
||||
#undef FREE_DEV
|
||||
}
|
||||
|
||||
|
||||
@@ -272,15 +276,49 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
|
||||
|
||||
AL_STRING_INIT(entry.name);
|
||||
AL_STRING_INIT(entry.device_name);
|
||||
al_string_copy_cstr(&entry.name, alsaDevice);
|
||||
al_string_copy_cstr(&entry.device_name, GetConfigValue("alsa", (stream==SND_PCM_STREAM_PLAYBACK) ?
|
||||
"device" : "capture", "default"));
|
||||
alstr_copy_cstr(&entry.name, alsaDevice);
|
||||
alstr_copy_cstr(&entry.device_name, GetConfigValue(
|
||||
NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? "device" : "capture", "default"
|
||||
));
|
||||
VECTOR_PUSH_BACK(*DeviceList, entry);
|
||||
|
||||
if(stream == SND_PCM_STREAM_PLAYBACK)
|
||||
{
|
||||
const char *customdevs, *sep, *next;
|
||||
next = GetConfigValue(NULL, "alsa", "custom-devices", "");
|
||||
while((customdevs=next) != NULL && customdevs[0])
|
||||
{
|
||||
next = strchr(customdevs, ';');
|
||||
sep = strchr(customdevs, '=');
|
||||
if(!sep)
|
||||
{
|
||||
al_string spec = AL_STRING_INIT_STATIC();
|
||||
if(next)
|
||||
alstr_copy_range(&spec, customdevs, next++);
|
||||
else
|
||||
alstr_copy_cstr(&spec, customdevs);
|
||||
ERR("Invalid ALSA device specification \"%s\"\n", alstr_get_cstr(spec));
|
||||
alstr_reset(&spec);
|
||||
continue;
|
||||
}
|
||||
|
||||
AL_STRING_INIT(entry.name);
|
||||
AL_STRING_INIT(entry.device_name);
|
||||
alstr_copy_range(&entry.name, customdevs, sep++);
|
||||
if(next)
|
||||
alstr_copy_range(&entry.device_name, sep, next++);
|
||||
else
|
||||
alstr_copy_cstr(&entry.device_name, sep);
|
||||
TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name),
|
||||
alstr_get_cstr(entry.device_name));
|
||||
VECTOR_PUSH_BACK(*DeviceList, entry);
|
||||
}
|
||||
}
|
||||
|
||||
card = -1;
|
||||
if((err=snd_card_next(&card)) < 0)
|
||||
ERR("Failed to find a card: %s\n", snd_strerror(err));
|
||||
ConfigValueStr("alsa", prefix_name(stream), &main_prefix);
|
||||
ConfigValueStr(NULL, "alsa", prefix_name(stream), &main_prefix);
|
||||
while(card >= 0)
|
||||
{
|
||||
const char *card_prefix = main_prefix;
|
||||
@@ -304,7 +342,7 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
|
||||
cardid = snd_ctl_card_info_get_id(info);
|
||||
|
||||
snprintf(name, sizeof(name), "%s-%s", prefix_name(stream), cardid);
|
||||
ConfigValueStr("alsa", name, &card_prefix);
|
||||
ConfigValueStr(NULL, "alsa", name, &card_prefix);
|
||||
|
||||
dev = -1;
|
||||
while(1)
|
||||
@@ -321,7 +359,8 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
|
||||
snd_pcm_info_set_device(pcminfo, dev);
|
||||
snd_pcm_info_set_subdevice(pcminfo, 0);
|
||||
snd_pcm_info_set_stream(pcminfo, stream);
|
||||
if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
|
||||
if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0)
|
||||
{
|
||||
if(err != -ENOENT)
|
||||
ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
|
||||
continue;
|
||||
@@ -330,18 +369,18 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
|
||||
devname = snd_pcm_info_get_name(pcminfo);
|
||||
|
||||
snprintf(name, sizeof(name), "%s-%s-%d", prefix_name(stream), cardid, dev);
|
||||
ConfigValueStr("alsa", name, &device_prefix);
|
||||
ConfigValueStr(NULL, "alsa", name, &device_prefix);
|
||||
|
||||
snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)",
|
||||
cardname, devname, cardid, dev);
|
||||
cardname, devname, cardid, dev);
|
||||
snprintf(device, sizeof(device), "%sCARD=%s,DEV=%d",
|
||||
device_prefix, cardid, dev);
|
||||
device_prefix, cardid, dev);
|
||||
|
||||
TRACE("Got device \"%s\", \"%s\"\n", name, device);
|
||||
AL_STRING_INIT(entry.name);
|
||||
AL_STRING_INIT(entry.device_name);
|
||||
al_string_copy_cstr(&entry.name, name);
|
||||
al_string_copy_cstr(&entry.device_name, device);
|
||||
alstr_copy_cstr(&entry.name, name);
|
||||
alstr_copy_cstr(&entry.device_name, device);
|
||||
VECTOR_PUSH_BACK(*DeviceList, entry);
|
||||
}
|
||||
snd_ctl_close(handle);
|
||||
@@ -413,7 +452,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self);
|
||||
static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self);
|
||||
static DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples)
|
||||
static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self);
|
||||
static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self);
|
||||
static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa)
|
||||
@@ -588,7 +627,9 @@ static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr)
|
||||
{
|
||||
case -EAGAIN:
|
||||
continue;
|
||||
#if ESTRPIPE != EPIPE
|
||||
case -ESTRPIPE:
|
||||
#endif
|
||||
case -EPIPE:
|
||||
case -EINTR:
|
||||
ret = snd_pcm_recover(self->pcmHandle, ret, 1);
|
||||
@@ -630,17 +671,17 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
|
||||
if(VECTOR_SIZE(PlaybackDevices) == 0)
|
||||
probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
|
||||
|
||||
#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0)
|
||||
#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
|
||||
#undef MATCH_NAME
|
||||
if(iter == VECTOR_ITER_END(PlaybackDevices))
|
||||
if(iter == VECTOR_END(PlaybackDevices))
|
||||
return ALC_INVALID_VALUE;
|
||||
driver = al_string_get_cstr(iter->device_name);
|
||||
driver = alstr_get_cstr(iter->device_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
name = alsaDevice;
|
||||
driver = GetConfigValue("alsa", "device", "default");
|
||||
driver = GetConfigValue(NULL, "alsa", "device", "default");
|
||||
}
|
||||
|
||||
TRACE("Opening device \"%s\"\n", driver);
|
||||
@@ -654,7 +695,7 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
|
||||
/* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
|
||||
snd_config_update_free_global();
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
@@ -677,6 +718,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
|
||||
unsigned int rate;
|
||||
const char *funcerr;
|
||||
int allowmmap;
|
||||
int dir;
|
||||
int err;
|
||||
|
||||
switch(device->FmtType)
|
||||
@@ -704,7 +746,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
|
||||
break;
|
||||
}
|
||||
|
||||
allowmmap = GetConfigValueBool("alsa", "mmap", 1);
|
||||
allowmmap = GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "mmap", 1);
|
||||
periods = device->NumUpdates;
|
||||
periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
|
||||
bufferLen = periodLen * periods;
|
||||
@@ -748,7 +790,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
|
||||
}
|
||||
CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
|
||||
/* test and set channels (implicitly sets frame bits) */
|
||||
if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0)
|
||||
if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)) < 0)
|
||||
{
|
||||
static const enum DevFmtChannels channellist[] = {
|
||||
DevFmtStereo,
|
||||
@@ -761,17 +803,24 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
|
||||
|
||||
for(k = 0;k < COUNTOF(channellist);k++)
|
||||
{
|
||||
if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0)
|
||||
if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k], 0)) >= 0)
|
||||
{
|
||||
device->FmtChans = channellist[k];
|
||||
device->AmbiOrder = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
|
||||
CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)));
|
||||
/* set rate (implicitly constrains period/buffer parameters) */
|
||||
if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0)
|
||||
ERR("Failed to disable ALSA resampler\n");
|
||||
if(!GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0) ||
|
||||
!(device->Flags&DEVICE_FREQUENCY_REQUEST))
|
||||
{
|
||||
if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0)
|
||||
ERR("Failed to disable ALSA resampler\n");
|
||||
}
|
||||
else if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 1) < 0)
|
||||
ERR("Failed to enable ALSA resampler\n");
|
||||
CHECK(snd_pcm_hw_params_set_rate_near(self->pcmHandle, hp, &rate, NULL));
|
||||
/* set buffer time (implicitly constrains period/buffer parameters) */
|
||||
if((err=snd_pcm_hw_params_set_buffer_time_near(self->pcmHandle, hp, &bufferLen, NULL)) < 0)
|
||||
@@ -784,7 +833,9 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
|
||||
/* retrieve configuration info */
|
||||
CHECK(snd_pcm_hw_params_get_access(hp, &access));
|
||||
CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL));
|
||||
CHECK(snd_pcm_hw_params_get_periods(hp, &periods, NULL));
|
||||
CHECK(snd_pcm_hw_params_get_periods(hp, &periods, &dir));
|
||||
if(dir != 0)
|
||||
WARN("Inexact period count: %u (%d)\n", periods, dir);
|
||||
|
||||
snd_pcm_hw_params_free(hp);
|
||||
hp = NULL;
|
||||
@@ -834,7 +885,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
|
||||
self->size = snd_pcm_frames_to_bytes(self->pcmHandle, device->UpdateSize);
|
||||
if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
|
||||
{
|
||||
self->buffer = malloc(self->size);
|
||||
self->buffer = al_malloc(16, self->size);
|
||||
if(!self->buffer)
|
||||
{
|
||||
ERR("buffer malloc failed\n");
|
||||
@@ -856,7 +907,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
|
||||
if(althrd_create(&self->thread, thread_func, self) != althrd_success)
|
||||
{
|
||||
ERR("Could not create playback thread\n");
|
||||
free(self->buffer);
|
||||
al_free(self->buffer);
|
||||
self->buffer = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
@@ -879,22 +930,29 @@ static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self)
|
||||
self->killNow = 1;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
free(self->buffer);
|
||||
al_free(self->buffer);
|
||||
self->buffer = NULL;
|
||||
}
|
||||
|
||||
static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self)
|
||||
static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
snd_pcm_sframes_t delay = 0;
|
||||
ClockLatency ret;
|
||||
int err;
|
||||
|
||||
ALCplaybackAlsa_lock(self);
|
||||
ret.ClockTime = GetDeviceClockTime(device);
|
||||
if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
|
||||
{
|
||||
ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
|
||||
return 0;
|
||||
delay = 0;
|
||||
}
|
||||
return maxi64((ALint64)delay*1000000000/device->Frequency, 0);
|
||||
if(delay < 0) delay = 0;
|
||||
ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
|
||||
ALCplaybackAlsa_unlock(self);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -907,7 +965,7 @@ typedef struct ALCcaptureAlsa {
|
||||
ALsizei size;
|
||||
|
||||
ALboolean doCapture;
|
||||
RingBuffer *ring;
|
||||
ll_ringbuffer_t *ring;
|
||||
|
||||
snd_pcm_sframes_t last_avail;
|
||||
} ALCcaptureAlsa;
|
||||
@@ -921,7 +979,7 @@ static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self);
|
||||
static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self);
|
||||
static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples);
|
||||
static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self);
|
||||
static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self);
|
||||
static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self);
|
||||
static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa)
|
||||
@@ -955,17 +1013,17 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
|
||||
if(VECTOR_SIZE(CaptureDevices) == 0)
|
||||
probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
|
||||
|
||||
#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0)
|
||||
#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
|
||||
#undef MATCH_NAME
|
||||
if(iter == VECTOR_ITER_END(CaptureDevices))
|
||||
if(iter == VECTOR_END(CaptureDevices))
|
||||
return ALC_INVALID_VALUE;
|
||||
driver = al_string_get_cstr(iter->device_name);
|
||||
driver = alstr_get_cstr(iter->device_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
name = alsaDevice;
|
||||
driver = GetConfigValue("alsa", "capture", "default");
|
||||
driver = GetConfigValue(NULL, "alsa", "capture", "default");
|
||||
}
|
||||
|
||||
TRACE("Opening device \"%s\"\n", driver);
|
||||
@@ -1017,7 +1075,7 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
|
||||
/* set format (implicitly sets sample bits) */
|
||||
CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
|
||||
/* set channels (implicitly sets frame bits) */
|
||||
CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
|
||||
CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)));
|
||||
/* set rate (implicitly constrains period/buffer parameters) */
|
||||
CHECK(snd_pcm_hw_params_set_rate(self->pcmHandle, hp, device->Frequency, 0));
|
||||
/* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
|
||||
@@ -1039,24 +1097,18 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
|
||||
|
||||
if(needring)
|
||||
{
|
||||
self->ring = CreateRingBuffer(FrameSizeFromDevFmt(device->FmtChans, device->FmtType),
|
||||
device->UpdateSize*device->NumUpdates);
|
||||
self->ring = ll_ringbuffer_create(
|
||||
device->UpdateSize*device->NumUpdates + 1,
|
||||
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
|
||||
);
|
||||
if(!self->ring)
|
||||
{
|
||||
ERR("ring buffer create failed\n");
|
||||
goto error2;
|
||||
}
|
||||
|
||||
self->size = snd_pcm_frames_to_bytes(self->pcmHandle, periodSizeInFrames);
|
||||
self->buffer = malloc(self->size);
|
||||
if(!self->buffer)
|
||||
{
|
||||
ERR("buffer malloc failed\n");
|
||||
goto error2;
|
||||
}
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
@@ -1065,9 +1117,7 @@ error:
|
||||
if(hp) snd_pcm_hw_params_free(hp);
|
||||
|
||||
error2:
|
||||
free(self->buffer);
|
||||
self->buffer = NULL;
|
||||
DestroyRingBuffer(self->ring);
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
snd_pcm_close(self->pcmHandle);
|
||||
|
||||
@@ -1077,9 +1127,9 @@ error2:
|
||||
static void ALCcaptureAlsa_close(ALCcaptureAlsa *self)
|
||||
{
|
||||
snd_pcm_close(self->pcmHandle);
|
||||
DestroyRingBuffer(self->ring);
|
||||
ll_ringbuffer_free(self->ring);
|
||||
|
||||
free(self->buffer);
|
||||
al_free(self->buffer);
|
||||
self->buffer = NULL;
|
||||
}
|
||||
|
||||
@@ -1114,11 +1164,11 @@ static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self)
|
||||
void *ptr;
|
||||
|
||||
size = snd_pcm_frames_to_bytes(self->pcmHandle, avail);
|
||||
ptr = malloc(size);
|
||||
ptr = al_malloc(16, size);
|
||||
if(ptr)
|
||||
{
|
||||
ALCcaptureAlsa_captureSamples(self, ptr, avail);
|
||||
free(self->buffer);
|
||||
al_free(self->buffer);
|
||||
self->buffer = ptr;
|
||||
self->size = size;
|
||||
}
|
||||
@@ -1135,7 +1185,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
|
||||
|
||||
if(self->ring)
|
||||
{
|
||||
ReadRingBuffer(self->ring, buffer, samples);
|
||||
ll_ringbuffer_read(self->ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -1160,7 +1210,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
|
||||
}
|
||||
else
|
||||
{
|
||||
free(self->buffer);
|
||||
al_free(self->buffer);
|
||||
self->buffer = NULL;
|
||||
self->size = 0;
|
||||
}
|
||||
@@ -1238,12 +1288,15 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
|
||||
|
||||
while(avail > 0)
|
||||
{
|
||||
ll_ringbuffer_data_t vec[2];
|
||||
snd_pcm_sframes_t amt;
|
||||
|
||||
amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
|
||||
if(avail < amt) amt = avail;
|
||||
ll_ringbuffer_get_write_vector(self->ring, vec);
|
||||
if(vec[0].len == 0) break;
|
||||
|
||||
amt = snd_pcm_readi(self->pcmHandle, self->buffer, amt);
|
||||
amt = (vec[0].len < (snd_pcm_uframes_t)avail) ?
|
||||
vec[0].len : (snd_pcm_uframes_t)avail;
|
||||
amt = snd_pcm_readi(self->pcmHandle, vec[0].buf, amt);
|
||||
if(amt < 0)
|
||||
{
|
||||
ERR("read error: %s\n", snd_strerror(amt));
|
||||
@@ -1267,32 +1320,39 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
|
||||
continue;
|
||||
}
|
||||
|
||||
WriteRingBuffer(self->ring, self->buffer, amt);
|
||||
ll_ringbuffer_write_advance(self->ring, amt);
|
||||
avail -= amt;
|
||||
}
|
||||
|
||||
return RingBufferSize(self->ring);
|
||||
return ll_ringbuffer_read_space(self->ring);
|
||||
}
|
||||
|
||||
static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self)
|
||||
static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
snd_pcm_sframes_t delay = 0;
|
||||
ClockLatency ret;
|
||||
int err;
|
||||
|
||||
ALCcaptureAlsa_lock(self);
|
||||
ret.ClockTime = GetDeviceClockTime(device);
|
||||
if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
|
||||
{
|
||||
ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
|
||||
return 0;
|
||||
delay = 0;
|
||||
}
|
||||
return maxi64((ALint64)delay*1000000000/device->Frequency, 0);
|
||||
if(delay < 0) delay = 0;
|
||||
ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
|
||||
ALCcaptureAlsa_unlock(self);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const DevMap *entry)
|
||||
{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
|
||||
{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
|
||||
static inline void AppendCaptureDeviceList2(const DevMap *entry)
|
||||
{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
|
||||
{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
|
||||
|
||||
typedef struct ALCalsaBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
@@ -1352,25 +1412,15 @@ static ALCbackend* ALCalsaBackendFactory_createBackend(ALCalsaBackendFactory* UN
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCplaybackAlsa *backend;
|
||||
|
||||
backend = ALCplaybackAlsa_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCplaybackAlsa)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCplaybackAlsa_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCcaptureAlsa *backend;
|
||||
|
||||
backend = ALCcaptureAlsa_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCcaptureAlsa)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCcaptureAlsa_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
|
||||
extern inline ALuint64 GetDeviceClockTime(ALCdevice *device);
|
||||
|
||||
/* Base ALCbackend method implementations. */
|
||||
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)
|
||||
{
|
||||
int ret = almtx_init(&self->mMutex, almtx_recursive);
|
||||
assert(ret == althrd_success);
|
||||
self->mDevice = device;
|
||||
}
|
||||
|
||||
void ALCbackend_Destruct(ALCbackend *self)
|
||||
{
|
||||
almtx_destroy(&self->mMutex);
|
||||
}
|
||||
|
||||
ALCboolean ALCbackend_reset(ALCbackend* UNUSED(self))
|
||||
{
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
ALCenum ALCbackend_captureSamples(ALCbackend* UNUSED(self), void* UNUSED(buffer), ALCuint UNUSED(samples))
|
||||
{
|
||||
return ALC_INVALID_DEVICE;
|
||||
}
|
||||
|
||||
ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ClockLatency ALCbackend_getClockLatency(ALCbackend *self)
|
||||
{
|
||||
ALCdevice *device = self->mDevice;
|
||||
ALuint refcount;
|
||||
ClockLatency ret;
|
||||
|
||||
do {
|
||||
while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1))
|
||||
althrd_yield();
|
||||
ret.ClockTime = GetDeviceClockTime(device);
|
||||
ATOMIC_THREAD_FENCE(almemory_order_acquire);
|
||||
} while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed));
|
||||
|
||||
/* NOTE: The device will generally have about all but one periods filled at
|
||||
* any given time during playback. Without a more accurate measurement from
|
||||
* the output, this is an okay approximation.
|
||||
*/
|
||||
ret.Latency = device->UpdateSize * DEVICE_CLOCK_RES / device->Frequency *
|
||||
maxu(device->NumUpdates-1, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ALCbackend_lock(ALCbackend *self)
|
||||
{
|
||||
int ret = almtx_lock(&self->mMutex);
|
||||
assert(ret == althrd_success);
|
||||
}
|
||||
|
||||
void ALCbackend_unlock(ALCbackend *self)
|
||||
{
|
||||
int ret = almtx_unlock(&self->mMutex);
|
||||
assert(ret == althrd_success);
|
||||
}
|
||||
|
||||
|
||||
/* Base ALCbackendFactory method implementations. */
|
||||
void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self))
|
||||
{
|
||||
}
|
||||
+28
-6
@@ -5,6 +5,21 @@
|
||||
#include "threads.h"
|
||||
|
||||
|
||||
typedef struct ClockLatency {
|
||||
ALint64 ClockTime;
|
||||
ALint64 Latency;
|
||||
} ClockLatency;
|
||||
|
||||
/* Helper to get the current clock time from the device's ClockBase, and
|
||||
* SamplesDone converted from the sample rate.
|
||||
*/
|
||||
inline ALuint64 GetDeviceClockTime(ALCdevice *device)
|
||||
{
|
||||
return device->ClockBase + (device->SamplesDone * DEVICE_CLOCK_RES /
|
||||
device->Frequency);
|
||||
}
|
||||
|
||||
|
||||
struct ALCbackendVtable;
|
||||
|
||||
typedef struct ALCbackend {
|
||||
@@ -20,7 +35,7 @@ void ALCbackend_Destruct(ALCbackend *self);
|
||||
ALCboolean ALCbackend_reset(ALCbackend *self);
|
||||
ALCenum ALCbackend_captureSamples(ALCbackend *self, void *buffer, ALCuint samples);
|
||||
ALCuint ALCbackend_availableSamples(ALCbackend *self);
|
||||
ALint64 ALCbackend_getLatency(ALCbackend *self);
|
||||
ClockLatency ALCbackend_getClockLatency(ALCbackend *self);
|
||||
void ALCbackend_lock(ALCbackend *self);
|
||||
void ALCbackend_unlock(ALCbackend *self);
|
||||
|
||||
@@ -37,7 +52,7 @@ struct ALCbackendVtable {
|
||||
ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint);
|
||||
ALCuint (*const availableSamples)(ALCbackend*);
|
||||
|
||||
ALint64 (*const getLatency)(ALCbackend*);
|
||||
ClockLatency (*const getClockLatency)(ALCbackend*);
|
||||
|
||||
void (*const lock)(ALCbackend*);
|
||||
void (*const unlock)(ALCbackend*);
|
||||
@@ -54,7 +69,7 @@ DECLARE_THUNK(T, ALCbackend, ALCboolean, start) \
|
||||
DECLARE_THUNK(T, ALCbackend, void, stop) \
|
||||
DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint) \
|
||||
DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples) \
|
||||
DECLARE_THUNK(T, ALCbackend, ALint64, getLatency) \
|
||||
DECLARE_THUNK(T, ALCbackend, ClockLatency, getClockLatency) \
|
||||
DECLARE_THUNK(T, ALCbackend, void, lock) \
|
||||
DECLARE_THUNK(T, ALCbackend, void, unlock) \
|
||||
static void T##_ALCbackend_Delete(void *ptr) \
|
||||
@@ -70,7 +85,7 @@ static const struct ALCbackendVtable T##_ALCbackend_vtable = { \
|
||||
T##_ALCbackend_stop, \
|
||||
T##_ALCbackend_captureSamples, \
|
||||
T##_ALCbackend_availableSamples, \
|
||||
T##_ALCbackend_getLatency, \
|
||||
T##_ALCbackend_getClockLatency, \
|
||||
T##_ALCbackend_lock, \
|
||||
T##_ALCbackend_unlock, \
|
||||
\
|
||||
@@ -122,12 +137,19 @@ static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
|
||||
|
||||
ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCportBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCopenslBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
|
||||
|
||||
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type);
|
||||
|
||||
#endif /* AL_BACKENDS_BASE_H */
|
||||
+316
-195
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
AudioUnit audioUnit;
|
||||
@@ -45,23 +47,12 @@ typedef struct {
|
||||
AudioBufferList *bufferList; // Buffer for data coming from the input device
|
||||
ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
|
||||
|
||||
RingBuffer *ring;
|
||||
ll_ringbuffer_t *ring;
|
||||
} ca_data;
|
||||
|
||||
static const ALCchar ca_device[] = "CoreAudio Default";
|
||||
|
||||
|
||||
static void destroy_buffer_list(AudioBufferList* list)
|
||||
{
|
||||
if(list)
|
||||
{
|
||||
UInt32 i;
|
||||
for(i = 0;i < list->mNumberBuffers;i++)
|
||||
free(list->mBuffers[i].mData);
|
||||
free(list);
|
||||
}
|
||||
}
|
||||
|
||||
static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
|
||||
{
|
||||
AudioBufferList *list;
|
||||
@@ -83,68 +74,85 @@ static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSiz
|
||||
return list;
|
||||
}
|
||||
|
||||
static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
|
||||
static void destroy_buffer_list(AudioBufferList* list)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)inRefCon;
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
|
||||
aluMixData(device, ioData->mBuffers[0].mData,
|
||||
ioData->mBuffers[0].mDataByteSize / data->frameSize);
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets,
|
||||
AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)inUserData;
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
|
||||
// Read from the ring buffer and store temporarily in a large buffer
|
||||
ReadRingBuffer(data->ring, data->resampleBuffer, (ALsizei)(*ioNumberDataPackets));
|
||||
|
||||
// Set the input data
|
||||
ioData->mNumberBuffers = 1;
|
||||
ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
|
||||
ioData->mBuffers[0].mData = data->resampleBuffer;
|
||||
ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames, AudioBufferList *ioData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)inRefCon;
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
AudioUnitRenderActionFlags flags = 0;
|
||||
OSStatus err;
|
||||
|
||||
// fill the bufferList with data from the input device
|
||||
err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList);
|
||||
if(err != noErr)
|
||||
if(list)
|
||||
{
|
||||
ERR("AudioUnitRender error: %d\n", err);
|
||||
return err;
|
||||
UInt32 i;
|
||||
for(i = 0;i < list->mNumberBuffers;i++)
|
||||
free(list->mBuffers[i].mData);
|
||||
free(list);
|
||||
}
|
||||
}
|
||||
|
||||
WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames);
|
||||
|
||||
typedef struct ALCcoreAudioPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
AudioUnit audioUnit;
|
||||
|
||||
ALuint frameSize;
|
||||
AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
|
||||
} ALCcoreAudioPlayback;
|
||||
|
||||
static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device);
|
||||
static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self);
|
||||
static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name);
|
||||
static void ALCcoreAudioPlayback_close(ALCcoreAudioPlayback *self);
|
||||
static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self);
|
||||
static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self);
|
||||
static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self);
|
||||
static DECLARE_FORWARD2(ALCcoreAudioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioPlayback)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioPlayback);
|
||||
|
||||
|
||||
static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCcoreAudioPlayback, ALCbackend, self);
|
||||
|
||||
self->frameSize = 0;
|
||||
memset(&self->format, 0, sizeof(self->format));
|
||||
}
|
||||
|
||||
static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self)
|
||||
{
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static OSStatus ALCcoreAudioPlayback_MixerProc(void *inRefCon,
|
||||
AudioUnitRenderActionFlags* UNUSED(ioActionFlags), const AudioTimeStamp* UNUSED(inTimeStamp),
|
||||
UInt32 UNUSED(inBusNumber), UInt32 UNUSED(inNumberFrames), AudioBufferList *ioData)
|
||||
{
|
||||
ALCcoreAudioPlayback *self = inRefCon;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
ALCdevice_Lock(device);
|
||||
aluMixData(device, ioData->mBuffers[0].mData,
|
||||
ioData->mBuffers[0].mDataByteSize / self->frameSize);
|
||||
ALCdevice_Unlock(device);
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
|
||||
static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name)
|
||||
{
|
||||
ComponentDescription desc;
|
||||
Component comp;
|
||||
ca_data *data;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
AudioComponentDescription desc;
|
||||
AudioComponent comp;
|
||||
OSStatus err;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = ca_device;
|
||||
else if(strcmp(deviceName, ca_device) != 0)
|
||||
if(!name)
|
||||
name = ca_device;
|
||||
else if(strcmp(name, ca_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
/* open the default output unit */
|
||||
@@ -154,64 +162,54 @@ static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
|
||||
comp = FindNextComponent(NULL, &desc);
|
||||
comp = AudioComponentFindNext(NULL, &desc);
|
||||
if(comp == NULL)
|
||||
{
|
||||
ERR("FindNextComponent failed\n");
|
||||
ERR("AudioComponentFindNext failed\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
|
||||
err = OpenAComponent(comp, &data->audioUnit);
|
||||
err = AudioComponentInstanceNew(comp, &self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("OpenAComponent failed\n");
|
||||
free(data);
|
||||
ERR("AudioComponentInstanceNew failed\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
/* init and start the default audio unit... */
|
||||
err = AudioUnitInitialize(data->audioUnit);
|
||||
err = AudioUnitInitialize(self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitInitialize failed\n");
|
||||
CloseComponent(data->audioUnit);
|
||||
free(data);
|
||||
AudioComponentInstanceDispose(self->audioUnit);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
device->ExtraData = data;
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ca_close_playback(ALCdevice *device)
|
||||
static void ALCcoreAudioPlayback_close(ALCcoreAudioPlayback *self)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
|
||||
AudioUnitUninitialize(data->audioUnit);
|
||||
CloseComponent(data->audioUnit);
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
AudioUnitUninitialize(self->audioUnit);
|
||||
AudioComponentInstanceDispose(self->audioUnit);
|
||||
}
|
||||
|
||||
static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
AudioStreamBasicDescription streamFormat;
|
||||
AURenderCallbackStruct input;
|
||||
OSStatus err;
|
||||
UInt32 size;
|
||||
|
||||
err = AudioUnitUninitialize(data->audioUnit);
|
||||
err = AudioUnitUninitialize(self->audioUnit);
|
||||
if(err != noErr)
|
||||
ERR("-- AudioUnitUninitialize failed.\n");
|
||||
|
||||
/* retrieve default output unit's properties (output side) */
|
||||
size = sizeof(AudioStreamBasicDescription);
|
||||
err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
|
||||
err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
|
||||
if(err != noErr || size != sizeof(AudioStreamBasicDescription))
|
||||
{
|
||||
ERR("AudioUnitGetProperty failed\n");
|
||||
@@ -229,7 +227,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
#endif
|
||||
|
||||
/* set default output unit's input side to match output side */
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -238,7 +236,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
|
||||
if(device->Frequency != streamFormat.mSampleRate)
|
||||
{
|
||||
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
|
||||
device->NumUpdates = (ALuint)((ALuint64)device->NumUpdates *
|
||||
streamFormat.mSampleRate /
|
||||
device->Frequency);
|
||||
device->Frequency = streamFormat.mSampleRate;
|
||||
@@ -313,7 +311,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
|
||||
kLinearPCMFormatFlagIsPacked;
|
||||
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -321,11 +319,11 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
}
|
||||
|
||||
/* setup callback */
|
||||
data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
input.inputProc = ca_callback;
|
||||
input.inputProcRefCon = device;
|
||||
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
input.inputProc = ALCcoreAudioPlayback_MixerProc;
|
||||
input.inputProcRefCon = self;
|
||||
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -333,7 +331,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
}
|
||||
|
||||
/* init the default audio unit... */
|
||||
err = AudioUnitInitialize(data->audioUnit);
|
||||
err = AudioUnitInitialize(self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitInitialize failed\n");
|
||||
@@ -343,12 +341,9 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ca_start_playback(ALCdevice *device)
|
||||
static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
OSStatus err;
|
||||
|
||||
err = AudioOutputUnitStart(data->audioUnit);
|
||||
OSStatus err = AudioOutputUnitStart(self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioOutputUnitStart failed\n");
|
||||
@@ -358,31 +353,125 @@ static ALCboolean ca_start_playback(ALCdevice *device)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ca_stop_playback(ALCdevice *device)
|
||||
static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
OSStatus err;
|
||||
|
||||
err = AudioOutputUnitStop(data->audioUnit);
|
||||
OSStatus err = AudioOutputUnitStop(self->audioUnit);
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStop failed\n");
|
||||
}
|
||||
|
||||
static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
|
||||
|
||||
|
||||
typedef struct ALCcoreAudioCapture {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
AudioUnit audioUnit;
|
||||
|
||||
ALuint frameSize;
|
||||
ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate
|
||||
AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
|
||||
|
||||
AudioConverterRef audioConverter; // Sample rate converter if needed
|
||||
AudioBufferList *bufferList; // Buffer for data coming from the input device
|
||||
ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
|
||||
|
||||
ll_ringbuffer_t *ring;
|
||||
} ALCcoreAudioCapture;
|
||||
|
||||
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device);
|
||||
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self);
|
||||
static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name);
|
||||
static void ALCcoreAudioCapture_close(ALCcoreAudioCapture *self);
|
||||
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self);
|
||||
static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self);
|
||||
static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples);
|
||||
static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self);
|
||||
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioCapture)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioCapture);
|
||||
|
||||
|
||||
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCcoreAudioCapture, ALCbackend, self);
|
||||
|
||||
}
|
||||
|
||||
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self)
|
||||
{
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static OSStatus ALCcoreAudioCapture_RecordProc(void *inRefCon,
|
||||
AudioUnitRenderActionFlags* UNUSED(ioActionFlags),
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 UNUSED(inBusNumber),
|
||||
UInt32 inNumberFrames, AudioBufferList* UNUSED(ioData))
|
||||
{
|
||||
ALCcoreAudioCapture *self = inRefCon;
|
||||
AudioUnitRenderActionFlags flags = 0;
|
||||
OSStatus err;
|
||||
|
||||
// fill the bufferList with data from the input device
|
||||
err = AudioUnitRender(self->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, self->bufferList);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitRender error: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
ll_ringbuffer_write(self->ring, self->bufferList->mBuffers[0].mData, inNumberFrames);
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static OSStatus ALCcoreAudioCapture_ConvertCallback(AudioConverterRef UNUSED(inAudioConverter),
|
||||
UInt32 *ioNumberDataPackets, AudioBufferList *ioData,
|
||||
AudioStreamPacketDescription** UNUSED(outDataPacketDescription),
|
||||
void *inUserData)
|
||||
{
|
||||
ALCcoreAudioCapture *self = inUserData;
|
||||
|
||||
// Read from the ring buffer and store temporarily in a large buffer
|
||||
ll_ringbuffer_read(self->ring, self->resampleBuffer, *ioNumberDataPackets);
|
||||
|
||||
// Set the input data
|
||||
ioData->mNumberBuffers = 1;
|
||||
ioData->mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame;
|
||||
ioData->mBuffers[0].mData = self->resampleBuffer;
|
||||
ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * self->format.mBytesPerFrame;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
AudioStreamBasicDescription requestedFormat; // The application requested format
|
||||
AudioStreamBasicDescription hardwareFormat; // The hardware format
|
||||
AudioStreamBasicDescription outputFormat; // The AudioUnit output format
|
||||
AURenderCallbackStruct input;
|
||||
ComponentDescription desc;
|
||||
AudioComponentDescription desc;
|
||||
AudioDeviceID inputDevice;
|
||||
UInt32 outputFrameCount;
|
||||
UInt32 propertySize;
|
||||
AudioObjectPropertyAddress propertyAddress;
|
||||
UInt32 enableIO;
|
||||
Component comp;
|
||||
ca_data *data;
|
||||
AudioComponent comp;
|
||||
OSStatus err;
|
||||
|
||||
if(!name)
|
||||
name = ca_device;
|
||||
else if(strcmp(name, ca_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
@@ -390,27 +479,24 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
desc.componentFlagsMask = 0;
|
||||
|
||||
// Search for component with given description
|
||||
comp = FindNextComponent(NULL, &desc);
|
||||
comp = AudioComponentFindNext(NULL, &desc);
|
||||
if(comp == NULL)
|
||||
{
|
||||
ERR("FindNextComponent failed\n");
|
||||
ERR("AudioComponentFindNext failed\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
device->ExtraData = data;
|
||||
|
||||
// Open the component
|
||||
err = OpenAComponent(comp, &data->audioUnit);
|
||||
err = AudioComponentInstanceNew(comp, &self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("OpenAComponent failed\n");
|
||||
ERR("AudioComponentInstanceNew failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Turn off AudioUnit output
|
||||
enableIO = 0;
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -419,7 +505,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
|
||||
// Turn on AudioUnit input
|
||||
enableIO = 1;
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -427,11 +513,16 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
}
|
||||
|
||||
// Get the default input device
|
||||
|
||||
propertySize = sizeof(AudioDeviceID);
|
||||
err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propertySize, &inputDevice);
|
||||
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
||||
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||
|
||||
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &inputDevice);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioHardwareGetProperty failed\n");
|
||||
ERR("AudioObjectGetPropertyData failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -442,7 +533,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
}
|
||||
|
||||
// Track the input device
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -450,10 +541,10 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
}
|
||||
|
||||
// set capture callback
|
||||
input.inputProc = ca_capture_callback;
|
||||
input.inputProcRefCon = device;
|
||||
input.inputProc = ALCcoreAudioCapture_RecordProc;
|
||||
input.inputProcRefCon = self;
|
||||
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -461,7 +552,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
}
|
||||
|
||||
// Initialize the device
|
||||
err = AudioUnitInitialize(data->audioUnit);
|
||||
err = AudioUnitInitialize(self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitInitialize failed\n");
|
||||
@@ -470,7 +561,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
|
||||
// Get the hardware format
|
||||
propertySize = sizeof(AudioStreamBasicDescription);
|
||||
err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
|
||||
err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
|
||||
if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
|
||||
{
|
||||
ERR("AudioUnitGetProperty failed\n");
|
||||
@@ -514,9 +605,10 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
|
||||
case DevFmtQuad:
|
||||
case DevFmtX51:
|
||||
case DevFmtX51Side:
|
||||
case DevFmtX51Rear:
|
||||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
case DevFmtAmbi3D:
|
||||
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
|
||||
goto error;
|
||||
}
|
||||
@@ -529,8 +621,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
requestedFormat.mFramesPerPacket = 1;
|
||||
|
||||
// save requested format description for later use
|
||||
data->format = requestedFormat;
|
||||
data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
self->format = requestedFormat;
|
||||
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
// Use intermediate format for sample rate conversion (outputFormat)
|
||||
// Set sample rate to the same as hardware for resampling later
|
||||
@@ -538,11 +630,11 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
outputFormat.mSampleRate = hardwareFormat.mSampleRate;
|
||||
|
||||
// Determine sample rate ratio for resampling
|
||||
data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
|
||||
self->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
|
||||
|
||||
// The output format should be the requested format, but using the hardware sample rate
|
||||
// This is because the AudioUnit will automatically scale other properties, except for sample rate
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -550,8 +642,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
}
|
||||
|
||||
// Set the AudioUnit output format frame count
|
||||
outputFrameCount = device->UpdateSize * data->sampleRateRatio;
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
|
||||
outputFrameCount = device->UpdateSize * self->sampleRateRatio;
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed: %d\n", err);
|
||||
@@ -559,7 +651,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
}
|
||||
|
||||
// Set up sample converter
|
||||
err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter);
|
||||
err = AudioConverterNew(&outputFormat, &requestedFormat, &self->audioConverter);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioConverterNew failed: %d\n", err);
|
||||
@@ -567,71 +659,71 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
}
|
||||
|
||||
// Create a buffer for use in the resample callback
|
||||
data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio);
|
||||
self->resampleBuffer = malloc(device->UpdateSize * self->frameSize * self->sampleRateRatio);
|
||||
|
||||
// Allocate buffer for the AudioUnit output
|
||||
data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio);
|
||||
if(data->bufferList == NULL)
|
||||
self->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * self->frameSize * self->sampleRateRatio);
|
||||
if(self->bufferList == NULL)
|
||||
goto error;
|
||||
|
||||
data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates);
|
||||
if(data->ring == NULL)
|
||||
goto error;
|
||||
self->ring = ll_ringbuffer_create(
|
||||
device->UpdateSize*self->sampleRateRatio*device->NumUpdates + 1,
|
||||
self->frameSize
|
||||
);
|
||||
if(!self->ring) goto error;
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
error:
|
||||
DestroyRingBuffer(data->ring);
|
||||
free(data->resampleBuffer);
|
||||
destroy_buffer_list(data->bufferList);
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
free(self->resampleBuffer);
|
||||
destroy_buffer_list(self->bufferList);
|
||||
|
||||
if(data->audioConverter)
|
||||
AudioConverterDispose(data->audioConverter);
|
||||
if(data->audioUnit)
|
||||
CloseComponent(data->audioUnit);
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
if(self->audioConverter)
|
||||
AudioConverterDispose(self->audioConverter);
|
||||
if(self->audioUnit)
|
||||
AudioComponentInstanceDispose(self->audioUnit);
|
||||
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void ca_close_capture(ALCdevice *device)
|
||||
|
||||
static void ALCcoreAudioCapture_close(ALCcoreAudioCapture *self)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
|
||||
DestroyRingBuffer(data->ring);
|
||||
free(data->resampleBuffer);
|
||||
destroy_buffer_list(data->bufferList);
|
||||
free(self->resampleBuffer);
|
||||
|
||||
AudioConverterDispose(data->audioConverter);
|
||||
CloseComponent(data->audioUnit);
|
||||
destroy_buffer_list(self->bufferList);
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
AudioConverterDispose(self->audioConverter);
|
||||
AudioComponentInstanceDispose(self->audioUnit);
|
||||
}
|
||||
|
||||
static void ca_start_capture(ALCdevice *device)
|
||||
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
OSStatus err = AudioOutputUnitStart(data->audioUnit);
|
||||
OSStatus err = AudioOutputUnitStart(self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioOutputUnitStart failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ca_stop_capture(ALCdevice *device)
|
||||
static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
OSStatus err = AudioOutputUnitStop(data->audioUnit);
|
||||
OSStatus err = AudioOutputUnitStop(self->audioUnit);
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStop failed\n");
|
||||
}
|
||||
|
||||
static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
|
||||
static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
AudioBufferList *list;
|
||||
UInt32 frameCount;
|
||||
OSStatus err;
|
||||
@@ -645,14 +737,15 @@ static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint sa
|
||||
|
||||
// Point the resampling buffer to the capture buffer
|
||||
list->mNumberBuffers = 1;
|
||||
list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
|
||||
list->mBuffers[0].mDataByteSize = samples * data->frameSize;
|
||||
list->mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame;
|
||||
list->mBuffers[0].mDataByteSize = samples * self->frameSize;
|
||||
list->mBuffers[0].mData = buffer;
|
||||
|
||||
// Resample into another AudioBufferList
|
||||
frameCount = samples;
|
||||
err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback,
|
||||
device, &frameCount, list, NULL);
|
||||
err = AudioConverterFillComplexBuffer(self->audioConverter,
|
||||
ALCcoreAudioCapture_ConvertCallback, self, &frameCount, list, NULL
|
||||
);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioConverterFillComplexBuffer error: %d\n", err);
|
||||
@@ -661,39 +754,47 @@ static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint sa
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCuint ca_available_samples(ALCdevice *device)
|
||||
static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self)
|
||||
{
|
||||
ca_data *data = device->ExtraData;
|
||||
return RingBufferSize(data->ring) / data->sampleRateRatio;
|
||||
return ll_ringbuffer_read_space(self->ring) / self->sampleRateRatio;
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs ca_funcs = {
|
||||
ca_open_playback,
|
||||
ca_close_playback,
|
||||
ca_reset_playback,
|
||||
ca_start_playback,
|
||||
ca_stop_playback,
|
||||
ca_open_capture,
|
||||
ca_close_capture,
|
||||
ca_start_capture,
|
||||
ca_stop_capture,
|
||||
ca_capture_samples,
|
||||
ca_available_samples,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
typedef struct ALCcoreAudioBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCcoreAudioBackendFactory;
|
||||
#define ALCCOREAUDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCcoreAudioBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCboolean alc_ca_init(BackendFuncs *func_list)
|
||||
ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCcoreAudioBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCcoreAudioBackendFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCcoreAudioBackendFactory factory = ALCCOREAUDIOBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory* UNUSED(self))
|
||||
{
|
||||
*func_list = ca_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_ca_deinit(void)
|
||||
static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback || ALCbackend_Capture)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
void alc_ca_probe(enum DevProbe type)
|
||||
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
@@ -705,3 +806,23 @@ void alc_ca_probe(enum DevProbe type)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCcoreAudioPlayback *backend;
|
||||
NEW_OBJ(backend, ALCcoreAudioPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCcoreAudioCapture *backend;
|
||||
NEW_OBJ(backend, ALCcoreAudioCapture)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
+150
-144
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
#ifndef DSSPEAKER_5POINT1
|
||||
# define DSSPEAKER_5POINT1 0x00000006
|
||||
#endif
|
||||
#ifndef DSSPEAKER_5POINT1_BACK
|
||||
# define DSSPEAKER_5POINT1_BACK 0x00000006
|
||||
#endif
|
||||
#ifndef DSSPEAKER_7POINT1
|
||||
# define DSSPEAKER_7POINT1 0x00000007
|
||||
#endif
|
||||
@@ -57,6 +60,8 @@
|
||||
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
|
||||
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
|
||||
|
||||
#define DEVNAME_HEAD "OpenAL Soft on "
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
static void *ds_handle;
|
||||
@@ -118,15 +123,14 @@ static void clear_devlist(vector_DevMap *list)
|
||||
{
|
||||
#define DEINIT_STR(i) AL_STRING_DEINIT((i)->name)
|
||||
VECTOR_FOR_EACH(DevMap, *list, DEINIT_STR);
|
||||
VECTOR_RESIZE(*list, 0, 0);
|
||||
#undef DEINIT_STR
|
||||
VECTOR_RESIZE(*list, 0);
|
||||
}
|
||||
|
||||
static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR* UNUSED(drvname), void *data)
|
||||
{
|
||||
vector_DevMap *devices = data;
|
||||
OLECHAR *guidstr = NULL;
|
||||
DevMap *iter, *end;
|
||||
DevMap entry;
|
||||
HRESULT hr;
|
||||
int count;
|
||||
@@ -137,30 +141,31 @@ static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHA
|
||||
AL_STRING_INIT(entry.name);
|
||||
|
||||
count = 0;
|
||||
do {
|
||||
al_string_copy_wcstr(&entry.name, desc);
|
||||
while(1)
|
||||
{
|
||||
const DevMap *iter;
|
||||
|
||||
alstr_copy_cstr(&entry.name, DEVNAME_HEAD);
|
||||
alstr_append_wcstr(&entry.name, desc);
|
||||
if(count != 0)
|
||||
{
|
||||
char str[64];
|
||||
snprintf(str, sizeof(str), " #%d", count+1);
|
||||
al_string_append_cstr(&entry.name, str);
|
||||
alstr_append_cstr(&entry.name, str);
|
||||
}
|
||||
count++;
|
||||
|
||||
iter = VECTOR_ITER_BEGIN(*devices);
|
||||
end = VECTOR_ITER_END(*devices);
|
||||
for(;iter != end;++iter)
|
||||
{
|
||||
if(al_string_cmp(entry.name, iter->name) == 0)
|
||||
break;
|
||||
}
|
||||
} while(iter != end);
|
||||
#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, *devices, MATCH_ENTRY);
|
||||
if(iter == VECTOR_END(*devices)) break;
|
||||
#undef MATCH_ENTRY
|
||||
count++;
|
||||
}
|
||||
entry.guid = *guid;
|
||||
|
||||
hr = StringFromCLSID(guid, &guidstr);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
TRACE("Got device \"%s\", GUID \"%ls\"\n", al_string_get_cstr(entry.name), guidstr);
|
||||
TRACE("Got device \"%s\", GUID \"%ls\"\n", alstr_get_cstr(entry.name), guidstr);
|
||||
CoTaskMemFree(guidstr);
|
||||
}
|
||||
|
||||
@@ -194,7 +199,7 @@ static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self);
|
||||
static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self);
|
||||
static DECLARE_FORWARD2(ALCdsoundPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback)
|
||||
@@ -239,7 +244,7 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
FragSize = device->UpdateSize * FrameSize;
|
||||
|
||||
IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &LastCursor, NULL);
|
||||
@@ -294,8 +299,10 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
|
||||
if(SUCCEEDED(err))
|
||||
{
|
||||
// If we have an active context, mix data directly into output buffer otherwise fill with silence
|
||||
ALCdevice_Lock(device);
|
||||
aluMixData(device, WritePtr1, WriteCnt1/FrameSize);
|
||||
aluMixData(device, WritePtr2, WriteCnt2/FrameSize);
|
||||
ALCdevice_Unlock(device);
|
||||
|
||||
// Unlock output buffer only when successfully locked
|
||||
IDirectSoundBuffer_Unlock(self->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
|
||||
@@ -336,23 +343,23 @@ static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *de
|
||||
|
||||
if(!deviceName && VECTOR_SIZE(PlaybackDevices) > 0)
|
||||
{
|
||||
deviceName = al_string_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
|
||||
deviceName = alstr_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
|
||||
guid = &VECTOR_FRONT(PlaybackDevices).guid;
|
||||
}
|
||||
else
|
||||
{
|
||||
const DevMap *iter;
|
||||
|
||||
#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
|
||||
#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
|
||||
#undef MATCH_NAME
|
||||
if(iter == VECTOR_ITER_END(PlaybackDevices))
|
||||
if(iter == VECTOR_END(PlaybackDevices))
|
||||
return ALC_INVALID_VALUE;
|
||||
guid = &iter->guid;
|
||||
}
|
||||
|
||||
hr = DS_OK;
|
||||
self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||
if(self->NotifyEvent == NULL)
|
||||
hr = E_FAIL;
|
||||
|
||||
@@ -374,7 +381,7 @@ static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *de
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
alstr_copy_cstr(&device->DeviceName, deviceName);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
@@ -441,28 +448,35 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
|
||||
hr = IDirectSound_GetSpeakerConfig(self->DS, &speakers);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
speakers = DSSPEAKER_CONFIG(speakers);
|
||||
if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
|
||||
{
|
||||
speakers = DSSPEAKER_CONFIG(speakers);
|
||||
if(speakers == DSSPEAKER_MONO)
|
||||
device->FmtChans = DevFmtMono;
|
||||
else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
else if(speakers == DSSPEAKER_QUAD)
|
||||
device->FmtChans = DevFmtQuad;
|
||||
else if(speakers == DSSPEAKER_5POINT1 || speakers == DSSPEAKER_5POINT1_SURROUND)
|
||||
else if(speakers == DSSPEAKER_5POINT1_SURROUND)
|
||||
device->FmtChans = DevFmtX51;
|
||||
else if(speakers == DSSPEAKER_5POINT1_BACK)
|
||||
device->FmtChans = DevFmtX51Rear;
|
||||
else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
|
||||
device->FmtChans = DevFmtX71;
|
||||
else
|
||||
ERR("Unknown system speaker config: 0x%lx\n", speakers);
|
||||
}
|
||||
device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
|
||||
speakers == DSSPEAKER_HEADPHONE);
|
||||
|
||||
switch(device->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
|
||||
break;
|
||||
case DevFmtAmbi3D:
|
||||
device->FmtChans = DevFmtStereo;
|
||||
/*fall-through*/
|
||||
case DevFmtStereo:
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT;
|
||||
@@ -478,16 +492,16 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
break;
|
||||
case DevFmtX51Side:
|
||||
case DevFmtX51Rear:
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
case DevFmtX61:
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
@@ -513,7 +527,7 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
|
||||
retry_open:
|
||||
hr = S_OK;
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
|
||||
OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
|
||||
OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
|
||||
OutputType.Format.nSamplesPerSec = device->Frequency;
|
||||
@@ -641,7 +655,8 @@ typedef struct ALCdsoundCapture {
|
||||
IDirectSoundCaptureBuffer *DSCbuffer;
|
||||
DWORD BufferBytes;
|
||||
DWORD Cursor;
|
||||
RingBuffer *Ring;
|
||||
|
||||
ll_ringbuffer_t *Ring;
|
||||
} ALCdsoundCapture;
|
||||
|
||||
static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device);
|
||||
@@ -653,7 +668,7 @@ static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self);
|
||||
static void ALCdsoundCapture_stop(ALCdsoundCapture *self);
|
||||
static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples);
|
||||
static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self);
|
||||
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture)
|
||||
@@ -689,17 +704,17 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
|
||||
|
||||
if(!deviceName && VECTOR_SIZE(CaptureDevices) > 0)
|
||||
{
|
||||
deviceName = al_string_get_cstr(VECTOR_FRONT(CaptureDevices).name);
|
||||
deviceName = alstr_get_cstr(VECTOR_FRONT(CaptureDevices).name);
|
||||
guid = &VECTOR_FRONT(CaptureDevices).guid;
|
||||
}
|
||||
else
|
||||
{
|
||||
const DevMap *iter;
|
||||
|
||||
#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
|
||||
#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
|
||||
#undef MATCH_NAME
|
||||
if(iter == VECTOR_ITER_END(CaptureDevices))
|
||||
if(iter == VECTOR_END(CaptureDevices))
|
||||
return ALC_INVALID_VALUE;
|
||||
guid = &iter->guid;
|
||||
}
|
||||
@@ -719,97 +734,98 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&InputType, 0, sizeof(InputType));
|
||||
switch(device->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
|
||||
break;
|
||||
case DevFmtStereo:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT;
|
||||
break;
|
||||
case DevFmtQuad:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
case DevFmtX51:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
break;
|
||||
case DevFmtX51Rear:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
case DevFmtX61:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_CENTER |
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
break;
|
||||
case DevFmtX71:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT |
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
break;
|
||||
case DevFmtAmbi3D:
|
||||
WARN("%s capture not supported\n", DevFmtChannelsString(device->FmtChans));
|
||||
return ALC_INVALID_ENUM;
|
||||
}
|
||||
|
||||
InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
|
||||
InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
|
||||
InputType.Format.nSamplesPerSec = device->Frequency;
|
||||
InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
|
||||
InputType.Format.cbSize = 0;
|
||||
InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
|
||||
if(device->FmtType == DevFmtFloat)
|
||||
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
else
|
||||
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
|
||||
if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
|
||||
{
|
||||
InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
}
|
||||
|
||||
samples = device->UpdateSize * device->NumUpdates;
|
||||
samples = maxu(samples, 100 * device->Frequency / 1000);
|
||||
|
||||
memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
|
||||
DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
|
||||
DSCBDescription.dwFlags = 0;
|
||||
DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
|
||||
DSCBDescription.lpwfxFormat = &InputType.Format;
|
||||
|
||||
//DirectSoundCapture Init code
|
||||
hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
memset(&InputType, 0, sizeof(InputType));
|
||||
|
||||
switch(device->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
|
||||
break;
|
||||
case DevFmtStereo:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT;
|
||||
break;
|
||||
case DevFmtQuad:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
case DevFmtX51:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
case DevFmtX51Side:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
break;
|
||||
case DevFmtX61:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_CENTER |
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
break;
|
||||
case DevFmtX71:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT |
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
break;
|
||||
}
|
||||
|
||||
InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
|
||||
InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
|
||||
InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
|
||||
InputType.Format.nSamplesPerSec = device->Frequency;
|
||||
InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
|
||||
InputType.Format.cbSize = 0;
|
||||
|
||||
if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
|
||||
{
|
||||
InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
|
||||
if(device->FmtType == DevFmtFloat)
|
||||
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
else
|
||||
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
}
|
||||
|
||||
samples = device->UpdateSize * device->NumUpdates;
|
||||
samples = maxu(samples, 100 * device->Frequency / 1000);
|
||||
|
||||
memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
|
||||
DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
|
||||
DSCBDescription.dwFlags = 0;
|
||||
DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
|
||||
DSCBDescription.lpwfxFormat = &InputType.Format;
|
||||
|
||||
hr = IDirectSoundCapture_CreateCaptureBuffer(self->DSC, &DSCBDescription, &self->DSCbuffer, NULL);
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
self->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates);
|
||||
self->Ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1,
|
||||
InputType.Format.nBlockAlign);
|
||||
if(self->Ring == NULL)
|
||||
hr = DSERR_OUTOFMEMORY;
|
||||
}
|
||||
@@ -818,7 +834,7 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
|
||||
{
|
||||
ERR("Device init failed: 0x%08lx\n", hr);
|
||||
|
||||
DestroyRingBuffer(self->Ring);
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = NULL;
|
||||
if(self->DSCbuffer != NULL)
|
||||
IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
|
||||
@@ -833,14 +849,14 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
|
||||
self->BufferBytes = DSCBDescription.dwBufferBytes;
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
alstr_copy_cstr(&device->DeviceName, deviceName);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCdsoundCapture_close(ALCdsoundCapture *self)
|
||||
{
|
||||
DestroyRingBuffer(self->Ring);
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = NULL;
|
||||
|
||||
if(self->DSCbuffer != NULL)
|
||||
@@ -883,7 +899,7 @@ static void ALCdsoundCapture_stop(ALCdsoundCapture *self)
|
||||
|
||||
static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
ReadRingBuffer(self->Ring, buffer, samples);
|
||||
ll_ringbuffer_read(self->Ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -899,7 +915,7 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
|
||||
if(!device->Connected)
|
||||
goto done;
|
||||
|
||||
FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
BufferBytes = self->BufferBytes;
|
||||
LastCursor = self->Cursor;
|
||||
|
||||
@@ -915,9 +931,9 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
WriteRingBuffer(self->Ring, ReadPtr1, ReadCnt1/FrameSize);
|
||||
ll_ringbuffer_write(self->Ring, ReadPtr1, ReadCnt1/FrameSize);
|
||||
if(ReadPtr2 != NULL)
|
||||
WriteRingBuffer(self->Ring, ReadPtr2, ReadCnt2/FrameSize);
|
||||
ll_ringbuffer_write(self->Ring, ReadPtr2, ReadCnt2/FrameSize);
|
||||
hr = IDirectSoundCaptureBuffer_Unlock(self->DSCbuffer,
|
||||
ReadPtr1, ReadCnt1,
|
||||
ReadPtr2, ReadCnt2);
|
||||
@@ -931,14 +947,14 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
|
||||
}
|
||||
|
||||
done:
|
||||
return RingBufferSize(self->Ring);
|
||||
return ll_ringbuffer_read_space(self->Ring);
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const DevMap *entry)
|
||||
{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
|
||||
{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
|
||||
static inline void AppendCaptureDeviceList2(const DevMap *entry)
|
||||
{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
|
||||
{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
|
||||
|
||||
typedef struct ALCdsoundBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
@@ -1027,26 +1043,16 @@ static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCdsoundPlayback *backend;
|
||||
|
||||
backend = ALCdsoundPlayback_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCdsoundPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCdsoundPlayback_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCdsoundCapture *backend;
|
||||
|
||||
backend = ALCdsoundCapture_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCdsoundCapture)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCdsoundCapture_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,640 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#include <jack/jack.h>
|
||||
#include <jack/ringbuffer.h>
|
||||
|
||||
|
||||
static const ALCchar jackDevice[] = "JACK Default";
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
#define JACK_FUNCS(MAGIC) \
|
||||
MAGIC(jack_client_open); \
|
||||
MAGIC(jack_client_close); \
|
||||
MAGIC(jack_client_name_size); \
|
||||
MAGIC(jack_get_client_name); \
|
||||
MAGIC(jack_connect); \
|
||||
MAGIC(jack_activate); \
|
||||
MAGIC(jack_deactivate); \
|
||||
MAGIC(jack_port_register); \
|
||||
MAGIC(jack_port_unregister); \
|
||||
MAGIC(jack_port_get_buffer); \
|
||||
MAGIC(jack_port_name); \
|
||||
MAGIC(jack_get_ports); \
|
||||
MAGIC(jack_free); \
|
||||
MAGIC(jack_get_sample_rate); \
|
||||
MAGIC(jack_set_error_function); \
|
||||
MAGIC(jack_set_process_callback); \
|
||||
MAGIC(jack_set_buffer_size_callback); \
|
||||
MAGIC(jack_set_buffer_size); \
|
||||
MAGIC(jack_get_buffer_size);
|
||||
|
||||
static void *jack_handle;
|
||||
#define MAKE_FUNC(f) static __typeof(f) * p##f
|
||||
JACK_FUNCS(MAKE_FUNC);
|
||||
static __typeof(jack_error_callback) * pjack_error_callback;
|
||||
#undef MAKE_FUNC
|
||||
|
||||
#define jack_client_open pjack_client_open
|
||||
#define jack_client_close pjack_client_close
|
||||
#define jack_client_name_size pjack_client_name_size
|
||||
#define jack_get_client_name pjack_get_client_name
|
||||
#define jack_connect pjack_connect
|
||||
#define jack_activate pjack_activate
|
||||
#define jack_deactivate pjack_deactivate
|
||||
#define jack_port_register pjack_port_register
|
||||
#define jack_port_unregister pjack_port_unregister
|
||||
#define jack_port_get_buffer pjack_port_get_buffer
|
||||
#define jack_port_name pjack_port_name
|
||||
#define jack_get_ports pjack_get_ports
|
||||
#define jack_free pjack_free
|
||||
#define jack_get_sample_rate pjack_get_sample_rate
|
||||
#define jack_set_error_function pjack_set_error_function
|
||||
#define jack_set_process_callback pjack_set_process_callback
|
||||
#define jack_set_buffer_size_callback pjack_set_buffer_size_callback
|
||||
#define jack_set_buffer_size pjack_set_buffer_size
|
||||
#define jack_get_buffer_size pjack_get_buffer_size
|
||||
#define jack_error_callback (*pjack_error_callback)
|
||||
#endif
|
||||
|
||||
|
||||
static jack_options_t ClientOptions = JackNullOption;
|
||||
|
||||
static ALCboolean jack_load(void)
|
||||
{
|
||||
ALCboolean error = ALC_FALSE;
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(!jack_handle)
|
||||
{
|
||||
al_string missing_funcs = AL_STRING_INIT_STATIC();
|
||||
|
||||
#ifdef _WIN32
|
||||
#define JACKLIB "libjack.dll"
|
||||
#else
|
||||
#define JACKLIB "libjack.so.0"
|
||||
#endif
|
||||
jack_handle = LoadLib(JACKLIB);
|
||||
if(!jack_handle)
|
||||
{
|
||||
WARN("Failed to load %s\n", JACKLIB);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
error = ALC_FALSE;
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = GetSymbol(jack_handle, #f); \
|
||||
if(p##f == NULL) { \
|
||||
error = ALC_TRUE; \
|
||||
alstr_append_cstr(&missing_funcs, "\n" #f); \
|
||||
} \
|
||||
} while(0)
|
||||
JACK_FUNCS(LOAD_FUNC);
|
||||
#undef LOAD_FUNC
|
||||
/* Optional symbols. These don't exist in all versions of JACK. */
|
||||
#define LOAD_SYM(f) p##f = GetSymbol(jack_handle, #f)
|
||||
LOAD_SYM(jack_error_callback);
|
||||
#undef LOAD_SYM
|
||||
|
||||
if(error)
|
||||
{
|
||||
WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
|
||||
CloseLib(jack_handle);
|
||||
jack_handle = NULL;
|
||||
}
|
||||
alstr_reset(&missing_funcs);
|
||||
}
|
||||
#endif
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCjackPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
jack_client_t *Client;
|
||||
jack_port_t *Port[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
ll_ringbuffer_t *Ring;
|
||||
alcnd_t Cond;
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} ALCjackPlayback;
|
||||
|
||||
static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg);
|
||||
|
||||
static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg);
|
||||
static int ALCjackPlayback_mixerProc(void *arg);
|
||||
|
||||
static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device);
|
||||
static void ALCjackPlayback_Destruct(ALCjackPlayback *self);
|
||||
static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name);
|
||||
static void ALCjackPlayback_close(ALCjackPlayback *self);
|
||||
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self);
|
||||
static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self);
|
||||
static void ALCjackPlayback_stop(ALCjackPlayback *self);
|
||||
static DECLARE_FORWARD2(ALCjackPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self);
|
||||
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCjackPlayback)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCjackPlayback);
|
||||
|
||||
|
||||
static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCjackPlayback, ALCbackend, self);
|
||||
|
||||
alcnd_init(&self->Cond);
|
||||
|
||||
self->Client = NULL;
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
self->Port[i] = NULL;
|
||||
self->Ring = NULL;
|
||||
|
||||
self->killNow = 1;
|
||||
}
|
||||
|
||||
static void ALCjackPlayback_Destruct(ALCjackPlayback *self)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
if(self->Client)
|
||||
{
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
{
|
||||
if(self->Port[i])
|
||||
jack_port_unregister(self->Client, self->Port[i]);
|
||||
self->Port[i] = NULL;
|
||||
}
|
||||
jack_client_close(self->Client);
|
||||
self->Client = NULL;
|
||||
}
|
||||
|
||||
alcnd_destroy(&self->Cond);
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg)
|
||||
{
|
||||
ALCjackPlayback *self = arg;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
ALuint bufsize;
|
||||
|
||||
ALCjackPlayback_lock(self);
|
||||
device->UpdateSize = numframes;
|
||||
device->NumUpdates = 2;
|
||||
|
||||
bufsize = device->UpdateSize;
|
||||
if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
|
||||
bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
|
||||
bufsize += device->UpdateSize;
|
||||
device->NumUpdates = bufsize / device->UpdateSize;
|
||||
|
||||
TRACE("%u update size x%u\n", device->UpdateSize, device->NumUpdates);
|
||||
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = ll_ringbuffer_create(bufsize,
|
||||
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
|
||||
);
|
||||
if(!self->Ring)
|
||||
{
|
||||
ERR("Failed to reallocate ringbuffer\n");
|
||||
aluHandleDisconnect(device);
|
||||
}
|
||||
ALCjackPlayback_unlock(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
|
||||
{
|
||||
ALCjackPlayback *self = arg;
|
||||
jack_default_audio_sample_t *out[MAX_OUTPUT_CHANNELS];
|
||||
ll_ringbuffer_data_t data[2];
|
||||
jack_nframes_t total = 0;
|
||||
jack_nframes_t todo;
|
||||
ALsizei i, c, numchans;
|
||||
|
||||
ll_ringbuffer_get_read_vector(self->Ring, data);
|
||||
|
||||
for(c = 0;c < MAX_OUTPUT_CHANNELS && self->Port[c];c++)
|
||||
out[c] = jack_port_get_buffer(self->Port[c], numframes);
|
||||
numchans = c;
|
||||
|
||||
todo = minu(numframes, data[0].len);
|
||||
for(c = 0;c < numchans;c++)
|
||||
{
|
||||
const ALfloat *restrict in = ((ALfloat*)data[0].buf) + c;
|
||||
for(i = 0;(jack_nframes_t)i < todo;i++)
|
||||
out[c][i] = in[i*numchans];
|
||||
out[c] += todo;
|
||||
}
|
||||
total += todo;
|
||||
|
||||
todo = minu(numframes-total, data[1].len);
|
||||
if(todo > 0)
|
||||
{
|
||||
for(c = 0;c < numchans;c++)
|
||||
{
|
||||
const ALfloat *restrict in = ((ALfloat*)data[1].buf) + c;
|
||||
for(i = 0;(jack_nframes_t)i < todo;i++)
|
||||
out[c][i] = in[i*numchans];
|
||||
out[c] += todo;
|
||||
}
|
||||
total += todo;
|
||||
}
|
||||
|
||||
ll_ringbuffer_read_advance(self->Ring, total);
|
||||
alcnd_signal(&self->Cond);
|
||||
|
||||
if(numframes > total)
|
||||
{
|
||||
todo = numframes-total;
|
||||
for(c = 0;c < numchans;c++)
|
||||
{
|
||||
for(i = 0;(jack_nframes_t)i < todo;i++)
|
||||
out[c][i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ALCjackPlayback_mixerProc(void *arg)
|
||||
{
|
||||
ALCjackPlayback *self = arg;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
ll_ringbuffer_data_t data[2];
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
ALCjackPlayback_lock(self);
|
||||
while(!self->killNow && device->Connected)
|
||||
{
|
||||
ALuint todo, len1, len2;
|
||||
|
||||
/* NOTE: Unfortunately, there is an unavoidable race condition here.
|
||||
* It's possible for the process() method to run, updating the read
|
||||
* pointer and signaling the condition variable, in between the mixer
|
||||
* loop checking the write size and waiting for the condition variable.
|
||||
* This will cause the mixer loop to wait until the *next* process()
|
||||
* invocation, most likely writing silence for it.
|
||||
*
|
||||
* However, this should only happen if the mixer is running behind
|
||||
* anyway (as ideally we'll be asleep in alcnd_wait by the time the
|
||||
* process() method is invoked), so this behavior is not unwarranted.
|
||||
* It's unfortunate since it'll be wasting time sleeping that could be
|
||||
* used to catch up, but there's no way around it without blocking in
|
||||
* the process() method.
|
||||
*/
|
||||
if(ll_ringbuffer_write_space(self->Ring) < device->UpdateSize)
|
||||
{
|
||||
alcnd_wait(&self->Cond, &STATIC_CAST(ALCbackend,self)->mMutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
ll_ringbuffer_get_write_vector(self->Ring, data);
|
||||
todo = data[0].len + data[1].len;
|
||||
todo -= todo%device->UpdateSize;
|
||||
|
||||
len1 = minu(data[0].len, todo);
|
||||
len2 = minu(data[1].len, todo-len1);
|
||||
|
||||
aluMixData(device, data[0].buf, len1);
|
||||
if(len2 > 0)
|
||||
aluMixData(device, data[1].buf, len2);
|
||||
ll_ringbuffer_write_advance(self->Ring, todo);
|
||||
}
|
||||
ALCjackPlayback_unlock(self);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
const char *client_name = "alsoft";
|
||||
jack_status_t status;
|
||||
|
||||
if(!name)
|
||||
name = jackDevice;
|
||||
else if(strcmp(name, jackDevice) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
self->Client = jack_client_open(client_name, ClientOptions, &status, NULL);
|
||||
if(self->Client == NULL)
|
||||
{
|
||||
ERR("jack_client_open() failed, status = 0x%02x\n", status);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
if((status&JackServerStarted))
|
||||
TRACE("JACK server started\n");
|
||||
if((status&JackNameNotUnique))
|
||||
{
|
||||
client_name = jack_get_client_name(self->Client);
|
||||
TRACE("Client name not unique, got `%s' instead\n", client_name);
|
||||
}
|
||||
|
||||
jack_set_process_callback(self->Client, ALCjackPlayback_process, self);
|
||||
jack_set_buffer_size_callback(self->Client, ALCjackPlayback_bufferSizeNotify, self);
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCjackPlayback_close(ALCjackPlayback *self)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
{
|
||||
if(self->Port[i])
|
||||
jack_port_unregister(self->Client, self->Port[i]);
|
||||
self->Port[i] = NULL;
|
||||
}
|
||||
jack_client_close(self->Client);
|
||||
self->Client = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALsizei numchans, i;
|
||||
ALuint bufsize;
|
||||
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
{
|
||||
if(self->Port[i])
|
||||
jack_port_unregister(self->Client, self->Port[i]);
|
||||
self->Port[i] = NULL;
|
||||
}
|
||||
|
||||
/* Ignore the requested buffer metrics and just keep one JACK-sized buffer
|
||||
* ready for when requested. Note that one period's worth of audio in the
|
||||
* ring buffer will always be left unfilled because one element of the ring
|
||||
* buffer will not be writeable, and we only write in period-sized chunks.
|
||||
*/
|
||||
device->Frequency = jack_get_sample_rate(self->Client);
|
||||
device->UpdateSize = jack_get_buffer_size(self->Client);
|
||||
device->NumUpdates = 2;
|
||||
|
||||
bufsize = device->UpdateSize;
|
||||
if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
|
||||
bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
|
||||
bufsize += device->UpdateSize;
|
||||
device->NumUpdates = bufsize / device->UpdateSize;
|
||||
|
||||
/* Force 32-bit float output. */
|
||||
device->FmtType = DevFmtFloat;
|
||||
|
||||
numchans = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
for(i = 0;i < numchans;i++)
|
||||
{
|
||||
char name[64];
|
||||
snprintf(name, sizeof(name), "channel_%d", i+1);
|
||||
self->Port[i] = jack_port_register(self->Client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
||||
if(self->Port[i] == NULL)
|
||||
{
|
||||
ERR("Not enough JACK ports available for %s output\n", DevFmtChannelsString(device->FmtChans));
|
||||
if(i == 0) return ALC_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i < numchans)
|
||||
{
|
||||
if(i == 1)
|
||||
device->FmtChans = DevFmtMono;
|
||||
else
|
||||
{
|
||||
for(--i;i >= 2;i--)
|
||||
{
|
||||
jack_port_unregister(self->Client, self->Port[i]);
|
||||
self->Port[i] = NULL;
|
||||
}
|
||||
device->FmtChans = DevFmtStereo;
|
||||
}
|
||||
}
|
||||
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = ll_ringbuffer_create(bufsize,
|
||||
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
|
||||
);
|
||||
if(!self->Ring)
|
||||
{
|
||||
ERR("Failed to allocate ringbuffer\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
|
||||
{
|
||||
const char **ports;
|
||||
ALsizei i;
|
||||
|
||||
if(jack_activate(self->Client))
|
||||
{
|
||||
ERR("Failed to activate client\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
ports = jack_get_ports(self->Client, NULL, NULL, JackPortIsPhysical|JackPortIsInput);
|
||||
if(ports == NULL)
|
||||
{
|
||||
ERR("No physical playback ports found\n");
|
||||
jack_deactivate(self->Client);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS && self->Port[i];i++)
|
||||
{
|
||||
if(!ports[i])
|
||||
{
|
||||
ERR("No physical playback port for \"%s\"\n", jack_port_name(self->Port[i]));
|
||||
break;
|
||||
}
|
||||
if(jack_connect(self->Client, jack_port_name(self->Port[i]), ports[i]))
|
||||
ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(self->Port[i]), ports[i]);
|
||||
}
|
||||
jack_free(ports);
|
||||
|
||||
self->killNow = 0;
|
||||
if(althrd_create(&self->thread, ALCjackPlayback_mixerProc, self) != althrd_success)
|
||||
{
|
||||
jack_deactivate(self->Client);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCjackPlayback_stop(ALCjackPlayback *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if(self->killNow)
|
||||
return;
|
||||
|
||||
self->killNow = 1;
|
||||
/* Lock the backend to ensure we don't flag the mixer to die and signal the
|
||||
* mixer to wake up in between it checking the flag and going to sleep and
|
||||
* wait for a wakeup (potentially leading to it never waking back up to see
|
||||
* the flag). */
|
||||
ALCjackPlayback_lock(self);
|
||||
ALCjackPlayback_unlock(self);
|
||||
alcnd_signal(&self->Cond);
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
jack_deactivate(self->Client);
|
||||
}
|
||||
|
||||
|
||||
static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ClockLatency ret;
|
||||
|
||||
ALCjackPlayback_lock(self);
|
||||
ret.ClockTime = GetDeviceClockTime(device);
|
||||
ret.Latency = ll_ringbuffer_read_space(self->Ring) * DEVICE_CLOCK_RES /
|
||||
device->Frequency;
|
||||
ALCjackPlayback_unlock(self);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void jack_msg_handler(const char *message)
|
||||
{
|
||||
WARN("%s\n", message);
|
||||
}
|
||||
|
||||
typedef struct ALCjackBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCjackBackendFactory;
|
||||
#define ALCJACKBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCjackBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self))
|
||||
{
|
||||
void (*old_error_cb)(const char*);
|
||||
jack_client_t *client;
|
||||
jack_status_t status;
|
||||
|
||||
if(!jack_load())
|
||||
return ALC_FALSE;
|
||||
|
||||
if(!GetConfigValueBool(NULL, "jack", "spawn-server", 0))
|
||||
ClientOptions |= JackNoStartServer;
|
||||
|
||||
old_error_cb = (&jack_error_callback ? jack_error_callback : NULL);
|
||||
jack_set_error_function(jack_msg_handler);
|
||||
client = jack_client_open("alsoft", ClientOptions, &status, NULL);
|
||||
jack_set_error_function(old_error_cb);
|
||||
if(client == NULL)
|
||||
{
|
||||
WARN("jack_client_open() failed, 0x%02x\n", status);
|
||||
if((status&JackServerFailed) && !(ClientOptions&JackNoStartServer))
|
||||
ERR("Unable to connect to JACK server\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
jack_client_close(client);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCjackBackendFactory_deinit(ALCjackBackendFactory* UNUSED(self))
|
||||
{
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(jack_handle)
|
||||
CloseLib(jack_handle);
|
||||
jack_handle = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static ALCboolean ALCjackBackendFactory_querySupport(ALCjackBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(jackDevice);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCjackBackendFactory_createBackend(ALCjackBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCjackPlayback *backend;
|
||||
NEW_OBJ(backend, ALCjackPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCjackBackendFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCjackBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCjackBackendFactory factory = ALCJACKBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
+5
-10
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -41,7 +41,7 @@ static ALCboolean ALCloopback_start(ALCloopback *self);
|
||||
static void ALCloopback_stop(ALCloopback *self);
|
||||
static DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCloopback)
|
||||
@@ -59,7 +59,7 @@ static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -124,13 +124,8 @@ static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory* UNUSED(s
|
||||
if(type == ALCbackend_Loopback)
|
||||
{
|
||||
ALCloopback *backend;
|
||||
|
||||
backend = ALCloopback_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCloopback)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCloopback_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
+968
-91
File diff suppressed because it is too large
Load Diff
+8
-11
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -51,7 +51,7 @@ static ALCboolean ALCnullBackend_start(ALCnullBackend *self);
|
||||
static void ALCnullBackend_stop(ALCnullBackend *self);
|
||||
static DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
|
||||
@@ -106,10 +106,12 @@ static int ALCnullBackend_mixerProc(void *ptr)
|
||||
}
|
||||
|
||||
if(avail-done < device->UpdateSize)
|
||||
al_nssleep(0, restTime);
|
||||
al_nssleep(restTime);
|
||||
else while(avail-done >= device->UpdateSize)
|
||||
{
|
||||
ALCnullBackend_lock(self);
|
||||
aluMixData(device, NULL, device->UpdateSize);
|
||||
ALCnullBackend_unlock(self);
|
||||
done += device->UpdateSize;
|
||||
}
|
||||
}
|
||||
@@ -128,7 +130,7 @@ static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
@@ -214,13 +216,8 @@ static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory* UN
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCnullBackend *backend;
|
||||
|
||||
backend = ALCnullBackend_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCnullBackend)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCnullBackend_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+375
-139
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -22,10 +22,12 @@
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <memory.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
@@ -51,11 +53,176 @@
|
||||
#define SOUND_MIXER_WRITE MIXER_WRITE
|
||||
#endif
|
||||
|
||||
#if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
|
||||
#define ALC_OSS_COMPAT
|
||||
#endif
|
||||
#ifndef SNDCTL_AUDIOINFO
|
||||
#define ALC_OSS_COMPAT
|
||||
#endif
|
||||
|
||||
static const ALCchar oss_device[] = "OSS Default";
|
||||
/*
|
||||
* FreeBSD strongly discourages the use of specific devices,
|
||||
* such as those returned in oss_audioinfo.devnode
|
||||
*/
|
||||
#ifdef __FreeBSD__
|
||||
#define ALC_OSS_DEVNODE_TRUC
|
||||
#endif
|
||||
|
||||
static const char *oss_driver = "/dev/dsp";
|
||||
static const char *oss_capture = "/dev/dsp";
|
||||
struct oss_device {
|
||||
const ALCchar *handle;
|
||||
const char *path;
|
||||
struct oss_device *next;
|
||||
};
|
||||
|
||||
static struct oss_device oss_playback = {
|
||||
"OSS Default",
|
||||
"/dev/dsp",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct oss_device oss_capture = {
|
||||
"OSS Default",
|
||||
"/dev/dsp",
|
||||
NULL
|
||||
};
|
||||
|
||||
#ifdef ALC_OSS_COMPAT
|
||||
|
||||
#define DSP_CAP_OUTPUT 0x00020000
|
||||
#define DSP_CAP_INPUT 0x00010000
|
||||
static void ALCossListPopulate(struct oss_device *UNUSED(devlist), int UNUSED(type_flag))
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifndef HAVE_STRNLEN
|
||||
static size_t strnlen(const char *str, size_t maxlen)
|
||||
{
|
||||
const char *end = memchr(str, 0, maxlen);
|
||||
if(!end) return maxlen;
|
||||
return end - str;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ALCossListAppend(struct oss_device *list, const char *handle, size_t hlen, const char *path, size_t plen)
|
||||
{
|
||||
struct oss_device *next;
|
||||
struct oss_device *last;
|
||||
size_t i;
|
||||
|
||||
/* skip the first item "OSS Default" */
|
||||
last = list;
|
||||
next = list->next;
|
||||
#ifdef ALC_OSS_DEVNODE_TRUC
|
||||
for(i = 0;i < plen;i++)
|
||||
{
|
||||
if(path[i] == '.')
|
||||
{
|
||||
if(strncmp(path + i, handle + hlen + i - plen, plen - i) == 0)
|
||||
hlen = hlen + i - plen;
|
||||
plen = i;
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)i;
|
||||
#endif
|
||||
if(handle[0] == '\0')
|
||||
{
|
||||
handle = path;
|
||||
hlen = plen;
|
||||
}
|
||||
|
||||
while(next != NULL)
|
||||
{
|
||||
if(strncmp(next->path, path, plen) == 0)
|
||||
return;
|
||||
last = next;
|
||||
next = next->next;
|
||||
}
|
||||
|
||||
next = (struct oss_device*)malloc(sizeof(struct oss_device) + hlen + plen + 2);
|
||||
next->handle = (char*)(next + 1);
|
||||
next->path = next->handle + hlen + 1;
|
||||
next->next = NULL;
|
||||
last->next = next;
|
||||
|
||||
strncpy((char*)next->handle, handle, hlen);
|
||||
((char*)next->handle)[hlen] = '\0';
|
||||
strncpy((char*)next->path, path, plen);
|
||||
((char*)next->path)[plen] = '\0';
|
||||
|
||||
TRACE("Got device \"%s\", \"%s\"\n", next->handle, next->path);
|
||||
}
|
||||
|
||||
static void ALCossListPopulate(struct oss_device *devlist, int type_flag)
|
||||
{
|
||||
struct oss_sysinfo si;
|
||||
struct oss_audioinfo ai;
|
||||
int fd, i;
|
||||
|
||||
if((fd=open("/dev/mixer", O_RDONLY)) < 0)
|
||||
{
|
||||
TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
|
||||
{
|
||||
TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
for(i = 0;i < si.numaudios;i++)
|
||||
{
|
||||
const char *handle;
|
||||
size_t len;
|
||||
|
||||
ai.dev = i;
|
||||
if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
|
||||
{
|
||||
ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
if(ai.devnode[0] == '\0')
|
||||
continue;
|
||||
|
||||
if(ai.handle[0] != '\0')
|
||||
{
|
||||
len = strnlen(ai.handle, sizeof(ai.handle));
|
||||
handle = ai.handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = strnlen(ai.name, sizeof(ai.name));
|
||||
handle = ai.name;
|
||||
}
|
||||
if((ai.caps&type_flag))
|
||||
ALCossListAppend(devlist, handle, len, ai.devnode,
|
||||
strnlen(ai.devnode, sizeof(ai.devnode)));
|
||||
}
|
||||
|
||||
done:
|
||||
close(fd);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void ALCossListFree(struct oss_device *list)
|
||||
{
|
||||
struct oss_device *cur;
|
||||
if(list == NULL)
|
||||
return;
|
||||
|
||||
/* skip the first item "OSS Default" */
|
||||
cur = list->next;
|
||||
list->next = NULL;
|
||||
|
||||
while(cur != NULL)
|
||||
{
|
||||
struct oss_device *next = cur->next;
|
||||
free(cur);
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
static int log2i(ALCuint x)
|
||||
{
|
||||
@@ -68,7 +235,6 @@ static int log2i(ALCuint x)
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCplaybackOSS {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
@@ -77,7 +243,7 @@ typedef struct ALCplaybackOSS {
|
||||
ALubyte *mix_data;
|
||||
int data_size;
|
||||
|
||||
volatile int killNow;
|
||||
ATOMIC(ALenum) killNow;
|
||||
althrd_t thread;
|
||||
} ALCplaybackOSS;
|
||||
|
||||
@@ -92,7 +258,7 @@ static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self);
|
||||
static void ALCplaybackOSS_stop(ALCplaybackOSS *self);
|
||||
static DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
|
||||
@@ -103,42 +269,64 @@ static int ALCplaybackOSS_mixerProc(void *ptr)
|
||||
{
|
||||
ALCplaybackOSS *self = (ALCplaybackOSS*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALint frameSize;
|
||||
struct timeval timeout;
|
||||
ALubyte *write_ptr;
|
||||
ALint frame_size;
|
||||
ALint to_write;
|
||||
ssize_t wrote;
|
||||
fd_set wfds;
|
||||
int sret;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
while(!self->killNow && device->Connected)
|
||||
ALCplaybackOSS_lock(self);
|
||||
while(!ATOMIC_LOAD_SEQ(&self->killNow) && device->Connected)
|
||||
{
|
||||
ALint len = self->data_size;
|
||||
ALubyte *WritePtr = self->mix_data;
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(self->fd, &wfds);
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
aluMixData(device, WritePtr, len/frameSize);
|
||||
while(len > 0 && !self->killNow)
|
||||
ALCplaybackOSS_unlock(self);
|
||||
sret = select(self->fd+1, NULL, &wfds, NULL, &timeout);
|
||||
ALCplaybackOSS_lock(self);
|
||||
if(sret < 0)
|
||||
{
|
||||
wrote = write(self->fd, WritePtr, len);
|
||||
if(errno == EINTR)
|
||||
continue;
|
||||
ERR("select failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
break;
|
||||
}
|
||||
else if(sret == 0)
|
||||
{
|
||||
WARN("select timeout\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
write_ptr = self->mix_data;
|
||||
to_write = self->data_size;
|
||||
aluMixData(device, write_ptr, to_write/frame_size);
|
||||
while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow))
|
||||
{
|
||||
wrote = write(self->fd, write_ptr, to_write);
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
|
||||
{
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
ALCplaybackOSS_lock(self);
|
||||
aluHandleDisconnect(device);
|
||||
ALCplaybackOSS_unlock(self);
|
||||
break;
|
||||
}
|
||||
|
||||
al_nssleep(0, 1000000);
|
||||
continue;
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||
continue;
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
break;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
to_write -= wrote;
|
||||
write_ptr += wrote;
|
||||
}
|
||||
}
|
||||
ALCplaybackOSS_unlock(self);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -148,27 +336,45 @@ static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
|
||||
|
||||
ATOMIC_INIT(&self->killNow, AL_FALSE);
|
||||
}
|
||||
|
||||
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
|
||||
{
|
||||
struct oss_device *dev = &oss_playback;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
if(!name)
|
||||
name = oss_device;
|
||||
else if(strcmp(name, oss_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
if(!name || strcmp(name, dev->handle) == 0)
|
||||
name = dev->handle;
|
||||
else
|
||||
{
|
||||
if(!dev->next)
|
||||
{
|
||||
ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
|
||||
dev = &oss_playback;
|
||||
}
|
||||
while(dev != NULL)
|
||||
{
|
||||
if (strcmp(dev->handle, name) == 0)
|
||||
break;
|
||||
dev = dev->next;
|
||||
}
|
||||
if(dev == NULL)
|
||||
{
|
||||
WARN("Could not find \"%s\" in device list\n", name);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
self->killNow = 0;
|
||||
|
||||
self->fd = open(oss_driver, O_WRONLY);
|
||||
self->fd = open(dev->path, O_WRONLY);
|
||||
if(self->fd == -1)
|
||||
{
|
||||
ERR("Could not open %s: %s\n", oss_driver, strerror(errno));
|
||||
ERR("Could not open %s: %s\n", dev->path, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
@@ -212,18 +418,11 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
|
||||
}
|
||||
|
||||
periods = device->NumUpdates;
|
||||
numChannels = ChannelsFromDevFmt(device->FmtChans);
|
||||
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
|
||||
|
||||
numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
ossSpeed = device->Frequency;
|
||||
log2FragmentSize = log2i(device->UpdateSize * frameSize);
|
||||
|
||||
/* according to the OSS spec, 16 bytes are the minimum */
|
||||
if (log2FragmentSize < 4)
|
||||
log2FragmentSize = 4;
|
||||
/* Subtract one period since the temp mixing buffer counts as one. Still
|
||||
* need at least two on the card, though. */
|
||||
if(periods > 2) periods--;
|
||||
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
|
||||
/* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
|
||||
log2FragmentSize = maxi(log2i(device->UpdateSize*frameSize), 4);
|
||||
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
|
||||
|
||||
#define CHECKERR(func) if((func) < 0) { \
|
||||
@@ -245,7 +444,7 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
|
||||
}
|
||||
#undef CHECKERR
|
||||
|
||||
if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
|
||||
if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels)
|
||||
{
|
||||
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
|
||||
return ALC_FALSE;
|
||||
@@ -261,7 +460,7 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
|
||||
|
||||
device->Frequency = ossSpeed;
|
||||
device->UpdateSize = info.fragsize / frameSize;
|
||||
device->NumUpdates = info.fragments + 1;
|
||||
device->NumUpdates = info.fragments;
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
@@ -272,10 +471,12 @@ static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
|
||||
device->FmtChans, device->FmtType, device->AmbiOrder
|
||||
);
|
||||
self->mix_data = calloc(1, self->data_size);
|
||||
|
||||
self->killNow = 0;
|
||||
ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
|
||||
if(althrd_create(&self->thread, ALCplaybackOSS_mixerProc, self) != althrd_success)
|
||||
{
|
||||
free(self->mix_data);
|
||||
@@ -290,10 +491,8 @@ static void ALCplaybackOSS_stop(ALCplaybackOSS *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if(self->killNow)
|
||||
if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
|
||||
return;
|
||||
|
||||
self->killNow = 1;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
|
||||
@@ -309,13 +508,9 @@ typedef struct ALCcaptureOSS {
|
||||
|
||||
int fd;
|
||||
|
||||
ALubyte *read_data;
|
||||
int data_size;
|
||||
ll_ringbuffer_t *ring;
|
||||
|
||||
RingBuffer *ring;
|
||||
int doCapture;
|
||||
|
||||
volatile int killNow;
|
||||
ATOMIC(ALenum) killNow;
|
||||
althrd_t thread;
|
||||
} ALCcaptureOSS;
|
||||
|
||||
@@ -330,7 +525,7 @@ static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self);
|
||||
static void ALCcaptureOSS_stop(ALCcaptureOSS *self);
|
||||
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples);
|
||||
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self);
|
||||
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
|
||||
@@ -341,32 +536,55 @@ static int ALCcaptureOSS_recordProc(void *ptr)
|
||||
{
|
||||
ALCcaptureOSS *self = (ALCcaptureOSS*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
int frameSize;
|
||||
int amt;
|
||||
struct timeval timeout;
|
||||
int frame_size;
|
||||
fd_set rfds;
|
||||
ssize_t amt;
|
||||
int sret;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), "alsoft-record");
|
||||
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
while(!self->killNow)
|
||||
while(!ATOMIC_LOAD_SEQ(&self->killNow))
|
||||
{
|
||||
amt = read(self->fd, self->read_data, self->data_size);
|
||||
if(amt < 0)
|
||||
ll_ringbuffer_data_t vec[2];
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(self->fd, &rfds);
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
sret = select(self->fd+1, &rfds, NULL, NULL, &timeout);
|
||||
if(sret < 0)
|
||||
{
|
||||
ERR("read failed: %s\n", strerror(errno));
|
||||
ALCcaptureOSS_lock(self);
|
||||
if(errno == EINTR)
|
||||
continue;
|
||||
ERR("select failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
ALCcaptureOSS_unlock(self);
|
||||
break;
|
||||
}
|
||||
if(amt == 0)
|
||||
else if(sret == 0)
|
||||
{
|
||||
al_nssleep(0, 1000000);
|
||||
WARN("select timeout\n");
|
||||
continue;
|
||||
}
|
||||
if(self->doCapture)
|
||||
WriteRingBuffer(self->ring, self->read_data, amt/frameSize);
|
||||
|
||||
ll_ringbuffer_get_write_vector(self->ring, vec);
|
||||
if(vec[0].len > 0)
|
||||
{
|
||||
amt = read(self->fd, vec[0].buf, vec[0].len*frame_size);
|
||||
if(amt < 0)
|
||||
{
|
||||
ERR("read failed: %s\n", strerror(errno));
|
||||
ALCcaptureOSS_lock(self);
|
||||
aluHandleDisconnect(device);
|
||||
ALCcaptureOSS_unlock(self);
|
||||
break;
|
||||
}
|
||||
ll_ringbuffer_write_advance(self->ring, amt/frame_size);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -377,11 +595,14 @@ static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
|
||||
|
||||
ATOMIC_INIT(&self->killNow, AL_FALSE);
|
||||
}
|
||||
|
||||
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
struct oss_device *dev = &oss_capture;
|
||||
int numFragmentsLogSize;
|
||||
int log2FragmentSize;
|
||||
unsigned int periods;
|
||||
@@ -392,15 +613,32 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
|
||||
int ossSpeed;
|
||||
char *err;
|
||||
|
||||
if(!name)
|
||||
name = oss_device;
|
||||
else if(strcmp(name, oss_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
if(!name || strcmp(name, dev->handle) == 0)
|
||||
name = dev->handle;
|
||||
else
|
||||
{
|
||||
if(!dev->next)
|
||||
{
|
||||
ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
|
||||
dev = &oss_capture;
|
||||
}
|
||||
while(dev != NULL)
|
||||
{
|
||||
if (strcmp(dev->handle, name) == 0)
|
||||
break;
|
||||
dev = dev->next;
|
||||
}
|
||||
if(dev == NULL)
|
||||
{
|
||||
WARN("Could not find \"%s\" in device list\n", name);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
self->fd = open(oss_capture, O_RDONLY);
|
||||
self->fd = open(dev->path, O_RDONLY);
|
||||
if(self->fd == -1)
|
||||
{
|
||||
ERR("Could not open %s: %s\n", oss_capture, strerror(errno));
|
||||
ERR("Could not open %s: %s\n", dev->path, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
@@ -424,7 +662,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
|
||||
}
|
||||
|
||||
periods = 4;
|
||||
numChannels = ChannelsFromDevFmt(device->FmtChans);
|
||||
numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
|
||||
ossSpeed = device->Frequency;
|
||||
log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates *
|
||||
@@ -454,7 +692,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
|
||||
}
|
||||
#undef CHECKERR
|
||||
|
||||
if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
|
||||
if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels)
|
||||
{
|
||||
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
|
||||
close(self->fd);
|
||||
@@ -472,7 +710,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
self->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
|
||||
self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1, frameSize);
|
||||
if(!self->ring)
|
||||
{
|
||||
ERR("Ring buffer create failed\n");
|
||||
@@ -481,60 +719,50 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
self->data_size = info.fragsize;
|
||||
self->read_data = calloc(1, self->data_size);
|
||||
|
||||
self->killNow = 0;
|
||||
if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success)
|
||||
{
|
||||
device->ExtraData = NULL;
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCcaptureOSS_close(ALCcaptureOSS *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
self->killNow = 1;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
|
||||
DestroyRingBuffer(self->ring);
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
|
||||
free(self->read_data);
|
||||
self->read_data = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
|
||||
{
|
||||
self->doCapture = 1;
|
||||
ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
|
||||
if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCcaptureOSS_stop(ALCcaptureOSS *self)
|
||||
{
|
||||
self->doCapture = 0;
|
||||
int res;
|
||||
|
||||
if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
|
||||
return;
|
||||
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
|
||||
ERR("Error resetting device: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
ReadRingBuffer(self->ring, buffer, samples);
|
||||
ll_ringbuffer_read(self->ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
|
||||
{
|
||||
return RingBufferSize(self->ring);
|
||||
return ll_ringbuffer_read_space(self->ring);
|
||||
}
|
||||
|
||||
|
||||
@@ -546,7 +774,7 @@ typedef struct ALCossBackendFactory {
|
||||
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCossBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static void ALCossBackendFactory_deinit(ALCossBackendFactory *self);
|
||||
static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
@@ -562,12 +790,19 @@ ALCbackendFactory *ALCossBackendFactory_getFactory(void)
|
||||
|
||||
ALCboolean ALCossBackendFactory_init(ALCossBackendFactory* UNUSED(self))
|
||||
{
|
||||
ConfigValueStr("oss", "device", &oss_driver);
|
||||
ConfigValueStr("oss", "capture", &oss_capture);
|
||||
ConfigValueStr(NULL, "oss", "device", &oss_playback.path);
|
||||
ConfigValueStr(NULL, "oss", "capture", &oss_capture.path);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void ALCossBackendFactory_deinit(ALCossBackendFactory* UNUSED(self))
|
||||
{
|
||||
ALCossListFree(&oss_playback);
|
||||
ALCossListFree(&oss_capture);
|
||||
}
|
||||
|
||||
|
||||
ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
|
||||
@@ -577,27 +812,38 @@ ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self),
|
||||
|
||||
void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
struct oss_device *cur;
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
{
|
||||
ALCossListFree(&oss_playback);
|
||||
ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
|
||||
cur = &oss_playback;
|
||||
while(cur != NULL)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(oss_driver, &buf) == 0)
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
#endif
|
||||
AppendAllDevicesList(oss_device);
|
||||
}
|
||||
break;
|
||||
AppendAllDevicesList(cur->handle);
|
||||
cur = cur->next;
|
||||
}
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
{
|
||||
ALCossListFree(&oss_capture);
|
||||
ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
|
||||
cur = &oss_capture;
|
||||
while(cur != NULL)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(oss_capture, &buf) == 0)
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
#endif
|
||||
AppendCaptureDeviceList(oss_device);
|
||||
}
|
||||
break;
|
||||
AppendCaptureDeviceList(cur->handle);
|
||||
cur = cur->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -606,25 +852,15 @@ ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCplaybackOSS *backend;
|
||||
|
||||
backend = ALCplaybackOSS_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCplaybackOSS)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCplaybackOSS_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCcaptureOSS *backend;
|
||||
|
||||
backend = ALCcaptureOSS_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCcaptureOSS)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCcaptureOSS_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,575 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#include <portaudio.h>
|
||||
|
||||
|
||||
static const ALCchar pa_device[] = "PortAudio Default";
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
static void *pa_handle;
|
||||
#define MAKE_FUNC(x) static __typeof(x) * p##x
|
||||
MAKE_FUNC(Pa_Initialize);
|
||||
MAKE_FUNC(Pa_Terminate);
|
||||
MAKE_FUNC(Pa_GetErrorText);
|
||||
MAKE_FUNC(Pa_StartStream);
|
||||
MAKE_FUNC(Pa_StopStream);
|
||||
MAKE_FUNC(Pa_OpenStream);
|
||||
MAKE_FUNC(Pa_CloseStream);
|
||||
MAKE_FUNC(Pa_GetDefaultOutputDevice);
|
||||
MAKE_FUNC(Pa_GetDefaultInputDevice);
|
||||
MAKE_FUNC(Pa_GetStreamInfo);
|
||||
#undef MAKE_FUNC
|
||||
|
||||
#define Pa_Initialize pPa_Initialize
|
||||
#define Pa_Terminate pPa_Terminate
|
||||
#define Pa_GetErrorText pPa_GetErrorText
|
||||
#define Pa_StartStream pPa_StartStream
|
||||
#define Pa_StopStream pPa_StopStream
|
||||
#define Pa_OpenStream pPa_OpenStream
|
||||
#define Pa_CloseStream pPa_CloseStream
|
||||
#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
|
||||
#define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
|
||||
#define Pa_GetStreamInfo pPa_GetStreamInfo
|
||||
#endif
|
||||
|
||||
static ALCboolean pa_load(void)
|
||||
{
|
||||
PaError err;
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(!pa_handle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
# define PALIB "portaudio.dll"
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
# define PALIB "libportaudio.2.dylib"
|
||||
#elif defined(__OpenBSD__)
|
||||
# define PALIB "libportaudio.so"
|
||||
#else
|
||||
# define PALIB "libportaudio.so.2"
|
||||
#endif
|
||||
|
||||
pa_handle = LoadLib(PALIB);
|
||||
if(!pa_handle)
|
||||
return ALC_FALSE;
|
||||
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = GetSymbol(pa_handle, #f); \
|
||||
if(p##f == NULL) \
|
||||
{ \
|
||||
CloseLib(pa_handle); \
|
||||
pa_handle = NULL; \
|
||||
return ALC_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
LOAD_FUNC(Pa_Initialize);
|
||||
LOAD_FUNC(Pa_Terminate);
|
||||
LOAD_FUNC(Pa_GetErrorText);
|
||||
LOAD_FUNC(Pa_StartStream);
|
||||
LOAD_FUNC(Pa_StopStream);
|
||||
LOAD_FUNC(Pa_OpenStream);
|
||||
LOAD_FUNC(Pa_CloseStream);
|
||||
LOAD_FUNC(Pa_GetDefaultOutputDevice);
|
||||
LOAD_FUNC(Pa_GetDefaultInputDevice);
|
||||
LOAD_FUNC(Pa_GetStreamInfo);
|
||||
#undef LOAD_FUNC
|
||||
|
||||
if((err=Pa_Initialize()) != paNoError)
|
||||
{
|
||||
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
CloseLib(pa_handle);
|
||||
pa_handle = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if((err=Pa_Initialize()) != paNoError)
|
||||
{
|
||||
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
#endif
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCportPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
PaStream *stream;
|
||||
PaStreamParameters params;
|
||||
ALuint update_size;
|
||||
} ALCportPlayback;
|
||||
|
||||
static int ALCportPlayback_WriteCallback(const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||
const PaStreamCallbackFlags statusFlags, void *userData);
|
||||
|
||||
static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device);
|
||||
static void ALCportPlayback_Destruct(ALCportPlayback *self);
|
||||
static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name);
|
||||
static void ALCportPlayback_close(ALCportPlayback *self);
|
||||
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self);
|
||||
static ALCboolean ALCportPlayback_start(ALCportPlayback *self);
|
||||
static void ALCportPlayback_stop(ALCportPlayback *self);
|
||||
static DECLARE_FORWARD2(ALCportPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCportPlayback);
|
||||
|
||||
|
||||
static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCportPlayback, ALCbackend, self);
|
||||
|
||||
self->stream = NULL;
|
||||
}
|
||||
|
||||
static void ALCportPlayback_Destruct(ALCportPlayback *self)
|
||||
{
|
||||
if(self->stream)
|
||||
Pa_CloseStream(self->stream);
|
||||
self->stream = NULL;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer), void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
|
||||
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
|
||||
{
|
||||
ALCportPlayback *self = userData;
|
||||
|
||||
ALCportPlayback_lock(self);
|
||||
aluMixData(STATIC_CAST(ALCbackend, self)->mDevice, outputBuffer, framesPerBuffer);
|
||||
ALCportPlayback_unlock(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
PaError err;
|
||||
|
||||
if(!name)
|
||||
name = pa_device;
|
||||
else if(strcmp(name, pa_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
self->update_size = device->UpdateSize;
|
||||
|
||||
self->params.device = -1;
|
||||
if(!ConfigValueInt(NULL, "port", "device", &self->params.device) ||
|
||||
self->params.device < 0)
|
||||
self->params.device = Pa_GetDefaultOutputDevice();
|
||||
self->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
|
||||
(float)device->Frequency;
|
||||
self->params.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
self->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
self->params.sampleFormat = paInt8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
self->params.sampleFormat = paUInt8;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
self->params.sampleFormat = paInt16;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
/* fall-through */
|
||||
case DevFmtInt:
|
||||
self->params.sampleFormat = paInt32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
self->params.sampleFormat = paFloat32;
|
||||
break;
|
||||
}
|
||||
|
||||
retry_open:
|
||||
err = Pa_OpenStream(&self->stream, NULL, &self->params,
|
||||
device->Frequency, device->UpdateSize, paNoFlag,
|
||||
ALCportPlayback_WriteCallback, self
|
||||
);
|
||||
if(err != paNoError)
|
||||
{
|
||||
if(self->params.sampleFormat == paFloat32)
|
||||
{
|
||||
self->params.sampleFormat = paInt16;
|
||||
goto retry_open;
|
||||
}
|
||||
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
}
|
||||
|
||||
static void ALCportPlayback_close(ALCportPlayback *self)
|
||||
{
|
||||
PaError err = Pa_CloseStream(self->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
self->stream = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
const PaStreamInfo *streamInfo;
|
||||
|
||||
streamInfo = Pa_GetStreamInfo(self->stream);
|
||||
device->Frequency = streamInfo->sampleRate;
|
||||
device->UpdateSize = self->update_size;
|
||||
|
||||
if(self->params.sampleFormat == paInt8)
|
||||
device->FmtType = DevFmtByte;
|
||||
else if(self->params.sampleFormat == paUInt8)
|
||||
device->FmtType = DevFmtUByte;
|
||||
else if(self->params.sampleFormat == paInt16)
|
||||
device->FmtType = DevFmtShort;
|
||||
else if(self->params.sampleFormat == paInt32)
|
||||
device->FmtType = DevFmtInt;
|
||||
else if(self->params.sampleFormat == paFloat32)
|
||||
device->FmtType = DevFmtFloat;
|
||||
else
|
||||
{
|
||||
ERR("Unexpected sample format: 0x%lx\n", self->params.sampleFormat);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(self->params.channelCount == 2)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
else if(self->params.channelCount == 1)
|
||||
device->FmtChans = DevFmtMono;
|
||||
else
|
||||
{
|
||||
ERR("Unexpected channel count: %u\n", self->params.channelCount);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCportPlayback_start(ALCportPlayback *self)
|
||||
{
|
||||
PaError err;
|
||||
|
||||
err = Pa_StartStream(self->stream);
|
||||
if(err != paNoError)
|
||||
{
|
||||
ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCportPlayback_stop(ALCportPlayback *self)
|
||||
{
|
||||
PaError err = Pa_StopStream(self->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCportCapture {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
PaStream *stream;
|
||||
PaStreamParameters params;
|
||||
|
||||
ll_ringbuffer_t *ring;
|
||||
} ALCportCapture;
|
||||
|
||||
static int ALCportCapture_ReadCallback(const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||
const PaStreamCallbackFlags statusFlags, void *userData);
|
||||
|
||||
static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device);
|
||||
static void ALCportCapture_Destruct(ALCportCapture *self);
|
||||
static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name);
|
||||
static void ALCportCapture_close(ALCportCapture *self);
|
||||
static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean ALCportCapture_start(ALCportCapture *self);
|
||||
static void ALCportCapture_stop(ALCportCapture *self);
|
||||
static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples);
|
||||
static ALCuint ALCportCapture_availableSamples(ALCportCapture *self);
|
||||
static DECLARE_FORWARD(ALCportCapture, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCportCapture)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCportCapture);
|
||||
|
||||
|
||||
static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCportCapture, ALCbackend, self);
|
||||
|
||||
self->stream = NULL;
|
||||
}
|
||||
|
||||
static void ALCportCapture_Destruct(ALCportCapture *self)
|
||||
{
|
||||
if(self->stream)
|
||||
Pa_CloseStream(self->stream);
|
||||
self->stream = NULL;
|
||||
|
||||
if(self->ring)
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static int ALCportCapture_ReadCallback(const void *inputBuffer, void *UNUSED(outputBuffer),
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
|
||||
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
|
||||
{
|
||||
ALCportCapture *self = userData;
|
||||
size_t writable = ll_ringbuffer_write_space(self->ring);
|
||||
|
||||
if(framesPerBuffer > writable)
|
||||
framesPerBuffer = writable;
|
||||
ll_ringbuffer_write(self->ring, inputBuffer, framesPerBuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALuint samples, frame_size;
|
||||
PaError err;
|
||||
|
||||
if(!name)
|
||||
name = pa_device;
|
||||
else if(strcmp(name, pa_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
samples = device->UpdateSize * device->NumUpdates;
|
||||
samples = maxu(samples, 100 * device->Frequency / 1000);
|
||||
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
self->ring = ll_ringbuffer_create(samples, frame_size);
|
||||
if(self->ring == NULL) return ALC_INVALID_VALUE;
|
||||
|
||||
self->params.device = -1;
|
||||
if(!ConfigValueInt(NULL, "port", "capture", &self->params.device) ||
|
||||
self->params.device < 0)
|
||||
self->params.device = Pa_GetDefaultInputDevice();
|
||||
self->params.suggestedLatency = 0.0f;
|
||||
self->params.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
self->params.sampleFormat = paInt8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
self->params.sampleFormat = paUInt8;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
self->params.sampleFormat = paInt16;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
self->params.sampleFormat = paInt32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
self->params.sampleFormat = paFloat32;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
case DevFmtUShort:
|
||||
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
self->params.channelCount = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
|
||||
err = Pa_OpenStream(&self->stream, &self->params, NULL,
|
||||
device->Frequency, paFramesPerBufferUnspecified, paNoFlag,
|
||||
ALCportCapture_ReadCallback, self
|
||||
);
|
||||
if(err != paNoError)
|
||||
{
|
||||
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCportCapture_close(ALCportCapture *self)
|
||||
{
|
||||
PaError err = Pa_CloseStream(self->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
self->stream = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCportCapture_start(ALCportCapture *self)
|
||||
{
|
||||
PaError err = Pa_StartStream(self->stream);
|
||||
if(err != paNoError)
|
||||
{
|
||||
ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCportCapture_stop(ALCportCapture *self)
|
||||
{
|
||||
PaError err = Pa_StopStream(self->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
|
||||
static ALCuint ALCportCapture_availableSamples(ALCportCapture *self)
|
||||
{
|
||||
return ll_ringbuffer_read_space(self->ring);
|
||||
}
|
||||
|
||||
static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
ll_ringbuffer_read(self->ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCportBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCportBackendFactory;
|
||||
#define ALCPORTBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCportBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory *self);
|
||||
static void ALCportBackendFactory_deinit(ALCportBackendFactory *self);
|
||||
static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCportBackendFactory);
|
||||
|
||||
|
||||
static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory* UNUSED(self))
|
||||
{
|
||||
if(!pa_load())
|
||||
return ALC_FALSE;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCportBackendFactory_deinit(ALCportBackendFactory* UNUSED(self))
|
||||
{
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(pa_handle)
|
||||
{
|
||||
Pa_Terminate();
|
||||
CloseLib(pa_handle);
|
||||
pa_handle = NULL;
|
||||
}
|
||||
#else
|
||||
Pa_Terminate();
|
||||
#endif
|
||||
}
|
||||
|
||||
static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(pa_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
AppendCaptureDeviceList(pa_device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCportPlayback *backend;
|
||||
NEW_OBJ(backend, ALCportPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCportCapture *backend;
|
||||
NEW_OBJ(backend, ALCportCapture)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALCbackendFactory *ALCportBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCportBackendFactory factory = ALCPORTBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
+349
-209
File diff suppressed because it is too large
Load Diff
+356
-448
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,346 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#include <sndio.h>
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct ALCsndioBackend {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
struct sio_hdl *sndHandle;
|
||||
|
||||
ALvoid *mix_data;
|
||||
ALsizei data_size;
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} ALCsndioBackend;
|
||||
|
||||
static int ALCsndioBackend_mixerProc(void *ptr);
|
||||
|
||||
static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device);
|
||||
static void ALCsndioBackend_Destruct(ALCsndioBackend *self);
|
||||
static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name);
|
||||
static void ALCsndioBackend_close(ALCsndioBackend *self);
|
||||
static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self);
|
||||
static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self);
|
||||
static void ALCsndioBackend_stop(ALCsndioBackend *self);
|
||||
static DECLARE_FORWARD2(ALCsndioBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCsndioBackend)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCsndioBackend);
|
||||
|
||||
|
||||
static const ALCchar sndio_device[] = "SndIO Default";
|
||||
|
||||
|
||||
static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCsndioBackend, ALCbackend, self);
|
||||
}
|
||||
|
||||
static void ALCsndioBackend_Destruct(ALCsndioBackend *self)
|
||||
{
|
||||
if(self->sndHandle)
|
||||
sio_close(self->sndHandle);
|
||||
self->sndHandle = NULL;
|
||||
|
||||
al_free(self->mix_data);
|
||||
self->mix_data = NULL;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static int ALCsndioBackend_mixerProc(void *ptr)
|
||||
{
|
||||
ALCsndioBackend *self = (ALCsndioBackend*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALsizei frameSize;
|
||||
size_t wrote;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
while(!self->killNow && device->Connected)
|
||||
{
|
||||
ALsizei len = self->data_size;
|
||||
ALubyte *WritePtr = self->mix_data;
|
||||
|
||||
ALCsndioBackend_lock(self);
|
||||
aluMixData(device, WritePtr, len/frameSize);
|
||||
ALCsndioBackend_unlock(self);
|
||||
while(len > 0 && !self->killNow)
|
||||
{
|
||||
wrote = sio_write(self->sndHandle, WritePtr, len);
|
||||
if(wrote == 0)
|
||||
{
|
||||
ERR("sio_write failed\n");
|
||||
ALCdevice_Lock(device);
|
||||
aluHandleDisconnect(device);
|
||||
ALCdevice_Unlock(device);
|
||||
break;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
if(!name)
|
||||
name = sndio_device;
|
||||
else if(strcmp(name, sndio_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
self->sndHandle = sio_open(NULL, SIO_PLAY, 0);
|
||||
if(self->sndHandle == NULL)
|
||||
{
|
||||
ERR("Could not open device\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCsndioBackend_close(ALCsndioBackend *self)
|
||||
{
|
||||
sio_close(self->sndHandle);
|
||||
self->sndHandle = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
struct sio_par par;
|
||||
|
||||
sio_initpar(&par);
|
||||
|
||||
par.rate = device->Frequency;
|
||||
par.pchan = ((device->FmtChans != DevFmtMono) ? 2 : 1);
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
par.bits = 8;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
par.bits = 8;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
case DevFmtShort:
|
||||
par.bits = 16;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
par.bits = 16;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
par.bits = 32;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
par.bits = 32;
|
||||
par.sig = 0;
|
||||
break;
|
||||
}
|
||||
par.le = SIO_LE_NATIVE;
|
||||
|
||||
par.round = device->UpdateSize;
|
||||
par.appbufsz = device->UpdateSize * (device->NumUpdates-1);
|
||||
if(!par.appbufsz) par.appbufsz = device->UpdateSize;
|
||||
|
||||
if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
|
||||
{
|
||||
ERR("Failed to set device parameters\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(par.bits != par.bps*8)
|
||||
{
|
||||
ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->Frequency = par.rate;
|
||||
device->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
|
||||
|
||||
if(par.bits == 8 && par.sig == 1)
|
||||
device->FmtType = DevFmtByte;
|
||||
else if(par.bits == 8 && par.sig == 0)
|
||||
device->FmtType = DevFmtUByte;
|
||||
else if(par.bits == 16 && par.sig == 1)
|
||||
device->FmtType = DevFmtShort;
|
||||
else if(par.bits == 16 && par.sig == 0)
|
||||
device->FmtType = DevFmtUShort;
|
||||
else if(par.bits == 32 && par.sig == 1)
|
||||
device->FmtType = DevFmtInt;
|
||||
else if(par.bits == 32 && par.sig == 0)
|
||||
device->FmtType = DevFmtUInt;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->UpdateSize = par.round;
|
||||
device->NumUpdates = (par.bufsz/par.round) + 1;
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
|
||||
device->FmtChans, device->FmtType, device->AmbiOrder
|
||||
);
|
||||
al_free(self->mix_data);
|
||||
self->mix_data = al_calloc(16, self->data_size);
|
||||
|
||||
if(!sio_start(self->sndHandle))
|
||||
{
|
||||
ERR("Error starting playback\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
self->killNow = 0;
|
||||
if(althrd_create(&self->thread, ALCsndioBackend_mixerProc, self) != althrd_success)
|
||||
{
|
||||
sio_stop(self->sndHandle);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCsndioBackend_stop(ALCsndioBackend *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if(self->killNow)
|
||||
return;
|
||||
|
||||
self->killNow = 1;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
if(!sio_stop(self->sndHandle))
|
||||
ERR("Error stopping device\n");
|
||||
|
||||
al_free(self->mix_data);
|
||||
self->mix_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCsndioBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCsndioBackendFactory;
|
||||
#define ALCSNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsndioBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCsndioBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsndioBackendFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCsndioBackendFactory factory = ALCSNDIOBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory* UNUSED(self))
|
||||
{
|
||||
/* No dynamic loading */
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(sndio_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCsndioBackend *backend;
|
||||
NEW_OBJ(backend, ALCsndioBackend)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#include <sys/audioio.h>
|
||||
|
||||
|
||||
typedef struct ALCsolarisBackend {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
int fd;
|
||||
|
||||
ALubyte *mix_data;
|
||||
int data_size;
|
||||
|
||||
ATOMIC(ALenum) killNow;
|
||||
althrd_t thread;
|
||||
} ALCsolarisBackend;
|
||||
|
||||
static int ALCsolarisBackend_mixerProc(void *ptr);
|
||||
|
||||
static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device);
|
||||
static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self);
|
||||
static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name);
|
||||
static void ALCsolarisBackend_close(ALCsolarisBackend *self);
|
||||
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self);
|
||||
static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self);
|
||||
static void ALCsolarisBackend_stop(ALCsolarisBackend *self);
|
||||
static DECLARE_FORWARD2(ALCsolarisBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCsolarisBackend)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCsolarisBackend);
|
||||
|
||||
|
||||
static const ALCchar solaris_device[] = "Solaris Default";
|
||||
|
||||
static const char *solaris_driver = "/dev/audio";
|
||||
|
||||
|
||||
static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCsolarisBackend, ALCbackend, self);
|
||||
|
||||
self->fd = -1;
|
||||
ATOMIC_INIT(&self->killNow, AL_FALSE);
|
||||
}
|
||||
|
||||
static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self)
|
||||
{
|
||||
if(self->fd != -1)
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
|
||||
free(self->mix_data);
|
||||
self->mix_data = NULL;
|
||||
self->data_size = 0;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static int ALCsolarisBackend_mixerProc(void *ptr)
|
||||
{
|
||||
ALCsolarisBackend *self = ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
struct timeval timeout;
|
||||
ALubyte *write_ptr;
|
||||
ALint frame_size;
|
||||
ALint to_write;
|
||||
ssize_t wrote;
|
||||
fd_set wfds;
|
||||
int sret;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
ALCsolarisBackend_lock(self);
|
||||
while(!ATOMIC_LOAD_SEQ(&self->killNow) && device->Connected)
|
||||
{
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(self->fd, &wfds);
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
ALCsolarisBackend_unlock(self);
|
||||
sret = select(self->fd+1, NULL, &wfds, NULL, &timeout);
|
||||
ALCsolarisBackend_lock(self);
|
||||
if(sret < 0)
|
||||
{
|
||||
if(errno == EINTR)
|
||||
continue;
|
||||
ERR("select failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
break;
|
||||
}
|
||||
else if(sret == 0)
|
||||
{
|
||||
WARN("select timeout\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
write_ptr = self->mix_data;
|
||||
to_write = self->data_size;
|
||||
aluMixData(device, write_ptr, to_write/frame_size);
|
||||
while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow))
|
||||
{
|
||||
wrote = write(self->fd, write_ptr, to_write);
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||
continue;
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
break;
|
||||
}
|
||||
|
||||
to_write -= wrote;
|
||||
write_ptr += wrote;
|
||||
}
|
||||
}
|
||||
ALCsolarisBackend_unlock(self);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device;
|
||||
|
||||
if(!name)
|
||||
name = solaris_device;
|
||||
else if(strcmp(name, solaris_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
self->fd = open(solaris_driver, O_WRONLY);
|
||||
if(self->fd == -1)
|
||||
{
|
||||
ERR("Could not open %s: %s\n", solaris_driver, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCsolarisBackend_close(ALCsolarisBackend *self)
|
||||
{
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
audio_info_t info;
|
||||
ALsizei frameSize;
|
||||
ALsizei numChannels;
|
||||
|
||||
AUDIO_INITINFO(&info);
|
||||
|
||||
info.play.sample_rate = device->Frequency;
|
||||
|
||||
if(device->FmtChans != DevFmtMono)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
info.play.channels = numChannels;
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
info.play.precision = 8;
|
||||
info.play.encoding = AUDIO_ENCODING_LINEAR;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
info.play.precision = 8;
|
||||
info.play.encoding = AUDIO_ENCODING_LINEAR8;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtUInt:
|
||||
case DevFmtFloat:
|
||||
device->FmtType = DevFmtShort;
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
info.play.precision = 16;
|
||||
info.play.encoding = AUDIO_ENCODING_LINEAR;
|
||||
break;
|
||||
}
|
||||
|
||||
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
|
||||
info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize;
|
||||
|
||||
if(ioctl(self->fd, AUDIO_SETINFO, &info) < 0)
|
||||
{
|
||||
ERR("ioctl failed: %s\n", strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)info.play.channels)
|
||||
{
|
||||
ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), info.play.channels);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && device->FmtType == DevFmtUByte) ||
|
||||
(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtByte) ||
|
||||
(info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtShort) ||
|
||||
(info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtInt)))
|
||||
{
|
||||
ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(device->FmtType),
|
||||
info.play.precision, info.play.encoding);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->Frequency = info.play.sample_rate;
|
||||
device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1;
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
free(self->mix_data);
|
||||
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
|
||||
device->FmtChans, device->FmtType, device->AmbiOrder
|
||||
);
|
||||
self->mix_data = calloc(1, self->data_size);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
|
||||
if(althrd_create(&self->thread, ALCsolarisBackend_mixerProc, self) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCsolarisBackend_stop(ALCsolarisBackend *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
|
||||
return;
|
||||
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
if(ioctl(self->fd, AUDIO_DRAIN) < 0)
|
||||
ERR("Error draining device: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCsolarisBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCsolarisBackendFactory;
|
||||
#define ALCSOLARISBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsolarisBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCsolarisBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsolarisBackendFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCsolarisBackendFactory factory = ALCSOLARISBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory* UNUSED(self))
|
||||
{
|
||||
ConfigValueStr(NULL, "solaris", "device", &solaris_driver);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(solaris_driver, &buf) == 0)
|
||||
#endif
|
||||
AppendAllDevicesList(solaris_device);
|
||||
}
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCsolarisBackend *backend;
|
||||
NEW_OBJ(backend, ALCsolarisBackend)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
|
||||
static const ALCchar waveDevice[] = "Wave File Writer";
|
||||
|
||||
static const ALubyte SUBTYPE_PCM[] = {
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
|
||||
0x00, 0x38, 0x9b, 0x71
|
||||
};
|
||||
static const ALubyte SUBTYPE_FLOAT[] = {
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
|
||||
0x00, 0x38, 0x9b, 0x71
|
||||
};
|
||||
|
||||
static const ALubyte SUBTYPE_BFORMAT_PCM[] = {
|
||||
0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
|
||||
0xca, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const ALubyte SUBTYPE_BFORMAT_FLOAT[] = {
|
||||
0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
|
||||
0xca, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static void fwrite16le(ALushort val, FILE *f)
|
||||
{
|
||||
ALubyte data[2] = { val&0xff, (val>>8)&0xff };
|
||||
fwrite(data, 1, 2, f);
|
||||
}
|
||||
|
||||
static void fwrite32le(ALuint val, FILE *f)
|
||||
{
|
||||
ALubyte data[4] = { val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff };
|
||||
fwrite(data, 1, 4, f);
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCwaveBackend {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
FILE *mFile;
|
||||
long mDataStart;
|
||||
|
||||
ALvoid *mBuffer;
|
||||
ALuint mSize;
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} ALCwaveBackend;
|
||||
|
||||
static int ALCwaveBackend_mixerProc(void *ptr);
|
||||
|
||||
static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, Destruct)
|
||||
static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name);
|
||||
static void ALCwaveBackend_close(ALCwaveBackend *self);
|
||||
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self);
|
||||
static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self);
|
||||
static void ALCwaveBackend_stop(ALCwaveBackend *self);
|
||||
static DECLARE_FORWARD2(ALCwaveBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCwaveBackend)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCwaveBackend);
|
||||
|
||||
|
||||
static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCwaveBackend, ALCbackend, self);
|
||||
|
||||
self->mFile = NULL;
|
||||
self->mDataStart = -1;
|
||||
|
||||
self->mBuffer = NULL;
|
||||
self->mSize = 0;
|
||||
|
||||
self->killNow = 1;
|
||||
}
|
||||
|
||||
|
||||
static int ALCwaveBackend_mixerProc(void *ptr)
|
||||
{
|
||||
ALCwaveBackend *self = (ALCwaveBackend*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
struct timespec now, start;
|
||||
ALint64 avail, done;
|
||||
ALuint frameSize;
|
||||
size_t fs;
|
||||
const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
|
||||
device->Frequency / 2);
|
||||
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
done = 0;
|
||||
if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
|
||||
{
|
||||
ERR("Failed to get starting time\n");
|
||||
return 1;
|
||||
}
|
||||
while(!self->killNow && device->Connected)
|
||||
{
|
||||
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
|
||||
{
|
||||
ERR("Failed to get current time\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
avail = (now.tv_sec - start.tv_sec) * device->Frequency;
|
||||
avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000;
|
||||
if(avail < done)
|
||||
{
|
||||
/* Oops, time skipped backwards. Reset the number of samples done
|
||||
* with one update available since we (likely) just came back from
|
||||
* sleeping. */
|
||||
done = avail - device->UpdateSize;
|
||||
}
|
||||
|
||||
if(avail-done < device->UpdateSize)
|
||||
al_nssleep(restTime);
|
||||
else while(avail-done >= device->UpdateSize)
|
||||
{
|
||||
ALCwaveBackend_lock(self);
|
||||
aluMixData(device, self->mBuffer, device->UpdateSize);
|
||||
ALCwaveBackend_unlock(self);
|
||||
done += device->UpdateSize;
|
||||
|
||||
if(!IS_LITTLE_ENDIAN)
|
||||
{
|
||||
ALuint bytesize = BytesFromDevFmt(device->FmtType);
|
||||
ALuint i;
|
||||
|
||||
if(bytesize == 2)
|
||||
{
|
||||
ALushort *samples = self->mBuffer;
|
||||
ALuint len = self->mSize / 2;
|
||||
for(i = 0;i < len;i++)
|
||||
{
|
||||
ALushort samp = samples[i];
|
||||
samples[i] = (samp>>8) | (samp<<8);
|
||||
}
|
||||
}
|
||||
else if(bytesize == 4)
|
||||
{
|
||||
ALuint *samples = self->mBuffer;
|
||||
ALuint len = self->mSize / 4;
|
||||
for(i = 0;i < len;i++)
|
||||
{
|
||||
ALuint samp = samples[i];
|
||||
samples[i] = (samp>>24) | ((samp>>8)&0x0000ff00) |
|
||||
((samp<<8)&0x00ff0000) | (samp<<24);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs = fwrite(self->mBuffer, frameSize, device->UpdateSize, self->mFile);
|
||||
(void)fs;
|
||||
if(ferror(self->mFile))
|
||||
{
|
||||
ERR("Error writing to file\n");
|
||||
ALCdevice_Lock(device);
|
||||
aluHandleDisconnect(device);
|
||||
ALCdevice_Unlock(device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device;
|
||||
const char *fname;
|
||||
|
||||
fname = GetConfigValue(NULL, "wave", "file", "");
|
||||
if(!fname[0]) return ALC_INVALID_VALUE;
|
||||
|
||||
if(!name)
|
||||
name = waveDevice;
|
||||
else if(strcmp(name, waveDevice) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
self->mFile = al_fopen(fname, "wb");
|
||||
if(!self->mFile)
|
||||
{
|
||||
ERR("Could not open file '%s': %s\n", fname, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCwaveBackend_close(ALCwaveBackend *self)
|
||||
{
|
||||
if(self->mFile)
|
||||
fclose(self->mFile);
|
||||
self->mFile = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALuint channels=0, bits=0, chanmask=0;
|
||||
int isbformat = 0;
|
||||
size_t val;
|
||||
|
||||
fseek(self->mFile, 0, SEEK_SET);
|
||||
clearerr(self->mFile);
|
||||
|
||||
if(GetConfigValueBool(NULL, "wave", "bformat", 0))
|
||||
{
|
||||
device->FmtChans = DevFmtAmbi3D;
|
||||
device->AmbiOrder = 1;
|
||||
}
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
device->FmtType = DevFmtUByte;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
device->FmtType = DevFmtShort;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
device->FmtType = DevFmtInt;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
case DevFmtShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtFloat:
|
||||
break;
|
||||
}
|
||||
switch(device->FmtChans)
|
||||
{
|
||||
case DevFmtMono: chanmask = 0x04; break;
|
||||
case DevFmtStereo: chanmask = 0x01 | 0x02; break;
|
||||
case DevFmtQuad: chanmask = 0x01 | 0x02 | 0x10 | 0x20; break;
|
||||
case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
|
||||
case DevFmtX51Rear: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020; break;
|
||||
case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
|
||||
case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
|
||||
case DevFmtAmbi3D:
|
||||
/* .amb output requires FuMa */
|
||||
device->AmbiLayout = AmbiLayout_FuMa;
|
||||
device->AmbiScale = AmbiNorm_FuMa;
|
||||
isbformat = 1;
|
||||
chanmask = 0;
|
||||
break;
|
||||
}
|
||||
bits = BytesFromDevFmt(device->FmtType) * 8;
|
||||
channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
|
||||
fputs("RIFF", self->mFile);
|
||||
fwrite32le(0xFFFFFFFF, self->mFile); // 'RIFF' header len; filled in at close
|
||||
|
||||
fputs("WAVE", self->mFile);
|
||||
|
||||
fputs("fmt ", self->mFile);
|
||||
fwrite32le(40, self->mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
|
||||
|
||||
// 16-bit val, format type id (extensible: 0xFFFE)
|
||||
fwrite16le(0xFFFE, self->mFile);
|
||||
// 16-bit val, channel count
|
||||
fwrite16le(channels, self->mFile);
|
||||
// 32-bit val, frequency
|
||||
fwrite32le(device->Frequency, self->mFile);
|
||||
// 32-bit val, bytes per second
|
||||
fwrite32le(device->Frequency * channels * bits / 8, self->mFile);
|
||||
// 16-bit val, frame size
|
||||
fwrite16le(channels * bits / 8, self->mFile);
|
||||
// 16-bit val, bits per sample
|
||||
fwrite16le(bits, self->mFile);
|
||||
// 16-bit val, extra byte count
|
||||
fwrite16le(22, self->mFile);
|
||||
// 16-bit val, valid bits per sample
|
||||
fwrite16le(bits, self->mFile);
|
||||
// 32-bit val, channel mask
|
||||
fwrite32le(chanmask, self->mFile);
|
||||
// 16 byte GUID, sub-type format
|
||||
val = fwrite((device->FmtType == DevFmtFloat) ?
|
||||
(isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
|
||||
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, self->mFile);
|
||||
(void)val;
|
||||
|
||||
fputs("data", self->mFile);
|
||||
fwrite32le(0xFFFFFFFF, self->mFile); // 'data' header len; filled in at close
|
||||
|
||||
if(ferror(self->mFile))
|
||||
{
|
||||
ERR("Error writing header: %s\n", strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
self->mDataStart = ftell(self->mFile);
|
||||
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
self->mSize = device->UpdateSize * FrameSizeFromDevFmt(
|
||||
device->FmtChans, device->FmtType, device->AmbiOrder
|
||||
);
|
||||
self->mBuffer = malloc(self->mSize);
|
||||
if(!self->mBuffer)
|
||||
{
|
||||
ERR("Buffer malloc failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
self->killNow = 0;
|
||||
if(althrd_create(&self->thread, ALCwaveBackend_mixerProc, self) != althrd_success)
|
||||
{
|
||||
free(self->mBuffer);
|
||||
self->mBuffer = NULL;
|
||||
self->mSize = 0;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCwaveBackend_stop(ALCwaveBackend *self)
|
||||
{
|
||||
ALuint dataLen;
|
||||
long size;
|
||||
int res;
|
||||
|
||||
if(self->killNow)
|
||||
return;
|
||||
|
||||
self->killNow = 1;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
free(self->mBuffer);
|
||||
self->mBuffer = NULL;
|
||||
|
||||
size = ftell(self->mFile);
|
||||
if(size > 0)
|
||||
{
|
||||
dataLen = size - self->mDataStart;
|
||||
if(fseek(self->mFile, self->mDataStart-4, SEEK_SET) == 0)
|
||||
fwrite32le(dataLen, self->mFile); // 'data' header len
|
||||
if(fseek(self->mFile, 4, SEEK_SET) == 0)
|
||||
fwrite32le(size-8, self->mFile); // 'WAVE' header len
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCwaveBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCwaveBackendFactory;
|
||||
#define ALCWAVEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwaveBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCwaveBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwaveBackendFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCwaveBackendFactory factory = ALCWAVEBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory* UNUSED(self))
|
||||
{
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
return !!ConfigValueExists(NULL, "wave", "file");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(waveDevice);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCwaveBackend *backend;
|
||||
NEW_OBJ(backend, ALCwaveBackend)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,803 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
||||
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
||||
#endif
|
||||
|
||||
#define DEVNAME_HEAD "OpenAL Soft on "
|
||||
|
||||
|
||||
static vector_al_string PlaybackDevices;
|
||||
static vector_al_string CaptureDevices;
|
||||
|
||||
static void clear_devlist(vector_al_string *list)
|
||||
{
|
||||
VECTOR_FOR_EACH(al_string, *list, alstr_reset);
|
||||
VECTOR_RESIZE(*list, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void ProbePlaybackDevices(void)
|
||||
{
|
||||
ALuint numdevs;
|
||||
ALuint i;
|
||||
|
||||
clear_devlist(&PlaybackDevices);
|
||||
|
||||
numdevs = waveOutGetNumDevs();
|
||||
VECTOR_RESIZE(PlaybackDevices, 0, numdevs);
|
||||
for(i = 0;i < numdevs;i++)
|
||||
{
|
||||
WAVEOUTCAPSW WaveCaps;
|
||||
const al_string *iter;
|
||||
al_string dname;
|
||||
|
||||
AL_STRING_INIT(dname);
|
||||
if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
||||
{
|
||||
ALuint count = 0;
|
||||
while(1)
|
||||
{
|
||||
alstr_copy_cstr(&dname, DEVNAME_HEAD);
|
||||
alstr_append_wcstr(&dname, WaveCaps.szPname);
|
||||
if(count != 0)
|
||||
{
|
||||
char str[64];
|
||||
snprintf(str, sizeof(str), " #%d", count+1);
|
||||
alstr_append_cstr(&dname, str);
|
||||
}
|
||||
count++;
|
||||
|
||||
#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
|
||||
VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_ENTRY);
|
||||
if(iter == VECTOR_END(PlaybackDevices)) break;
|
||||
#undef MATCH_ENTRY
|
||||
}
|
||||
|
||||
TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
|
||||
}
|
||||
VECTOR_PUSH_BACK(PlaybackDevices, dname);
|
||||
}
|
||||
}
|
||||
|
||||
static void ProbeCaptureDevices(void)
|
||||
{
|
||||
ALuint numdevs;
|
||||
ALuint i;
|
||||
|
||||
clear_devlist(&CaptureDevices);
|
||||
|
||||
numdevs = waveInGetNumDevs();
|
||||
VECTOR_RESIZE(CaptureDevices, 0, numdevs);
|
||||
for(i = 0;i < numdevs;i++)
|
||||
{
|
||||
WAVEINCAPSW WaveCaps;
|
||||
const al_string *iter;
|
||||
al_string dname;
|
||||
|
||||
AL_STRING_INIT(dname);
|
||||
if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
||||
{
|
||||
ALuint count = 0;
|
||||
while(1)
|
||||
{
|
||||
alstr_copy_cstr(&dname, DEVNAME_HEAD);
|
||||
alstr_append_wcstr(&dname, WaveCaps.szPname);
|
||||
if(count != 0)
|
||||
{
|
||||
char str[64];
|
||||
snprintf(str, sizeof(str), " #%d", count+1);
|
||||
alstr_append_cstr(&dname, str);
|
||||
}
|
||||
count++;
|
||||
|
||||
#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
|
||||
VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_ENTRY);
|
||||
if(iter == VECTOR_END(CaptureDevices)) break;
|
||||
#undef MATCH_ENTRY
|
||||
}
|
||||
|
||||
TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
|
||||
}
|
||||
VECTOR_PUSH_BACK(CaptureDevices, dname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCwinmmPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
RefCount WaveBuffersCommitted;
|
||||
WAVEHDR WaveBuffer[4];
|
||||
|
||||
HWAVEOUT OutHdl;
|
||||
|
||||
WAVEFORMATEX Format;
|
||||
|
||||
volatile ALboolean killNow;
|
||||
althrd_t thread;
|
||||
} ALCwinmmPlayback;
|
||||
|
||||
static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device);
|
||||
static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self);
|
||||
|
||||
static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
|
||||
static int ALCwinmmPlayback_mixerProc(void *arg);
|
||||
|
||||
static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *name);
|
||||
static void ALCwinmmPlayback_close(ALCwinmmPlayback *self);
|
||||
static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self);
|
||||
static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self);
|
||||
static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self);
|
||||
static DECLARE_FORWARD2(ALCwinmmPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCwinmmPlayback)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCwinmmPlayback);
|
||||
|
||||
|
||||
static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCwinmmPlayback, ALCbackend, self);
|
||||
|
||||
InitRef(&self->WaveBuffersCommitted, 0);
|
||||
self->OutHdl = NULL;
|
||||
|
||||
self->killNow = AL_TRUE;
|
||||
}
|
||||
|
||||
static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self)
|
||||
{
|
||||
if(self->OutHdl)
|
||||
waveOutClose(self->OutHdl);
|
||||
self->OutHdl = 0;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
/* ALCwinmmPlayback_waveOutProc
|
||||
*
|
||||
* Posts a message to 'ALCwinmmPlayback_mixerProc' everytime a WaveOut Buffer
|
||||
* is completed and returns to the application (for more data)
|
||||
*/
|
||||
static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
|
||||
{
|
||||
ALCwinmmPlayback *self = (ALCwinmmPlayback*)instance;
|
||||
|
||||
if(msg != WOM_DONE)
|
||||
return;
|
||||
|
||||
DecrementRef(&self->WaveBuffersCommitted);
|
||||
PostThreadMessage(self->thread, msg, 0, param1);
|
||||
}
|
||||
|
||||
FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
|
||||
{
|
||||
ALCwinmmPlayback *self = arg;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
WAVEHDR *WaveHdr;
|
||||
MSG msg;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
while(GetMessage(&msg, NULL, 0, 0))
|
||||
{
|
||||
if(msg.message != WOM_DONE)
|
||||
continue;
|
||||
|
||||
if(self->killNow)
|
||||
{
|
||||
if(ReadRef(&self->WaveBuffersCommitted) == 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
WaveHdr = ((WAVEHDR*)msg.lParam);
|
||||
ALCwinmmPlayback_lock(self);
|
||||
aluMixData(device, WaveHdr->lpData, WaveHdr->dwBufferLength /
|
||||
self->Format.nBlockAlign);
|
||||
ALCwinmmPlayback_unlock(self);
|
||||
|
||||
// Send buffer back to play more data
|
||||
waveOutWrite(self->OutHdl, WaveHdr, sizeof(WAVEHDR));
|
||||
IncrementRef(&self->WaveBuffersCommitted);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *deviceName)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
const al_string *iter;
|
||||
UINT DeviceID;
|
||||
MMRESULT res;
|
||||
|
||||
if(VECTOR_SIZE(PlaybackDevices) == 0)
|
||||
ProbePlaybackDevices();
|
||||
|
||||
// Find the Device ID matching the deviceName if valid
|
||||
#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && \
|
||||
(!deviceName || alstr_cmp_cstr(*(iter), deviceName) == 0))
|
||||
VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_DEVNAME);
|
||||
if(iter == VECTOR_END(PlaybackDevices))
|
||||
return ALC_INVALID_VALUE;
|
||||
#undef MATCH_DEVNAME
|
||||
|
||||
DeviceID = (UINT)(iter - VECTOR_BEGIN(PlaybackDevices));
|
||||
|
||||
retry_open:
|
||||
memset(&self->Format, 0, sizeof(WAVEFORMATEX));
|
||||
if(device->FmtType == DevFmtFloat)
|
||||
{
|
||||
self->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
self->Format.wBitsPerSample = 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
if(device->FmtType == DevFmtUByte || device->FmtType == DevFmtByte)
|
||||
self->Format.wBitsPerSample = 8;
|
||||
else
|
||||
self->Format.wBitsPerSample = 16;
|
||||
}
|
||||
self->Format.nChannels = ((device->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
self->Format.nBlockAlign = self->Format.wBitsPerSample *
|
||||
self->Format.nChannels / 8;
|
||||
self->Format.nSamplesPerSec = device->Frequency;
|
||||
self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
|
||||
self->Format.nBlockAlign;
|
||||
self->Format.cbSize = 0;
|
||||
|
||||
if((res=waveOutOpen(&self->OutHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmPlayback_waveOutProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
|
||||
{
|
||||
if(device->FmtType == DevFmtFloat)
|
||||
{
|
||||
device->FmtType = DevFmtShort;
|
||||
goto retry_open;
|
||||
}
|
||||
ERR("waveOutOpen failed: %u\n", res);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
alstr_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
failure:
|
||||
if(self->OutHdl)
|
||||
waveOutClose(self->OutHdl);
|
||||
self->OutHdl = NULL;
|
||||
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void ALCwinmmPlayback_close(ALCwinmmPlayback* UNUSED(self))
|
||||
{ }
|
||||
|
||||
static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
|
||||
self->Format.nSamplesPerSec /
|
||||
device->Frequency);
|
||||
device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4;
|
||||
device->NumUpdates = 4;
|
||||
device->Frequency = self->Format.nSamplesPerSec;
|
||||
|
||||
if(self->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
|
||||
{
|
||||
if(self->Format.wBitsPerSample == 32)
|
||||
device->FmtType = DevFmtFloat;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled IEEE float sample depth: %d\n", self->Format.wBitsPerSample);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
}
|
||||
else if(self->Format.wFormatTag == WAVE_FORMAT_PCM)
|
||||
{
|
||||
if(self->Format.wBitsPerSample == 16)
|
||||
device->FmtType = DevFmtShort;
|
||||
else if(self->Format.wBitsPerSample == 8)
|
||||
device->FmtType = DevFmtUByte;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled PCM sample depth: %d\n", self->Format.wBitsPerSample);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unhandled format tag: 0x%04x\n", self->Format.wFormatTag);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(self->Format.nChannels == 2)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
else if(self->Format.nChannels == 1)
|
||||
device->FmtChans = DevFmtMono;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled channel count: %d\n", self->Format.nChannels);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALbyte *BufferData;
|
||||
ALint BufferSize;
|
||||
ALuint i;
|
||||
|
||||
self->killNow = AL_FALSE;
|
||||
if(althrd_create(&self->thread, ALCwinmmPlayback_mixerProc, self) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
|
||||
InitRef(&self->WaveBuffersCommitted, 0);
|
||||
|
||||
// Create 4 Buffers
|
||||
BufferSize = device->UpdateSize*device->NumUpdates / 4;
|
||||
BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
BufferData = calloc(4, BufferSize);
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
|
||||
self->WaveBuffer[i].dwBufferLength = BufferSize;
|
||||
self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
|
||||
(self->WaveBuffer[i-1].lpData +
|
||||
self->WaveBuffer[i-1].dwBufferLength));
|
||||
waveOutPrepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
waveOutWrite(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
IncrementRef(&self->WaveBuffersCommitted);
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self)
|
||||
{
|
||||
void *buffer = NULL;
|
||||
int i;
|
||||
|
||||
if(self->killNow)
|
||||
return;
|
||||
|
||||
// Set flag to stop processing headers
|
||||
self->killNow = AL_TRUE;
|
||||
althrd_join(self->thread, &i);
|
||||
|
||||
// Release the wave buffers
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
waveOutUnprepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
if(i == 0) buffer = self->WaveBuffer[i].lpData;
|
||||
self->WaveBuffer[i].lpData = NULL;
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct ALCwinmmCapture {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
RefCount WaveBuffersCommitted;
|
||||
WAVEHDR WaveBuffer[4];
|
||||
|
||||
HWAVEIN InHdl;
|
||||
|
||||
ll_ringbuffer_t *Ring;
|
||||
|
||||
WAVEFORMATEX Format;
|
||||
|
||||
volatile ALboolean killNow;
|
||||
althrd_t thread;
|
||||
} ALCwinmmCapture;
|
||||
|
||||
static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device);
|
||||
static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self);
|
||||
|
||||
static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
|
||||
static int ALCwinmmCapture_captureProc(void *arg);
|
||||
|
||||
static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name);
|
||||
static void ALCwinmmCapture_close(ALCwinmmCapture *self);
|
||||
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self);
|
||||
static void ALCwinmmCapture_stop(ALCwinmmCapture *self);
|
||||
static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples);
|
||||
static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self);
|
||||
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCwinmmCapture)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCwinmmCapture);
|
||||
|
||||
|
||||
static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCwinmmCapture, ALCbackend, self);
|
||||
|
||||
InitRef(&self->WaveBuffersCommitted, 0);
|
||||
self->InHdl = NULL;
|
||||
|
||||
self->killNow = AL_TRUE;
|
||||
}
|
||||
|
||||
static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self)
|
||||
{
|
||||
if(self->InHdl)
|
||||
waveInClose(self->InHdl);
|
||||
self->InHdl = 0;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
/* ALCwinmmCapture_waveInProc
|
||||
*
|
||||
* Posts a message to 'ALCwinmmCapture_captureProc' everytime a WaveIn Buffer
|
||||
* is completed and returns to the application (with more data).
|
||||
*/
|
||||
static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
|
||||
{
|
||||
ALCwinmmCapture *self = (ALCwinmmCapture*)instance;
|
||||
|
||||
if(msg != WIM_DATA)
|
||||
return;
|
||||
|
||||
DecrementRef(&self->WaveBuffersCommitted);
|
||||
PostThreadMessage(self->thread, msg, 0, param1);
|
||||
}
|
||||
|
||||
static int ALCwinmmCapture_captureProc(void *arg)
|
||||
{
|
||||
ALCwinmmCapture *self = arg;
|
||||
WAVEHDR *WaveHdr;
|
||||
MSG msg;
|
||||
|
||||
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
|
||||
|
||||
while(GetMessage(&msg, NULL, 0, 0))
|
||||
{
|
||||
if(msg.message != WIM_DATA)
|
||||
continue;
|
||||
/* Don't wait for other buffers to finish before quitting. We're
|
||||
* closing so we don't need them. */
|
||||
if(self->killNow)
|
||||
break;
|
||||
|
||||
WaveHdr = ((WAVEHDR*)msg.lParam);
|
||||
ll_ringbuffer_write(self->Ring, WaveHdr->lpData,
|
||||
WaveHdr->dwBytesRecorded / self->Format.nBlockAlign
|
||||
);
|
||||
|
||||
// Send buffer back to capture more data
|
||||
waveInAddBuffer(self->InHdl, WaveHdr, sizeof(WAVEHDR));
|
||||
IncrementRef(&self->WaveBuffersCommitted);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
const al_string *iter;
|
||||
ALbyte *BufferData = NULL;
|
||||
DWORD CapturedDataSize;
|
||||
ALint BufferSize;
|
||||
UINT DeviceID;
|
||||
MMRESULT res;
|
||||
ALuint i;
|
||||
|
||||
if(VECTOR_SIZE(CaptureDevices) == 0)
|
||||
ProbeCaptureDevices();
|
||||
|
||||
// Find the Device ID matching the deviceName if valid
|
||||
#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && (!name || alstr_cmp_cstr(*iter, name) == 0))
|
||||
VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_DEVNAME);
|
||||
if(iter == VECTOR_END(CaptureDevices))
|
||||
return ALC_INVALID_VALUE;
|
||||
#undef MATCH_DEVNAME
|
||||
|
||||
DeviceID = (UINT)(iter - VECTOR_BEGIN(CaptureDevices));
|
||||
|
||||
switch(device->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
case DevFmtStereo:
|
||||
break;
|
||||
|
||||
case DevFmtQuad:
|
||||
case DevFmtX51:
|
||||
case DevFmtX51Rear:
|
||||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
case DevFmtAmbi3D:
|
||||
return ALC_INVALID_ENUM;
|
||||
}
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtUByte:
|
||||
case DevFmtShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtFloat:
|
||||
break;
|
||||
|
||||
case DevFmtByte:
|
||||
case DevFmtUShort:
|
||||
case DevFmtUInt:
|
||||
return ALC_INVALID_ENUM;
|
||||
}
|
||||
|
||||
memset(&self->Format, 0, sizeof(WAVEFORMATEX));
|
||||
self->Format.wFormatTag = ((device->FmtType == DevFmtFloat) ?
|
||||
WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
|
||||
self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
self->Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
|
||||
self->Format.nBlockAlign = self->Format.wBitsPerSample *
|
||||
self->Format.nChannels / 8;
|
||||
self->Format.nSamplesPerSec = device->Frequency;
|
||||
self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
|
||||
self->Format.nBlockAlign;
|
||||
self->Format.cbSize = 0;
|
||||
|
||||
if((res=waveInOpen(&self->InHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmCapture_waveInProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
|
||||
{
|
||||
ERR("waveInOpen failed: %u\n", res);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
// Allocate circular memory buffer for the captured audio
|
||||
CapturedDataSize = device->UpdateSize*device->NumUpdates;
|
||||
|
||||
// Make sure circular buffer is at least 100ms in size
|
||||
if(CapturedDataSize < (self->Format.nSamplesPerSec / 10))
|
||||
CapturedDataSize = self->Format.nSamplesPerSec / 10;
|
||||
|
||||
self->Ring = ll_ringbuffer_create(CapturedDataSize+1, self->Format.nBlockAlign);
|
||||
if(!self->Ring) goto failure;
|
||||
|
||||
InitRef(&self->WaveBuffersCommitted, 0);
|
||||
|
||||
// Create 4 Buffers of 50ms each
|
||||
BufferSize = self->Format.nAvgBytesPerSec / 20;
|
||||
BufferSize -= (BufferSize % self->Format.nBlockAlign);
|
||||
|
||||
BufferData = calloc(4, BufferSize);
|
||||
if(!BufferData) goto failure;
|
||||
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
|
||||
self->WaveBuffer[i].dwBufferLength = BufferSize;
|
||||
self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
|
||||
(self->WaveBuffer[i-1].lpData +
|
||||
self->WaveBuffer[i-1].dwBufferLength));
|
||||
self->WaveBuffer[i].dwFlags = 0;
|
||||
self->WaveBuffer[i].dwLoops = 0;
|
||||
waveInPrepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
waveInAddBuffer(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
IncrementRef(&self->WaveBuffersCommitted);
|
||||
}
|
||||
|
||||
self->killNow = AL_FALSE;
|
||||
if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success)
|
||||
goto failure;
|
||||
|
||||
alstr_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
failure:
|
||||
if(BufferData)
|
||||
{
|
||||
for(i = 0;i < 4;i++)
|
||||
waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
free(BufferData);
|
||||
}
|
||||
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = NULL;
|
||||
|
||||
if(self->InHdl)
|
||||
waveInClose(self->InHdl);
|
||||
self->InHdl = NULL;
|
||||
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void ALCwinmmCapture_close(ALCwinmmCapture *self)
|
||||
{
|
||||
void *buffer = NULL;
|
||||
int i;
|
||||
|
||||
/* Tell the processing thread to quit and wait for it to do so. */
|
||||
self->killNow = AL_TRUE;
|
||||
PostThreadMessage(self->thread, WM_QUIT, 0, 0);
|
||||
|
||||
althrd_join(self->thread, &i);
|
||||
|
||||
/* Make sure capture is stopped and all pending buffers are flushed. */
|
||||
waveInReset(self->InHdl);
|
||||
|
||||
// Release the wave buffers
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
if(i == 0) buffer = self->WaveBuffer[i].lpData;
|
||||
self->WaveBuffer[i].lpData = NULL;
|
||||
}
|
||||
free(buffer);
|
||||
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = NULL;
|
||||
|
||||
// Close the Wave device
|
||||
waveInClose(self->InHdl);
|
||||
self->InHdl = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self)
|
||||
{
|
||||
waveInStart(self->InHdl);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCwinmmCapture_stop(ALCwinmmCapture *self)
|
||||
{
|
||||
waveInStop(self->InHdl);
|
||||
}
|
||||
|
||||
static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
ll_ringbuffer_read(self->Ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
|
||||
{
|
||||
return ll_ringbuffer_read_space(self->Ring);
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const al_string *name)
|
||||
{
|
||||
if(!alstr_empty(*name))
|
||||
AppendAllDevicesList(alstr_get_cstr(*name));
|
||||
}
|
||||
static inline void AppendCaptureDeviceList2(const al_string *name)
|
||||
{
|
||||
if(!alstr_empty(*name))
|
||||
AppendCaptureDeviceList(alstr_get_cstr(*name));
|
||||
}
|
||||
|
||||
typedef struct ALCwinmmBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCwinmmBackendFactory;
|
||||
#define ALCWINMMBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwinmmBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory *self);
|
||||
static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory *self);
|
||||
static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwinmmBackendFactory);
|
||||
|
||||
|
||||
static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory* UNUSED(self))
|
||||
{
|
||||
VECTOR_INIT(PlaybackDevices);
|
||||
VECTOR_INIT(CaptureDevices);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory* UNUSED(self))
|
||||
{
|
||||
clear_devlist(&PlaybackDevices);
|
||||
VECTOR_DEINIT(PlaybackDevices);
|
||||
|
||||
clear_devlist(&CaptureDevices);
|
||||
VECTOR_DEINIT(CaptureDevices);
|
||||
}
|
||||
|
||||
static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
ProbePlaybackDevices();
|
||||
VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
ProbeCaptureDevices();
|
||||
VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCwinmmPlayback *backend;
|
||||
NEW_OBJ(backend, ALCwinmmPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCwinmmCapture *backend;
|
||||
NEW_OBJ(backend, ALCwinmmCapture)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCwinmmBackendFactory factory = ALCWINMMBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
@@ -0,0 +1,612 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "bformatdec.h"
|
||||
#include "ambdec.h"
|
||||
#include "mixer_defs.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "bool.h"
|
||||
#include "threads.h"
|
||||
#include "almalloc.h"
|
||||
|
||||
|
||||
void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult)
|
||||
{
|
||||
ALfloat w = freq_mult * F_TAU;
|
||||
ALfloat cw = cosf(w);
|
||||
if(cw > FLT_EPSILON)
|
||||
splitter->coeff = (sinf(w) - 1.0f) / cw;
|
||||
else
|
||||
splitter->coeff = cw * -0.5f;
|
||||
|
||||
splitter->lp_z1 = 0.0f;
|
||||
splitter->lp_z2 = 0.0f;
|
||||
splitter->hp_z1 = 0.0f;
|
||||
}
|
||||
|
||||
void bandsplit_clear(BandSplitter *splitter)
|
||||
{
|
||||
splitter->lp_z1 = 0.0f;
|
||||
splitter->lp_z2 = 0.0f;
|
||||
splitter->hp_z1 = 0.0f;
|
||||
}
|
||||
|
||||
void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
|
||||
const ALfloat *input, ALsizei count)
|
||||
{
|
||||
ALfloat coeff, d, x;
|
||||
ALfloat z1, z2;
|
||||
ALsizei i;
|
||||
|
||||
coeff = splitter->coeff*0.5f + 0.5f;
|
||||
z1 = splitter->lp_z1;
|
||||
z2 = splitter->lp_z2;
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
x = input[i];
|
||||
|
||||
d = (x - z1) * coeff;
|
||||
x = z1 + d;
|
||||
z1 = x + d;
|
||||
|
||||
d = (x - z2) * coeff;
|
||||
x = z2 + d;
|
||||
z2 = x + d;
|
||||
|
||||
lpout[i] = x;
|
||||
}
|
||||
splitter->lp_z1 = z1;
|
||||
splitter->lp_z2 = z2;
|
||||
|
||||
coeff = splitter->coeff;
|
||||
z1 = splitter->hp_z1;
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
x = input[i];
|
||||
|
||||
d = x - coeff*z1;
|
||||
x = z1 + coeff*d;
|
||||
z1 = d;
|
||||
|
||||
hpout[i] = x - lpout[i];
|
||||
}
|
||||
splitter->hp_z1 = z1;
|
||||
}
|
||||
|
||||
|
||||
void splitterap_init(SplitterAllpass *splitter, ALfloat freq_mult)
|
||||
{
|
||||
ALfloat w = freq_mult * F_TAU;
|
||||
ALfloat cw = cosf(w);
|
||||
if(cw > FLT_EPSILON)
|
||||
splitter->coeff = (sinf(w) - 1.0f) / cw;
|
||||
else
|
||||
splitter->coeff = cw * -0.5f;
|
||||
|
||||
splitter->z1 = 0.0f;
|
||||
}
|
||||
|
||||
void splitterap_clear(SplitterAllpass *splitter)
|
||||
{
|
||||
splitter->z1 = 0.0f;
|
||||
}
|
||||
|
||||
void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count)
|
||||
{
|
||||
ALfloat coeff, d, x;
|
||||
ALfloat z1;
|
||||
ALsizei i;
|
||||
|
||||
coeff = splitter->coeff;
|
||||
z1 = splitter->z1;
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
x = samples[i];
|
||||
|
||||
d = x - coeff*z1;
|
||||
x = z1 + coeff*d;
|
||||
z1 = d;
|
||||
|
||||
samples[i] = x;
|
||||
}
|
||||
splitter->z1 = z1;
|
||||
}
|
||||
|
||||
|
||||
static const ALfloat UnitScale[MAX_AMBI_COEFFS] = {
|
||||
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
|
||||
};
|
||||
static const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
|
||||
1.000000000f, /* ACN 0 (W), sqrt(1) */
|
||||
1.732050808f, /* ACN 1 (Y), sqrt(3) */
|
||||
1.732050808f, /* ACN 2 (Z), sqrt(3) */
|
||||
1.732050808f, /* ACN 3 (X), sqrt(3) */
|
||||
2.236067978f, /* ACN 4 (V), sqrt(5) */
|
||||
2.236067978f, /* ACN 5 (T), sqrt(5) */
|
||||
2.236067978f, /* ACN 6 (R), sqrt(5) */
|
||||
2.236067978f, /* ACN 7 (S), sqrt(5) */
|
||||
2.236067978f, /* ACN 8 (U), sqrt(5) */
|
||||
2.645751311f, /* ACN 9 (Q), sqrt(7) */
|
||||
2.645751311f, /* ACN 10 (O), sqrt(7) */
|
||||
2.645751311f, /* ACN 11 (M), sqrt(7) */
|
||||
2.645751311f, /* ACN 12 (K), sqrt(7) */
|
||||
2.645751311f, /* ACN 13 (L), sqrt(7) */
|
||||
2.645751311f, /* ACN 14 (N), sqrt(7) */
|
||||
2.645751311f, /* ACN 15 (P), sqrt(7) */
|
||||
};
|
||||
static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
|
||||
1.414213562f, /* ACN 0 (W), sqrt(2) */
|
||||
1.732050808f, /* ACN 1 (Y), sqrt(3) */
|
||||
1.732050808f, /* ACN 2 (Z), sqrt(3) */
|
||||
1.732050808f, /* ACN 3 (X), sqrt(3) */
|
||||
1.936491673f, /* ACN 4 (V), sqrt(15)/2 */
|
||||
1.936491673f, /* ACN 5 (T), sqrt(15)/2 */
|
||||
2.236067978f, /* ACN 6 (R), sqrt(5) */
|
||||
1.936491673f, /* ACN 7 (S), sqrt(15)/2 */
|
||||
1.936491673f, /* ACN 8 (U), sqrt(15)/2 */
|
||||
2.091650066f, /* ACN 9 (Q), sqrt(35/8) */
|
||||
1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
|
||||
2.231093404f, /* ACN 11 (M), sqrt(224/45) */
|
||||
2.645751311f, /* ACN 12 (K), sqrt(7) */
|
||||
2.231093404f, /* ACN 13 (L), sqrt(224/45) */
|
||||
1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
|
||||
2.091650066f, /* ACN 15 (P), sqrt(35/8) */
|
||||
};
|
||||
|
||||
|
||||
enum FreqBand {
|
||||
FB_HighFreq,
|
||||
FB_LowFreq,
|
||||
FB_Max
|
||||
};
|
||||
|
||||
/* These points are in AL coordinates! */
|
||||
static const ALfloat Ambi3DPoints[8][3] = {
|
||||
{ -0.577350269f, 0.577350269f, -0.577350269f },
|
||||
{ 0.577350269f, 0.577350269f, -0.577350269f },
|
||||
{ -0.577350269f, 0.577350269f, 0.577350269f },
|
||||
{ 0.577350269f, 0.577350269f, 0.577350269f },
|
||||
{ -0.577350269f, -0.577350269f, -0.577350269f },
|
||||
{ 0.577350269f, -0.577350269f, -0.577350269f },
|
||||
{ -0.577350269f, -0.577350269f, 0.577350269f },
|
||||
{ 0.577350269f, -0.577350269f, 0.577350269f },
|
||||
};
|
||||
static const ALfloat Ambi3DDecoder[8][FB_Max][MAX_AMBI_COEFFS] = {
|
||||
{ { 0.25f, 0.1443375672f, 0.1443375672f, 0.1443375672f }, { 0.125f, 0.125f, 0.125f, 0.125f } },
|
||||
{ { 0.25f, -0.1443375672f, 0.1443375672f, 0.1443375672f }, { 0.125f, -0.125f, 0.125f, 0.125f } },
|
||||
{ { 0.25f, 0.1443375672f, 0.1443375672f, -0.1443375672f }, { 0.125f, 0.125f, 0.125f, -0.125f } },
|
||||
{ { 0.25f, -0.1443375672f, 0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, 0.125f, -0.125f } },
|
||||
{ { 0.25f, 0.1443375672f, -0.1443375672f, 0.1443375672f }, { 0.125f, 0.125f, -0.125f, 0.125f } },
|
||||
{ { 0.25f, -0.1443375672f, -0.1443375672f, 0.1443375672f }, { 0.125f, -0.125f, -0.125f, 0.125f } },
|
||||
{ { 0.25f, 0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, 0.125f, -0.125f, -0.125f } },
|
||||
{ { 0.25f, -0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, -0.125f, -0.125f } },
|
||||
};
|
||||
|
||||
|
||||
static RowMixerFunc MixMatrixRow = MixRow_C;
|
||||
|
||||
|
||||
static alonce_flag bformatdec_inited = AL_ONCE_FLAG_INIT;
|
||||
|
||||
static void init_bformatdec(void)
|
||||
{
|
||||
MixMatrixRow = SelectRowMixer();
|
||||
}
|
||||
|
||||
|
||||
/* NOTE: BandSplitter filters are unused with single-band decoding */
|
||||
typedef struct BFormatDec {
|
||||
ALboolean Enabled[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
union {
|
||||
alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][FB_Max][MAX_AMBI_COEFFS];
|
||||
alignas(16) ALfloat Single[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
|
||||
} Matrix;
|
||||
|
||||
BandSplitter XOver[MAX_AMBI_COEFFS];
|
||||
|
||||
ALfloat (*Samples)[BUFFERSIZE];
|
||||
/* These two alias into Samples */
|
||||
ALfloat (*SamplesHF)[BUFFERSIZE];
|
||||
ALfloat (*SamplesLF)[BUFFERSIZE];
|
||||
|
||||
alignas(16) ALfloat ChannelMix[BUFFERSIZE];
|
||||
|
||||
struct {
|
||||
BandSplitter XOver;
|
||||
ALfloat Gains[FB_Max];
|
||||
} UpSampler[4];
|
||||
|
||||
ALsizei NumChannels;
|
||||
ALboolean DualBand;
|
||||
} BFormatDec;
|
||||
|
||||
BFormatDec *bformatdec_alloc()
|
||||
{
|
||||
alcall_once(&bformatdec_inited, init_bformatdec);
|
||||
return al_calloc(16, sizeof(BFormatDec));
|
||||
}
|
||||
|
||||
void bformatdec_free(BFormatDec *dec)
|
||||
{
|
||||
if(dec)
|
||||
{
|
||||
al_free(dec->Samples);
|
||||
dec->Samples = NULL;
|
||||
dec->SamplesHF = NULL;
|
||||
dec->SamplesLF = NULL;
|
||||
|
||||
memset(dec, 0, sizeof(*dec));
|
||||
al_free(dec);
|
||||
}
|
||||
}
|
||||
|
||||
void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
static const ALsizei map2DTo3D[MAX_AMBI2D_COEFFS] = {
|
||||
0, 1, 3, 4, 8, 9, 15
|
||||
};
|
||||
const ALfloat *coeff_scale = UnitScale;
|
||||
bool periphonic;
|
||||
ALfloat ratio;
|
||||
ALsizei i;
|
||||
|
||||
al_free(dec->Samples);
|
||||
dec->Samples = NULL;
|
||||
dec->SamplesHF = NULL;
|
||||
dec->SamplesLF = NULL;
|
||||
|
||||
dec->NumChannels = chancount;
|
||||
dec->Samples = al_calloc(16, dec->NumChannels*2 * sizeof(dec->Samples[0]));
|
||||
dec->SamplesHF = dec->Samples;
|
||||
dec->SamplesLF = dec->SamplesHF + dec->NumChannels;
|
||||
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
dec->Enabled[i] = AL_FALSE;
|
||||
for(i = 0;i < conf->NumSpeakers;i++)
|
||||
dec->Enabled[chanmap[i]] = AL_TRUE;
|
||||
|
||||
if(conf->CoeffScale == ADS_SN3D)
|
||||
coeff_scale = SN3D2N3DScale;
|
||||
else if(conf->CoeffScale == ADS_FuMa)
|
||||
coeff_scale = FuMa2N3DScale;
|
||||
|
||||
memset(dec->UpSampler, 0, sizeof(dec->UpSampler));
|
||||
ratio = 400.0f / (ALfloat)srate;
|
||||
for(i = 0;i < 4;i++)
|
||||
bandsplit_init(&dec->UpSampler[i].XOver, ratio);
|
||||
if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
|
||||
{
|
||||
periphonic = true;
|
||||
|
||||
dec->UpSampler[0].Gains[FB_HighFreq] = (dec->NumChannels > 9) ? W_SCALE3D_THIRD :
|
||||
(dec->NumChannels > 4) ? W_SCALE3D_SECOND : 1.0f;
|
||||
dec->UpSampler[0].Gains[FB_LowFreq] = 1.0f;
|
||||
for(i = 1;i < 4;i++)
|
||||
{
|
||||
dec->UpSampler[i].Gains[FB_HighFreq] = (dec->NumChannels > 9) ? XYZ_SCALE3D_THIRD :
|
||||
(dec->NumChannels > 4) ? XYZ_SCALE3D_SECOND : 1.0f;
|
||||
dec->UpSampler[i].Gains[FB_LowFreq] = 1.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
periphonic = false;
|
||||
|
||||
dec->UpSampler[0].Gains[FB_HighFreq] = (dec->NumChannels > 5) ? W_SCALE2D_THIRD :
|
||||
(dec->NumChannels > 3) ? W_SCALE2D_SECOND : 1.0f;
|
||||
dec->UpSampler[0].Gains[FB_LowFreq] = 1.0f;
|
||||
for(i = 1;i < 3;i++)
|
||||
{
|
||||
dec->UpSampler[i].Gains[FB_HighFreq] = (dec->NumChannels > 5) ? XYZ_SCALE2D_THIRD :
|
||||
(dec->NumChannels > 3) ? XYZ_SCALE2D_SECOND : 1.0f;
|
||||
dec->UpSampler[i].Gains[FB_LowFreq] = 1.0f;
|
||||
}
|
||||
dec->UpSampler[3].Gains[FB_HighFreq] = 0.0f;
|
||||
dec->UpSampler[3].Gains[FB_LowFreq] = 0.0f;
|
||||
}
|
||||
|
||||
memset(&dec->Matrix, 0, sizeof(dec->Matrix));
|
||||
if(conf->FreqBands == 1)
|
||||
{
|
||||
dec->DualBand = AL_FALSE;
|
||||
for(i = 0;i < conf->NumSpeakers;i++)
|
||||
{
|
||||
ALsizei chan = chanmap[i];
|
||||
ALfloat gain;
|
||||
ALsizei j, k;
|
||||
|
||||
if(!periphonic)
|
||||
{
|
||||
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
|
||||
{
|
||||
ALsizei l = map2DTo3D[j];
|
||||
if(j == 0) gain = conf->HFOrderGain[0];
|
||||
else if(j == 1) gain = conf->HFOrderGain[1];
|
||||
else if(j == 3) gain = conf->HFOrderGain[2];
|
||||
else if(j == 5) gain = conf->HFOrderGain[3];
|
||||
if((conf->ChanMask&(1<<l)))
|
||||
dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[l] *
|
||||
gain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
|
||||
{
|
||||
if(j == 0) gain = conf->HFOrderGain[0];
|
||||
else if(j == 1) gain = conf->HFOrderGain[1];
|
||||
else if(j == 4) gain = conf->HFOrderGain[2];
|
||||
else if(j == 9) gain = conf->HFOrderGain[3];
|
||||
if((conf->ChanMask&(1<<j)))
|
||||
dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] *
|
||||
gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dec->DualBand = AL_TRUE;
|
||||
|
||||
ratio = conf->XOverFreq / (ALfloat)srate;
|
||||
for(i = 0;i < MAX_AMBI_COEFFS;i++)
|
||||
bandsplit_init(&dec->XOver[i], ratio);
|
||||
|
||||
ratio = powf(10.0f, conf->XOverRatio / 40.0f);
|
||||
for(i = 0;i < conf->NumSpeakers;i++)
|
||||
{
|
||||
ALsizei chan = chanmap[i];
|
||||
ALfloat gain;
|
||||
ALsizei j, k;
|
||||
|
||||
if(!periphonic)
|
||||
{
|
||||
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
|
||||
{
|
||||
ALsizei l = map2DTo3D[j];
|
||||
if(j == 0) gain = conf->HFOrderGain[0] * ratio;
|
||||
else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
|
||||
else if(j == 3) gain = conf->HFOrderGain[2] * ratio;
|
||||
else if(j == 5) gain = conf->HFOrderGain[3] * ratio;
|
||||
if((conf->ChanMask&(1<<l)))
|
||||
dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
|
||||
coeff_scale[l] * gain;
|
||||
}
|
||||
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
|
||||
{
|
||||
ALsizei l = map2DTo3D[j];
|
||||
if(j == 0) gain = conf->LFOrderGain[0] / ratio;
|
||||
else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
|
||||
else if(j == 3) gain = conf->LFOrderGain[2] / ratio;
|
||||
else if(j == 5) gain = conf->LFOrderGain[3] / ratio;
|
||||
if((conf->ChanMask&(1<<l)))
|
||||
dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
|
||||
coeff_scale[l] * gain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
|
||||
{
|
||||
if(j == 0) gain = conf->HFOrderGain[0] * ratio;
|
||||
else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
|
||||
else if(j == 4) gain = conf->HFOrderGain[2] * ratio;
|
||||
else if(j == 9) gain = conf->HFOrderGain[3] * ratio;
|
||||
if((conf->ChanMask&(1<<j)))
|
||||
dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
|
||||
coeff_scale[j] * gain;
|
||||
}
|
||||
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
|
||||
{
|
||||
if(j == 0) gain = conf->LFOrderGain[0] / ratio;
|
||||
else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
|
||||
else if(j == 4) gain = conf->LFOrderGain[2] / ratio;
|
||||
else if(j == 9) gain = conf->LFOrderGain[3] / ratio;
|
||||
if((conf->ChanMask&(1<<j)))
|
||||
dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
|
||||
coeff_scale[j] * gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
|
||||
{
|
||||
ALsizei chan, i;
|
||||
|
||||
OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
|
||||
if(dec->DualBand)
|
||||
{
|
||||
for(i = 0;i < dec->NumChannels;i++)
|
||||
bandsplit_process(&dec->XOver[i], dec->SamplesHF[i], dec->SamplesLF[i],
|
||||
InSamples[i], SamplesToDo);
|
||||
|
||||
for(chan = 0;chan < OutChannels;chan++)
|
||||
{
|
||||
if(!dec->Enabled[chan])
|
||||
continue;
|
||||
|
||||
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
|
||||
MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_HighFreq],
|
||||
SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesHF), dec->NumChannels, 0,
|
||||
SamplesToDo
|
||||
);
|
||||
MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_LowFreq],
|
||||
SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesLF), dec->NumChannels, 0,
|
||||
SamplesToDo
|
||||
);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[chan][i] += dec->ChannelMix[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(chan = 0;chan < OutChannels;chan++)
|
||||
{
|
||||
if(!dec->Enabled[chan])
|
||||
continue;
|
||||
|
||||
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
|
||||
MixMatrixRow(dec->ChannelMix, dec->Matrix.Single[chan], InSamples,
|
||||
dec->NumChannels, 0, SamplesToDo);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[chan][i] += dec->ChannelMix[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei InChannels, ALsizei SamplesToDo)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
/* This up-sampler leverages the differences observed in dual-band second-
|
||||
* and third-order decoder matrices compared to first-order. For the same
|
||||
* output channel configuration, the low-frequency matrix has identical
|
||||
* coefficients in the shared input channels, while the high-frequency
|
||||
* matrix has extra scalars applied to the W channel and X/Y/Z channels.
|
||||
* Mixing the first-order content into the higher-order stream with the
|
||||
* appropriate counter-scales applied to the HF response results in the
|
||||
* subsequent higher-order decode generating the same response as a first-
|
||||
* order decode.
|
||||
*/
|
||||
for(i = 0;i < InChannels;i++)
|
||||
{
|
||||
/* First, split the first-order components into low and high frequency
|
||||
* bands.
|
||||
*/
|
||||
bandsplit_process(&dec->UpSampler[i].XOver,
|
||||
dec->Samples[FB_HighFreq], dec->Samples[FB_LowFreq],
|
||||
InSamples[i], SamplesToDo
|
||||
);
|
||||
|
||||
/* Now write each band to the output. */
|
||||
MixMatrixRow(OutBuffer[i], dec->UpSampler[i].Gains,
|
||||
SAFE_CONST(ALfloatBUFFERSIZE*,dec->Samples), FB_Max, 0,
|
||||
SamplesToDo
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define INVALID_UPSAMPLE_INDEX INT_MAX
|
||||
|
||||
static ALsizei GetACNIndex(const BFChannelConfig *chans, ALsizei numchans, ALsizei acn)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < numchans;i++)
|
||||
{
|
||||
if(chans[i].Index == acn)
|
||||
return i;
|
||||
}
|
||||
return INVALID_UPSAMPLE_INDEX;
|
||||
}
|
||||
#define GetChannelForACN(b, a) GetACNIndex((b).Ambi.Map, (b).NumChannels, (a))
|
||||
|
||||
typedef struct AmbiUpsampler {
|
||||
alignas(16) ALfloat Samples[FB_Max][BUFFERSIZE];
|
||||
|
||||
BandSplitter XOver[4];
|
||||
|
||||
ALfloat Gains[4][MAX_OUTPUT_CHANNELS][FB_Max];
|
||||
} AmbiUpsampler;
|
||||
|
||||
AmbiUpsampler *ambiup_alloc()
|
||||
{
|
||||
alcall_once(&bformatdec_inited, init_bformatdec);
|
||||
return al_calloc(16, sizeof(AmbiUpsampler));
|
||||
}
|
||||
|
||||
void ambiup_free(struct AmbiUpsampler *ambiup)
|
||||
{
|
||||
al_free(ambiup);
|
||||
}
|
||||
|
||||
void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device)
|
||||
{
|
||||
ALfloat ratio;
|
||||
size_t i;
|
||||
|
||||
ratio = 400.0f / (ALfloat)device->Frequency;
|
||||
for(i = 0;i < 4;i++)
|
||||
bandsplit_init(&ambiup->XOver[i], ratio);
|
||||
|
||||
memset(ambiup->Gains, 0, sizeof(ambiup->Gains));
|
||||
if(device->Dry.CoeffCount > 0)
|
||||
{
|
||||
ALfloat encgains[8][MAX_OUTPUT_CHANNELS];
|
||||
ALsizei j;
|
||||
size_t k;
|
||||
|
||||
for(i = 0;i < COUNTOF(Ambi3DPoints);i++)
|
||||
{
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS] = { 0.0f };
|
||||
CalcDirectionCoeffs(Ambi3DPoints[i], 0.0f, coeffs);
|
||||
ComputePanningGains(device->Dry, coeffs, 1.0f, encgains[i]);
|
||||
}
|
||||
|
||||
/* Combine the matrices that do the in->virt and virt->out conversions
|
||||
* so we get a single in->out conversion. NOTE: the Encoder matrix
|
||||
* (encgains) and output are transposed, so the input channels line up
|
||||
* with the rows and the output channels line up with the columns.
|
||||
*/
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
for(j = 0;j < device->Dry.NumChannels;j++)
|
||||
{
|
||||
ALfloat hfgain=0.0f, lfgain=0.0f;
|
||||
for(k = 0;k < COUNTOF(Ambi3DDecoder);k++)
|
||||
{
|
||||
hfgain += Ambi3DDecoder[k][FB_HighFreq][i]*encgains[k][j];
|
||||
lfgain += Ambi3DDecoder[k][FB_LowFreq][i]*encgains[k][j];
|
||||
}
|
||||
ambiup->Gains[i][j][FB_HighFreq] = hfgain;
|
||||
ambiup->Gains[i][j][FB_LowFreq] = lfgain;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Assumes full 3D/periphonic on the input and output mixes! */
|
||||
ALfloat w_scale = (device->Dry.NumChannels > 9) ? W_SCALE3D_THIRD :
|
||||
(device->Dry.NumChannels > 4) ? W_SCALE3D_SECOND : 1.0f;
|
||||
ALfloat xyz_scale = (device->Dry.NumChannels > 9) ? XYZ_SCALE3D_THIRD :
|
||||
(device->Dry.NumChannels > 4) ? XYZ_SCALE3D_SECOND : 1.0f;
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
ALsizei index = GetChannelForACN(device->Dry, i);
|
||||
if(index != INVALID_UPSAMPLE_INDEX)
|
||||
{
|
||||
ALfloat scale = device->Dry.Ambi.Map[index].Scale;
|
||||
ambiup->Gains[i][index][FB_HighFreq] = scale * ((i==0) ? w_scale : xyz_scale);
|
||||
ambiup->Gains[i][index][FB_LowFreq] = scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
|
||||
{
|
||||
ALsizei i, j;
|
||||
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
bandsplit_process(&ambiup->XOver[i],
|
||||
ambiup->Samples[FB_HighFreq], ambiup->Samples[FB_LowFreq],
|
||||
InSamples[i], SamplesToDo
|
||||
);
|
||||
|
||||
for(j = 0;j < OutChannels;j++)
|
||||
MixMatrixRow(OutBuffer[j], ambiup->Gains[i][j],
|
||||
SAFE_CONST(ALfloatBUFFERSIZE*,ambiup->Samples), FB_Max, 0,
|
||||
SamplesToDo
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef BFORMATDEC_H
|
||||
#define BFORMATDEC_H
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
|
||||
/* These are the necessary scales for first-order HF responses to play over
|
||||
* higher-order 2D (non-periphonic) decoders.
|
||||
*/
|
||||
#define W_SCALE2D_SECOND 1.224744871f /* sqrt(1.5) */
|
||||
#define XYZ_SCALE2D_SECOND 1.0f
|
||||
#define W_SCALE2D_THIRD 1.414213562f /* sqrt(2) */
|
||||
#define XYZ_SCALE2D_THIRD 1.082392196f
|
||||
|
||||
/* These are the necessary scales for first-order HF responses to play over
|
||||
* higher-order 3D (periphonic) decoders.
|
||||
*/
|
||||
#define W_SCALE3D_SECOND 1.341640787f /* sqrt(1.8) */
|
||||
#define XYZ_SCALE3D_SECOND 1.0f
|
||||
#define W_SCALE3D_THIRD 1.695486018f
|
||||
#define XYZ_SCALE3D_THIRD 1.136697713f
|
||||
|
||||
|
||||
struct AmbDecConf;
|
||||
struct BFormatDec;
|
||||
struct AmbiUpsampler;
|
||||
|
||||
|
||||
struct BFormatDec *bformatdec_alloc();
|
||||
void bformatdec_free(struct BFormatDec *dec);
|
||||
void bformatdec_reset(struct BFormatDec *dec, const struct AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS]);
|
||||
|
||||
/* Decodes the ambisonic input to the given output channels. */
|
||||
void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
|
||||
|
||||
/* Up-samples a first-order input to the decoder's configuration. */
|
||||
void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei InChannels, ALsizei SamplesToDo);
|
||||
|
||||
|
||||
/* Stand-alone first-order upsampler. Kept here because it shares some stuff
|
||||
* with bformatdec.
|
||||
*/
|
||||
struct AmbiUpsampler *ambiup_alloc();
|
||||
void ambiup_free(struct AmbiUpsampler *ambiup);
|
||||
void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device);
|
||||
|
||||
void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
|
||||
|
||||
|
||||
/* Band splitter. Splits a signal into two phase-matching frequency bands. */
|
||||
typedef struct BandSplitter {
|
||||
ALfloat coeff;
|
||||
ALfloat lp_z1;
|
||||
ALfloat lp_z2;
|
||||
ALfloat hp_z1;
|
||||
} BandSplitter;
|
||||
|
||||
void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult);
|
||||
void bandsplit_clear(BandSplitter *splitter);
|
||||
void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
|
||||
const ALfloat *input, ALsizei count);
|
||||
|
||||
/* The all-pass portion of the band splitter. Applies the same phase shift
|
||||
* without splitting the signal.
|
||||
*/
|
||||
typedef struct SplitterAllpass {
|
||||
ALfloat coeff;
|
||||
ALfloat z1;
|
||||
} SplitterAllpass;
|
||||
|
||||
void splitterap_init(SplitterAllpass *splitter, ALfloat freq_mult);
|
||||
void splitterap_clear(SplitterAllpass *splitter);
|
||||
void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count);
|
||||
|
||||
#endif /* BFORMATDEC_H */
|
||||
+62
-18
@@ -29,9 +29,6 @@
|
||||
#include "bs2b.h"
|
||||
#include "alu.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
/* Set up all data. */
|
||||
static void init(struct bs2b *bs2b)
|
||||
@@ -40,8 +37,6 @@ static void init(struct bs2b *bs2b)
|
||||
float G_lo, G_hi;
|
||||
float x, g;
|
||||
|
||||
bs2b->srate = clampi(bs2b->srate, 2000, 192000);
|
||||
|
||||
switch(bs2b->level)
|
||||
{
|
||||
case BS2B_LOW_CLEVEL: /* Low crossfeed level */
|
||||
@@ -105,31 +100,25 @@ static void init(struct bs2b *bs2b)
|
||||
bs2b->a1_hi = -x * g;
|
||||
} /* init */
|
||||
|
||||
|
||||
/* Exported functions.
|
||||
* See descriptions in "bs2b.h"
|
||||
*/
|
||||
|
||||
void bs2b_set_level(struct bs2b *bs2b, int level)
|
||||
void bs2b_set_params(struct bs2b *bs2b, int level, int srate)
|
||||
{
|
||||
if(level == bs2b->level)
|
||||
return;
|
||||
if(srate <= 0) srate = 1;
|
||||
|
||||
bs2b->level = level;
|
||||
bs2b->srate = srate;
|
||||
init(bs2b);
|
||||
} /* bs2b_set_level */
|
||||
} /* bs2b_set_params */
|
||||
|
||||
int bs2b_get_level(struct bs2b *bs2b)
|
||||
{
|
||||
return bs2b->level;
|
||||
} /* bs2b_get_level */
|
||||
|
||||
void bs2b_set_srate(struct bs2b *bs2b, int srate)
|
||||
{
|
||||
if (srate == bs2b->srate)
|
||||
return;
|
||||
bs2b->srate = srate;
|
||||
init(bs2b);
|
||||
} /* bs2b_set_srate */
|
||||
|
||||
int bs2b_get_srate(struct bs2b *bs2b)
|
||||
{
|
||||
return bs2b->srate;
|
||||
@@ -140,4 +129,59 @@ void bs2b_clear(struct bs2b *bs2b)
|
||||
memset(&bs2b->last_sample, 0, sizeof(bs2b->last_sample));
|
||||
} /* bs2b_clear */
|
||||
|
||||
extern inline void bs2b_cross_feed(struct bs2b *bs2b, float *restrict samples);
|
||||
void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, int SamplesToDo)
|
||||
{
|
||||
float lsamples[128][2];
|
||||
float rsamples[128][2];
|
||||
int base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
int todo = mini(128, SamplesToDo-base);
|
||||
int i;
|
||||
|
||||
/* Process left input */
|
||||
lsamples[0][0] = bs2b->a0_lo*Left[0] +
|
||||
bs2b->b1_lo*bs2b->last_sample[0].lo;
|
||||
lsamples[0][1] = bs2b->a0_hi*Left[0] +
|
||||
bs2b->a1_hi*bs2b->last_sample[0].asis +
|
||||
bs2b->b1_hi*bs2b->last_sample[0].hi;
|
||||
for(i = 1;i < todo;i++)
|
||||
{
|
||||
lsamples[i][0] = bs2b->a0_lo*Left[i] +
|
||||
bs2b->b1_lo*lsamples[i-1][0];
|
||||
lsamples[i][1] = bs2b->a0_hi*Left[i] +
|
||||
bs2b->a1_hi*Left[i-1] +
|
||||
bs2b->b1_hi*lsamples[i-1][1];
|
||||
}
|
||||
bs2b->last_sample[0].asis = Left[i-1];
|
||||
bs2b->last_sample[0].lo = lsamples[i-1][0];
|
||||
bs2b->last_sample[0].hi = lsamples[i-1][1];
|
||||
|
||||
/* Process right input */
|
||||
rsamples[0][0] = bs2b->a0_lo*Right[0] +
|
||||
bs2b->b1_lo*bs2b->last_sample[1].lo;
|
||||
rsamples[0][1] = bs2b->a0_hi*Right[0] +
|
||||
bs2b->a1_hi*bs2b->last_sample[1].asis +
|
||||
bs2b->b1_hi*bs2b->last_sample[1].hi;
|
||||
for(i = 1;i < todo;i++)
|
||||
{
|
||||
rsamples[i][0] = bs2b->a0_lo*Right[i] +
|
||||
bs2b->b1_lo*rsamples[i-1][0];
|
||||
rsamples[i][1] = bs2b->a0_hi*Right[i] +
|
||||
bs2b->a1_hi*Right[i-1] +
|
||||
bs2b->b1_hi*rsamples[i-1][1];
|
||||
}
|
||||
bs2b->last_sample[1].asis = Right[i-1];
|
||||
bs2b->last_sample[1].lo = rsamples[i-1][0];
|
||||
bs2b->last_sample[1].hi = rsamples[i-1][1];
|
||||
|
||||
/* Crossfeed */
|
||||
for(i = 0;i < todo;i++)
|
||||
*(Left++) = lsamples[i][1] + rsamples[i][0];
|
||||
for(i = 0;i < todo;i++)
|
||||
*(Right++) = rsamples[i][1] + lsamples[i][0];
|
||||
|
||||
base += todo;
|
||||
}
|
||||
} /* bs2b_cross_feed */
|
||||
File diff suppressed because it is too large
Load Diff
+25
@@ -1,6 +1,8 @@
|
||||
#ifndef AL_COMPAT_H
|
||||
#define AL_COMPAT_H
|
||||
|
||||
#include "alstring.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
@@ -23,10 +25,33 @@ FILE *al_fopen(const char *fname, const char *mode);
|
||||
|
||||
#endif
|
||||
|
||||
struct FileMapping {
|
||||
#ifdef _WIN32
|
||||
HANDLE file;
|
||||
HANDLE fmap;
|
||||
#else
|
||||
int fd;
|
||||
#endif
|
||||
void *ptr;
|
||||
size_t len;
|
||||
};
|
||||
struct FileMapping MapFileToMem(const char *fname);
|
||||
void UnmapFileMem(const struct FileMapping *mapping);
|
||||
|
||||
al_string GetProcPath(void);
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
void *LoadLib(const char *name);
|
||||
void CloseLib(void *handle);
|
||||
void *GetSymbol(void *handle, const char *name);
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define JCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
|
||||
#define JCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
|
||||
|
||||
/** Returns a JNIEnv*. */
|
||||
void *Android_GetJNIEnv(void);
|
||||
#endif
|
||||
|
||||
#endif /* AL_COMPAT_H */
|
||||
@@ -0,0 +1,466 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "converter.h"
|
||||
|
||||
#include "mixer_defs.h"
|
||||
|
||||
|
||||
SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate)
|
||||
{
|
||||
SampleConverter *converter;
|
||||
ALsizei step;
|
||||
|
||||
if(numchans <= 0 || srcRate <= 0 || dstRate <= 0)
|
||||
return NULL;
|
||||
|
||||
converter = al_calloc(16, FAM_SIZE(SampleConverter, Chan, numchans));
|
||||
converter->mSrcType = srcType;
|
||||
converter->mDstType = dstType;
|
||||
converter->mNumChannels = numchans;
|
||||
converter->mSrcTypeSize = BytesFromDevFmt(srcType);
|
||||
converter->mDstTypeSize = BytesFromDevFmt(dstType);
|
||||
|
||||
converter->mSrcPrepCount = 0;
|
||||
converter->mFracOffset = 0;
|
||||
|
||||
/* Have to set the mixer FPU mode since that's what the resampler code expects. */
|
||||
START_MIXER_MODE();
|
||||
step = fastf2i(minf((ALdouble)srcRate / dstRate, MAX_PITCH)*FRACTIONONE + 0.5f);
|
||||
converter->mIncrement = maxi(step, 1);
|
||||
if(converter->mIncrement == FRACTIONONE)
|
||||
converter->mResample = Resample_copy32_C;
|
||||
else
|
||||
{
|
||||
/* TODO: Allow other resamplers. */
|
||||
BsincPrepare(converter->mIncrement, &converter->mState.bsinc);
|
||||
converter->mResample = SelectResampler(BSincResampler);
|
||||
}
|
||||
END_MIXER_MODE();
|
||||
|
||||
return converter;
|
||||
}
|
||||
|
||||
void DestroySampleConverter(SampleConverter **converter)
|
||||
{
|
||||
if(converter)
|
||||
{
|
||||
al_free(*converter);
|
||||
*converter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline ALfloat Sample_ALbyte(ALbyte val)
|
||||
{ return val * (1.0f/128.0f); }
|
||||
static inline ALfloat Sample_ALubyte(ALubyte val)
|
||||
{ return Sample_ALbyte((ALint)val - 128); }
|
||||
|
||||
static inline ALfloat Sample_ALshort(ALshort val)
|
||||
{ return val * (1.0f/32768.0f); }
|
||||
static inline ALfloat Sample_ALushort(ALushort val)
|
||||
{ return Sample_ALshort((ALint)val - 32768); }
|
||||
|
||||
static inline ALfloat Sample_ALint(ALint val)
|
||||
{ return (val>>7) * (1.0f/16777216.0f); }
|
||||
static inline ALfloat Sample_ALuint(ALuint val)
|
||||
{ return Sample_ALint(val - INT_MAX - 1); }
|
||||
|
||||
static inline ALfloat Sample_ALfloat(ALfloat val)
|
||||
{ return val; }
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
static inline void Load_##T(ALfloat *restrict dst, const T *restrict src, \
|
||||
ALint srcstep, ALsizei samples) \
|
||||
{ \
|
||||
ALsizei i; \
|
||||
for(i = 0;i < samples;i++) \
|
||||
dst[i] = Sample_##T(src[i*srcstep]); \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALbyte)
|
||||
DECL_TEMPLATE(ALubyte)
|
||||
DECL_TEMPLATE(ALshort)
|
||||
DECL_TEMPLATE(ALushort)
|
||||
DECL_TEMPLATE(ALint)
|
||||
DECL_TEMPLATE(ALuint)
|
||||
DECL_TEMPLATE(ALfloat)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum DevFmtType srctype, ALsizei samples)
|
||||
{
|
||||
switch(srctype)
|
||||
{
|
||||
case DevFmtByte:
|
||||
Load_ALbyte(dst, src, srcstep, samples);
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
Load_ALubyte(dst, src, srcstep, samples);
|
||||
break;
|
||||
case DevFmtShort:
|
||||
Load_ALshort(dst, src, srcstep, samples);
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
Load_ALushort(dst, src, srcstep, samples);
|
||||
break;
|
||||
case DevFmtInt:
|
||||
Load_ALint(dst, src, srcstep, samples);
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
Load_ALuint(dst, src, srcstep, samples);
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
Load_ALfloat(dst, src, srcstep, samples);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline ALbyte ALbyte_Sample(ALfloat val)
|
||||
{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
|
||||
static inline ALubyte ALubyte_Sample(ALfloat val)
|
||||
{ return ALbyte_Sample(val)+128; }
|
||||
|
||||
static inline ALshort ALshort_Sample(ALfloat val)
|
||||
{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
|
||||
static inline ALushort ALushort_Sample(ALfloat val)
|
||||
{ return ALshort_Sample(val)+32768; }
|
||||
|
||||
static inline ALint ALint_Sample(ALfloat val)
|
||||
{ return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f)) << 7; }
|
||||
static inline ALuint ALuint_Sample(ALfloat val)
|
||||
{ return ALint_Sample(val)+INT_MAX+1; }
|
||||
|
||||
static inline ALfloat ALfloat_Sample(ALfloat val)
|
||||
{ return val; }
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
static inline void Store_##T(T *restrict dst, const ALfloat *restrict src, \
|
||||
ALint dststep, ALsizei samples) \
|
||||
{ \
|
||||
ALsizei i; \
|
||||
for(i = 0;i < samples;i++) \
|
||||
dst[i*dststep] = T##_Sample(src[i]); \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALbyte)
|
||||
DECL_TEMPLATE(ALubyte)
|
||||
DECL_TEMPLATE(ALshort)
|
||||
DECL_TEMPLATE(ALushort)
|
||||
DECL_TEMPLATE(ALint)
|
||||
DECL_TEMPLATE(ALuint)
|
||||
DECL_TEMPLATE(ALfloat)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static void StoreSamples(ALvoid *dst, const ALfloat *src, ALint dststep, enum DevFmtType dsttype, ALsizei samples)
|
||||
{
|
||||
switch(dsttype)
|
||||
{
|
||||
case DevFmtByte:
|
||||
Store_ALbyte(dst, src, dststep, samples);
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
Store_ALubyte(dst, src, dststep, samples);
|
||||
break;
|
||||
case DevFmtShort:
|
||||
Store_ALshort(dst, src, dststep, samples);
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
Store_ALushort(dst, src, dststep, samples);
|
||||
break;
|
||||
case DevFmtInt:
|
||||
Store_ALint(dst, src, dststep, samples);
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
Store_ALuint(dst, src, dststep, samples);
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
Store_ALfloat(dst, src, dststep, samples);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes)
|
||||
{
|
||||
ALint prepcount = converter->mSrcPrepCount;
|
||||
ALsizei increment = converter->mIncrement;
|
||||
ALsizei DataPosFrac = converter->mFracOffset;
|
||||
ALuint64 DataSize64;
|
||||
|
||||
if(prepcount < 0)
|
||||
{
|
||||
/* Negative prepcount means we need to skip that many input samples. */
|
||||
if(-prepcount >= srcframes)
|
||||
return 0;
|
||||
srcframes += prepcount;
|
||||
prepcount = 0;
|
||||
}
|
||||
|
||||
if(srcframes < 1)
|
||||
{
|
||||
/* No output samples if there's no input samples. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES &&
|
||||
MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= srcframes)
|
||||
{
|
||||
/* Not enough input samples to generate an output sample. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
DataSize64 = prepcount;
|
||||
DataSize64 += srcframes;
|
||||
DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
|
||||
DataSize64 <<= FRACTIONBITS;
|
||||
DataSize64 -= DataPosFrac;
|
||||
|
||||
/* If we have a full prep, we can generate at least one sample. */
|
||||
return (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
|
||||
}
|
||||
|
||||
|
||||
ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes)
|
||||
{
|
||||
const ALsizei SrcFrameSize = converter->mNumChannels * converter->mSrcTypeSize;
|
||||
const ALsizei DstFrameSize = converter->mNumChannels * converter->mDstTypeSize;
|
||||
const ALsizei increment = converter->mIncrement;
|
||||
ALsizei pos = 0;
|
||||
|
||||
START_MIXER_MODE();
|
||||
while(pos < dstframes && *srcframes > 0)
|
||||
{
|
||||
ALfloat *restrict SrcData = ASSUME_ALIGNED(converter->mSrcSamples, 16);
|
||||
ALfloat *restrict DstData = ASSUME_ALIGNED(converter->mDstSamples, 16);
|
||||
ALint prepcount = converter->mSrcPrepCount;
|
||||
ALsizei DataPosFrac = converter->mFracOffset;
|
||||
ALuint64 DataSize64;
|
||||
ALsizei DstSize;
|
||||
ALint toread;
|
||||
ALsizei chan;
|
||||
|
||||
if(prepcount < 0)
|
||||
{
|
||||
/* Negative prepcount means we need to skip that many input samples. */
|
||||
if(-prepcount >= *srcframes)
|
||||
{
|
||||
converter->mSrcPrepCount = prepcount + *srcframes;
|
||||
*srcframes = 0;
|
||||
break;
|
||||
}
|
||||
*src = (const ALbyte*)*src + SrcFrameSize*-prepcount;
|
||||
*srcframes += prepcount;
|
||||
converter->mSrcPrepCount = 0;
|
||||
continue;
|
||||
}
|
||||
toread = mini(*srcframes, BUFFERSIZE-(MAX_POST_SAMPLES+MAX_PRE_SAMPLES));
|
||||
|
||||
if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES &&
|
||||
MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= toread)
|
||||
{
|
||||
/* Not enough input samples to generate an output sample. Store
|
||||
* what we're given for later.
|
||||
*/
|
||||
for(chan = 0;chan < converter->mNumChannels;chan++)
|
||||
LoadSamples(&converter->Chan[chan].mPrevSamples[prepcount],
|
||||
(const ALbyte*)*src + converter->mSrcTypeSize*chan,
|
||||
converter->mNumChannels, converter->mSrcType, toread
|
||||
);
|
||||
|
||||
converter->mSrcPrepCount = prepcount + toread;
|
||||
*srcframes = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
DataSize64 = prepcount;
|
||||
DataSize64 += toread;
|
||||
DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
|
||||
DataSize64 <<= FRACTIONBITS;
|
||||
DataSize64 -= DataPosFrac;
|
||||
|
||||
/* If we have a full prep, we can generate at least one sample. */
|
||||
DstSize = (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
|
||||
DstSize = mini(DstSize, dstframes-pos);
|
||||
|
||||
for(chan = 0;chan < converter->mNumChannels;chan++)
|
||||
{
|
||||
const ALbyte *SrcSamples = (const ALbyte*)*src + converter->mSrcTypeSize*chan;
|
||||
ALbyte *DstSamples = (ALbyte*)dst + converter->mDstTypeSize*chan;
|
||||
const ALfloat *ResampledData;
|
||||
ALsizei SrcDataEnd;
|
||||
|
||||
/* Load the previous samples into the source data first, then the
|
||||
* new samples from the input buffer.
|
||||
*/
|
||||
memcpy(SrcData, converter->Chan[chan].mPrevSamples,
|
||||
prepcount*sizeof(ALfloat));
|
||||
LoadSamples(SrcData + prepcount, SrcSamples,
|
||||
converter->mNumChannels, converter->mSrcType, toread
|
||||
);
|
||||
|
||||
/* Store as many prep samples for next time as possible, given the
|
||||
* number of output samples being generated.
|
||||
*/
|
||||
SrcDataEnd = (DataPosFrac + increment*DstSize)>>FRACTIONBITS;
|
||||
if(SrcDataEnd >= prepcount+toread)
|
||||
memset(converter->Chan[chan].mPrevSamples, 0,
|
||||
sizeof(converter->Chan[chan].mPrevSamples));
|
||||
else
|
||||
{
|
||||
size_t len = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES, prepcount+toread-SrcDataEnd);
|
||||
memcpy(converter->Chan[chan].mPrevSamples, &SrcData[SrcDataEnd],
|
||||
len*sizeof(ALfloat));
|
||||
memset(converter->Chan[chan].mPrevSamples+len, 0,
|
||||
sizeof(converter->Chan[chan].mPrevSamples) - len*sizeof(ALfloat));
|
||||
}
|
||||
|
||||
/* Now resample, and store the result in the output buffer. */
|
||||
ResampledData = converter->mResample(&converter->mState,
|
||||
SrcData+MAX_PRE_SAMPLES, DataPosFrac, increment,
|
||||
DstData, DstSize
|
||||
);
|
||||
|
||||
StoreSamples(DstSamples, ResampledData, converter->mNumChannels,
|
||||
converter->mDstType, DstSize);
|
||||
}
|
||||
|
||||
/* Update the number of prep samples still available, as well as the
|
||||
* fractional offset.
|
||||
*/
|
||||
DataPosFrac += increment*DstSize;
|
||||
converter->mSrcPrepCount = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES,
|
||||
prepcount+toread-(DataPosFrac>>FRACTIONBITS));
|
||||
converter->mFracOffset = DataPosFrac & FRACTIONMASK;
|
||||
|
||||
/* Update the src and dst pointers in case there's still more to do. */
|
||||
*src = (const ALbyte*)*src + SrcFrameSize*(DataPosFrac>>FRACTIONBITS);
|
||||
*srcframes -= mini(*srcframes, (DataPosFrac>>FRACTIONBITS));
|
||||
|
||||
dst = (ALbyte*)dst + DstFrameSize*DstSize;
|
||||
pos += DstSize;
|
||||
}
|
||||
END_MIXER_MODE();
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
ChannelConverter *CreateChannelConverter(enum DevFmtType srcType, enum DevFmtChannels srcChans, enum DevFmtChannels dstChans)
|
||||
{
|
||||
ChannelConverter *converter;
|
||||
|
||||
if(srcChans != dstChans && !((srcChans == DevFmtMono && dstChans == DevFmtStereo) ||
|
||||
(srcChans == DevFmtStereo && dstChans == DevFmtMono)))
|
||||
return NULL;
|
||||
|
||||
converter = al_calloc(DEF_ALIGN, sizeof(*converter));
|
||||
converter->mSrcType = srcType;
|
||||
converter->mSrcChans = srcChans;
|
||||
converter->mDstChans = dstChans;
|
||||
|
||||
return converter;
|
||||
}
|
||||
|
||||
void DestroyChannelConverter(ChannelConverter **converter)
|
||||
{
|
||||
if(converter)
|
||||
{
|
||||
al_free(*converter);
|
||||
*converter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
static void Mono2Stereo##T(ALfloat *restrict dst, const T *src, ALsizei frames)\
|
||||
{ \
|
||||
ALsizei i; \
|
||||
for(i = 0;i < frames;i++) \
|
||||
dst[i*2 + 1] = dst[i*2 + 0] = Sample_##T(src[i]) * 0.707106781187f; \
|
||||
} \
|
||||
\
|
||||
static void Stereo2Mono##T(ALfloat *restrict dst, const T *src, ALsizei frames)\
|
||||
{ \
|
||||
ALsizei i; \
|
||||
for(i = 0;i < frames;i++) \
|
||||
dst[i] = (Sample_##T(src[i*2 + 0])+Sample_##T(src[i*2 + 1])) * \
|
||||
0.707106781187f; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALbyte)
|
||||
DECL_TEMPLATE(ALubyte)
|
||||
DECL_TEMPLATE(ALshort)
|
||||
DECL_TEMPLATE(ALushort)
|
||||
DECL_TEMPLATE(ALint)
|
||||
DECL_TEMPLATE(ALuint)
|
||||
DECL_TEMPLATE(ALfloat)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
void ChannelConverterInput(ChannelConverter *converter, const ALvoid *src, ALfloat *dst, ALsizei frames)
|
||||
{
|
||||
if(converter->mSrcChans == converter->mDstChans)
|
||||
{
|
||||
LoadSamples(dst, src, 1, converter->mSrcType,
|
||||
frames*ChannelsFromDevFmt(converter->mSrcChans, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
if(converter->mSrcChans == DevFmtStereo && converter->mDstChans == DevFmtMono)
|
||||
{
|
||||
switch(converter->mSrcType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
Stereo2MonoALbyte(dst, src, frames);
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
Stereo2MonoALubyte(dst, src, frames);
|
||||
break;
|
||||
case DevFmtShort:
|
||||
Stereo2MonoALshort(dst, src, frames);
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
Stereo2MonoALushort(dst, src, frames);
|
||||
break;
|
||||
case DevFmtInt:
|
||||
Stereo2MonoALint(dst, src, frames);
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
Stereo2MonoALuint(dst, src, frames);
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
Stereo2MonoALfloat(dst, src, frames);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else /*if(converter->mSrcChans == DevFmtMono && converter->mDstChans == DevFmtStereo)*/
|
||||
{
|
||||
switch(converter->mSrcType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
Mono2StereoALbyte(dst, src, frames);
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
Mono2StereoALubyte(dst, src, frames);
|
||||
break;
|
||||
case DevFmtShort:
|
||||
Mono2StereoALshort(dst, src, frames);
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
Mono2StereoALushort(dst, src, frames);
|
||||
break;
|
||||
case DevFmtInt:
|
||||
Mono2StereoALint(dst, src, frames);
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
Mono2StereoALuint(dst, src, frames);
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
Mono2StereoALfloat(dst, src, frames);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef CONVERTER_H
|
||||
#define CONVERTER_H
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
#ifdef __cpluspluc
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct SampleConverter {
|
||||
enum DevFmtType mSrcType;
|
||||
enum DevFmtType mDstType;
|
||||
ALsizei mNumChannels;
|
||||
ALsizei mSrcTypeSize;
|
||||
ALsizei mDstTypeSize;
|
||||
|
||||
ALint mSrcPrepCount;
|
||||
|
||||
ALsizei mFracOffset;
|
||||
ALsizei mIncrement;
|
||||
InterpState mState;
|
||||
ResamplerFunc mResample;
|
||||
|
||||
alignas(16) ALfloat mSrcSamples[BUFFERSIZE];
|
||||
alignas(16) ALfloat mDstSamples[BUFFERSIZE];
|
||||
|
||||
struct {
|
||||
alignas(16) ALfloat mPrevSamples[MAX_PRE_SAMPLES+MAX_POST_SAMPLES];
|
||||
} Chan[];
|
||||
} SampleConverter;
|
||||
|
||||
SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate);
|
||||
void DestroySampleConverter(SampleConverter **converter);
|
||||
|
||||
ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes);
|
||||
ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes);
|
||||
|
||||
|
||||
typedef struct ChannelConverter {
|
||||
enum DevFmtType mSrcType;
|
||||
enum DevFmtChannels mSrcChans;
|
||||
enum DevFmtChannels mDstChans;
|
||||
} ChannelConverter;
|
||||
|
||||
ChannelConverter *CreateChannelConverter(enum DevFmtType srcType, enum DevFmtChannels srcChans, enum DevFmtChannels dstChans);
|
||||
void DestroyChannelConverter(ChannelConverter **converter);
|
||||
|
||||
void ChannelConverterInput(ChannelConverter *converter, const ALvoid *src, ALfloat *dst, ALsizei frames);
|
||||
|
||||
#ifdef __cpluspluc
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONVERTER_H */
|
||||
+122
-109
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -39,14 +39,14 @@ typedef struct ALchorusState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat *SampleBuffer[2];
|
||||
ALuint BufferLength;
|
||||
ALuint offset;
|
||||
ALuint lfo_range;
|
||||
ALsizei BufferLength;
|
||||
ALsizei offset;
|
||||
ALsizei lfo_range;
|
||||
ALfloat lfo_scale;
|
||||
ALint lfo_disp;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
ALfloat Gain[2][MaxChannels];
|
||||
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* effect parameters */
|
||||
enum ChorusWaveForm waveform;
|
||||
@@ -55,27 +55,51 @@ typedef struct ALchorusState {
|
||||
ALfloat feedback;
|
||||
} ALchorusState;
|
||||
|
||||
static ALvoid ALchorusState_Destruct(ALchorusState *state)
|
||||
static ALvoid ALchorusState_Destruct(ALchorusState *state);
|
||||
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device);
|
||||
static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
|
||||
|
||||
|
||||
static void ALchorusState_Construct(ALchorusState *state)
|
||||
{
|
||||
free(state->SampleBuffer[0]);
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALchorusState, ALeffectState, state);
|
||||
|
||||
state->BufferLength = 0;
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
state->offset = 0;
|
||||
state->lfo_range = 1;
|
||||
state->waveform = CWF_Triangle;
|
||||
}
|
||||
|
||||
static ALvoid ALchorusState_Destruct(ALchorusState *state)
|
||||
{
|
||||
al_free(state->SampleBuffer[0]);
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
|
||||
{
|
||||
ALuint maxlen;
|
||||
ALuint it;
|
||||
ALsizei maxlen;
|
||||
ALsizei it;
|
||||
|
||||
maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1;
|
||||
maxlen = fastf2i(AL_CHORUS_MAX_DELAY * 2.0f * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
{
|
||||
void *temp;
|
||||
|
||||
temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
|
||||
void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2);
|
||||
if(!temp) return AL_FALSE;
|
||||
|
||||
al_free(state->SampleBuffer[0]);
|
||||
state->SampleBuffer[0] = temp;
|
||||
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
|
||||
|
||||
@@ -91,13 +115,14 @@ static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Dev
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)Device->Frequency;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat rate;
|
||||
ALint phase;
|
||||
|
||||
switch(Slot->EffectProps.Chorus.Waveform)
|
||||
switch(props->Chorus.Waveform)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM_TRIANGLE:
|
||||
state->waveform = CWF_Triangle;
|
||||
@@ -106,16 +131,19 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons
|
||||
state->waveform = CWF_Sinusoid;
|
||||
break;
|
||||
}
|
||||
state->depth = Slot->EffectProps.Chorus.Depth;
|
||||
state->feedback = Slot->EffectProps.Chorus.Feedback;
|
||||
state->delay = fastf2i(Slot->EffectProps.Chorus.Delay * frequency);
|
||||
state->feedback = props->Chorus.Feedback;
|
||||
state->delay = fastf2i(props->Chorus.Delay * frequency);
|
||||
/* The LFO depth is scaled to be relative to the sample delay. */
|
||||
state->depth = props->Chorus.Depth * state->delay;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
|
||||
ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
|
||||
CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]);
|
||||
CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]);
|
||||
|
||||
phase = Slot->EffectProps.Chorus.Phase;
|
||||
rate = Slot->EffectProps.Chorus.Rate;
|
||||
phase = props->Chorus.Phase;
|
||||
rate = props->Chorus.Rate;
|
||||
if(!(rate > 0.0f))
|
||||
{
|
||||
state->lfo_scale = 0.0f;
|
||||
@@ -125,127 +153,120 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons
|
||||
else
|
||||
{
|
||||
/* Calculate LFO coefficient */
|
||||
state->lfo_range = fastf2u(frequency/rate + 0.5f);
|
||||
state->lfo_range = fastf2i(frequency/rate + 0.5f);
|
||||
switch(state->waveform)
|
||||
{
|
||||
case CWF_Triangle:
|
||||
state->lfo_scale = 4.0f / state->lfo_range;
|
||||
break;
|
||||
case CWF_Sinusoid:
|
||||
state->lfo_scale = F_2PI / state->lfo_range;
|
||||
state->lfo_scale = F_TAU / state->lfo_range;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate lfo phase displacement */
|
||||
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
|
||||
if(phase >= 0)
|
||||
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
|
||||
else
|
||||
state->lfo_disp = fastf2i(state->lfo_range * ((360+phase)/360.0f));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
|
||||
static void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
|
||||
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
|
||||
const ALsizei todo)
|
||||
{
|
||||
ALfloat lfo_value;
|
||||
|
||||
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth * state->delay;
|
||||
*delay_left = fastf2i(lfo_value) + state->delay;
|
||||
|
||||
offset += state->lfo_disp;
|
||||
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth * state->delay;
|
||||
*delay_right = fastf2i(lfo_value) + state->delay;
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
|
||||
static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
|
||||
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
|
||||
const ALsizei todo)
|
||||
{
|
||||
ALfloat lfo_value;
|
||||
|
||||
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth * state->delay;
|
||||
*delay_left = fastf2i(lfo_value) + state->delay;
|
||||
|
||||
offset += state->lfo_disp;
|
||||
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth * state->delay;
|
||||
*delay_right = fastf2i(lfo_value) + state->delay;
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Func) \
|
||||
static void Process##Func(ALchorusState *state, const ALuint SamplesToDo, \
|
||||
const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
|
||||
{ \
|
||||
const ALuint bufmask = state->BufferLength-1; \
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
|
||||
ALuint offset = state->offset; \
|
||||
const ALfloat feedback = state->feedback; \
|
||||
ALuint it; \
|
||||
\
|
||||
for(it = 0;it < SamplesToDo;it++) \
|
||||
{ \
|
||||
ALint delay_left, delay_right; \
|
||||
Func(&delay_left, &delay_right, offset, state); \
|
||||
\
|
||||
out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
|
||||
leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \
|
||||
\
|
||||
out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
|
||||
rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \
|
||||
\
|
||||
offset++; \
|
||||
} \
|
||||
state->offset = offset; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(Triangle)
|
||||
DECL_TEMPLATE(Sinusoid)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALuint it, kt;
|
||||
ALuint base;
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0];
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1];
|
||||
const ALsizei bufmask = state->BufferLength-1;
|
||||
const ALfloat feedback = state->feedback;
|
||||
ALsizei offset = state->offset;
|
||||
ALsizei i, c;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64][2];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
const ALsizei todo = mini(128, SamplesToDo-base);
|
||||
ALfloat temps[128][2];
|
||||
ALint moddelays[2][128];
|
||||
|
||||
switch(state->waveform)
|
||||
{
|
||||
case CWF_Triangle:
|
||||
ProcessTriangle(state, td, SamplesIn+base, temps);
|
||||
GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
case CWF_Sinusoid:
|
||||
ProcessSinusoid(state, td, SamplesIn+base, temps);
|
||||
GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
}
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][kt];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
leftbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
|
||||
leftbuf[offset&bufmask] += temps[i][0];
|
||||
|
||||
rightbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
|
||||
rightbuf[offset&bufmask] += temps[i][1];
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(c = 0;c < NumChannels;c++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][0] * gain;
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][kt];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
gain = state->Gain[1][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][1] * gain;
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][1] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
base += td;
|
||||
base += todo;
|
||||
}
|
||||
|
||||
state->offset = offset;
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
|
||||
|
||||
|
||||
typedef struct ALchorusStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
@@ -255,16 +276,8 @@ static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(f
|
||||
{
|
||||
ALchorusState *state;
|
||||
|
||||
state = ALchorusState_New(sizeof(*state));
|
||||
NEW_OBJ0(state, ALchorusState)();
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALchorusState, ALeffectState, state);
|
||||
|
||||
state->BufferLength = 0;
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
state->offset = 0;
|
||||
state->lfo_range = 1;
|
||||
state->waveform = CWF_Triangle;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
+79
-45
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,7 @@ typedef struct ALcompressorState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MaxChannels];
|
||||
ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
ALboolean Enabled;
|
||||
@@ -40,8 +40,29 @@ typedef struct ALcompressorState {
|
||||
ALfloat GainCtrl;
|
||||
} ALcompressorState;
|
||||
|
||||
static ALvoid ALcompressorState_Destruct(ALcompressorState *UNUSED(state))
|
||||
static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
|
||||
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device);
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
|
||||
|
||||
|
||||
static void ALcompressorState_Construct(ALcompressorState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALcompressorState, ALeffectState, state);
|
||||
|
||||
state->Enabled = AL_TRUE;
|
||||
state->AttackRate = 0.0f;
|
||||
state->ReleaseRate = 0.0f;
|
||||
state->GainCtrl = 1.0f;
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
|
||||
@@ -55,88 +76,107 @@ static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdev
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
ALfloat gain;
|
||||
ALuint i;
|
||||
|
||||
state->Enabled = Slot->EffectProps.Compressor.OnOff;
|
||||
state->Enabled = props->Compressor.OnOff;
|
||||
|
||||
gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
|
||||
SetGains(Device, gain, state->Gain);
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < 4;i++)
|
||||
ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Gain[i]);
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALuint it, kt;
|
||||
ALuint base;
|
||||
ALsizei i, j, k;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
ALfloat temps[64][4];
|
||||
ALsizei td = mini(64, SamplesToDo-base);
|
||||
|
||||
/* Load samples into the temp buffer first. */
|
||||
for(j = 0;j < 4;j++)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
temps[i][j] = SamplesIn[j][i+base];
|
||||
}
|
||||
|
||||
if(state->Enabled)
|
||||
{
|
||||
ALfloat output, smp, amplitude;
|
||||
ALfloat gain = state->GainCtrl;
|
||||
ALfloat output, amplitude;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
for(i = 0;i < td;i++)
|
||||
{
|
||||
smp = SamplesIn[it+base];
|
||||
|
||||
amplitude = fabsf(smp);
|
||||
/* Roughly calculate the maximum amplitude from the 4-channel
|
||||
* signal, and attack or release the gain control to reach it.
|
||||
*/
|
||||
amplitude = fabsf(temps[i][0]);
|
||||
amplitude = maxf(amplitude + fabsf(temps[i][1]),
|
||||
maxf(amplitude + fabsf(temps[i][2]),
|
||||
amplitude + fabsf(temps[i][3])));
|
||||
if(amplitude > gain)
|
||||
gain = minf(gain+state->AttackRate, amplitude);
|
||||
else if(amplitude < gain)
|
||||
gain = maxf(gain-state->ReleaseRate, amplitude);
|
||||
output = 1.0f / clampf(gain, 0.5f, 2.0f);
|
||||
|
||||
temps[it] = smp * output;
|
||||
/* Apply the inverse of the gain control to normalize/compress
|
||||
* the volume. */
|
||||
output = 1.0f / clampf(gain, 0.5f, 2.0f);
|
||||
for(j = 0;j < 4;j++)
|
||||
temps[i][j] *= output;
|
||||
}
|
||||
|
||||
state->GainCtrl = gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALfloat output, smp, amplitude;
|
||||
ALfloat gain = state->GainCtrl;
|
||||
ALfloat output, amplitude;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
for(i = 0;i < td;i++)
|
||||
{
|
||||
smp = SamplesIn[it+base];
|
||||
|
||||
/* Same as above, except the amplitude is forced to 1. This
|
||||
* helps ensure smooth gain changes when the compressor is
|
||||
* turned on and off.
|
||||
*/
|
||||
amplitude = 1.0f;
|
||||
if(amplitude > gain)
|
||||
gain = minf(gain+state->AttackRate, amplitude);
|
||||
else if(amplitude < gain)
|
||||
gain = maxf(gain-state->ReleaseRate, amplitude);
|
||||
output = 1.0f / clampf(gain, 0.5f, 2.0f);
|
||||
|
||||
temps[it] = smp * output;
|
||||
output = 1.0f / clampf(gain, 0.5f, 2.0f);
|
||||
for(j = 0;j < 4;j++)
|
||||
temps[i][j] *= output;
|
||||
}
|
||||
|
||||
state->GainCtrl = gain;
|
||||
}
|
||||
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
/* Now mix to the output. */
|
||||
for(j = 0;j < 4;j++)
|
||||
{
|
||||
ALfloat gain = state->Gain[kt];
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
ALfloat gain = state->Gain[j][k];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * temps[it];
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][base+i] += gain * temps[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
|
||||
|
||||
|
||||
typedef struct ALcompressorStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
@@ -146,14 +186,8 @@ static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *
|
||||
{
|
||||
ALcompressorState *state;
|
||||
|
||||
state = ALcompressorState_New(sizeof(*state));
|
||||
NEW_OBJ0(state, ALcompressorState)();
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALcompressorState, ALeffectState, state);
|
||||
|
||||
state->Enabled = AL_TRUE;
|
||||
state->AttackRate = 0.0f;
|
||||
state->ReleaseRate = 0.0f;
|
||||
state->GainCtrl = 1.0f;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
+68
-29
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -32,12 +32,32 @@
|
||||
typedef struct ALdedicatedState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat gains[MaxChannels];
|
||||
ALfloat gains[MAX_OUTPUT_CHANNELS];
|
||||
} ALdedicatedState;
|
||||
|
||||
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state);
|
||||
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *device);
|
||||
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
|
||||
|
||||
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *UNUSED(state))
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
|
||||
|
||||
|
||||
static void ALdedicatedState_Construct(ALdedicatedState *state)
|
||||
{
|
||||
ALsizei s;
|
||||
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALdedicatedState, ALeffectState, state);
|
||||
|
||||
for(s = 0;s < MAX_OUTPUT_CHANNELS;s++)
|
||||
state->gains[s] = 0.0f;
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
@@ -45,41 +65,65 @@ static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state),
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot)
|
||||
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
{
|
||||
ALfloat Gain;
|
||||
ALsizei s;
|
||||
ALuint i;
|
||||
|
||||
Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain;
|
||||
if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
|
||||
ComputeAngleGains(device, atan2f(0.0f, 1.0f), 0.0f, Gain, state->gains);
|
||||
else if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
state->gains[i] = 0.0f;
|
||||
|
||||
Gain = Slot->Params.Gain * props->Dedicated.Gain;
|
||||
if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
|
||||
{
|
||||
for(s = 0;s < MaxChannels;s++)
|
||||
state->gains[s] = 0.0f;
|
||||
state->gains[LFE] = Gain;
|
||||
int idx;
|
||||
if((idx=GetChannelIdxByName(device->RealOut, LFE)) != -1)
|
||||
{
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
|
||||
state->gains[idx] = Gain;
|
||||
}
|
||||
}
|
||||
else if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
|
||||
{
|
||||
int idx;
|
||||
/* Dialog goes to the front-center speaker if it exists, otherwise it
|
||||
* plays from the front-center location. */
|
||||
if((idx=GetChannelIdxByName(device->RealOut, FrontCenter)) != -1)
|
||||
{
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
|
||||
state->gains[idx] = Gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels;
|
||||
ComputePanningGains(device->Dry, coeffs, Gain, state->gains);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
const ALfloat *gains = state->gains;
|
||||
ALuint i, c;
|
||||
ALsizei i, c;
|
||||
|
||||
for(c = 0;c < MaxChannels;c++)
|
||||
SamplesIn = ASSUME_ALIGNED(SamplesIn, 16);
|
||||
SamplesOut = ASSUME_ALIGNED(SamplesOut, 16);
|
||||
for(c = 0;c < NumChannels;c++)
|
||||
{
|
||||
if(!(gains[c] > GAIN_SILENCE_THRESHOLD))
|
||||
const ALfloat gain = state->gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
SamplesOut[c][i] = SamplesIn[i] * gains[c];
|
||||
SamplesOut[c][i] += SamplesIn[0][i] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
|
||||
|
||||
|
||||
typedef struct ALdedicatedStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
@@ -88,14 +132,9 @@ typedef struct ALdedicatedStateFactory {
|
||||
ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALdedicatedState *state;
|
||||
ALsizei s;
|
||||
|
||||
state = ALdedicatedState_New(sizeof(*state));
|
||||
NEW_OBJ0(state, ALdedicatedState)();
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALdedicatedState, ALeffectState, state);
|
||||
|
||||
for(s = 0;s < MaxChannels;s++)
|
||||
state->gains[s] = 0.0f;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
+90
-90
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,7 @@ typedef struct ALdistortionState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MaxChannels];
|
||||
ALfloat Gain[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
ALfilterState lowpass;
|
||||
@@ -43,8 +43,27 @@ typedef struct ALdistortionState {
|
||||
ALfloat edge_coeff;
|
||||
} ALdistortionState;
|
||||
|
||||
static ALvoid ALdistortionState_Destruct(ALdistortionState *UNUSED(state))
|
||||
static ALvoid ALdistortionState_Destruct(ALdistortionState *state);
|
||||
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device);
|
||||
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
|
||||
|
||||
|
||||
static void ALdistortionState_Construct(ALdistortionState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALdistortionState, ALeffectState, state);
|
||||
|
||||
ALfilterState_clear(&state->lowpass);
|
||||
ALfilterState_clear(&state->bandpass);
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
@@ -52,128 +71,113 @@ static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state)
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)Device->Frequency;
|
||||
ALfloat bandwidth;
|
||||
ALfloat cutoff;
|
||||
ALfloat edge;
|
||||
ALfloat gain;
|
||||
|
||||
/* Store distorted signal attenuation settings */
|
||||
state->attenuation = Slot->EffectProps.Distortion.Gain;
|
||||
/* Store distorted signal attenuation settings. */
|
||||
state->attenuation = props->Distortion.Gain;
|
||||
|
||||
/* Store waveshaper edge settings */
|
||||
edge = sinf(Slot->EffectProps.Distortion.Edge * (F_PI_2));
|
||||
/* Store waveshaper edge settings. */
|
||||
edge = sinf(props->Distortion.Edge * (F_PI_2));
|
||||
edge = minf(edge, 0.99f);
|
||||
state->edge_coeff = 2.0f * edge / (1.0f-edge);
|
||||
|
||||
/* Lowpass filter */
|
||||
cutoff = Slot->EffectProps.Distortion.LowpassCutoff;
|
||||
/* Bandwidth value is constant in octaves */
|
||||
cutoff = props->Distortion.LowpassCutoff;
|
||||
/* Bandwidth value is constant in octaves. */
|
||||
bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
|
||||
/* Multiply sampling frequency by the amount of oversampling done during
|
||||
* processing.
|
||||
*/
|
||||
ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), bandwidth);
|
||||
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
);
|
||||
|
||||
/* Bandpass filter */
|
||||
cutoff = Slot->EffectProps.Distortion.EQCenter;
|
||||
/* Convert bandwidth in Hz to octaves */
|
||||
bandwidth = Slot->EffectProps.Distortion.EQBandwidth / (cutoff * 0.67f);
|
||||
cutoff = props->Distortion.EQCenter;
|
||||
/* Convert bandwidth in Hz to octaves. */
|
||||
bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
|
||||
ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), bandwidth);
|
||||
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
);
|
||||
|
||||
gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
|
||||
SetGains(Device, gain, state->Gain);
|
||||
ComputeAmbientGains(Device->Dry, Slot->Params.Gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
const ALfloat fc = state->edge_coeff;
|
||||
float oversample_buffer[64][4];
|
||||
ALuint base;
|
||||
ALuint it;
|
||||
ALuint ot;
|
||||
ALuint kt;
|
||||
ALsizei it, kt;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
float buffer[2][64 * 4];
|
||||
ALsizei td = mini(64, SamplesToDo-base);
|
||||
|
||||
/* Perform 4x oversampling to avoid aliasing. */
|
||||
/* Oversampling greatly improves distortion */
|
||||
/* quality and allows to implement lowpass and */
|
||||
/* bandpass filters using high frequencies, at */
|
||||
/* which classic IIR filters became unstable. */
|
||||
/* Perform 4x oversampling to avoid aliasing. Oversampling greatly
|
||||
* improves distortion quality and allows to implement lowpass and
|
||||
* bandpass filters using high frequencies, at which classic IIR
|
||||
* filters became unstable.
|
||||
*/
|
||||
|
||||
/* Fill oversample buffer using zero stuffing */
|
||||
/* Fill oversample buffer using zero stuffing. */
|
||||
for(it = 0;it < td;it++)
|
||||
{
|
||||
oversample_buffer[it][0] = SamplesIn[it+base];
|
||||
oversample_buffer[it][1] = 0.0f;
|
||||
oversample_buffer[it][2] = 0.0f;
|
||||
oversample_buffer[it][3] = 0.0f;
|
||||
/* Multiply the sample by the amount of oversampling to maintain
|
||||
* the signal's power.
|
||||
*/
|
||||
buffer[0][it*4 + 0] = SamplesIn[0][it+base] * 4.0f;
|
||||
buffer[0][it*4 + 1] = 0.0f;
|
||||
buffer[0][it*4 + 2] = 0.0f;
|
||||
buffer[0][it*4 + 3] = 0.0f;
|
||||
}
|
||||
|
||||
/* First step, do lowpass filtering of original signal, */
|
||||
/* additionally perform buffer interpolation and lowpass */
|
||||
/* cutoff for oversampling (which is fortunately first */
|
||||
/* step of distortion). So combine three operations into */
|
||||
/* the one. */
|
||||
for(it = 0;it < td;it++)
|
||||
{
|
||||
for(ot = 0;ot < 4;ot++)
|
||||
{
|
||||
ALfloat smp;
|
||||
smp = ALfilterState_processSingle(&state->lowpass, oversample_buffer[it][ot]);
|
||||
/* First step, do lowpass filtering of original signal. Additionally
|
||||
* perform buffer interpolation and lowpass cutoff for oversampling
|
||||
* (which is fortunately first step of distortion). So combine three
|
||||
* operations into the one.
|
||||
*/
|
||||
ALfilterState_process(&state->lowpass, buffer[1], buffer[0], td*4);
|
||||
|
||||
/* Restore signal power by multiplying sample by amount of oversampling */
|
||||
oversample_buffer[it][ot] = smp * 4.0f;
|
||||
}
|
||||
/* Second step, do distortion using waveshaper function to emulate
|
||||
* signal processing during tube overdriving. Three steps of
|
||||
* waveshaping are intended to modify waveform without boost/clipping/
|
||||
* attenuation process.
|
||||
*/
|
||||
for(it = 0;it < td*4;it++)
|
||||
{
|
||||
ALfloat smp = buffer[1][it];
|
||||
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
|
||||
|
||||
buffer[0][it] = smp;
|
||||
}
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
/* Third step, do bandpass filtering of distorted signal. */
|
||||
ALfilterState_process(&state->bandpass, buffer[1], buffer[0], td*4);
|
||||
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
{
|
||||
/* Second step, do distortion using waveshaper function */
|
||||
/* to emulate signal processing during tube overdriving. */
|
||||
/* Three steps of waveshaping are intended to modify */
|
||||
/* waveform without boost/clipping/attenuation process. */
|
||||
for(ot = 0;ot < 4;ot++)
|
||||
{
|
||||
ALfloat smp = oversample_buffer[it][ot];
|
||||
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
|
||||
|
||||
/* Third step, do bandpass filtering of distorted signal */
|
||||
smp = ALfilterState_processSingle(&state->bandpass, smp);
|
||||
oversample_buffer[it][ot] = smp;
|
||||
}
|
||||
|
||||
/* Fourth step, final, do attenuation and perform decimation, */
|
||||
/* store only one sample out of 4. */
|
||||
temps[it] = oversample_buffer[it][0] * state->attenuation;
|
||||
}
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[kt];
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
/* Fourth step, final, do attenuation and perform decimation,
|
||||
* store only one sample out of 4.
|
||||
*/
|
||||
ALfloat gain = state->Gain[kt] * state->attenuation;
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * temps[it];
|
||||
SamplesOut[kt][base+it] += gain * buffer[1][it*4];
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
|
||||
|
||||
|
||||
typedef struct ALdistortionStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
@@ -183,12 +187,8 @@ static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *
|
||||
{
|
||||
ALdistortionState *state;
|
||||
|
||||
state = ALdistortionState_New(sizeof(*state));
|
||||
NEW_OBJ0(state, ALdistortionState)();
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALdistortionState, ALeffectState, state);
|
||||
|
||||
ALfilterState_clear(&state->lowpass);
|
||||
ALfilterState_clear(&state->bandpass);
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
+159
-126
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -34,148 +34,34 @@ typedef struct ALechoState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat *SampleBuffer;
|
||||
ALuint BufferLength;
|
||||
ALsizei BufferLength;
|
||||
|
||||
// The echo is two tap. The delay is the number of samples from before the
|
||||
// current offset
|
||||
struct {
|
||||
ALuint delay;
|
||||
ALsizei delay;
|
||||
} Tap[2];
|
||||
ALuint Offset;
|
||||
ALsizei Offset;
|
||||
/* The panning gains for the two taps */
|
||||
ALfloat Gain[2][MaxChannels];
|
||||
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
|
||||
|
||||
ALfloat FeedGain;
|
||||
|
||||
ALfilterState Filter;
|
||||
} ALechoState;
|
||||
|
||||
static ALvoid ALechoState_Destruct(ALechoState *state)
|
||||
{
|
||||
free(state->SampleBuffer);
|
||||
state->SampleBuffer = NULL;
|
||||
}
|
||||
|
||||
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
|
||||
{
|
||||
ALuint maxlen, i;
|
||||
|
||||
// Use the next power of 2 for the buffer length, so the tap offsets can be
|
||||
// wrapped using a mask instead of a modulo
|
||||
maxlen = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
|
||||
maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
{
|
||||
void *temp;
|
||||
|
||||
temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
|
||||
if(!temp) return AL_FALSE;
|
||||
state->SampleBuffer = temp;
|
||||
state->BufferLength = maxlen;
|
||||
}
|
||||
for(i = 0;i < state->BufferLength;i++)
|
||||
state->SampleBuffer[i] = 0.0f;
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALuint frequency = Device->Frequency;
|
||||
ALfloat lrpan, gain;
|
||||
ALfloat dirGain;
|
||||
|
||||
state->Tap[0].delay = fastf2u(Slot->EffectProps.Echo.Delay * frequency) + 1;
|
||||
state->Tap[1].delay = fastf2u(Slot->EffectProps.Echo.LRDelay * frequency);
|
||||
state->Tap[1].delay += state->Tap[0].delay;
|
||||
|
||||
lrpan = Slot->EffectProps.Echo.Spread;
|
||||
|
||||
state->FeedGain = Slot->EffectProps.Echo.Feedback;
|
||||
|
||||
ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
|
||||
1.0f - Slot->EffectProps.Echo.Damping,
|
||||
LOWPASSFREQREF/frequency, 0.0f);
|
||||
|
||||
gain = Slot->Gain;
|
||||
dirGain = fabsf(lrpan);
|
||||
|
||||
/* First tap panning */
|
||||
ComputeAngleGains(Device, atan2f(-lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[0]);
|
||||
|
||||
/* Second tap panning */
|
||||
ComputeAngleGains(Device, atan2f(+lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[1]);
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
{
|
||||
const ALuint mask = state->BufferLength-1;
|
||||
const ALuint tap1 = state->Tap[0].delay;
|
||||
const ALuint tap2 = state->Tap[1].delay;
|
||||
ALuint offset = state->Offset;
|
||||
ALfloat smp;
|
||||
ALuint base;
|
||||
ALuint i, k;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64][2];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
{
|
||||
/* First tap */
|
||||
temps[i][0] = state->SampleBuffer[(offset-tap1) & mask];
|
||||
/* Second tap */
|
||||
temps[i][1] = state->SampleBuffer[(offset-tap2) & mask];
|
||||
|
||||
// Apply damping and feedback gain to the second tap, and mix in the
|
||||
// new sample
|
||||
smp = ALfilterState_processSingle(&state->Filter, temps[i][1]+SamplesIn[i+base]);
|
||||
state->SampleBuffer[offset&mask] = smp * state->FeedGain;
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(k = 0;k < MaxChannels;k++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][k];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][i+base] += temps[i][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][k];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][i+base] += temps[i][1] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
|
||||
state->Offset = offset;
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_Destruct(ALechoState *state);
|
||||
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device);
|
||||
static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALechoState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
|
||||
|
||||
|
||||
typedef struct ALechoStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALechoStateFactory;
|
||||
|
||||
ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
|
||||
static void ALechoState_Construct(ALechoState *state)
|
||||
{
|
||||
ALechoState *state;
|
||||
|
||||
state = ALechoState_New(sizeof(*state));
|
||||
if(!state) return NULL;
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALechoState, ALeffectState, state);
|
||||
|
||||
state->BufferLength = 0;
|
||||
@@ -186,6 +72,153 @@ ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
|
||||
state->Offset = 0;
|
||||
|
||||
ALfilterState_clear(&state->Filter);
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_Destruct(ALechoState *state)
|
||||
{
|
||||
al_free(state->SampleBuffer);
|
||||
state->SampleBuffer = NULL;
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
|
||||
{
|
||||
ALsizei maxlen, i;
|
||||
|
||||
// Use the next power of 2 for the buffer length, so the tap offsets can be
|
||||
// wrapped using a mask instead of a modulo
|
||||
maxlen = fastf2i(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
|
||||
maxlen += fastf2i(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
{
|
||||
void *temp = al_calloc(16, maxlen * sizeof(ALfloat));
|
||||
if(!temp) return AL_FALSE;
|
||||
|
||||
al_free(state->SampleBuffer);
|
||||
state->SampleBuffer = temp;
|
||||
state->BufferLength = maxlen;
|
||||
}
|
||||
for(i = 0;i < state->BufferLength;i++)
|
||||
state->SampleBuffer[i] = 0.0f;
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
{
|
||||
ALuint frequency = Device->Frequency;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat gain, lrpan, spread;
|
||||
|
||||
state->Tap[0].delay = fastf2i(props->Echo.Delay * frequency) + 1;
|
||||
state->Tap[1].delay = fastf2i(props->Echo.LRDelay * frequency);
|
||||
state->Tap[1].delay += state->Tap[0].delay;
|
||||
|
||||
spread = props->Echo.Spread;
|
||||
if(spread < 0.0f) lrpan = -1.0f;
|
||||
else lrpan = 1.0f;
|
||||
/* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage
|
||||
* spread (where 0 = point, tau = omni).
|
||||
*/
|
||||
spread = asinf(1.0f - fabsf(spread))*4.0f;
|
||||
|
||||
state->FeedGain = props->Echo.Feedback;
|
||||
|
||||
gain = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
|
||||
ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
|
||||
gain, LOWPASSFREQREF/frequency,
|
||||
calc_rcpQ_from_slope(gain, 1.0f));
|
||||
|
||||
gain = Slot->Params.Gain;
|
||||
|
||||
/* First tap panning */
|
||||
CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[0]);
|
||||
|
||||
/* Second tap panning */
|
||||
CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[1]);
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
const ALsizei mask = state->BufferLength-1;
|
||||
const ALsizei tap1 = state->Tap[0].delay;
|
||||
const ALsizei tap2 = state->Tap[1].delay;
|
||||
ALsizei offset = state->Offset;
|
||||
ALfloat x[2], y[2], in, out;
|
||||
ALsizei base, k;
|
||||
ALsizei i;
|
||||
|
||||
x[0] = state->Filter.x[0];
|
||||
x[1] = state->Filter.x[1];
|
||||
y[0] = state->Filter.y[0];
|
||||
y[1] = state->Filter.y[1];
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[128][2];
|
||||
ALsizei td = mini(128, SamplesToDo-base);
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
{
|
||||
/* First tap */
|
||||
temps[i][0] = state->SampleBuffer[(offset-tap1) & mask];
|
||||
/* Second tap */
|
||||
temps[i][1] = state->SampleBuffer[(offset-tap2) & mask];
|
||||
|
||||
// Apply damping and feedback gain to the second tap, and mix in the
|
||||
// new sample
|
||||
in = temps[i][1] + SamplesIn[0][i+base];
|
||||
out = in*state->Filter.b0 +
|
||||
x[0]*state->Filter.b1 + x[1]*state->Filter.b2 -
|
||||
y[0]*state->Filter.a1 - y[1]*state->Filter.a2;
|
||||
x[1] = x[0]; x[0] = in;
|
||||
y[1] = y[0]; y[0] = out;
|
||||
|
||||
state->SampleBuffer[offset&mask] = out * state->FeedGain;
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][k];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][i+base] += temps[i][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][k];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][i+base] += temps[i][1] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
state->Filter.x[0] = x[0];
|
||||
state->Filter.x[1] = x[1];
|
||||
state->Filter.y[0] = y[0];
|
||||
state->Filter.y[1] = y[1];
|
||||
|
||||
state->Offset = offset;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALechoStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALechoStateFactory;
|
||||
|
||||
ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALechoState *state;
|
||||
|
||||
NEW_OBJ0(state, ALechoState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
+106
-60
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -71,18 +71,50 @@
|
||||
* filter coefficients" by Robert Bristow-Johnson *
|
||||
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
|
||||
|
||||
|
||||
/* The maximum number of sample frames per update. */
|
||||
#define MAX_UPDATE_SAMPLES 256
|
||||
|
||||
typedef struct ALequalizerState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MaxChannels];
|
||||
ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
ALfilterState filter[4];
|
||||
ALfilterState filter[4][MAX_EFFECT_CHANNELS];
|
||||
|
||||
ALfloat SampleBuffer[4][MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES];
|
||||
} ALequalizerState;
|
||||
|
||||
static ALvoid ALequalizerState_Destruct(ALequalizerState *UNUSED(state))
|
||||
static ALvoid ALequalizerState_Destruct(ALequalizerState *state);
|
||||
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device);
|
||||
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
|
||||
|
||||
|
||||
static void ALequalizerState_Construct(ALequalizerState *state)
|
||||
{
|
||||
int it, ft;
|
||||
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALequalizerState, ALeffectState, state);
|
||||
|
||||
/* Initialize sample history only on filter creation to avoid */
|
||||
/* sound clicks if filter settings were changed in runtime. */
|
||||
for(it = 0; it < 4; it++)
|
||||
{
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_clear(&state->filter[it][ft]);
|
||||
}
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
@@ -90,75 +122,96 @@ static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state),
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_update(ALequalizerState *state, ALCdevice *device, const ALeffectslot *slot)
|
||||
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)device->Frequency;
|
||||
ALfloat gain = sqrtf(1.0f / device->NumChan) * slot->Gain;
|
||||
ALfloat gain, freq_mult;
|
||||
ALuint i;
|
||||
|
||||
SetGains(device, gain, state->Gain);
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Gain[i]);
|
||||
|
||||
/* Calculate coefficients for the each type of filter */
|
||||
ALfilterState_setParams(&state->filter[0], ALfilterType_LowShelf,
|
||||
sqrtf(slot->EffectProps.Equalizer.LowGain),
|
||||
slot->EffectProps.Equalizer.LowCutoff/frequency,
|
||||
0.0f);
|
||||
/* Calculate coefficients for the each type of filter. Note that the shelf
|
||||
* filters' gain is for the reference frequency, which is the centerpoint
|
||||
* of the transition band.
|
||||
*/
|
||||
gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
|
||||
freq_mult = props->Equalizer.LowCutoff/frequency;
|
||||
ALfilterState_setParams(&state->filter[0][0], ALfilterType_LowShelf,
|
||||
gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
);
|
||||
/* Copy the filter coefficients for the other input channels. */
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ALfilterState_copyParams(&state->filter[0][i], &state->filter[0][0]);
|
||||
|
||||
ALfilterState_setParams(&state->filter[1], ALfilterType_Peaking,
|
||||
sqrtf(slot->EffectProps.Equalizer.Mid1Gain),
|
||||
slot->EffectProps.Equalizer.Mid1Center/frequency,
|
||||
slot->EffectProps.Equalizer.Mid1Width);
|
||||
gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
|
||||
freq_mult = props->Equalizer.Mid1Center/frequency;
|
||||
ALfilterState_setParams(&state->filter[1][0], ALfilterType_Peaking,
|
||||
gain, freq_mult, calc_rcpQ_from_bandwidth(
|
||||
freq_mult, props->Equalizer.Mid1Width
|
||||
)
|
||||
);
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ALfilterState_copyParams(&state->filter[1][i], &state->filter[1][0]);
|
||||
|
||||
ALfilterState_setParams(&state->filter[2], ALfilterType_Peaking,
|
||||
sqrtf(slot->EffectProps.Equalizer.Mid2Gain),
|
||||
slot->EffectProps.Equalizer.Mid2Center/frequency,
|
||||
slot->EffectProps.Equalizer.Mid2Width);
|
||||
gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
|
||||
freq_mult = props->Equalizer.Mid2Center/frequency;
|
||||
ALfilterState_setParams(&state->filter[2][0], ALfilterType_Peaking,
|
||||
gain, freq_mult, calc_rcpQ_from_bandwidth(
|
||||
freq_mult, props->Equalizer.Mid2Width
|
||||
)
|
||||
);
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ALfilterState_copyParams(&state->filter[2][i], &state->filter[2][0]);
|
||||
|
||||
ALfilterState_setParams(&state->filter[3], ALfilterType_HighShelf,
|
||||
sqrtf(slot->EffectProps.Equalizer.HighGain),
|
||||
slot->EffectProps.Equalizer.HighCutoff/frequency,
|
||||
0.0f);
|
||||
gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
|
||||
freq_mult = props->Equalizer.HighCutoff/frequency;
|
||||
ALfilterState_setParams(&state->filter[3][0], ALfilterType_HighShelf,
|
||||
gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
);
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ALfilterState_copyParams(&state->filter[3][i], &state->filter[3][0]);
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALuint base;
|
||||
ALuint it;
|
||||
ALuint kt;
|
||||
ALuint ft;
|
||||
ALfloat (*Samples)[MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES] = state->SampleBuffer;
|
||||
ALsizei it, kt, ft;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_process(&state->filter[0][ft], Samples[0][ft], &SamplesIn[ft][base], td);
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_process(&state->filter[1][ft], Samples[1][ft], Samples[0][ft], td);
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_process(&state->filter[2][ft], Samples[2][ft], Samples[1][ft], td);
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_process(&state->filter[3][ft], Samples[3][ft], Samples[2][ft], td);
|
||||
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
{
|
||||
ALfloat smp = SamplesIn[base+it];
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[ft][kt];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(ft = 0;ft < 4;ft++)
|
||||
smp = ALfilterState_processSingle(&state->filter[ft], smp);
|
||||
|
||||
temps[it] = smp;
|
||||
}
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[kt];
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * temps[it];
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * Samples[3][ft][it];
|
||||
}
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
|
||||
|
||||
|
||||
typedef struct ALequalizerStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
@@ -167,16 +220,9 @@ typedef struct ALequalizerStateFactory {
|
||||
ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALequalizerState *state;
|
||||
int it;
|
||||
|
||||
state = ALequalizerState_New(sizeof(*state));
|
||||
NEW_OBJ0(state, ALequalizerState)();
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALequalizerState, ALeffectState, state);
|
||||
|
||||
/* Initialize sample history only on filter creation to avoid */
|
||||
/* sound clicks if filter settings were changed in runtime. */
|
||||
for(it = 0; it < 4; it++)
|
||||
ALfilterState_clear(&state->filter[it]);
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user