annotate org/ear.org @ 15:19ff95c69cf5

moved send.c to org file
author Robert McIntyre <rlm@mit.edu>
date Thu, 03 Nov 2011 12:08:39 -0700
parents c41d773a85fb
children 1e201037f666
rev   line source
rlm@15 1 #+title: Simulated Sense of Hearing
rlm@0 2 #+author: Robert McIntyre
rlm@0 3 #+email: rlm@mit.edu
rlm@15 4 #+description: Simulating multiple listeners and the sense of hearing in jMonkeyEngine3
rlm@15 5 #+keywords: simulated hearing, openal, clojure, jMonkeyEngine3, LWJGL, AI
rlm@15 6 #+SETUPFILE: ../../aurellem/org/setup.org
rlm@15 7 #+INCLUDE: ../../aurellem/org/level-0.org
rlm@0 8 #+BABEL: :exports both :noweb yes :cache no :mkdirp yes
rlm@0 9
rlm@0 10
rlm@0 11
rlm@0 12
rlm@15 13 * Hearing
rlm@0 14
rlm@0 15 I want to be able to place ears in a similiar manner to how I place
rlm@0 16 the eyes. I want to be able to place ears in a unique spatial
rlm@0 17 position, and recieve as output at every tick the FFT of whatever
rlm@0 18 signals are happening at that point.
rlm@0 19
rlm@15 20 Hearing is one of the more difficult senses to simulate, because there
rlm@15 21 is less support for obtaining the actual sound data that is processed
rlm@15 22 by jMonkeyEngine3.
rlm@15 23
rlm@15 24 jMonkeyEngine's sound system works as follows:
rlm@15 25
rlm@15 26 - jMonkeyEngine uese the =AppSettings= for the particular application
rlm@15 27 to determine what sort of =AudioRenderer= should be used.
rlm@15 28 - although some support is provided for multiple AudioRendering
rlm@15 29 backends, jMonkeyEngine at the time of this writing will either
rlm@15 30 pick no AudioRender at all, or the =LwjglAudioRenderer=
rlm@15 31 - jMonkeyEngine tries to figure out what sort of system you're
rlm@15 32 running and extracts the appropiate native libraries.
rlm@15 33 - the =LwjglAudioRenderer= uses the [[http://lwjgl.org/][=LWJGL=]] (lightweight java game
rlm@15 34 library) bindings to interface with a C library called [[http://kcat.strangesoft.net/openal.html][=OpenAL=]]
rlm@15 35 - =OpenAL= calculates the 3D sound localization and feeds a stream of
rlm@15 36 sound to any of various sound output devices with which it knows
rlm@15 37 how to communicate.
rlm@15 38
rlm@15 39 A consequence of this is that there's no way to access the actual
rlm@15 40 sound data produced by =OpenAL=. Even worse, =OpanAL= only supports
rlm@15 41 one /listener/, which normally isn't a problem for games, but becomes
rlm@15 42 a problem when trying to make multiple AI creatures that can each hear
rlm@15 43 the world from a different perspective.
rlm@15 44
rlm@15 45 To make many AI creatures in jMonkeyEngine that can each hear the
rlm@15 46 world from their own perspective, it is necessary to go all the way
rlm@15 47 back to =OpenAL= and implement support for simulated hearing there.
rlm@15 48
rlm@15 49 ** =OpenAL= Devices
rlm@15 50
rlm@15 51 =OpenAL= goes to great lengths to support many different systems, all
rlm@15 52 with different sound capabilities and interfaces. It acomplishes this
rlm@15 53 difficult task by providing code for many different sound backends in
rlm@15 54 pseudo-objects called /Devices/. There's a device for the Linux Open
rlm@15 55 Sound System and the Advanced Linxu Sound Architechture, there's one
rlm@15 56 for Direct Sound on Windows, there's even one for Solaris. =OpenAL=
rlm@15 57 solves the problem of platform independence by providing all these
rlm@15 58 Devices.
rlm@15 59
rlm@15 60 Wrapper libraries such as LWJGL are free to examine the system on
rlm@15 61 which they are running and then select an appropiate device for that
rlm@15 62 system.
rlm@15 63
rlm@15 64 There are also a few "special" devices that don't interface with any
rlm@15 65 particular system. These include the Null Device, which doesn't do
rlm@15 66 anything, and the Wave Device, which writes whatever sound it recieves
rlm@15 67 to a file, if everything has been set up correctly when configuring
rlm@15 68 =OpenAL=.
rlm@15 69
rlm@15 70 Actual mixing of the sound data happens in the Devices, and they are
rlm@15 71 the only point in the sound rendering process where this data is
rlm@15 72 available.
rlm@15 73
rlm@15 74 Therefore, in order to support multiple listeners, and get the sound
rlm@15 75 data in a form that the AIs can use, it is necessary to create a new
rlm@15 76 Device, which supports this features.
rlm@15 77
rlm@15 78
rlm@15 79 ** The Send Device
rlm@15 80 Adding a device to OpenAL is rather tricky -- there are five separate
rlm@15 81 files in the =OpenAL= source tree that must be modified to do so. I've
rlm@15 82 documented this process [[./add-new-device.org][here]] for anyone who is interested.
rlm@15 83
rlm@15 84 #+srcname: send
rlm@15 85 #+begin_src C
rlm@15 86 #include "config.h"
rlm@15 87 #include <stdlib.h>
rlm@15 88 #include "alMain.h"
rlm@15 89 #include "AL/al.h"
rlm@15 90 #include "AL/alc.h"
rlm@15 91 #include "alSource.h"
rlm@15 92 #include <jni.h>
rlm@15 93
rlm@15 94 //////////////////// Summary
rlm@15 95
rlm@15 96 struct send_data;
rlm@15 97 struct context_data;
rlm@15 98
rlm@15 99 static void addContext(ALCdevice *, ALCcontext *);
rlm@15 100 static void syncContexts(ALCcontext *master, ALCcontext *slave);
rlm@15 101 static void syncSources(ALsource *master, ALsource *slave,
rlm@15 102 ALCcontext *masterCtx, ALCcontext *slaveCtx);
rlm@15 103
rlm@15 104 static void syncSourcei(ALuint master, ALuint slave,
rlm@15 105 ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param);
rlm@15 106 static void syncSourcef(ALuint master, ALuint slave,
rlm@15 107 ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param);
rlm@15 108 static void syncSource3f(ALuint master, ALuint slave,
rlm@15 109 ALCcontext *masterCtx, ALCcontext *ctx2, ALenum param);
rlm@15 110
rlm@15 111 static void swapInContext(ALCdevice *, struct context_data *);
rlm@15 112 static void saveContext(ALCdevice *, struct context_data *);
rlm@15 113 static void limitContext(ALCdevice *, ALCcontext *);
rlm@15 114 static void unLimitContext(ALCdevice *);
rlm@15 115
rlm@15 116 static void init(ALCdevice *);
rlm@15 117 static void renderData(ALCdevice *, int samples);
rlm@15 118
rlm@15 119 #define UNUSED(x) (void)(x)
rlm@15 120
rlm@15 121 //////////////////// State
rlm@15 122
rlm@15 123 typedef struct context_data {
rlm@15 124 ALfloat ClickRemoval[MAXCHANNELS];
rlm@15 125 ALfloat PendingClicks[MAXCHANNELS];
rlm@15 126 ALvoid *renderBuffer;
rlm@15 127 ALCcontext *ctx;
rlm@15 128 } context_data;
rlm@15 129
rlm@15 130 typedef struct send_data {
rlm@15 131 ALuint size;
rlm@15 132 context_data **contexts;
rlm@15 133 ALuint numContexts;
rlm@15 134 ALuint maxContexts;
rlm@15 135 } send_data;
rlm@15 136
rlm@15 137
rlm@15 138
rlm@15 139 //////////////////// Context Creation / Synchronization
rlm@15 140
rlm@15 141 #define _MAKE_SYNC(NAME, INIT_EXPR, GET_EXPR, SET_EXPR) \
rlm@15 142 void NAME (ALuint sourceID1, ALuint sourceID2, \
rlm@15 143 ALCcontext *ctx1, ALCcontext *ctx2, \
rlm@15 144 ALenum param){ \
rlm@15 145 INIT_EXPR; \
rlm@15 146 ALCcontext *current = alcGetCurrentContext(); \
rlm@15 147 alcMakeContextCurrent(ctx1); \
rlm@15 148 GET_EXPR; \
rlm@15 149 alcMakeContextCurrent(ctx2); \
rlm@15 150 SET_EXPR; \
rlm@15 151 alcMakeContextCurrent(current); \
rlm@15 152 }
rlm@15 153
rlm@15 154 #define MAKE_SYNC(NAME, TYPE, GET, SET) \
rlm@15 155 _MAKE_SYNC(NAME, \
rlm@15 156 TYPE value, \
rlm@15 157 GET(sourceID1, param, &value), \
rlm@15 158 SET(sourceID2, param, value))
rlm@15 159
rlm@15 160 #define MAKE_SYNC3(NAME, TYPE, GET, SET) \
rlm@15 161 _MAKE_SYNC(NAME, \
rlm@15 162 TYPE value1; TYPE value2; TYPE value3;, \
rlm@15 163 GET(sourceID1, param, &value1, &value2, &value3), \
rlm@15 164 SET(sourceID2, param, value1, value2, value3))
rlm@15 165
rlm@15 166 MAKE_SYNC( syncSourcei, ALint, alGetSourcei, alSourcei);
rlm@15 167 MAKE_SYNC( syncSourcef, ALfloat, alGetSourcef, alSourcef);
rlm@15 168 MAKE_SYNC3(syncSource3i, ALint, alGetSource3i, alSource3i);
rlm@15 169 MAKE_SYNC3(syncSource3f, ALfloat, alGetSource3f, alSource3f);
rlm@15 170
rlm@15 171 void syncSources(ALsource *masterSource, ALsource *slaveSource,
rlm@15 172 ALCcontext *masterCtx, ALCcontext *slaveCtx){
rlm@15 173 ALuint master = masterSource->source;
rlm@15 174 ALuint slave = slaveSource->source;
rlm@15 175 ALCcontext *current = alcGetCurrentContext();
rlm@15 176
rlm@15 177 syncSourcef(master,slave,masterCtx,slaveCtx,AL_PITCH);
rlm@15 178 syncSourcef(master,slave,masterCtx,slaveCtx,AL_GAIN);
rlm@15 179 syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_DISTANCE);
rlm@15 180 syncSourcef(master,slave,masterCtx,slaveCtx,AL_ROLLOFF_FACTOR);
rlm@15 181 syncSourcef(master,slave,masterCtx,slaveCtx,AL_REFERENCE_DISTANCE);
rlm@15 182 syncSourcef(master,slave,masterCtx,slaveCtx,AL_MIN_GAIN);
rlm@15 183 syncSourcef(master,slave,masterCtx,slaveCtx,AL_MAX_GAIN);
rlm@15 184 syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_GAIN);
rlm@15 185 syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_INNER_ANGLE);
rlm@15 186 syncSourcef(master,slave,masterCtx,slaveCtx,AL_CONE_OUTER_ANGLE);
rlm@15 187 syncSourcef(master,slave,masterCtx,slaveCtx,AL_SEC_OFFSET);
rlm@15 188 syncSourcef(master,slave,masterCtx,slaveCtx,AL_SAMPLE_OFFSET);
rlm@15 189 syncSourcef(master,slave,masterCtx,slaveCtx,AL_BYTE_OFFSET);
rlm@15 190
rlm@15 191 syncSource3f(master,slave,masterCtx,slaveCtx,AL_POSITION);
rlm@15 192 syncSource3f(master,slave,masterCtx,slaveCtx,AL_VELOCITY);
rlm@15 193 syncSource3f(master,slave,masterCtx,slaveCtx,AL_DIRECTION);
rlm@15 194
rlm@15 195 syncSourcei(master,slave,masterCtx,slaveCtx,AL_SOURCE_RELATIVE);
rlm@15 196 syncSourcei(master,slave,masterCtx,slaveCtx,AL_LOOPING);
rlm@15 197
rlm@15 198 alcMakeContextCurrent(masterCtx);
rlm@15 199 ALint source_type;
rlm@15 200 alGetSourcei(master, AL_SOURCE_TYPE, &source_type);
rlm@15 201
rlm@15 202 // Only static sources are currently synchronized!
rlm@15 203 if (AL_STATIC == source_type){
rlm@15 204 ALint master_buffer;
rlm@15 205 ALint slave_buffer;
rlm@15 206 alGetSourcei(master, AL_BUFFER, &master_buffer);
rlm@15 207 alcMakeContextCurrent(slaveCtx);
rlm@15 208 alGetSourcei(slave, AL_BUFFER, &slave_buffer);
rlm@15 209 if (master_buffer != slave_buffer){
rlm@15 210 alSourcei(slave, AL_BUFFER, master_buffer);
rlm@15 211 }
rlm@15 212 }
rlm@15 213
rlm@15 214 // Synchronize the state of the two sources.
rlm@15 215 alcMakeContextCurrent(masterCtx);
rlm@15 216 ALint masterState;
rlm@15 217 ALint slaveState;
rlm@15 218
rlm@15 219 alGetSourcei(master, AL_SOURCE_STATE, &masterState);
rlm@15 220 alcMakeContextCurrent(slaveCtx);
rlm@15 221 alGetSourcei(slave, AL_SOURCE_STATE, &slaveState);
rlm@15 222
rlm@15 223 if (masterState != slaveState){
rlm@15 224 switch (masterState){
rlm@15 225 case AL_INITIAL : alSourceRewind(slave); break;
rlm@15 226 case AL_PLAYING : alSourcePlay(slave); break;
rlm@15 227 case AL_PAUSED : alSourcePause(slave); break;
rlm@15 228 case AL_STOPPED : alSourceStop(slave); break;
rlm@15 229 }
rlm@15 230 }
rlm@15 231 // Restore whatever context was previously active.
rlm@15 232 alcMakeContextCurrent(current);
rlm@15 233 }
rlm@15 234
rlm@15 235
rlm@15 236 void syncContexts(ALCcontext *master, ALCcontext *slave){
rlm@15 237 /* If there aren't sufficient sources in slave to mirror
rlm@15 238 the sources in master, create them. */
rlm@15 239 ALCcontext *current = alcGetCurrentContext();
rlm@15 240
rlm@15 241 UIntMap *masterSourceMap = &(master->SourceMap);
rlm@15 242 UIntMap *slaveSourceMap = &(slave->SourceMap);
rlm@15 243 ALuint numMasterSources = masterSourceMap->size;
rlm@15 244 ALuint numSlaveSources = slaveSourceMap->size;
rlm@15 245
rlm@15 246 alcMakeContextCurrent(slave);
rlm@15 247 if (numSlaveSources < numMasterSources){
rlm@15 248 ALuint numMissingSources = numMasterSources - numSlaveSources;
rlm@15 249 ALuint newSources[numMissingSources];
rlm@15 250 alGenSources(numMissingSources, newSources);
rlm@15 251 }
rlm@15 252
rlm@15 253 /* Now, slave is gauranteed to have at least as many sources
rlm@15 254 as master. Sync each source from master to the corresponding
rlm@15 255 source in slave. */
rlm@15 256 int i;
rlm@15 257 for(i = 0; i < masterSourceMap->size; i++){
rlm@15 258 syncSources((ALsource*)masterSourceMap->array[i].value,
rlm@15 259 (ALsource*)slaveSourceMap->array[i].value,
rlm@15 260 master, slave);
rlm@15 261 }
rlm@15 262 alcMakeContextCurrent(current);
rlm@15 263 }
rlm@15 264
rlm@15 265 static void addContext(ALCdevice *Device, ALCcontext *context){
rlm@15 266 send_data *data = (send_data*)Device->ExtraData;
rlm@15 267 // expand array if necessary
rlm@15 268 if (data->numContexts >= data->maxContexts){
rlm@15 269 ALuint newMaxContexts = data->maxContexts*2 + 1;
rlm@15 270 data->contexts = realloc(data->contexts, newMaxContexts*sizeof(context_data));
rlm@15 271 data->maxContexts = newMaxContexts;
rlm@15 272 }
rlm@15 273 // create context_data and add it to the main array
rlm@15 274 context_data *ctxData;
rlm@15 275 ctxData = (context_data*)calloc(1, sizeof(*ctxData));
rlm@15 276 ctxData->renderBuffer =
rlm@15 277 malloc(BytesFromDevFmt(Device->FmtType) *
rlm@15 278 Device->NumChan * Device->UpdateSize);
rlm@15 279 ctxData->ctx = context;
rlm@15 280
rlm@15 281 data->contexts[data->numContexts] = ctxData;
rlm@15 282 data->numContexts++;
rlm@15 283 }
rlm@15 284
rlm@15 285
rlm@15 286 //////////////////// Context Switching
rlm@15 287
rlm@15 288 /* A device brings along with it two pieces of state
rlm@15 289 * which have to be swapped in and out with each context.
rlm@15 290 */
rlm@15 291 static void swapInContext(ALCdevice *Device, context_data *ctxData){
rlm@15 292 memcpy(Device->ClickRemoval, ctxData->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS);
rlm@15 293 memcpy(Device->PendingClicks, ctxData->PendingClicks, sizeof(ALfloat)*MAXCHANNELS);
rlm@15 294 }
rlm@15 295
rlm@15 296 static void saveContext(ALCdevice *Device, context_data *ctxData){
rlm@15 297 memcpy(ctxData->ClickRemoval, Device->ClickRemoval, sizeof(ALfloat)*MAXCHANNELS);
rlm@15 298 memcpy(ctxData->PendingClicks, Device->PendingClicks, sizeof(ALfloat)*MAXCHANNELS);
rlm@15 299 }
rlm@15 300
rlm@15 301 static ALCcontext **currentContext;
rlm@15 302 static ALuint currentNumContext;
rlm@15 303
rlm@15 304 /* By default, all contexts are rendered at once for each call to aluMixData.
rlm@15 305 * This function uses the internals of the ALCdecice struct to temporarly
rlm@15 306 * cause aluMixData to only render the chosen context.
rlm@15 307 */
rlm@15 308 static void limitContext(ALCdevice *Device, ALCcontext *ctx){
rlm@15 309 currentContext = Device->Contexts;
rlm@15 310 currentNumContext = Device->NumContexts;
rlm@15 311 Device->Contexts = &ctx;
rlm@15 312 Device->NumContexts = 1;
rlm@15 313 }
rlm@15 314
rlm@15 315 static void unLimitContext(ALCdevice *Device){
rlm@15 316 Device->Contexts = currentContext;
rlm@15 317 Device->NumContexts = currentNumContext;
rlm@15 318 }
rlm@15 319
rlm@15 320
rlm@15 321 //////////////////// Main Device Loop
rlm@15 322
rlm@15 323 /* Establish the LWJGL context as the main context, which will
rlm@15 324 * be synchronized to all the slave contexts
rlm@15 325 */
rlm@15 326 static void init(ALCdevice *Device){
rlm@15 327 ALCcontext *masterContext = alcGetCurrentContext();
rlm@15 328 addContext(Device, masterContext);
rlm@15 329 }
rlm@15 330
rlm@15 331
rlm@15 332 static void renderData(ALCdevice *Device, int samples){
rlm@15 333 if(!Device->Connected){return;}
rlm@15 334 send_data *data = (send_data*)Device->ExtraData;
rlm@15 335 ALCcontext *current = alcGetCurrentContext();
rlm@15 336
rlm@15 337 ALuint i;
rlm@15 338 for (i = 1; i < data->numContexts; i++){
rlm@15 339 syncContexts(data->contexts[0]->ctx , data->contexts[i]->ctx);
rlm@15 340 }
rlm@15 341
rlm@15 342 if ((uint) samples > Device->UpdateSize){
rlm@15 343 printf("exceeding internal buffer size; dropping samples\n");
rlm@15 344 printf("requested %d; available %d\n", samples, Device->UpdateSize);
rlm@15 345 samples = (int) Device->UpdateSize;
rlm@15 346 }
rlm@15 347
rlm@15 348 for (i = 0; i < data->numContexts; i++){
rlm@15 349 context_data *ctxData = data->contexts[i];
rlm@15 350 ALCcontext *ctx = ctxData->ctx;
rlm@15 351 alcMakeContextCurrent(ctx);
rlm@15 352 limitContext(Device, ctx);
rlm@15 353 swapInContext(Device, ctxData);
rlm@15 354 aluMixData(Device, ctxData->renderBuffer, samples);
rlm@15 355 saveContext(Device, ctxData);
rlm@15 356 unLimitContext(Device);
rlm@15 357 }
rlm@15 358 alcMakeContextCurrent(current);
rlm@15 359 }
rlm@15 360
rlm@15 361
rlm@15 362 //////////////////// JNI Methods
rlm@15 363
rlm@15 364 #include "com_aurellem_send_AudioSend.h"
rlm@15 365
rlm@15 366 /*
rlm@15 367 * Class: com_aurellem_send_AudioSend
rlm@15 368 * Method: nstep
rlm@15 369 * Signature: (JI)V
rlm@15 370 */
rlm@15 371 JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_nstep
rlm@15 372 (JNIEnv *env, jclass clazz, jlong device, jint samples){
rlm@15 373 UNUSED(env);UNUSED(clazz);UNUSED(device);
rlm@15 374 renderData((ALCdevice*)((intptr_t)device), samples);
rlm@15 375 }
rlm@15 376
rlm@15 377 /*
rlm@15 378 * Class: com_aurellem_send_AudioSend
rlm@15 379 * Method: ngetSamples
rlm@15 380 * Signature: (JLjava/nio/ByteBuffer;III)V
rlm@15 381 */
rlm@15 382 JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_ngetSamples
rlm@15 383 (JNIEnv *env, jclass clazz, jlong device, jobject buffer, jint position,
rlm@15 384 jint samples, jint n){
rlm@15 385 UNUSED(clazz);
rlm@15 386
rlm@15 387 ALvoid *buffer_address =
rlm@15 388 ((ALbyte *)(((char*)(*env)->GetDirectBufferAddress(env, buffer)) + position));
rlm@15 389 ALCdevice *recorder = (ALCdevice*) ((intptr_t)device);
rlm@15 390 send_data *data = (send_data*)recorder->ExtraData;
rlm@15 391 if ((ALuint)n > data->numContexts){return;}
rlm@15 392
rlm@15 393 //printf("Want %d samples for listener %d\n", samples, n);
rlm@15 394 //printf("Device's format type is %d bytes per sample,\n",
rlm@15 395 // BytesFromDevFmt(recorder->FmtType));
rlm@15 396 //printf("and it has %d channels, making for %d requested bytes\n",
rlm@15 397 // recorder->NumChan,
rlm@15 398 // BytesFromDevFmt(recorder->FmtType) * recorder->NumChan * samples);
rlm@15 399
rlm@15 400 memcpy(buffer_address, data->contexts[n]->renderBuffer,
rlm@15 401 BytesFromDevFmt(recorder->FmtType) * recorder->NumChan * samples);
rlm@15 402 //samples*sizeof(ALfloat));
rlm@15 403 }
rlm@15 404
rlm@15 405 /*
rlm@15 406 * Class: com_aurellem_send_AudioSend
rlm@15 407 * Method: naddListener
rlm@15 408 * Signature: (J)V
rlm@15 409 */
rlm@15 410 JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_naddListener
rlm@15 411 (JNIEnv *env, jclass clazz, jlong device){
rlm@15 412 UNUSED(env); UNUSED(clazz);
rlm@15 413 //printf("creating new context via naddListener\n");
rlm@15 414 ALCdevice *Device = (ALCdevice*) ((intptr_t)device);
rlm@15 415 ALCcontext *new = alcCreateContext(Device, NULL);
rlm@15 416 addContext(Device, new);
rlm@15 417 }
rlm@15 418
rlm@15 419 /*
rlm@15 420 * Class: com_aurellem_send_AudioSend
rlm@15 421 * Method: nsetNthListener3f
rlm@15 422 * Signature: (IFFFJI)V
rlm@15 423 */
rlm@15 424 JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_nsetNthListener3f
rlm@15 425 (JNIEnv *env, jclass clazz, jint param,
rlm@15 426 jfloat v1, jfloat v2, jfloat v3, jlong device, jint contextNum){
rlm@15 427 UNUSED(env);UNUSED(clazz);
rlm@15 428
rlm@15 429 ALCdevice *Device = (ALCdevice*) ((intptr_t)device);
rlm@15 430 send_data *data = (send_data*)Device->ExtraData;
rlm@15 431
rlm@15 432 ALCcontext *current = alcGetCurrentContext();
rlm@15 433 if ((ALuint)contextNum > data->numContexts){return;}
rlm@15 434 alcMakeContextCurrent(data->contexts[contextNum]->ctx);
rlm@15 435 alListener3f(param, v1, v2, v3);
rlm@15 436 alcMakeContextCurrent(current);
rlm@15 437 }
rlm@15 438
rlm@15 439 /*
rlm@15 440 * Class: com_aurellem_send_AudioSend
rlm@15 441 * Method: nsetNthListenerf
rlm@15 442 * Signature: (IFJI)V
rlm@15 443 */
rlm@15 444 JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_nsetNthListenerf
rlm@15 445 (JNIEnv *env, jclass clazz, jint param, jfloat v1, jlong device,
rlm@15 446 jint contextNum){
rlm@15 447
rlm@15 448 UNUSED(env);UNUSED(clazz);
rlm@15 449
rlm@15 450 ALCdevice *Device = (ALCdevice*) ((intptr_t)device);
rlm@15 451 send_data *data = (send_data*)Device->ExtraData;
rlm@15 452
rlm@15 453 ALCcontext *current = alcGetCurrentContext();
rlm@15 454 if ((ALuint)contextNum > data->numContexts){return;}
rlm@15 455 alcMakeContextCurrent(data->contexts[contextNum]->ctx);
rlm@15 456 alListenerf(param, v1);
rlm@15 457 alcMakeContextCurrent(current);
rlm@15 458 }
rlm@15 459
rlm@15 460 /*
rlm@15 461 * Class: com_aurellem_send_AudioSend
rlm@15 462 * Method: ninitDevice
rlm@15 463 * Signature: (J)V
rlm@15 464 */
rlm@15 465 JNIEXPORT void JNICALL Java_com_aurellem_send_AudioSend_ninitDevice
rlm@15 466 (JNIEnv *env, jclass clazz, jlong device){
rlm@15 467 UNUSED(env);UNUSED(clazz);
rlm@15 468
rlm@15 469 ALCdevice *Device = (ALCdevice*) ((intptr_t)device);
rlm@15 470 init(Device);
rlm@15 471
rlm@15 472 }
rlm@15 473
rlm@15 474
rlm@15 475 /*
rlm@15 476 * Class: com_aurellem_send_AudioSend
rlm@15 477 * Method: ngetAudioFormat
rlm@15 478 * Signature: (J)Ljavax/sound/sampled/AudioFormat;
rlm@15 479 */
rlm@15 480 JNIEXPORT jobject JNICALL Java_com_aurellem_send_AudioSend_ngetAudioFormat
rlm@15 481 (JNIEnv *env, jclass clazz, jlong device){
rlm@15 482 UNUSED(clazz);
rlm@15 483 jclass AudioFormatClass =
rlm@15 484 (*env)->FindClass(env, "javax/sound/sampled/AudioFormat");
rlm@15 485 jmethodID AudioFormatConstructor =
rlm@15 486 (*env)->GetMethodID(env, AudioFormatClass, "<init>", "(FIIZZ)V");
rlm@15 487
rlm@15 488 ALCdevice *Device = (ALCdevice*) ((intptr_t)device);
rlm@15 489
rlm@15 490 //float frequency
rlm@15 491
rlm@15 492 int isSigned;
rlm@15 493 switch (Device->FmtType)
rlm@15 494 {
rlm@15 495 case DevFmtUByte:
rlm@15 496 case DevFmtUShort: isSigned = 0; break;
rlm@15 497 default : isSigned = 1;
rlm@15 498 }
rlm@15 499 float frequency = Device->Frequency;
rlm@15 500 int bitsPerFrame = (8 * BytesFromDevFmt(Device->FmtType));
rlm@15 501 int channels = Device->NumChan;
rlm@15 502
rlm@15 503
rlm@15 504 //printf("freq = %f, bpf = %d, channels = %d, signed? = %d\n",
rlm@15 505 // frequency, bitsPerFrame, channels, isSigned);
rlm@15 506
rlm@15 507 jobject format = (*env)->
rlm@15 508 NewObject(
rlm@15 509 env,AudioFormatClass,AudioFormatConstructor,
rlm@15 510 frequency,
rlm@15 511 bitsPerFrame,
rlm@15 512 channels,
rlm@15 513 isSigned,
rlm@15 514 0);
rlm@15 515 return format;
rlm@15 516 }
rlm@15 517
rlm@15 518
rlm@15 519
rlm@15 520 //////////////////// Device Initilization / Management
rlm@15 521
rlm@15 522 static const ALCchar sendDevice[] = "Multiple Audio Send";
rlm@15 523
rlm@15 524 static ALCboolean send_open_playback(ALCdevice *device,
rlm@15 525 const ALCchar *deviceName)
rlm@15 526 {
rlm@15 527 send_data *data;
rlm@15 528 // stop any buffering for stdout, so that I can
rlm@15 529 // see the printf statements in my terminal immediatley
rlm@15 530 setbuf(stdout, NULL);
rlm@15 531
rlm@15 532 if(!deviceName)
rlm@15 533 deviceName = sendDevice;
rlm@15 534 else if(strcmp(deviceName, sendDevice) != 0)
rlm@15 535 return ALC_FALSE;
rlm@15 536 data = (send_data*)calloc(1, sizeof(*data));
rlm@15 537 device->szDeviceName = strdup(deviceName);
rlm@15 538 device->ExtraData = data;
rlm@15 539 return ALC_TRUE;
rlm@15 540 }
rlm@15 541
rlm@15 542 static void send_close_playback(ALCdevice *device)
rlm@15 543 {
rlm@15 544 send_data *data = (send_data*)device->ExtraData;
rlm@15 545 alcMakeContextCurrent(NULL);
rlm@15 546 ALuint i;
rlm@15 547 // Destroy all slave contexts. LWJGL will take care of
rlm@15 548 // its own context.
rlm@15 549 for (i = 1; i < data->numContexts; i++){
rlm@15 550 context_data *ctxData = data->contexts[i];
rlm@15 551 alcDestroyContext(ctxData->ctx);
rlm@15 552 free(ctxData->renderBuffer);
rlm@15 553 free(ctxData);
rlm@15 554 }
rlm@15 555 free(data);
rlm@15 556 device->ExtraData = NULL;
rlm@15 557 }
rlm@15 558
rlm@15 559 static ALCboolean send_reset_playback(ALCdevice *device)
rlm@15 560 {
rlm@15 561 SetDefaultWFXChannelOrder(device);
rlm@15 562 return ALC_TRUE;
rlm@15 563 }
rlm@15 564
rlm@15 565 static void send_stop_playback(ALCdevice *Device){
rlm@15 566 UNUSED(Device);
rlm@15 567 }
rlm@15 568
rlm@15 569 static const BackendFuncs send_funcs = {
rlm@15 570 send_open_playback,
rlm@15 571 send_close_playback,
rlm@15 572 send_reset_playback,
rlm@15 573 send_stop_playback,
rlm@15 574 NULL,
rlm@15 575 NULL, /* These would be filled with functions to */
rlm@15 576 NULL, /* handle capturing audio if we we into that */
rlm@15 577 NULL, /* sort of thing... */
rlm@15 578 NULL,
rlm@15 579 NULL
rlm@15 580 };
rlm@15 581
rlm@15 582 ALCboolean alc_send_init(BackendFuncs *func_list){
rlm@15 583 *func_list = send_funcs;
rlm@15 584 return ALC_TRUE;
rlm@15 585 }
rlm@15 586
rlm@15 587 void alc_send_deinit(void){}
rlm@15 588
rlm@15 589 void alc_send_probe(enum DevProbe type)
rlm@15 590 {
rlm@15 591 switch(type)
rlm@15 592 {
rlm@15 593 case DEVICE_PROBE:
rlm@15 594 AppendDeviceList(sendDevice);
rlm@15 595 break;
rlm@15 596 case ALL_DEVICE_PROBE:
rlm@15 597 AppendAllDeviceList(sendDevice);
rlm@15 598 break;
rlm@15 599 case CAPTURE_DEVICE_PROBE:
rlm@15 600 break;
rlm@15 601 }
rlm@15 602 }
rlm@15 603 #+end_src
rlm@15 604
rlm@15 605
rlm@15 606
rlm@15 607
rlm@15 608
rlm@15 609
rlm@15 610
rlm@15 611
rlm@15 612 #+srcname: ears
rlm@0 613 #+begin_src clojure
rlm@15 614 (ns cortex.hearing)
rlm@0 615 (use 'cortex.world)
rlm@0 616 (use 'cortex.import)
rlm@0 617 (use 'clojure.contrib.def)
rlm@0 618 (cortex.import/mega-import-jme3)
rlm@0 619 (rlm.rlm-commands/help)
rlm@0 620 (import java.nio.ByteBuffer)
rlm@0 621 (import java.awt.image.BufferedImage)
rlm@0 622 (import java.awt.Color)
rlm@0 623 (import java.awt.Dimension)
rlm@0 624 (import java.awt.Graphics)
rlm@0 625 (import java.awt.Graphics2D)
rlm@0 626 (import java.awt.event.WindowAdapter)
rlm@0 627 (import java.awt.event.WindowEvent)
rlm@0 628 (import java.awt.image.BufferedImage)
rlm@0 629 (import java.nio.ByteBuffer)
rlm@0 630 (import javax.swing.JFrame)
rlm@0 631 (import javax.swing.JPanel)
rlm@0 632 (import javax.swing.SwingUtilities)
rlm@0 633 (import javax.swing.ImageIcon)
rlm@0 634 (import javax.swing.JOptionPane)
rlm@0 635 (import java.awt.image.ImageObserver)
rlm@0 636
rlm@0 637 (import 'com.jme3.capture.SoundProcessor)
rlm@0 638
rlm@0 639
rlm@0 640 (defn sound-processor
rlm@0 641 "deals with converting ByteBuffers into Arrays of bytes so that the
rlm@0 642 continuation functions can be defined in terms of immutable stuff."
rlm@0 643 [continuation]
rlm@0 644 (proxy [SoundProcessor] []
rlm@0 645 (cleanup [])
rlm@0 646 (process
rlm@0 647 [#^ByteBuffer audioSamples numSamples]
rlm@0 648 (no-exceptions
rlm@0 649 (let [byte-array (byte-array numSamples)]
rlm@0 650 (.get audioSamples byte-array 0 numSamples)
rlm@0 651 (continuation
rlm@0 652 (vec byte-array)))))))
rlm@0 653
rlm@0 654 (defn add-ear
rlm@0 655 "add an ear to the world. The continuation function will be called
rlm@0 656 on the FFT or the sounds which the ear hears in the given
rlm@0 657 timeframe. Sound is 3D."
rlm@0 658 [world listener continuation]
rlm@0 659 (let [renderer (.getAudioRenderer world)]
rlm@0 660 (.addListener renderer listener)
rlm@0 661 (.registerSoundProcessor renderer listener
rlm@0 662 (sound-processor continuation))
rlm@0 663 listener))
rlm@0 664
rlm@0 665 #+end_src
rlm@0 666
rlm@0 667
rlm@0 668
rlm@0 669 #+srcname: test-hearing
rlm@0 670 #+begin_src clojure :results silent
rlm@0 671 (ns test.hearing)
rlm@0 672 (use 'cortex.world)
rlm@0 673 (use 'cortex.import)
rlm@0 674 (use 'clojure.contrib.def)
rlm@0 675 (use 'body.ear)
rlm@0 676 (cortex.import/mega-import-jme3)
rlm@0 677 (rlm.rlm-commands/help)
rlm@0 678
rlm@0 679 (defn setup-fn [world]
rlm@0 680 (let [listener (Listener.)]
rlm@0 681 (add-ear world listener #(println (nth % 0)))))
rlm@0 682
rlm@0 683 (defn play-sound [node world value]
rlm@0 684 (if (not value)
rlm@0 685 (do
rlm@0 686 (.playSource (.getAudioRenderer world) node))))
rlm@0 687
rlm@0 688 (defn test-world []
rlm@0 689 (let [node1 (AudioNode. (asset-manager) "Sounds/pure.wav" false false)]
rlm@0 690 (world
rlm@0 691 (Node.)
rlm@0 692 {"key-space" (partial play-sound node1)}
rlm@0 693 setup-fn
rlm@0 694 no-op
rlm@0 695 )))
rlm@0 696
rlm@0 697
rlm@0 698 #+end_src
rlm@0 699
rlm@0 700
rlm@0 701
rlm@15 702 * Example
rlm@15 703
rlm@0 704 * COMMENT Code Generation
rlm@0 705
rlm@15 706 #+begin_src clojure :tangle ../../cortex/src/cortex/hearing.clj
rlm@15 707 <<ears>>
rlm@0 708 #+end_src
rlm@0 709
rlm@15 710 #+begin_src clojure :tangle ../../cortex/src/test/hearing.clj
rlm@0 711 <<test-hearing>>
rlm@0 712 #+end_src
rlm@0 713
rlm@0 714
rlm@15 715 #+begin_src C :tangle ../Alc/backends/send.c
rlm@15 716 <<send>>
rlm@15 717 #+end_src