69818820c36f — Chris Cannam 9 months ago
Merge from branch rblive
M .build.yml +9 -1
@@ 8,11 8,18 @@ packages:
   - lv2-dev
   - vamp-plugin-sdk
   - libboost-test-dev
-  - meson
   - ninja-build
+  - openjdk-21-jdk
+  - wget
 sources:
   - hg+https://hg.sr.ht/~breakfastquay/rubberband
 tasks:
+  - install-meson: |
+      mkdir -p tmp/meson
+      cd tmp/meson
+      wget https://github.com/mesonbuild/meson/releases/download/1.5.2/meson-1.5.2.tar.gz
+      tar xvf meson-1.5.2.tar.gz
+      sudo ln -s $(pwd)/meson-1.5.2/meson.py /usr/bin/meson
   - setup: |
       cd rubberband
       meson setup build

          
@@ 24,6 31,7 @@ tasks:
       cd rubberband
       ninja -C build
       meson test -C build
+      java -Djava.library.path=build -cp build/rubberband-test.jar com.breakfastquay.rubberband.test.RubberBandTest
       build/rubberband -V
       ninja -C build_speex
       meson test -C build_speex

          
M .hgignore +1 -0
@@ 31,3 31,4 @@ playlist-out/*
 formant-out-*/
 out*.wav
 packages/
+otherbuilds/docker/Dockerfile

          
A => com/breakfastquay/rubberband/RubberBandLiveShifter.java +70 -0
@@ 0,0 1,70 @@ 
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Rubber Band Library
+    An audio time-stretching and pitch-shifting library.
+    Copyright 2007-2022 Particular Programs Ltd.
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+
+    Alternatively, if you have a valid commercial licence for the
+    Rubber Band Library obtained by agreement with the copyright
+    holders, you may redistribute and/or modify it under the terms
+    described in that licence.
+
+    If you wish to distribute code using the Rubber Band Library
+    under terms other than those of the GNU General Public License,
+    you must obtain a valid commercial licence before doing so.
+*/
+
+package com.breakfastquay.rubberband;
+
+public class RubberBandLiveShifter
+{
+    public RubberBandLiveShifter(int sampleRate, int channels,
+                                 int options) {
+	handle = 0;
+	initialise(sampleRate, channels, options);
+    }
+
+    public native void dispose();
+
+    public native void reset();
+
+    public native void setPitchScale(double scale);
+
+    public native int getChannelCount();
+    public native double getPitchScale();
+
+    public native int getStartDelay();
+
+    public native void setFormantOption(int options);
+
+    public native int getBlockSize();
+    
+    public native void shift(float[][] input, int inOffset, float[][] output, int outOffset);
+    public void shift(float[][] input, float[][] output) {
+        shift(input, 0, output, 0);
+    }
+
+    private native void initialise(int sampleRate, int channels, int options);
+    private long handle;
+
+    public static final int OptionWindowShort          = 0x00000000;
+    public static final int OptionWindowMedium         = 0x00100000;
+
+    public static final int OptionFormantShifted       = 0x00000000;
+    public static final int OptionFormantPreserved     = 0x01000000;
+
+    public static final int OptionChannelsApart        = 0x00000000;
+    public static final int OptionChannelsTogether     = 0x10000000;
+
+    static {
+	System.loadLibrary("rubberband-jni");
+    }
+};
+

          
M com/breakfastquay/rubberband/test/RubberBandTest.java +86 -5
@@ 2,14 2,14 @@ 
 package com.breakfastquay.rubberband.test;
 
 import com.breakfastquay.rubberband.RubberBandStretcher;
