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