Add OpenAL-Soft 1.15.1.

This commit is contained in:
rude
2013-09-25 20:36:51 +02:00
parent 4c59d385ef
commit 00c0af3b7c
85 changed files with 43429 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,706 @@
/**
* 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 <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;
}
device->DeviceName = strdup(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;
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_LockDefault,
ALCdevice_UnlockDefault,
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
@@ -0,0 +1,88 @@
/**
* 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"
static ALCenum loopback_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
device->DeviceName = strdup(deviceName);
return ALC_NO_ERROR;
}
static void loopback_close_playback(ALCdevice *device)
{
(void)device;
}
static ALCboolean loopback_reset_playback(ALCdevice *device)
{
SetDefaultWFXChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean loopback_start_playback(ALCdevice *device)
{
return ALC_TRUE;
(void)device;
}
static void loopback_stop_playback(ALCdevice *device)
{
(void)device;
}
static const BackendFuncs loopback_funcs = {
loopback_open_playback,
loopback_close_playback,
loopback_reset_playback,
loopback_start_playback,
loopback_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
ALCdevice_LockDefault,
ALCdevice_UnlockDefault,
ALCdevice_GetLatencyDefault
};
ALCboolean alc_loopback_init(BackendFuncs *func_list)
{
*func_list = loopback_funcs;
return ALC_TRUE;
}
void alc_loopback_deinit(void)
{
}
void alc_loopback_probe(enum DevProbe type)
{
(void)type;
}
File diff suppressed because it is too large Load Diff
+172
View File
@@ -0,0 +1,172 @@
/**
* 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"
typedef struct {
volatile int killNow;
ALvoid *thread;
} null_data;
static const ALCchar nullDevice[] = "No Output";
static ALuint NullProc(ALvoid *ptr)
{
ALCdevice *Device = (ALCdevice*)ptr;
null_data *data = (null_data*)Device->ExtraData;
ALuint now, start;
ALuint64 avail, done;
const ALuint restTime = (ALuint64)Device->UpdateSize * 1000 /
Device->Frequency / 2;
done = 0;
start = timeGetTime();
while(!data->killNow && Device->Connected)
{
now = timeGetTime();
avail = (ALuint64)(now-start) * Device->Frequency / 1000;
if(avail < done)
{
/* Timer wrapped (50 days???). Add the remainder of the cycle to
* the available count and reset the number of samples done */
avail += ((ALuint64)1<<32)*Device->Frequency/1000 - done;
done = 0;
}
if(avail-done < Device->UpdateSize)
{
Sleep(restTime);
continue;
}
while(avail-done >= Device->UpdateSize)
{
aluMixData(Device, NULL, Device->UpdateSize);
done += Device->UpdateSize;
}
}
return 0;
}
static ALCenum null_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
null_data *data;
if(!deviceName)
deviceName = nullDevice;
else if(strcmp(deviceName, nullDevice) != 0)
return ALC_INVALID_VALUE;
data = (null_data*)calloc(1, sizeof(*data));
device->DeviceName = strdup(deviceName);
device->ExtraData = data;
return ALC_NO_ERROR;
}
static void null_close_playback(ALCdevice *device)
{
null_data *data = (null_data*)device->ExtraData;
free(data);
device->ExtraData = NULL;
}
static ALCboolean null_reset_playback(ALCdevice *device)
{
SetDefaultWFXChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean null_start_playback(ALCdevice *device)
{
null_data *data = (null_data*)device->ExtraData;
data->thread = StartThread(NullProc, device);
if(data->thread == NULL)
return ALC_FALSE;
return ALC_TRUE;
}
static void null_stop_playback(ALCdevice *device)
{
null_data *data = (null_data*)device->ExtraData;
if(!data->thread)
return;
data->killNow = 1;
StopThread(data->thread);
data->thread = NULL;
data->killNow = 0;
}
static const BackendFuncs null_funcs = {
null_open_playback,
null_close_playback,
null_reset_playback,
null_start_playback,
null_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
ALCdevice_LockDefault,
ALCdevice_UnlockDefault,
ALCdevice_GetLatencyDefault
};
ALCboolean alc_null_init(BackendFuncs *func_list)
{
*func_list = null_funcs;
return ALC_TRUE;
}
void alc_null_deinit(void)
{
}
void alc_null_probe(enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(nullDevice);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
@@ -0,0 +1,442 @@
/*
* 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>
#if 1
#include <SLES/OpenSLES_Android.h>
#else
extern SLAPIENTRY const SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
struct SLAndroidSimpleBufferQueueItf_;
typedef const struct SLAndroidSimpleBufferQueueItf_ * const * SLAndroidSimpleBufferQueueItf;
typedef void (*slAndroidSimpleBufferQueueCallback)(SLAndroidSimpleBufferQueueItf caller, void *pContext);
typedef struct SLAndroidSimpleBufferQueueState_ {
SLuint32 count;
SLuint32 index;
} SLAndroidSimpleBufferQueueState;
struct SLAndroidSimpleBufferQueueItf_ {
SLresult (*Enqueue) (
SLAndroidSimpleBufferQueueItf self,
const void *pBuffer,
SLuint32 size
);
SLresult (*Clear) (
SLAndroidSimpleBufferQueueItf self
);
SLresult (*GetState) (
SLAndroidSimpleBufferQueueItf self,
SLAndroidSimpleBufferQueueState *pState
);
SLresult (*RegisterCallback) (
SLAndroidSimpleBufferQueueItf self,
slAndroidSimpleBufferQueueCallback callback,
void* pContext
);
};
#define SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE ((SLuint32) 0x800007BD)
typedef struct SLDataLocator_AndroidSimpleBufferQueue {
SLuint32 locatorType;
SLuint32 numBuffers;
} SLDataLocator_AndroidSimpleBufferQueue;
#endif
/* Helper macros */
#define SLObjectItf_Realize(a,b) ((*(a))->Realize((a),(b)))
#define SLObjectItf_GetInterface(a,b,c) ((*(a))->GetInterface((a),(b),(c)))
#define SLObjectItf_Destroy(a) ((*(a))->Destroy((a)))
#define SLEngineItf_CreateOutputMix(a,b,c,d,e) ((*(a))->CreateOutputMix((a),(b),(c),(d),(e)))
#define SLEngineItf_CreateAudioPlayer(a,b,c,d,e,f,g) ((*(a))->CreateAudioPlayer((a),(b),(c),(d),(e),(f),(g)))
#define SLPlayItf_SetPlayState(a,b) ((*(a))->SetPlayState((a),(b)))
typedef struct {
/* engine interfaces */
SLObjectItf engineObject;
SLEngineItf engine;
/* output mix interfaces */
SLObjectItf outputMix;
/* buffer queue player interfaces */
SLObjectItf bufferQueueObject;
void *buffer;
ALuint bufferSize;
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";
case SL_RESULT_READONLY: return "ReadOnly";
case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported";
case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible";
}
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;
SLresult result;
aluMixData(Device, data->buffer, data->bufferSize/data->frameSize);
result = (*bq)->Enqueue(bq, data->buffer, data->bufferSize);
PRINTERR(result, "bq->Enqueue");
}
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 = SLObjectItf_Realize(data->engineObject, SL_BOOLEAN_FALSE);
PRINTERR(result, "engine->Realize");
}
if(SL_RESULT_SUCCESS == result)
{
result = SLObjectItf_GetInterface(data->engineObject, SL_IID_ENGINE, &data->engine);
PRINTERR(result, "engine->GetInterface");
}
if(SL_RESULT_SUCCESS == result)
{
result = SLEngineItf_CreateOutputMix(data->engine, &data->outputMix, 0, NULL, NULL);
PRINTERR(result, "engine->CreateOutputMix");
}
if(SL_RESULT_SUCCESS == result)
{
result = SLObjectItf_Realize(data->outputMix, SL_BOOLEAN_FALSE);
PRINTERR(result, "outputMix->Realize");
}
if(SL_RESULT_SUCCESS != result)
{
if(data->outputMix != NULL)
SLObjectItf_Destroy(data->outputMix);
data->outputMix = NULL;
if(data->engineObject != NULL)
SLObjectItf_Destroy(data->engineObject);
data->engineObject = NULL;
data->engine = NULL;
free(data);
return ALC_INVALID_VALUE;
}
Device->DeviceName = strdup(deviceName);
Device->ExtraData = data;
return ALC_NO_ERROR;
}
static void opensl_close_playback(ALCdevice *Device)
{
osl_data *data = Device->ExtraData;
if(data->bufferQueueObject != NULL)
SLObjectItf_Destroy(data->bufferQueueObject);
data->bufferQueueObject = NULL;
SLObjectItf_Destroy(data->outputMix);
data->outputMix = NULL;
SLObjectItf_Destroy(data->engineObject);
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 = SL_BYTEORDER_NATIVE;
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)
SLObjectItf_Destroy(data->bufferQueueObject);
data->bufferQueueObject = NULL;
result = SLEngineItf_CreateAudioPlayer(data->engine, &data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req);
PRINTERR(result, "engine->CreateAudioPlayer");
if(SL_RESULT_SUCCESS == result)
{
result = SLObjectItf_Realize(data->bufferQueueObject, SL_BOOLEAN_FALSE);
PRINTERR(result, "bufferQueue->Realize");
}
if(SL_RESULT_SUCCESS != result)
{
if(data->bufferQueueObject != NULL)
SLObjectItf_Destroy(data->bufferQueueObject);
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 = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_BUFFERQUEUE, &bufferQueue);
PRINTERR(result, "bufferQueue->GetInterface");
if(SL_RESULT_SUCCESS == result)
{
result = (*bufferQueue)->RegisterCallback(bufferQueue, 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(1, 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)
{
result = (*bufferQueue)->Enqueue(bufferQueue, data->buffer, data->bufferSize);
PRINTERR(result, "bufferQueue->Enqueue");
}
}
if(SL_RESULT_SUCCESS == result)
{
result = SLObjectItf_GetInterface(data->bufferQueueObject, SL_IID_PLAY, &player);
PRINTERR(result, "bufferQueue->GetInterface");
}
if(SL_RESULT_SUCCESS == result)
{
result = SLPlayItf_SetPlayState(player, SL_PLAYSTATE_PLAYING);
PRINTERR(result, "player->SetPlayState");
}
if(SL_RESULT_SUCCESS != result)
{
if(data->bufferQueueObject != NULL)
SLObjectItf_Destroy(data->bufferQueueObject);
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;
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_LockDefault,
ALCdevice_UnlockDefault,
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;
}
}
+537
View File
@@ -0,0 +1,537 @@
/**
* 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 <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";
typedef struct {
int fd;
volatile int killNow;
ALvoid *thread;
ALubyte *mix_data;
int data_size;
RingBuffer *ring;
int doCapture;
} oss_data;
static int log2i(ALCuint x)
{
int y = 0;
while (x > 1)
{
x >>= 1;
y++;
}
return y;
}
static ALuint OSSProc(ALvoid *ptr)
{
ALCdevice *Device = (ALCdevice*)ptr;
oss_data *data = (oss_data*)Device->ExtraData;
ALint frameSize;
ssize_t wrote;
SetRTPriority();
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;
}
Sleep(1);
continue;
}
len -= wrote;
WritePtr += wrote;
}
}
return 0;
}
static ALuint OSSCaptureProc(ALvoid *ptr)
{
ALCdevice *Device = (ALCdevice*)ptr;
oss_data *data = (oss_data*)Device->ExtraData;
int frameSize;
int amt;
SetRTPriority();
frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
while(!data->killNow)
{
amt = read(data->fd, data->mix_data, data->data_size);
if(amt < 0)
{
ERR("read failed: %s\n", strerror(errno));
ALCdevice_Lock(Device);
aluHandleDisconnect(Device);
ALCdevice_Unlock(Device);
break;
}
if(amt == 0)
{
Sleep(1);
continue;
}
if(data->doCapture)
WriteRingBuffer(data->ring, data->mix_data, amt/frameSize);
}
return 0;
}
static ALCenum oss_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
oss_data *data;
if(!deviceName)
deviceName = oss_device;
else if(strcmp(deviceName, oss_device) != 0)
return ALC_INVALID_VALUE;
data = (oss_data*)calloc(1, sizeof(oss_data));
data->killNow = 0;
data->fd = open(oss_driver, O_WRONLY);
if(data->fd == -1)
{
free(data);
ERR("Could not open %s: %s\n", oss_driver, strerror(errno));
return ALC_INVALID_VALUE;
}
device->DeviceName = strdup(deviceName);
device->ExtraData = data;
return ALC_NO_ERROR;
}
static void oss_close_playback(ALCdevice *device)
{
oss_data *data = (oss_data*)device->ExtraData;
close(data->fd);
free(data);
device->ExtraData = NULL;
}
static ALCboolean oss_reset_playback(ALCdevice *device)
{
oss_data *data = (oss_data*)device->ExtraData;
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(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat));
CHECKERR(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels));
CHECKERR(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed));
CHECKERR(ioctl(data->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 oss_start_playback(ALCdevice *device)
{
oss_data *data = (oss_data*)device->ExtraData;
data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
data->mix_data = calloc(1, data->data_size);
data->thread = StartThread(OSSProc, device);
if(data->thread == NULL)
{
free(data->mix_data);
data->mix_data = NULL;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void oss_stop_playback(ALCdevice *device)
{
oss_data *data = (oss_data*)device->ExtraData;
if(!data->thread)
return;
data->killNow = 1;
StopThread(data->thread);
data->thread = NULL;
data->killNow = 0;
if(ioctl(data->fd, SNDCTL_DSP_RESET) != 0)
ERR("Error resetting device: %s\n", strerror(errno));
free(data->mix_data);
data->mix_data = NULL;
}
static ALCenum oss_open_capture(ALCdevice *device, const ALCchar *deviceName)
{
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
audio_buf_info info;
ALuint frameSize;
int numChannels;
oss_data *data;
int ossFormat;
int ossSpeed;
char *err;
if(!deviceName)
deviceName = oss_device;
else if(strcmp(deviceName, oss_device) != 0)
return ALC_INVALID_VALUE;
data = (oss_data*)calloc(1, sizeof(oss_data));
data->killNow = 0;
data->fd = open(oss_capture, O_RDONLY);
if(data->fd == -1)
{
free(data);
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:
free(data);
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(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat));
CHECKERR(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels));
CHECKERR(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed));
CHECKERR(ioctl(data->fd, SNDCTL_DSP_GETISPACE, &info));
if(0)
{
err:
ERR("%s failed: %s\n", err, strerror(errno));
close(data->fd);
free(data);
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(data->fd);
free(data);
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(data->fd);
free(data);
return ALC_INVALID_VALUE;
}
data->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
if(!data->ring)
{
ERR("Ring buffer create failed\n");
close(data->fd);
free(data);
return ALC_OUT_OF_MEMORY;
}
data->data_size = info.fragsize;
data->mix_data = calloc(1, data->data_size);
device->ExtraData = data;
data->thread = StartThread(OSSCaptureProc, device);
if(data->thread == NULL)
{
device->ExtraData = NULL;
free(data->mix_data);
free(data);
return ALC_OUT_OF_MEMORY;
}
device->DeviceName = strdup(deviceName);
return ALC_NO_ERROR;
}
static void oss_close_capture(ALCdevice *device)
{
oss_data *data = (oss_data*)device->ExtraData;
data->killNow = 1;
StopThread(data->thread);
close(data->fd);
DestroyRingBuffer(data->ring);
free(data->mix_data);
free(data);
device->ExtraData = NULL;
}
static void oss_start_capture(ALCdevice *Device)
{
oss_data *data = (oss_data*)Device->ExtraData;
data->doCapture = 1;
}
static void oss_stop_capture(ALCdevice *Device)
{
oss_data *data = (oss_data*)Device->ExtraData;
data->doCapture = 0;
}
static ALCenum oss_capture_samples(ALCdevice *Device, ALCvoid *pBuffer, ALCuint lSamples)
{
oss_data *data = (oss_data*)Device->ExtraData;
ReadRingBuffer(data->ring, pBuffer, lSamples);
return ALC_NO_ERROR;
}
static ALCuint oss_available_samples(ALCdevice *Device)
{
oss_data *data = (oss_data*)Device->ExtraData;
return RingBufferSize(data->ring);
}
static const BackendFuncs oss_funcs = {
oss_open_playback,
oss_close_playback,
oss_reset_playback,
oss_start_playback,
oss_stop_playback,
oss_open_capture,
oss_close_capture,
oss_start_capture,
oss_stop_capture,
oss_capture_samples,
oss_available_samples,
ALCdevice_LockDefault,
ALCdevice_UnlockDefault,
ALCdevice_GetLatencyDefault
};
ALCboolean alc_oss_init(BackendFuncs *func_list)
{
ConfigValueStr("oss", "device", &oss_driver);
ConfigValueStr("oss", "capture", &oss_capture);
*func_list = oss_funcs;
return ALC_TRUE;
}
void alc_oss_deinit(void)
{
}
void alc_oss_probe(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;
}
}
@@ -0,0 +1,472 @@
/**
* 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 <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_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_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_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 *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
const PaStreamCallbackFlags statusFlags, void *userData)
{
ALCdevice *device = (ALCdevice*)userData;
(void)inputBuffer;
(void)timeInfo;
(void)statusFlags;
aluMixData(device, outputBuffer, framesPerBuffer);
return 0;
}
static int pa_capture_cb(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
const PaStreamCallbackFlags statusFlags, void *userData)
{
ALCdevice *device = (ALCdevice*)userData;
pa_data *data = (pa_data*)device->ExtraData;
(void)outputBuffer;
(void)timeInfo;
(void)statusFlags;
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;
device->DeviceName = strdup(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_GetDefaultOutputDevice();
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;
}
device->DeviceName = strdup(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));
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_LockDefault,
ALCdevice_UnlockDefault,
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
@@ -0,0 +1,296 @@
/**
* 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 <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;
ALvoid *thread;
} sndio_data;
static ALuint sndio_proc(ALvoid *ptr)
{
ALCdevice *device = ptr;
sndio_data *data = device->ExtraData;
ALsizei frameSize;
size_t wrote;
SetRTPriority();
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;
}
device->DeviceName = strdup(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->thread = StartThread(sndio_proc, device);
if(data->thread == NULL)
{
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;
if(!data->thread)
return;
data->killNow = 1;
StopThread(data->thread);
data->thread = NULL;
data->killNow = 0;
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_LockDefault,
ALCdevice_UnlockDefault,
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;
}
}
@@ -0,0 +1,287 @@
/**
* 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 <sys/audioio.h>
static const ALCchar solaris_device[] = "Solaris Default";
static const char *solaris_driver = "/dev/audio";
typedef struct {
int fd;
volatile int killNow;
ALvoid *thread;
ALubyte *mix_data;
int data_size;
} solaris_data;
static ALuint SolarisProc(ALvoid *ptr)
{
ALCdevice *Device = (ALCdevice*)ptr;
solaris_data *data = (solaris_data*)Device->ExtraData;
ALint frameSize;
int wrote;
SetRTPriority();
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;
}
Sleep(1);
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;
}
device->DeviceName = strdup(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->thread = StartThread(SolarisProc, device);
if(data->thread == NULL)
{
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;
if(!data->thread)
return;
data->killNow = 1;
StopThread(data->thread);
data->thread = NULL;
data->killNow = 0;
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_LockDefault,
ALCdevice_UnlockDefault,
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;
}
}
+370
View File
@@ -0,0 +1,370 @@
/**
* 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>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include "alMain.h"
#include "alu.h"
typedef struct {
FILE *f;
long DataStart;
ALvoid *buffer;
ALuint size;
volatile int killNow;
ALvoid *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 ALuint WaveProc(ALvoid *ptr)
{
ALCdevice *Device = (ALCdevice*)ptr;
wave_data *data = (wave_data*)Device->ExtraData;
ALuint frameSize;
ALuint now, start;
ALuint64 avail, done;
size_t fs;
const ALuint restTime = (ALuint64)Device->UpdateSize * 1000 /
Device->Frequency / 2;
frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
done = 0;
start = timeGetTime();
while(!data->killNow && Device->Connected)
{
now = timeGetTime();
avail = (ALuint64)(now-start) * Device->Frequency / 1000;
if(avail < done)
{
/* Timer wrapped (50 days???). Add the remainder of the cycle to
* the available count and reset the number of samples done */
avail += ((ALuint64)1<<32)*Device->Frequency/1000 - done;
done = 0;
}
if(avail-done < Device->UpdateSize)
{
Sleep(restTime);
continue;
}
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);
fs = 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 = fopen(fname, "wb");
if(!data->f)
{
free(data);
ERR("Could not open file '%s': %s\n", fname, strerror(errno));
return ALC_INVALID_VALUE;
}
device->DeviceName = strdup(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);
val = 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->thread = StartThread(WaveProc, device);
if(data->thread == NULL)
{
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;
if(!data->thread)
return;
data->killNow = 1;
StopThread(data->thread);
data->thread = NULL;
data->killNow = 0;
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_LockDefault,
ALCdevice_UnlockDefault,
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;
}
}
@@ -0,0 +1,774 @@
/**
* 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"
#ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif
typedef struct {
// MMSYSTEM Device
volatile ALboolean killNow;
HANDLE WaveThreadEvent;
HANDLE WaveThread;
DWORD WaveThreadID;
volatile LONG WaveBuffersCommitted;
WAVEHDR WaveBuffer[4];
union {
HWAVEIN In;
HWAVEOUT Out;
} WaveHandle;
WAVEFORMATEX Format;
RingBuffer *Ring;
} WinMMData;
static ALCchar **PlaybackDeviceList;
static ALuint NumPlaybackDevices;
static ALCchar **CaptureDeviceList;
static ALuint NumCaptureDevices;
static void ProbePlaybackDevices(void)
{
ALuint i;
for(i = 0;i < NumPlaybackDevices;i++)
free(PlaybackDeviceList[i]);
NumPlaybackDevices = waveOutGetNumDevs();
PlaybackDeviceList = realloc(PlaybackDeviceList, sizeof(ALCchar*) * NumPlaybackDevices);
for(i = 0;i < NumPlaybackDevices;i++)
{
WAVEOUTCAPS WaveCaps;
PlaybackDeviceList[i] = NULL;
if(waveOutGetDevCaps(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
{
char name[1024];
ALuint count, j;
count = 0;
do {
if(count == 0)
snprintf(name, sizeof(name), "%s", WaveCaps.szPname);
else
snprintf(name, sizeof(name), "%s #%d", WaveCaps.szPname, count+1);
count++;
for(j = 0;j < i;j++)
{
if(strcmp(name, PlaybackDeviceList[j]) == 0)
break;
}
} while(j != i);
PlaybackDeviceList[i] = strdup(name);
}
}
}
static void ProbeCaptureDevices(void)
{
ALuint i;
for(i = 0;i < NumCaptureDevices;i++)
free(CaptureDeviceList[i]);
NumCaptureDevices = waveInGetNumDevs();
CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices);
for(i = 0;i < NumCaptureDevices;i++)
{
WAVEINCAPS WaveInCaps;
CaptureDeviceList[i] = NULL;
if(waveInGetDevCaps(i, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR)
{
char name[1024];
ALuint count, j;
count = 0;
do {
if(count == 0)
snprintf(name, sizeof(name), "%s", WaveInCaps.szPname);
else
snprintf(name, sizeof(name), "%s #%d", WaveInCaps.szPname, count+1);
count++;
for(j = 0;j < i;j++)
{
if(strcmp(name, CaptureDeviceList[j]) == 0)
break;
}
} while(j != i);
CaptureDeviceList[i] = strdup(name);
}
}
}
/*
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 device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
{
ALCdevice *Device = (ALCdevice*)instance;
WinMMData *data = Device->ExtraData;
(void)device;
(void)param2;
if(msg != WOM_DONE)
return;
InterlockedDecrement(&data->WaveBuffersCommitted);
PostThreadMessage(data->WaveThreadID, msg, 0, param1);
}
/*
PlaybackThreadProc
Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its
audio data.
*/
static DWORD WINAPI PlaybackThreadProc(LPVOID param)
{
ALCdevice *Device = (ALCdevice*)param;
WinMMData *data = Device->ExtraData;
LPWAVEHDR WaveHdr;
ALuint FrameSize;
MSG msg;
FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
SetRTPriority();
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message != WOM_DONE)
continue;
if(data->killNow)
{
if(data->WaveBuffersCommitted == 0)
break;
continue;
}
WaveHdr = ((LPWAVEHDR)msg.lParam);
aluMixData(Device, WaveHdr->lpData, WaveHdr->dwBufferLength/FrameSize);
// Send buffer back to play more data
waveOutWrite(data->WaveHandle.Out, WaveHdr, sizeof(WAVEHDR));
InterlockedIncrement(&data->WaveBuffersCommitted);
}
// Signal Wave Thread completed event
if(data->WaveThreadEvent)
SetEvent(data->WaveThreadEvent);
ExitThread(0);
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 device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
{
ALCdevice *Device = (ALCdevice*)instance;
WinMMData *data = Device->ExtraData;
(void)device;
(void)param2;
if(msg != WIM_DATA)
return;
InterlockedDecrement(&data->WaveBuffersCommitted);
PostThreadMessage(data->WaveThreadID, msg, 0, param1);
}
/*
CaptureThreadProc
Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
audio data.
*/
static DWORD WINAPI CaptureThreadProc(LPVOID param)
{
ALCdevice *Device = (ALCdevice*)param;
WinMMData *data = Device->ExtraData;
LPWAVEHDR WaveHdr;
ALuint FrameSize;
MSG msg;
FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
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 = ((LPWAVEHDR)msg.lParam);
WriteRingBuffer(data->Ring, (ALubyte*)WaveHdr->lpData, WaveHdr->dwBytesRecorded/FrameSize);
// Send buffer back to capture more data
waveInAddBuffer(data->WaveHandle.In, WaveHdr, sizeof(WAVEHDR));
InterlockedIncrement(&data->WaveBuffersCommitted);
}
// Signal Wave Thread completed event
if(data->WaveThreadEvent)
SetEvent(data->WaveThreadEvent);
ExitThread(0);
return 0;
}
static ALCenum WinMMOpenPlayback(ALCdevice *Device, const ALCchar *deviceName)
{
WinMMData *data = NULL;
UINT DeviceID = 0;
MMRESULT res;
ALuint i = 0;
if(!PlaybackDeviceList)
ProbePlaybackDevices();
// Find the Device ID matching the deviceName if valid
for(i = 0;i < NumPlaybackDevices;i++)
{
if(PlaybackDeviceList[i] &&
(!deviceName || strcmp(deviceName, PlaybackDeviceList[i]) == 0))
{
DeviceID = i;
break;
}
}
if(i == NumPlaybackDevices)
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;
}
data->WaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(data->WaveThreadEvent == NULL)
{
ERR("CreateEvent failed: %lu\n", GetLastError());
goto failure;
}
Device->DeviceName = strdup(PlaybackDeviceList[DeviceID]);
return ALC_NO_ERROR;
failure:
if(data->WaveThreadEvent)
CloseHandle(data->WaveThreadEvent);
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
CloseHandle(data->WaveThreadEvent);
data->WaveThreadEvent = 0;
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->WaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlaybackThreadProc, (LPVOID)device, 0, &data->WaveThreadID);
if(data->WaveThread == NULL)
return ALC_FALSE;
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) ? (LPSTR)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));
InterlockedIncrement(&data->WaveBuffersCommitted);
}
return ALC_TRUE;
}
static void WinMMStopPlayback(ALCdevice *device)
{
WinMMData *data = (WinMMData*)device->ExtraData;
void *buffer = NULL;
int i;
if(data->WaveThread == NULL)
return;
// Set flag to stop processing headers
data->killNow = AL_TRUE;
// Wait for signal that Wave Thread has been destroyed
WaitForSingleObjectEx(data->WaveThreadEvent, 5000, FALSE);
CloseHandle(data->WaveThread);
data->WaveThread = 0;
data->killNow = AL_FALSE;
// 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)
{
ALbyte *BufferData = NULL;
DWORD CapturedDataSize;
WinMMData *data = NULL;
UINT DeviceID = 0;
ALint BufferSize;
MMRESULT res;
ALuint i;
if(!CaptureDeviceList)
ProbeCaptureDevices();
// Find the Device ID matching the deviceName if valid
for(i = 0;i < NumCaptureDevices;i++)
{
if(CaptureDeviceList[i] &&
(!deviceName || strcmp(deviceName, CaptureDeviceList[i]) == 0))
{
DeviceID = i;
break;
}
}
if(i == NumCaptureDevices)
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;
}
data->WaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(data->WaveThreadEvent == NULL)
{
ERR("CreateEvent failed: %lu\n", GetLastError());
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;
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) ? (LPSTR)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));
InterlockedIncrement(&data->WaveBuffersCommitted);
}
data->WaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)Device, 0, &data->WaveThreadID);
if (data->WaveThread == NULL)
goto failure;
Device->DeviceName = strdup(CaptureDeviceList[DeviceID]);
return ALC_NO_ERROR;
failure:
if(data->WaveThread)
CloseHandle(data->WaveThread);
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->WaveThreadEvent)
CloseHandle(data->WaveThreadEvent);
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->WaveThreadID, WM_QUIT, 0, 0);
WaitForSingleObjectEx(data->WaveThreadEvent, 5000, FALSE);
/* Make sure capture is stopped and all pending buffers are flushed. */
waveInReset(data->WaveHandle.In);
CloseHandle(data->WaveThread);
data->WaveThread = 0;
// 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
CloseHandle(data->WaveThreadEvent);
data->WaveThreadEvent = 0;
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 const BackendFuncs WinMMFuncs = {
WinMMOpenPlayback,
WinMMClosePlayback,
WinMMResetPlayback,
WinMMStartPlayback,
WinMMStopPlayback,
WinMMOpenCapture,
WinMMCloseCapture,
WinMMStartCapture,
WinMMStopCapture,
WinMMCaptureSamples,
WinMMAvailableSamples,
ALCdevice_LockDefault,
ALCdevice_UnlockDefault,
ALCdevice_GetLatencyDefault
};
ALCboolean alcWinMMInit(BackendFuncs *FuncList)
{
*FuncList = WinMMFuncs;
return ALC_TRUE;
}
void alcWinMMDeinit()
{
ALuint i;
for(i = 0;i < NumPlaybackDevices;i++)
free(PlaybackDeviceList[i]);
free(PlaybackDeviceList);
PlaybackDeviceList = NULL;
NumPlaybackDevices = 0;
for(i = 0;i < NumCaptureDevices;i++)
free(CaptureDeviceList[i]);
free(CaptureDeviceList);
CaptureDeviceList = NULL;
NumCaptureDevices = 0;
}
void alcWinMMProbe(enum DevProbe type)
{
ALuint i;
switch(type)
{
case ALL_DEVICE_PROBE:
ProbePlaybackDevices();
for(i = 0;i < NumPlaybackDevices;i++)
{
if(PlaybackDeviceList[i])
AppendAllDevicesList(PlaybackDeviceList[i]);
}
break;
case CAPTURE_DEVICE_PROBE:
ProbeCaptureDevices();
for(i = 0;i < NumCaptureDevices;i++)
{
if(CaptureDeviceList[i])
AppendCaptureDeviceList(CaptureDeviceList[i]);
}
break;
}
}