Added OpenAL-Soft 1.16.0.

This commit is contained in:
rude
2015-02-10 19:40:59 +01:00
parent 5afec7c793
commit 22245fc23f
147 changed files with 61030 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+232
View File
@@ -0,0 +1,232 @@
#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;
}
+133
View File
@@ -0,0 +1,133 @@
#ifndef AL_BACKENDS_BASE_H
#define AL_BACKENDS_BASE_H
#include "alMain.h"
#include "threads.h"
struct ALCbackendVtable;
typedef struct ALCbackend {
const struct ALCbackendVtable *vtbl;
ALCdevice *mDevice;
almtx_t mMutex;
} ALCbackend;
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device);
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);
void ALCbackend_lock(ALCbackend *self);
void ALCbackend_unlock(ALCbackend *self);
struct ALCbackendVtable {
void (*const Destruct)(ALCbackend*);
ALCenum (*const open)(ALCbackend*, const ALCchar*);
void (*const close)(ALCbackend*);
ALCboolean (*const reset)(ALCbackend*);
ALCboolean (*const start)(ALCbackend*);
void (*const stop)(ALCbackend*);
ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint);
ALCuint (*const availableSamples)(ALCbackend*);
ALint64 (*const getLatency)(ALCbackend*);
void (*const lock)(ALCbackend*);
void (*const unlock)(ALCbackend*);
void (*const Delete)(void*);
};
#define DEFINE_ALCBACKEND_VTABLE(T) \
DECLARE_THUNK(T, ALCbackend, void, Destruct) \
DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*) \
DECLARE_THUNK(T, ALCbackend, void, close) \
DECLARE_THUNK(T, ALCbackend, ALCboolean, reset) \
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, void, lock) \
DECLARE_THUNK(T, ALCbackend, void, unlock) \
static void T##_ALCbackend_Delete(void *ptr) \
{ T##_Delete(STATIC_UPCAST(T, ALCbackend, (ALCbackend*)ptr)); } \
\
static const struct ALCbackendVtable T##_ALCbackend_vtable = { \
T##_ALCbackend_Destruct, \
\
T##_ALCbackend_open, \
T##_ALCbackend_close, \
T##_ALCbackend_reset, \
T##_ALCbackend_start, \
T##_ALCbackend_stop, \
T##_ALCbackend_captureSamples, \
T##_ALCbackend_availableSamples, \
T##_ALCbackend_getLatency, \
T##_ALCbackend_lock, \
T##_ALCbackend_unlock, \
\
T##_ALCbackend_Delete, \
}
typedef enum ALCbackend_Type {
ALCbackend_Playback,
ALCbackend_Capture,
ALCbackend_Loopback
} ALCbackend_Type;
struct ALCbackendFactoryVtable;
typedef struct ALCbackendFactory {
const struct ALCbackendFactoryVtable *vtbl;
} ALCbackendFactory;
void ALCbackendFactory_deinit(ALCbackendFactory *self);
struct ALCbackendFactoryVtable {
ALCboolean (*const init)(ALCbackendFactory *self);
void (*const deinit)(ALCbackendFactory *self);
ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type);
void (*const probe)(ALCbackendFactory *self, enum DevProbe type);
ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type);
};
#define DEFINE_ALCBACKENDFACTORY_VTABLE(T) \
DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init) \
DECLARE_THUNK(T, ALCbackendFactory, void, deinit) \
DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \
DECLARE_THUNK1(T, ALCbackendFactory, void, probe, enum DevProbe) \
DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \
\
static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
T##_ALCbackendFactory_init, \
T##_ALCbackendFactory_deinit, \
T##_ALCbackendFactory_querySupport, \
T##_ALCbackendFactory_probe, \
T##_ALCbackendFactory_createBackend, \
}
ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type);
#endif /* AL_BACKENDS_BASE_H */
+707
View File
@@ -0,0 +1,707 @@
/**
* 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 <alloca.h>
#include "alMain.h"
#include "alu.h"
#include <CoreServices/CoreServices.h>
#include <unistd.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
typedef struct {
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
RingBuffer *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;
list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer));
if(list)
{
list->mNumberBuffers = 1;
list->mBuffers[0].mNumberChannels = channelCount;
list->mBuffers[0].mDataByteSize = byteSize;
list->mBuffers[0].mData = malloc(byteSize);
if(list->mBuffers[0].mData == NULL)
{
free(list);
list = NULL;
}
}
return list;
}
static OSStatus ca_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;
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)
{
ERR("AudioUnitRender error: %d\n", err);
return err;
}
WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames);
return noErr;
}
static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
ComponentDescription desc;
Component comp;
ca_data *data;
OSStatus err;
if(!deviceName)
deviceName = ca_device;
else if(strcmp(deviceName, ca_device) != 0)
return ALC_INVALID_VALUE;
/* open the default output unit */
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
comp = FindNextComponent(NULL, &desc);
if(comp == NULL)
{
ERR("FindNextComponent failed\n");
return ALC_INVALID_VALUE;
}
data = calloc(1, sizeof(*data));
err = OpenAComponent(comp, &data->audioUnit);
if(err != noErr)
{
ERR("OpenAComponent failed\n");
free(data);
return ALC_INVALID_VALUE;
}
/* init and start the default audio unit... */
err = AudioUnitInitialize(data->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
CloseComponent(data->audioUnit);
free(data);
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
device->ExtraData = data;
return ALC_NO_ERROR;
}
static void ca_close_playback(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
AudioUnitUninitialize(data->audioUnit);
CloseComponent(data->audioUnit);
free(data);
device->ExtraData = NULL;
}
static ALCboolean ca_reset_playback(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
AudioStreamBasicDescription streamFormat;
AURenderCallbackStruct input;
OSStatus err;
UInt32 size;
err = AudioUnitUninitialize(data->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);
if(err != noErr || size != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
return ALC_FALSE;
}
#if 0
TRACE("Output streamFormat of default output unit -\n");
TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket);
TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame);
TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel);
TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket);
TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame);
TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
#endif
/* set default output unit's input side to match output side */
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
return ALC_FALSE;
}
if(device->Frequency != streamFormat.mSampleRate)
{
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
streamFormat.mSampleRate /
device->Frequency);
device->Frequency = streamFormat.mSampleRate;
}
/* FIXME: How to tell what channels are what in the output device, and how
* to specify what we're giving? eg, 6.0 vs 5.1 */
switch(streamFormat.mChannelsPerFrame)
{
case 1:
device->FmtChans = DevFmtMono;
break;
case 2:
device->FmtChans = DevFmtStereo;
break;
case 4:
device->FmtChans = DevFmtQuad;
break;
case 6:
device->FmtChans = DevFmtX51;
break;
case 7:
device->FmtChans = DevFmtX61;
break;
case 8:
device->FmtChans = DevFmtX71;
break;
default:
ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame);
device->FmtChans = DevFmtStereo;
streamFormat.mChannelsPerFrame = 2;
break;
}
SetDefaultWFXChannelOrder(device);
/* use channel count and sample rate from the default output unit's current
* parameters, but reset everything else */
streamFormat.mFramesPerPacket = 1;
streamFormat.mFormatFlags = 0;
switch(device->FmtType)
{
case DevFmtUByte:
device->FmtType = DevFmtByte;
/* fall-through */
case DevFmtByte:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
streamFormat.mBitsPerChannel = 8;
break;
case DevFmtUShort:
device->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
streamFormat.mBitsPerChannel = 16;
break;
case DevFmtUInt:
device->FmtType = DevFmtInt;
/* fall-through */
case DevFmtInt:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
streamFormat.mBitsPerChannel = 32;
break;
case DevFmtFloat:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
streamFormat.mBitsPerChannel = 32;
break;
}
streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame *
streamFormat.mBitsPerChannel / 8;
streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
kLinearPCMFormatFlagIsPacked;
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
return ALC_FALSE;
}
/* setup callback */
data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
input.inputProc = ca_callback;
input.inputProcRefCon = device;
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
return ALC_FALSE;
}
/* init the default audio unit... */
err = AudioUnitInitialize(data->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
return ALC_FALSE;
}
return ALC_TRUE;
}
static ALCboolean ca_start_playback(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err;
err = AudioOutputUnitStart(data->audioUnit);
if(err != noErr)
{
ERR("AudioOutputUnitStart failed\n");
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ca_stop_playback(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err;
err = AudioOutputUnitStop(data->audioUnit);
if(err != noErr)
ERR("AudioOutputUnitStop failed\n");
}
static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
{
AudioStreamBasicDescription requestedFormat; // The application requested format
AudioStreamBasicDescription hardwareFormat; // The hardware format
AudioStreamBasicDescription outputFormat; // The AudioUnit output format
AURenderCallbackStruct input;
ComponentDescription desc;
AudioDeviceID inputDevice;
UInt32 outputFrameCount;
UInt32 propertySize;
UInt32 enableIO;
Component comp;
ca_data *data;
OSStatus err;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_HALOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
// Search for component with given description
comp = FindNextComponent(NULL, &desc);
if(comp == NULL)
{
ERR("FindNextComponent failed\n");
return ALC_INVALID_VALUE;
}
data = calloc(1, sizeof(*data));
device->ExtraData = data;
// Open the component
err = OpenAComponent(comp, &data->audioUnit);
if(err != noErr)
{
ERR("OpenAComponent failed\n");
goto error;
}
// Turn off AudioUnit output
enableIO = 0;
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// Turn on AudioUnit input
enableIO = 1;
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// Get the default input device
propertySize = sizeof(AudioDeviceID);
err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propertySize, &inputDevice);
if(err != noErr)
{
ERR("AudioHardwareGetProperty failed\n");
goto error;
}
if(inputDevice == kAudioDeviceUnknown)
{
ERR("No input device found\n");
goto error;
}
// Track the input device
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// set capture callback
input.inputProc = ca_capture_callback;
input.inputProcRefCon = device;
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// Initialize the device
err = AudioUnitInitialize(data->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
goto error;
}
// Get the hardware format
propertySize = sizeof(AudioStreamBasicDescription);
err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
goto error;
}
// Set up the requested format description
switch(device->FmtType)
{
case DevFmtUByte:
requestedFormat.mBitsPerChannel = 8;
requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
break;
case DevFmtShort:
requestedFormat.mBitsPerChannel = 16;
requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
break;
case DevFmtInt:
requestedFormat.mBitsPerChannel = 32;
requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
break;
case DevFmtFloat:
requestedFormat.mBitsPerChannel = 32;
requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
break;
case DevFmtByte:
case DevFmtUShort:
case DevFmtUInt:
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
goto error;
}
switch(device->FmtChans)
{
case DevFmtMono:
requestedFormat.mChannelsPerFrame = 1;
break;
case DevFmtStereo:
requestedFormat.mChannelsPerFrame = 2;
break;
case DevFmtQuad:
case DevFmtX51:
case DevFmtX51Side:
case DevFmtX61:
case DevFmtX71:
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
goto error;
}
requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
requestedFormat.mSampleRate = device->Frequency;
requestedFormat.mFormatID = kAudioFormatLinearPCM;
requestedFormat.mReserved = 0;
requestedFormat.mFramesPerPacket = 1;
// save requested format description for later use
data->format = requestedFormat;
data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
// Use intermediate format for sample rate conversion (outputFormat)
// Set sample rate to the same as hardware for resampling later
outputFormat = requestedFormat;
outputFormat.mSampleRate = hardwareFormat.mSampleRate;
// Determine sample rate ratio for resampling
data->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));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// Set the AudioUnit output format frame count
outputFrameCount = device->UpdateSize * data->sampleRateRatio;
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed: %d\n", err);
goto error;
}
// Set up sample converter
err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter);
if(err != noErr)
{
ERR("AudioConverterNew failed: %d\n", err);
goto error;
}
// Create a buffer for use in the resample callback
data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio);
// Allocate buffer for the AudioUnit output
data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio);
if(data->bufferList == NULL)
goto error;
data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates);
if(data->ring == NULL)
goto error;
al_string_copy_cstr(&device->DeviceName, deviceName);
return ALC_NO_ERROR;
error:
DestroyRingBuffer(data->ring);
free(data->resampleBuffer);
destroy_buffer_list(data->bufferList);
if(data->audioConverter)
AudioConverterDispose(data->audioConverter);
if(data->audioUnit)
CloseComponent(data->audioUnit);
free(data);
device->ExtraData = NULL;
return ALC_INVALID_VALUE;
}
static void ca_close_capture(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
DestroyRingBuffer(data->ring);
free(data->resampleBuffer);
destroy_buffer_list(data->bufferList);
AudioConverterDispose(data->audioConverter);
CloseComponent(data->audioUnit);
free(data);
device->ExtraData = NULL;
}
static void ca_start_capture(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err = AudioOutputUnitStart(data->audioUnit);
if(err != noErr)
ERR("AudioOutputUnitStart failed\n");
}
static void ca_stop_capture(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err = AudioOutputUnitStop(data->audioUnit);
if(err != noErr)
ERR("AudioOutputUnitStop failed\n");
}
static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
{
ca_data *data = (ca_data*)device->ExtraData;
AudioBufferList *list;
UInt32 frameCount;
OSStatus err;
// If no samples are requested, just return
if(samples == 0)
return ALC_NO_ERROR;
// Allocate a temporary AudioBufferList to use as the return resamples data
list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer));
// 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].mData = buffer;
// Resample into another AudioBufferList
frameCount = samples;
err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback,
device, &frameCount, list, NULL);
if(err != noErr)
{
ERR("AudioConverterFillComplexBuffer error: %d\n", err);
return ALC_INVALID_VALUE;
}
return ALC_NO_ERROR;
}
static ALCuint ca_available_samples(ALCdevice *device)
{
ca_data *data = device->ExtraData;
return RingBufferSize(data->ring) / data->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
};
ALCboolean alc_ca_init(BackendFuncs *func_list)
{
*func_list = ca_funcs;
return ALC_TRUE;
}
void alc_ca_deinit(void)
{
}
void alc_ca_probe(enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(ca_device);
break;
case CAPTURE_DEVICE_PROBE:
AppendCaptureDeviceList(ca_device);
break;
}
}
File diff suppressed because it is too large Load Diff
+138
View File
@@ -0,0 +1,138 @@
/**
* 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 "alMain.h"
#include "alu.h"
#include "backends/base.h"
typedef struct ALCloopback {
DERIVE_FROM_TYPE(ALCbackend);
} ALCloopback;
static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device);
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, Destruct)
static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name);
static void ALCloopback_close(ALCloopback *self);
static ALCboolean ALCloopback_reset(ALCloopback *self);
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, void, lock)
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCloopback)
DEFINE_ALCBACKEND_VTABLE(ALCloopback);
static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCloopback, ALCbackend, self);
}
static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
al_string_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCloopback_close(ALCloopback* UNUSED(self))
{
}
static ALCboolean ALCloopback_reset(ALCloopback *self)
{
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
return ALC_TRUE;
}
static ALCboolean ALCloopback_start(ALCloopback* UNUSED(self))
{
return ALC_TRUE;
}
static void ALCloopback_stop(ALCloopback* UNUSED(self))
{
}
typedef struct ALCloopbackFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCloopbackFactory;
#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCloopbackFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory *self);
static DECLARE_FORWARD(ALCloopbackFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory *self, ALCbackend_Type type);
static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type);
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory);
ALCbackendFactory *ALCloopbackFactory_getFactory(void)
{
static ALCloopbackFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory* UNUSED(self))
{
return ALC_TRUE;
}
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Loopback)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type))
{
}
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Loopback)
{
ALCloopback *backend;
backend = ALCloopback_New(sizeof(*backend));
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCloopback_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
File diff suppressed because it is too large Load Diff
+228
View File
@@ -0,0 +1,228 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2010 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>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "compat.h"
#include "backends/base.h"
typedef struct ALCnullBackend {
DERIVE_FROM_TYPE(ALCbackend);
volatile int killNow;
althrd_t thread;
} ALCnullBackend;
static int ALCnullBackend_mixerProc(void *ptr);
static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device);
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, Destruct)
static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name);
static void ALCnullBackend_close(ALCnullBackend *self);
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self);
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, void, lock)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
DEFINE_ALCBACKEND_VTABLE(ALCnullBackend);
static const ALCchar nullDevice[] = "No Output";
static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCnullBackend, ALCbackend, self);
}
static int ALCnullBackend_mixerProc(void *ptr)
{
ALCnullBackend *self = (ALCnullBackend*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
struct timespec now, start;
ALuint64 avail, done;
const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
device->Frequency / 2);
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
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(0, restTime);
else while(avail-done >= device->UpdateSize)
{
aluMixData(device, NULL, device->UpdateSize);
done += device->UpdateSize;
}
}
return 0;
}
static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
{
ALCdevice *device;
if(!name)
name = nullDevice;
else if(strcmp(name, nullDevice) != 0)
return ALC_INVALID_VALUE;
device = STATIC_CAST(ALCbackend, self)->mDevice;
al_string_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCnullBackend_close(ALCnullBackend* UNUSED(self))
{
}
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
{
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
return ALC_TRUE;
}
static ALCboolean ALCnullBackend_start(ALCnullBackend *self)
{
self->killNow = 0;
if(althrd_create(&self->thread, ALCnullBackend_mixerProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
}
static void ALCnullBackend_stop(ALCnullBackend *self)
{
int res;
if(self->killNow)
return;
self->killNow = 1;
althrd_join(self->thread, &res);
}
typedef struct ALCnullBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCnullBackendFactory;
#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCnullBackendFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory *self);
static DECLARE_FORWARD(ALCnullBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory *self, ALCbackend_Type type);
static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory);
ALCbackendFactory *ALCnullBackendFactory_getFactory(void)
{
static ALCnullBackendFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory* UNUSED(self))
{
return ALC_TRUE;
}
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(nullDevice);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCnullBackend *backend;
backend = ALCnullBackend_New(sizeof(*backend));
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCnullBackend_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+424
View File
@@ -0,0 +1,424 @@
/*
* 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;
}
}
+632
View File
@@ -0,0 +1,632 @@
/**
* 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 "backends/base.h"
#include <sys/soundcard.h>
/*
* The OSS documentation talks about SOUND_MIXER_READ, but the header
* only contains MIXER_READ. Play safe. Same for WRITE.
*/
#ifndef SOUND_MIXER_READ
#define SOUND_MIXER_READ MIXER_READ
#endif
#ifndef SOUND_MIXER_WRITE
#define SOUND_MIXER_WRITE MIXER_WRITE
#endif
static const ALCchar oss_device[] = "OSS Default";
static const char *oss_driver = "/dev/dsp";
static const char *oss_capture = "/dev/dsp";
static int log2i(ALCuint x)
{
int y = 0;
while (x > 1)
{
x >>= 1;
y++;
}
return y;
}
typedef struct ALCplaybackOSS {
DERIVE_FROM_TYPE(ALCbackend);
int fd;
ALubyte *mix_data;
int data_size;
volatile int killNow;
althrd_t thread;
} ALCplaybackOSS;
static int ALCplaybackOSS_mixerProc(void *ptr);
static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device);
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, Destruct)
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name);
static void ALCplaybackOSS_close(ALCplaybackOSS *self);
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self);
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, void, lock)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS);
static int ALCplaybackOSS_mixerProc(void *ptr)
{
ALCplaybackOSS *self = (ALCplaybackOSS*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALint frameSize;
ssize_t wrote;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
while(!self->killNow && device->Connected)
{
ALint len = self->data_size;
ALubyte *WritePtr = self->mix_data;
aluMixData(device, WritePtr, len/frameSize);
while(len > 0 && !self->killNow)
{
wrote = write(self->fd, WritePtr, len);
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;
}
len -= wrote;
WritePtr += wrote;
}
}
return 0;
}
static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
}
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
if(!name)
name = oss_device;
else if(strcmp(name, oss_device) != 0)
return ALC_INVALID_VALUE;
self->killNow = 0;
self->fd = open(oss_driver, O_WRONLY);
if(self->fd == -1)
{
ERR("Could not open %s: %s\n", oss_driver, strerror(errno));
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCplaybackOSS_close(ALCplaybackOSS *self)
{
close(self->fd);
self->fd = -1;
}
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
audio_buf_info info;
ALuint frameSize;
int numChannels;
int ossFormat;
int ossSpeed;
char *err;
switch(device->FmtType)
{
case DevFmtByte:
ossFormat = AFMT_S8;
break;
case DevFmtUByte:
ossFormat = AFMT_U8;
break;
case DevFmtUShort:
case DevFmtInt:
case DevFmtUInt:
case DevFmtFloat:
device->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
ossFormat = AFMT_S16_NE;
break;
}
periods = device->NumUpdates;
numChannels = ChannelsFromDevFmt(device->FmtChans);
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
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--;
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
#define CHECKERR(func) if((func) < 0) { \
err = #func; \
goto err; \
}
/* Don't fail if SETFRAGMENT fails. We can handle just about anything
* that's reported back via GETOSPACE */
ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &info));
if(0)
{
err:
ERR("%s failed: %s\n", err, strerror(errno));
return ALC_FALSE;
}
#undef CHECKERR
if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
return ALC_FALSE;
}
if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
(ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
(ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
{
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
return ALC_FALSE;
}
device->Frequency = ossSpeed;
device->UpdateSize = info.fragsize / frameSize;
device->NumUpdates = info.fragments + 1;
SetDefaultChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
self->mix_data = calloc(1, self->data_size);
self->killNow = 0;
if(althrd_create(&self->thread, ALCplaybackOSS_mixerProc, self) != althrd_success)
{
free(self->mix_data);
self->mix_data = NULL;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ALCplaybackOSS_stop(ALCplaybackOSS *self)
{
int res;
if(self->killNow)
return;
self->killNow = 1;
althrd_join(self->thread, &res);
if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
ERR("Error resetting device: %s\n", strerror(errno));
free(self->mix_data);
self->mix_data = NULL;
}
typedef struct ALCcaptureOSS {
DERIVE_FROM_TYPE(ALCbackend);
int fd;
ALubyte *read_data;
int data_size;
RingBuffer *ring;
int doCapture;
volatile int killNow;
althrd_t thread;
} ALCcaptureOSS;
static int ALCcaptureOSS_recordProc(void *ptr);
static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device);
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, Destruct)
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name);
static void ALCcaptureOSS_close(ALCcaptureOSS *self);
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALCboolean, reset)
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, void, lock)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS);
static int ALCcaptureOSS_recordProc(void *ptr)
{
ALCcaptureOSS *self = (ALCcaptureOSS*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
int frameSize;
int amt;
SetRTPriority();
althrd_setname(althrd_current(), "alsoft-record");
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
while(!self->killNow)
{
amt = read(self->fd, self->read_data, self->data_size);
if(amt < 0)
{
ERR("read failed: %s\n", strerror(errno));
ALCcaptureOSS_lock(self);
aluHandleDisconnect(device);
ALCcaptureOSS_unlock(self);
break;
}
if(amt == 0)
{
al_nssleep(0, 1000000);
continue;
}
if(self->doCapture)
WriteRingBuffer(self->ring, self->read_data, amt/frameSize);
}
return 0;
}
static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
}
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
audio_buf_info info;
ALuint frameSize;
int numChannels;
int ossFormat;
int ossSpeed;
char *err;
if(!name)
name = oss_device;
else if(strcmp(name, oss_device) != 0)
return ALC_INVALID_VALUE;
self->fd = open(oss_capture, O_RDONLY);
if(self->fd == -1)
{
ERR("Could not open %s: %s\n", oss_capture, strerror(errno));
return ALC_INVALID_VALUE;
}
switch(device->FmtType)
{
case DevFmtByte:
ossFormat = AFMT_S8;
break;
case DevFmtUByte:
ossFormat = AFMT_U8;
break;
case DevFmtShort:
ossFormat = AFMT_S16_NE;
break;
case DevFmtUShort:
case DevFmtInt:
case DevFmtUInt:
case DevFmtFloat:
ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
return ALC_INVALID_VALUE;
}
periods = 4;
numChannels = ChannelsFromDevFmt(device->FmtChans);
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
ossSpeed = device->Frequency;
log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates *
frameSize / periods);
/* according to the OSS spec, 16 bytes are the minimum */
if (log2FragmentSize < 4)
log2FragmentSize = 4;
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
#define CHECKERR(func) if((func) < 0) { \
err = #func; \
goto err; \
}
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETISPACE, &info));
if(0)
{
err:
ERR("%s failed: %s\n", err, strerror(errno));
close(self->fd);
self->fd = -1;
return ALC_INVALID_VALUE;
}
#undef CHECKERR
if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
close(self->fd);
self->fd = -1;
return ALC_INVALID_VALUE;
}
if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
(ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
(ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
{
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
close(self->fd);
self->fd = -1;
return ALC_INVALID_VALUE;
}
self->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
if(!self->ring)
{
ERR("Ring buffer create failed\n");
close(self->fd);
self->fd = -1;
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);
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);
self->ring = NULL;
free(self->read_data);
self->read_data = NULL;
}
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
{
self->doCapture = 1;
return ALC_TRUE;
}
static void ALCcaptureOSS_stop(ALCcaptureOSS *self)
{
self->doCapture = 0;
}
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples)
{
ReadRingBuffer(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
{
return RingBufferSize(self->ring);
}
typedef struct ALCossBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCossBackendFactory;
#define ALCOSSBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCossBackendFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self);
static DECLARE_FORWARD(ALCossBackendFactory, ALCbackendFactory, void, deinit)
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);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory);
ALCbackendFactory *ALCossBackendFactory_getFactory(void)
{
static ALCossBackendFactory factory = ALCOSSBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
ALCboolean ALCossBackendFactory_init(ALCossBackendFactory* UNUSED(self))
{
ConfigValueStr("oss", "device", &oss_driver);
ConfigValueStr("oss", "capture", &oss_capture);
return ALC_TRUE;
}
ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(oss_driver, &buf) == 0)
#endif
AppendAllDevicesList(oss_device);
}
break;
case CAPTURE_DEVICE_PROBE:
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(oss_capture, &buf) == 0)
#endif
AppendCaptureDeviceList(oss_device);
}
break;
}
}
ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCplaybackOSS *backend;
backend = ALCplaybackOSS_New(sizeof(*backend));
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));
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCcaptureOSS_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+469
View File
@@ -0,0 +1,469 @@
/**
* 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;
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+295
View File
@@ -0,0 +1,295 @@
/**
* 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;
}
}
+288
View File
@@ -0,0 +1,288 @@
/**
* 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;
}
}
+377
View File
@@ -0,0 +1,377 @@
/**
* 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;
}
}
+716
View File
@@ -0,0 +1,716 @@
/**
* 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;
}
}