# HG changeset patch # User Robert McIntyre # Date 1321657364 25200 # Node ID 45dc4f76e5485bdfa339fb4559aea84990cfce02 # Parent 616215c81d233a1a71b47bdc9cfaf369ed1cfd36 adding send.c for cross compiling purposes diff -r 616215c81d23 -r 45dc4f76e548 .hgignore --- a/.hgignore Thu Nov 03 15:14:44 2011 -0700 +++ b/.hgignore Fri Nov 18 16:02:44 2011 -0700 @@ -1,5 +1,4 @@ syntax: glob -send.c build* html* java/.classpath diff -r 616215c81d23 -r 45dc4f76e548 Alc/backends/send.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Alc/backends/send.c Fri Nov 18 16:02:44 2011 -0700 @@ -0,0 +1,479 @@ + +#include "config.h" +#include +#include "alMain.h" +#include "AL/al.h" +#include "AL/alc.h" +#include "alSource.h" +#include + +//////////////////// 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 guaranteed 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(BytesFromDevFmt(Device->FmtType) * + Device->NumChan * Device->UpdateSize); + 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 ALCdevice struct to temporally + * 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 master 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_aurellem_send_AudioSend.h" + +/* + * Class: com_aurellem_send_AudioSend + * Method: nstep + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_nstep +(JNIEnv *env, jclass clazz, jlong device, jint samples){ + UNUSED(env);UNUSED(clazz);UNUSED(device); + renderData((ALCdevice*)((intptr_t)device), samples); +} +/* + * Class: com_aurellem_send_AudioSend + * Method: ngetSamples + * Signature: (JLjava/nio/ByteBuffer;III)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_send_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;} + memcpy(buffer_address, data->contexts[n]->renderBuffer, + BytesFromDevFmt(recorder->FmtType) * recorder->NumChan * samples); +} +/* + * Class: com_aurellem_send_AudioSend + * Method: naddListener + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_send_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_aurellem_send_AudioSend + * Method: nsetNthListener3f + * Signature: (IFFFJI)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_send_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_aurellem_send_AudioSend + * Method: nsetNthListenerf + * Signature: (IFJI)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_send_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_aurellem_send_AudioSend + * Method: ninitDevice + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_ninitDevice +(JNIEnv *env, jclass clazz, jlong device){ + UNUSED(env);UNUSED(clazz); + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); + init(Device); +} + +/* + * Class: com_aurellem_send_AudioSend + * Method: ngetAudioFormat + * Signature: (J)Ljavax/sound/sampled/AudioFormat; + */ +JNIEXPORT jobject JNICALL Java_com_aurellem_send_AudioSend_ngetAudioFormat +(JNIEnv *env, jclass clazz, jlong device){ + UNUSED(clazz); + jclass AudioFormatClass = + (*env)->FindClass(env, "javax/sound/sampled/AudioFormat"); + jmethodID AudioFormatConstructor = + (*env)->GetMethodID(env, AudioFormatClass, "", "(FIIZZ)V"); + + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); + int isSigned; + switch (Device->FmtType) + { + case DevFmtUByte: + case DevFmtUShort: isSigned = 0; break; + default : isSigned = 1; + } + float frequency = Device->Frequency; + int bitsPerFrame = (8 * BytesFromDevFmt(Device->FmtType)); + int channels = Device->NumChan; + jobject format = (*env)-> + NewObject( + env,AudioFormatClass,AudioFormatConstructor, + frequency, + bitsPerFrame, + channels, + isSigned, + 0); + return format; +} +//////////////////// Device Initialization / 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 immediately + 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) +{ + SetDefaultWFXChannelOrder(device); + 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; + } +}