Mercurial > audio-send
comparison org/ear.org @ 29:cdf320cb5949
updated test suite
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 10 Dec 2011 21:42:50 -0600 |
parents | f4c7260d397a |
children | 32c69ba451d9 |
comparison
equal
deleted
inserted
replaced
28:1fc162d84343 | 29:cdf320cb5949 |
---|---|
88 clojure. | 88 clojure. |
89 | 89 |
90 ** =send.c= | 90 ** =send.c= |
91 | 91 |
92 ** Header | 92 ** Header |
93 #+srcname: send-header | 93 #+name: send-header |
94 #+begin_src C | 94 #+begin_src C |
95 #include "config.h" | 95 #include "config.h" |
96 #include <stdlib.h> | 96 #include <stdlib.h> |
97 #include "alMain.h" | 97 #include "alMain.h" |
98 #include "AL/al.h" | 98 #include "AL/al.h" |
148 which are in the master context. | 148 which are in the master context. |
149 - =limitContext()= and =unLimitContext()= make it possible to render | 149 - =limitContext()= and =unLimitContext()= make it possible to render |
150 only one context at a time. | 150 only one context at a time. |
151 | 151 |
152 ** Necessary State | 152 ** Necessary State |
153 #+srcname: send-state | 153 #+name: send-state |
154 #+begin_src C | 154 #+begin_src C |
155 //////////////////// State | 155 //////////////////// State |
156 | 156 |
157 typedef struct context_data { | 157 typedef struct context_data { |
158 ALfloat ClickRemoval[MAXCHANNELS]; | 158 ALfloat ClickRemoval[MAXCHANNELS]; |
176 parallel. The solution is to create a copy of this normally global | 176 parallel. The solution is to create a copy of this normally global |
177 device state for each context, and copy it back and forth into and out | 177 device state for each context, and copy it back and forth into and out |
178 of the actual device state whenever a context is rendered. | 178 of the actual device state whenever a context is rendered. |
179 | 179 |
180 ** Synchronization Macros | 180 ** Synchronization Macros |
181 #+srcname: sync-macros | 181 #+name: sync-macros |
182 #+begin_src C | 182 #+begin_src C |
183 //////////////////// Context Creation / Synchronization | 183 //////////////////// Context Creation / Synchronization |
184 | 184 |
185 #define _MAKE_SYNC(NAME, INIT_EXPR, GET_EXPR, SET_EXPR) \ | 185 #define _MAKE_SYNC(NAME, INIT_EXPR, GET_EXPR, SET_EXPR) \ |
186 void NAME (ALuint sourceID1, ALuint sourceID2, \ | 186 void NAME (ALuint sourceID1, ALuint sourceID2, \ |
219 completely synchronize two sources, it is necessary to use all of | 219 completely synchronize two sources, it is necessary to use all of |
220 them. These macros help to condense the otherwise repetitive | 220 them. These macros help to condense the otherwise repetitive |
221 synchronization code involving these similar low-level =OpenAL= functions. | 221 synchronization code involving these similar low-level =OpenAL= functions. |
222 | 222 |
223 ** Source Synchronization | 223 ** Source Synchronization |
224 #+srcname: sync-sources | 224 #+name: sync-sources |
225 #+begin_src C | 225 #+begin_src C |
226 void syncSources(ALsource *masterSource, ALsource *slaveSource, | 226 void syncSources(ALsource *masterSource, ALsource *slaveSource, |
227 ALCcontext *masterCtx, ALCcontext *slaveCtx){ | 227 ALCcontext *masterCtx, ALCcontext *slaveCtx){ |
228 ALuint master = masterSource->source; | 228 ALuint master = masterSource->source; |
229 ALuint slave = slaveSource->source; | 229 ALuint slave = slaveSource->source; |
292 same between the master and slave sources. I'd like to take this | 292 same between the master and slave sources. I'd like to take this |
293 moment to salute the [[http://connect.creativelabs.com/openal/Documentation/Forms/AllItems.aspx][=OpenAL= Reference Manual]], which provides a very | 293 moment to salute the [[http://connect.creativelabs.com/openal/Documentation/Forms/AllItems.aspx][=OpenAL= Reference Manual]], which provides a very |
294 good description of =OpenAL='s internals. | 294 good description of =OpenAL='s internals. |
295 | 295 |
296 ** Context Synchronization | 296 ** Context Synchronization |
297 #+srcname: sync-contexts | 297 #+name: sync-contexts |
298 #+begin_src C | 298 #+begin_src C |
299 void syncContexts(ALCcontext *master, ALCcontext *slave){ | 299 void syncContexts(ALCcontext *master, ALCcontext *slave){ |
300 /* If there aren't sufficient sources in slave to mirror | 300 /* If there aren't sufficient sources in slave to mirror |
301 the sources in master, create them. */ | 301 the sources in master, create them. */ |
302 ALCcontext *current = alcGetCurrentContext(); | 302 ALCcontext *current = alcGetCurrentContext(); |
330 =syncSources()=. The only thing that =syncContexts()= has to worry | 330 =syncSources()=. The only thing that =syncContexts()= has to worry |
331 about is automatically creating new sources whenever a slave context | 331 about is automatically creating new sources whenever a slave context |
332 does not have the same number of sources as the master context. | 332 does not have the same number of sources as the master context. |
333 | 333 |
334 ** Context Creation | 334 ** Context Creation |
335 #+srcname: context-creation | 335 #+name: context-creation |
336 #+begin_src C | 336 #+begin_src C |
337 static void addContext(ALCdevice *Device, ALCcontext *context){ | 337 static void addContext(ALCdevice *Device, ALCcontext *context){ |
338 send_data *data = (send_data*)Device->ExtraData; | 338 send_data *data = (send_data*)Device->ExtraData; |
339 // expand array if necessary | 339 // expand array if necessary |
340 if (data->numContexts >= data->maxContexts){ | 340 if (data->numContexts >= data->maxContexts){ |
359 device-wide =ExtraData= structure. The =renderBuffer= that is created | 359 device-wide =ExtraData= structure. The =renderBuffer= that is created |
360 here is where the rendered sound samples for this slave context will | 360 here is where the rendered sound samples for this slave context will |
361 eventually go. | 361 eventually go. |
362 | 362 |
363 ** Context Switching | 363 ** Context Switching |
364 #+srcname: context-switching | 364 #+name: context-switching |
365 #+begin_src C | 365 #+begin_src C |
366 //////////////////// Context Switching | 366 //////////////////// Context Switching |
367 | 367 |
368 /* A device brings along with it two pieces of state | 368 /* A device brings along with it two pieces of state |
369 * which have to be swapped in and out with each context. | 369 * which have to be swapped in and out with each context. |
405 the Device's context list to put the desired context-to-be-rendered | 405 the Device's context list to put the desired context-to-be-rendered |
406 into position 0, we can get trick =OpenAL= into rendering each slave | 406 into position 0, we can get trick =OpenAL= into rendering each slave |
407 context separate from all the others. | 407 context separate from all the others. |
408 | 408 |
409 ** Main Device Loop | 409 ** Main Device Loop |
410 #+srcname: main-loop | 410 #+name: main-loop |
411 #+begin_src C | 411 #+begin_src C |
412 //////////////////// Main Device Loop | 412 //////////////////// Main Device Loop |
413 | 413 |
414 /* Establish the LWJGL context as the master context, which will | 414 /* Establish the LWJGL context as the master context, which will |
415 * be synchronized to all the slave contexts | 415 * be synchronized to all the slave contexts |
462 a way to transport this information to Java, and also a way to drive | 462 a way to transport this information to Java, and also a way to drive |
463 this device from Java. The following JNI interface code is inspired | 463 this device from Java. The following JNI interface code is inspired |
464 by the way LWJGL interfaces with =OpenAL=. | 464 by the way LWJGL interfaces with =OpenAL=. |
465 | 465 |
466 *** step | 466 *** step |
467 #+srcname: jni-step | 467 #+name: jni-step |
468 #+begin_src C | 468 #+begin_src C |
469 //////////////////// JNI Methods | 469 //////////////////// JNI Methods |
470 | 470 |
471 #include "com_aurellem_send_AudioSend.h" | 471 #include "com_aurellem_send_AudioSend.h" |
472 | 472 |
489 simulate 1 second of AI-time would miss almost all of the sound in | 489 simulate 1 second of AI-time would miss almost all of the sound in |
490 its environment. | 490 its environment. |
491 | 491 |
492 | 492 |
493 *** getSamples | 493 *** getSamples |
494 #+srcname: jni-get-samples | 494 #+name: jni-get-samples |
495 #+begin_src C | 495 #+begin_src C |
496 /* | 496 /* |
497 * Class: com_aurellem_send_AudioSend | 497 * Class: com_aurellem_send_AudioSend |
498 * Method: ngetSamples | 498 * Method: ngetSamples |
499 * Signature: (JLjava/nio/ByteBuffer;III)V | 499 * Signature: (JLjava/nio/ByteBuffer;III)V |
520 | 520 |
521 =addListener=, =setNthListenerf=, and =setNthListener3f= are | 521 =addListener=, =setNthListenerf=, and =setNthListener3f= are |
522 necessary to change the properties of any listener other than the | 522 necessary to change the properties of any listener other than the |
523 master one, since only the listener of the current active context is | 523 master one, since only the listener of the current active context is |
524 affected by the normal =OpenAL= listener calls. | 524 affected by the normal =OpenAL= listener calls. |
525 #+srcname: listener-manage | 525 #+name: listener-manage |
526 #+begin_src C | 526 #+begin_src C |
527 /* | 527 /* |
528 * Class: com_aurellem_send_AudioSend | 528 * Class: com_aurellem_send_AudioSend |
529 * Method: naddListener | 529 * Method: naddListener |
530 * Signature: (J)V | 530 * Signature: (J)V |
587 | 587 |
588 =getAudioFormat= is a convenience function that uses JNI to build up a | 588 =getAudioFormat= is a convenience function that uses JNI to build up a |
589 =javax.sound.sampled.AudioFormat= object from data in the Device. This | 589 =javax.sound.sampled.AudioFormat= object from data in the Device. This |
590 way, there is no ambiguity about what the bits created by =step= and | 590 way, there is no ambiguity about what the bits created by =step= and |
591 returned by =getSamples= mean. | 591 returned by =getSamples= mean. |
592 #+srcname: jni-init | 592 #+name: jni-init |
593 #+begin_src C | 593 #+begin_src C |
594 /* | 594 /* |
595 * Class: com_aurellem_send_AudioSend | 595 * Class: com_aurellem_send_AudioSend |
596 * Method: ninitDevice | 596 * Method: ninitDevice |
597 * Signature: (J)V | 597 * Signature: (J)V |
640 #+end_src | 640 #+end_src |
641 | 641 |
642 ** Boring Device management stuff | 642 ** Boring Device management stuff |
643 This code is more-or-less copied verbatim from the other =OpenAL= | 643 This code is more-or-less copied verbatim from the other =OpenAL= |
644 backends. It's the basis for =OpenAL='s primitive object system. | 644 backends. It's the basis for =OpenAL='s primitive object system. |
645 #+srcname: device-init | 645 #+name: device-init |
646 #+begin_src C | 646 #+begin_src C |
647 //////////////////// Device Initialization / Management | 647 //////////////////// Device Initialization / Management |
648 | 648 |
649 static const ALCchar sendDevice[] = "Multiple Audio Send"; | 649 static const ALCchar sendDevice[] = "Multiple Audio Send"; |
650 | 650 |
747 simple. Just as there were =SceneProcessors= for vision, there are | 747 simple. Just as there were =SceneProcessors= for vision, there are |
748 now =SoundProcessors= for hearing. | 748 now =SoundProcessors= for hearing. |
749 | 749 |
750 #+include "../../jmeCapture/src/com/aurellem/capture/audio/SoundProcessor.java" src java | 750 #+include "../../jmeCapture/src/com/aurellem/capture/audio/SoundProcessor.java" src java |
751 | 751 |
752 #+srcname: ears | 752 #+name: ears |
753 #+begin_src clojure | 753 #+begin_src clojure |
754 (ns cortex.hearing | 754 (ns cortex.hearing |
755 "Simulate the sense of hearing in jMonkeyEngine3. Enables multiple | 755 "Simulate the sense of hearing in jMonkeyEngine3. Enables multiple |
756 listeners at different positions in the same world. Passes vectors | 756 listeners at different positions in the same world. Passes vectors |
757 of floats in the range [-1.0 -- 1.0] in PCM format to any arbitrary | 757 of floats in the range [-1.0 -- 1.0] in PCM format to any arbitrary |
793 listener)) | 793 listener)) |
794 #+end_src | 794 #+end_src |
795 | 795 |
796 * Example | 796 * Example |
797 | 797 |
798 #+srcname: test-hearing | 798 #+name: test-hearing |
799 #+begin_src clojure :results silent | 799 #+begin_src clojure :results silent |
800 (ns test.hearing | 800 (ns cortex.test.hearing |
801 (:use (cortex world util hearing)) | 801 (:use (cortex world util hearing)) |
802 (:import (com.jme3.audio AudioNode Listener)) | 802 (:import (com.jme3.audio AudioNode Listener)) |
803 (:import com.jme3.scene.Node)) | 803 (:import com.jme3.scene.Node |
804 com.jme3.system.AppSettings)) | |
804 | 805 |
805 (defn setup-fn [world] | 806 (defn setup-fn [world] |
806 (let [listener (Listener.)] | 807 (let [listener (Listener.)] |
807 (add-ear world listener #(println-repl (nth % 0))))) | 808 (add-ear world listener #(println-repl (nth % 0))))) |
808 | 809 |
810 (if (not value) | 811 (if (not value) |
811 (do | 812 (do |
812 (.playSource (.getAudioRenderer world) node)))) | 813 (.playSource (.getAudioRenderer world) node)))) |
813 | 814 |
814 (defn test-basic-hearing [] | 815 (defn test-basic-hearing [] |
815 (.start | |
816 (let [node1 (AudioNode. (asset-manager) "Sounds/pure.wav" false false)] | 816 (let [node1 (AudioNode. (asset-manager) "Sounds/pure.wav" false false)] |
817 (world | 817 (world |
818 (Node.) | 818 (Node.) |
819 {"key-space" (partial play-sound node1)} | 819 {"key-space" (partial play-sound node1)} |
820 setup-fn | 820 setup-fn |
821 no-op)))) | 821 no-op))) |
822 | |
823 (defn test-advanced-hearing | |
824 "Testing hearing: | |
825 You should see a blue sphere flying around several | |
826 cubes. As the sphere approaches each cube, it turns | |
827 green." | |
828 [] | |
829 (doto (com.aurellem.capture.examples.Advanced.) | |
830 (.setSettings | |
831 (doto (AppSettings. true) | |
832 (.setAudioRenderer "Send"))) | |
833 (.setShowSettings false) | |
834 (.setPauseOnLostFocus false))) | |
835 | |
822 #+end_src | 836 #+end_src |
823 | 837 |
824 This extremely basic program prints out the first sample it encounters | 838 This extremely basic program prints out the first sample it encounters |
825 at every time stamp. You can see the rendered sound begin printed at | 839 at every time stamp. You can see the rendered sound being printed at |
826 the REPL. | 840 the REPL. |
827 | 841 |
828 - As a bonus, this method of capturing audio for AI can also be used | 842 - As a bonus, this method of capturing audio for AI can also be used |
829 to capture perfect audio from a jMonkeyEngine application, for use | 843 to capture perfect audio from a jMonkeyEngine application, for use |
830 in demos and the like. | 844 in demos and the like. |
834 | 848 |
835 #+begin_src clojure :tangle ../../cortex/src/cortex/hearing.clj | 849 #+begin_src clojure :tangle ../../cortex/src/cortex/hearing.clj |
836 <<ears>> | 850 <<ears>> |
837 #+end_src | 851 #+end_src |
838 | 852 |
839 #+begin_src clojure :tangle ../../cortex/src/test/hearing.clj | 853 #+begin_src clojure :tangle ../../cortex/src/cortex/test/hearing.clj |
840 <<test-hearing>> | 854 <<test-hearing>> |
841 #+end_src | 855 #+end_src |
842 | |
843 | 856 |
844 #+begin_src C :tangle ../Alc/backends/send.c | 857 #+begin_src C :tangle ../Alc/backends/send.c |
845 <<send-header>> | 858 <<send-header>> |
846 <<send-state>> | 859 <<send-state>> |
847 <<sync-macros>> | 860 <<sync-macros>> |