Mercurial > audio-send
changeset 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 | 616215c81d23 |
children | d51f1a2b4c30 |
files | .hgignore Alc/backends/send.c |
diffstat | 2 files changed, 479 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
1.1 --- a/.hgignore Thu Nov 03 15:14:44 2011 -0700 1.2 +++ b/.hgignore Fri Nov 18 16:02:44 2011 -0700 1.3 @@ -1,5 +1,4 @@ 1.4 syntax: glob 1.5 -send.c 1.6 build* 1.7 html* 1.8 java/.classpath
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/Alc/backends/send.c Fri Nov 18 16:02:44 2011 -0700 2.3 @@ -0,0 +1,479 @@ 2.4 + 2.5 +#include "config.h" 2.6 +#include <stdlib.h> 2.7 +#include "alMain.h" 2.8 +#include "AL/al.h" 2.9 +#include "AL/alc.h" 2.10 +#include "alSource.h" 2.11 +#include <jni.h> 2.12 + 2.13 +//////////////////// Summary 2.14 + 2.15 +struct send_data; 2.16 +struct context_data; 2.17 + 2.18 +static void addContext(ALCdevice *, ALCcontext *); 2.19 +static void syncContexts(ALCcontext *master, ALCcontext *slave); 2.20 +static void syncSources(ALsource *master, ALsource *slave, 2.21 + ALCcontext *masterCtx, ALCcontext *slaveCtx); 2.22 + 2.23 +static void syncSourcei(ALuint master, ALuint slave, 2.24 + ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param); 2.25 +static void syncSourcef(ALuint master, ALuint slave, 2.26 + ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param); 2.27 +static void syncSource3f(ALuint master, ALuint slave, 2.28 + ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param); 2.29 + 2.30 +static void swapInContext(ALCdevice *, struct context_data *); 2.31 +static void saveContext(ALCdevice *, struct context_data *); 2.32 +static void limitContext(ALCdevice *, ALCcontext *); 2.33 +static void unLimitContext(ALCdevice *); 2.34 + 2.35 +static void init(ALCdevice *); 2.36 +static void renderData(ALCdevice *, int samples); 2.37 + 2.38 +#define UNUSED(x) (void)(x) 2.39 +//////////////////// State 2.40 + 2.41 +typedef struct context_data { 2.42 + ALfloat ClickRemoval[MAXCHANNELS]; 2.43 + ALfloat PendingClicks[MAXCHANNELS]; 2.44 + ALvoid *renderBuffer; 2.45 + ALCcontext *ctx; 2.46 +} context_data; 2.47 + 2.48 +typedef struct send_data { 2.49 + ALuint size; 2.50 + context_data **contexts; 2.51 + ALuint numContexts; 2.52 + ALuint maxContexts; 2.53 +} send_data; 2.54 +//////////////////// Context Creation / Synchronization 2.55 + 2.56 +#define _MAKE_SYNC(NAME, INIT_EXPR, GET_EXPR, SET_EXPR) \ 2.57 + void NAME (ALuint sourceID1, ALuint sourceID2, \ 2.58 + ALCcontext *ctx1, ALCcontext *ctx2, \ 2.59 + ALenum param){ \ 2.60 + INIT_EXPR; \ 2.61 + ALCcontext *current = alcGetCurrentContext(); \ 2.62 + alcMakeContextCurrent(ctx1); \ 2.63 + GET_EXPR; \ 2.64 + alcMakeContextCurrent(ctx2); \ 2.65 + SET_EXPR; \ 2.66 + alcMakeContextCurrent(current); \ 2.67 + } 2.68 + 2.69 +#define MAKE_SYNC(NAME, TYPE, GET, SET) \ 2.70 + _MAKE_SYNC(NAME, \ 2.71 + TYPE value, \ 2.72 + GET(sourceID1, param, &value), \ 2.73 + SET(sourceID2, param, value)) 2.74 + 2.75 +#define MAKE_SYNC3(NAME, TYPE, GET, SET) \ 2.76 + _MAKE_SYNC(NAME, \ 2.77 + TYPE value1; TYPE value2; TYPE value3;, \ 2.78 + GET(sourceID1, param, &value1, &value2, &value3), \ 2.79 + SET(sourceID2, param, value1, value2, value3)) 2.80 + 2.81 +MAKE_SYNC( syncSourcei, ALint, alGetSourcei, alSourcei); 2.82 +MAKE_SYNC( syncSourcef, ALfloat, alGetSourcef, alSourcef); 2.83 +MAKE_SYNC3(syncSource3i, ALint, alGetSource3i, alSource3i); 2.84 +MAKE_SYNC3(syncSource3f, ALfloat, alGetSource3f, alSource3f); 2.85 + 2.86 +void syncSources(ALsource *masterSource, ALsource *slaveSource, 2.87 + ALCcontext *masterCtx, ALCcontext *slaveCtx){ 2.88 + ALuint master = masterSource->source; 2.89 + ALuint slave = slaveSource->source; 2.90 + ALCcontext *current = alcGetCurrentContext(); 2.91 + 2.92 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_PITCH); 2.93 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_GAIN); 2.94 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_DISTANCE); 2.95 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_ROLLOFF_FACTOR); 2.96 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_REFERENCE_DISTANCE); 2.97 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_MIN_GAIN); 2.98 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_GAIN); 2.99 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_GAIN); 2.100 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_INNER_ANGLE); 2.101 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_ANGLE); 2.102 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_SEC_OFFSET); 2.103 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_SAMPLE_OFFSET); 2.104 + syncSourcef(master,slave,masterCtx,slaveCtx,AL_BYTE_OFFSET); 2.105 + 2.106 + syncSource3f(master,slave,masterCtx,slaveCtx,AL_POSITION); 2.107 + syncSource3f(master,slave,masterCtx,slaveCtx,AL_VELOCITY); 2.108 + syncSource3f(master,slave,masterCtx,slaveCtx,AL_DIRECTION); 2.109 + 2.110 + syncSourcei(master,slave,masterCtx,slaveCtx,AL_SOURCE_RELATIVE); 2.111 + syncSourcei(master,slave,masterCtx,slaveCtx,AL_LOOPING); 2.112 + 2.113 + alcMakeContextCurrent(masterCtx); 2.114 + ALint source_type; 2.115 + alGetSourcei(master, AL_SOURCE_TYPE, &source_type); 2.116 + 2.117 + // Only static sources are currently synchronized! 2.118 + if (AL_STATIC == source_type){ 2.119 + ALint master_buffer; 2.120 + ALint slave_buffer; 2.121 + alGetSourcei(master, AL_BUFFER, &master_buffer); 2.122 + alcMakeContextCurrent(slaveCtx); 2.123 + alGetSourcei(slave, AL_BUFFER, &slave_buffer); 2.124 + if (master_buffer != slave_buffer){ 2.125 + alSourcei(slave, AL_BUFFER, master_buffer); 2.126 + } 2.127 + } 2.128 + 2.129 + // Synchronize the state of the two sources. 2.130 + alcMakeContextCurrent(masterCtx); 2.131 + ALint masterState; 2.132 + ALint slaveState; 2.133 + 2.134 + alGetSourcei(master, AL_SOURCE_STATE, &masterState); 2.135 + alcMakeContextCurrent(slaveCtx); 2.136 + alGetSourcei(slave, AL_SOURCE_STATE, &slaveState); 2.137 + 2.138 + if (masterState != slaveState){ 2.139 + switch (masterState){ 2.140 + case AL_INITIAL : alSourceRewind(slave); break; 2.141 + case AL_PLAYING : alSourcePlay(slave); break; 2.142 + case AL_PAUSED : alSourcePause(slave); break; 2.143 + case AL_STOPPED : alSourceStop(slave); break; 2.144 + } 2.145 + } 2.146 + // Restore whatever context was previously active. 2.147 + alcMakeContextCurrent(current); 2.148 +} 2.149 +void syncContexts(ALCcontext *master, ALCcontext *slave){ 2.150 + /* If there aren't sufficient sources in slave to mirror 2.151 + the sources in master, create them. */ 2.152 + ALCcontext *current = alcGetCurrentContext(); 2.153 + 2.154 + UIntMap *masterSourceMap = &(master->SourceMap); 2.155 + UIntMap *slaveSourceMap = &(slave->SourceMap); 2.156 + ALuint numMasterSources = masterSourceMap->size; 2.157 + ALuint numSlaveSources = slaveSourceMap->size; 2.158 + 2.159 + alcMakeContextCurrent(slave); 2.160 + if (numSlaveSources < numMasterSources){ 2.161 + ALuint numMissingSources = numMasterSources - numSlaveSources; 2.162 + ALuint newSources[numMissingSources]; 2.163 + alGenSources(numMissingSources, newSources); 2.164 + } 2.165 + 2.166 + /* Now, slave is guaranteed to have at least as many sources 2.167 + as master. Sync each source from master to the corresponding 2.168 + source in slave. */ 2.169 + int i; 2.170 + for(i = 0; i < masterSourceMap->size; i++){ 2.171 + syncSources((ALsource*)masterSourceMap->array[i].value, 2.172 + (ALsource*)slaveSourceMap->array[i].value, 2.173 + master, slave); 2.174 + } 2.175 + alcMakeContextCurrent(current); 2.176 +} 2.177 +static void addContext(ALCdevice *Device, ALCcontext *context){ 2.178 + send_data *data = (send_data*)Device->ExtraData; 2.179 + // expand array if necessary 2.180 + if (data->numContexts >= data->maxContexts){ 2.181 + ALuint newMaxContexts = data->maxContexts*2 + 1; 2.182 + data->contexts = realloc(data->contexts, newMaxContexts*sizeof(context_data)); 2.183 + data->maxContexts = newMaxContexts; 2.184 + } 2.185 + // create context_data and add it to the main array 2.186 + context_data *ctxData; 2.187 + ctxData = (context_data*)calloc(1, sizeof(*ctxData)); 2.188 + ctxData->renderBuffer = 2.189 + malloc(BytesFromDevFmt(Device->FmtType) * 2.190 + Device->NumChan * Device->UpdateSize); 2.191 + ctxData->ctx = context; 2.192 + 2.193 + data->contexts[data->numContexts] = ctxData; 2.194 + data->numContexts++; 2.195 +} 2.196 +//////////////////// Context Switching 2.197 + 2.198 +/* A device brings along with it two pieces of state 2.199 + * which have to be swapped in and out with each context. 2.200 + */ 2.201 +static void swapInContext(ALCdevice *Device, context_data *ctxData){ 2.202 + memcpy(Device->ClickRemoval, ctxData->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS); 2.203 + memcpy(Device->PendingClicks, ctxData->PendingClicks, sizeof(ALfloat)*MAXCHANNELS); 2.204 +} 2.205 + 2.206 +static void saveContext(ALCdevice *Device, context_data *ctxData){ 2.207 + memcpy(ctxData->ClickRemoval, Device->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS); 2.208 + memcpy(ctxData->PendingClicks, Device->PendingClicks, sizeof(ALfloat)*MAXCHANNELS); 2.209 +} 2.210 + 2.211 +static ALCcontext **currentContext; 2.212 +static ALuint currentNumContext; 2.213 + 2.214 +/* By default, all contexts are rendered at once for each call to aluMixData. 2.215 + * This function uses the internals of the ALCdevice struct to temporally 2.216 + * cause aluMixData to only render the chosen context. 2.217 + */ 2.218 +static void limitContext(ALCdevice *Device, ALCcontext *ctx){ 2.219 + currentContext = Device->Contexts; 2.220 + currentNumContext = Device->NumContexts; 2.221 + Device->Contexts = &ctx; 2.222 + Device->NumContexts = 1; 2.223 +} 2.224 + 2.225 +static void unLimitContext(ALCdevice *Device){ 2.226 + Device->Contexts = currentContext; 2.227 + Device->NumContexts = currentNumContext; 2.228 +} 2.229 +//////////////////// Main Device Loop 2.230 + 2.231 +/* Establish the LWJGL context as the master context, which will 2.232 + * be synchronized to all the slave contexts 2.233 + */ 2.234 +static void init(ALCdevice *Device){ 2.235 + ALCcontext *masterContext = alcGetCurrentContext(); 2.236 + addContext(Device, masterContext); 2.237 +} 2.238 + 2.239 + 2.240 +static void renderData(ALCdevice *Device, int samples){ 2.241 + if(!Device->Connected){return;} 2.242 + send_data *data = (send_data*)Device->ExtraData; 2.243 + ALCcontext *current = alcGetCurrentContext(); 2.244 + 2.245 + ALuint i; 2.246 + for (i = 1; i < data->numContexts; i++){ 2.247 + syncContexts(data->contexts[0]->ctx , data->contexts[i]->ctx); 2.248 + } 2.249 + 2.250 + if ((uint) samples > Device->UpdateSize){ 2.251 + printf("exceeding internal buffer size; dropping samples\n"); 2.252 + printf("requested %d; available %d\n", samples, Device->UpdateSize); 2.253 + samples = (int) Device->UpdateSize; 2.254 + } 2.255 + 2.256 + for (i = 0; i < data->numContexts; i++){ 2.257 + context_data *ctxData = data->contexts[i]; 2.258 + ALCcontext *ctx = ctxData->ctx; 2.259 + alcMakeContextCurrent(ctx); 2.260 + limitContext(Device, ctx); 2.261 + swapInContext(Device, ctxData); 2.262 + aluMixData(Device, ctxData->renderBuffer, samples); 2.263 + saveContext(Device, ctxData); 2.264 + unLimitContext(Device); 2.265 + } 2.266 + alcMakeContextCurrent(current); 2.267 +} 2.268 +//////////////////// JNI Methods 2.269 + 2.270 +#include "com_aurellem_send_AudioSend.h" 2.271 + 2.272 +/* 2.273 + * Class: com_aurellem_send_AudioSend 2.274 + * Method: nstep 2.275 + * Signature: (JI)V 2.276 + */ 2.277 +JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_nstep 2.278 +(JNIEnv *env, jclass clazz, jlong device, jint samples){ 2.279 + UNUSED(env);UNUSED(clazz);UNUSED(device); 2.280 + renderData((ALCdevice*)((intptr_t)device), samples); 2.281 +} 2.282 +/* 2.283 + * Class: com_aurellem_send_AudioSend 2.284 + * Method: ngetSamples 2.285 + * Signature: (JLjava/nio/ByteBuffer;III)V 2.286 + */ 2.287 +JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_ngetSamples 2.288 +(JNIEnv *env, jclass clazz, jlong device, jobject buffer, jint position, 2.289 + jint samples, jint n){ 2.290 + UNUSED(clazz); 2.291 + 2.292 + ALvoid *buffer_address = 2.293 + ((ALbyte *)(((char*)(*env)->GetDirectBufferAddress(env, buffer)) + position)); 2.294 + ALCdevice *recorder = (ALCdevice*) ((intptr_t)device); 2.295 + send_data *data = (send_data*)recorder->ExtraData; 2.296 + if ((ALuint)n > data->numContexts){return;} 2.297 + memcpy(buffer_address, data->contexts[n]->renderBuffer, 2.298 + BytesFromDevFmt(recorder->FmtType) * recorder->NumChan * samples); 2.299 +} 2.300 +/* 2.301 + * Class: com_aurellem_send_AudioSend 2.302 + * Method: naddListener 2.303 + * Signature: (J)V 2.304 + */ 2.305 +JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_naddListener 2.306 +(JNIEnv *env, jclass clazz, jlong device){ 2.307 + UNUSED(env); UNUSED(clazz); 2.308 + //printf("creating new context via naddListener\n"); 2.309 + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); 2.310 + ALCcontext *new = alcCreateContext(Device, NULL); 2.311 + addContext(Device, new); 2.312 +} 2.313 + 2.314 +/* 2.315 + * Class: com_aurellem_send_AudioSend 2.316 + * Method: nsetNthListener3f 2.317 + * Signature: (IFFFJI)V 2.318 + */ 2.319 +JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_nsetNthListener3f 2.320 + (JNIEnv *env, jclass clazz, jint param, 2.321 + jfloat v1, jfloat v2, jfloat v3, jlong device, jint contextNum){ 2.322 + UNUSED(env);UNUSED(clazz); 2.323 + 2.324 + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); 2.325 + send_data *data = (send_data*)Device->ExtraData; 2.326 + 2.327 + ALCcontext *current = alcGetCurrentContext(); 2.328 + if ((ALuint)contextNum > data->numContexts){return;} 2.329 + alcMakeContextCurrent(data->contexts[contextNum]->ctx); 2.330 + alListener3f(param, v1, v2, v3); 2.331 + alcMakeContextCurrent(current); 2.332 +} 2.333 + 2.334 +/* 2.335 + * Class: com_aurellem_send_AudioSend 2.336 + * Method: nsetNthListenerf 2.337 + * Signature: (IFJI)V 2.338 + */ 2.339 +JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_nsetNthListenerf 2.340 +(JNIEnv *env, jclass clazz, jint param, jfloat v1, jlong device, 2.341 + jint contextNum){ 2.342 + 2.343 + UNUSED(env);UNUSED(clazz); 2.344 + 2.345 + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); 2.346 + send_data *data = (send_data*)Device->ExtraData; 2.347 + 2.348 + ALCcontext *current = alcGetCurrentContext(); 2.349 + if ((ALuint)contextNum > data->numContexts){return;} 2.350 + alcMakeContextCurrent(data->contexts[contextNum]->ctx); 2.351 + alListenerf(param, v1); 2.352 + alcMakeContextCurrent(current); 2.353 +} 2.354 +/* 2.355 + * Class: com_aurellem_send_AudioSend 2.356 + * Method: ninitDevice 2.357 + * Signature: (J)V 2.358 + */ 2.359 +JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_ninitDevice 2.360 +(JNIEnv *env, jclass clazz, jlong device){ 2.361 + UNUSED(env);UNUSED(clazz); 2.362 + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); 2.363 + init(Device); 2.364 +} 2.365 + 2.366 +/* 2.367 + * Class: com_aurellem_send_AudioSend 2.368 + * Method: ngetAudioFormat 2.369 + * Signature: (J)Ljavax/sound/sampled/AudioFormat; 2.370 + */ 2.371 +JNIEXPORT jobject JNICALL Java_com_aurellem_send_AudioSend_ngetAudioFormat 2.372 +(JNIEnv *env, jclass clazz, jlong device){ 2.373 + UNUSED(clazz); 2.374 + jclass AudioFormatClass = 2.375 + (*env)->FindClass(env, "javax/sound/sampled/AudioFormat"); 2.376 + jmethodID AudioFormatConstructor = 2.377 + (*env)->GetMethodID(env, AudioFormatClass, "<init>", "(FIIZZ)V"); 2.378 + 2.379 + ALCdevice *Device = (ALCdevice*) ((intptr_t)device); 2.380 + int isSigned; 2.381 + switch (Device->FmtType) 2.382 + { 2.383 + case DevFmtUByte: 2.384 + case DevFmtUShort: isSigned = 0; break; 2.385 + default : isSigned = 1; 2.386 + } 2.387 + float frequency = Device->Frequency; 2.388 + int bitsPerFrame = (8 * BytesFromDevFmt(Device->FmtType)); 2.389 + int channels = Device->NumChan; 2.390 + jobject format = (*env)-> 2.391 + NewObject( 2.392 + env,AudioFormatClass,AudioFormatConstructor, 2.393 + frequency, 2.394 + bitsPerFrame, 2.395 + channels, 2.396 + isSigned, 2.397 + 0); 2.398 + return format; 2.399 +} 2.400 +//////////////////// Device Initialization / Management 2.401 + 2.402 +static const ALCchar sendDevice[] = "Multiple Audio Send"; 2.403 + 2.404 +static ALCboolean send_open_playback(ALCdevice *device, 2.405 + const ALCchar *deviceName) 2.406 +{ 2.407 + send_data *data; 2.408 + // stop any buffering for stdout, so that I can 2.409 + // see the printf statements in my terminal immediately 2.410 + setbuf(stdout, NULL); 2.411 + 2.412 + if(!deviceName) 2.413 + deviceName = sendDevice; 2.414 + else if(strcmp(deviceName, sendDevice) != 0) 2.415 + return ALC_FALSE; 2.416 + data = (send_data*)calloc(1, sizeof(*data)); 2.417 + device->szDeviceName = strdup(deviceName); 2.418 + device->ExtraData = data; 2.419 + return ALC_TRUE; 2.420 +} 2.421 + 2.422 +static void send_close_playback(ALCdevice *device) 2.423 +{ 2.424 + send_data *data = (send_data*)device->ExtraData; 2.425 + alcMakeContextCurrent(NULL); 2.426 + ALuint i; 2.427 + // Destroy all slave contexts. LWJGL will take care of 2.428 + // its own context. 2.429 + for (i = 1; i < data->numContexts; i++){ 2.430 + context_data *ctxData = data->contexts[i]; 2.431 + alcDestroyContext(ctxData->ctx); 2.432 + free(ctxData->renderBuffer); 2.433 + free(ctxData); 2.434 + } 2.435 + free(data); 2.436 + device->ExtraData = NULL; 2.437 +} 2.438 + 2.439 +static ALCboolean send_reset_playback(ALCdevice *device) 2.440 +{ 2.441 + SetDefaultWFXChannelOrder(device); 2.442 + return ALC_TRUE; 2.443 +} 2.444 + 2.445 +static void send_stop_playback(ALCdevice *Device){ 2.446 + UNUSED(Device); 2.447 +} 2.448 + 2.449 +static const BackendFuncs send_funcs = { 2.450 + send_open_playback, 2.451 + send_close_playback, 2.452 + send_reset_playback, 2.453 + send_stop_playback, 2.454 + NULL, 2.455 + NULL, /* These would be filled with functions to */ 2.456 + NULL, /* handle capturing audio if we we into that */ 2.457 + NULL, /* sort of thing... */ 2.458 + NULL, 2.459 + NULL 2.460 +}; 2.461 + 2.462 +ALCboolean alc_send_init(BackendFuncs *func_list){ 2.463 + *func_list = send_funcs; 2.464 + return ALC_TRUE; 2.465 +} 2.466 + 2.467 +void alc_send_deinit(void){} 2.468 + 2.469 +void alc_send_probe(enum DevProbe type) 2.470 +{ 2.471 + switch(type) 2.472 + { 2.473 + case DEVICE_PROBE: 2.474 + AppendDeviceList(sendDevice); 2.475 + break; 2.476 + case ALL_DEVICE_PROBE: 2.477 + AppendAllDeviceList(sendDevice); 2.478 + break; 2.479 + case CAPTURE_DEVICE_PROBE: 2.480 + break; 2.481 + } 2.482 +}