Mercurial > audio-send
view Alc/backends/send.c @ 15:19ff95c69cf5
moved send.c to org file
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Thu, 03 Nov 2011 12:08:39 -0700 |
parents | 63312ec4a2bf |
children |
line wrap: on
line source
2 #include "config.h"3 #include <stdlib.h>4 #include "alMain.h"5 #include "AL/al.h"6 #include "AL/alc.h"7 #include "alSource.h"8 #include <jni.h>10 //////////////////// Summary12 struct send_data;13 struct context_data;15 static void addContext(ALCdevice *, ALCcontext *);16 static void syncContexts(ALCcontext *master, ALCcontext *slave);17 static void syncSources(ALsource *master, ALsource *slave,18 ALCcontext *masterCtx, ALCcontext *slaveCtx);20 static void syncSourcei(ALuint master, ALuint slave,21 ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param);22 static void syncSourcef(ALuint master, ALuint slave,23 ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param);24 static void syncSource3f(ALuint master, ALuint slave,25 ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param);27 static void swapInContext(ALCdevice *, struct context_data *);28 static void saveContext(ALCdevice *, struct context_data *);29 static void limitContext(ALCdevice *, ALCcontext *);30 static void unLimitContext(ALCdevice *);32 static void init(ALCdevice *);33 static void renderData(ALCdevice *, int samples);35 #define UNUSED(x) (void)(x)37 //////////////////// State39 typedef struct context_data {40 ALfloat ClickRemoval[MAXCHANNELS];41 ALfloat PendingClicks[MAXCHANNELS];42 ALvoid *renderBuffer;43 ALCcontext *ctx;44 } context_data;46 typedef struct send_data {47 ALuint size;48 context_data **contexts;49 ALuint numContexts;50 ALuint maxContexts;51 } send_data;55 //////////////////// Context Creation / Synchronization57 #define _MAKE_SYNC(NAME, INIT_EXPR, GET_EXPR, SET_EXPR) \58 void NAME (ALuint sourceID1, ALuint sourceID2, \59 ALCcontext *ctx1, ALCcontext *ctx2, \60 ALenum param){ \61 INIT_EXPR; \62 ALCcontext *current = alcGetCurrentContext(); \63 alcMakeContextCurrent(ctx1); \64 GET_EXPR; \65 alcMakeContextCurrent(ctx2); \66 SET_EXPR; \67 alcMakeContextCurrent(current); \68 }70 #define MAKE_SYNC(NAME, TYPE, GET, SET) \71 _MAKE_SYNC(NAME, \72 TYPE value, \73 GET(sourceID1, param, &value), \74 SET(sourceID2, param, value))76 #define MAKE_SYNC3(NAME, TYPE, GET, SET) \77 _MAKE_SYNC(NAME, \78 TYPE value1; TYPE value2; TYPE value3;, \79 GET(sourceID1, param, &value1, &value2, &value3), \80 SET(sourceID2, param, value1, value2, value3))82 MAKE_SYNC( syncSourcei, ALint, alGetSourcei, alSourcei);83 MAKE_SYNC( syncSourcef, ALfloat, alGetSourcef, alSourcef);84 MAKE_SYNC3(syncSource3i, ALint, alGetSource3i, alSource3i);85 MAKE_SYNC3(syncSource3f, ALfloat, alGetSource3f, alSource3f);87 void syncSources(ALsource *masterSource, ALsource *slaveSource,88 ALCcontext *masterCtx, ALCcontext *slaveCtx){89 ALuint master = masterSource->source;90 ALuint slave = slaveSource->source;91 ALCcontext *current = alcGetCurrentContext();93 syncSourcef(master,slave,masterCtx,slaveCtx,AL_PITCH);94 syncSourcef(master,slave,masterCtx,slaveCtx,AL_GAIN);95 syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_DISTANCE);96 syncSourcef(master,slave,masterCtx,slaveCtx,AL_ROLLOFF_FACTOR);97 syncSourcef(master,slave,masterCtx,slaveCtx,AL_REFERENCE_DISTANCE);98 syncSourcef(master,slave,masterCtx,slaveCtx,AL_MIN_GAIN);99 syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_GAIN);100 syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_GAIN);101 syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_INNER_ANGLE);102 syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_ANGLE);103 syncSourcef(master,slave,masterCtx,slaveCtx,AL_SEC_OFFSET);104 syncSourcef(master,slave,masterCtx,slaveCtx,AL_SAMPLE_OFFSET);105 syncSourcef(master,slave,masterCtx,slaveCtx,AL_BYTE_OFFSET);107 syncSource3f(master,slave,masterCtx,slaveCtx,AL_POSITION);108 syncSource3f(master,slave,masterCtx,slaveCtx,AL_VELOCITY);109 syncSource3f(master,slave,masterCtx,slaveCtx,AL_DIRECTION);111 syncSourcei(master,slave,masterCtx,slaveCtx,AL_SOURCE_RELATIVE);112 syncSourcei(master,slave,masterCtx,slaveCtx,AL_LOOPING);114 alcMakeContextCurrent(masterCtx);115 ALint source_type;116 alGetSourcei(master, AL_SOURCE_TYPE, &source_type);118 // Only static sources are currently synchronized!119 if (AL_STATIC == source_type){120 ALint master_buffer;121 ALint slave_buffer;122 alGetSourcei(master, AL_BUFFER, &master_buffer);123 alcMakeContextCurrent(slaveCtx);124 alGetSourcei(slave, AL_BUFFER, &slave_buffer);125 if (master_buffer != slave_buffer){126 alSourcei(slave, AL_BUFFER, master_buffer);127 }128 }130 // Synchronize the state of the two sources.131 alcMakeContextCurrent(masterCtx);132 ALint masterState;133 ALint slaveState;135 alGetSourcei(master, AL_SOURCE_STATE, &masterState);136 alcMakeContextCurrent(slaveCtx);137 alGetSourcei(slave, AL_SOURCE_STATE, &slaveState);139 if (masterState != slaveState){140 switch (masterState){141 case AL_INITIAL : alSourceRewind(slave); break;142 case AL_PLAYING : alSourcePlay(slave); break;143 case AL_PAUSED : alSourcePause(slave); break;144 case AL_STOPPED : alSourceStop(slave); break;145 }146 }147 // Restore whatever context was previously active.148 alcMakeContextCurrent(current);149 }152 void syncContexts(ALCcontext *master, ALCcontext *slave){153 /* If there aren't sufficient sources in slave to mirror154 the sources in master, create them. */155 ALCcontext *current = alcGetCurrentContext();157 UIntMap *masterSourceMap = &(master->SourceMap);158 UIntMap *slaveSourceMap = &(slave->SourceMap);159 ALuint numMasterSources = masterSourceMap->size;160 ALuint numSlaveSources = slaveSourceMap->size;162 alcMakeContextCurrent(slave);163 if (numSlaveSources < numMasterSources){164 ALuint numMissingSources = numMasterSources - numSlaveSources;165 ALuint newSources[numMissingSources];166 alGenSources(numMissingSources, newSources);167 }169 /* Now, slave is gauranteed to have at least as many sources170 as master. Sync each source from master to the corresponding171 source in slave. */172 int i;173 for(i = 0; i < masterSourceMap->size; i++){174 syncSources((ALsource*)masterSourceMap->array[i].value,175 (ALsource*)slaveSourceMap->array[i].value,176 master, slave);177 }178 alcMakeContextCurrent(current);179 }181 static void addContext(ALCdevice *Device, ALCcontext *context){182 send_data *data = (send_data*)Device->ExtraData;183 // expand array if necessary184 if (data->numContexts >= data->maxContexts){185 ALuint newMaxContexts = data->maxContexts*2 + 1;186 data->contexts = realloc(data->contexts, newMaxContexts*sizeof(context_data));187 data->maxContexts = newMaxContexts;188 }189 // create context_data and add it to the main array190 context_data *ctxData;191 ctxData = (context_data*)calloc(1, sizeof(*ctxData));192 ctxData->renderBuffer =193 malloc(BytesFromDevFmt(Device->FmtType) *194 Device->NumChan * Device->UpdateSize);195 ctxData->ctx = context;197 data->contexts[data->numContexts] = ctxData;198 data->numContexts++;199 }202 //////////////////// Context Switching204 /* A device brings along with it two pieces of state205 * which have to be swapped in and out with each context.206 */207 static void swapInContext(ALCdevice *Device, context_data *ctxData){208 memcpy(Device->ClickRemoval, ctxData->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS);209 memcpy(Device->PendingClicks, ctxData->PendingClicks, sizeof(ALfloat)*MAXCHANNELS);210 }212 static void saveContext(ALCdevice *Device, context_data *ctxData){213 memcpy(ctxData->ClickRemoval, Device->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS);214 memcpy(ctxData->PendingClicks, Device->PendingClicks, sizeof(ALfloat)*MAXCHANNELS);215 }217 static ALCcontext **currentContext;218 static ALuint currentNumContext;220 /* By default, all contexts are rendered at once for each call to aluMixData.221 * This function uses the internals of the ALCdecice struct to temporarly222 * cause aluMixData to only render the chosen context.223 */224 static void limitContext(ALCdevice *Device, ALCcontext *ctx){225 currentContext = Device->Contexts;226 currentNumContext = Device->NumContexts;227 Device->Contexts = &ctx;228 Device->NumContexts = 1;229 }231 static void unLimitContext(ALCdevice *Device){232 Device->Contexts = currentContext;233 Device->NumContexts = currentNumContext;234 }237 //////////////////// Main Device Loop239 /* Establish the LWJGL context as the main context, which will240 * be synchronized to all the slave contexts241 */242 static void init(ALCdevice *Device){243 ALCcontext *masterContext = alcGetCurrentContext();244 addContext(Device, masterContext);245 }248 static void renderData(ALCdevice *Device, int samples){249 if(!Device->Connected){return;}250 send_data *data = (send_data*)Device->ExtraData;251 ALCcontext *current = alcGetCurrentContext();253 ALuint i;254 for (i = 1; i < data->numContexts; i++){255 syncContexts(data->contexts[0]->ctx , data->contexts[i]->ctx);256 }258 if ((uint) samples > Device->UpdateSize){259 printf("exceeding internal buffer size; dropping samples\n");260 printf("requested %d; available %d\n", samples, Device->UpdateSize);261 samples = (int) Device->UpdateSize;262 }264 for (i = 0; i < data->numContexts; i++){265 context_data *ctxData = data->contexts[i];266 ALCcontext *ctx = ctxData->ctx;267 alcMakeContextCurrent(ctx);268 limitContext(Device, ctx);269 swapInContext(Device, ctxData);270 aluMixData(Device, ctxData->renderBuffer, samples);271 saveContext(Device, ctxData);272 unLimitContext(Device);273 }274 alcMakeContextCurrent(current);275 }278 //////////////////// JNI Methods280 #include "com_aurellem_send_AudioSend.h"282 /*283 * Class: com_aurellem_send_AudioSend284 * Method: nstep285 * Signature: (JI)V286 */287 JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_nstep288 (JNIEnv *env, jclass clazz, jlong device, jint samples){289 UNUSED(env);UNUSED(clazz);UNUSED(device);290 renderData((ALCdevice*)((intptr_t)device), samples);291 }293 /*294 * Class: com_aurellem_send_AudioSend295 * Method: ngetSamples296 * Signature: (JLjava/nio/ByteBuffer;III)V297 */298 JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_ngetSamples299 (JNIEnv *env, jclass clazz, jlong device, jobject buffer, jint position,300 jint samples, jint n){301 UNUSED(clazz);303 ALvoid *buffer_address =304 ((ALbyte *)(((char*)(*env)->GetDirectBufferAddress(env, buffer)) + position));305 ALCdevice *recorder = (ALCdevice*) ((intptr_t)device);306 send_data *data = (send_data*)recorder->ExtraData;307 if ((ALuint)n > data->numContexts){return;}309 //printf("Want %d samples for listener %d\n", samples, n);310 //printf("Device's format type is %d bytes per sample,\n",311 // BytesFromDevFmt(recorder->FmtType));312 //printf("and it has %d channels, making for %d requested bytes\n",313 // recorder->NumChan,314 // BytesFromDevFmt(recorder->FmtType) * recorder->NumChan * samples);316 memcpy(buffer_address, data->contexts[n]->renderBuffer,317 BytesFromDevFmt(recorder->FmtType) * recorder->NumChan * samples);318 //samples*sizeof(ALfloat));319 }321 /*322 * Class: com_aurellem_send_AudioSend323 * Method: naddListener324 * Signature: (J)V325 */326 JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_naddListener327 (JNIEnv *env, jclass clazz, jlong device){328 UNUSED(env); UNUSED(clazz);329 //printf("creating new context via naddListener\n");330 ALCdevice *Device = (ALCdevice*) ((intptr_t)device);331 ALCcontext *new = alcCreateContext(Device, NULL);332 addContext(Device, new);333 }335 /*336 * Class: com_aurellem_send_AudioSend337 * Method: nsetNthListener3f338 * Signature: (IFFFJI)V339 */340 JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_nsetNthListener3f341 (JNIEnv *env, jclass clazz, jint param,342 jfloat v1, jfloat v2, jfloat v3, jlong device, jint contextNum){343 UNUSED(env);UNUSED(clazz);345 ALCdevice *Device = (ALCdevice*) ((intptr_t)device);346 send_data *data = (send_data*)Device->ExtraData;348 ALCcontext *current = alcGetCurrentContext();349 if ((ALuint)contextNum > data->numContexts){return;}350 alcMakeContextCurrent(data->contexts[contextNum]->ctx);351 alListener3f(param, v1, v2, v3);352 alcMakeContextCurrent(current);353 }355 /*356 * Class: com_aurellem_send_AudioSend357 * Method: nsetNthListenerf358 * Signature: (IFJI)V359 */360 JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_nsetNthListenerf361 (JNIEnv *env, jclass clazz, jint param, jfloat v1, jlong device,362 jint contextNum){364 UNUSED(env);UNUSED(clazz);366 ALCdevice *Device = (ALCdevice*) ((intptr_t)device);367 send_data *data = (send_data*)Device->ExtraData;369 ALCcontext *current = alcGetCurrentContext();370 if ((ALuint)contextNum > data->numContexts){return;}371 alcMakeContextCurrent(data->contexts[contextNum]->ctx);372 alListenerf(param, v1);373 alcMakeContextCurrent(current);374 }376 /*377 * Class: com_aurellem_send_AudioSend378 * Method: ninitDevice379 * Signature: (J)V380 */381 JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_ninitDevice382 (JNIEnv *env, jclass clazz, jlong device){383 UNUSED(env);UNUSED(clazz);385 ALCdevice *Device = (ALCdevice*) ((intptr_t)device);386 init(Device);388 }391 /*392 * Class: com_aurellem_send_AudioSend393 * Method: ngetAudioFormat394 * Signature: (J)Ljavax/sound/sampled/AudioFormat;395 */396 JNIEXPORT jobject JNICALL Java_com_aurellem_send_AudioSend_ngetAudioFormat397 (JNIEnv *env, jclass clazz, jlong device){398 UNUSED(clazz);399 jclass AudioFormatClass =400 (*env)->FindClass(env, "javax/sound/sampled/AudioFormat");401 jmethodID AudioFormatConstructor =402 (*env)->GetMethodID(env, AudioFormatClass, "<init>", "(FIIZZ)V");404 ALCdevice *Device = (ALCdevice*) ((intptr_t)device);406 //float frequency408 int isSigned;409 switch (Device->FmtType)410 {411 case DevFmtUByte:412 case DevFmtUShort: isSigned = 0; break;413 default : isSigned = 1;414 }415 float frequency = Device->Frequency;416 int bitsPerFrame = (8 * BytesFromDevFmt(Device->FmtType));417 int channels = Device->NumChan;420 //printf("freq = %f, bpf = %d, channels = %d, signed? = %d\n",421 // frequency, bitsPerFrame, channels, isSigned);423 jobject format = (*env)->424 NewObject(425 env,AudioFormatClass,AudioFormatConstructor,426 frequency,427 bitsPerFrame,428 channels,429 isSigned,430 0);431 return format;432 }436 //////////////////// Device Initilization / Management438 static const ALCchar sendDevice[] = "Multiple Audio Send";440 static ALCboolean send_open_playback(ALCdevice *device,441 const ALCchar *deviceName)442 {443 send_data *data;444 // stop any buffering for stdout, so that I can445 // see the printf statements in my terminal immediatley446 setbuf(stdout, NULL);448 if(!deviceName)449 deviceName = sendDevice;450 else if(strcmp(deviceName, sendDevice) != 0)451 return ALC_FALSE;452 data = (send_data*)calloc(1, sizeof(*data));453 device->szDeviceName = strdup(deviceName);454 device->ExtraData = data;455 return ALC_TRUE;456 }458 static void send_close_playback(ALCdevice *device)459 {460 send_data *data = (send_data*)device->ExtraData;461 alcMakeContextCurrent(NULL);462 ALuint i;463 // Destroy all slave contexts. LWJGL will take care of464 // its own context.465 for (i = 1; i < data->numContexts; i++){466 context_data *ctxData = data->contexts[i];467 alcDestroyContext(ctxData->ctx);468 free(ctxData->renderBuffer);469 free(ctxData);470 }471 free(data);472 device->ExtraData = NULL;473 }475 static ALCboolean send_reset_playback(ALCdevice *device)476 {477 SetDefaultWFXChannelOrder(device);478 return ALC_TRUE;479 }481 static void send_stop_playback(ALCdevice *Device){482 UNUSED(Device);483 }485 static const BackendFuncs send_funcs = {486 send_open_playback,487 send_close_playback,488 send_reset_playback,489 send_stop_playback,490 NULL,491 NULL, /* These would be filled with functions to */492 NULL, /* handle capturing audio if we we into that */493 NULL, /* sort of thing... */494 NULL,495 NULL496 };498 ALCboolean alc_send_init(BackendFuncs *func_list){499 *func_list = send_funcs;500 return ALC_TRUE;501 }503 void alc_send_deinit(void){}505 void alc_send_probe(enum DevProbe type)506 {507 switch(type)508 {509 case DEVICE_PROBE:510 AppendDeviceList(sendDevice);511 break;512 case ALL_DEVICE_PROBE:513 AppendAllDeviceList(sendDevice);514 break;515 case CAPTURE_DEVICE_PROBE:516 break;517 }518 }