send.c
Written by
Robert McIntyre
#include "config.h"
#include <stdlib.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
#include "alSource.h"
#include <jni.h>
//////////////////// Summary
struct send_data;
struct context_data;
static void addContext(ALCdevice *, ALCcontext *);
static void syncContexts(ALCcontext *master, ALCcontext *slave);
static void syncSources(ALsource *master, ALsource *slave,
ALCcontext *masterCtx, ALCcontext *slaveCtx);
static void syncSourcei(ALuint master, ALuint slave,
ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param);
static void syncSourcef(ALuint master, ALuint slave,
ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param);
static void syncSource3f(ALuint master, ALuint slave,
ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param);
static void swapInContext(ALCdevice *, struct context_data *);
static void saveContext(ALCdevice *, struct context_data *);
static void limitContext(ALCdevice *, ALCcontext *);
static void unLimitContext(ALCdevice *);
static void init(ALCdevice *);
static void renderData(ALCdevice *, int samples);
#define UNUSED(x) (void)(x)
//////////////////// State
typedef struct context_data {
ALfloat ClickRemoval[MAXCHANNELS];
ALfloat PendingClicks[MAXCHANNELS];
ALvoid *renderBuffer;
ALCcontext *ctx;
} context_data;
typedef struct send_data {
ALuint size;
context_data **contexts;
ALuint numContexts;
ALuint maxContexts;
} send_data;
//////////////////// Context Creation / Synchronization
#define _MAKE_SYNC(NAME, INIT_EXPR, GET_EXPR, SET_EXPR) \
void NAME (ALuint sourceID1, ALuint sourceID2, \
ALCcontext *ctx1, ALCcontext *ctx2, \
ALenum param){ \
INIT_EXPR; \
ALCcontext *current = alcGetCurrentContext(); \
alcMakeContextCurrent(ctx1); \
GET_EXPR; \
alcMakeContextCurrent(ctx2); \
SET_EXPR; \
alcMakeContextCurrent(current); \
}
#define MAKE_SYNC(NAME, TYPE, GET, SET) \
_MAKE_SYNC(NAME, \
TYPE value, \
GET(sourceID1, param, &value), \
SET(sourceID2, param, value))
#define MAKE_SYNC3(NAME, TYPE, GET, SET) \
_MAKE_SYNC(NAME, \
TYPE value1; TYPE value2; TYPE value3;, \
GET(sourceID1, param, &value1, &value2, &value3), \
SET(sourceID2, param, value1, value2, value3))
MAKE_SYNC( syncSourcei, ALint, alGetSourcei, alSourcei);
MAKE_SYNC( syncSourcef, ALfloat, alGetSourcef, alSourcef);
MAKE_SYNC3(syncSource3i, ALint, alGetSource3i, alSource3i);
MAKE_SYNC3(syncSource3f, ALfloat, alGetSource3f, alSource3f);
void syncSources(ALsource *masterSource, ALsource *slaveSource,
ALCcontext *masterCtx, ALCcontext *slaveCtx){
ALuint master = masterSource->source;
ALuint slave = slaveSource->source;
ALCcontext *current = alcGetCurrentContext();
syncSourcef(master,slave,masterCtx,slaveCtx,AL_PITCH);
syncSourcef(master,slave,masterCtx,slaveCtx,AL_GAIN);
syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_DISTANCE);
syncSourcef(master,slave,masterCtx,slaveCtx,AL_ROLLOFF_FACTOR);
syncSourcef(master,slave,masterCtx,slaveCtx,AL_REFERENCE_DISTANCE);
syncSourcef(master,slave,masterCtx,slaveCtx,AL_MIN_GAIN);
syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_GAIN);
syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_GAIN);
syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_INNER_ANGLE);
syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_ANGLE);
syncSourcef(master,slave,masterCtx,slaveCtx,AL_SEC_OFFSET);
syncSourcef(master,slave,masterCtx,slaveCtx,AL_SAMPLE_OFFSET);
syncSourcef(master,slave,masterCtx,slaveCtx,AL_BYTE_OFFSET);
syncSource3f(master,slave,masterCtx,slaveCtx,AL_POSITION);
syncSource3f(master,slave,masterCtx,slaveCtx,AL_VELOCITY);
syncSource3f(master,slave,masterCtx,slaveCtx,AL_DIRECTION);
syncSourcei(master,slave,masterCtx,slaveCtx,AL_SOURCE_RELATIVE);
syncSourcei(master,slave,masterCtx,slaveCtx,AL_LOOPING);
alcMakeContextCurrent(masterCtx);
ALint source_type;
alGetSourcei(master, AL_SOURCE_TYPE, &source_type);
// Only static sources are currently synchronized!
if (AL_STATIC == source_type){
ALint master_buffer;
ALint slave_buffer;
alGetSourcei(master, AL_BUFFER, &master_buffer);
alcMakeContextCurrent(slaveCtx);
alGetSourcei(slave, AL_BUFFER, &slave_buffer);
if (master_buffer != slave_buffer){
alSourcei(slave, AL_BUFFER, master_buffer);
}
}
// Synchronize the state of the two sources.
alcMakeContextCurrent(masterCtx);
ALint masterState;
ALint slaveState;
alGetSourcei(master, AL_SOURCE_STATE, &masterState);
alcMakeContextCurrent(slaveCtx);
alGetSourcei(slave, AL_SOURCE_STATE, &slaveState);
if (masterState != slaveState){
switch (masterState){
case AL_INITIAL : alSourceRewind(slave); break;
case AL_PLAYING : alSourcePlay(slave); break;
case AL_PAUSED : alSourcePause(slave); break;
case AL_STOPPED : alSourceStop(slave); break;
}
}
// Restore whatever context was previously active.
alcMakeContextCurrent(current);
}
void syncContexts(ALCcontext *master, ALCcontext *slave){
/* If there aren't sufficient sources in slave to mirror
the sources in master, create them. */
ALCcontext *current = alcGetCurrentContext();
UIntMap *masterSourceMap = &(master->SourceMap);
UIntMap *slaveSourceMap = &(slave->SourceMap);
ALuint numMasterSources = masterSourceMap->size;
ALuint numSlaveSources = slaveSourceMap->size;
alcMakeContextCurrent(slave);
if (numSlaveSources < numMasterSources){
ALuint numMissingSources = numMasterSources - numSlaveSources;
ALuint newSources[numMissingSources];
alGenSources(numMissingSources, newSources);
}
/* Now, slave is gauranteed to have at least as many sources
as master. Sync each source from master to the corresponding
source in slave. */
int i;
for(i = 0; i < masterSourceMap->size; i++){
syncSources((ALsource*)masterSourceMap->array[i].value,
(ALsource*)slaveSourceMap->array[i].value,
master, slave);
}
alcMakeContextCurrent(current);
}
static void addContext(ALCdevice *Device, ALCcontext *context){
send_data *data = (send_data*)Device->ExtraData;
// expand array if necessary
if (data->numContexts >= data->maxContexts){
ALuint newMaxContexts = data->maxContexts*2 + 1;
data->contexts = realloc(data->contexts, newMaxContexts*sizeof(context_data));
data->maxContexts = newMaxContexts;
}
// create context_data and add it to the main array
context_data *ctxData;
ctxData = (context_data*)calloc(1, sizeof(*ctxData));
ctxData->renderBuffer = malloc(data->size);
ctxData->ctx = context;
data->contexts[data->numContexts] = ctxData;
data->numContexts++;
}
//////////////////// Context Switching
/* A device brings along with it two pieces of state
* which have to be swapped in and out with each context.
*/
static void swapInContext(ALCdevice *Device, context_data *ctxData){
memcpy(Device->ClickRemoval, ctxData->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS);
memcpy(Device->PendingClicks, ctxData->PendingClicks, sizeof(ALfloat)*MAXCHANNELS);
}
static void saveContext(ALCdevice *Device, context_data *ctxData){
memcpy(ctxData->ClickRemoval, Device->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS);
memcpy(ctxData->PendingClicks, Device->PendingClicks, sizeof(ALfloat)*MAXCHANNELS);
}
static ALCcontext **currentContext;
static ALuint currentNumContext;
/* By default, all contexts are rendered at once for each call to aluMixData.
* This function uses the internals of the ALCdecice struct to temporarly
* cause aluMixData to only render the chosen context.
*/
static void limitContext(ALCdevice *Device, ALCcontext *ctx){
currentContext = Device->Contexts;
currentNumContext = Device->NumContexts;
Device->Contexts = &ctx;
Device->NumContexts = 1;
}
static void unLimitContext(ALCdevice *Device){
Device->Contexts = currentContext;
Device->NumContexts = currentNumContext;
}
//////////////////// Main Device Loop
/* Establish the LWJGL context as the main context, which will
* be synchronized to all the slave contexts
*/
static void init(ALCdevice *Device){
ALCcontext *masterContext = alcGetCurrentContext();
addContext(Device, masterContext);
}
static void renderData(ALCdevice *Device, int samples){
if(!Device->Connected){return;}
send_data *data = (send_data*)Device->ExtraData;
ALCcontext *current = alcGetCurrentContext();
ALuint i;
for (i = 1; i < data->numContexts; i++){
syncContexts(data->contexts[0]->ctx , data->contexts[i]->ctx);
}
if ((uint) samples > Device->UpdateSize){
printf("exceeding internal buffer size; dropping samples\n");
printf("requested %d; available %d\n", samples, Device->UpdateSize);
samples = (int) Device->UpdateSize;
}
for (i = 0; i < data->numContexts; i++){
context_data *ctxData = data->contexts[i];
ALCcontext *ctx = ctxData->ctx;
alcMakeContextCurrent(ctx);
limitContext(Device, ctx);
swapInContext(Device, ctxData);
aluMixData(Device, ctxData->renderBuffer, samples);
saveContext(Device, ctxData);
unLimitContext(Device);
}
alcMakeContextCurrent(current);
}
//////////////////// JNI Methods
#include "com_jme3_capture_AudioSend.h"
/*
* Class: com_jme3_capture_AudioSend
* Method: nstep
* Signature: (JI)V
*/
JNIEXPORT void JNICALL Java_com_jme3_capture_AudioSend_nstep
(JNIEnv *env, jclass clazz, jlong device, jint samples){
UNUSED(env);UNUSED(clazz);UNUSED(device);
renderData((ALCdevice*)((intptr_t)device), samples);
}
/*
* Class: com_jme3_capture_AudioSend
* Method: ngetSamples
* Signature: (JLjava/nio/ByteBuffer;III)V
*/
JNIEXPORT void JNICALL Java_com_jme3_capture_AudioSend_ngetSamples
(JNIEnv *env, jclass clazz, jlong device, jobject buffer, jint position,
jint samples, jint n){
UNUSED(clazz);
ALvoid *buffer_address =
((ALbyte *)(((char*)(*env)->GetDirectBufferAddress(env, buffer)) + position));
ALCdevice *recorder = (ALCdevice*) ((intptr_t)device);
send_data *data = (send_data*)recorder->ExtraData;
if ((ALuint)n > data->numContexts){return;}
if ((uint) samples > data->size){
samples = (int) data->size;
}
memcpy(buffer_address, data->contexts[n]->renderBuffer, samples*sizeof(ALfloat));
}
/*
* Class: com_jme3_capture_AudioSend
* Method: naddListener
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_jme3_capture_AudioSend_naddListener
(JNIEnv *env, jclass clazz, jlong device){
UNUSED(env); UNUSED(clazz);
printf("creating new context via naddListener\n");
ALCdevice *Device = (ALCdevice*) ((intptr_t)device);
ALCcontext *new = alcCreateContext(Device, NULL);
addContext(Device, new);
}
/*
* Class: com_jme3_capture_AudioSend
* Method: nsetNthListener3f
* Signature: (IFFFJI)V
*/
JNIEXPORT void JNICALL Java_com_jme3_capture_AudioSend_nsetNthListener3f
(JNIEnv *env, jclass clazz, jint param,
jfloat v1, jfloat v2, jfloat v3, jlong device, jint contextNum){
UNUSED(env);UNUSED(clazz);
ALCdevice *Device = (ALCdevice*) ((intptr_t)device);
send_data *data = (send_data*)Device->ExtraData;
ALCcontext *current = alcGetCurrentContext();
if ((ALuint)contextNum > data->numContexts){return;}
alcMakeContextCurrent(data->contexts[contextNum]->ctx);
alListener3f(param, v1, v2, v3);
alcMakeContextCurrent(current);
}
/*
* Class: com_jme3_capture_AudioSend
* Method: nsetNthListenerf
* Signature: (IFJI)V
*/
JNIEXPORT void JNICALL Java_com_jme3_capture_AudioSend_nsetNthListenerf
(JNIEnv *env, jclass clazz, jint param, jfloat v1, jlong device,
jint contextNum){
UNUSED(env);UNUSED(clazz);
ALCdevice *Device = (ALCdevice*) ((intptr_t)device);
send_data *data = (send_data*)Device->ExtraData;
ALCcontext *current = alcGetCurrentContext();
if ((ALuint)contextNum > data->numContexts){return;}
alcMakeContextCurrent(data->contexts[contextNum]->ctx);
alListenerf(param, v1);
alcMakeContextCurrent(current);
}
/*
* Class: com_jme3_capture_AudioSend
* Method: ninitDevice
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_jme3_capture_AudioSend_ninitDevice
(JNIEnv *env, jclass clazz, jlong device){
UNUSED(env);UNUSED(clazz);
ALCdevice *Device = (ALCdevice*) ((intptr_t)device);
init(Device);
}
//////////////////// Device Initilization / Management
static const ALCchar sendDevice[] = "Multiple Audio Send";
static ALCboolean send_open_playback(ALCdevice *device,
const ALCchar *deviceName)
{
send_data *data;
// stop any buffering for stdout, so that I can
// see the printf statements in my terminal immediatley
setbuf(stdout, NULL);
if(!deviceName)
deviceName = sendDevice;
else if(strcmp(deviceName, sendDevice) != 0)
return ALC_FALSE;
data = (send_data*)calloc(1, sizeof(*data));
device->szDeviceName = strdup(deviceName);
device->ExtraData = data;
return ALC_TRUE;
}
static void send_close_playback(ALCdevice *device)
{
send_data *data = (send_data*)device->ExtraData;
alcMakeContextCurrent(NULL);
ALuint i;
// Destroy all slave contexts. LWJGL will take care of
// its own context.
for (i = 1; i < data->numContexts; i++){
context_data *ctxData = data->contexts[i];
alcDestroyContext(ctxData->ctx);
free(ctxData->renderBuffer);
free(ctxData);
}
free(data);
device->ExtraData = NULL;
}
static ALCboolean send_reset_playback(ALCdevice *device)
{
send_data *data = (send_data*)device->ExtraData;
ALuint channels=0, bits=0;
device->FmtType = DevFmtShort;
bits = BytesFromDevFmt(device->FmtType) * 8;
channels = ChannelsFromDevFmt(device->FmtChans);
data->size = device->UpdateSize * channels * bits / 8;
return ALC_TRUE;
}
static void send_stop_playback(ALCdevice *Device){
UNUSED(Device);
}
static const BackendFuncs send_funcs = {
send_open_playback,
send_close_playback,
send_reset_playback,
send_stop_playback,
NULL,
NULL, /* These would be filled with functions to */
NULL, /* handle capturing audio if we we into that */
NULL, /* sort of thing... */
NULL,
NULL
};
ALCboolean alc_send_init(BackendFuncs *func_list){
*func_list = send_funcs;
return ALC_TRUE;
}
void alc_send_deinit(void){}
void alc_send_probe(enum DevProbe type)
{
switch(type)
{
case DEVICE_PROBE:
AppendDeviceList(sendDevice);
break;
case ALL_DEVICE_PROBE:
AppendAllDeviceList(sendDevice);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
Date: 2011-10-20 15:48:47 MST
Author: Robert McIntyre
Org version 7.6 with Emacs version 23
Validate XHTML 1.0