diff --git a/com/breakfastquay/rubberband/RubberBandStretcher.java b/com/breakfastquay/rubberband/RubberBandStretcher.java index 8469d62..036516d 100644 --- a/com/breakfastquay/rubberband/RubberBandStretcher.java +++ b/com/breakfastquay/rubberband/RubberBandStretcher.java @@ -23,6 +23,9 @@ package com.breakfastquay.rubberband; +import java.util.Map; +import java.util.Set; + public class RubberBandStretcher { public RubberBandStretcher(int sampleRate, int channels, @@ -45,6 +48,8 @@ public class RubberBandStretcher public native double getTimeRatio(); public native double getPitchScale(); + public native int getPreferredStartPad(); + public native int getStartDelay(); public native int getLatency(); public native void setTransientsOption(int options); @@ -54,11 +59,25 @@ public class RubberBandStretcher public native void setPitchOption(int options); public native void setExpectedInputDuration(long samples); + public native int getProcessSizeLimit(); public native void setMaxProcessSize(int samples); public native int getSamplesRequired(); - //!!! todo: setKeyFrameMap + public native void setKeyFrameMap(long[] from, long[] to); + public void setKeyFrameMap(Map m) { + Set keys = m.keySet(); + int n = keys.size(); + long[] from = new long[n]; + long[] to = new long[n]; + int i = 0; + for (Long k : keys) { + from[i] = k.longValue(); + to[i] = m.get(k).longValue(); + ++i; + } + setKeyFrameMap(from, to); + } public native void study(float[][] input, int offset, int n, boolean finalBlock); public void study(float[][] input, boolean finalBlock) { @@ -120,6 +139,9 @@ public class RubberBandStretcher public static final int OptionChannelsApart = 0x00000000; public static final int OptionChannelsTogether = 0x10000000; + public static final int OptionEngineFaster = 0x00000000; + public static final int OptionEngineFiner = 0x20000000; + public static final int DefaultOptions = 0x00000000; public static final int PercussiveOptions = 0x00102000; diff --git a/com/breakfastquay/rubberband/test/RubberBandTest.java b/com/breakfastquay/rubberband/test/RubberBandTest.java new file mode 100644 index 0000000..578f5ec --- /dev/null +++ b/com/breakfastquay/rubberband/test/RubberBandTest.java @@ -0,0 +1,110 @@ + +package com.breakfastquay.rubberband.test; + +import com.breakfastquay.rubberband.RubberBandStretcher; + +import java.util.TreeMap; + +public class RubberBandTest +{ + + public static void main(String[] args) { + + int channels = 1; + int rate = 44100; + + RubberBandStretcher stretcher = new RubberBandStretcher + (rate, + channels, + RubberBandStretcher.OptionEngineFiner + + RubberBandStretcher.OptionProcessOffline, + 1.0, + 1.0); + + stretcher.setTimeRatio(1.5); + stretcher.setPitchScale(0.8); + + System.err.println + (String.format("Channel count: %d\n" + + "Time ratio: %f\n" + + "Pitch scale: %f\n" + + "Preferred start pad: %d\n" + + "Start delay: %d\n" + + "Process size limit: %d", + stretcher.getChannelCount(), + stretcher.getTimeRatio(), + stretcher.getPitchScale(), + stretcher.getPreferredStartPad(), + stretcher.getStartDelay(), + stretcher.getProcessSizeLimit() + )); + + int blocksize = 1024; + int blocks = 400; + double freq = 440.0; + + stretcher.setMaxProcessSize(blocksize); + + TreeMap keyFrameMap = new TreeMap(); + keyFrameMap.put((long)(3 * rate), (long)(4 * rate)); + keyFrameMap.put((long)(5 * rate), (long)(5 * rate)); + stretcher.setKeyFrameMap(keyFrameMap); + + float[][] buffer = new float[channels][blocksize]; + + int i0 = 0; + + for (int block = 0; block < blocks; ++block) { + + for (int c = 0; c < channels; ++c) { + for (int i = 0; i < blocksize; ++i) { + buffer[c][i] = (float)Math.sin + ((double)i0 * freq * Math.PI * 2.0 / (double)rate); + if (i0 % rate == 0) { + buffer[c][i] = 1.f; + } + ++i0; + } + } + + stretcher.study(buffer, block + 1 == blocks); + } + + i0 = 0; + + for (int block = 0; block < blocks; ++block) { + + for (int c = 0; c < channels; ++c) { + for (int i = 0; i < blocksize; ++i) { + buffer[c][i] = (float)Math.sin + ((double)i0 * freq * Math.PI * 2.0 / (double)rate); + if (i0 % rate == 0) { + buffer[c][i] = 1.f; + } + ++i0; + } + } + + stretcher.process(buffer, block + 1 == blocks); + + while (true) { + int available = stretcher.available(); + if (available <= 0) { + break; + } + int requested = available; + if (requested > blocksize) { + requested = blocksize; + } + int obtained = stretcher.retrieve(buffer, 0, requested); + for (int i = 0; i < obtained; ++i) { + System.out.println(Float.toString(buffer[0][i])); + } + } + } + + stretcher.dispose(); + } + +} + diff --git a/meson.build b/meson.build index 16ad97c..ebf5a89 100644 --- a/meson.build +++ b/meson.build @@ -62,6 +62,10 @@ java_sources = [ 'com/breakfastquay/rubberband/RubberBandStretcher.java', ] +java_test_sources = [ + 'com/breakfastquay/rubberband/test/RubberBandTest.java', +] + program_sources = [ 'main/main.cpp', ] @@ -651,7 +655,8 @@ if have_jni # NB the JNI library is not versioned install: true, ) - jar('rubberband', 'com/breakfastquay/rubberband/RubberBandStretcher.java') + jar('rubberband', java_sources) + jar('rubberband-test', java_test_sources) else target_summary += { 'JNI library': false } if not have_java diff --git a/src/jni/RubberBandStretcherJNI.cpp b/src/jni/RubberBandStretcherJNI.cpp index e1e6cf9..9cac5fb 100644 --- a/src/jni/RubberBandStretcherJNI.cpp +++ b/src/jni/RubberBandStretcherJNI.cpp @@ -87,6 +87,22 @@ JNIEXPORT jdouble JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_ JNIEXPORT jdouble JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_getPitchScale (JNIEnv *, jobject); +/* + * Class: com_breakfastquay_rubberband_RubberBandStretcher + * Method: getPreferredStartPad + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_getPreferredStartPad + (JNIEnv *, jobject); + +/* + * Class: com_breakfastquay_rubberband_RubberBandStretcher + * Method: getStartDelay + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_getStartDelay + (JNIEnv *, jobject); + /* * Class: com_breakfastquay_rubberband_RubberBandStretcher * Method: getLatency @@ -151,6 +167,14 @@ JNIEXPORT void JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_set JNIEXPORT void JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_setMaxProcessSize (JNIEnv *, jobject, jint); +/* + * Class: com_breakfastquay_rubberband_RubberBandStretcher + * Method: getProcessSizeLimit + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_getProcessSizeLimit + (JNIEnv *, jobject); + /* * Class: com_breakfastquay_rubberband_RubberBandStretcher * Method: getSamplesRequired @@ -159,6 +183,14 @@ JNIEXPORT void JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_set JNIEXPORT jint JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_getSamplesRequired (JNIEnv *, jobject); +/* + * Class: com_breakfastquay_rubberband_RubberBandStretcher + * Method: setKeyFrameMap + * Signature: ([J[J)V + */ +JNIEXPORT void JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_setKeyFrameMap + (JNIEnv *, jobject, jlongArray, jlongArray); + /* * Class: com_breakfastquay_rubberband_RubberBandStretcher * Method: study @@ -269,6 +301,18 @@ Java_com_breakfastquay_rubberband_RubberBandStretcher_getPitchScale(JNIEnv *env, return getStretcher(env, obj)->getPitchScale(); } +JNIEXPORT jint JNICALL +Java_com_breakfastquay_rubberband_RubberBandStretcher_getPreferredStartPad(JNIEnv *env, jobject obj) +{ + return getStretcher(env, obj)->getPreferredStartPad(); +} + +JNIEXPORT jint JNICALL +Java_com_breakfastquay_rubberband_RubberBandStretcher_getStartDelay(JNIEnv *env, jobject obj) +{ + return getStretcher(env, obj)->getStartDelay(); +} + JNIEXPORT jint JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_getLatency(JNIEnv *env, jobject obj) { @@ -317,12 +361,34 @@ Java_com_breakfastquay_rubberband_RubberBandStretcher_setMaxProcessSize(JNIEnv * getStretcher(env, obj)->setMaxProcessSize(size); } +JNIEXPORT jint JNICALL +Java_com_breakfastquay_rubberband_RubberBandStretcher_getProcessSizeLimit(JNIEnv *env, jobject obj) +{ + return getStretcher(env, obj)->getProcessSizeLimit(); +} + JNIEXPORT jint JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_getSamplesRequired(JNIEnv *env, jobject obj) { return getStretcher(env, obj)->getSamplesRequired(); } +JNIEXPORT void JNICALL +Java_com_breakfastquay_rubberband_RubberBandStretcher_setKeyFrameMap(JNIEnv *env, jobject obj, jlongArray from, jlongArray to) +{ + std::map m; + int flen = env->GetArrayLength(from); + int tlen = env->GetArrayLength(to); + jlong *farr = env->GetLongArrayElements(from, 0); + jlong *tarr = env->GetLongArrayElements(to, 0); + for (int i = 0; i < flen && i < tlen; ++i) { + m[farr[i]] = tarr[i]; + } + env->ReleaseLongArrayElements(from, farr, 0); + env->ReleaseLongArrayElements(to, tarr, 0); + getStretcher(env, obj)->setKeyFrameMap(m); +} + JNIEXPORT void JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_study(JNIEnv *env, jobject obj, jobjectArray data, jint offset, jint n, jboolean final) {