mirror of
https://github.com/love2d/love-android.git
synced 2026-08-22 13:35:05 +02:00
added dependencies and build files (loosely inspired by Sebastian Dorda's love-native-android)
This commit is contained in:
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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user