Mercurial > audio-send
diff Alc/backends/send.c @ 0:f9476ff7637e
initial forking of open-al to create multiple listeners
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 25 Oct 2011 13:02:31 -0700 |
parents | |
children | 37f25cb34196 |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/Alc/backends/send.c Tue Oct 25 13:02:31 2011 -0700 1.3 @@ -0,0 +1,472 @@ 1.4 +#include "config.h" 1.5 +#include <stdlib.h> 1.6 +#include "alMain.h" 1.7 +#include "AL/al.h" 1.8 +#include "AL/alc.h" 1.9 +#include "alSource.h" 1.10 +#include <jni.h> 1.11 + 1.12 +//////////////////// Summary 1.13 + 1.14 +struct send_data; 1.15 +struct context_data; 1.16 + 1.17 +static void addContext(ALCdevice *, ALCcontext *); 1.18 +static void syncContexts(ALCcontext *master, ALCcontext *slave); 1.19 +static void syncSources(ALsource *master, ALsource *slave, 1.20 + ALCcontext *masterCtx, ALCcontext *slaveCtx); 1.21 + 1.22 +static void syncSourcei(ALuint master, ALuint slave, 1.23 + ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param); 1.24 +static void syncSourcef(ALuint master, ALuint slave, 1.25 + ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param); 1.26 +static void syncSource3f(ALuint master, ALuint slave, 1.27 + ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param); 1.28 + 1.29 +static void swapInContext(ALCdevice *, struct context_data *); 1.30 +static void saveContext(ALCdevice *, struct context_data *); 1.31 +static void limitContext(ALCdevice *, ALCcontext *); 1.32 +static void unLimitContext(ALCdevice *); 1.33 + 1.34 +static void init(ALCdevice *); 1.35 +static void renderData(ALCdevice *, int samples); 1.36 + 1.37 +#define UNUSED(x) (void)(x) 1.38 + 1.39 +//////////////////// State 1.40 + 1.41 +typedef struct context_data { 1.42 + ALfloat ClickRemoval[MAXCHANNELS]; 1.43 + ALfloat PendingClicks[MAXCHANNELS]; 1.44 + ALvoid *renderBuffer; 1.45 + ALCcontext *ctx; 1.46 +} context_data; 1.47 + 1.48 +typedef struct send_data { 1.49 + ALuint size; 1.50 + context_data **contexts; 1.51 + ALuint numContexts; 1.52 + ALuint maxContexts; 1.53 +} send_data; 1.54 + 1.55 + 1.56 + 1.57 +//////////////////// Context Creation / Synchronization 1.58 + 1.59 +#define _MAKE_SYNC(NAME, INIT_EXPR, GET_EXPR, SET_EXPR) \ 1.60 + void NAME (ALuint sourceID1, ALuint sourceID2, \ 1.61 + ALCcontext *ctx1, ALCcontext *ctx2, \ 1.62 + ALenum param){ \ 1.63 + INIT_EXPR; \ 1.64 + ALCcontext *current = alcGetCurrentContext(); \ 1.65 + alcMakeContextCurrent(ctx1); \ 1.66 + GET_EXPR; \ 1.67 + alcMakeContextCurrent(ctx2); \ 1.68 + SET_EXPR; \ 1.69 + alcMakeContextCurrent(current); \ 1.70 + } 1.71 + 1.72 +#define MAKE_SYNC(NAME, TYPE, GET, SET) \ 1.73 + _MAKE_SYNC(NAME, \ 1.74 + TYPE value, \ 1.75 + GET(sourceID1, param, &value), \ 1.76 + SET(sourceID2, param, value)) 1.77 + 1.78 +#define MAKE_SYNC3(NAME, TYPE, GET, SET) \ 1.79 + _MAKE_SYNC(NAME, \ 1.80 + TYPE value1; TYPE value2; TYPE value3;, \ 1.81 + GET(sourceID1, param, &value1, &value2, &value3), \ 1.82 + SET(sourceID2, param, value1, value2, value3)) 1.83 + 1.84 +MAKE_SYNC( syncSourcei, ALint, alGetSourcei, alSourcei); 1.85 +MAKE_SYNC( syncSourcef, ALfloat, alGetSourcef, alSourcef); 1.86 +MAKE_SYNC3(syncSource3i, ALint, alGetSource3i, alSource3i); 1.87 +MAKE_SYNC3(syncSource3f, ALfloat, alGetSource3f, alSource3f); 1.88 + 1.89 +void syncSources(ALsource *masterSource, ALsource *slaveSource, 1.90 + ALCcontext *masterCtx, ALCcontext *slaveCtx){ 1.91 + ALuint master = masterSource->source; 1.92 + ALuint slave = slaveSource->source; 1.93 + ALCcontext *current = alcGetCurrentContext(); 1.94 + 1.95 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_PITCH); 1.96 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_GAIN); 1.97 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_DISTANCE); 1.98 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_ROLLOFF_FACTOR); 1.99 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_REFERENCE_DISTANCE); 1.100 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_MIN_GAIN); 1.101 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_GAIN); 1.102 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_GAIN); 1.103 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_INNER_ANGLE); 1.104 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_ANGLE); 1.105 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_SEC_OFFSET); 1.106 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_SAMPLE_OFFSET); 1.107 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_BYTE_OFFSET); 1.108 + 1.109 + syncSource3f(master,slave,masterCtx,slaveCtx,AL_POSITION); 1.110 + syncSource3f(master,slave,masterCtx,slaveCtx,AL_VELOCITY); 1.111 + syncSource3f(master,slave,masterCtx,slaveCtx,AL_DIRECTION); 1.112 + 1.113 + syncSourcei(master,slave,masterCtx,slaveCtx,AL_SOURCE_RELATIVE); 1.114 + syncSourcei(master,slave,masterCtx,slaveCtx,AL_LOOPING); 1.115 + 1.116 + alcMakeContextCurrent(masterCtx); 1.117 + ALint source_type; 1.118 + alGetSourcei(master, AL_SOURCE_TYPE, &source_type); 1.119 + 1.120 + // Only static sources are currently synchronized! 1.121 + if (AL_STATIC == source_type){ 1.122 + ALint master_buffer; 1.123 + ALint slave_buffer; 1.124 + alGetSourcei(master, AL_BUFFER, &master_buffer); 1.125 + alcMakeContextCurrent(slaveCtx); 1.126 + alGetSourcei(slave, AL_BUFFER, &slave_buffer); 1.127 + if (master_buffer != slave_buffer){ 1.128 + alSourcei(slave, AL_BUFFER, master_buffer); 1.129 + } 1.130 + } 1.131 + 1.132 + // Synchronize the state of the two sources. 1.133 + alcMakeContextCurrent(masterCtx); 1.134 + ALint masterState; 1.135 + ALint slaveState; 1.136 + 1.137 + alGetSourcei(master, AL_SOURCE_STATE, &masterState); 1.138 + alcMakeContextCurrent(slaveCtx); 1.139 + alGetSourcei(slave, AL_SOURCE_STATE, &slaveState); 1.140 + 1.141 + if (masterState != slaveState){ 1.142 + switch (masterState){ 1.143 + case AL_INITIAL : alSourceRewind(slave); break; 1.144 + case AL_PLAYING : alSourcePlay(slave); break; 1.145 + case AL_PAUSED : alSourcePause(slave); break; 1.146 + case AL_STOPPED : alSourceStop(slave); break; 1.147 + } 1.148 + } 1.149 + // Restore whatever context was previously active. 1.150 + alcMakeContextCurrent(current); 1.151 +} 1.152 + 1.153 + 1.154 +void syncContexts(ALCcontext *master, ALCcontext *slave){ 1.155 + /* If there aren't sufficient sources in slave to mirror 1.156 + the sources in master, create them. */ 1.157 + ALCcontext *current = alcGetCurrentContext(); 1.158 + 1.159 + UIntMap *masterSourceMap = &(master->SourceMap); 1.160 + UIntMap *slaveSourceMap = &(slave->SourceMap); 1.161 + ALuint numMasterSources = masterSourceMap->size; 1.162 + ALuint numSlaveSources = slaveSourceMap->size; 1.163 + 1.164 + alcMakeContextCurrent(slave); 1.165 + if (numSlaveSources < numMasterSources){ 1.166 + ALuint numMissingSources = numMasterSources - numSlaveSources; 1.167 + ALuint newSources[numMissingSources]; 1.168 + alGenSources(numMissingSources, newSources); 1.169 + } 1.170 + 1.171 + /* Now, slave is gauranteed to have at least as many sources 1.172 + as master. Sync each source from master to the corresponding 1.173 + source in slave. */ 1.174 + int i; 1.175 + for(i = 0; i < masterSourceMap->size; i++){ 1.176 + syncSources((ALsource*)masterSourceMap->array[i].value, 1.177 + (ALsource*)slaveSourceMap->array[i].value, 1.178 + master, slave); 1.179 + } 1.180 + alcMakeContextCurrent(current); 1.181 +} 1.182 + 1.183 +static void addContext(ALCdevice *Device, ALCcontext *context){ 1.184 + send_data *data = (send_data*)Device->ExtraData; 1.185 + // expand array if necessary 1.186 + if (data->numContexts >= data->maxContexts){ 1.187 + ALuint newMaxContexts = data->maxContexts*2 + 1; 1.188 + data->contexts = realloc(data->contexts, newMaxContexts*sizeof(context_data)); 1.189 + data->maxContexts = newMaxContexts; 1.190 + } 1.191 + // create context_data and add it to the main array 1.192 + context_data *ctxData; 1.193 + ctxData = (context_data*)calloc(1, sizeof(*ctxData)); 1.194 + ctxData->renderBuffer = malloc(data->size); 1.195 + ctxData->ctx = context; 1.196 + 1.197 + data->contexts[data->numContexts] = ctxData; 1.198 + data->numContexts++; 1.199 +} 1.200 + 1.201 + 1.202 +//////////////////// Context Switching 1.203 + 1.204 +/* A device brings along with it two pieces of state 1.205 + * which have to be swapped in and out with each context. 1.206 + */ 1.207 +static void swapInContext(ALCdevice *Device, context_data *ctxData){ 1.208 + memcpy(Device->ClickRemoval, ctxData->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS); 1.209 + memcpy(Device->PendingClicks, ctxData->PendingClicks, sizeof(ALfloat)*MAXCHANNELS); 1.210 +} 1.211 + 1.212 +static void saveContext(ALCdevice *Device, context_data *ctxData){ 1.213 + memcpy(ctxData->ClickRemoval, Device->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS); 1.214 + memcpy(ctxData->PendingClicks, Device->PendingClicks, sizeof(ALfloat)*MAXCHANNELS); 1.215 +} 1.216 + 1.217 +static ALCcontext **currentContext; 1.218 +static ALuint currentNumContext; 1.219 + 1.220 +/* By default, all contexts are rendered at once for each call to aluMixData. 1.221 + * This function uses the internals of the ALCdecice struct to temporarly 1.222 + * cause aluMixData to only render the chosen context. 1.223 + */ 1.224 +static void limitContext(ALCdevice *Device, ALCcontext *ctx){ 1.225 + currentContext = Device->Contexts; 1.226 + currentNumContext = Device->NumContexts; 1.227 + Device->Contexts = &ctx; 1.228 + Device->NumContexts = 1; 1.229 +} 1.230 + 1.231 +static void unLimitContext(ALCdevice *Device){ 1.232 + Device->Contexts = currentContext; 1.233 + Device->NumContexts = currentNumContext; 1.234 +} 1.235 + 1.236 + 1.237 +//////////////////// Main Device Loop 1.238 + 1.239 +/* Establish the LWJGL context as the main context, which will 1.240 + * be synchronized to all the slave contexts 1.241 + */ 1.242 +static void init(ALCdevice *Device){ 1.243 + ALCcontext *masterContext = alcGetCurrentContext(); 1.244 + addContext(Device, masterContext); 1.245 +} 1.246 + 1.247 + 1.248 +static void renderData(ALCdevice *Device, int samples){ 1.249 + if(!Device->Connected){return;} 1.250 + send_data *data = (send_data*)Device->ExtraData; 1.251 + ALCcontext *current = alcGetCurrentContext(); 1.252 + 1.253 + ALuint i; 1.254 + for (i = 1; i < data->numContexts; i++){ 1.255 + syncContexts(data->contexts[0]->ctx , data->contexts[i]->ctx); 1.256 + } 1.257 + 1.258 + if ((uint) samples > Device->UpdateSize){ 1.259 + printf("exceeding internal buffer size; dropping samples\n"); 1.260 + printf("requested %d; available %d\n", samples, Device->UpdateSize); 1.261 + samples = (int) Device->UpdateSize; 1.262 + } 1.263 + 1.264 + for (i = 0; i < data->numContexts; i++){ 1.265 + context_data *ctxData = data->contexts[i]; 1.266 + ALCcontext *ctx = ctxData->ctx; 1.267 + alcMakeContextCurrent(ctx); 1.268 + limitContext(Device, ctx); 1.269 + swapInContext(Device, ctxData); 1.270 + aluMixData(Device, ctxData->renderBuffer, samples); 1.271 + saveContext(Device, ctxData); 1.272 + unLimitContext(Device); 1.273 + } 1.274 + alcMakeContextCurrent(current); 1.275 +} 1.276 + 1.277 + 1.278 +//////////////////// JNI Methods 1.279 + 1.280 +#include "com_aurellem_capture_AudioSend.h" 1.281 + 1.282 +/* 1.283 + * Class: com_aurellem_capture_AudioSend 1.284 + * Method: nstep 1.285 + * Signature: (JI)V 1.286 + */ 1.287 +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_nstep 1.288 +(JNIEnv *env, jclass clazz, jlong device, jint samples){ 1.289 + UNUSED(env);UNUSED(clazz);UNUSED(device); 1.290 + renderData((ALCdevice*)((intptr_t)device), samples); 1.291 +} 1.292 + 1.293 +/* 1.294 + * Class: com_aurellem_capture_AudioSend 1.295 + * Method: ngetSamples 1.296 + * Signature: (JLjava/nio/ByteBuffer;III)V 1.297 + */ 1.298 +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_ngetSamples 1.299 +(JNIEnv *env, jclass clazz, jlong device, jobject buffer, jint position, 1.300 + jint samples, jint n){ 1.301 + UNUSED(clazz); 1.302 + 1.303 + ALvoid *buffer_address = 1.304 + ((ALbyte *)(((char*)(*env)->GetDirectBufferAddress(env, buffer)) + position)); 1.305 + ALCdevice *recorder = (ALCdevice*) ((intptr_t)device); 1.306 + send_data *data = (send_data*)recorder->ExtraData; 1.307 + if ((ALuint)n > data->numContexts){return;} 1.308 + if ((uint) samples > data->size){ 1.309 + samples = (int) data->size; 1.310 + } 1.311 + memcpy(buffer_address, data->contexts[n]->renderBuffer, samples*sizeof(ALfloat)); 1.312 +} 1.313 + 1.314 +/* 1.315 + * Class: com_aurellem_capture_AudioSend 1.316 + * Method: naddListener 1.317 + * Signature: (J)V 1.318 + */ 1.319 +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_naddListener 1.320 +(JNIEnv *env, jclass clazz, jlong device){ 1.321 + UNUSED(env); UNUSED(clazz); 1.322 + printf("creating new context via naddListener\n"); 1.323 + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); 1.324 + ALCcontext *new = alcCreateContext(Device, NULL); 1.325 + addContext(Device, new); 1.326 +} 1.327 + 1.328 +/* 1.329 + * Class: com_aurellem_capture_AudioSend 1.330 + * Method: nsetNthListener3f 1.331 + * Signature: (IFFFJI)V 1.332 + */ 1.333 +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_nsetNthListener3f 1.334 + (JNIEnv *env, jclass clazz, jint param, 1.335 + jfloat v1, jfloat v2, jfloat v3, jlong device, jint contextNum){ 1.336 + UNUSED(env);UNUSED(clazz); 1.337 + 1.338 + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); 1.339 + send_data *data = (send_data*)Device->ExtraData; 1.340 + 1.341 + ALCcontext *current = alcGetCurrentContext(); 1.342 + if ((ALuint)contextNum > data->numContexts){return;} 1.343 + alcMakeContextCurrent(data->contexts[contextNum]->ctx); 1.344 + alListener3f(param, v1, v2, v3); 1.345 + alcMakeContextCurrent(current); 1.346 +} 1.347 + 1.348 +/* 1.349 + * Class: com_aurellem_capture_AudioSend 1.350 + * Method: nsetNthListenerf 1.351 + * Signature: (IFJI)V 1.352 + */ 1.353 +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_nsetNthListenerf 1.354 +(JNIEnv *env, jclass clazz, jint param, jfloat v1, jlong device, 1.355 + jint contextNum){ 1.356 + 1.357 + UNUSED(env);UNUSED(clazz); 1.358 + 1.359 + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); 1.360 + send_data *data = (send_data*)Device->ExtraData; 1.361 + 1.362 + ALCcontext *current = alcGetCurrentContext(); 1.363 + if ((ALuint)contextNum > data->numContexts){return;} 1.364 + alcMakeContextCurrent(data->contexts[contextNum]->ctx); 1.365 + alListenerf(param, v1); 1.366 + alcMakeContextCurrent(current); 1.367 +} 1.368 + 1.369 +/* 1.370 + * Class: com_aurellem_capture_AudioSend 1.371 + * Method: ninitDevice 1.372 + * Signature: (J)V 1.373 + */ 1.374 +JNIEXPORT void JNICALL Java_com_aurellem_capture_AudioSend_ninitDevice 1.375 +(JNIEnv *env, jclass clazz, jlong device){ 1.376 + UNUSED(env);UNUSED(clazz); 1.377 + 1.378 + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); 1.379 + init(Device); 1.380 + 1.381 +} 1.382 + 1.383 + 1.384 +//////////////////// Device Initilization / Management 1.385 + 1.386 +static const ALCchar sendDevice[] = "Multiple Audio Send"; 1.387 + 1.388 +static ALCboolean send_open_playback(ALCdevice *device, 1.389 + const ALCchar *deviceName) 1.390 +{ 1.391 + send_data *data; 1.392 + // stop any buffering for stdout, so that I can 1.393 + // see the printf statements in my terminal immediatley 1.394 + setbuf(stdout, NULL); 1.395 + 1.396 + if(!deviceName) 1.397 + deviceName = sendDevice; 1.398 + else if(strcmp(deviceName, sendDevice) != 0) 1.399 + return ALC_FALSE; 1.400 + data = (send_data*)calloc(1, sizeof(*data)); 1.401 + device->szDeviceName = strdup(deviceName); 1.402 + device->ExtraData = data; 1.403 + return ALC_TRUE; 1.404 +} 1.405 + 1.406 +static void send_close_playback(ALCdevice *device) 1.407 +{ 1.408 + send_data *data = (send_data*)device->ExtraData; 1.409 + alcMakeContextCurrent(NULL); 1.410 + ALuint i; 1.411 + // Destroy all slave contexts. LWJGL will take care of 1.412 + // its own context. 1.413 + for (i = 1; i < data->numContexts; i++){ 1.414 + context_data *ctxData = data->contexts[i]; 1.415 + alcDestroyContext(ctxData->ctx); 1.416 + free(ctxData->renderBuffer); 1.417 + free(ctxData); 1.418 + } 1.419 + free(data); 1.420 + device->ExtraData = NULL; 1.421 +} 1.422 + 1.423 +static ALCboolean send_reset_playback(ALCdevice *device) 1.424 +{ 1.425 + send_data *data = (send_data*)device->ExtraData; 1.426 + ALuint channels=0, bits=0; 1.427 + device->FmtType = DevFmtShort; 1.428 + bits = BytesFromDevFmt(device->FmtType) * 8; 1.429 + channels = ChannelsFromDevFmt(device->FmtChans); 1.430 + data->size = device->UpdateSize * channels * bits / 8; 1.431 + 1.432 + return ALC_TRUE; 1.433 +} 1.434 + 1.435 +static void send_stop_playback(ALCdevice *Device){ 1.436 + UNUSED(Device); 1.437 +} 1.438 + 1.439 +static const BackendFuncs send_funcs = { 1.440 + send_open_playback, 1.441 + send_close_playback, 1.442 + send_reset_playback, 1.443 + send_stop_playback, 1.444 + NULL, 1.445 + NULL, /* These would be filled with functions to */ 1.446 + NULL, /* handle capturing audio if we we into that */ 1.447 + NULL, /* sort of thing... */ 1.448 + NULL, 1.449 + NULL 1.450 +}; 1.451 + 1.452 +ALCboolean alc_send_init(BackendFuncs *func_list){ 1.453 + *func_list = send_funcs; 1.454 + return ALC_TRUE; 1.455 +} 1.456 + 1.457 +void alc_send_deinit(void){} 1.458 + 1.459 +void alc_send_probe(enum DevProbe type) 1.460 +{ 1.461 + switch(type) 1.462 + { 1.463 + case DEVICE_PROBE: 1.464 + AppendDeviceList(sendDevice); 1.465 + break; 1.466 + case ALL_DEVICE_PROBE: 1.467 + AppendAllDeviceList(sendDevice); 1.468 + break; 1.469 + case CAPTURE_DEVICE_PROBE: 1.470 + break; 1.471 + } 1.472 +} 1.473 + 1.474 + 1.475 +