From 5803d98ddc2364ad6c33ee21400207bca33306d0 Mon Sep 17 00:00:00 2001
From: Brian Matzon
Date: Mon, 31 May 2004 12:42:09 +0000
Subject: [PATCH] FMusic done, including callbacks! Added synctest to fmod,
showing how to use FMusic
---
src/java/org/lwjgl/fmod/FMOD.java | 64 +++--
src/java/org/lwjgl/fmod/FMusic.java | 135 +++++++---
src/java/org/lwjgl/fmod/FSound.java | 129 ++++++----
src/java/org/lwjgl/test/fmod/MusicPlayer.java | 82 +++---
src/java/org/lwjgl/test/fmod/SyncTest.java | 242 ++++++++++++++++++
src/native/common/fmod/extfmod.cpp | 21 +-
src/native/common/fmod/extfmod.h | 24 +-
.../common/fmod/org_lwjgl_fmod_FMOD.cpp | 2 +-
.../common/fmod/org_lwjgl_fmod_FMusic.cpp | 101 +++++---
9 files changed, 633 insertions(+), 167 deletions(-)
create mode 100644 src/java/org/lwjgl/test/fmod/SyncTest.java
diff --git a/src/java/org/lwjgl/fmod/FMOD.java b/src/java/org/lwjgl/fmod/FMOD.java
index c700fc85..ea223b9a 100644
--- a/src/java/org/lwjgl/fmod/FMOD.java
+++ b/src/java/org/lwjgl/fmod/FMOD.java
@@ -33,9 +33,12 @@ package org.lwjgl.fmod;
import java.io.File;
import java.nio.FloatBuffer;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;
+import org.lwjgl.Sys;
+
/**
* $Id$
*
@@ -45,7 +48,7 @@ import java.util.StringTokenizer;
public class FMOD {
/** Array of hashmaps for callbacks */
- private static HashMap[] callbacks = new HashMap[14];
+ private static HashMap[] callbacks = new HashMap[17];
/** FMOD System level clear dsp unit */
static FSoundDSPUnit fmodClearUnit;
@@ -66,46 +69,56 @@ public class FMOD {
static FloatBuffer fmodFFTBuffer;
/** Type defining the music callback entries in callback hashmap array */
- public static final int FMUSIC_CALLBACK = 0;
+ public static final int FMUSIC_INSTCALLBACK = 0;
+
+ /** Type defining the music callback entries in callback hashmap array */
+ public static final int FMUSIC_ORDERCALLBACK = 1;
+
+ /** Type defining the music callback entries in callback hashmap array */
+ public static final int FMUSIC_ROWCALLBACK = 2;
+
+ /** Type defining the music callback entries in callback hashmap array */
+ public static final int FMUSIC_ZXXCALLBACK = 3;
+
/** Type defining the dsp callback entries in callback hashmap array */
- public static final int FSOUND_DSPCALLBACK = 1;
+ public static final int FSOUND_DSPCALLBACK = 4;
/** Type defining the stream callback entries in callback hashmap array */
- public static final int FSOUND_STREAMCALLBACK = 2;
+ public static final int FSOUND_STREAMCALLBACK = 5;
/** Type defining the alloc callback entries in callback hashmap array */
- public static final int FSOUND_ALLOCCALLBACK = 3;
+ public static final int FSOUND_ALLOCCALLBACK = 6;
/** Type defining the realloc callback entries in callback hashmap array */
- public static final int FSOUND_REALLOCCALLBACK = 4;
+ public static final int FSOUND_REALLOCCALLBACK = 7;
/** Type defining the free callback entries in callback hashmap array */
- public static final int FSOUND_FREECALLBACK = 5;
+ public static final int FSOUND_FREECALLBACK = 8;
/** Type defining the open callback entries in callback hashmap array */
- public static final int FSOUND_OPENCALLBACK = 6;
+ public static final int FSOUND_OPENCALLBACK = 9;
/** Type defining the close callback entries in callback hashmap array */
- public static final int FSOUND_CLOSECALLBACK = 7;
+ public static final int FSOUND_CLOSECALLBACK = 10;
/** Type defining the metadata callback entries in callback hashmap array */
- public static final int FSOUND_METADATACALLBACK = 8;
+ public static final int FSOUND_METADATACALLBACK = 11;
/** Type defining the read callback entries in callback hashmap array */
- public static final int FSOUND_READCALLBACK = 9;
+ public static final int FSOUND_READCALLBACK = 12;
/** Type defining the seek callback entries in callback hashmap array */
- public static final int FSOUND_SEEKCALLBACK = 10;
+ public static final int FSOUND_SEEKCALLBACK = 13;
/** Type defining the tell callback entries in callback hashmap array */
- public static final int FSOUND_TELLCALLBACK = 11;
+ public static final int FSOUND_TELLCALLBACK = 14;
/** Type defining the "end" callback entries in callback hashmap array */
- public static final int FSOUND_ENDCALLBACK = 12;
+ public static final int FSOUND_ENDCALLBACK = 15;
/** Type defining the "sync" callback entries in callback hashmap array */
- public static final int FSOUND_SYNCCALLBACK = 13;
+ public static final int FSOUND_SYNCCALLBACK = 16;
/** Have we been created? */
protected static boolean created;
@@ -304,10 +317,23 @@ public class FMOD {
* @param callbackHandler Object to register as the call back handler
*/
static void registerCallback(int type, long handle, Object handled, Object callbackHandler) {
+ Long callbackID = new Long(handle);
+ ArrayList callbackList = (ArrayList) callbacks[type].get(callbackID);
+
+ if (callbackList == null ) {
+ if (callbackHandler == null) {
+ Sys.log("No callbackhandlers registered for handle: " + handle);
+ } else {
+ callbackList = new ArrayList();
+ callbacks[type].put(callbackID, callbackList);
+ }
+ }
+
+ // are we going to add or remove from the list?
if(callbackHandler == null) {
- callbacks[type].remove(new Long(handle));
+ callbacks[type].remove(callbackID);
} else {
- callbacks[type].put(new Long(handle), new FMOD.WrappedCallback(handled, callbackHandler));
+ callbackList.add(new FMOD.WrappedCallback(handled, callbackHandler));
}
}
@@ -316,8 +342,8 @@ public class FMOD {
* @param handle Handle to native object being monitored
* @return Call back handler, or null if no such handler
*/
- static WrappedCallback getCallback(int type, long handle) {
- return (WrappedCallback) callbacks[type].get(new Long(handle));
+ static ArrayList getCallbacks(int type, long handle) {
+ return (ArrayList) callbacks[type].get(new Long(handle));
}
/**
diff --git a/src/java/org/lwjgl/fmod/FMusic.java b/src/java/org/lwjgl/fmod/FMusic.java
index 8c3c9925..c1937f98 100644
--- a/src/java/org/lwjgl/fmod/FMusic.java
+++ b/src/java/org/lwjgl/fmod/FMusic.java
@@ -32,7 +32,9 @@
package org.lwjgl.fmod;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.nio.IntBuffer;
+import java.util.ArrayList;
import org.lwjgl.fmod.callbacks.FMusicCallback;
@@ -107,27 +109,38 @@ public class FMusic {
*
*
*
- * @param name_or_data Name of song or data containing song to load (if loading from memory and not file). On PlayStation 2 data must be 16 byte aligned if loading from memory
+ * @param data containing song to load. On PlayStation 2 data must be 16 byte aligned if loading from memory
* @param offset Optional. 0 by default. If > 0, this value is used to specify an offset in a file, so fmod will seek before opening
* @param length Optional. 0 by default. If > 0, this value is used to specify the length of a memory block when using FSOUND_LOADMEMORY, or it is the length of a file or file segment if the offset parameter is used. On PlayStation 2 this must be 16 byte aligned for memory loading
* @param mode Mode for opening song. With module files, only FSOUND_LOADMEMORY, FSOUND_NONBLOCKING, FSOUND_LOOP_NORMAL, or FSOUND_LOOP_OFF are supported. For FSB files, FSOUND_2D, FSOUND_HW3D, FSOUND_FORCEMONO also work
* @param sampleList Optional. Buffer of sample indicies to load. Leave as Null if you want all samples to be loaded (default behaviour). See Remarks for more on this
* @return On success, a FMusicModule instance is returned. On failure, Null is returned
*/
- public static FMusicModule FMUSIC_LoadSongEx(ByteBuffer name_or_data, int offset, int length, int mode, IntBuffer sampleList) {
- long result = 0;
-
- if((mode & FSound.FSOUND_LOADMEMORY) == FSound.FSOUND_LOADMEMORY) {
- result = nFMUSIC_LoadSongEx(name_or_data, name_or_data.position(), offset, length, mode, (sampleList != null) ? sampleList : null, (sampleList != null) ? sampleList.position() : 0, (sampleList != null) ? sampleList.remaining() : 0);
- } else {
- byte[] data = new byte[name_or_data.remaining()];
- result = nFMUSIC_LoadSongEx(new String(data), offset, length, mode, (sampleList != null) ? sampleList : null, (sampleList != null) ? sampleList.position() : 0, (sampleList != null) ? sampleList.remaining() : 0);
- }
+ public static FMusicModule FMUSIC_LoadSongEx(ByteBuffer data, int offset, int length, int mode, IntBuffer sampleList) {
+ long result = nFMUSIC_LoadSongEx(data, data.position(), offset, length, mode, (sampleList != null) ? sampleList : null, (sampleList != null) ? sampleList.position() : 0, (sampleList != null) ? sampleList.remaining() : 0);
if(result != FMUSIC_TYPE_NONE) {
return new FMusicModule(result);
}
return null;
}
+
+ /**
+ * @see #FMUSIC_LoadSongEx(ByteBuffer, int, int, int, IntBuffer)
+ * @param name of song
+ * @param offset Optional. 0 by default. If > 0, this value is used to specify an offset in a file, so fmod will seek before opening
+ * @param length Optional. 0 by default. If > 0, this value is used to specify the length of a memory block when using FSOUND_LOADMEMORY, or it is the length of a file or file segment if the offset parameter is used. On PlayStation 2 this must be 16 byte aligned for memory loading
+ * @param mode Mode for opening song. With module files, only FSOUND_LOADMEMORY, FSOUND_NONBLOCKING, FSOUND_LOOP_NORMAL, or FSOUND_LOOP_OFF are supported. For FSB files, FSOUND_2D, FSOUND_HW3D, FSOUND_FORCEMONO also work
+ * @param sampleList Optional. Buffer of sample indicies to load. Leave as Null if you want all samples to be loaded (default behaviour). See Remarks for more on this
+ * @return On success, a FMusicModule instance is returned. On failure, Null is returned
+ */
+ public static FMusicModule FMUSIC_LoadSongEx(String name, int offset, int length, int mode, IntBuffer sampleList) {
+ long result = nFMUSIC_LoadSongEx(name, offset, length, mode, (sampleList != null) ? sampleList : null, (sampleList != null) ? sampleList.position() : 0, (sampleList != null) ? sampleList.remaining() : 0);
+
+ if(result != FMUSIC_TYPE_NONE) {
+ return new FMusicModule(result);
+ }
+ return null;
+ }
private static native long nFMUSIC_LoadSongEx(ByteBuffer data, int dataOffset, int offset, int length, int mode, IntBuffer sampleList, int bufferOffset, int remaining);
private static native long nFMUSIC_LoadSongEx(String name, int offset, int length, int mode, IntBuffer sampleList, int bufferOffset, int remaining);
@@ -148,8 +161,10 @@ public class FMusic {
*/
public static boolean FMUSIC_FreeSong(FMusicModule module) {
// when freeing a song, we automatically deregister any callbacks
- FMOD.registerCallback(FMOD.FMUSIC_CALLBACK, module.moduleHandle, null, null);
-
+ FMOD.registerCallback(FMOD.FMUSIC_INSTCALLBACK, module.moduleHandle, null, null);
+ FMOD.registerCallback(FMOD.FMUSIC_ORDERCALLBACK, module.moduleHandle, null, null);
+ FMOD.registerCallback(FMOD.FMUSIC_ROWCALLBACK, module.moduleHandle, null, null);
+ FMOD.registerCallback(FMOD.FMUSIC_ZXXCALLBACK, module.moduleHandle, null, null);
return nFMUSIC_FreeSong(module.moduleHandle);
}
private static native boolean nFMUSIC_FreeSong(long module);
@@ -204,10 +219,10 @@ public class FMusic {
* @return On success, true is returned. On failure, false is returned
*/
public static boolean FMUSIC_SetZxxCallback(FMusicModule module, FMusicCallback callback) {
- FMOD.registerCallback(FMOD.FMUSIC_CALLBACK, module.moduleHandle, module, callback);
- return nFMUSIC_SetZxxCallback(module.moduleHandle, callback);
+ FMOD.registerCallback(FMOD.FMUSIC_ZXXCALLBACK, module.moduleHandle, module, callback);
+ return nFMUSIC_SetZxxCallback(module.moduleHandle);
}
- private static native boolean nFMUSIC_SetZxxCallback(long module, FMusicCallback callback);
+ private static native boolean nFMUSIC_SetZxxCallback(long module);
/**
* Sets a user callback to occur on every row divisible by the rowstep parameter, played from a MOD, S3M, XM or IT file.
@@ -231,10 +246,10 @@ public class FMusic {
* @return On success, true is returned. On failure, false is returned
*/
public static boolean FMUSIC_SetRowCallback(FMusicModule module, FMusicCallback callback, int rowstep) {
- FMOD.registerCallback(FMOD.FMUSIC_CALLBACK, module.moduleHandle, module, callback);
- return nFMUSIC_SetRowCallback(module.moduleHandle, callback, rowstep);
+ FMOD.registerCallback(FMOD.FMUSIC_ROWCALLBACK, module.moduleHandle, module, callback);
+ return nFMUSIC_SetRowCallback(module.moduleHandle, rowstep);
}
- private static native boolean nFMUSIC_SetRowCallback(long module, FMusicCallback callback, int rowstep);
+ private static native boolean nFMUSIC_SetRowCallback(long module, int rowstep);
/**
* Sets a user callback to occur on every order divisible by the orderstep parameter, played from a MOD, S3M, XM or IT file
@@ -258,10 +273,10 @@ public class FMusic {
* @return On success, true is returned. On failure, false is returned
*/
public static boolean FMUSIC_SetOrderCallback(FMusicModule module, FMusicCallback callback, int orderstep) {
- FMOD.registerCallback(FMOD.FMUSIC_CALLBACK, module.moduleHandle, module, callback);
- return nFMUSIC_SetOrderCallback(module.moduleHandle, callback, orderstep);
+ FMOD.registerCallback(FMOD.FMUSIC_ORDERCALLBACK, module.moduleHandle, module, callback);
+ return nFMUSIC_SetOrderCallback(module.moduleHandle, orderstep);
}
- private static native boolean nFMUSIC_SetOrderCallback(long module, FMusicCallback callback, int orderstep);
+ private static native boolean nFMUSIC_SetOrderCallback(long module, int orderstep);
/**
* Sets a user callback to occur every time a instrument is played, triggered from a MOD, S3M, XM or IT file.
@@ -285,10 +300,10 @@ public class FMusic {
* @return On success, true is returned. On failure, false is returned
*/
public static boolean FMUSIC_SetInstCallback(FMusicModule module, FMusicCallback callback, int instrument) {
- FMOD.registerCallback(FMOD.FMUSIC_CALLBACK, module.moduleHandle, module, callback);
- return nFMUSIC_SetInstCallback(module.moduleHandle, callback, instrument);
+ FMOD.registerCallback(FMOD.FMUSIC_INSTCALLBACK, module.moduleHandle, module, callback);
+ return nFMUSIC_SetInstCallback(module.moduleHandle, instrument);
}
- private static native boolean nFMUSIC_SetInstCallback(long module, FMusicCallback callback, int instrument);
+ private static native boolean nFMUSIC_SetInstCallback(long module, int instrument);
/**
* Replaces a mod's sample with a sample definition specified.
@@ -712,10 +727,12 @@ public class FMusic {
* @param module Module to get the open state from
* @return On success, userdata set by FMUSIC_SetUserData is returned. On failure, Null is returned.
*/
- public static ByteBuffer FMUSIC_GetUserData(FMusicModule module) {
- return nFMUSIC_GetUserData(module.moduleHandle);
+ public static ByteBuffer FMUSIC_GetUserData(FMusicModule module, int capacity) {
+ ByteBuffer buffer = nFMUSIC_GetUserData(module.moduleHandle, capacity);
+ buffer.order(ByteOrder.nativeOrder());
+ return buffer;
}
- private static native ByteBuffer nFMUSIC_GetUserData(long module);
+ private static native ByteBuffer nFMUSIC_GetUserData(long module, int capacity);
/**
* This is the callback rutine called by the native implementation whenever a
@@ -724,10 +741,64 @@ public class FMusic {
* @param handle Handle to native object being monitored
* @param param parameter passed to callback
*/
- private static void music_callback(long modulehandle, int param) {
- // locate out callback and call it back
- FMOD.WrappedCallback wCallback = (FMOD.WrappedCallback) FMOD.getCallback(FMOD.FSOUND_DSPCALLBACK, modulehandle);
- FMusicCallback callback = (FMusicCallback) wCallback.callback;
- callback.FMUSIC_CALLBACK((FMusicModule) wCallback.handled, param);
+ public static void music_instcallback(long modulehandle, int param) {
+ // we got a callback - notify everybody
+ ArrayList handlers = FMOD.getCallbacks(FMOD.FMUSIC_INSTCALLBACK, modulehandle);
+ for(int i=0; i 0, this value is used to specify an offset in a file, so fmod will seek before opening. length must also be specified if this value is used.
* @param length Optional. 0 by default. If > 0, this value is used to specify the length of a memory block when using FSOUND_LOADMEMORY, or it is the length of a file or file segment if the offset parameter is used. On PlayStation 2 this must be 16 byte aligned for memory loading.
* @return On success, a sample is returned. On failure, NULL is returned.
*/
- public static FSoundSample FSOUND_Sample_Load(int index, ByteBuffer name_or_data, int inputmode, int offset, int length) {
- long result = 0;
-
- if((inputmode & FSound.FSOUND_LOADMEMORY) == FSound.FSOUND_LOADMEMORY) {
- result = nFSOUND_Sample_Load(index, name_or_data, name_or_data.position(), inputmode, offset, length);
- } else {
- byte[] data = new byte[name_or_data.remaining()];
- result = nFSOUND_Sample_Load(index, new String(data), inputmode, offset, length);
- }
+ public static FSoundSample FSOUND_Sample_Load(int index, ByteBuffer data, int inputmode, int offset, int length) {
+ long result = nFSOUND_Sample_Load(index, data, data.position(), inputmode, offset, length);
if(result != 0) {
return new FSoundSample(result);
}
return null;
}
+
+ /**
+ * @see #FSOUND_Sample_Load(int, ByteBuffer, int, int, int)
+ * @param index Sample pool index. See remarks for more on the sample pool.
+ * 0 or above - The absolute index into the sample pool. The pool will grow as the index gets larger. If a slot is already used it will be replaced.
+ * FSOUND_FREE - Let FSOUND select an arbitrary sample slot.
+ * FSOUND_UNMANAGED - Dont have this sample managed within fsounds sample management system
+ * @param name Name of sound file.
+ * @param inputmode Description of the data format, OR in the bits defined in FSOUND_MODES to describe the data being loaded.
+ * @param offset Optional. 0 by default. If > 0, this value is used to specify an offset in a file, so fmod will seek before opening. length must also be specified if this value is used.
+ * @param length Optional. 0 by default. If > 0, this value is used to specify the length of a memory block when using FSOUND_LOADMEMORY, or it is the length of a file or file segment if the offset parameter is used. On PlayStation 2 this must be 16 byte aligned for memory loading.
+ * @return On success, a sample is returned. On failure, NULL is returned.
+ */
+ public static FSoundSample FSOUND_Sample_Load(int index, String name, int inputmode, int offset, int length) {
+ long result = nFSOUND_Sample_Load(index, name, inputmode, offset, length);
+ if(result != 0) {
+ return new FSoundSample(result);
+ }
+ return null;
+ }
private static native long nFSOUND_Sample_Load(int index, ByteBuffer data, int dataOffset, int inputmode, int offset, int length);
private static native long nFSOUND_Sample_Load(int index, String name, int inputmode, int offset, int length);
@@ -3508,9 +3527,13 @@ public class FSound {
* @param param parameter passed to callback
*/
private static void dsp_callback(long dspHandle, ByteBuffer originalbuffer, ByteBuffer newbuffer, int length) {
- // locate out callback and call it back
- FSoundDSPCallback callback = (FSoundDSPCallback) ((FMOD.WrappedCallback) FMOD.getCallback(FMOD.FSOUND_DSPCALLBACK, dspHandle)).callback;
- callback.FSOUND_DSPCALLBACK(originalbuffer, newbuffer, length);
+ // we got a callback - notify everybody
+ ArrayList handlers = FMOD.getCallbacks(FMOD.FSOUND_DSPCALLBACK, dspHandle);
+ for(int i=0; i
+ *
+ * @author Brian Matzon
+ * @version $Revision$
+ */
+public class SyncTest {
+
+ /** Path to file to play */
+ private String filePath;
+
+ /** Module instance loaded */
+ private FMusicModule module;
+
+ /** Whether test is running */
+ private boolean running = true;
+
+ /** Current row */
+ private int row;
+
+ /** Current order */
+ private int order;
+
+ /** Number of orders in the song */
+ private int numOrders;
+
+ /**
+ * Creates a new SyncTest
+ * @param string
+ */
+ public SyncTest(String filePath) {
+ this.filePath = filePath;
+
+ // create thread to exit when a key has been pressed
+ Thread t = new Thread() {
+ public void run() {
+ try {
+ System.in.read();
+ } catch (IOException ioe) {
+ }
+ running = false;
+ }
+ };
+ t.setDaemon(true);
+ t.start();
+ }
+
+ /**
+ *
+ * @param args
+ */
+ public static void main(String[] args) {
+
+ // check for file existance
+ File file = new File(args[0]);
+ if (!file.exists()) {
+ System.out.println("No such file: " + args[0]);
+ return;
+ }
+
+ // initialize FMOD
+ try {
+ System.out.println("Initializing FMOD");
+ FMOD.create();
+ } catch (FMODException fmode) {
+ fmode.printStackTrace();
+ return;
+ }
+
+ // start actual test
+ SyncTest sync = new SyncTest(args[0]);
+ sync.executeTest();
+ }
+
+ /**
+ * Executes the test
+ */
+ public void executeTest() {
+ // do setup
+ setup();
+
+ // if we have a module - get going
+ if (module != null) {
+ // high priority... - might otherwise skip
+ Sys.setProcessPriority(Sys.HIGH_PRIORITY);
+
+ // go go go!
+ run();
+ }
+
+ // we're done - clean up
+ destroy();
+ }
+
+ /**
+ * Setup FMOD
+ */
+ private void setup() {
+ if (!FSound.FSOUND_Init(44100, 32, 0)) {
+ System.out.println("Failed to initialize FMOD");
+ System.out.println("Error: " + FMOD.FMOD_ErrorString(FSound.FSOUND_GetError()));
+ return;
+ }
+
+ // load module
+ System.out.println("Loading " + filePath);
+ module = FMusic.FMUSIC_LoadSong(filePath);
+
+ if (module == null) {
+ System.out.println("Unable to load " + filePath + ": " + FMOD.FMOD_ErrorString(FSound.FSOUND_GetError()));
+ return;
+ }
+
+ // get number of orders
+ numOrders = FMusic.FMUSIC_GetNumOrders(module);
+
+ // install order callback
+ FMusic.FMUSIC_SetOrderCallback(module, new FMusicCallback() {
+ public void FMUSIC_CALLBACK(FMusicModule module, int param) {
+ order = param;
+ }
+ }, 1);
+
+ // install row callback
+ // will be called once PER CHANNEL! - but since we only set the row
+ // no harm is done :)
+ FMusic.FMUSIC_SetRowCallback(module, new FMusicCallback() {
+ public void FMUSIC_CALLBACK(FMusicModule module, int param) {
+ row = param;
+ }
+ }, 1);
+
+ // try to add some userdata
+ ByteBuffer buffer = BufferUtils.createByteBuffer(24);
+ buffer.putInt(1).putInt(2).putInt(3).putInt(4).putInt(5).putInt(6).rewind();
+ FMusic.FMUSIC_SetUserData(module, buffer);
+ }
+
+ /**
+ * Update status of module - spew it out in console
+ */
+ private void update() {
+ // mark as not running when finished
+ if(FMusic.FMUSIC_IsFinished(module)) {
+ running = false;
+ }
+
+ int patternLength = FMusic.FMUSIC_GetPatternLength(module, order);
+ int time = FMusic.FMUSIC_GetTime(module) / 1000;
+ int time2 = FMusic.FMUSIC_GetTime(module) % 1000 / 10;
+ double cpu = Math.round(FSound.FSOUND_GetCPUUsage() * 100.0) / 100.0;
+
+ System.out.println("O: " +
+ ((order < 10) ? "0" : "") + order + "/" + numOrders + " | R: "
+ + ((row < 10) ? "0" : "") + row + "/" + patternLength + " | T: "
+ + time + "."
+ + ((time2 < 10) ? "0" : "") + time2 + " | BPM: " + FMusic.FMUSIC_GetBPM(module) + " | Play: " + FMusic.FMUSIC_IsFinished(module)
+ + " | C: " + FSound.FSOUND_GetChannelsPlaying() + " | CPU: " + cpu);
+ }
+
+ /**
+ * Runs the actual test - that is, play | update ad nausea
+ */
+ private void run() {
+ // play
+ FMusic.FMUSIC_PlaySong(module);
+
+ // loop, printing update, if we actually changed row
+ int lastRow = row;
+ while(running) {
+ if(lastRow != row) {
+ lastRow = row;
+ update();
+ } else {
+ Thread.yield();
+ }
+ }
+ }
+
+ // clean up our own mess
+ private void destroy() {
+ if(module != null) {
+ // retrieve userdata
+ ByteBuffer buffer = FMusic.FMUSIC_GetUserData(module, 24);
+
+ // should contain 1,2,3,4,5,6
+ for(int i=0; i<6; i++) {
+ System.out.println(buffer.getInt());
+ }
+
+ FMusic.FMUSIC_FreeSong(module);
+ }
+ FSound.FSOUND_Close();
+ FMOD.destroy();
+ }
+}
\ No newline at end of file
diff --git a/src/native/common/fmod/extfmod.cpp b/src/native/common/fmod/extfmod.cpp
index 17004019..9ad58cca 100644
--- a/src/native/common/fmod/extfmod.cpp
+++ b/src/native/common/fmod/extfmod.cpp
@@ -41,6 +41,17 @@
/** Instance of fmod */
FMOD_INSTANCE * fmod_instance = NULL;
+// jnienvs
+JNIEnv *mixer_jnienv;
+JNIEnv *stream_jnienv;
+
+// FMusic cached fields
+jmethodID music_instcallback;
+jmethodID music_ordercallback;
+jmethodID music_rowcallback;
+jmethodID music_zxxcallback;
+jclass fmusic;
+
#ifdef _WIN32
/**
* DLL entry point for Windows. Called when Java loads the .dll
@@ -55,8 +66,16 @@ BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
*
* @param path path to try to load dll
*/
-void fmod_create(char* path) {
+void fmod_create(JNIEnv *env, char* path) {
fmod_instance = FMOD_CreateInstance(path);
+
+ if (fmod_instance != NULL) {
+ fmusic = env->FindClass("org/lwjgl/fmod/FMusic");
+ music_instcallback = env->GetStaticMethodID(fmusic, "music_instcallback", "(JI)V");
+ music_ordercallback = env->GetStaticMethodID(fmusic, "music_ordercallback", "(JI)V");
+ music_rowcallback = env->GetStaticMethodID(fmusic, "music_rowcallback", "(JI)V");
+ music_zxxcallback = env->GetStaticMethodID(fmusic, "music_zxxcallback", "(JI)V");
+ }
}
/**
diff --git a/src/native/common/fmod/extfmod.h b/src/native/common/fmod/extfmod.h
index 1a319c7c..d303b24d 100644
--- a/src/native/common/fmod/extfmod.h
+++ b/src/native/common/fmod/extfmod.h
@@ -34,14 +34,30 @@
#define _EXT_FMOD_H
#include
-#include "common_tools.h"
+#include "../common_tools.h"
#include "fmoddyn.h"
#include "fmod_errors.h"
-extern FMOD_INSTANCE * fmod_instance;
-
-void fmod_create(char*);
+void fmod_create(JNIEnv *env, char*);
void fmod_destroy();
+extern FMOD_INSTANCE * fmod_instance;
+
+// Setup for callback. The callbacks don't have access to a JNIEnv pointer, so we have to provide
+// one. Unfortunately we cannot cache one, since JNIEnv er thread local copies. We can however
+// aquire one, using AttachCurrent. However we need a VM instance for that.
+// so we supply 1 VM instance for use by threads (in common_tools) (VM instances are shared across threads), and
+// 1 JNIEnv pointer per thread. At this time, 2 threads have been identified - the Stream thread
+// and the mixer thread.
+extern JNIEnv *mixer_jnienv;
+extern JNIEnv *stream_jnienv;
+
+// FMusic cached fields
+extern jmethodID music_instcallback;
+extern jmethodID music_ordercallback;
+extern jmethodID music_rowcallback;
+extern jmethodID music_zxxcallback;
+extern jclass fmusic;
+
#endif
diff --git a/src/native/common/fmod/org_lwjgl_fmod_FMOD.cpp b/src/native/common/fmod/org_lwjgl_fmod_FMOD.cpp
index a51da593..7dc0778a 100644
--- a/src/native/common/fmod/org_lwjgl_fmod_FMOD.cpp
+++ b/src/native/common/fmod/org_lwjgl_fmod_FMOD.cpp
@@ -55,7 +55,7 @@ JNIEXPORT void JNICALL Java_org_lwjgl_fmod_FMOD_nCreate(JNIEnv *env, jclass claz
jstring path = (jstring) env->GetObjectArrayElement(paths, i);
char *path_str = (char *) env->GetStringUTFChars(path, NULL);
printfDebug("Trying to load fmod_instance from %s\n", path_str);
- fmod_create(path_str);
+ fmod_create(env, path_str);
env->ReleaseStringUTFChars(path, path_str);
if(fmod_instance != NULL) {
diff --git a/src/native/common/fmod/org_lwjgl_fmod_FMusic.cpp b/src/native/common/fmod/org_lwjgl_fmod_FMusic.cpp
index 802cc6de..76179637 100644
--- a/src/native/common/fmod/org_lwjgl_fmod_FMusic.cpp
+++ b/src/native/common/fmod/org_lwjgl_fmod_FMusic.cpp
@@ -33,6 +33,12 @@
#include "org_lwjgl_fmod_FMusic.h"
#include "extfmod.h"
+// callback
+void F_CALLBACKAPI fmusic_instcallback(FMUSIC_MODULE *mod, unsigned char param);
+void F_CALLBACKAPI fmusic_ordercallback(FMUSIC_MODULE *mod, unsigned char param);
+void F_CALLBACKAPI fmusic_rowcallback(FMUSIC_MODULE *mod, unsigned char param);
+void F_CALLBACKAPI fmusic_zxxcallback(FMUSIC_MODULE *mod, unsigned char param);
+
/*
* Class: org_lwjgl_fmod_FMusic
* Method: nFMUSIC_LoadSong
@@ -130,32 +136,26 @@ JNIEXPORT void JNICALL Java_org_lwjgl_fmod_FMusic_FMUSIC_1StopAllSongs
* Method: nFMUSIC_SetZxxCallback
* Signature: (JLorg/lwjgl/fmod_instance/FMusicCallback;)Z
*/
-JNIEXPORT jboolean JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1SetZxxCallback
- (JNIEnv *env, jclass clazz, jlong, jobject){
- throwFMODException(env, "missing implementation");
- return false;
- }
+JNIEXPORT jboolean JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1SetZxxCallback(JNIEnv *env, jclass clazz, jlong module){
+ return fmod_instance->FMUSIC_SetZxxCallback((FMUSIC_MODULE*)module, fmusic_zxxcallback);
+}
/*
* Class: org_lwjgl_fmod_FMusic
* Method: nFMUSIC_SetRowCallback
* Signature: (JLorg/lwjgl/fmod_instance/FMusicCallback;I)Z
*/
-JNIEXPORT jboolean JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1SetRowCallback
- (JNIEnv *env, jclass clazz, jlong, jobject, jint){
- throwFMODException(env, "missing implementation");
- return false;
- }
+JNIEXPORT jboolean JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1SetRowCallback(JNIEnv *env, jclass clazz, jlong module, jint row) {
+ return fmod_instance->FMUSIC_SetRowCallback((FMUSIC_MODULE*)module, fmusic_rowcallback, row);
+}
/*
* Class: org_lwjgl_fmod_FMusic
* Method: nFMUSIC_SetOrderCallback
* Signature: (JLorg/lwjgl/fmod_instance/FMusicCallback;I)Z
*/
-JNIEXPORT jboolean JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1SetOrderCallback
- (JNIEnv *env, jclass clazz, jlong, jobject, jint){
- throwFMODException(env, "missing implementation");
- return false;
+JNIEXPORT jboolean JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1SetOrderCallback(JNIEnv *env, jclass clazz, jlong module, jint order) {
+ return fmod_instance->FMUSIC_SetOrderCallback((FMUSIC_MODULE*)module, fmusic_ordercallback, order);
}
/*
@@ -163,11 +163,9 @@ JNIEXPORT jboolean JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1SetOrderCallback
* Method: nFMUSIC_SetInstCallback
* Signature: (JLorg/lwjgl/fmod_instance/FMusicCallback;I)Z
*/
-JNIEXPORT jboolean JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1SetInstCallback
- (JNIEnv *env, jclass clazz, jlong, jobject, jint){
- throwFMODException(env, "missing implementation");
- return false;
- }
+JNIEXPORT jboolean JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1SetInstCallback(JNIEnv *env, jclass clazz, jlong module, jint inst){
+ return fmod_instance->FMUSIC_SetInstCallback((FMUSIC_MODULE*)module, fmusic_instcallback, inst);
+}
/*
* Class: org_lwjgl_fmod_FMusic
@@ -184,11 +182,10 @@ JNIEXPORT jboolean JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1SetSample
* Method: nFMUSIC_SetUserData
* Signature: (JLjava/nio/ByteBuffer;I)Z
*/
-JNIEXPORT jboolean JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1SetUserData
- (JNIEnv *env, jclass clazz, jlong module, jobject data, jint offset){
- throwFMODException(env, "missing implementation");
- return false;
- }
+JNIEXPORT jboolean JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1SetUserData(JNIEnv *env, jclass clazz, jlong module, jobject data, jint offset) {
+ void *userdata = offset + (char*) env->GetDirectBufferAddress(data);
+ return fmod_instance->FMUSIC_SetUserData((FMUSIC_MODULE *) module, userdata);
+}
/*
* Class: org_lwjgl_fmod_FMusic
@@ -486,8 +483,54 @@ JNIEXPORT jint JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1GetRealChannel
* Method: nFMUSIC_GetUserData
* Signature: (J)Ljava/nio/ByteBuffer;
*/
-JNIEXPORT jobject JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1GetUserData
- (JNIEnv *env, jclass clazz, jlong module) {
- throwFMODException(env, "missing implementation");
- return NULL;
- }
+JNIEXPORT jobject JNICALL Java_org_lwjgl_fmod_FMusic_nFMUSIC_1GetUserData(JNIEnv *env, jclass clazz, jlong module, jint capacity) {
+ void* data = (void*) fmod_instance->FMUSIC_GetUserData((FMUSIC_MODULE *) module);
+ return env->NewDirectByteBuffer(data, capacity);
+}
+
+/**
+ * This attaches the mixer thread as a daemon thread, and sets its
+ * priority to max value
+ */
+void attachMixerThread() {
+ jvm->AttachCurrentThreadAsDaemon((void**) &mixer_jnienv, NULL);
+
+ // set to high priority
+ // ==============================
+ // get current thread
+ jclass threadClass = mixer_jnienv->FindClass("java/lang/Thread");
+ jmethodID currentThread = mixer_jnienv->GetStaticMethodID(threadClass, "currentThread", "()Ljava/lang/Thread;");
+ jobject myThread = mixer_jnienv->CallStaticObjectMethod(threadClass, currentThread);
+
+ // get value of high priority
+ jfieldID highPriority = mixer_jnienv->GetStaticFieldID(threadClass, "MAX_PRIORITY", "I");
+ jint highPriorityValue = mixer_jnienv->GetStaticIntField(threadClass, highPriority);
+
+ // call set priority
+ jmethodID priority = mixer_jnienv->GetMethodID(threadClass, "setPriority", "(I)V");
+ mixer_jnienv->CallVoidMethod(myThread, priority, highPriorityValue);
+ // ------------------------------
+}
+
+// FMusic callbacks
+// =======================================
+void F_CALLBACKAPI fmusic_instcallback(FMUSIC_MODULE *mod, unsigned char param) {
+ if (mixer_jnienv == NULL) { attachMixerThread(); }
+ mixer_jnienv->CallStaticVoidMethod(fmusic, music_instcallback, (jlong) mod, (jint) param);
+}
+
+void F_CALLBACKAPI fmusic_ordercallback(FMUSIC_MODULE *mod, unsigned char param) {
+ if (mixer_jnienv == NULL) { attachMixerThread(); }
+ mixer_jnienv->CallStaticVoidMethod(fmusic, music_ordercallback, (jlong) mod, (jint) param);
+}
+
+void F_CALLBACKAPI fmusic_rowcallback(FMUSIC_MODULE *mod, unsigned char param) {
+ if (mixer_jnienv == NULL) { attachMixerThread(); }
+ mixer_jnienv->CallStaticVoidMethod(fmusic, music_rowcallback, (jlong) mod, (jint) param);
+}
+
+void F_CALLBACKAPI fmusic_zxxcallback(FMUSIC_MODULE *mod, unsigned char param) {
+ if (mixer_jnienv == NULL) { attachMixerThread(); }
+ mixer_jnienv->CallStaticVoidMethod(fmusic, music_zxxcallback, (jlong) mod, (jint) param);
+}
+// ------------------------------------------
\ No newline at end of file