ba1c946d8039 — Chris Cannam 6 months ago
Merge from branch keyframe-jni
M com/breakfastquay/rubberband/RubberBandStretcher.java +23 -1
@@ 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<Long, Long> m) {
+        Set<Long> 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;
 

          
A => com/breakfastquay/rubberband/test/RubberBandTest.java +110 -0
@@ 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<Long, Long> keyFrameMap = new TreeMap<Long, Long>();
+        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();
+    }
+    
+}
+

          
M meson.build +6 -1
@@ 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

          
M src/jni/RubberBandStretcherJNI.cpp +66 -0
@@ 89,6 89,22 @@ JNIEXPORT jdouble JNICALL Java_com_break
 
 /*
  * 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
  * Signature: ()I
  */

          
@@ 153,6 169,14 @@ JNIEXPORT void JNICALL Java_com_breakfas
 
 /*
  * 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
  * Signature: ()I
  */

          
@@ 161,6 185,14 @@ JNIEXPORT jint JNICALL Java_com_breakfas
 
 /*
  * 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
  * Signature: ([[FZ)V
  */

          
@@ 270,6 302,18 @@ Java_com_breakfastquay_rubberband_Rubber
 }
 
 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)
 {
     return getStretcher(env, obj)->getLatency();

          
@@ 318,12 362,34 @@ Java_com_breakfastquay_rubberband_Rubber
 }
 
 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<size_t, size_t> 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)
 {
     int channels = env->GetArrayLength(data);