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