added dependencies and build files (loosely inspired by Sebastian Dorda's love-native-android)

This commit is contained in:
Martin Felis
2013-12-05 17:58:37 +01:00
parent 9604528a96
commit 95a086a47f
3700 changed files with 1691119 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,270 @@
/**
* 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>
#include <jni.h>
#include <pthread.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
static const ALCchar android_device[] = "Android Legacy";
static JavaVM* javaVM = NULL;
static jclass cAudioTrack = NULL;
static jmethodID mAudioTrack;
static jmethodID mGetMinBufferSize;
static jmethodID mPlay;
static jmethodID mStop;
static jmethodID mRelease;
static jmethodID mWrite;
__attribute__((visibility("default"))) jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
(void)reserved;
javaVM = vm;
return JNI_VERSION_1_2;
}
static JNIEnv* GetEnv()
{
JNIEnv* env = NULL;
if (javaVM) (*javaVM)->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_2);
return env;
}
typedef struct
{
pthread_t thread;
volatile int running;
} AndroidData;
#define STREAM_MUSIC 3
#define CHANNEL_CONFIGURATION_MONO 2
#define CHANNEL_CONFIGURATION_STEREO 3
#define ENCODING_PCM_8BIT 3
#define ENCODING_PCM_16BIT 2
#define MODE_STREAM 1
static void* thread_function(void* arg)
{
ALCdevice* device = (ALCdevice*)arg;
AndroidData* data = (AndroidData*)device->ExtraData;
JNIEnv* env;
(*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
(*env)->PushLocalFrame(env, 2);
int sampleRateInHz = device->Frequency;
int channelConfig = ChannelsFromDevFmt(device->FmtChans) == 1 ? CHANNEL_CONFIGURATION_MONO : CHANNEL_CONFIGURATION_STEREO;
int audioFormat = BytesFromDevFmt(device->FmtType) == 1 ? ENCODING_PCM_8BIT : ENCODING_PCM_16BIT;
int bufferSizeInBytes = (*env)->CallStaticIntMethod(env, cAudioTrack,
mGetMinBufferSize, sampleRateInHz, channelConfig, audioFormat);
int bufferSizeInSamples = bufferSizeInBytes / FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
jobject track = (*env)->NewObject(env, cAudioTrack, mAudioTrack,
STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat, device->NumUpdates * bufferSizeInBytes, MODE_STREAM);
#ifdef HAVE_ANDROID_LOW_LATENCY
int started = 0;
size_t overallBytes = 0;
#else
(*env)->CallNonvirtualVoidMethod(env, track, cAudioTrack, mPlay);
#endif
jarray buffer = (*env)->NewByteArray(env, bufferSizeInBytes);
while (data->running)
{
void* pBuffer = (*env)->GetPrimitiveArrayCritical(env, buffer, NULL);
if (pBuffer)
{
aluMixData(device, pBuffer, bufferSizeInSamples);
(*env)->ReleasePrimitiveArrayCritical(env, buffer, pBuffer, 0);
#ifdef HAVE_ANDROID_LOW_LATENCY
if (bufferSizeInBytes >= 0)
{
if (started)
{
#endif
(*env)->CallNonvirtualIntMethod(env, track, cAudioTrack, mWrite, buffer, 0, bufferSizeInBytes);
#ifdef HAVE_ANDROID_LOW_LATENCY
}
else
{
overallBytes += (*env)->CallNonvirtualIntMethod(env, track, cAudioTrack, mWrite, buffer, 0, bufferSizeInBytes);
if (overallBytes >= (device->NumUpdates * bufferSizeInBytes))
{
(*env)->CallNonvirtualVoidMethod(env, track, cAudioTrack, mPlay);
started = 1;
}
}
}
#endif
}
else
{
AL_PRINT("Failed to get pointer to array bytes");
}
}
(*env)->CallNonvirtualVoidMethod(env, track, cAudioTrack, mStop);
(*env)->CallNonvirtualVoidMethod(env, track, cAudioTrack, mRelease);
(*env)->PopLocalFrame(env, NULL);
(*javaVM)->DetachCurrentThread(javaVM);
return NULL;
}
static ALCenum android_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
JNIEnv* env = GetEnv();
AndroidData* data;
if (!cAudioTrack)
{
/* Cache AudioTrack class and it's method id's
* And do this only once!
*/
cAudioTrack = (*env)->FindClass(env, "android/media/AudioTrack");
if (!cAudioTrack)
{
AL_PRINT("android.media.AudioTrack class is not found. Are you running at least 1.5 version?");
return ALC_INVALID_VALUE;
}
cAudioTrack = (*env)->NewGlobalRef(env, cAudioTrack);
mAudioTrack = (*env)->GetMethodID(env, cAudioTrack, "<init>", "(IIIIII)V");
mGetMinBufferSize = (*env)->GetStaticMethodID(env, cAudioTrack, "getMinBufferSize", "(III)I");
mPlay = (*env)->GetMethodID(env, cAudioTrack, "play", "()V");
mStop = (*env)->GetMethodID(env, cAudioTrack, "stop", "()V");
mRelease = (*env)->GetMethodID(env, cAudioTrack, "release", "()V");
mWrite = (*env)->GetMethodID(env, cAudioTrack, "write", "([BII)I");
}
if (!deviceName)
{
deviceName = android_device;
}
else if (strcmp(deviceName, android_device) != 0)
{
return ALC_INVALID_VALUE;
}
data = (AndroidData*)calloc(1, sizeof(*data));
device->szDeviceName = strdup(deviceName);
device->ExtraData = data;
#ifdef HAVE_ANDROID_LOW_LATENCY
device->Frequency = 22050;
device->NumUpdates = 1;
#endif
return ALC_NO_ERROR;
}
static void android_close_playback(ALCdevice *device)
{
AndroidData* data = (AndroidData*)device->ExtraData;
if (data != NULL)
{
free(data);
device->ExtraData = NULL;
}
}
static ALCboolean android_reset_playback(ALCdevice *device)
{
AndroidData* data = (AndroidData*)device->ExtraData;
device->FmtChans = ChannelsFromDevFmt(device->FmtChans) >= 2 ? DevFmtStereo : DevFmtMono;
SetDefaultChannelOrder(device);
data->running = 1;
pthread_create(&data->thread, NULL, thread_function, device);
return ALC_TRUE;
}
static void android_stop_playback(ALCdevice *device)
{
AndroidData* data = (AndroidData*)device->ExtraData;
if (data->running)
{
data->running = 0;
pthread_join(data->thread, NULL);
}
}
static const BackendFuncs android_funcs = {
android_open_playback,
android_close_playback,
android_reset_playback,
android_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
ALCboolean alc_android_init(BackendFuncs *func_list)
{
*func_list = android_funcs;
return ALC_TRUE;
}
void alc_android_deinit(void)
{
JNIEnv* env = GetEnv();
/* release cached AudioTrack class */
(*env)->DeleteGlobalRef(env, cAudioTrack);
}
void alc_android_probe(enum DevProbe type)
{
switch(type)
{
case DEVICE_PROBE:
AppendDeviceList(android_device);
break;
case ALL_DEVICE_PROBE:
AppendAllDeviceList(android_device);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
@@ -0,0 +1,709 @@
/**
* 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 "AL/al.h"
#include "AL/alc.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));
device->ExtraData = data;
err = OpenAComponent(comp, &data->audioUnit);
if(err != noErr)
{
ERR("OpenAComponent failed\n");
free(data);
device->ExtraData = NULL;
return ALC_INVALID_VALUE;
}
return ALC_NO_ERROR;
}
static void ca_close_playback(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
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;
/* init and start the default audio unit... */
err = AudioUnitInitialize(data->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
return ALC_FALSE;
}
err = AudioOutputUnitStart(data->audioUnit);
if(err != noErr)
{
ERR("AudioOutputUnitStart failed\n");
return ALC_FALSE;
}
/* 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)
{
if((device->Flags&DEVICE_FREQUENCY_REQUEST))
ERR("CoreAudio does not support changing sample rates (wanted %dhz, got %dhz)\n", device->Frequency, streamFormat.mSampleRate);
device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
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:
if((device->Flags&DEVICE_CHANNELS_REQUEST) &&
device->FmtChans != DevFmtMono)
{
ERR("Failed to set %s, got Mono instead\n", DevFmtChannelsString(device->FmtChans));
device->Flags &= ~DEVICE_CHANNELS_REQUEST;
}
device->FmtChans = DevFmtMono;
break;
case 2:
if((device->Flags&DEVICE_CHANNELS_REQUEST) &&
device->FmtChans != DevFmtStereo)
{
ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(device->FmtChans));
device->Flags &= ~DEVICE_CHANNELS_REQUEST;
}
device->FmtChans = DevFmtStereo;
break;
case 4:
if((device->Flags&DEVICE_CHANNELS_REQUEST) &&
device->FmtChans != DevFmtQuad)
{
ERR("Failed to set %s, got Quad instead\n", DevFmtChannelsString(device->FmtChans));
device->Flags &= ~DEVICE_CHANNELS_REQUEST;
}
device->FmtChans = DevFmtQuad;
break;
case 6:
if((device->Flags&DEVICE_CHANNELS_REQUEST) &&
device->FmtChans != DevFmtX51)
{
ERR("Failed to set %s, got 5.1 Surround instead\n", DevFmtChannelsString(device->FmtChans));
device->Flags &= ~DEVICE_CHANNELS_REQUEST;
}
device->FmtChans = DevFmtX51;
break;
case 7:
if((device->Flags&DEVICE_CHANNELS_REQUEST) &&
device->FmtChans != DevFmtX61)
{
ERR("Failed to set %s, got 6.1 Surround instead\n", DevFmtChannelsString(device->FmtChans));
device->Flags &= ~DEVICE_CHANNELS_REQUEST;
}
device->FmtChans = DevFmtX61;
break;
case 8:
if((device->Flags&DEVICE_CHANNELS_REQUEST) &&
device->FmtChans != DevFmtX71)
{
ERR("Failed to set %s, got 7.1 Surround instead\n", DevFmtChannelsString(device->FmtChans));
device->Flags &= ~DEVICE_CHANNELS_REQUEST;
}
device->FmtChans = DevFmtX71;
break;
default:
ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame);
device->Flags &= ~DEVICE_CHANNELS_REQUEST;
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;
switch(device->FmtType)
{
case DevFmtUByte:
device->FmtType = DevFmtByte;
/* fall-through */
case DevFmtByte:
streamFormat.mBitsPerChannel = 8;
streamFormat.mBytesPerPacket = streamFormat.mChannelsPerFrame;
streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame;
break;
case DevFmtUShort:
case DevFmtFloat:
device->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
streamFormat.mBitsPerChannel = 16;
streamFormat.mBytesPerPacket = 2 * streamFormat.mChannelsPerFrame;
streamFormat.mBytesPerFrame = 2 * streamFormat.mChannelsPerFrame;
break;
}
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
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;
}
return ALC_TRUE;
}
static void ca_stop_playback(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err;
AudioOutputUnitStop(data->audioUnit);
err = AudioUnitUninitialize(data->audioUnit);
if(err != noErr)
ERR("-- AudioUnitUninitialize 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 DevFmtFloat:
requestedFormat.mBitsPerChannel = 32;
requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
break;
case DevFmtByte:
case DevFmtUShort:
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_stop_playback,
ca_open_capture,
ca_close_capture,
ca_start_capture,
ca_stop_capture,
ca_capture_samples,
ca_available_samples
};
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 DEVICE_PROBE:
AppendDeviceList(ca_device);
break;
case ALL_DEVICE_PROBE:
AppendAllDeviceList(ca_device);
break;
case CAPTURE_DEVICE_PROBE:
AppendCaptureDeviceList(ca_device);
break;
}
}
@@ -0,0 +1,610 @@
/**
* 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 <dsound.h>
#include <cguid.h>
#include <mmreg.h>
#ifndef _WAVEFORMATEXTENSIBLE_
#include <ks.h>
#include <ksmedia.h>
#endif
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
#ifndef DSSPEAKER_5POINT1
#define DSSPEAKER_5POINT1 6
#endif
#ifndef DSSPEAKER_7POINT1
#define DSSPEAKER_7POINT1 7
#endif
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
static HMODULE ds_handle;
static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext);
#define DirectSoundCreate pDirectSoundCreate
#define DirectSoundEnumerateA pDirectSoundEnumerateA
typedef struct {
// DirectSound Playback Device
IDirectSound *lpDS;
IDirectSoundBuffer *DSpbuffer;
IDirectSoundBuffer *DSsbuffer;
IDirectSoundNotify *DSnotify;
HANDLE hNotifyEvent;
volatile int killNow;
ALvoid *thread;
} DSoundData;
typedef struct {
ALCchar *name;
GUID guid;
} DevMap;
static const ALCchar dsDevice[] = "DirectSound Default";
static DevMap *DeviceList;
static ALuint NumDevices;
#define MAX_UPDATES 128
static ALCboolean DSoundLoad(void)
{
ALCboolean ok = ALC_TRUE;
if(!ds_handle)
{
ds_handle = LoadLibraryA("dsound.dll");
if(ds_handle == NULL)
{
ERR("Failed to load dsound.dll\n");
return ALC_FALSE;
}
#define LOAD_FUNC(x) do { \
if((p##x = (void*)GetProcAddress(ds_handle, #x)) == NULL) { \
ERR("Could not load %s from dsound.dll\n", #x); \
ok = ALC_FALSE; \
} \
} while(0)
LOAD_FUNC(DirectSoundCreate);
LOAD_FUNC(DirectSoundEnumerateA);
#undef LOAD_FUNC
if(!ok)
{
FreeLibrary(ds_handle);
ds_handle = NULL;
}
}
return ok;
}
static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
{
char str[1024];
void *temp;
int count;
ALuint i;
(void)data;
(void)drvname;
if(!guid)
return TRUE;
count = 0;
do {
if(count == 0)
snprintf(str, sizeof(str), "%s", desc);
else
snprintf(str, sizeof(str), "%s #%d", desc, count+1);
count++;
for(i = 0;i < NumDevices;i++)
{
if(strcmp(str, DeviceList[i].name) == 0)
break;
}
} while(i != NumDevices);
temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
if(temp)
{
DeviceList = temp;
DeviceList[NumDevices].name = strdup(str);
DeviceList[NumDevices].guid = *guid;
NumDevices++;
}
return TRUE;
}
static ALuint DSoundProc(ALvoid *ptr)
{
ALCdevice *pDevice = (ALCdevice*)ptr;
DSoundData *pData = (DSoundData*)pDevice->ExtraData;
DSBCAPS DSBCaps;
DWORD LastCursor = 0;
DWORD PlayCursor;
VOID *WritePtr1, *WritePtr2;
DWORD WriteCnt1, WriteCnt2;
BOOL Playing = FALSE;
DWORD FrameSize;
DWORD FragSize;
DWORD avail;
HRESULT err;
SetRTPriority();
memset(&DSBCaps, 0, sizeof(DSBCaps));
DSBCaps.dwSize = sizeof(DSBCaps);
err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
if(FAILED(err))
{
ERR("Failed to get buffer caps: 0x%lx\n", err);
aluHandleDisconnect(pDevice);
return 1;
}
FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
FragSize = pDevice->UpdateSize * FrameSize;
IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
while(!pData->killNow)
{
// Get current play cursor
IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
if(avail < FragSize)
{
if(!Playing)
{
err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
if(FAILED(err))
{
ERR("Failed to play buffer: 0x%lx\n", err);
aluHandleDisconnect(pDevice);
return 1;
}
Playing = TRUE;
}
avail = WaitForSingleObjectEx(pData->hNotifyEvent, 2000, FALSE);
if(avail != WAIT_OBJECT_0)
ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
continue;
}
avail -= avail%FragSize;
// Lock output buffer
WriteCnt1 = 0;
WriteCnt2 = 0;
err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
// If the buffer is lost, restore it and lock
if(err == DSERR_BUFFERLOST)
{
WARN("Buffer lost, restoring...\n");
err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
if(SUCCEEDED(err))
{
Playing = FALSE;
LastCursor = 0;
err = IDirectSoundBuffer_Lock(pData->DSsbuffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
}
}
// Successfully locked the output buffer
if(SUCCEEDED(err))
{
// If we have an active context, mix data directly into output buffer otherwise fill with silence
aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize);
aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize);
// Unlock output buffer only when successfully locked
IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
}
else
{
ERR("Buffer lock error: %#lx\n", err);
aluHandleDisconnect(pDevice);
return 1;
}
// Update old write cursor location
LastCursor += WriteCnt1+WriteCnt2;
LastCursor %= DSBCaps.dwBufferBytes;
}
return 0;
}
static ALCenum DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
{
DSoundData *pData = NULL;
LPGUID guid = NULL;
HRESULT hr;
if(!deviceName)
deviceName = dsDevice;
else if(strcmp(deviceName, dsDevice) != 0)
{
ALuint i;
if(!DeviceList)
{
hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL);
if(FAILED(hr))
ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
}
for(i = 0;i < NumDevices;i++)
{
if(strcmp(deviceName, DeviceList[i].name) == 0)
{
guid = &DeviceList[i].guid;
break;
}
}
if(i == NumDevices)
return ALC_INVALID_VALUE;
}
//Initialise requested device
pData = calloc(1, sizeof(DSoundData));
if(!pData)
return ALC_OUT_OF_MEMORY;
hr = DS_OK;
pData->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(pData->hNotifyEvent == NULL)
hr = E_FAIL;
//DirectSound Init code
if(SUCCEEDED(hr))
hr = DirectSoundCreate(guid, &pData->lpDS, NULL);
if(SUCCEEDED(hr))
hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
if(FAILED(hr))
{
if(pData->lpDS)
IDirectSound_Release(pData->lpDS);
if(pData->hNotifyEvent)
CloseHandle(pData->hNotifyEvent);
free(pData);
ERR("Device init failed: 0x%08lx\n", hr);
return ALC_INVALID_VALUE;
}
device->szDeviceName = strdup(deviceName);
device->ExtraData = pData;
return ALC_NO_ERROR;
}
static void DSoundClosePlayback(ALCdevice *device)
{
DSoundData *pData = device->ExtraData;
IDirectSound_Release(pData->lpDS);
CloseHandle(pData->hNotifyEvent);
free(pData);
device->ExtraData = NULL;
}
static ALCboolean DSoundResetPlayback(ALCdevice *device)
{
DSoundData *pData = (DSoundData*)device->ExtraData;
DSBUFFERDESC DSBDescription;
WAVEFORMATEXTENSIBLE OutputType;
DWORD speakers;
HRESULT hr;
memset(&OutputType, 0, sizeof(OutputType));
switch(device->FmtType)
{
case DevFmtByte:
device->FmtType = DevFmtUByte;
break;
case DevFmtUShort:
device->FmtType = DevFmtShort;
break;
case DevFmtUByte:
case DevFmtShort:
case DevFmtFloat:
break;
}
hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
if(SUCCEEDED(hr))
{
if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
{
speakers = DSSPEAKER_CONFIG(speakers);
if(speakers == DSSPEAKER_MONO)
device->FmtChans = DevFmtMono;
else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
device->FmtChans = DevFmtStereo;
else if(speakers == DSSPEAKER_QUAD)
device->FmtChans = DevFmtQuad;
else if(speakers == DSSPEAKER_5POINT1)
device->FmtChans = DevFmtX51;
else if(speakers == DSSPEAKER_7POINT1)
device->FmtChans = DevFmtX71;
else
ERR("Unknown system speaker config: 0x%lx\n", speakers);
}
switch(device->FmtChans)
{
case DevFmtMono:
OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
break;
case DevFmtStereo:
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT;
break;
case DevFmtQuad:
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_BACK_LEFT |
SPEAKER_BACK_RIGHT;
break;
case DevFmtX51:
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_LEFT |
SPEAKER_BACK_RIGHT;
break;
case DevFmtX51Side:
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_SIDE_LEFT |
SPEAKER_SIDE_RIGHT;
break;
case DevFmtX61:
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_CENTER |
SPEAKER_SIDE_LEFT |
SPEAKER_SIDE_RIGHT;
break;
case DevFmtX71:
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_LEFT |
SPEAKER_BACK_RIGHT |
SPEAKER_SIDE_LEFT |
SPEAKER_SIDE_RIGHT;
break;
}
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
OutputType.Format.nSamplesPerSec = device->Frequency;
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
OutputType.Format.cbSize = 0;
}
if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
{
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
if(device->FmtType == DevFmtFloat)
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
else
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
else
{
if(SUCCEEDED(hr))
{
memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
DSBDescription.dwSize=sizeof(DSBUFFERDESC);
DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
}
if(SUCCEEDED(hr))
hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
}
if(SUCCEEDED(hr))
{
if(device->NumUpdates > MAX_UPDATES)
{
device->UpdateSize = (device->UpdateSize*device->NumUpdates +
MAX_UPDATES-1) / MAX_UPDATES;
device->NumUpdates = MAX_UPDATES;
}
memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
DSBDescription.dwSize=sizeof(DSBUFFERDESC);
DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;
DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
OutputType.Format.nBlockAlign;
DSBDescription.lpwfxFormat=&OutputType.Format;
hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
}
if(SUCCEEDED(hr))
{
hr = IDirectSoundBuffer_QueryInterface(pData->DSsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&pData->DSnotify);
if(SUCCEEDED(hr))
{
DSBPOSITIONNOTIFY notifies[MAX_UPDATES];
ALuint i;
for(i = 0;i < device->NumUpdates;++i)
{
notifies[i].dwOffset = i * device->UpdateSize *
OutputType.Format.nBlockAlign;
notifies[i].hEventNotify = pData->hNotifyEvent;
}
if(IDirectSoundNotify_SetNotificationPositions(pData->DSnotify, device->NumUpdates, notifies) != DS_OK)
hr = E_FAIL;
}
}
if(SUCCEEDED(hr))
{
ResetEvent(pData->hNotifyEvent);
SetDefaultWFXChannelOrder(device);
pData->thread = StartThread(DSoundProc, device);
if(pData->thread == NULL)
hr = E_FAIL;
}
if(FAILED(hr))
{
if(pData->DSnotify != NULL)
IDirectSoundNotify_Release(pData->DSnotify);
pData->DSnotify = NULL;
if(pData->DSsbuffer != NULL)
IDirectSoundBuffer_Release(pData->DSsbuffer);
pData->DSsbuffer = NULL;
if(pData->DSpbuffer != NULL)
IDirectSoundBuffer_Release(pData->DSpbuffer);
pData->DSpbuffer = NULL;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void DSoundStopPlayback(ALCdevice *device)
{
DSoundData *pData = device->ExtraData;
if(!pData->thread)
return;
pData->killNow = 1;
StopThread(pData->thread);
pData->thread = NULL;
pData->killNow = 0;
IDirectSoundNotify_Release(pData->DSnotify);
pData->DSnotify = NULL;
IDirectSoundBuffer_Release(pData->DSsbuffer);
pData->DSsbuffer = NULL;
if(pData->DSpbuffer != NULL)
IDirectSoundBuffer_Release(pData->DSpbuffer);
pData->DSpbuffer = NULL;
}
static const BackendFuncs DSoundFuncs = {
DSoundOpenPlayback,
DSoundClosePlayback,
DSoundResetPlayback,
DSoundStopPlayback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
ALCboolean alcDSoundInit(BackendFuncs *FuncList)
{
if(!DSoundLoad())
return ALC_FALSE;
*FuncList = DSoundFuncs;
return ALC_TRUE;
}
void alcDSoundDeinit(void)
{
ALuint i;
for(i = 0;i < NumDevices;++i)
free(DeviceList[i].name);
free(DeviceList);
DeviceList = NULL;
NumDevices = 0;
if(ds_handle)
FreeLibrary(ds_handle);
ds_handle = NULL;
}
void alcDSoundProbe(enum DevProbe type)
{
HRESULT hr;
ALuint i;
switch(type)
{
case DEVICE_PROBE:
AppendDeviceList(dsDevice);
break;
case ALL_DEVICE_PROBE:
for(i = 0;i < NumDevices;++i)
free(DeviceList[i].name);
free(DeviceList);
DeviceList = NULL;
NumDevices = 0;
hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL);
if(FAILED(hr))
ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
else
{
for(i = 0;i < NumDevices;i++)
AppendAllDeviceList(DeviceList[i].name);
}
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
@@ -0,0 +1,77 @@
/**
* 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 "AL/al.h"
#include "AL/alc.h"
static ALCenum loopback_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
device->szDeviceName = 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 void loopback_stop_playback(ALCdevice *device)
{
(void)device;
}
static const BackendFuncs loopback_funcs = {
loopback_open_playback,
loopback_close_playback,
loopback_reset_playback,
loopback_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
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;
}
@@ -0,0 +1,780 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2011 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#define COBJMACROS
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#include <cguid.h>
#include <mmreg.h>
#ifndef _WAVEFORMATEXTENSIBLE_
#include <ks.h>
#include <ksmedia.h>
#endif
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
#define MONO SPEAKER_FRONT_CENTER
#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
#define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
#define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
#define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
#define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
typedef struct {
IMMDevice *mmdev;
IAudioClient *client;
HANDLE hNotifyEvent;
HANDLE MsgEvent;
volatile int killNow;
ALvoid *thread;
} MMDevApiData;
static const ALCchar mmDevice[] = "WASAPI Default";
static HANDLE ThreadHdl;
static DWORD ThreadID;
typedef struct {
HANDLE FinishedEvt;
HRESULT result;
} ThreadRequest;
#define WM_USER_OpenDevice (WM_USER+0)
#define WM_USER_ResetDevice (WM_USER+1)
#define WM_USER_StopDevice (WM_USER+2)
#define WM_USER_CloseDevice (WM_USER+3)
static HRESULT WaitForResponse(ThreadRequest *req)
{
if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
return req->result;
ERR("Message response error: %lu\n", GetLastError());
return E_FAIL;
}
static ALuint MMDevApiProc(ALvoid *ptr)
{
ALCdevice *device = ptr;
MMDevApiData *data = device->ExtraData;
union {
IAudioRenderClient *iface;
void *ptr;
} render;
UINT32 written, len;
BYTE *buffer;
HRESULT hr;
hr = CoInitialize(NULL);
if(FAILED(hr))
{
ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
aluHandleDisconnect(device);
return 0;
}
hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &render.ptr);
if(FAILED(hr))
{
ERR("Failed to get AudioRenderClient service: 0x%08lx\n", hr);
aluHandleDisconnect(device);
return 0;
}
SetRTPriority();
while(!data->killNow)
{
hr = IAudioClient_GetCurrentPadding(data->client, &written);
if(FAILED(hr))
{
ERR("Failed to get padding: 0x%08lx\n", hr);
aluHandleDisconnect(device);
break;
}
len = device->UpdateSize*device->NumUpdates - written;
if(len < device->UpdateSize)
{
DWORD res;
res = WaitForSingleObjectEx(data->hNotifyEvent, 2000, FALSE);
if(res != WAIT_OBJECT_0)
ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
continue;
}
len -= len%device->UpdateSize;
hr = IAudioRenderClient_GetBuffer(render.iface, len, &buffer);
if(SUCCEEDED(hr))
{
aluMixData(device, buffer, len);
hr = IAudioRenderClient_ReleaseBuffer(render.iface, len, 0);
}
if(FAILED(hr))
{
ERR("Failed to buffer data: 0x%08lx\n", hr);
aluHandleDisconnect(device);
break;
}
}
IAudioRenderClient_Release(render.iface);
CoUninitialize();
return 0;
}
static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
{
memset(out, 0, sizeof(*out));
if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
*out = *(WAVEFORMATEXTENSIBLE*)in;
else if(in->wFormatTag == WAVE_FORMAT_PCM)
{
out->Format = *in;
out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
out->Format.cbSize = sizeof(*out) - sizeof(*in);
if(out->Format.nChannels == 1)
out->dwChannelMask = MONO;
else if(out->Format.nChannels == 2)
out->dwChannelMask = STEREO;
else
ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
{
out->Format = *in;
out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
out->Format.cbSize = sizeof(*out) - sizeof(*in);
if(out->Format.nChannels == 1)
out->dwChannelMask = MONO;
else if(out->Format.nChannels == 2)
out->dwChannelMask = STEREO;
else
ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
}
else
{
ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
return ALC_FALSE;
}
return ALC_TRUE;
}
static HRESULT DoReset(ALCdevice *device)
{
MMDevApiData *data = device->ExtraData;
WAVEFORMATEXTENSIBLE OutputType;
WAVEFORMATEX *wfx = NULL;
REFERENCE_TIME min_per;
UINT32 buffer_len, min_len;
HRESULT hr;
hr = IAudioClient_GetMixFormat(data->client, &wfx);
if(FAILED(hr))
{
ERR("Failed to get mix format: 0x%08lx\n", hr);
return hr;
}
if(!MakeExtensible(&OutputType, wfx))
{
CoTaskMemFree(wfx);
return E_FAIL;
}
CoTaskMemFree(wfx);
wfx = NULL;
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
device->Frequency = OutputType.Format.nSamplesPerSec;
if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
{
if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
device->FmtChans = DevFmtMono;
else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
device->FmtChans = DevFmtStereo;
else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
device->FmtChans = DevFmtQuad;
else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
device->FmtChans = DevFmtX51;
else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
device->FmtChans = DevFmtX51Side;
else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
device->FmtChans = DevFmtX61;
else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
device->FmtChans = DevFmtX71;
else
ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
}
switch(device->FmtChans)
{
case DevFmtMono:
OutputType.Format.nChannels = 1;
OutputType.dwChannelMask = MONO;
break;
case DevFmtStereo:
OutputType.Format.nChannels = 2;
OutputType.dwChannelMask = STEREO;
break;
case DevFmtQuad:
OutputType.Format.nChannels = 4;
OutputType.dwChannelMask = QUAD;
break;
case DevFmtX51:
OutputType.Format.nChannels = 6;
OutputType.dwChannelMask = X5DOT1;
break;
case DevFmtX51Side:
OutputType.Format.nChannels = 6;
OutputType.dwChannelMask = X5DOT1SIDE;
break;
case DevFmtX61:
OutputType.Format.nChannels = 7;
OutputType.dwChannelMask = X6DOT1;
break;
case DevFmtX71:
OutputType.Format.nChannels = 8;
OutputType.dwChannelMask = X7DOT1;
break;
}
switch(device->FmtType)
{
case DevFmtByte:
device->FmtType = DevFmtUByte;
/* fall-through */
case DevFmtUByte:
OutputType.Format.wBitsPerSample = 8;
OutputType.Samples.wValidBitsPerSample = 8;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
break;
case DevFmtUShort:
device->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
OutputType.Format.wBitsPerSample = 16;
OutputType.Samples.wValidBitsPerSample = 16;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
break;
case DevFmtFloat:
OutputType.Format.wBitsPerSample = 32;
OutputType.Samples.wValidBitsPerSample = 32;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
break;
}
OutputType.Format.nSamplesPerSec = device->Frequency;
OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
OutputType.Format.wBitsPerSample / 8;
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
OutputType.Format.nBlockAlign;
hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
if(FAILED(hr))
{
ERR("Failed to check format support: 0x%08lx\n", hr);
hr = IAudioClient_GetMixFormat(data->client, &wfx);
}
if(FAILED(hr))
{
ERR("Failed to find a supported format: 0x%08lx\n", hr);
return hr;
}
if(wfx != NULL)
{
if(!MakeExtensible(&OutputType, wfx))
{
CoTaskMemFree(wfx);
return E_FAIL;
}
CoTaskMemFree(wfx);
wfx = NULL;
if(device->Frequency != OutputType.Format.nSamplesPerSec)
{
if((device->Flags&DEVICE_FREQUENCY_REQUEST))
ERR("Failed to set %dhz, got %ldhz instead\n", device->Frequency, OutputType.Format.nSamplesPerSec);
device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
device->Frequency = OutputType.Format.nSamplesPerSec;
}
if(!((device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO) ||
(device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO) ||
(device->FmtChans == DevFmtQuad && OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD) ||
(device->FmtChans == DevFmtX51 && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1) ||
(device->FmtChans == DevFmtX51Side && OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE) ||
(device->FmtChans == DevFmtX61 && OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1) ||
(device->FmtChans == DevFmtX71 && OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)))
{
if((device->Flags&DEVICE_CHANNELS_REQUEST))
ERR("Failed to set %s, got %d channels (0x%08lx) instead\n", DevFmtChannelsString(device->FmtChans), OutputType.Format.nChannels, OutputType.dwChannelMask);
device->Flags &= ~DEVICE_CHANNELS_REQUEST;
if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
device->FmtChans = DevFmtMono;
else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
device->FmtChans = DevFmtStereo;
else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
device->FmtChans = DevFmtQuad;
else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
device->FmtChans = DevFmtX51;
else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
device->FmtChans = DevFmtX51Side;
else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
device->FmtChans = DevFmtX61;
else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
device->FmtChans = DevFmtX71;
else
{
ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
device->FmtChans = DevFmtStereo;
OutputType.Format.nChannels = 2;
OutputType.dwChannelMask = STEREO;
}
}
if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
{
if(OutputType.Samples.wValidBitsPerSample == 0)
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample ||
!((device->FmtType == DevFmtUByte && OutputType.Format.wBitsPerSample == 8) ||
(device->FmtType == DevFmtShort && OutputType.Format.wBitsPerSample == 16)))
{
ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample);
if(OutputType.Format.wBitsPerSample == 8)
device->FmtType = DevFmtUByte;
else if(OutputType.Format.wBitsPerSample == 16)
device->FmtType = DevFmtShort;
else
{
device->FmtType = DevFmtShort;
OutputType.Format.wBitsPerSample = 16;
}
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
}
}
else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
{
if(OutputType.Samples.wValidBitsPerSample == 0)
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
if(OutputType.Samples.wValidBitsPerSample != OutputType.Format.wBitsPerSample ||
!((device->FmtType == DevFmtFloat && OutputType.Format.wBitsPerSample == 32)))
{
ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device->FmtType), OutputType.Samples.wValidBitsPerSample, OutputType.Format.wBitsPerSample);
if(OutputType.Format.wBitsPerSample != 32)
{
device->FmtType = DevFmtFloat;
OutputType.Format.wBitsPerSample = 32;
}
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
}
}
else
{
ERR("Unhandled format sub-type\n");
device->FmtType = DevFmtShort;
OutputType.Format.wBitsPerSample = 16;
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
}
SetDefaultWFXChannelOrder(device);
hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
if(SUCCEEDED(hr))
{
min_len = (min_per*device->Frequency + 10000000-1) / 10000000;
if(min_len < device->UpdateSize)
min_len *= (device->UpdateSize + min_len/2)/min_len;
device->NumUpdates = (device->NumUpdates*device->UpdateSize + min_len/2) /
min_len;
device->NumUpdates = maxu(device->NumUpdates, 2);
device->UpdateSize = min_len;
hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
((REFERENCE_TIME)device->UpdateSize*
device->NumUpdates*10000000 +
device->Frequency-1) / device->Frequency,
0, &OutputType.Format, NULL);
}
if(FAILED(hr))
{
ERR("Failed to initialize audio client: 0x%08lx\n", hr);
return hr;
}
hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
if(FAILED(hr))
{
ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
return hr;
}
device->NumUpdates = buffer_len / device->UpdateSize;
if(device->NumUpdates <= 1)
{
device->NumUpdates = 1;
ERR("Audio client returned buffer_len < period*2; expect break up\n");
}
ResetEvent(data->hNotifyEvent);
hr = IAudioClient_SetEventHandle(data->client, data->hNotifyEvent);
if(SUCCEEDED(hr))
hr = IAudioClient_Start(data->client);
if(FAILED(hr))
{
ERR("Failed to start audio client: 0x%08lx\n", hr);
return hr;
}
data->thread = StartThread(MMDevApiProc, device);
if(!data->thread)
{
IAudioClient_Stop(data->client);
ERR("Failed to start thread\n");
return E_FAIL;
}
return hr;
}
static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
{
ThreadRequest *req = ptr;
IMMDeviceEnumerator *Enumerator;
ALuint deviceCount = 0;
MMDevApiData *data;
ALCdevice *device;
HRESULT hr;
MSG msg;
TRACE("Starting message thread\n");
hr = CoInitialize(NULL);
if(FAILED(hr))
{
WARN("Failed to initialize COM: 0x%08lx\n", hr);
req->result = hr;
SetEvent(req->FinishedEvt);
return 0;
}
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
if(FAILED(hr))
{
WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
CoUninitialize();
req->result = hr;
SetEvent(req->FinishedEvt);
return 0;
}
Enumerator = ptr;
IMMDeviceEnumerator_Release(Enumerator);
Enumerator = NULL;
CoUninitialize();
req->result = S_OK;
SetEvent(req->FinishedEvt);
TRACE("Starting message loop\n");
while(GetMessage(&msg, NULL, 0, 0))
{
TRACE("Got message %u\n", msg.message);
switch(msg.message)
{
case WM_USER_OpenDevice:
req = (ThreadRequest*)msg.wParam;
device = (ALCdevice*)msg.lParam;
data = device->ExtraData;
hr = S_OK;
if(++deviceCount == 1)
hr = CoInitialize(NULL);
if(SUCCEEDED(hr))
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
if(SUCCEEDED(hr))
{
Enumerator = ptr;
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
IMMDeviceEnumerator_Release(Enumerator);
Enumerator = NULL;
}
if(SUCCEEDED(hr))
hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
if(SUCCEEDED(hr))
data->client = ptr;
if(FAILED(hr))
{
if(data->mmdev)
IMMDevice_Release(data->mmdev);
data->mmdev = NULL;
}
req->result = hr;
SetEvent(req->FinishedEvt);
continue;
case WM_USER_ResetDevice:
req = (ThreadRequest*)msg.wParam;
device = (ALCdevice*)msg.lParam;
req->result = DoReset(device);
SetEvent(req->FinishedEvt);
continue;
case WM_USER_StopDevice:
req = (ThreadRequest*)msg.wParam;
device = (ALCdevice*)msg.lParam;
data = device->ExtraData;
if(data->thread)
{
data->killNow = 1;
StopThread(data->thread);
data->thread = NULL;
data->killNow = 0;
IAudioClient_Stop(data->client);
}
req->result = S_OK;
SetEvent(req->FinishedEvt);
continue;
case WM_USER_CloseDevice:
req = (ThreadRequest*)msg.wParam;
device = (ALCdevice*)msg.lParam;
data = device->ExtraData;
IAudioClient_Release(data->client);
data->client = NULL;
IMMDevice_Release(data->mmdev);
data->mmdev = NULL;
if(--deviceCount == 0)
CoUninitialize();
req->result = S_OK;
SetEvent(req->FinishedEvt);
continue;
default:
ERR("Unexpected message: %u\n", msg.message);
continue;
}
}
TRACE("Message loop finished\n");
return 0;
}
static BOOL MMDevApiLoad(void)
{
static HRESULT InitResult;
if(!ThreadHdl)
{
ThreadRequest req;
InitResult = E_FAIL;
req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
if(req.FinishedEvt == NULL)
ERR("Failed to create event: %lu\n", GetLastError());
else
{
ThreadHdl = CreateThread(NULL, 0, MMDevApiMsgProc, &req, 0, &ThreadID);
if(ThreadHdl != NULL)
InitResult = WaitForResponse(&req);
CloseHandle(req.FinishedEvt);
}
}
return SUCCEEDED(InitResult);
}
static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
{
MMDevApiData *data = NULL;
HRESULT hr;
if(!deviceName)
deviceName = mmDevice;
else if(strcmp(deviceName, mmDevice) != 0)
return ALC_INVALID_VALUE;
//Initialise requested device
data = calloc(1, sizeof(MMDevApiData));
if(!data)
return ALC_OUT_OF_MEMORY;
device->ExtraData = data;
hr = S_OK;
data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(data->hNotifyEvent == NULL || data->MsgEvent == NULL)
hr = E_FAIL;
if(SUCCEEDED(hr))
{
ThreadRequest req = { data->MsgEvent, 0 };
hr = E_FAIL;
if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
hr = WaitForResponse(&req);
}
if(FAILED(hr))
{
if(data->hNotifyEvent != NULL)
CloseHandle(data->hNotifyEvent);
data->hNotifyEvent = NULL;
if(data->MsgEvent != NULL)
CloseHandle(data->MsgEvent);
data->MsgEvent = NULL;
free(data);
device->ExtraData = NULL;
ERR("Device init failed: 0x%08lx\n", hr);
return ALC_INVALID_VALUE;
}
device->szDeviceName = strdup(deviceName);
return ALC_NO_ERROR;
}
static void MMDevApiClosePlayback(ALCdevice *device)
{
MMDevApiData *data = device->ExtraData;
ThreadRequest req = { data->MsgEvent, 0 };
if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
(void)WaitForResponse(&req);
CloseHandle(data->MsgEvent);
data->MsgEvent = NULL;
CloseHandle(data->hNotifyEvent);
data->hNotifyEvent = NULL;
free(data);
device->ExtraData = NULL;
}
static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
{
MMDevApiData *data = device->ExtraData;
ThreadRequest req = { data->MsgEvent, 0 };
HRESULT hr = E_FAIL;
if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
hr = WaitForResponse(&req);
return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
}
static void MMDevApiStopPlayback(ALCdevice *device)
{
MMDevApiData *data = device->ExtraData;
ThreadRequest req = { data->MsgEvent, 0 };
if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
(void)WaitForResponse(&req);
}
static const BackendFuncs MMDevApiFuncs = {
MMDevApiOpenPlayback,
MMDevApiClosePlayback,
MMDevApiResetPlayback,
MMDevApiStopPlayback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
{
if(!MMDevApiLoad())
return ALC_FALSE;
*FuncList = MMDevApiFuncs;
return ALC_TRUE;
}
void alcMMDevApiDeinit(void)
{
if(ThreadHdl)
{
TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
CloseHandle(ThreadHdl);
ThreadHdl = NULL;
}
}
void alcMMDevApiProbe(enum DevProbe type)
{
switch(type)
{
case DEVICE_PROBE:
AppendDeviceList(mmDevice);
break;
case ALL_DEVICE_PROBE:
AppendAllDeviceList(mmDevice);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
+164
View File
@@ -0,0 +1,164 @@
/**
* 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>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.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. Add the remainder of the cycle to the available
* count and reset the number of samples done */
avail += (ALuint64)0xFFFFFFFFu*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->szDeviceName = 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)
{
null_data *data = (null_data*)device->ExtraData;
SetDefaultWFXChannelOrder(device);
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_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
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 DEVICE_PROBE:
AppendDeviceList(nullDevice);
break;
case ALL_DEVICE_PROBE:
AppendAllDeviceList(nullDevice);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
@@ -0,0 +1,430 @@
/*
* 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 "AL/al.h"
#include "AL/alc.h"
#include <SLES/OpenSLES.h>
#ifdef HAVE_ANDROID
#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";
#ifdef HAVE_OPENSL_1_1
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";
#endif
}
return "Unknown error code";
}
#define PRINTERR(x, s) do { \
if((x) != SL_RESULT_SUCCESS) \
ERR("%s: %s\n", (s), res_str((x))); \
} while(0)
/* this callback handler is called every time a buffer finishes playing */
static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context)
{
ALCdevice *Device = context;
osl_data *data = Device->ExtraData;
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->szDeviceName = strdup(deviceName);
Device->ExtraData = data;
return ALC_NO_ERROR;
}
static void opensl_close_playback(ALCdevice *Device)
{
osl_data *data = Device->ExtraData;
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;
SLAndroidSimpleBufferQueueItf bufferQueue;
SLDataLocator_OutputMix loc_outmix;
SLDataFormat_PCM format_pcm;
SLDataSource audioSrc;
SLDataSink audioSnk;
SLPlayItf player;
SLInterfaceID id;
SLboolean req;
SLresult result;
ALuint i;
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);
#ifdef HAVE_OPENSL_1_1
format_pcm.endianness = SL_BYTEORDER_NATIVE;
#elif _BYTE_ORDER == _BIG_ENDIAN
format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
#else
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
#endif
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;
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)
{
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;
if(data->bufferQueueObject != NULL)
SLObjectItf_Destroy(data->bufferQueueObject);
data->bufferQueueObject = NULL;
free(data->buffer);
data->buffer = NULL;
data->bufferSize = 0;
}
static const BackendFuncs opensl_funcs = {
opensl_open_playback,
opensl_close_playback,
opensl_reset_playback,
opensl_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
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 DEVICE_PROBE:
AppendDeviceList(opensl_device);
break;
case ALL_DEVICE_PROBE:
AppendAllDeviceList(opensl_device);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
+535
View File
@@ -0,0 +1,535 @@
/**
* 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 "AL/al.h"
#include "AL/alc.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";
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 *pDevice = (ALCdevice*)ptr;
oss_data *data = (oss_data*)pDevice->ExtraData;
ALint frameSize;
ssize_t wrote;
SetRTPriority();
frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
while(!data->killNow && pDevice->Connected)
{
ALint len = data->data_size;
ALubyte *WritePtr = data->mix_data;
aluMixData(pDevice, 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));
aluHandleDisconnect(pDevice);
break;
}
Sleep(1);
continue;
}
len -= wrote;
WritePtr += wrote;
}
}
return 0;
}
static ALuint OSSCaptureProc(ALvoid *ptr)
{
ALCdevice *pDevice = (ALCdevice*)ptr;
oss_data *data = (oss_data*)pDevice->ExtraData;
int frameSize;
int amt;
SetRTPriority();
frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
while(!data->killNow)
{
amt = read(data->fd, data->mix_data, data->data_size);
if(amt < 0)
{
ERR("read failed: %s\n", strerror(errno));
aluHandleDisconnect(pDevice);
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)
{
char driver[64];
oss_data *data;
strncpy(driver, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver)-1);
driver[sizeof(driver)-1] = 0;
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(driver, O_WRONLY);
if(data->fd == -1)
{
free(data);
ERR("Could not open %s: %s\n", driver, strerror(errno));
return ALC_INVALID_VALUE;
}
device->szDeviceName = 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 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;
}
if(device->Frequency != (ALuint)ossSpeed)
{
if((device->Flags&DEVICE_FREQUENCY_REQUEST))
ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, ossSpeed);
device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
device->Frequency = ossSpeed;
}
device->UpdateSize = info.fragsize / frameSize;
device->NumUpdates = info.fragments + 1;
data->data_size = device->UpdateSize * frameSize;
data->mix_data = calloc(1, data->data_size);
SetDefaultChannelOrder(device);
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;
char driver[64];
oss_data *data;
int ossFormat;
int ossSpeed;
char *err;
strncpy(driver, GetConfigValue("oss", "capture", "/dev/dsp"), sizeof(driver)-1);
driver[sizeof(driver)-1] = 0;
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(driver, O_RDONLY);
if(data->fd == -1)
{
free(data);
ERR("Could not open %s: %s\n", driver, 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 DevFmtFloat:
free(data);
ERR("%s capture samples not supported on OSS\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->szDeviceName = 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 *pDevice)
{
oss_data *data = (oss_data*)pDevice->ExtraData;
data->doCapture = 1;
}
static void oss_stop_capture(ALCdevice *pDevice)
{
oss_data *data = (oss_data*)pDevice->ExtraData;
data->doCapture = 0;
}
static ALCenum oss_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
{
oss_data *data = (oss_data*)pDevice->ExtraData;
ReadRingBuffer(data->ring, pBuffer, lSamples);
return ALC_NO_ERROR;
}
static ALCuint oss_available_samples(ALCdevice *pDevice)
{
oss_data *data = (oss_data*)pDevice->ExtraData;
return RingBufferSize(data->ring);
}
static const BackendFuncs oss_funcs = {
oss_open_playback,
oss_close_playback,
oss_reset_playback,
oss_stop_playback,
oss_open_capture,
oss_close_capture,
oss_start_capture,
oss_stop_capture,
oss_capture_samples,
oss_available_samples
};
ALCboolean alc_oss_init(BackendFuncs *func_list)
{
*func_list = oss_funcs;
return ALC_TRUE;
}
void alc_oss_deinit(void)
{
}
void alc_oss_probe(enum DevProbe type)
{
switch(type)
{
case DEVICE_PROBE:
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0)
#endif
AppendDeviceList(oss_device);
}
break;
case ALL_DEVICE_PROBE:
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0)
#endif
AppendAllDeviceList(oss_device);
}
break;
case CAPTURE_DEVICE_PROBE:
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(GetConfigValue("oss", "capture", "/dev/dsp"), &buf) == 0)
#endif
AppendCaptureDeviceList(oss_device);
}
break;
}
}
@@ -0,0 +1,441 @@
/**
* 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 "AL/al.h"
#include "AL/alc.h"
#include <portaudio.h>
static const ALCchar pa_device[] = "PortAudio Default";
static void *pa_handle;
#ifdef HAVE_DYNLOAD
#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)
{
if(!pa_handle)
{
PaError err;
#ifdef HAVE_DYNLOAD
#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
#else
pa_handle = (void*)0xDEADBEEF;
#endif
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;
}
}
return ALC_TRUE;
}
typedef struct {
PaStream *stream;
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)
{
PaStreamParameters outParams;
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;
device->ExtraData = data;
outParams.device = -1;
if(!ConfigValueInt("port", "device", &outParams.device) || outParams.device < 0)
outParams.device = Pa_GetDefaultOutputDevice();
outParams.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
(float)device->Frequency;
outParams.hostApiSpecificStreamInfo = NULL;
switch(device->FmtType)
{
case DevFmtByte:
outParams.sampleFormat = paInt8;
break;
case DevFmtUByte:
outParams.sampleFormat = paUInt8;
break;
case DevFmtUShort:
device->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
outParams.sampleFormat = paInt16;
break;
case DevFmtFloat:
outParams.sampleFormat = paFloat32;
break;
}
outParams.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
SetDefaultChannelOrder(device);
err = Pa_OpenStream(&data->stream, NULL, &outParams, device->Frequency,
device->UpdateSize, paNoFlag, pa_callback, device);
if(err != paNoError)
{
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
device->ExtraData = NULL;
free(data);
return ALC_INVALID_VALUE;
}
device->szDeviceName = strdup(deviceName);
if((ALuint)outParams.channelCount != ChannelsFromDevFmt(device->FmtChans))
{
if(outParams.channelCount != 1 && outParams.channelCount != 2)
{
ERR("Unhandled channel count: %u\n", outParams.channelCount);
Pa_CloseStream(data->stream);
device->ExtraData = NULL;
free(data);
return ALC_INVALID_VALUE;
}
if((device->Flags&DEVICE_CHANNELS_REQUEST))
ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), outParams.channelCount);
device->Flags &= ~DEVICE_CHANNELS_REQUEST;
device->FmtChans = ((outParams.channelCount==1) ? DevFmtMono : DevFmtStereo);
}
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;
PaError err;
streamInfo = Pa_GetStreamInfo(data->stream);
if(device->Frequency != streamInfo->sampleRate)
{
if((device->Flags&DEVICE_FREQUENCY_REQUEST))
ERR("PortAudio does not support changing sample rates (wanted %dhz, got %.1fhz)\n", device->Frequency, streamInfo->sampleRate);
device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
device->Frequency = streamInfo->sampleRate;
}
device->UpdateSize = data->update_size;
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)
{
PaStreamParameters inParams;
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;
inParams.device = -1;
if(!ConfigValueInt("port", "capture", &inParams.device) || inParams.device < 0)
inParams.device = Pa_GetDefaultOutputDevice();
inParams.suggestedLatency = 0.0f;
inParams.hostApiSpecificStreamInfo = NULL;
switch(device->FmtType)
{
case DevFmtByte:
inParams.sampleFormat = paInt8;
break;
case DevFmtUByte:
inParams.sampleFormat = paUInt8;
break;
case DevFmtShort:
inParams.sampleFormat = paInt16;
break;
case DevFmtFloat:
inParams.sampleFormat = paFloat32;
break;
case DevFmtUShort:
ERR("Unsigned short samples not supported\n");
goto error;
}
inParams.channelCount = ChannelsFromDevFmt(device->FmtChans);
err = Pa_OpenStream(&data->stream, &inParams, 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->szDeviceName = 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_stop_playback,
pa_open_capture,
pa_close_capture,
pa_start_capture,
pa_stop_capture,
pa_capture_samples,
pa_available_samples
};
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)
{
if(pa_handle)
{
Pa_Terminate();
#ifdef HAVE_DYNLOAD
CloseLib(pa_handle);
#endif
pa_handle = NULL;
}
}
void alc_pa_probe(enum DevProbe type)
{
switch(type)
{
case DEVICE_PROBE:
AppendDeviceList(pa_device);
break;
case ALL_DEVICE_PROBE:
AppendAllDeviceList(pa_device);
break;
case CAPTURE_DEVICE_PROBE:
AppendCaptureDeviceList(pa_device);
break;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,381 @@
/**
* 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 "AL/al.h"
#include "AL/alc.h"
#include <sndio.h>
static const ALCchar sndio_device[] = "SndIO Default";
static void *sndio_handle;
#ifdef HAVE_DYNLOAD
#define MAKE_FUNC(x) static typeof(x) * p##x
MAKE_FUNC(sio_initpar);
MAKE_FUNC(sio_open);
MAKE_FUNC(sio_close);
MAKE_FUNC(sio_setpar);
MAKE_FUNC(sio_getpar);
MAKE_FUNC(sio_getcap);
MAKE_FUNC(sio_onmove);
MAKE_FUNC(sio_write);
MAKE_FUNC(sio_read);
MAKE_FUNC(sio_start);
MAKE_FUNC(sio_stop);
MAKE_FUNC(sio_nfds);
MAKE_FUNC(sio_pollfd);
MAKE_FUNC(sio_revents);
MAKE_FUNC(sio_eof);
MAKE_FUNC(sio_setvol);
MAKE_FUNC(sio_onvol);
#define sio_initpar psio_initpar
#define sio_open psio_open
#define sio_close psio_close
#define sio_setpar psio_setpar
#define sio_getpar psio_getpar
#define sio_getcap psio_getcap
#define sio_onmove psio_onmove
#define sio_write psio_write
#define sio_read psio_read
#define sio_start psio_start
#define sio_stop psio_stop
#define sio_nfds psio_nfds
#define sio_pollfd psio_pollfd
#define sio_revents psio_revents
#define sio_eof psio_eof
#define sio_setvol psio_setvol
#define sio_onvol psio_onvol
#endif
static ALCboolean sndio_load(void)
{
if(!sndio_handle)
{
#ifdef HAVE_DYNLOAD
sndio_handle = LoadLib("libsndio.so");
if(!sndio_handle)
return ALC_FALSE;
#define LOAD_FUNC(f) do { \
p##f = GetSymbol(sndio_handle, #f); \
if(p##f == NULL) { \
CloseLib(sndio_handle); \
sndio_handle = NULL; \
return ALC_FALSE; \
} \
} while(0)
LOAD_FUNC(sio_initpar);
LOAD_FUNC(sio_open);
LOAD_FUNC(sio_close);
LOAD_FUNC(sio_setpar);
LOAD_FUNC(sio_getpar);
LOAD_FUNC(sio_getcap);
LOAD_FUNC(sio_onmove);
LOAD_FUNC(sio_write);
LOAD_FUNC(sio_read);
LOAD_FUNC(sio_start);
LOAD_FUNC(sio_stop);
LOAD_FUNC(sio_nfds);
LOAD_FUNC(sio_pollfd);
LOAD_FUNC(sio_revents);
LOAD_FUNC(sio_eof);
LOAD_FUNC(sio_setvol);
LOAD_FUNC(sio_onvol);
#undef LOAD_FUNC
#else
sndio_handle = (void*)0xDEADBEEF;
#endif
}
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");
aluHandleDisconnect(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->szDeviceName = 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:
device->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
par.bits = 16;
par.sig = 1;
break;
case DevFmtUShort:
par.bits = 16;
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.rate != device->Frequency)
{
if((device->Flags&DEVICE_FREQUENCY_REQUEST))
ERR("Failed to set frequency %uhz, got %uhz instead\n", device->Frequency, par.rate);
device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
device->Frequency = par.rate;
}
if(par.pchan != ChannelsFromDevFmt(device->FmtChans))
{
if(par.pchan != 1 && par.pchan != 2)
{
ERR("Unhandled channel count: %u\n", par.pchan);
return ALC_FALSE;
}
if((device->Flags&DEVICE_CHANNELS_REQUEST))
ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), par.pchan);
device->Flags &= ~DEVICE_CHANNELS_REQUEST;
device->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
}
if(par.bits != par.bps*8)
{
ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
return ALC_FALSE;
}
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
{
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);
if(!sio_start(data->sndHandle))
{
ERR("Error starting playback\n");
return ALC_FALSE;
}
data->data_size = device->UpdateSize * par.bps * par.pchan;
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_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
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)
{
#ifdef HAVE_DYNLOAD
if(sndio_handle)
CloseLib(sndio_handle);
sndio_handle = NULL;
#endif
}
void alc_sndio_probe(enum DevProbe type)
{
switch(type)
{
case DEVICE_PROBE:
AppendDeviceList(sndio_device);
break;
case ALL_DEVICE_PROBE:
AppendAllDeviceList(sndio_device);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
@@ -0,0 +1,282 @@
/**
* 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 "AL/al.h"
#include "AL/alc.h"
#include <sys/audioio.h>
static const ALCchar solaris_device[] = "Solaris Default";
typedef struct {
int fd;
volatile int killNow;
ALvoid *thread;
ALubyte *mix_data;
int data_size;
} solaris_data;
static ALuint SolarisProc(ALvoid *ptr)
{
ALCdevice *pDevice = (ALCdevice*)ptr;
solaris_data *data = (solaris_data*)pDevice->ExtraData;
ALint frameSize;
int wrote;
SetRTPriority();
frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
while(!data->killNow && pDevice->Connected)
{
ALint len = data->data_size;
ALubyte *WritePtr = data->mix_data;
aluMixData(pDevice, 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));
aluHandleDisconnect(pDevice);
break;
}
Sleep(1);
continue;
}
len -= wrote;
WritePtr += wrote;
}
}
return 0;
}
static ALCenum solaris_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
char driver[64];
solaris_data *data;
strncpy(driver, GetConfigValue("solaris", "device", "/dev/audio"), sizeof(driver)-1);
driver[sizeof(driver)-1] = 0;
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(driver, O_WRONLY);
if(data->fd == -1)
{
free(data);
ERR("Could not open %s: %s\n", driver, strerror(errno));
return ALC_INVALID_VALUE;
}
device->szDeviceName = 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 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_LINEAR &&
device->FmtType == DevFmtByte) ||
(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 &&
device->FmtType == DevFmtUByte) ||
(info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR &&
device->FmtType == DevFmtShort)))
{
ERR("Could not set %#x sample type, got %d (%#x)\n",
device->FmtType, info.play.precision, info.play.encoding);
return ALC_FALSE;
}
if(device->Frequency != info.play.sample_rate)
{
if((device->Flags&DEVICE_FREQUENCY_REQUEST))
ERR("Failed to set requested frequency %dhz, got %dhz instead\n", device->Frequency, info.play.sample_rate);
device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
device->Frequency = info.play.sample_rate;
}
device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1;
data->data_size = device->UpdateSize * frameSize;
data->mix_data = calloc(1, data->data_size);
SetDefaultChannelOrder(device);
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_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
ALCboolean alc_solaris_init(BackendFuncs *func_list)
{
*func_list = solaris_funcs;
return ALC_TRUE;
}
void alc_solaris_deinit(void)
{
}
void alc_solaris_probe(enum DevProbe type)
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(GetConfigValue("solaris", "device", "/dev/audio"), &buf) != 0)
return;
#endif
switch(type)
{
case DEVICE_PROBE:
AppendDeviceList(solaris_device);
break;
case ALL_DEVICE_PROBE:
AppendAllDeviceList(solaris_device);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
+355
View File
@@ -0,0 +1,355 @@
/**
* 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 "alMain.h"
#include "AL/al.h"
#include "AL/alc.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 *pDevice = (ALCdevice*)ptr;
wave_data *data = (wave_data*)pDevice->ExtraData;
ALuint frameSize;
ALuint now, start;
ALuint64 avail, done;
size_t fs;
union {
short s;
char b[sizeof(short)];
} uSB;
const ALuint restTime = (ALuint64)pDevice->UpdateSize * 1000 /
pDevice->Frequency / 2;
uSB.s = 1;
frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
done = 0;
start = timeGetTime();
while(!data->killNow && pDevice->Connected)
{
now = timeGetTime();
avail = (ALuint64)(now-start) * pDevice->Frequency / 1000;
if(avail < done)
{
/* Timer wrapped. Add the remainder of the cycle to the available
* count and reset the number of samples done */
avail += (ALuint64)0xFFFFFFFFu*pDevice->Frequency/1000 - done;
done = 0;
}
if(avail-done < pDevice->UpdateSize)
{
Sleep(restTime);
continue;
}
while(avail-done >= pDevice->UpdateSize)
{
aluMixData(pDevice, data->buffer, pDevice->UpdateSize);
done += pDevice->UpdateSize;
if(uSB.b[0] != 1)
{
ALuint bytesize = BytesFromDevFmt(pDevice->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, pDevice->UpdateSize,
data->f);
if(ferror(data->f))
{
ERR("Error writing to file\n");
aluHandleDisconnect(pDevice);
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->szDeviceName = 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 DevFmtUByte:
case DevFmtShort:
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);
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);
data->size = device->UpdateSize * channels * bits / 8;
data->buffer = malloc(data->size);
if(!data->buffer)
{
ERR("Buffer malloc failed\n");
return ALC_FALSE;
}
SetDefaultWFXChannelOrder(device);
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_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
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 DEVICE_PROBE:
AppendDeviceList(waveDevice);
break;
case ALL_DEVICE_PROBE:
AppendAllDeviceList(waveDevice);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
@@ -0,0 +1,764 @@
/**
* 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 "AL/al.h"
#include "AL/alc.h"
typedef struct {
// MMSYSTEM Device
volatile ALboolean bWaveShutdown;
HANDLE hWaveThreadEvent;
HANDLE hWaveThread;
DWORD ulWaveThreadID;
LONG lWaveBuffersCommitted;
WAVEHDR WaveBuffer[4];
union {
HWAVEIN In;
HWAVEOUT Out;
} hWaveHandle;
ALuint Frequency;
RingBuffer *pRing;
} WinMMData;
static const ALCchar woDefault[] = "WaveOut Default";
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 hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
{
ALCdevice *pDevice = (ALCdevice*)dwInstance;
WinMMData *pData = pDevice->ExtraData;
(void)hDevice;
(void)dwParam2;
if(uMsg != WOM_DONE)
return;
// Decrement number of buffers in use
InterlockedDecrement(&pData->lWaveBuffersCommitted);
if(pData->bWaveShutdown == AL_FALSE)
{
// Notify Wave Processor Thread that a Wave Header has returned
PostThreadMessage(pData->ulWaveThreadID, uMsg, 0, dwParam1);
}
else
{
if(pData->lWaveBuffersCommitted == 0)
{
// Post 'Quit' Message to WaveOut Processor Thread
PostThreadMessage(pData->ulWaveThreadID, WM_QUIT, 0, 0);
}
}
}
/*
PlaybackThreadProc
Used by "MMSYSTEM" Device. Called when a WaveOut buffer has used up its
audio data.
*/
static DWORD WINAPI PlaybackThreadProc(LPVOID lpParameter)
{
ALCdevice *pDevice = (ALCdevice*)lpParameter;
WinMMData *pData = pDevice->ExtraData;
LPWAVEHDR pWaveHdr;
ALuint FrameSize;
MSG msg;
FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
SetRTPriority();
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message != WOM_DONE || pData->bWaveShutdown)
continue;
pWaveHdr = ((LPWAVEHDR)msg.lParam);
aluMixData(pDevice, pWaveHdr->lpData, pWaveHdr->dwBufferLength/FrameSize);
// Send buffer back to play more data
waveOutWrite(pData->hWaveHandle.Out, pWaveHdr, sizeof(WAVEHDR));
InterlockedIncrement(&pData->lWaveBuffersCommitted);
}
// Signal Wave Thread completed event
if(pData->hWaveThreadEvent)
SetEvent(pData->hWaveThreadEvent);
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 hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
{
ALCdevice *pDevice = (ALCdevice*)dwInstance;
WinMMData *pData = pDevice->ExtraData;
(void)hDevice;
(void)dwParam2;
if(uMsg != WIM_DATA)
return;
// Decrement number of buffers in use
InterlockedDecrement(&pData->lWaveBuffersCommitted);
if(pData->bWaveShutdown == AL_FALSE)
{
// Notify Wave Processor Thread that a Wave Header has returned
PostThreadMessage(pData->ulWaveThreadID,uMsg,0,dwParam1);
}
else
{
if(pData->lWaveBuffersCommitted == 0)
{
// Post 'Quit' Message to WaveIn Processor Thread
PostThreadMessage(pData->ulWaveThreadID,WM_QUIT,0,0);
}
}
}
/*
CaptureThreadProc
Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
audio data.
*/
static DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
{
ALCdevice *pDevice = (ALCdevice*)lpParameter;
WinMMData *pData = pDevice->ExtraData;
LPWAVEHDR pWaveHdr;
ALuint FrameSize;
MSG msg;
FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message != WIM_DATA || pData->bWaveShutdown)
continue;
pWaveHdr = ((LPWAVEHDR)msg.lParam);
WriteRingBuffer(pData->pRing, (ALubyte*)pWaveHdr->lpData,
pWaveHdr->dwBytesRecorded/FrameSize);
// Send buffer back to capture more data
waveInAddBuffer(pData->hWaveHandle.In,pWaveHdr,sizeof(WAVEHDR));
InterlockedIncrement(&pData->lWaveBuffersCommitted);
}
// Signal Wave Thread completed event
if(pData->hWaveThreadEvent)
SetEvent(pData->hWaveThreadEvent);
ExitThread(0);
return 0;
}
static ALCenum WinMMOpenPlayback(ALCdevice *pDevice, const ALCchar *deviceName)
{
WAVEFORMATEX wfexFormat;
WinMMData *pData = NULL;
UINT lDeviceID = 0;
MMRESULT res;
ALuint i = 0;
// Find the Device ID matching the deviceName if valid
if(!deviceName || strcmp(deviceName, woDefault) == 0)
lDeviceID = WAVE_MAPPER;
else
{
if(!PlaybackDeviceList)
ProbePlaybackDevices();
for(i = 0;i < NumPlaybackDevices;i++)
{
if(PlaybackDeviceList[i] &&
strcmp(deviceName, PlaybackDeviceList[i]) == 0)
{
lDeviceID = i;
break;
}
}
if(i == NumPlaybackDevices)
return ALC_INVALID_VALUE;
}
pData = calloc(1, sizeof(*pData));
if(!pData)
return ALC_OUT_OF_MEMORY;
pDevice->ExtraData = pData;
if(pDevice->FmtChans != DevFmtMono)
{
if((pDevice->Flags&DEVICE_CHANNELS_REQUEST) &&
pDevice->FmtChans != DevFmtStereo)
{
ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(pDevice->FmtChans));
pDevice->Flags &= ~DEVICE_CHANNELS_REQUEST;
}
pDevice->FmtChans = DevFmtStereo;
}
switch(pDevice->FmtType)
{
case DevFmtByte:
pDevice->FmtType = DevFmtUByte;
break;
case DevFmtUShort:
case DevFmtFloat:
pDevice->FmtType = DevFmtShort;
break;
case DevFmtUByte:
case DevFmtShort:
break;
}
memset(&wfexFormat, 0, sizeof(WAVEFORMATEX));
wfexFormat.wFormatTag = WAVE_FORMAT_PCM;
wfexFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans);
wfexFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8;
wfexFormat.nBlockAlign = wfexFormat.wBitsPerSample *
wfexFormat.nChannels / 8;
wfexFormat.nSamplesPerSec = pDevice->Frequency;
wfexFormat.nAvgBytesPerSec = wfexFormat.nSamplesPerSec *
wfexFormat.nBlockAlign;
wfexFormat.cbSize = 0;
if((res=waveOutOpen(&pData->hWaveHandle.Out, lDeviceID, &wfexFormat, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
{
ERR("waveOutOpen failed: %u\n", res);
goto failure;
}
pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(pData->hWaveThreadEvent == NULL)
{
ERR("CreateEvent failed: %lu\n", GetLastError());
goto failure;
}
pData->Frequency = pDevice->Frequency;
pDevice->szDeviceName = strdup((lDeviceID==WAVE_MAPPER) ? woDefault :
PlaybackDeviceList[lDeviceID]);
return ALC_NO_ERROR;
failure:
if(pData->hWaveThreadEvent)
CloseHandle(pData->hWaveThreadEvent);
if(pData->hWaveHandle.Out)
waveOutClose(pData->hWaveHandle.Out);
free(pData);
pDevice->ExtraData = NULL;
return ALC_INVALID_VALUE;
}
static void WinMMClosePlayback(ALCdevice *device)
{
WinMMData *pData = (WinMMData*)device->ExtraData;
// Close the Wave device
CloseHandle(pData->hWaveThreadEvent);
pData->hWaveThreadEvent = 0;
waveOutClose(pData->hWaveHandle.Out);
pData->hWaveHandle.Out = 0;
free(pData);
device->ExtraData = NULL;
}
static ALCboolean WinMMResetPlayback(ALCdevice *device)
{
WinMMData *pData = (WinMMData*)device->ExtraData;
ALbyte *BufferData;
ALint lBufferSize;
ALuint i;
pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlaybackThreadProc, (LPVOID)device, 0, &pData->ulWaveThreadID);
if(pData->hWaveThread == NULL)
return ALC_FALSE;
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
pData->Frequency / device->Frequency);
if(device->Frequency != pData->Frequency)
{
if((device->Flags&DEVICE_FREQUENCY_REQUEST))
ERR("WinMM does not support changing sample rates (wanted %dhz, got %dhz)\n", device->Frequency, pData->Frequency);
device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
device->Frequency = pData->Frequency;
}
SetDefaultWFXChannelOrder(device);
pData->lWaveBuffersCommitted = 0;
// Create 4 Buffers
lBufferSize = device->UpdateSize*device->NumUpdates / 4;
lBufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
BufferData = calloc(4, lBufferSize);
for(i = 0;i < 4;i++)
{
memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR));
pData->WaveBuffer[i].dwBufferLength = lBufferSize;
pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData :
(pData->WaveBuffer[i-1].lpData +
pData->WaveBuffer[i-1].dwBufferLength));
waveOutPrepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR));
waveOutWrite(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR));
InterlockedIncrement(&pData->lWaveBuffersCommitted);
}
return ALC_TRUE;
}
static void WinMMStopPlayback(ALCdevice *device)
{
WinMMData *pData = (WinMMData*)device->ExtraData;
void *buffer = NULL;
int i;
if(pData->hWaveThread == NULL)
return;
// Set flag to stop processing headers
pData->bWaveShutdown = AL_TRUE;
// Wait for signal that Wave Thread has been destroyed
WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE);
CloseHandle(pData->hWaveThread);
pData->hWaveThread = 0;
pData->bWaveShutdown = AL_FALSE;
// Release the wave buffers
for(i = 0;i < 4;i++)
{
waveOutUnprepareHeader(pData->hWaveHandle.Out, &pData->WaveBuffer[i], sizeof(WAVEHDR));
if(i == 0) buffer = pData->WaveBuffer[i].lpData;
pData->WaveBuffer[i].lpData = NULL;
}
free(buffer);
}
static ALCenum WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
{
WAVEFORMATEX wfexCaptureFormat;
ALbyte *BufferData = NULL;
DWORD ulCapturedDataSize;
WinMMData *pData = NULL;
UINT lDeviceID = 0;
ALint lBufferSize;
MMRESULT res;
ALuint i;
if(!CaptureDeviceList)
ProbeCaptureDevices();
// Find the Device ID matching the deviceName if valid
if(deviceName)
{
for(i = 0;i < NumCaptureDevices;i++)
{
if(CaptureDeviceList[i] &&
strcmp(deviceName, CaptureDeviceList[i]) == 0)
{
lDeviceID = i;
break;
}
}
}
else
{
for(i = 0;i < NumCaptureDevices;i++)
{
if(CaptureDeviceList[i])
{
lDeviceID = i;
break;
}
}
}
if(i == NumCaptureDevices)
return ALC_INVALID_VALUE;
pData = calloc(1, sizeof(*pData));
if(!pData)
return ALC_OUT_OF_MEMORY;
pDevice->ExtraData = pData;
if((pDevice->FmtChans != DevFmtMono && pDevice->FmtChans != DevFmtStereo) ||
(pDevice->FmtType != DevFmtUByte && pDevice->FmtType != DevFmtShort))
goto failure;
memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX));
wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
wfexCaptureFormat.nChannels = ChannelsFromDevFmt(pDevice->FmtChans);
wfexCaptureFormat.wBitsPerSample = BytesFromDevFmt(pDevice->FmtType) * 8;
wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample *
wfexCaptureFormat.nChannels / 8;
wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency;
wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec *
wfexCaptureFormat.nBlockAlign;
wfexCaptureFormat.cbSize = 0;
if((res=waveInOpen(&pData->hWaveHandle.In, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
{
ERR("waveInOpen failed: %u\n", res);
goto failure;
}
pData->hWaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(pData->hWaveThreadEvent == NULL)
{
ERR("CreateEvent failed: %lu\n", GetLastError());
goto failure;
}
pData->Frequency = pDevice->Frequency;
// Allocate circular memory buffer for the captured audio
ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates;
// Make sure circular buffer is at least 100ms in size
if(ulCapturedDataSize < (wfexCaptureFormat.nSamplesPerSec / 10))
ulCapturedDataSize = wfexCaptureFormat.nSamplesPerSec / 10;
pData->pRing = CreateRingBuffer(wfexCaptureFormat.nBlockAlign, ulCapturedDataSize);
if(!pData->pRing)
goto failure;
pData->lWaveBuffersCommitted = 0;
// Create 4 Buffers of 50ms each
lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20;
lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign);
BufferData = calloc(4, lBufferSize);
if(!BufferData)
goto failure;
for(i = 0;i < 4;i++)
{
memset(&pData->WaveBuffer[i], 0, sizeof(WAVEHDR));
pData->WaveBuffer[i].dwBufferLength = lBufferSize;
pData->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData :
(pData->WaveBuffer[i-1].lpData +
pData->WaveBuffer[i-1].dwBufferLength));
pData->WaveBuffer[i].dwFlags = 0;
pData->WaveBuffer[i].dwLoops = 0;
waveInPrepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
waveInAddBuffer(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
InterlockedIncrement(&pData->lWaveBuffersCommitted);
}
pData->hWaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveThreadID);
if (pData->hWaveThread == NULL)
goto failure;
pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]);
return ALC_NO_ERROR;
failure:
if(pData->hWaveThread)
CloseHandle(pData->hWaveThread);
if(BufferData)
{
for(i = 0;i < 4;i++)
waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
free(BufferData);
}
if(pData->pRing)
DestroyRingBuffer(pData->pRing);
if(pData->hWaveThreadEvent)
CloseHandle(pData->hWaveThreadEvent);
if(pData->hWaveHandle.In)
waveInClose(pData->hWaveHandle.In);
free(pData);
pDevice->ExtraData = NULL;
return ALC_INVALID_VALUE;
}
static void WinMMCloseCapture(ALCdevice *pDevice)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
void *buffer = NULL;
int i;
// Call waveOutReset to shutdown wave device
pData->bWaveShutdown = AL_TRUE;
waveInReset(pData->hWaveHandle.In);
// Wait for signal that Wave Thread has been destroyed
WaitForSingleObjectEx(pData->hWaveThreadEvent, 5000, FALSE);
CloseHandle(pData->hWaveThread);
pData->hWaveThread = 0;
// Release the wave buffers
for(i = 0;i < 4;i++)
{
waveInUnprepareHeader(pData->hWaveHandle.In, &pData->WaveBuffer[i], sizeof(WAVEHDR));
if(i == 0) buffer = pData->WaveBuffer[i].lpData;
pData->WaveBuffer[i].lpData = NULL;
}
free(buffer);
DestroyRingBuffer(pData->pRing);
pData->pRing = NULL;
// Close the Wave device
CloseHandle(pData->hWaveThreadEvent);
pData->hWaveThreadEvent = 0;
waveInClose(pData->hWaveHandle.In);
pData->hWaveHandle.In = 0;
free(pData);
pDevice->ExtraData = NULL;
}
static void WinMMStartCapture(ALCdevice *pDevice)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
waveInStart(pData->hWaveHandle.In);
}
static void WinMMStopCapture(ALCdevice *pDevice)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
waveInStop(pData->hWaveHandle.In);
}
static ALCenum WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
ReadRingBuffer(pData->pRing, pBuffer, lSamples);
return ALC_NO_ERROR;
}
static ALCuint WinMMAvailableSamples(ALCdevice *pDevice)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
return RingBufferSize(pData->pRing);
}
static const BackendFuncs WinMMFuncs = {
WinMMOpenPlayback,
WinMMClosePlayback,
WinMMResetPlayback,
WinMMStopPlayback,
WinMMOpenCapture,
WinMMCloseCapture,
WinMMStartCapture,
WinMMStopCapture,
WinMMCaptureSamples,
WinMMAvailableSamples
};
ALCboolean alcWinMMInit(BackendFuncs *FuncList)
{
*FuncList = WinMMFuncs;
return ALC_TRUE;
}
void alcWinMMDeinit()
{
ALuint lLoop;
for(lLoop = 0;lLoop < NumPlaybackDevices;lLoop++)
free(PlaybackDeviceList[lLoop]);
free(PlaybackDeviceList);
PlaybackDeviceList = NULL;
NumPlaybackDevices = 0;
for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
free(CaptureDeviceList[lLoop]);
free(CaptureDeviceList);
CaptureDeviceList = NULL;
NumCaptureDevices = 0;
}
void alcWinMMProbe(enum DevProbe type)
{
ALuint i;
switch(type)
{
case DEVICE_PROBE:
ProbePlaybackDevices();
if(NumPlaybackDevices > 0)
AppendDeviceList(woDefault);
break;
case ALL_DEVICE_PROBE:
ProbePlaybackDevices();
if(NumPlaybackDevices > 0)
AppendAllDeviceList(woDefault);
for(i = 0;i < NumPlaybackDevices;i++)
{
if(PlaybackDeviceList[i])
AppendAllDeviceList(PlaybackDeviceList[i]);
}
break;
case CAPTURE_DEVICE_PROBE:
ProbeCaptureDevices();
for(i = 0;i < NumCaptureDevices;i++)
{
if(CaptureDeviceList[i])
AppendCaptureDeviceList(CaptureDeviceList[i]);
}
break;
}
}