open source
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,338 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef __MINGW64__
|
||||
#define _WIN32_IE 0x501
|
||||
#else
|
||||
#define _WIN32_IE 0x400
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef _WIN32_IE
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
typedef struct ConfigEntry {
|
||||
char *key;
|
||||
char *value;
|
||||
} ConfigEntry;
|
||||
|
||||
typedef struct ConfigBlock {
|
||||
char *name;
|
||||
ConfigEntry *entries;
|
||||
size_t entryCount;
|
||||
} ConfigBlock;
|
||||
|
||||
static ConfigBlock *cfgBlocks;
|
||||
static size_t cfgCount;
|
||||
|
||||
static char buffer[1024];
|
||||
|
||||
static void LoadConfigFromFile(FILE *f)
|
||||
{
|
||||
ConfigBlock *curBlock = cfgBlocks;
|
||||
ConfigEntry *ent;
|
||||
|
||||
while(fgets(buffer, sizeof(buffer), f))
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
while(isspace(buffer[i]))
|
||||
i++;
|
||||
if(!buffer[i] || buffer[i] == '#')
|
||||
continue;
|
||||
|
||||
memmove(buffer, buffer+i, strlen(buffer+i)+1);
|
||||
|
||||
if(buffer[0] == '[')
|
||||
{
|
||||
ConfigBlock *nextBlock;
|
||||
|
||||
i = 1;
|
||||
while(buffer[i] && buffer[i] != ']')
|
||||
i++;
|
||||
|
||||
if(!buffer[i])
|
||||
{
|
||||
AL_PRINT("config parse error: bad line \"%s\"\n", buffer);
|
||||
continue;
|
||||
}
|
||||
buffer[i] = 0;
|
||||
|
||||
do {
|
||||
i++;
|
||||
if(buffer[i] && !isspace(buffer[i]))
|
||||
{
|
||||
if(buffer[i] != '#')
|
||||
AL_PRINT("config warning: extra data after block: \"%s\"\n", buffer+i);
|
||||
break;
|
||||
}
|
||||
} while(buffer[i]);
|
||||
|
||||
nextBlock = NULL;
|
||||
for(i = 0;i < cfgCount;i++)
|
||||
{
|
||||
if(strcasecmp(cfgBlocks[i].name, buffer+1) == 0)
|
||||
{
|
||||
nextBlock = cfgBlocks+i;
|
||||
// AL_PRINT("found block '%s'\n", nextBlock->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!nextBlock)
|
||||
{
|
||||
nextBlock = realloc(cfgBlocks, (cfgCount+1)*sizeof(ConfigBlock));
|
||||
if(!nextBlock)
|
||||
{
|
||||
AL_PRINT("config parse error: error reallocating config blocks\n");
|
||||
continue;
|
||||
}
|
||||
cfgBlocks = nextBlock;
|
||||
nextBlock = cfgBlocks+cfgCount;
|
||||
cfgCount++;
|
||||
|
||||
nextBlock->name = strdup(buffer+1);
|
||||
nextBlock->entries = NULL;
|
||||
nextBlock->entryCount = 0;
|
||||
|
||||
// AL_PRINT("found new block '%s'\n", nextBlock->name);
|
||||
}
|
||||
curBlock = nextBlock;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Look for the option name */
|
||||
i = 0;
|
||||
while(buffer[i] && buffer[i] != '#' && buffer[i] != '=' &&
|
||||
!isspace(buffer[i]))
|
||||
i++;
|
||||
|
||||
if(!buffer[i] || buffer[i] == '#' || i == 0)
|
||||
{
|
||||
AL_PRINT("config parse error: malformed option line: \"%s\"\n", buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Seperate the option */
|
||||
if(buffer[i] != '=')
|
||||
{
|
||||
buffer[i++] = 0;
|
||||
|
||||
while(isspace(buffer[i]))
|
||||
i++;
|
||||
if(buffer[i] != '=')
|
||||
{
|
||||
AL_PRINT("config parse error: option without a value: \"%s\"\n", buffer);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* Find the start of the value */
|
||||
buffer[i++] = 0;
|
||||
while(isspace(buffer[i]))
|
||||
i++;
|
||||
|
||||
/* Check if we already have this option set */
|
||||
ent = curBlock->entries;
|
||||
while((size_t)(ent-curBlock->entries) < curBlock->entryCount)
|
||||
{
|
||||
if(strcasecmp(ent->key, buffer) == 0)
|
||||
break;
|
||||
ent++;
|
||||
}
|
||||
|
||||
if((size_t)(ent-curBlock->entries) >= curBlock->entryCount)
|
||||
{
|
||||
/* Allocate a new option entry */
|
||||
ent = realloc(curBlock->entries, (curBlock->entryCount+1)*sizeof(ConfigEntry));
|
||||
if(!ent)
|
||||
{
|
||||
AL_PRINT("config parse error: error reallocating config entries\n");
|
||||
continue;
|
||||
}
|
||||
curBlock->entries = ent;
|
||||
ent = curBlock->entries + curBlock->entryCount;
|
||||
curBlock->entryCount++;
|
||||
|
||||
ent->key = strdup(buffer);
|
||||
ent->value = NULL;
|
||||
}
|
||||
|
||||
/* Look for the end of the line (Null term, new-line, or #-symbol) and
|
||||
eat up the trailing whitespace */
|
||||
memmove(buffer, buffer+i, strlen(buffer+i)+1);
|
||||
|
||||
i = 0;
|
||||
while(buffer[i] && buffer[i] != '#' && buffer[i] != '\n')
|
||||
i++;
|
||||
do {
|
||||
i--;
|
||||
} while(isspace(buffer[i]));
|
||||
buffer[++i] = 0;
|
||||
|
||||
free(ent->value);
|
||||
ent->value = strdup(buffer);
|
||||
|
||||
// AL_PRINT("found '%s' = '%s'\n", ent->key, ent->value);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadALConfig(void)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
cfgBlocks = calloc(1, sizeof(ConfigBlock));
|
||||
cfgBlocks->name = strdup("general");
|
||||
cfgCount = 1;
|
||||
|
||||
#ifdef _WIN32
|
||||
if(SHGetSpecialFolderPathA(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
|
||||
{
|
||||
size_t p = strlen(buffer);
|
||||
snprintf(buffer+p, sizeof(buffer)-p, "\\alsoft.ini");
|
||||
f = fopen(buffer, "rt");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
#else
|
||||
f = fopen("/etc/openal/alsoft.conf", "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
if(getenv("HOME") && *(getenv("HOME")))
|
||||
{
|
||||
snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", getenv("HOME"));
|
||||
f = fopen(buffer, "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(getenv("ALSOFT_CONF"))
|
||||
{
|
||||
f = fopen(getenv("ALSOFT_CONF"), "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FreeALConfig(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for(i = 0;i < cfgCount;i++)
|
||||
{
|
||||
size_t j;
|
||||
for(j = 0;j < cfgBlocks[i].entryCount;j++)
|
||||
{
|
||||
free(cfgBlocks[i].entries[j].key);
|
||||
free(cfgBlocks[i].entries[j].value);
|
||||
}
|
||||
free(cfgBlocks[i].entries);
|
||||
free(cfgBlocks[i].name);
|
||||
}
|
||||
free(cfgBlocks);
|
||||
cfgBlocks = NULL;
|
||||
cfgCount = 0;
|
||||
}
|
||||
|
||||
const char *GetConfigValue(const char *blockName, const char *keyName, const char *def)
|
||||
{
|
||||
size_t i, j;
|
||||
|
||||
if(!keyName)
|
||||
return def;
|
||||
|
||||
if(!blockName)
|
||||
blockName = "general";
|
||||
|
||||
for(i = 0;i < cfgCount;i++)
|
||||
{
|
||||
if(strcasecmp(cfgBlocks[i].name, blockName) != 0)
|
||||
continue;
|
||||
|
||||
for(j = 0;j < cfgBlocks[i].entryCount;j++)
|
||||
{
|
||||
if(strcasecmp(cfgBlocks[i].entries[j].key, keyName) == 0)
|
||||
{
|
||||
if(cfgBlocks[i].entries[j].value[0])
|
||||
return cfgBlocks[i].entries[j].value;
|
||||
return def;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
int ConfigValueExists(const char *blockName, const char *keyName)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
return !!val[0];
|
||||
}
|
||||
|
||||
int GetConfigValueInt(const char *blockName, const char *keyName, int def)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
|
||||
if(!val[0]) return def;
|
||||
return strtol(val, NULL, 0);
|
||||
}
|
||||
|
||||
float GetConfigValueFloat(const char *blockName, const char *keyName, float def)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
|
||||
if(!val[0]) return def;
|
||||
#ifdef HAVE_STRTOF
|
||||
return strtof(val, NULL);
|
||||
#else
|
||||
return (float)strtod(val, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
int GetConfigValueBool(const char *blockName, const char *keyName, int def)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
|
||||
if(!val[0]) return !!def;
|
||||
return (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
|
||||
strcasecmp(val, "on") == 0 || atoi(val) != 0);
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2009 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 <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
typedef struct ALechoState {
|
||||
// Must be first in all effects!
|
||||
ALeffectState state;
|
||||
|
||||
ALfloat *SampleBuffer;
|
||||
ALuint BufferLength;
|
||||
|
||||
// The echo is two tap. The delay is the number of samples from before the
|
||||
// current offset
|
||||
struct {
|
||||
ALuint delay;
|
||||
} Tap[2];
|
||||
ALuint Offset;
|
||||
// The LR gains for the first tap. The second tap uses the reverse
|
||||
ALfloat GainL;
|
||||
ALfloat GainR;
|
||||
|
||||
ALfloat FeedGain;
|
||||
|
||||
ALfloat Scale;
|
||||
|
||||
FILTER iirFilter;
|
||||
ALfloat history[2];
|
||||
} ALechoState;
|
||||
|
||||
static ALvoid EchoDestroy(ALeffectState *effect)
|
||||
{
|
||||
ALechoState *state = (ALechoState*)effect;
|
||||
if(state)
|
||||
{
|
||||
free(state->SampleBuffer);
|
||||
state->SampleBuffer = NULL;
|
||||
free(state);
|
||||
}
|
||||
}
|
||||
|
||||
static ALboolean EchoDeviceUpdate(ALeffectState *effect, ALCdevice *Device)
|
||||
{
|
||||
ALechoState *state = (ALechoState*)effect;
|
||||
ALuint maxlen, i;
|
||||
|
||||
// Use the next power of 2 for the buffer length, so the tap offsets can be
|
||||
// wrapped using a mask instead of a modulo
|
||||
maxlen = (ALuint)(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
|
||||
maxlen += (ALuint)(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
{
|
||||
void *temp;
|
||||
|
||||
temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
|
||||
if(!temp)
|
||||
return AL_FALSE;
|
||||
state->SampleBuffer = temp;
|
||||
state->BufferLength = maxlen;
|
||||
}
|
||||
for(i = 0;i < state->BufferLength;i++)
|
||||
state->SampleBuffer[i] = 0.0f;
|
||||
|
||||
state->Scale = aluSqrt(Device->NumChan / 6.0f);
|
||||
state->Scale = __min(state->Scale, 1.0f);
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid EchoUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffect *Effect)
|
||||
{
|
||||
ALechoState *state = (ALechoState*)effect;
|
||||
ALuint frequency = Context->Device->Frequency;
|
||||
ALfloat lrpan, cw, a, g;
|
||||
|
||||
state->Tap[0].delay = (ALuint)(Effect->Echo.Delay * frequency) + 1;
|
||||
state->Tap[1].delay = (ALuint)(Effect->Echo.LRDelay * frequency);
|
||||
state->Tap[1].delay += state->Tap[0].delay;
|
||||
|
||||
lrpan = Effect->Echo.Spread*0.5f + 0.5f;
|
||||
state->GainL = aluSqrt( lrpan);
|
||||
state->GainR = aluSqrt(1.0f-lrpan);
|
||||
|
||||
state->FeedGain = Effect->Echo.Feedback;
|
||||
|
||||
cw = cos(2.0*M_PI * LOWPASSFREQCUTOFF / frequency);
|
||||
g = 1.0f - Effect->Echo.Damping;
|
||||
a = 0.0f;
|
||||
if(g < 0.9999f) // 1-epsilon
|
||||
a = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
|
||||
state->iirFilter.coeff = a;
|
||||
}
|
||||
|
||||
static ALvoid EchoProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS])
|
||||
{
|
||||
ALechoState *state = (ALechoState*)effect;
|
||||
const ALuint mask = state->BufferLength-1;
|
||||
const ALuint tap1 = state->Tap[0].delay;
|
||||
const ALuint tap2 = state->Tap[1].delay;
|
||||
ALuint offset = state->Offset;
|
||||
const ALfloat gain = Slot->Gain * state->Scale;
|
||||
ALfloat samp[2], smp;
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++,offset++)
|
||||
{
|
||||
// Sample first tap
|
||||
smp = state->SampleBuffer[(offset-tap1) & mask];
|
||||
samp[0] = smp * state->GainL;
|
||||
samp[1] = smp * state->GainR;
|
||||
// Sample second tap. Reverse LR panning
|
||||
smp = state->SampleBuffer[(offset-tap2) & mask];
|
||||
samp[0] += smp * state->GainR;
|
||||
samp[1] += smp * state->GainL;
|
||||
|
||||
// Apply damping and feedback gain to the second tap, and mix in the
|
||||
// new sample
|
||||
smp = lpFilter2P(&state->iirFilter, 0, smp+SamplesIn[i]);
|
||||
state->SampleBuffer[offset&mask] = smp * state->FeedGain;
|
||||
|
||||
// Apply slot gain
|
||||
samp[0] *= gain;
|
||||
samp[1] *= gain;
|
||||
|
||||
SamplesOut[i][FRONT_LEFT] += samp[0];
|
||||
SamplesOut[i][FRONT_RIGHT] += samp[1];
|
||||
SamplesOut[i][SIDE_LEFT] += samp[0];
|
||||
SamplesOut[i][SIDE_RIGHT] += samp[1];
|
||||
SamplesOut[i][BACK_LEFT] += samp[0];
|
||||
SamplesOut[i][BACK_RIGHT] += samp[1];
|
||||
}
|
||||
state->Offset = offset;
|
||||
}
|
||||
|
||||
ALeffectState *EchoCreate(void)
|
||||
{
|
||||
ALechoState *state;
|
||||
|
||||
state = malloc(sizeof(*state));
|
||||
if(!state)
|
||||
return NULL;
|
||||
|
||||
state->state.Destroy = EchoDestroy;
|
||||
state->state.DeviceUpdate = EchoDeviceUpdate;
|
||||
state->state.Update = EchoUpdate;
|
||||
state->state.Process = EchoProcess;
|
||||
|
||||
state->BufferLength = 0;
|
||||
state->SampleBuffer = NULL;
|
||||
|
||||
state->Tap[0].delay = 0;
|
||||
state->Tap[1].delay = 0;
|
||||
state->Offset = 0;
|
||||
state->GainL = 0.0f;
|
||||
state->GainR = 0.0f;
|
||||
|
||||
state->Scale = 1.0f;
|
||||
|
||||
state->iirFilter.coeff = 0.0f;
|
||||
state->iirFilter.history[0] = 0.0f;
|
||||
state->iirFilter.history[1] = 0.0f;
|
||||
|
||||
return &state->state;
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2009 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 <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
typedef struct ALmodulatorState {
|
||||
// Must be first in all effects!
|
||||
ALeffectState state;
|
||||
|
||||
enum {
|
||||
SINUSOID,
|
||||
SAWTOOTH,
|
||||
SQUARE
|
||||
} Waveform;
|
||||
|
||||
ALuint index;
|
||||
ALuint step;
|
||||
|
||||
ALfloat Scale;
|
||||
|
||||
FILTER iirFilter;
|
||||
ALfloat history[1];
|
||||
} ALmodulatorState;
|
||||
|
||||
#define WAVEFORM_FRACBITS 16
|
||||
#define WAVEFORM_FRACMASK ((1<<WAVEFORM_FRACBITS)-1)
|
||||
|
||||
static __inline ALfloat sin_func(ALuint index)
|
||||
{
|
||||
return sin(index / (double)(1<<WAVEFORM_FRACBITS) * M_PI * 2.0f);
|
||||
}
|
||||
|
||||
static __inline ALfloat saw_func(ALuint index)
|
||||
{
|
||||
return index*2.0f/(1<<WAVEFORM_FRACBITS) - 1.0f;
|
||||
}
|
||||
|
||||
static __inline ALfloat square_func(ALuint index)
|
||||
{
|
||||
return ((index>>(WAVEFORM_FRACBITS-1))&1) ? -1.0f : 1.0f;
|
||||
}
|
||||
|
||||
|
||||
static __inline ALfloat hpFilter1P(FILTER *iir, ALuint offset, ALfloat input)
|
||||
{
|
||||
ALfloat *history = &iir->history[offset];
|
||||
ALfloat a = iir->coeff;
|
||||
ALfloat output = input;
|
||||
|
||||
output = output + (history[0]-output)*a;
|
||||
history[0] = output;
|
||||
|
||||
return input - output;
|
||||
}
|
||||
|
||||
|
||||
static ALvoid ModulatorDestroy(ALeffectState *effect)
|
||||
{
|
||||
ALmodulatorState *state = (ALmodulatorState*)effect;
|
||||
free(state);
|
||||
}
|
||||
|
||||
static ALboolean ModulatorDeviceUpdate(ALeffectState *effect, ALCdevice *Device)
|
||||
{
|
||||
ALmodulatorState *state = (ALmodulatorState*)effect;
|
||||
|
||||
state->Scale = aluSqrt(Device->NumChan / 8.0f);
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ModulatorUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffect *Effect)
|
||||
{
|
||||
ALmodulatorState *state = (ALmodulatorState*)effect;
|
||||
ALfloat cw, a = 0.0f;
|
||||
|
||||
if(Effect->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
|
||||
state->Waveform = SINUSOID;
|
||||
else if(Effect->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
|
||||
state->Waveform = SAWTOOTH;
|
||||
else if(Effect->Modulator.Waveform == AL_RING_MODULATOR_SQUARE)
|
||||
state->Waveform = SQUARE;
|
||||
|
||||
state->step = Effect->Modulator.Frequency*(1<<WAVEFORM_FRACBITS) /
|
||||
Context->Device->Frequency;
|
||||
if(!state->step)
|
||||
state->step = 1;
|
||||
|
||||
cw = cos(2.0*M_PI * Effect->Modulator.HighPassCutoff / Context->Device->Frequency);
|
||||
a = (2.0f-cw) - aluSqrt(aluPow(2.0f-cw, 2.0f) - 1.0f);
|
||||
state->iirFilter.coeff = a;
|
||||
}
|
||||
|
||||
static ALvoid ModulatorProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS])
|
||||
{
|
||||
ALmodulatorState *state = (ALmodulatorState*)effect;
|
||||
const ALfloat gain = Slot->Gain * state->Scale;
|
||||
const ALuint step = state->step;
|
||||
ALuint index = state->index;
|
||||
ALfloat samp;
|
||||
ALuint i;
|
||||
|
||||
switch(state->Waveform)
|
||||
{
|
||||
case SINUSOID:
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
#define FILTER_OUT(func) do { \
|
||||
samp = SamplesIn[i]; \
|
||||
\
|
||||
index += step; \
|
||||
index &= WAVEFORM_FRACMASK; \
|
||||
samp *= func(index); \
|
||||
\
|
||||
samp = hpFilter1P(&state->iirFilter, 0, samp); \
|
||||
\
|
||||
/* Apply slot gain */ \
|
||||
samp *= gain; \
|
||||
\
|
||||
SamplesOut[i][FRONT_LEFT] += samp; \
|
||||
SamplesOut[i][FRONT_RIGHT] += samp; \
|
||||
SamplesOut[i][FRONT_CENTER] += samp; \
|
||||
SamplesOut[i][SIDE_LEFT] += samp; \
|
||||
SamplesOut[i][SIDE_RIGHT] += samp; \
|
||||
SamplesOut[i][BACK_LEFT] += samp; \
|
||||
SamplesOut[i][BACK_RIGHT] += samp; \
|
||||
SamplesOut[i][BACK_CENTER] += samp; \
|
||||
} while(0)
|
||||
FILTER_OUT(sin_func);
|
||||
}
|
||||
break;
|
||||
|
||||
case SAWTOOTH:
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
FILTER_OUT(saw_func);
|
||||
}
|
||||
break;
|
||||
|
||||
case SQUARE:
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
FILTER_OUT(square_func);
|
||||
#undef FILTER_OUT
|
||||
}
|
||||
break;
|
||||
}
|
||||
state->index = index;
|
||||
}
|
||||
|
||||
ALeffectState *ModulatorCreate(void)
|
||||
{
|
||||
ALmodulatorState *state;
|
||||
|
||||
state = malloc(sizeof(*state));
|
||||
if(!state)
|
||||
return NULL;
|
||||
|
||||
state->state.Destroy = ModulatorDestroy;
|
||||
state->state.DeviceUpdate = ModulatorDeviceUpdate;
|
||||
state->state.Update = ModulatorUpdate;
|
||||
state->state.Process = ModulatorProcess;
|
||||
|
||||
state->index = 0.0f;
|
||||
state->step = 1.0f;
|
||||
|
||||
state->Scale = 1.0f;
|
||||
|
||||
state->iirFilter.coeff = 0.0f;
|
||||
state->iirFilter.history[0] = 0.0f;
|
||||
|
||||
return &state->state;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
|
||||
struct RingBuffer {
|
||||
ALubyte *mem;
|
||||
|
||||
ALsizei frame_size;
|
||||
ALsizei length;
|
||||
ALint read_pos;
|
||||
ALint write_pos;
|
||||
|
||||
CRITICAL_SECTION cs;
|
||||
};
|
||||
|
||||
|
||||
RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length)
|
||||
{
|
||||
RingBuffer *ring = calloc(1, sizeof(*ring));
|
||||
if(ring)
|
||||
{
|
||||
ring->frame_size = frame_size;
|
||||
ring->length = length+1;
|
||||
ring->write_pos = 1;
|
||||
ring->mem = malloc(ring->length * ring->frame_size);
|
||||
if(!ring->mem)
|
||||
{
|
||||
free(ring);
|
||||
ring = NULL;
|
||||
}
|
||||
|
||||
InitializeCriticalSection(&ring->cs);
|
||||
}
|
||||
return ring;
|
||||
}
|
||||
|
||||
void DestroyRingBuffer(RingBuffer *ring)
|
||||
{
|
||||
if(ring)
|
||||
{
|
||||
DeleteCriticalSection(&ring->cs);
|
||||
free(ring->mem);
|
||||
free(ring);
|
||||
}
|
||||
}
|
||||
|
||||
ALsizei RingBufferSize(RingBuffer *ring)
|
||||
{
|
||||
ALsizei s;
|
||||
|
||||
EnterCriticalSection(&ring->cs);
|
||||
s = (ring->write_pos-ring->read_pos-1+ring->length) % ring->length;
|
||||
LeaveCriticalSection(&ring->cs);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len)
|
||||
{
|
||||
int remain;
|
||||
|
||||
EnterCriticalSection(&ring->cs);
|
||||
|
||||
remain = (ring->read_pos-ring->write_pos+ring->length) % ring->length;
|
||||
if(remain < len) len = remain;
|
||||
|
||||
if(len > 0)
|
||||
{
|
||||
remain = ring->length - ring->write_pos;
|
||||
if(remain < len)
|
||||
{
|
||||
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
|
||||
remain*ring->frame_size);
|
||||
memcpy(ring->mem, data+(remain*ring->frame_size),
|
||||
(len-remain)*ring->frame_size);
|
||||
}
|
||||
else
|
||||
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
|
||||
len*ring->frame_size);
|
||||
|
||||
ring->write_pos += len;
|
||||
ring->write_pos %= ring->length;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&ring->cs);
|
||||
}
|
||||
|
||||
void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len)
|
||||
{
|
||||
int remain;
|
||||
|
||||
EnterCriticalSection(&ring->cs);
|
||||
|
||||
remain = ring->length - ring->read_pos;
|
||||
if(remain < len)
|
||||
{
|
||||
memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), remain*ring->frame_size);
|
||||
memcpy(data+(remain*ring->frame_size), ring->mem, (len-remain)*ring->frame_size);
|
||||
}
|
||||
else
|
||||
memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), len*ring->frame_size);
|
||||
|
||||
ring->read_pos += len;
|
||||
ring->read_pos %= ring->length;
|
||||
|
||||
LeaveCriticalSection(&ring->cs);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* 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 "alMain.h"
|
||||
#include "alThunk.h"
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
typedef struct {
|
||||
ALuint (*func)(ALvoid*);
|
||||
ALvoid *ptr;
|
||||
HANDLE thread;
|
||||
} ThreadInfo;
|
||||
|
||||
static DWORD CALLBACK StarterFunc(void *ptr)
|
||||
{
|
||||
ThreadInfo *inf = (ThreadInfo*)ptr;
|
||||
ALint ret;
|
||||
|
||||
ret = inf->func(inf->ptr);
|
||||
ExitThread((DWORD)ret);
|
||||
|
||||
return (DWORD)ret;
|
||||
}
|
||||
|
||||
ALvoid *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr)
|
||||
{
|
||||
DWORD dummy;
|
||||
ThreadInfo *inf = malloc(sizeof(ThreadInfo));
|
||||
if(!inf) return 0;
|
||||
|
||||
inf->func = func;
|
||||
inf->ptr = ptr;
|
||||
|
||||
inf->thread = CreateThread(NULL, 0, StarterFunc, inf, 0, &dummy);
|
||||
if(!inf->thread)
|
||||
{
|
||||
free(inf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return inf;
|
||||
}
|
||||
|
||||
ALuint StopThread(ALvoid *thread)
|
||||
{
|
||||
ThreadInfo *inf = thread;
|
||||
DWORD ret = 0;
|
||||
|
||||
WaitForSingleObject(inf->thread, INFINITE);
|
||||
GetExitCodeThread(inf->thread, &ret);
|
||||
CloseHandle(inf->thread);
|
||||
|
||||
free(inf);
|
||||
|
||||
return (ALuint)ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct {
|
||||
ALuint (*func)(ALvoid*);
|
||||
ALvoid *ptr;
|
||||
ALuint ret;
|
||||
pthread_t thread;
|
||||
} ThreadInfo;
|
||||
|
||||
static void *StarterFunc(void *ptr)
|
||||
{
|
||||
ThreadInfo *inf = (ThreadInfo*)ptr;
|
||||
inf->ret = inf->func(inf->ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALvoid *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr)
|
||||
{
|
||||
ThreadInfo *inf = malloc(sizeof(ThreadInfo));
|
||||
if(!inf) return NULL;
|
||||
|
||||
inf->func = func;
|
||||
inf->ptr = ptr;
|
||||
if(pthread_create(&inf->thread, NULL, StarterFunc, inf) != 0)
|
||||
{
|
||||
free(inf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return inf;
|
||||
}
|
||||
|
||||
ALuint StopThread(ALvoid *thread)
|
||||
{
|
||||
ThreadInfo *inf = thread;
|
||||
ALuint ret;
|
||||
|
||||
pthread_join(inf->thread, NULL);
|
||||
ret = inf->ret;
|
||||
|
||||
free(inf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,287 @@
|
||||
/**
|
||||
* 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 Default";
|
||||
|
||||
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;
|
||||
|
||||
jint JNI_OnLoad(JavaVM* vm, 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;
|
||||
*/
|
||||
JNIEnv* env = NULL;
|
||||
|
||||
if (javaVM) (*javaVM)->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_2);
|
||||
|
||||
(*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
|
||||
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 = aluChannelsFromFormat(device->Format) == 1 ? CHANNEL_CONFIGURATION_MONO : CHANNEL_CONFIGURATION_STEREO;
|
||||
int audioFormat = aluBytesFromFormat(device->Format) == 1 ? ENCODING_PCM_8BIT : ENCODING_PCM_16BIT;
|
||||
|
||||
int bufferSizeInBytes = (*env)->CallStaticIntMethod(env, cAudioTrack,
|
||||
mGetMinBufferSize, sampleRateInHz, channelConfig, audioFormat);
|
||||
|
||||
int bufferSizeInSamples = bufferSizeInBytes / aluFrameSizeFromFormat(device->Format);
|
||||
|
||||
jobject track = (*env)->NewObject(env, cAudioTrack, mAudioTrack,
|
||||
STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat, device->NumUpdates * bufferSizeInBytes, MODE_STREAM);
|
||||
|
||||
(*env)->CallNonvirtualVoidMethod(env, track, cAudioTrack, mPlay);
|
||||
|
||||
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);
|
||||
|
||||
(*env)->CallNonvirtualIntMethod(env, track, cAudioTrack, mWrite, buffer, 0, bufferSizeInBytes);
|
||||
}
|
||||
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 ALCboolean android_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
JNIEnv* env = GetEnv();
|
||||
AndroidData* data;
|
||||
int channels;
|
||||
int bytes;
|
||||
|
||||
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_FALSE;
|
||||
}
|
||||
|
||||
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_FALSE;
|
||||
}
|
||||
|
||||
data = (AndroidData*)calloc(1, sizeof(*data));
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (aluChannelsFromFormat(device->Format) >= 2)
|
||||
{
|
||||
device->Format = aluBytesFromFormat(device->Format) >= 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO8;
|
||||
}
|
||||
else
|
||||
{
|
||||
device->Format = aluBytesFromFormat(device->Format) >= 2 ? AL_FORMAT_MONO16 : AL_FORMAT_MONO8;
|
||||
}
|
||||
|
||||
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 ALCboolean android_open_capture(ALCdevice *pDevice, const ALCchar *deviceName)
|
||||
{
|
||||
(void)pDevice;
|
||||
(void)deviceName;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void android_close_capture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void android_start_capture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void android_stop_capture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void android_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
|
||||
{
|
||||
(void)pDevice;
|
||||
(void)pBuffer;
|
||||
(void)lSamples;
|
||||
}
|
||||
|
||||
static ALCuint android_available_samples(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const BackendFuncs android_funcs = {
|
||||
android_open_playback,
|
||||
android_close_playback,
|
||||
android_reset_playback,
|
||||
android_stop_playback,
|
||||
android_open_capture,
|
||||
android_close_capture,
|
||||
android_start_capture,
|
||||
android_stop_capture,
|
||||
android_capture_samples,
|
||||
android_available_samples
|
||||
};
|
||||
|
||||
void alc_android_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = android_funcs;
|
||||
}
|
||||
|
||||
void alc_android_deinit(void)
|
||||
{
|
||||
JNIEnv* env = GetEnv();
|
||||
|
||||
/* release cached AudioTrack class */
|
||||
(*env)->DeleteGlobalRef(env, cAudioTrack);
|
||||
}
|
||||
|
||||
void alc_android_probe(int type)
|
||||
{
|
||||
if (type == DEVICE_PROBE)
|
||||
{
|
||||
AppendDeviceList(android_device);
|
||||
}
|
||||
else if (type == ALL_DEVICE_PROBE)
|
||||
{
|
||||
AppendAllDeviceList(android_device);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*-
|
||||
* Copyright (c) 2005 Boris Mikhaylov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "bs2b.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
/* Single pole IIR filter.
|
||||
* O[n] = a0*I[n] + a1*I[n-1] + b1*O[n-1]
|
||||
*/
|
||||
|
||||
/* Lowpass filter */
|
||||
#define lo_filter(in, out_1) (bs2b->a0_lo*(in) + bs2b->b1_lo*(out_1))
|
||||
|
||||
/* Highboost filter */
|
||||
#define hi_filter(in, in_1, out_1) (bs2b->a0_hi*(in) + bs2b->a1_hi*(in_1) + bs2b->b1_hi*(out_1))
|
||||
|
||||
/* Set up all data. */
|
||||
static void init(struct bs2b *bs2b)
|
||||
{
|
||||
double Fc_lo, Fc_hi;
|
||||
double G_lo, G_hi;
|
||||
double x;
|
||||
|
||||
if ((bs2b->srate > 192000) || (bs2b->srate < 2000))
|
||||
bs2b->srate = BS2B_DEFAULT_SRATE;
|
||||
|
||||
switch(bs2b->level)
|
||||
{
|
||||
case BS2B_LOW_CLEVEL: /* Low crossfeed level */
|
||||
Fc_lo = 360.0;
|
||||
Fc_hi = 501.0;
|
||||
G_lo = 0.398107170553497;
|
||||
G_hi = 0.205671765275719;
|
||||
break;
|
||||
|
||||
case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */
|
||||
Fc_lo = 500.0;
|
||||
Fc_hi = 711.0;
|
||||
G_lo = 0.459726988530872;
|
||||
G_hi = 0.228208484414988;
|
||||
break;
|
||||
|
||||
case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */
|
||||
Fc_lo = 700.0;
|
||||
Fc_hi = 1021.0;
|
||||
G_lo = 0.530884444230988;
|
||||
G_hi = 0.250105790667544;
|
||||
break;
|
||||
|
||||
case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */
|
||||
Fc_lo = 360.0;
|
||||
Fc_hi = 494.0;
|
||||
G_lo = 0.316227766016838;
|
||||
G_hi = 0.168236228897329;
|
||||
break;
|
||||
|
||||
case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */
|
||||
Fc_lo = 500.0;
|
||||
Fc_hi = 689.0;
|
||||
G_lo = 0.354813389233575;
|
||||
G_hi = 0.187169483835901;
|
||||
break;
|
||||
|
||||
default: /* High easy crossfeed level */
|
||||
bs2b->level = BS2B_HIGH_ECLEVEL;
|
||||
|
||||
Fc_lo = 700.0;
|
||||
Fc_hi = 975.0;
|
||||
G_lo = 0.398107170553497;
|
||||
G_hi = 0.205671765275719;
|
||||
break;
|
||||
} /* switch */
|
||||
|
||||
/* $fc = $Fc / $s;
|
||||
* $d = 1 / 2 / pi / $fc;
|
||||
* $x = exp(-1 / $d);
|
||||
*/
|
||||
|
||||
x = exp(-2.0 * M_PI * Fc_lo / bs2b->srate);
|
||||
bs2b->b1_lo = x;
|
||||
bs2b->a0_lo = G_lo * (1.0 - x);
|
||||
|
||||
x = exp(-2.0 * M_PI * Fc_hi / bs2b->srate);
|
||||
bs2b->b1_hi = x;
|
||||
bs2b->a0_hi = 1.0 - G_hi * (1.0 - x);
|
||||
bs2b->a1_hi = -x;
|
||||
|
||||
bs2b->gain = 1.0 / (1.0 - G_hi + G_lo);
|
||||
} /* init */
|
||||
|
||||
/* Exported functions.
|
||||
* See descriptions in "bs2b.h"
|
||||
*/
|
||||
|
||||
void bs2b_set_level(struct bs2b *bs2b, int level)
|
||||
{
|
||||
if(level == bs2b->level)
|
||||
return;
|
||||
bs2b->level = level;
|
||||
init(bs2b);
|
||||
} /* bs2b_set_level */
|
||||
|
||||
int bs2b_get_level(struct bs2b *bs2b)
|
||||
{
|
||||
return bs2b->level;
|
||||
} /* bs2b_get_level */
|
||||
|
||||
void bs2b_set_srate(struct bs2b *bs2b, int srate)
|
||||
{
|
||||
if (srate == bs2b->srate)
|
||||
return;
|
||||
bs2b->srate = srate;
|
||||
init(bs2b);
|
||||
} /* bs2b_set_srate */
|
||||
|
||||
int bs2b_get_srate(struct bs2b *bs2b)
|
||||
{
|
||||
return bs2b->srate;
|
||||
} /* bs2b_get_srate */
|
||||
|
||||
void bs2b_clear(struct bs2b *bs2b)
|
||||
{
|
||||
int loopv = sizeof(bs2b->last_sample);
|
||||
|
||||
while (loopv)
|
||||
{
|
||||
((char *)&bs2b->last_sample)[--loopv] = 0;
|
||||
}
|
||||
} /* bs2b_clear */
|
||||
|
||||
int bs2b_is_clear(struct bs2b *bs2b)
|
||||
{
|
||||
int loopv = sizeof(bs2b->last_sample);
|
||||
|
||||
while (loopv)
|
||||
{
|
||||
if (((char *)&bs2b->last_sample)[--loopv] != 0)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
} /* bs2b_is_clear */
|
||||
|
||||
void bs2b_cross_feed(struct bs2b *bs2b, float *sample)
|
||||
{
|
||||
/* Lowpass filter */
|
||||
bs2b->last_sample.lo[0] = lo_filter(sample[0], bs2b->last_sample.lo[0]);
|
||||
bs2b->last_sample.lo[1] = lo_filter(sample[1], bs2b->last_sample.lo[1]);
|
||||
|
||||
/* Highboost filter */
|
||||
bs2b->last_sample.hi[0] = hi_filter(sample[0], bs2b->last_sample.asis[0], bs2b->last_sample.hi[0]);
|
||||
bs2b->last_sample.hi[1] = hi_filter(sample[1], bs2b->last_sample.asis[1], bs2b->last_sample.hi[1]);
|
||||
bs2b->last_sample.asis[0] = sample[0];
|
||||
bs2b->last_sample.asis[1] = sample[1];
|
||||
|
||||
/* Crossfeed */
|
||||
sample[0] = bs2b->last_sample.hi[0] + bs2b->last_sample.lo[1];
|
||||
sample[1] = bs2b->last_sample.hi[1] + bs2b->last_sample.lo[0];
|
||||
|
||||
/* Bass boost cause allpass attenuation */
|
||||
sample[0] *= bs2b->gain;
|
||||
sample[1] *= bs2b->gain;
|
||||
|
||||
/* Clipping of overloaded samples */
|
||||
#if 0
|
||||
if (sample[0] > 1.0)
|
||||
sample[0] = 1.0;
|
||||
if (sample[0] < -1.0)
|
||||
sample[0] = -1.0;
|
||||
if (sample[1] > 1.0)
|
||||
sample[1] = 1.0;
|
||||
if (sample[1] < -1.0)
|
||||
sample[1] = -1.0;
|
||||
#endif
|
||||
} /* bs2b_cross_feed */
|
||||
@@ -0,0 +1,578 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
#define _WIN32_WINNT 0x0500
|
||||
#define INITGUID
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include <dsound.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 void *ds_handle;
|
||||
static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter);
|
||||
static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
|
||||
|
||||
|
||||
typedef struct {
|
||||
// DirectSound Playback Device
|
||||
LPDIRECTSOUND lpDS;
|
||||
LPDIRECTSOUNDBUFFER DSpbuffer;
|
||||
LPDIRECTSOUNDBUFFER DSsbuffer;
|
||||
|
||||
volatile int killNow;
|
||||
ALvoid *thread;
|
||||
} DSoundData;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ALCchar *name;
|
||||
GUID guid;
|
||||
} DevMap;
|
||||
|
||||
static const ALCchar dsDevice[] = "DirectSound Software";
|
||||
static DevMap *DeviceList;
|
||||
static ALuint NumDevices;
|
||||
|
||||
|
||||
void *DSoundLoad(void)
|
||||
{
|
||||
if(!ds_handle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ds_handle = LoadLibraryA("dsound.dll");
|
||||
if(ds_handle == NULL)
|
||||
{
|
||||
AL_PRINT("Failed to load dsound.dll\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = (void*)GetProcAddress((HMODULE)ds_handle, #f); \
|
||||
if(p##f == NULL) \
|
||||
{ \
|
||||
FreeLibrary(ds_handle); \
|
||||
ds_handle = NULL; \
|
||||
AL_PRINT("Could not load %s from dsound.dll\n", #f); \
|
||||
return NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
#else
|
||||
ds_handle = (void*)0xDEADBEEF;
|
||||
#define LOAD_FUNC(f) p##f = f
|
||||
#endif
|
||||
|
||||
LOAD_FUNC(DirectSoundCreate);
|
||||
LOAD_FUNC(DirectSoundEnumerateA);
|
||||
#undef LOAD_FUNC
|
||||
}
|
||||
return ds_handle;
|
||||
}
|
||||
|
||||
|
||||
static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
|
||||
{
|
||||
(void)data;
|
||||
(void)drvname;
|
||||
|
||||
if(guid)
|
||||
{
|
||||
char str[1024];
|
||||
void *temp;
|
||||
|
||||
temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
|
||||
if(temp)
|
||||
{
|
||||
DeviceList = temp;
|
||||
|
||||
snprintf(str, sizeof(str), "%s via DirectSound", desc);
|
||||
|
||||
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;
|
||||
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))
|
||||
{
|
||||
AL_PRINT("Failed to get buffer caps: 0x%lx\n", err);
|
||||
aluHandleDisconnect(pDevice);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FrameSize = aluFrameSizeFromFormat(pDevice->Format);
|
||||
FragSize = pDevice->UpdateSize * FrameSize;
|
||||
|
||||
IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
|
||||
while(!pData->killNow)
|
||||
{
|
||||
// Get current play and write cursors
|
||||
IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
|
||||
avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
|
||||
|
||||
if(avail < FragSize)
|
||||
{
|
||||
Sleep(1);
|
||||
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, play and lock
|
||||
if(err == DSERR_BUFFERLOST)
|
||||
{
|
||||
err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
|
||||
if(SUCCEEDED(err))
|
||||
err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
|
||||
if(SUCCEEDED(err))
|
||||
err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &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
|
||||
AL_PRINT("Buffer lock error: %#lx\n", err);
|
||||
|
||||
// Update old write cursor location
|
||||
LastCursor += WriteCnt1+WriteCnt2;
|
||||
LastCursor %= DSBCaps.dwBufferBytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
DSoundData *pData = NULL;
|
||||
LPGUID guid = NULL;
|
||||
HRESULT hr;
|
||||
|
||||
if(!DSoundLoad())
|
||||
return ALC_FALSE;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = dsDevice;
|
||||
else if(strcmp(deviceName, dsDevice) != 0)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
if(!DeviceList)
|
||||
{
|
||||
hr = pDirectSoundEnumerateA(DSoundEnumDevices, NULL);
|
||||
if(FAILED(hr))
|
||||
AL_PRINT("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_FALSE;
|
||||
}
|
||||
|
||||
//Initialise requested device
|
||||
pData = calloc(1, sizeof(DSoundData));
|
||||
if(!pData)
|
||||
{
|
||||
alcSetError(device, ALC_OUT_OF_MEMORY);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
//DirectSound Init code
|
||||
hr = pDirectSoundCreate(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);
|
||||
free(pData);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = pData;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void DSoundClosePlayback(ALCdevice *device)
|
||||
{
|
||||
DSoundData *pData = device->ExtraData;
|
||||
|
||||
IDirectSound_Release(pData->lpDS);
|
||||
free(pData);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean DSoundResetPlayback(ALCdevice *device)
|
||||
{
|
||||
DSoundData *pData = (DSoundData*)device->ExtraData;
|
||||
DSBUFFERDESC DSBDescription;
|
||||
WAVEFORMATEXTENSIBLE OutputType;
|
||||
DWORD frameSize = 0;
|
||||
ALenum format = 0;
|
||||
DWORD speakers;
|
||||
HRESULT hr;
|
||||
|
||||
memset(&OutputType, 0, sizeof(OutputType));
|
||||
|
||||
hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
|
||||
if(SUCCEEDED(hr) && ConfigValueExists(NULL, "format"))
|
||||
{
|
||||
if(aluChannelsFromFormat(device->Format) == 1)
|
||||
speakers = DSSPEAKER_COMBINED(DSSPEAKER_MONO, 0);
|
||||
else if(aluChannelsFromFormat(device->Format) == 2)
|
||||
speakers = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, 0);
|
||||
else if(aluChannelsFromFormat(device->Format) == 4)
|
||||
speakers = DSSPEAKER_COMBINED(DSSPEAKER_QUAD, 0);
|
||||
else if(aluChannelsFromFormat(device->Format) == 6)
|
||||
speakers = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, 0);
|
||||
else if(aluChannelsFromFormat(device->Format) == 8)
|
||||
speakers = DSSPEAKER_COMBINED(DSSPEAKER_7POINT1, 0);
|
||||
else
|
||||
{
|
||||
AL_PRINT("Unknown format: 0x%x\n", device->Format);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
speakers = DSSPEAKER_CONFIG(speakers);
|
||||
if(speakers == DSSPEAKER_MONO)
|
||||
{
|
||||
if(aluBytesFromFormat(device->Format) == 1)
|
||||
format = AL_FORMAT_MONO8;
|
||||
else if(aluBytesFromFormat(device->Format) == 2)
|
||||
format = AL_FORMAT_MONO16;
|
||||
else if(aluBytesFromFormat(device->Format) == 4)
|
||||
format = AL_FORMAT_MONO_FLOAT32;
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
|
||||
}
|
||||
else if(speakers == DSSPEAKER_STEREO)
|
||||
{
|
||||
if(aluBytesFromFormat(device->Format) == 1)
|
||||
format = AL_FORMAT_STEREO8;
|
||||
else if(aluBytesFromFormat(device->Format) == 2)
|
||||
format = AL_FORMAT_STEREO16;
|
||||
else if(aluBytesFromFormat(device->Format) == 4)
|
||||
format = AL_FORMAT_STEREO_FLOAT32;
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT;
|
||||
}
|
||||
else if(speakers == DSSPEAKER_QUAD)
|
||||
{
|
||||
if(aluBytesFromFormat(device->Format) == 1)
|
||||
format = AL_FORMAT_QUAD8;
|
||||
else if(aluBytesFromFormat(device->Format) == 2)
|
||||
format = AL_FORMAT_QUAD16;
|
||||
else if(aluBytesFromFormat(device->Format) == 4)
|
||||
format = AL_FORMAT_QUAD32;
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
}
|
||||
else if(speakers == DSSPEAKER_5POINT1)
|
||||
{
|
||||
if(aluBytesFromFormat(device->Format) == 1)
|
||||
format = AL_FORMAT_51CHN8;
|
||||
else if(aluBytesFromFormat(device->Format) == 2)
|
||||
format = AL_FORMAT_51CHN16;
|
||||
else if(aluBytesFromFormat(device->Format) == 4)
|
||||
format = AL_FORMAT_51CHN32;
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
}
|
||||
else if(speakers == DSSPEAKER_7POINT1)
|
||||
{
|
||||
if(aluBytesFromFormat(device->Format) == 1)
|
||||
format = AL_FORMAT_71CHN8;
|
||||
else if(aluBytesFromFormat(device->Format) == 2)
|
||||
format = AL_FORMAT_71CHN16;
|
||||
else if(aluBytesFromFormat(device->Format) == 4)
|
||||
format = AL_FORMAT_71CHN32;
|
||||
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;
|
||||
}
|
||||
else
|
||||
format = device->Format;
|
||||
frameSize = aluFrameSizeFromFormat(format);
|
||||
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
OutputType.Format.nChannels = aluChannelsFromFormat(format);
|
||||
OutputType.Format.wBitsPerSample = aluBytesFromFormat(format) * 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 || OutputType.Format.wBitsPerSample > 16)
|
||||
{
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
|
||||
OutputType.Format.cbSize = 22;
|
||||
if(OutputType.Format.wBitsPerSample == 32)
|
||||
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))
|
||||
{
|
||||
memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
|
||||
DSBDescription.dwSize=sizeof(DSBUFFERDESC);
|
||||
DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
|
||||
DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates * frameSize;
|
||||
DSBDescription.lpwfxFormat=&OutputType.Format;
|
||||
hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
|
||||
}
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
hr = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
device->Format = format;
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
pData->thread = StartThread(DSoundProc, device);
|
||||
if(!pData->thread)
|
||||
hr = E_FAIL;
|
||||
}
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
if (pData->DSsbuffer)
|
||||
IDirectSoundBuffer_Release(pData->DSsbuffer);
|
||||
pData->DSsbuffer = NULL;
|
||||
if (pData->DSpbuffer)
|
||||
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;
|
||||
|
||||
IDirectSoundBuffer_Release(pData->DSsbuffer);
|
||||
pData->DSsbuffer = NULL;
|
||||
if (pData->DSpbuffer)
|
||||
IDirectSoundBuffer_Release(pData->DSpbuffer);
|
||||
pData->DSpbuffer = NULL;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
|
||||
{
|
||||
(void)pDevice;
|
||||
(void)deviceName;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void DSoundCloseCapture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void DSoundStartCapture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void DSoundStopCapture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
|
||||
{
|
||||
(void)pDevice;
|
||||
(void)pBuffer;
|
||||
(void)lSamples;
|
||||
}
|
||||
|
||||
static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
BackendFuncs DSoundFuncs = {
|
||||
DSoundOpenPlayback,
|
||||
DSoundClosePlayback,
|
||||
DSoundResetPlayback,
|
||||
DSoundStopPlayback,
|
||||
DSoundOpenCapture,
|
||||
DSoundCloseCapture,
|
||||
DSoundStartCapture,
|
||||
DSoundStopCapture,
|
||||
DSoundCaptureSamples,
|
||||
DSoundAvailableSamples
|
||||
};
|
||||
|
||||
|
||||
void alcDSoundInit(BackendFuncs *FuncList)
|
||||
{
|
||||
*FuncList = DSoundFuncs;
|
||||
}
|
||||
|
||||
void alcDSoundDeinit(void)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < NumDevices;++i)
|
||||
free(DeviceList[i].name);
|
||||
free(DeviceList);
|
||||
DeviceList = NULL;
|
||||
NumDevices = 0;
|
||||
|
||||
if(ds_handle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
FreeLibrary(ds_handle);
|
||||
#endif
|
||||
ds_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void alcDSoundProbe(int type)
|
||||
{
|
||||
if(!DSoundLoad()) return;
|
||||
|
||||
if(type == DEVICE_PROBE)
|
||||
AppendDeviceList(dsDevice);
|
||||
else if(type == ALL_DEVICE_PROBE)
|
||||
{
|
||||
HRESULT hr;
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < NumDevices;++i)
|
||||
free(DeviceList[i].name);
|
||||
free(DeviceList);
|
||||
DeviceList = NULL;
|
||||
NumDevices = 0;
|
||||
|
||||
hr = pDirectSoundEnumerateA(DSoundEnumDevices, NULL);
|
||||
if(FAILED(hr))
|
||||
AL_PRINT("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
|
||||
else
|
||||
{
|
||||
for(i = 0;i < NumDevices;i++)
|
||||
AppendAllDeviceList(DeviceList[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* 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 {
|
||||
ALvoid *buffer;
|
||||
ALuint size;
|
||||
|
||||
volatile int killNow;
|
||||
ALvoid *thread;
|
||||
} null_data;
|
||||
|
||||
|
||||
static const ALCchar nullDevice[] = "Null Output";
|
||||
|
||||
static ALuint NullProc(ALvoid *ptr)
|
||||
{
|
||||
ALCdevice *Device = (ALCdevice*)ptr;
|
||||
null_data *data = (null_data*)Device->ExtraData;
|
||||
ALuint frameSize;
|
||||
ALuint now, last;
|
||||
ALuint avail;
|
||||
|
||||
frameSize = aluFrameSizeFromFormat(Device->Format);
|
||||
|
||||
last = timeGetTime()<<8;
|
||||
while(!data->killNow && Device->Connected)
|
||||
{
|
||||
now = timeGetTime()<<8;
|
||||
|
||||
avail = (ALuint64)(now-last) * Device->Frequency / (1000<<8);
|
||||
if(avail < Device->UpdateSize)
|
||||
{
|
||||
Sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
while(avail >= Device->UpdateSize)
|
||||
{
|
||||
aluMixData(Device, data->buffer, Device->UpdateSize);
|
||||
|
||||
avail -= Device->UpdateSize;
|
||||
last += (ALuint64)Device->UpdateSize * (1000<<8) / Device->Frequency;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALCboolean null_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
null_data *data;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = nullDevice;
|
||||
else if(strcmp(deviceName, nullDevice) != 0)
|
||||
return ALC_FALSE;
|
||||
|
||||
data = (null_data*)calloc(1, sizeof(*data));
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
data->size = device->UpdateSize * aluFrameSizeFromFormat(device->Format);
|
||||
data->buffer = malloc(data->size);
|
||||
if(!data->buffer)
|
||||
{
|
||||
AL_PRINT("buffer malloc failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
|
||||
data->thread = StartThread(NullProc, device);
|
||||
if(data->thread == NULL)
|
||||
{
|
||||
free(data->buffer);
|
||||
data->buffer = 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;
|
||||
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean null_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
(void)device;
|
||||
(void)deviceName;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
|
||||
BackendFuncs null_funcs = {
|
||||
null_open_playback,
|
||||
null_close_playback,
|
||||
null_reset_playback,
|
||||
null_stop_playback,
|
||||
null_open_capture,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
void alc_null_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = null_funcs;
|
||||
}
|
||||
|
||||
void alc_null_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_null_probe(int type)
|
||||
{
|
||||
if(type == DEVICE_PROBE)
|
||||
AppendDeviceList(nullDevice);
|
||||
else if(type == ALL_DEVICE_PROBE)
|
||||
AppendAllDeviceList(nullDevice);
|
||||
}
|
||||
@@ -0,0 +1,517 @@
|
||||
/**
|
||||
* 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 = aluFrameSizeFromFormat(pDevice->Format);
|
||||
|
||||
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)
|
||||
{
|
||||
AL_PRINT("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 = aluFrameSizeFromFormat(pDevice->Format);
|
||||
|
||||
while(!data->killNow)
|
||||
{
|
||||
amt = read(data->fd, data->mix_data, data->data_size);
|
||||
if(amt < 0)
|
||||
{
|
||||
AL_PRINT("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 ALCboolean 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_FALSE;
|
||||
|
||||
data = (oss_data*)calloc(1, sizeof(oss_data));
|
||||
data->killNow = 0;
|
||||
|
||||
data->fd = open(driver, O_WRONLY);
|
||||
if(data->fd == -1)
|
||||
{
|
||||
free(data);
|
||||
if(errno != ENOENT)
|
||||
AL_PRINT("Could not open %s: %s\n", driver, strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
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;
|
||||
int i;
|
||||
|
||||
switch(aluBytesFromFormat(device->Format))
|
||||
{
|
||||
case 1:
|
||||
ossFormat = AFMT_U8;
|
||||
break;
|
||||
case 4:
|
||||
switch(aluChannelsFromFormat(device->Format))
|
||||
{
|
||||
case 1: device->Format = AL_FORMAT_MONO16; break;
|
||||
case 2: device->Format = AL_FORMAT_STEREO16; break;
|
||||
case 4: device->Format = AL_FORMAT_QUAD16; break;
|
||||
case 6: device->Format = AL_FORMAT_51CHN16; break;
|
||||
case 7: device->Format = AL_FORMAT_61CHN16; break;
|
||||
case 8: device->Format = AL_FORMAT_71CHN16; break;
|
||||
}
|
||||
/* fall-through */
|
||||
case 2:
|
||||
ossFormat = AFMT_S16_NE;
|
||||
break;
|
||||
default:
|
||||
AL_PRINT("Unknown format: 0x%x\n", device->Format);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
periods = device->NumUpdates;
|
||||
numChannels = aluChannelsFromFormat(device->Format);
|
||||
frameSize = numChannels * aluBytesFromFormat(device->Format);
|
||||
|
||||
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 ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
|
||||
/* 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);
|
||||
if (!(ok(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat), "set format") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels), "set channels") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed), "set speed") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_GETOSPACE, &info), "get space")))
|
||||
{
|
||||
AL_PRINT("%s failed: %s\n", err, strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
#undef ok
|
||||
|
||||
if((int)aluChannelsFromFormat(device->Format) != numChannels)
|
||||
{
|
||||
AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), numChannels);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!((ossFormat == AFMT_U8 && aluBytesFromFormat(device->Format) == 1) ||
|
||||
(ossFormat == AFMT_S16_NE && aluBytesFromFormat(device->Format) == 2)))
|
||||
{
|
||||
AL_PRINT("Could not set %d-bit output, got format %#x\n", aluBytesFromFormat(device->Format)*8, ossFormat);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
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)
|
||||
AL_PRINT("Error resetting device: %s\n", strerror(errno));
|
||||
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean 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;
|
||||
int i;
|
||||
|
||||
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_FALSE;
|
||||
|
||||
data = (oss_data*)calloc(1, sizeof(oss_data));
|
||||
data->killNow = 0;
|
||||
|
||||
data->fd = open(driver, O_RDONLY);
|
||||
if(data->fd == -1)
|
||||
{
|
||||
free(data);
|
||||
if(errno != ENOENT)
|
||||
AL_PRINT("Could not open %s: %s\n", driver, strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
switch(aluBytesFromFormat(device->Format))
|
||||
{
|
||||
case 1:
|
||||
ossFormat = AFMT_U8;
|
||||
break;
|
||||
case 2:
|
||||
ossFormat = AFMT_S16_NE;
|
||||
break;
|
||||
default:
|
||||
AL_PRINT("Unknown format: 0x%x\n", device->Format);
|
||||
close(data->fd);
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
periods = 4;
|
||||
numChannels = aluChannelsFromFormat(device->Format);
|
||||
frameSize = numChannels * aluBytesFromFormat(device->Format);
|
||||
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 ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
|
||||
if (!(ok(ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize), "set fragment") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat), "set format") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels), "set channels") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed), "set speed") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_GETISPACE, &info), "get space")))
|
||||
{
|
||||
AL_PRINT("%s failed: %s\n", err, strerror(errno));
|
||||
close(data->fd);
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
#undef ok
|
||||
|
||||
if((int)aluChannelsFromFormat(device->Format) != numChannels)
|
||||
{
|
||||
AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), numChannels);
|
||||
close(data->fd);
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!((ossFormat == AFMT_U8 && aluBytesFromFormat(device->Format) == 1) ||
|
||||
(ossFormat == AFMT_S16_NE && aluBytesFromFormat(device->Format) == 2)))
|
||||
{
|
||||
AL_PRINT("Could not set %d-bit input, got format %#x\n", aluBytesFromFormat(device->Format)*8, ossFormat);
|
||||
close(data->fd);
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
|
||||
if(!data->ring)
|
||||
{
|
||||
AL_PRINT("ring buffer create failed\n");
|
||||
close(data->fd);
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
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_FALSE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
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 void oss_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
|
||||
{
|
||||
oss_data *data = (oss_data*)pDevice->ExtraData;
|
||||
if(lSamples <= (ALCuint)RingBufferSize(data->ring))
|
||||
ReadRingBuffer(data->ring, pBuffer, lSamples);
|
||||
else
|
||||
alcSetError(pDevice, ALC_INVALID_VALUE);
|
||||
}
|
||||
|
||||
static ALCuint oss_available_samples(ALCdevice *pDevice)
|
||||
{
|
||||
oss_data *data = (oss_data*)pDevice->ExtraData;
|
||||
return RingBufferSize(data->ring);
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
void alc_oss_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = oss_funcs;
|
||||
}
|
||||
|
||||
void alc_oss_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_oss_probe(int type)
|
||||
{
|
||||
if(type == DEVICE_PROBE)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0)
|
||||
#endif
|
||||
AppendDeviceList(oss_device);
|
||||
}
|
||||
else if(type == ALL_DEVICE_PROBE)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0)
|
||||
#endif
|
||||
AppendAllDeviceList(oss_device);
|
||||
}
|
||||
else if(type == CAPTURE_DEVICE_PROBE)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(GetConfigValue("oss", "capture", "/dev/dsp"), &buf) == 0)
|
||||
#endif
|
||||
AppendCaptureDeviceList(oss_device);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
/**
|
||||
* 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"
|
||||
#ifdef HAVE_DLFCN_H
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <portaudio.h>
|
||||
|
||||
static void *pa_handle;
|
||||
#define MAKE_FUNC(x) static typeof(x) * p##x
|
||||
MAKE_FUNC(Pa_Initialize);
|
||||
MAKE_FUNC(Pa_Terminate);
|
||||
MAKE_FUNC(Pa_GetErrorText);
|
||||
MAKE_FUNC(Pa_StartStream);
|
||||
MAKE_FUNC(Pa_StopStream);
|
||||
MAKE_FUNC(Pa_OpenStream);
|
||||
MAKE_FUNC(Pa_CloseStream);
|
||||
MAKE_FUNC(Pa_GetDefaultOutputDevice);
|
||||
MAKE_FUNC(Pa_GetStreamInfo);
|
||||
#undef MAKE_FUNC
|
||||
|
||||
|
||||
static const ALCchar pa_device[] = "PortAudio Default";
|
||||
|
||||
|
||||
void *pa_load(void)
|
||||
{
|
||||
if(!pa_handle)
|
||||
{
|
||||
PaError err;
|
||||
|
||||
#ifdef _WIN32
|
||||
pa_handle = LoadLibrary("portaudio.dll");
|
||||
#define LOAD_FUNC(x) do { \
|
||||
p##x = (typeof(p##x))GetProcAddress(pa_handle, #x); \
|
||||
if(!(p##x)) { \
|
||||
AL_PRINT("Could not load %s from portaudio.dll\n", #x); \
|
||||
FreeLibrary(pa_handle); \
|
||||
pa_handle = NULL; \
|
||||
return NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#elif defined(HAVE_DLFCN_H)
|
||||
|
||||
const char *str;
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
# define PALIB "libportaudio.2.dylib"
|
||||
#else
|
||||
# define PALIB "libportaudio.so.2"
|
||||
#endif
|
||||
pa_handle = dlopen(PALIB, RTLD_NOW);
|
||||
dlerror();
|
||||
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = (typeof(f)*)dlsym(pa_handle, #f); \
|
||||
if((str=dlerror()) != NULL) \
|
||||
{ \
|
||||
dlclose(pa_handle); \
|
||||
pa_handle = NULL; \
|
||||
AL_PRINT("Could not load %s from "PALIB": %s\n", #f, str); \
|
||||
return NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#else
|
||||
pa_handle = (void*)0xDEADBEEF;
|
||||
#define LOAD_FUNC(f) p##f = f
|
||||
#endif
|
||||
|
||||
if(!pa_handle)
|
||||
return NULL;
|
||||
|
||||
LOAD_FUNC(Pa_Initialize);
|
||||
LOAD_FUNC(Pa_Terminate);
|
||||
LOAD_FUNC(Pa_GetErrorText);
|
||||
LOAD_FUNC(Pa_StartStream);
|
||||
LOAD_FUNC(Pa_StopStream);
|
||||
LOAD_FUNC(Pa_OpenStream);
|
||||
LOAD_FUNC(Pa_CloseStream);
|
||||
LOAD_FUNC(Pa_GetDefaultOutputDevice);
|
||||
LOAD_FUNC(Pa_GetStreamInfo);
|
||||
|
||||
#undef LOAD_FUNC
|
||||
|
||||
if((err=pPa_Initialize()) != paNoError)
|
||||
{
|
||||
AL_PRINT("Pa_Initialize() returned an error: %s\n", pPa_GetErrorText(err));
|
||||
#ifdef _WIN32
|
||||
FreeLibrary(pa_handle);
|
||||
#elif defined(HAVE_DLFCN_H)
|
||||
dlclose(pa_handle);
|
||||
#endif
|
||||
pa_handle = NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return pa_handle;
|
||||
}
|
||||
|
||||
|
||||
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 ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
const PaStreamInfo *streamInfo;
|
||||
PaStreamParameters outParams;
|
||||
pa_data *data;
|
||||
PaError err;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = pa_device;
|
||||
else if(strcmp(deviceName, pa_device) != 0)
|
||||
return ALC_FALSE;
|
||||
|
||||
if(!pa_load())
|
||||
return ALC_FALSE;
|
||||
|
||||
data = (pa_data*)calloc(1, sizeof(pa_data));
|
||||
data->update_size = device->UpdateSize;
|
||||
|
||||
device->ExtraData = data;
|
||||
|
||||
outParams.device = GetConfigValueInt("port", "device", -1);
|
||||
if(outParams.device < 0)
|
||||
outParams.device = pPa_GetDefaultOutputDevice();
|
||||
outParams.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
|
||||
(float)device->Frequency;
|
||||
outParams.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
switch(aluBytesFromFormat(device->Format))
|
||||
{
|
||||
case 1:
|
||||
outParams.sampleFormat = paUInt8;
|
||||
break;
|
||||
case 2:
|
||||
outParams.sampleFormat = paInt16;
|
||||
break;
|
||||
case 4:
|
||||
outParams.sampleFormat = paFloat32;
|
||||
break;
|
||||
default:
|
||||
AL_PRINT("Unknown format: 0x%x\n", device->Format);
|
||||
device->ExtraData = NULL;
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
outParams.channelCount = aluChannelsFromFormat(device->Format);
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
err = pPa_OpenStream(&data->stream, NULL, &outParams, device->Frequency,
|
||||
device->UpdateSize, paNoFlag, pa_callback, device);
|
||||
if(err != paNoError)
|
||||
{
|
||||
AL_PRINT("Pa_OpenStream() returned an error: %s\n", pPa_GetErrorText(err));
|
||||
device->ExtraData = NULL;
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
streamInfo = pPa_GetStreamInfo(data->stream);
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->Frequency = streamInfo->sampleRate;
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void pa_close_playback(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = pPa_CloseStream(data->stream);
|
||||
if(err != paNoError)
|
||||
AL_PRINT("Error closing stream: %s\n", pPa_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 = pPa_GetStreamInfo(data->stream);
|
||||
device->Frequency = streamInfo->sampleRate;
|
||||
device->UpdateSize = data->update_size;
|
||||
|
||||
err = pPa_StartStream(data->stream);
|
||||
if(err != paNoError)
|
||||
{
|
||||
AL_PRINT("Pa_StartStream() returned an error: %s\n", pPa_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 = pPa_StopStream(data->stream);
|
||||
if(err != paNoError)
|
||||
AL_PRINT("Error stopping stream: %s\n", pPa_GetErrorText(err));
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean 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_FALSE;
|
||||
|
||||
if(!pa_load())
|
||||
return ALC_FALSE;
|
||||
|
||||
data = (pa_data*)calloc(1, sizeof(pa_data));
|
||||
if(data == NULL)
|
||||
{
|
||||
alcSetError(device, ALC_OUT_OF_MEMORY);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
frame_size = aluFrameSizeFromFormat(device->Format);
|
||||
data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);
|
||||
if(data->ring == NULL)
|
||||
{
|
||||
alcSetError(device, ALC_OUT_OF_MEMORY);
|
||||
goto error;
|
||||
}
|
||||
|
||||
inParams.device = GetConfigValueInt("port", "capture", -1);
|
||||
if(inParams.device < 0)
|
||||
inParams.device = pPa_GetDefaultOutputDevice();
|
||||
inParams.suggestedLatency = 0.0f;
|
||||
inParams.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
switch(aluBytesFromFormat(device->Format))
|
||||
{
|
||||
case 1:
|
||||
inParams.sampleFormat = paUInt8;
|
||||
break;
|
||||
case 2:
|
||||
inParams.sampleFormat = paInt16;
|
||||
break;
|
||||
case 4:
|
||||
inParams.sampleFormat = paFloat32;
|
||||
break;
|
||||
default:
|
||||
AL_PRINT("Unknown format: 0x%x\n", device->Format);
|
||||
goto error;
|
||||
}
|
||||
inParams.channelCount = aluChannelsFromFormat(device->Format);
|
||||
|
||||
err = pPa_OpenStream(&data->stream, &inParams, NULL, device->Frequency,
|
||||
paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device);
|
||||
if(err != paNoError)
|
||||
{
|
||||
AL_PRINT("Pa_OpenStream() returned an error: %s\n", pPa_GetErrorText(err));
|
||||
goto error;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
|
||||
device->ExtraData = data;
|
||||
return ALC_TRUE;
|
||||
|
||||
error:
|
||||
DestroyRingBuffer(data->ring);
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void pa_close_capture(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = pPa_CloseStream(data->stream);
|
||||
if(err != paNoError)
|
||||
AL_PRINT("Error closing stream: %s\n", pPa_GetErrorText(err));
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void pa_start_capture(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = pPa_StartStream(data->stream);
|
||||
if(err != paNoError)
|
||||
AL_PRINT("Error starting stream: %s\n", pPa_GetErrorText(err));
|
||||
}
|
||||
|
||||
static void pa_stop_capture(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = pPa_StopStream(data->stream);
|
||||
if(err != paNoError)
|
||||
AL_PRINT("Error stopping stream: %s\n", pPa_GetErrorText(err));
|
||||
}
|
||||
|
||||
static void pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
if(samples <= (ALCuint)RingBufferSize(data->ring))
|
||||
ReadRingBuffer(data->ring, buffer, samples);
|
||||
else
|
||||
alcSetError(device, ALC_INVALID_VALUE);
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
void alc_pa_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = pa_funcs;
|
||||
}
|
||||
|
||||
void alc_pa_deinit(void)
|
||||
{
|
||||
if(pa_handle)
|
||||
{
|
||||
pPa_Terminate();
|
||||
#ifdef _WIN32
|
||||
FreeLibrary(pa_handle);
|
||||
#elif defined(HAVE_DLFCN_H)
|
||||
dlclose(pa_handle);
|
||||
#endif
|
||||
pa_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void alc_pa_probe(int type)
|
||||
{
|
||||
if(!pa_load()) return;
|
||||
|
||||
if(type == DEVICE_PROBE)
|
||||
AppendDeviceList(pa_device);
|
||||
else if(type == ALL_DEVICE_PROBE)
|
||||
AppendAllDeviceList(pa_device);
|
||||
else if(type == CAPTURE_DEVICE_PROBE)
|
||||
AppendCaptureDeviceList(pa_device);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,304 @@
|
||||
/**
|
||||
* 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;
|
||||
int remaining = 0;
|
||||
ALint frameSize;
|
||||
int wrote;
|
||||
|
||||
SetRTPriority();
|
||||
|
||||
frameSize = aluFrameSizeFromFormat(pDevice->Format);
|
||||
|
||||
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)
|
||||
{
|
||||
AL_PRINT("write failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(pDevice);
|
||||
break;
|
||||
}
|
||||
|
||||
Sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean 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_FALSE;
|
||||
|
||||
data = (solaris_data*)calloc(1, sizeof(solaris_data));
|
||||
data->killNow = 0;
|
||||
|
||||
data->fd = open(driver, O_WRONLY);
|
||||
if(data->fd == -1)
|
||||
{
|
||||
free(data);
|
||||
if(errno != ENOENT)
|
||||
AL_PRINT("Could not open %s: %s\n", driver, strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
switch(aluBytesFromFormat(device->Format))
|
||||
{
|
||||
case 1:
|
||||
info.play.precision = 8;
|
||||
info.play.encoding = AUDIO_ENCODING_LINEAR8;
|
||||
break;
|
||||
case 4:
|
||||
switch(numChannels)
|
||||
{
|
||||
case 1: device->Format = AL_FORMAT_MONO16; break;
|
||||
case 2: device->Format = AL_FORMAT_STEREO16; break;
|
||||
case 4: device->Format = AL_FORMAT_QUAD16; break;
|
||||
case 6: device->Format = AL_FORMAT_51CHN16; break;
|
||||
case 7: device->Format = AL_FORMAT_61CHN16; break;
|
||||
case 8: device->Format = AL_FORMAT_71CHN16; break;
|
||||
}
|
||||
/* fall-through */
|
||||
case 2:
|
||||
info.play.precision = 16;
|
||||
info.play.encoding = AUDIO_ENCODING_LINEAR;
|
||||
break;
|
||||
default:
|
||||
AL_PRINT("Unknown format: 0x%x\n", device->Format);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
numChannels = aluChannelsFromFormat(device->Format);
|
||||
info.play.sample_rate = device->Frequency;
|
||||
info.play.channels = numChannels;
|
||||
|
||||
frameSize = numChannels * aluBytesFromFormat(device->Format);
|
||||
info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize;
|
||||
|
||||
if(ioctl(data->fd, AUDIO_SETINFO, &info) < 0)
|
||||
{
|
||||
AL_PRINT("ioctl failed: %s\n", strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(aluChannelsFromFormat(device->Format) != info.play.channels)
|
||||
{
|
||||
AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), info.play.channels);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!((info.play.precision == 8 && aluBytesFromFormat(device->Format) == 1) ||
|
||||
(info.play.precision == 16 && aluBytesFromFormat(device->Format) == 2)))
|
||||
{
|
||||
AL_PRINT("Could not set %d-bit output, got %d\n", aluBytesFromFormat(device->Format)*8, info.play.precision);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean solaris_open_capture(ALCdevice *device, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
|
||||
{
|
||||
(void)device;
|
||||
(void)deviceName;
|
||||
(void)frequency;
|
||||
(void)format;
|
||||
(void)SampleSize;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void solaris_close_capture(ALCdevice *device)
|
||||
{
|
||||
(void)device;
|
||||
}
|
||||
|
||||
static void solaris_start_capture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void solaris_stop_capture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void solaris_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
|
||||
{
|
||||
(void)pDevice;
|
||||
(void)pBuffer;
|
||||
(void)lSamples;
|
||||
}
|
||||
|
||||
static ALCuint solaris_available_samples(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
BackendFuncs solaris_funcs = {
|
||||
solaris_open_playback,
|
||||
solaris_close_playback,
|
||||
solaris_reset_playback,
|
||||
solaris_stop_playback,
|
||||
solaris_open_capture,
|
||||
solaris_close_capture,
|
||||
solaris_start_capture,
|
||||
solaris_stop_capture,
|
||||
solaris_capture_samples,
|
||||
solaris_available_samples
|
||||
};
|
||||
|
||||
void alc_solaris_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = solaris_funcs;
|
||||
}
|
||||
|
||||
void alc_solaris_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_solaris_probe(int type)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(GetConfigValue("solaris", "device", "/dev/audio"), &buf) != 0)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if(type == DEVICE_PROBE)
|
||||
AppendDeviceList(solaris_device);
|
||||
else if(type == ALL_DEVICE_PROBE)
|
||||
AppendAllDeviceList(solaris_device);
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
/**
|
||||
* 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 ALuint WaveProc(ALvoid *ptr)
|
||||
{
|
||||
ALCdevice *pDevice = (ALCdevice*)ptr;
|
||||
wave_data *data = (wave_data*)pDevice->ExtraData;
|
||||
ALuint frameSize;
|
||||
ALuint now, last;
|
||||
size_t fs;
|
||||
ALuint avail;
|
||||
union {
|
||||
short s;
|
||||
char b[sizeof(short)];
|
||||
} uSB;
|
||||
|
||||
uSB.s = 1;
|
||||
frameSize = aluFrameSizeFromFormat(pDevice->Format);
|
||||
|
||||
last = timeGetTime()<<8;
|
||||
while(!data->killNow && pDevice->Connected)
|
||||
{
|
||||
now = timeGetTime()<<8;
|
||||
|
||||
avail = (ALuint64)(now-last) * pDevice->Frequency / (1000<<8);
|
||||
if(avail < pDevice->UpdateSize)
|
||||
{
|
||||
Sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
while(avail >= pDevice->UpdateSize)
|
||||
{
|
||||
aluMixData(pDevice, data->buffer, pDevice->UpdateSize);
|
||||
|
||||
if(uSB.b[0] != 1)
|
||||
{
|
||||
ALubyte *bytes = data->buffer;
|
||||
ALuint i;
|
||||
|
||||
if(aluBytesFromFormat(pDevice->Format) == 1)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i], data->f);
|
||||
}
|
||||
else if(aluBytesFromFormat(pDevice->Format) == 2)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i^1], data->f);
|
||||
}
|
||||
else if(aluBytesFromFormat(pDevice->Format) == 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))
|
||||
{
|
||||
AL_PRINT("Error writing to file\n");
|
||||
aluHandleDisconnect(pDevice);
|
||||
break;
|
||||
}
|
||||
|
||||
avail -= pDevice->UpdateSize;
|
||||
last += (ALuint64)pDevice->UpdateSize * (1000<<8) / pDevice->Frequency;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALCboolean wave_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
wave_data *data;
|
||||
const char *fname;
|
||||
|
||||
fname = GetConfigValue("wave", "file", "");
|
||||
if(!fname[0])
|
||||
return ALC_FALSE;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = waveDevice;
|
||||
else if(strcmp(deviceName, waveDevice) != 0)
|
||||
return ALC_FALSE;
|
||||
|
||||
data = (wave_data*)calloc(1, sizeof(wave_data));
|
||||
|
||||
data->f = fopen(fname, "wb");
|
||||
if(!data->f)
|
||||
{
|
||||
free(data);
|
||||
AL_PRINT("Could not open file '%s': %s\n", fname, strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
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, bits, i;
|
||||
size_t val;
|
||||
|
||||
fseek(data->f, 0, SEEK_SET);
|
||||
clearerr(data->f);
|
||||
|
||||
bits = aluBytesFromFormat(device->Format) * 8;
|
||||
channels = aluChannelsFromFormat(device->Format);
|
||||
|
||||
/* 7.1 max */
|
||||
if(channels > 8)
|
||||
{
|
||||
if(bits == 8)
|
||||
device->Format = AL_FORMAT_71CHN8;
|
||||
else if(bits == 16)
|
||||
device->Format = AL_FORMAT_71CHN16;
|
||||
else
|
||||
{
|
||||
device->Format = AL_FORMAT_71CHN32;
|
||||
bits = 32;
|
||||
}
|
||||
channels = 8;
|
||||
}
|
||||
|
||||
fprintf(data->f, "RIFF");
|
||||
fputc(0xFF, data->f); // 'RIFF' header len; filled in at close
|
||||
fputc(0xFF, data->f);
|
||||
fputc(0xFF, data->f);
|
||||
fputc(0xFF, data->f);
|
||||
|
||||
fprintf(data->f, "WAVE");
|
||||
|
||||
fprintf(data->f, "fmt ");
|
||||
fputc(40, data->f); // 'fmt ' header len; 40 bytes for EXTENSIBLE
|
||||
fputc(0, data->f);
|
||||
fputc(0, data->f);
|
||||
fputc(0, data->f);
|
||||
// 16-bit val, format type id (extensible: 0xFFFE)
|
||||
fputc(0xFE, data->f);
|
||||
fputc(0xFF, data->f);
|
||||
// 16-bit val, channel count
|
||||
fputc(channels&0xff, data->f);
|
||||
fputc((channels>>8)&0xff, data->f);
|
||||
// 32-bit val, frequency
|
||||
fputc(device->Frequency&0xff, data->f);
|
||||
fputc((device->Frequency>>8)&0xff, data->f);
|
||||
fputc((device->Frequency>>16)&0xff, data->f);
|
||||
fputc((device->Frequency>>24)&0xff, data->f);
|
||||
// 32-bit val, bytes per second
|
||||
i = device->Frequency * channels * bits / 8;
|
||||
fputc(i&0xff, data->f);
|
||||
fputc((i>>8)&0xff, data->f);
|
||||
fputc((i>>16)&0xff, data->f);
|
||||
fputc((i>>24)&0xff, data->f);
|
||||
// 16-bit val, frame size
|
||||
i = channels * bits / 8;
|
||||
fputc(i&0xff, data->f);
|
||||
fputc((i>>8)&0xff, data->f);
|
||||
// 16-bit val, bits per sample
|
||||
fputc(bits&0xff, data->f);
|
||||
fputc((bits>>8)&0xff, data->f);
|
||||
// 16-bit val, extra byte count
|
||||
fputc(22, data->f);
|
||||
fputc(0, data->f);
|
||||
// 16-bit val, valid bits per sample
|
||||
fputc(bits&0xff, data->f);
|
||||
fputc((bits>>8)&0xff, data->f);
|
||||
// 32-bit val, channel mask
|
||||
i = channel_masks[channels];
|
||||
fputc(i&0xff, data->f);
|
||||
fputc((i>>8)&0xff, data->f);
|
||||
fputc((i>>16)&0xff, data->f);
|
||||
fputc((i>>24)&0xff, data->f);
|
||||
// 16 byte GUID, sub-type format
|
||||
val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f);
|
||||
|
||||
fprintf(data->f, "data");
|
||||
fputc(0xFF, data->f); // 'data' header len; filled in at close
|
||||
fputc(0xFF, data->f);
|
||||
fputc(0xFF, data->f);
|
||||
fputc(0xFF, data->f);
|
||||
|
||||
if(ferror(data->f))
|
||||
{
|
||||
AL_PRINT("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)
|
||||
{
|
||||
AL_PRINT("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)
|
||||
{
|
||||
fputc(dataLen&0xff, data->f); // 'data' header len
|
||||
fputc((dataLen>>8)&0xff, data->f);
|
||||
fputc((dataLen>>16)&0xff, data->f);
|
||||
fputc((dataLen>>24)&0xff, data->f);
|
||||
}
|
||||
if(fseek(data->f, 4, SEEK_SET) == 0)
|
||||
{
|
||||
size -= 8;
|
||||
fputc(size&0xff, data->f); // 'WAVE' header len
|
||||
fputc((size>>8)&0xff, data->f);
|
||||
fputc((size>>16)&0xff, data->f);
|
||||
fputc((size>>24)&0xff, data->f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean wave_open_capture(ALCdevice *pDevice, const ALCchar *deviceName)
|
||||
{
|
||||
(void)pDevice;
|
||||
(void)deviceName;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
|
||||
BackendFuncs wave_funcs = {
|
||||
wave_open_playback,
|
||||
wave_close_playback,
|
||||
wave_reset_playback,
|
||||
wave_stop_playback,
|
||||
wave_open_capture,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
void alc_wave_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = wave_funcs;
|
||||
}
|
||||
|
||||
void alc_wave_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_wave_probe(int type)
|
||||
{
|
||||
if(!ConfigValueExists("wave", "file"))
|
||||
return;
|
||||
|
||||
if(type == DEVICE_PROBE)
|
||||
AppendDeviceList(waveDevice);
|
||||
else if(type == ALL_DEVICE_PROBE)
|
||||
AppendAllDeviceList(waveDevice);
|
||||
}
|
||||
@@ -0,0 +1,493 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
#define _WIN32_WINNT 0x0500
|
||||
#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 Capture Device
|
||||
ALboolean bWaveInShutdown;
|
||||
HANDLE hWaveInHdrEvent;
|
||||
HANDLE hWaveInThreadEvent;
|
||||
HANDLE hWaveInThread;
|
||||
DWORD ulWaveInThreadID;
|
||||
ALint lWaveInBuffersCommitted;
|
||||
HWAVEIN hWaveInHandle;
|
||||
WAVEHDR WaveInBuffer[4];
|
||||
ALCchar *pCapturedSampleData;
|
||||
ALuint ulCapturedDataSize;
|
||||
ALuint ulReadCapturedDataPos;
|
||||
ALuint ulWriteCapturedDataPos;
|
||||
} WinMMData;
|
||||
|
||||
|
||||
static ALCchar **CaptureDeviceList;
|
||||
static ALuint NumCaptureDevices;
|
||||
|
||||
static void ProbeDevices(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];
|
||||
snprintf(name, sizeof(name), "%s via WaveIn", WaveInCaps.szPname);
|
||||
CaptureDeviceList[i] = strdup(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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))
|
||||
{
|
||||
// Decrement number of buffers in use
|
||||
pData->lWaveInBuffersCommitted--;
|
||||
|
||||
if (pData->bWaveInShutdown == AL_FALSE)
|
||||
{
|
||||
// Notify Wave Processor Thread that a Wave Header has returned
|
||||
PostThreadMessage(pData->ulWaveInThreadID,uMsg,0,dwParam1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pData->lWaveInBuffersCommitted == 0)
|
||||
{
|
||||
// Signal Wave Buffers Returned event
|
||||
if (pData->hWaveInHdrEvent)
|
||||
SetEvent(pData->hWaveInHdrEvent);
|
||||
|
||||
// Post 'Quit' Message to WaveIn Processor Thread
|
||||
PostThreadMessage(pData->ulWaveInThreadID,WM_QUIT,0,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
CaptureThreadProc
|
||||
|
||||
Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
|
||||
audio data.
|
||||
*/
|
||||
DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
|
||||
{
|
||||
ALCdevice *pDevice = (ALCdevice*)lpParameter;
|
||||
WinMMData *pData = pDevice->ExtraData;
|
||||
ALuint ulOffset, ulMaxSize, ulSection;
|
||||
LPWAVEHDR pWaveHdr;
|
||||
MSG msg;
|
||||
|
||||
while (GetMessage(&msg, NULL, 0, 0))
|
||||
{
|
||||
if ((msg.message==WIM_DATA)&&(!pData->bWaveInShutdown))
|
||||
{
|
||||
SuspendContext(NULL);
|
||||
|
||||
pWaveHdr = ((LPWAVEHDR)msg.lParam);
|
||||
|
||||
// Calculate offset in local buffer to write data to
|
||||
ulOffset = pData->ulWriteCapturedDataPos % pData->ulCapturedDataSize;
|
||||
|
||||
if ((ulOffset + pWaveHdr->dwBytesRecorded) > pData->ulCapturedDataSize)
|
||||
{
|
||||
ulSection = pData->ulCapturedDataSize - ulOffset;
|
||||
memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, ulSection);
|
||||
memcpy(pData->pCapturedSampleData, pWaveHdr->lpData + ulSection, pWaveHdr->dwBytesRecorded - ulSection);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, pWaveHdr->dwBytesRecorded);
|
||||
}
|
||||
|
||||
pData->ulWriteCapturedDataPos += pWaveHdr->dwBytesRecorded;
|
||||
|
||||
if (pData->ulWriteCapturedDataPos > (pData->ulReadCapturedDataPos + pData->ulCapturedDataSize))
|
||||
{
|
||||
// Application has not read enough audio data from the capture buffer so data has been
|
||||
// overwritten. Reset ReadPosition.
|
||||
pData->ulReadCapturedDataPos = pData->ulWriteCapturedDataPos - pData->ulCapturedDataSize;
|
||||
}
|
||||
|
||||
// To prevent an over-flow prevent the offset values from getting too large
|
||||
ulMaxSize = pData->ulCapturedDataSize << 4;
|
||||
if ((pData->ulReadCapturedDataPos > ulMaxSize) && (pData->ulWriteCapturedDataPos > ulMaxSize))
|
||||
{
|
||||
pData->ulReadCapturedDataPos -= ulMaxSize;
|
||||
pData->ulWriteCapturedDataPos -= ulMaxSize;
|
||||
}
|
||||
|
||||
// Send buffer back to capture more data
|
||||
waveInAddBuffer(pData->hWaveInHandle,pWaveHdr,sizeof(WAVEHDR));
|
||||
pData->lWaveInBuffersCommitted++;
|
||||
|
||||
ProcessContext(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// Signal Wave Thread completed event
|
||||
if (pData->hWaveInThreadEvent)
|
||||
SetEvent(pData->hWaveInThreadEvent);
|
||||
|
||||
ExitThread(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean WinMMOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
(void)device;
|
||||
(void)deviceName;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void WinMMClosePlayback(ALCdevice *device)
|
||||
{
|
||||
(void)device;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
|
||||
{
|
||||
WAVEFORMATEX wfexCaptureFormat;
|
||||
WinMMData *pData = NULL;
|
||||
ALint lDeviceID = 0;
|
||||
ALint lBufferSize;
|
||||
ALuint i;
|
||||
|
||||
if(!CaptureDeviceList)
|
||||
ProbeDevices();
|
||||
|
||||
// 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_FALSE;
|
||||
|
||||
pData = calloc(1, sizeof(*pData));
|
||||
if(!pData)
|
||||
{
|
||||
alcSetError(pDevice, ALC_OUT_OF_MEMORY);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX));
|
||||
wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfexCaptureFormat.nChannels = aluChannelsFromFormat(pDevice->Format);
|
||||
wfexCaptureFormat.wBitsPerSample = aluBytesFromFormat(pDevice->Format) * 8;
|
||||
wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample *
|
||||
wfexCaptureFormat.nChannels / 8;
|
||||
wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency;
|
||||
wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec *
|
||||
wfexCaptureFormat.nBlockAlign;
|
||||
wfexCaptureFormat.cbSize = 0;
|
||||
|
||||
if (waveInOpen(&pData->hWaveInHandle, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
|
||||
goto failure;
|
||||
|
||||
pData->hWaveInHdrEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInAllHeadersReturned");
|
||||
if (pData->hWaveInHdrEvent == NULL)
|
||||
goto failure;
|
||||
|
||||
pData->hWaveInThreadEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInThreadDestroyed");
|
||||
if (pData->hWaveInThreadEvent == NULL)
|
||||
goto failure;
|
||||
|
||||
// Allocate circular memory buffer for the captured audio
|
||||
pData->ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates *
|
||||
wfexCaptureFormat.nBlockAlign;
|
||||
|
||||
// Make sure circular buffer is at least 100ms in size (and an exact multiple of
|
||||
// the block alignment
|
||||
if (pData->ulCapturedDataSize < (wfexCaptureFormat.nAvgBytesPerSec / 10))
|
||||
{
|
||||
pData->ulCapturedDataSize = wfexCaptureFormat.nAvgBytesPerSec / 10;
|
||||
pData->ulCapturedDataSize -= (pData->ulCapturedDataSize % wfexCaptureFormat.nBlockAlign);
|
||||
}
|
||||
|
||||
pData->pCapturedSampleData = (ALCchar*)malloc(pData->ulCapturedDataSize);
|
||||
pData->lWaveInBuffersCommitted=0;
|
||||
|
||||
// Create 4 Buffers of 50ms each
|
||||
lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20;
|
||||
lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign);
|
||||
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
memset(&pData->WaveInBuffer[i], 0, sizeof(WAVEHDR));
|
||||
pData->WaveInBuffer[i].dwBufferLength = lBufferSize;
|
||||
pData->WaveInBuffer[i].lpData = calloc(1,pData->WaveInBuffer[i].dwBufferLength);
|
||||
pData->WaveInBuffer[i].dwFlags = 0;
|
||||
pData->WaveInBuffer[i].dwLoops = 0;
|
||||
waveInPrepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
|
||||
waveInAddBuffer(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
|
||||
pData->lWaveInBuffersCommitted++;
|
||||
}
|
||||
|
||||
pData->ulReadCapturedDataPos = 0;
|
||||
pData->ulWriteCapturedDataPos = 0;
|
||||
|
||||
pDevice->ExtraData = pData;
|
||||
|
||||
pData->hWaveInThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveInThreadID);
|
||||
if (pData->hWaveInThread == NULL)
|
||||
goto failure;
|
||||
|
||||
pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]);
|
||||
return ALC_TRUE;
|
||||
|
||||
failure:
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
if(pData->WaveInBuffer[i].lpData)
|
||||
{
|
||||
waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
|
||||
free(pData->WaveInBuffer[i].lpData);
|
||||
}
|
||||
}
|
||||
|
||||
free(pData->pCapturedSampleData);
|
||||
if(pData->hWaveInHandle)
|
||||
waveInClose(pData->hWaveInHandle);
|
||||
if(pData->hWaveInThread)
|
||||
CloseHandle(pData->hWaveInThread);
|
||||
if (pData->hWaveInHdrEvent)
|
||||
CloseHandle(pData->hWaveInHdrEvent);
|
||||
if (pData->hWaveInThreadEvent)
|
||||
CloseHandle(pData->hWaveInThreadEvent);
|
||||
|
||||
free(pData);
|
||||
pDevice->ExtraData = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void WinMMCloseCapture(ALCdevice *pDevice)
|
||||
{
|
||||
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
||||
int i;
|
||||
|
||||
// Call waveOutReset to shutdown wave device
|
||||
pData->bWaveInShutdown = AL_TRUE;
|
||||
waveInReset(pData->hWaveInHandle);
|
||||
|
||||
// Wait for signal that all Wave Buffers have returned
|
||||
WaitForSingleObjectEx(pData->hWaveInHdrEvent, 5000, FALSE);
|
||||
|
||||
// Wait for signal that Wave Thread has been destroyed
|
||||
WaitForSingleObjectEx(pData->hWaveInThreadEvent, 5000, FALSE);
|
||||
|
||||
// Release the wave buffers
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
|
||||
free(pData->WaveInBuffer[i].lpData);
|
||||
}
|
||||
|
||||
// Free Audio Buffer data
|
||||
free(pData->pCapturedSampleData);
|
||||
pData->pCapturedSampleData = NULL;
|
||||
|
||||
// Close the Wave device
|
||||
waveInClose(pData->hWaveInHandle);
|
||||
pData->hWaveInHandle = 0;
|
||||
|
||||
CloseHandle(pData->hWaveInThread);
|
||||
pData->hWaveInThread = 0;
|
||||
|
||||
if (pData->hWaveInHdrEvent)
|
||||
{
|
||||
CloseHandle(pData->hWaveInHdrEvent);
|
||||
pData->hWaveInHdrEvent = 0;
|
||||
}
|
||||
|
||||
if (pData->hWaveInThreadEvent)
|
||||
{
|
||||
CloseHandle(pData->hWaveInThreadEvent);
|
||||
pData->hWaveInThreadEvent = 0;
|
||||
}
|
||||
|
||||
free(pData);
|
||||
pDevice->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void WinMMStartCapture(ALCdevice *pDevice)
|
||||
{
|
||||
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
||||
waveInStart(pData->hWaveInHandle);
|
||||
}
|
||||
|
||||
static void WinMMStopCapture(ALCdevice *pDevice)
|
||||
{
|
||||
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
||||
waveInStop(pData->hWaveInHandle);
|
||||
}
|
||||
|
||||
static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
|
||||
{
|
||||
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
||||
ALuint ulSamples = (unsigned long)lSamples;
|
||||
ALuint ulBytes, ulBytesToCopy;
|
||||
ALuint ulCapturedSamples;
|
||||
ALuint ulReadOffset;
|
||||
ALuint frameSize = aluFrameSizeFromFormat(pDevice->Format);
|
||||
|
||||
// Check that we have the requested numbers of Samples
|
||||
ulCapturedSamples = (pData->ulWriteCapturedDataPos -
|
||||
pData->ulReadCapturedDataPos) /
|
||||
frameSize;
|
||||
if(ulSamples > ulCapturedSamples)
|
||||
{
|
||||
alcSetError(pDevice, ALC_INVALID_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
ulBytes = ulSamples * frameSize;
|
||||
|
||||
// Get Read Offset
|
||||
ulReadOffset = (pData->ulReadCapturedDataPos % pData->ulCapturedDataSize);
|
||||
|
||||
// Check for wrap-around condition
|
||||
if ((ulReadOffset + ulBytes) > pData->ulCapturedDataSize)
|
||||
{
|
||||
// Copy data from last Read position to end of data
|
||||
ulBytesToCopy = pData->ulCapturedDataSize - ulReadOffset;
|
||||
memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytesToCopy);
|
||||
|
||||
// Copy rest of the data from the start of the captured data
|
||||
memcpy(((char *)pBuffer) + ulBytesToCopy, pData->pCapturedSampleData, ulBytes - ulBytesToCopy);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy data from the read position in the captured data
|
||||
memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytes);
|
||||
}
|
||||
|
||||
// Update Read Position
|
||||
pData->ulReadCapturedDataPos += ulBytes;
|
||||
}
|
||||
|
||||
static ALCuint WinMMAvailableSamples(ALCdevice *pDevice)
|
||||
{
|
||||
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
||||
ALCuint lCapturedBytes = (pData->ulWriteCapturedDataPos - pData->ulReadCapturedDataPos);
|
||||
return lCapturedBytes / aluFrameSizeFromFormat(pDevice->Format);
|
||||
}
|
||||
|
||||
|
||||
BackendFuncs WinMMFuncs = {
|
||||
WinMMOpenPlayback,
|
||||
WinMMClosePlayback,
|
||||
NULL,
|
||||
NULL,
|
||||
WinMMOpenCapture,
|
||||
WinMMCloseCapture,
|
||||
WinMMStartCapture,
|
||||
WinMMStopCapture,
|
||||
WinMMCaptureSamples,
|
||||
WinMMAvailableSamples
|
||||
};
|
||||
|
||||
void alcWinMMInit(BackendFuncs *FuncList)
|
||||
{
|
||||
*FuncList = WinMMFuncs;
|
||||
}
|
||||
|
||||
void alcWinMMDeinit()
|
||||
{
|
||||
ALuint lLoop;
|
||||
|
||||
for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
|
||||
free(CaptureDeviceList[lLoop]);
|
||||
free(CaptureDeviceList);
|
||||
CaptureDeviceList = NULL;
|
||||
|
||||
NumCaptureDevices = 0;
|
||||
}
|
||||
|
||||
void alcWinMMProbe(int type)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
if(type != CAPTURE_DEVICE_PROBE)
|
||||
return;
|
||||
|
||||
ProbeDevices();
|
||||
for(i = 0;i < NumCaptureDevices;i++)
|
||||
{
|
||||
if(CaptureDeviceList[i])
|
||||
AppendCaptureDeviceList(CaptureDeviceList[i]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user