+import com.breakfastquay.rubberband.RubberBandLiveShifter;
 
 import java.util.TreeMap;
 
 public class RubberBandTest
 {
-
-    public static void main(String[] args) {
-
+    public static void exerciseStretcher() {
+        
         int channels = 1;
         int rate = 44100;
         

          
@@ 24,7 24,7 @@ public class RubberBandTest
         stretcher.setTimeRatio(1.5);
         stretcher.setPitchScale(0.8);
         
-        System.err.println
+        System.out.println
             (String.format("Channel count: %d\n" +
                            "Time ratio: %f\n" +
                            "Pitch scale: %f\n" +

          
@@ 72,6 72,9 @@ public class RubberBandTest
 
         i0 = 0;
 
+        double sqrtotal = 0.0;
+        int n = 0;
+
         for (int block = 0; block < blocks; ++block) {
 
             for (int c = 0; c < channels; ++c) {

          
@@ 98,13 101,91 @@ public class RubberBandTest
                 }
                 int obtained = stretcher.retrieve(buffer, 0, requested);
                 for (int i = 0; i < obtained; ++i) {
-                    System.out.println(Float.toString(buffer[0][i]));
+                    sqrtotal += (double)(buffer[0][i] * buffer[0][i]);
+                    ++n;
                 }
             }
         }
+
+        System.out.println
+            (String.format("in = %d, out = %d, rms = %f",
+                           blocksize * blocks, n,
+                           Math.sqrt(sqrtotal / (double)n)));
         
         stretcher.dispose();
     }
+
+    public static void exerciseLiveShifter() {
+        
+        int channels = 1;
+        int rate = 44100;
+        
+        RubberBandLiveShifter shifter = new RubberBandLiveShifter
+            (rate,
+             channels,
+             0);
+
+        shifter.setPitchScale(0.8);
+        
+        System.out.println
+            (String.format("Channel count: %d\n" +
+                           "Pitch scale: %f\n" +
+                           "Block size: %d\n" +
+                           "Start delay: %d",
+                           shifter.getChannelCount(),
+                           shifter.getPitchScale(),
+                           shifter.getBlockSize(),
+                           shifter.getStartDelay()
+                ));
+
+        int blocksize = shifter.getBlockSize();
+        int blocks = 400;
+        double freq = 440.0;
+        
+        float[][] inbuf = new float[channels][blocksize];
+        float[][] outbuf = new float[channels][blocksize];
+
+        int i0 = 0;
+
+        double sqrtotal = 0.0;
+        int n = 0;
+
+        for (int block = 0; block < blocks; ++block) {
+
+            for (int c = 0; c < channels; ++c) {
+                for (int i = 0; i < blocksize; ++i) {
+                    inbuf[c][i] = (float)Math.sin
+                        ((double)i0 * freq * Math.PI * 2.0 / (double)rate);
+                    if (i0 % rate == 0) {
+                        inbuf[c][i] = 1.f;
+                    }
+                    ++i0;
+                }
+            }
+            
+            shifter.shift(inbuf, outbuf);
+
+            for (int i = 0; i < blocksize; ++i) {
+                sqrtotal += (double)(outbuf[0][i] * outbuf[0][i]);
+                ++n;
+            }
+        }
+
+        System.out.println
+            (String.format("in = %d, out = %d, rms = %f",
+                           blocksize * blocks, n,
+                           Math.sqrt(sqrtotal / (double)n)));
+
+        shifter.dispose();
+    }
+
+    public static void main(String[] args) {
+        System.out.println("Exercising RubberBandStretcher through JNI...");
+        exerciseStretcher();
+        System.out.println("Exercising RubberBandLiveShifter through JNI...");
+        exerciseLiveShifter();
+        System.out.println("Done");
+    }
     
 }
 

          
M meson.build +1 -0
@@ 63,6 63,7 @@ jni_sources = [
 
 java_sources = [
   'com/breakfastquay/rubberband/RubberBandStretcher.java',
+  'com/breakfastquay/rubberband/RubberBandLiveShifter.java',
 ]
 
 java_test_sources = [

          
A => otherbuilds/docker/Dockerfile.in +39 -0
@@ 0,0 1,39 @@ 
+FROM ubuntu:22.04
+MAINTAINER Chris Cannam <cannam@all-day-breakfast.com>
+
+RUN apt-get update && \
+    apt-get install -y \
+    software-properties-common \
+    build-essential \
+    pkg-config \
+    libsamplerate0-dev \
+    libsndfile1-dev \
+    libfftw3-dev \
+    ladspa-sdk \
+    lv2-dev \
+    vamp-plugin-sdk \
+    libboost-test-dev \
+    mercurial \
+    ninja-build \
+    plocate
+    
+RUN apt-get install -y \
+    openjdk-21-jdk
+
+WORKDIR /root
+
+ADD https://github.com/mesonbuild/meson/releases/download/1.5.2/meson-1.5.2.tar.gz .
+RUN tar xvf meson-1.5.2.tar.gz
+RUN ln -s $(pwd)/meson-1.5.2/meson.py /usr/bin/meson
+
+RUN hg clone -u [[REVISION]] https://hg.sr.ht/~breakfastquay/rubberband
+
+WORKDIR rubberband
+
+RUN meson setup build
+RUN ninja -C build
+RUN meson test -C build
+
+WORKDIR build
+
+RUN java -Djava.library.path=$(pwd) -cp rubberband-test.jar com.breakfastquay.rubberband.test.RubberBandTest

          
A => otherbuilds/docker/run.sh +8 -0
@@ 0,0 1,8 @@ 
+#!/bin/sh
+
+revision=$(hg id | sed 's/[^0-9a-z].*$//')
+
+cat Dockerfile.in | perl -p -e "s/\[\[REVISION\]\]/$revision/g" > Dockerfile
+
+sudo docker build -f Dockerfile .
+

          
M rubberband/RubberBandLiveShifter.h +4 -1
@@ 102,7 102,7 @@ public:
         OptionFormantPreserved     = 0x01000000,
 
         OptionChannelsApart        = 0x00000000,
-        OptionChannelsTogether     = 0x10000000,
+        OptionChannelsTogether     = 0x10000000
 
         // n.b. Options is int, so we must stop before 0x80000000
     };

          
@@ 293,6 293,9 @@ public:
      * array having enough room to store n samples where n is the value
      * returned by getBlockSize().
      *
+     * The input and output must be separate arrays; they cannot alias
+     * one another or overlap.
+     *
      * Sample values are conventionally expected to be in the range
      * -1.0f to +1.0f.
      */

          
M rubberband/rubberband-c.h +56 -8
@@ 48,11 48,13 @@ extern "C" {
  * This is a C-linkage interface to the Rubber Band time stretcher.
  * 
  * This is a wrapper interface: the primary interface is in C++ and is
- * defined and documented in RubberBandStretcher.h.  The library
- * itself is implemented in C++, and requires C++ standard library
- * support even when using the C-linkage API.
+ * defined and documented in RubberBandStretcher.h and
+ * RubberBandLiveShifter.h.  The library itself is implemented in C++,
+ * and requires C++ standard library support even when using the
+ * C-linkage API.
  *
- * Please see RubberBandStretcher.h for documentation.
+ * Please see RubberBandStretcher.h and RubberBandLiveShifter.h for
+ * documentation.
  *
  * If you are writing to the C++ API, do not include this header.
  */

          
@@ 107,10 109,10 @@ struct RubberBandState_;
 typedef struct RubberBandState_ *RubberBandState;
 
 RB_EXTERN RubberBandState rubberband_new(unsigned int sampleRate,
-                                      unsigned int channels,
-                                      RubberBandOptions options,
-                                      double initialTimeRatio,
-                                      double initialPitchScale);
+                                         unsigned int channels,
+                                         RubberBandOptions options,
+                                         double initialTimeRatio,
+                                         double initialPitchScale);
 
 RB_EXTERN void rubberband_delete(RubberBandState);
 

          
@@ 159,6 161,52 @@ RB_EXTERN void rubberband_calculate_stre
 RB_EXTERN void rubberband_set_debug_level(RubberBandState, int level);
 RB_EXTERN void rubberband_set_default_debug_level(int level);
 
+
+enum RubberBandLiveOption {
+
+    RubberBandLiveOptionWindowShort      = 0x00000000,
+    RubberBandLiveOptionWindowMedium     = 0x00100000,
+
+    RubberBandLiveOptionFormantShifted   = 0x00000000,
+    RubberBandLiveOptionFormantPreserved = 0x01000000,
+
+    RubberBandLiveOptionChannelsApart    = 0x00000000,
+    RubberBandLiveOptionChannelsTogether = 0x10000000
+};
+
+typedef int RubberBandLiveOptions;
+
+struct RubberBandLiveState_;
+typedef struct RubberBandLiveState_ *RubberBandLiveState;
+
+RB_EXTERN RubberBandLiveState rubberband_live_new(unsigned int sampleRate,
+                                                  unsigned int channels,
+                                                  RubberBandOptions options);
+
+RB_EXTERN void rubberband_live_delete(RubberBandLiveState);
+
+RB_EXTERN void rubberband_live_reset(RubberBandLiveState);
+
+RB_EXTERN void rubberband_live_set_pitch_scale(RubberBandLiveState, double scale);
+RB_EXTERN double rubberband_live_get_pitch_scale(const RubberBandLiveState);
+
+RB_EXTERN void rubberband_live_set_formant_scale(RubberBandLiveState, double scale);
+RB_EXTERN double rubberband_live_get_formant_scale(const RubberBandLiveState);
+
+RB_EXTERN unsigned int rubberband_live_get_start_delay(const RubberBandLiveState);
+
+RB_EXTERN void rubberband_live_set_formant_option(RubberBandLiveState, RubberBandOptions options);
+
+RB_EXTERN unsigned int rubberband_live_get_block_size(RubberBandLiveState);
+
+RB_EXTERN void rubberband_live_shift(RubberBandLiveState, const float *const *input, float *const *output);
+
+RB_EXTERN unsigned int rubberband_live_get_channel_count(const RubberBandLiveState);
+
+RB_EXTERN void rubberband_live_set_debug_level(RubberBandLiveState, int level);
+RB_EXTERN void rubberband_live_set_default_debug_level(int level);
+
+    
 #ifdef __cplusplus
 }
 #endif

          
M src/jni/RubberBandStretcherJNI.cpp +207 -15
@@ 22,6 22,7 @@ 
 */
 
 #include "rubberband/RubberBandStretcher.h"
+#include "rubberband/RubberBandLiveShifter.h"
 
 #include "common/Allocators.h"
 

          
@@ 231,6 232,86 @@ JNIEXPORT jint JNICALL Java_com_breakfas
 JNIEXPORT void JNICALL Java_com_breakfastquay_rubberband_RubberBandStretcher_initialise
   (JNIEnv *, jobject, jint, jint, jint, jdouble, jdouble);
 
+/*
+ * Class:     com_breakfastquay_rubberband_RubberBandLiveShifter
+ * Method:    dispose
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_com_breakfastquay_rubberband_RubberBandLiveShifter_dispose
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     com_breakfastquay_rubberband_RubberBandLiveShifter
+ * Method:    reset
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_com_breakfastquay_rubberband_RubberBandLiveShifter_reset
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     com_breakfastquay_rubberband_RubberBandLiveShifter
+ * Method:    setPitchScale
+ * Signature: (D)V
+ */
+JNIEXPORT void JNICALL Java_com_breakfastquay_rubberband_RubberBandLiveShifter_setPitchScale
+  (JNIEnv *, jobject, jdouble);
+
+/*
+ * Class:     com_breakfastquay_rubberband_RubberBandLiveShifter
+ * Method:    getChannelCount
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_com_breakfastquay_rubberband_RubberBandLiveShifter_getChannelCount
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     com_breakfastquay_rubberband_RubberBandLiveShifter
+ * Method:    getPitchScale
+ * Signature: ()D
+ */
+JNIEXPORT jdouble JNICALL Java_com_breakfastquay_rubberband_RubberBandLiveShifter_getPitchScale
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     com_breakfastquay_rubberband_RubberBandLiveShifter
+ * Method:    getStartDelay
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_com_breakfastquay_rubberband_RubberBandLiveShifter_getStartDelay
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     com_breakfastquay_rubberband_RubberBandLiveShifter
+ * Method:    setFormantOption
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_com_breakfastquay_rubberband_RubberBandLiveShifter_setFormantOption
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     com_breakfastquay_rubberband_RubberBandLiveShifter
+ * Method:    getBlockSize
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_com_breakfastquay_rubberband_RubberBandLiveShifter_getBlockSize
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     com_breakfastquay_rubberband_RubberBandLiveShifter
+ * Method:    shift
+ * Signature: ([[FI[[FI)V
+ */
+JNIEXPORT void JNICALL Java_com_breakfastquay_rubberband_RubberBandLiveShifter_shift
+(JNIEnv *, jobject, jobjectArray, jint, jobjectArray, jint);
+
+/*
+ * Class:     com_breakfastquay_rubberband_RubberBandLiveShifter
+ * Method:    initialise
+ * Signature: (III)V
+ */
+JNIEXPORT void JNICALL Java_com_breakfastquay_rubberband_RubberBandLiveShifter_initialise
+  (JNIEnv *, jobject, jint, jint, jint);
+
 }
 
 RubberBandStretcher *

          
@@ 390,45 471,48 @@ Java_com_breakfastquay_rubberband_Rubber
 }
 
 JNIEXPORT void JNICALL
-Java_com_breakfastquay_rubberband_RubberBandStretcher_study(JNIEnv *env, jobject obj, jobjectArray data, jint offset, jint n, jboolean final)
+Java_com_breakfastquay_rubberband_RubberBandStretcher_study(JNIEnv *env, jobject obj, jobjectArray input, jint offset, jint n, jboolean final)
 {
-    int channels = env->GetArrayLength(data);
+    int channels = env->GetArrayLength(input);
     float **arr = allocate<float *>(channels);
-    float **input = allocate<float *>(channels);
+    float **inbuf = allocate<float *>(channels);
     for (int c = 0; c < channels; ++c) {
-        jfloatArray cdata = (jfloatArray)env->GetObjectArrayElement(data, c);
+        jfloatArray cdata = (jfloatArray)env->GetObjectArrayElement(input, c);
         arr[c] = env->GetFloatArrayElements(cdata, 0);
-        input[c] = arr[c] + offset;
+        inbuf[c] = arr[c] + offset;
     }
 
-    getStretcher(env, obj)->study(input, n, final);
+    getStretcher(env, obj)->study(inbuf, n, final);
 
     for (int c = 0; c < channels; ++c) {
-        jfloatArray cdata = (jfloatArray)env->GetObjectArrayElement(data, c);
+        jfloatArray cdata = (jfloatArray)env->GetObjectArrayElement(input, c);
         env->ReleaseFloatArrayElements(cdata, arr[c], 0);
     }
+
+    deallocate(inbuf);
+    deallocate(arr);
 }
 
 JNIEXPORT void JNICALL
-Java_com_breakfastquay_rubberband_RubberBandStretcher_process(JNIEnv *env, jobject obj, jobjectArray data, jint offset, jint n, jboolean final)
+Java_com_breakfastquay_rubberband_RubberBandStretcher_process(JNIEnv *env, jobject obj, jobjectArray input, jint offset, jint n, jboolean final)
 {
-    int channels = env->GetArrayLength(data);
+    int channels = env->GetArrayLength(input);
     float **arr = allocate<float *>(channels);
-    float **input = allocate<float *>(channels);
+    float **inbuf = allocate<float *>(channels);
     for (int c = 0; c < channels; ++c) {
-        jfloatArray cdata = (jfloatArray)env->GetObjectArrayElement(data, c);
+        jfloatArray cdata = (jfloatArray)env->GetObjectArrayElement(input, c);
         arr[c] = env->GetFloatArrayElements(cdata, 0);
-        input[c] = arr[c] + offset;
+        inbuf[c] = arr[c] + offset;
     }
 
-    getStretcher(env, obj)->process(input, n, final);
+    getStretcher(env, obj)->process(inbuf, n, final);
 
     for (int c = 0; c < channels; ++c) {
-        jfloatArray cdata = (jfloatArray)env->GetObjectArrayElement(data, c);
+        jfloatArray cdata = (jfloatArray)env->GetObjectArrayElement(input, c);
         env->ReleaseFloatArrayElements(cdata, arr[c], 0);
     }
 
-    deallocate(input);
+    deallocate(inbuf);
     deallocate(arr);
 }
 

          
@@ 456,3 540,111 @@ Java_com_breakfastquay_rubberband_Rubber
     return retrieved;
 }
 
+RubberBandLiveShifter *
+getLiveShifter(JNIEnv *env, jobject obj)
+{
+    jclass c = env->GetObjectClass(obj);
+    jfieldID fid = env->GetFieldID(c, "handle", "J");
+    jlong handle = env->GetLongField(obj, fid);
+    return (RubberBandLiveShifter *)handle;
+}
+
+void
+setLiveShifter(JNIEnv *env, jobject obj, RubberBandLiveShifter *stretcher)
+{
+    jclass c = env->GetObjectClass(obj);
+    jfieldID fid = env->GetFieldID(c, "handle", "J");
+    jlong handle = (jlong)stretcher;
+    env->SetLongField(obj, fid, handle);
+}
+
+JNIEXPORT void JNICALL
+Java_com_breakfastquay_rubberband_RubberBandLiveShifter_initialise(JNIEnv *env, jobject obj, jint sampleRate, jint channels, jint options)
+{
+    setLiveShifter(env, obj, new RubberBandLiveShifter
+                   (sampleRate, channels, options));
+}
+
+JNIEXPORT void JNICALL
+Java_com_breakfastquay_rubberband_RubberBandLiveShifter_dispose(JNIEnv *env, jobject obj)
+{
+    delete getLiveShifter(env, obj);
+    setLiveShifter(env, obj, 0);
+}
+
+JNIEXPORT void JNICALL
+Java_com_breakfastquay_rubberband_RubberBandLiveShifter_reset(JNIEnv *env, jobject obj)
+{
+    getLiveShifter(env, obj)->reset();
+}
+
+JNIEXPORT void JNICALL
+Java_com_breakfastquay_rubberband_RubberBandLiveShifter_setPitchScale(JNIEnv *env, jobject obj, jdouble scale)
+{
+    getLiveShifter(env, obj)->setPitchScale(scale);
+}
+
+JNIEXPORT jint JNICALL
+Java_com_breakfastquay_rubberband_RubberBandLiveShifter_getChannelCount(JNIEnv *env, jobject obj)
+{
+    return getLiveShifter(env, obj)->getChannelCount();
+}
+
+JNIEXPORT jdouble JNICALL
+Java_com_breakfastquay_rubberband_RubberBandLiveShifter_getPitchScale(JNIEnv *env, jobject obj)
+{
+    return getLiveShifter(env, obj)->getPitchScale();
+}
+
+JNIEXPORT jint JNICALL
+Java_com_breakfastquay_rubberband_RubberBandLiveShifter_getStartDelay(JNIEnv *env, jobject obj)
+{
+    return getLiveShifter(env, obj)->getStartDelay();
+}
+
+JNIEXPORT void JNICALL
+Java_com_breakfastquay_rubberband_RubberBandLiveShifter_setFormantOption(JNIEnv *env, jobject obj, jint options)
+{
+    getLiveShifter(env, obj)->setFormantOption(options);
+}
+
+JNIEXPORT jint JNICALL
+Java_com_breakfastquay_rubberband_RubberBandLiveShifter_getBlockSize(JNIEnv *env, jobject obj)
+{
+    return getLiveShifter(env, obj)->getBlockSize();
+}
+
+JNIEXPORT void JNICALL
+Java_com_breakfastquay_rubberband_RubberBandLiveShifter_shift(JNIEnv *env, jobject obj, jobjectArray input, jint inOffset, jobjectArray output, jint outOffset)
+{
+    int channels = env->GetArrayLength(input);
+    float **inarr = allocate<float *>(channels);
+    float **inbuf = allocate<float *>(channels);
+    float **outarr = allocate<float *>(channels);
+    float **outbuf = allocate<float *>(channels);
+
+    for (int c = 0; c < channels; ++c) {
+        jfloatArray cdata = (jfloatArray)env->GetObjectArrayElement(input, c);
+        inarr[c] = env->GetFloatArrayElements(cdata, 0);
+        inbuf[c] = inarr[c] + inOffset;
+        cdata = (jfloatArray)env->GetObjectArrayElement(output, c);
+        outarr[c] = env->GetFloatArrayElements(cdata, 0);
+        outbuf[c] = outarr[c] + outOffset;
+    }
+
+    getLiveShifter(env, obj)->shift(inbuf, outbuf);
+
+    for (int c = 0; c < channels; ++c) {
+        jfloatArray cdata = (jfloatArray)env->GetObjectArrayElement(input, c);
+        env->ReleaseFloatArrayElements(cdata, inarr[c], 0);
+        cdata = (jfloatArray)env->GetObjectArrayElement(output, c);
+        env->ReleaseFloatArrayElements(cdata, outarr[c], 0);
+    }
+
+    deallocate(inbuf);
+    deallocate(inarr);
+    deallocate(outbuf);
+    deallocate(outarr);
+}
+
+

          
M src/rubberband-c.cpp +81 -0
@@ 23,6 23,7 @@ 
 
 #include "../rubberband/rubberband-c.h"
 #include "../rubberband/RubberBandStretcher.h"
+#include "../rubberband/RubberBandLiveShifter.h"
 
 struct RubberBandState_
 {

          
@@ 197,3 198,83 @@ void rubberband_set_default_debug_level(
     RubberBand::RubberBandStretcher::setDefaultDebugLevel(level);
 }
 
+struct RubberBandLiveState_
+{
+    RubberBand::RubberBandLiveShifter *m_s;
+};
+
+RubberBandLiveState rubberband_live_new(unsigned int sampleRate,
+                                        unsigned int channels,
+                                        RubberBandOptions options)
+{
+    RubberBandLiveState_ *state = new RubberBandLiveState_();
+    state->m_s = new RubberBand::RubberBandLiveShifter
+        (sampleRate, channels, options);
+    return state;
+}
+
+void rubberband_live_delete(RubberBandLiveState state)
+{
+    delete state->m_s;
+    delete state;
+}
+
+void rubberband_live_reset(RubberBandLiveState state)
+{
+    state->m_s->reset();
+}
+
+void rubberband_live_set_pitch_scale(RubberBandLiveState state, double scale)
+{
+    state->m_s->setPitchScale(scale);
+}
+
+double rubberband_live_get_pitch_scale(const RubberBandLiveState state)
+{
+    return state->m_s->getPitchScale();
+}
+
+void rubberband_live_set_formant_scale(RubberBandLiveState state, double scale)
+{
+    state->m_s->setFormantScale(scale);
+}
+
+double rubberband_live_get_formant_scale(const RubberBandLiveState state)
+{
+    return state->m_s->getFormantScale();
+}
+
+unsigned int rubberband_live_get_start_delay(const RubberBandLiveState state) 
+{
+    return (unsigned int)state->m_s->getStartDelay();
+}
+
+void rubberband_live_set_formant_option(RubberBandLiveState state, RubberBandOptions options)
+{
+    state->m_s->setFormantOption(options);
+}
+
+unsigned int rubberband_live_get_block_size(RubberBandLiveState state)
+{
+    return (unsigned int)state->m_s->getBlockSize();
+}
+
+void rubberband_live_shift(RubberBandLiveState state, const float *const *input, float *const *output)
+{
+    state->m_s->shift(input, output);
+}
+
+unsigned int rubberband_live_get_channel_count(const RubberBandLiveState state)
+{
+    return (unsigned int)state->m_s->getChannelCount();
+}
+
+void rubberband_live_set_debug_level(RubberBandLiveState state, int level)
+{
+    state->m_s->setDebugLevel(level);
+}
+
+void rubberband_live_set_default_debug_level(int level)
+{
+    RubberBand::RubberBandStretcher::setDefaultDebugLevel(level);
+}