From 4353ddd78da7b912e5f9de664ca89f6c82b03056 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Wed, 2 Oct 2024 11:29:01 +0100 Subject: [PATCH 1/6] Add live shifter to C API --- rubberband/RubberBandLiveShifter.h | 2 +- rubberband/rubberband-c.h | 64 +++++++++++++++++++++---- src/rubberband-c.cpp | 76 ++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 9 deletions(-) diff --git a/rubberband/RubberBandLiveShifter.h b/rubberband/RubberBandLiveShifter.h index dbc23d6b..1222adb2 100644 --- a/rubberband/RubberBandLiveShifter.h +++ b/rubberband/RubberBandLiveShifter.h @@ -102,7 +102,7 @@ RubberBandLiveShifter OptionFormantPreserved = 0x01000000, OptionChannelsApart = 0x00000000, - OptionChannelsTogether = 0x10000000, + OptionChannelsTogether = 0x10000000 // n.b. Options is int, so we must stop before 0x80000000 }; diff --git a/rubberband/rubberband-c.h b/rubberband/rubberband-c.h index 66d584bb..b24bd782 100644 --- a/rubberband/rubberband-c.h +++ b/rubberband/rubberband-c.h @@ -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_stretch(RubberBandState); 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, RubberBandOptions options); + +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 diff --git a/src/rubberband-c.cpp b/src/rubberband-c.cpp index 7c450b07..9289c6cd 100644 --- a/src/rubberband-c.cpp +++ b/src/rubberband-c.cpp @@ -23,6 +23,7 @@ #include "../rubberband/rubberband-c.h" #include "../rubberband/RubberBandStretcher.h" +#include "../rubberband/RubberBandLiveShifter.h" struct RubberBandState_ { @@ -197,3 +198,78 @@ void rubberband_set_default_debug_level(int 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); +} + +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); +} From 923a196f8fa8b2b8acacb2b5ea4c1145d6b8215c Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Wed, 2 Oct 2024 18:22:06 +0100 Subject: [PATCH 2/6] Add live shifter to JNI --- .hgignore | 1 + .../rubberband/RubberBandLiveShifter.java | 70 ++++++ .../rubberband/test/RubberBandTest.java | 86 ++++++- meson.build | 1 + otherbuilds/docker/Dockerfile.in | 32 +++ otherbuilds/docker/run.sh | 8 + rubberband/RubberBandLiveShifter.h | 3 + rubberband/rubberband-c.h | 2 +- src/jni/RubberBandStretcherJNI.cpp | 222 ++++++++++++++++-- src/rubberband-c.cpp | 5 + 10 files changed, 410 insertions(+), 20 deletions(-) create mode 100644 com/breakfastquay/rubberband/RubberBandLiveShifter.java create mode 100644 otherbuilds/docker/Dockerfile.in create mode 100755 otherbuilds/docker/run.sh diff --git a/.hgignore b/.hgignore index 9d0f7d98..c207c4d2 100644 --- a/.hgignore +++ b/.hgignore @@ -31,3 +31,4 @@ playlist-out/* formant-out-*/ out*.wav packages/ +otherbuilds/docker/Dockerfile diff --git a/com/breakfastquay/rubberband/RubberBandLiveShifter.java b/com/breakfastquay/rubberband/RubberBandLiveShifter.java new file mode 100644 index 00000000..3b83ef07 --- /dev/null +++ b/com/breakfastquay/rubberband/RubberBandLiveShifter.java @@ -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"); + } +}; + diff --git a/com/breakfastquay/rubberband/test/RubberBandTest.java b/com/breakfastquay/rubberband/test/RubberBandTest.java index 578f5ecc..0b13f9b0 100644 --- a/com/breakfastquay/rubberband/test/RubberBandTest.java +++ b/com/breakfastquay/rubberband/test/RubberBandTest.java @@ -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; @@ -72,6 +72,9 @@ public static void main(String[] args) { 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,88 @@ public static void main(String[] args) { } 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.err.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.err.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.err.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) { + exerciseStretcher(); + exerciseLiveShifter(); + } } diff --git a/meson.build b/meson.build index 1fabcc32..d4d845ee 100644 --- a/meson.build +++ b/meson.build @@ -63,6 +63,7 @@ jni_sources = [ java_sources = [ 'com/breakfastquay/rubberband/RubberBandStretcher.java', + 'com/breakfastquay/rubberband/RubberBandLiveShifter.java', ] java_test_sources = [ diff --git a/otherbuilds/docker/Dockerfile.in b/otherbuilds/docker/Dockerfile.in new file mode 100644 index 00000000..e810101f --- /dev/null +++ b/otherbuilds/docker/Dockerfile.in @@ -0,0 +1,32 @@ +FROM ubuntu:22.04 +MAINTAINER Chris Cannam +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 \ + meson \ + ninja-build \ + openjdk-8-jre \ + openjdk-8-jdk + +WORKDIR /root + +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 diff --git a/otherbuilds/docker/run.sh b/otherbuilds/docker/run.sh new file mode 100755 index 00000000..3f90be0d --- /dev/null +++ b/otherbuilds/docker/run.sh @@ -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 . + diff --git a/rubberband/RubberBandLiveShifter.h b/rubberband/RubberBandLiveShifter.h index 1222adb2..80811954 100644 --- a/rubberband/RubberBandLiveShifter.h +++ b/rubberband/RubberBandLiveShifter.h @@ -293,6 +293,9 @@ RubberBandLiveShifter * 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. */ diff --git a/rubberband/rubberband-c.h b/rubberband/rubberband-c.h index b24bd782..ec4638da 100644 --- a/rubberband/rubberband-c.h +++ b/rubberband/rubberband-c.h @@ -197,7 +197,7 @@ 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, 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); diff --git a/src/jni/RubberBandStretcherJNI.cpp b/src/jni/RubberBandStretcherJNI.cpp index fa5ce303..87aec2eb 100644 --- a/src/jni/RubberBandStretcherJNI.cpp +++ b/src/jni/RubberBandStretcherJNI.cpp @@ -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_breakfastquay_rubberband_RubberBandStretcher_ret 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_RubberBandStretcher_setKeyFrameMap(JNIEnv *env } 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(channels); - float **input = allocate(channels); + float **inbuf = allocate(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(channels); - float **input = allocate(channels); + float **inbuf = allocate(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_RubberBandStretcher_retrieve(JNIEnv *env, jobj 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(channels); + float **inbuf = allocate(channels); + float **outarr = allocate(channels); + float **outbuf = allocate(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); +} + + diff --git a/src/rubberband-c.cpp b/src/rubberband-c.cpp index 9289c6cd..64c23ed8 100644 --- a/src/rubberband-c.cpp +++ b/src/rubberband-c.cpp @@ -254,6 +254,11 @@ void rubberband_live_set_formant_option(RubberBandLiveState state, RubberBandOpt 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); From 1d28a7c43bd585f7cd60fe2ea7130aeaa0a4c8cd Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Thu, 3 Oct 2024 09:14:35 +0100 Subject: [PATCH 3/6] Get JNI build working in test --- otherbuilds/docker/Dockerfile.in | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/otherbuilds/docker/Dockerfile.in b/otherbuilds/docker/Dockerfile.in index e810101f..2cba31fb 100644 --- a/otherbuilds/docker/Dockerfile.in +++ b/otherbuilds/docker/Dockerfile.in @@ -1,5 +1,6 @@ FROM ubuntu:22.04 MAINTAINER Chris Cannam + RUN apt-get update && \ apt-get install -y \ software-properties-common \ @@ -13,13 +14,18 @@ RUN apt-get update && \ vamp-plugin-sdk \ libboost-test-dev \ mercurial \ - meson \ ninja-build \ - openjdk-8-jre \ - openjdk-8-jdk + plocate + +RUN apt-get install -y \ + openjdk-21-jdk WORKDIR /root +ADD https://github.com/mesonbuild/meson/releases/download/1.3.1/meson-1.3.1.tar.gz . +RUN tar xvf meson-1.3.1.tar.gz +RUN ln -s $(pwd)/meson-1.3.1/meson.py /usr/bin/meson + RUN hg clone -u [[REVISION]] https://hg.sr.ht/~breakfastquay/rubberband WORKDIR rubberband @@ -29,4 +35,5 @@ 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 From ab27a6485da55030d6e1a919616b5ccef192a7c4 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Thu, 3 Oct 2024 09:20:53 +0100 Subject: [PATCH 4/6] Use a newer Meson --- otherbuilds/docker/Dockerfile.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/otherbuilds/docker/Dockerfile.in b/otherbuilds/docker/Dockerfile.in index 2cba31fb..139a8400 100644 --- a/otherbuilds/docker/Dockerfile.in +++ b/otherbuilds/docker/Dockerfile.in @@ -22,9 +22,9 @@ RUN apt-get install -y \ WORKDIR /root -ADD https://github.com/mesonbuild/meson/releases/download/1.3.1/meson-1.3.1.tar.gz . -RUN tar xvf meson-1.3.1.tar.gz -RUN ln -s $(pwd)/meson-1.3.1/meson.py /usr/bin/meson +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 From 05acd2173849429a502f6dc465eac049f100d978 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Thu, 3 Oct 2024 09:21:07 +0100 Subject: [PATCH 5/6] Exercise JNI in the CI build as well --- .build.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.build.yml b/.build.yml index aac1bada..8ddb0dbf 100644 --- a/.build.yml +++ b/.build.yml @@ -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 From 28fa720e936b4a6b7890244e6e007be82d793520 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Thu, 3 Oct 2024 09:21:12 +0100 Subject: [PATCH 6/6] More output --- .../rubberband/test/RubberBandTest.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/com/breakfastquay/rubberband/test/RubberBandTest.java b/com/breakfastquay/rubberband/test/RubberBandTest.java index 0b13f9b0..726f323d 100644 --- a/com/breakfastquay/rubberband/test/RubberBandTest.java +++ b/com/breakfastquay/rubberband/test/RubberBandTest.java @@ -24,7 +24,7 @@ public static void exerciseStretcher() { 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" + @@ -107,7 +107,7 @@ public static void exerciseStretcher() { } } - System.err.println + System.out.println (String.format("in = %d, out = %d, rms = %f", blocksize * blocks, n, Math.sqrt(sqrtotal / (double)n))); @@ -127,7 +127,7 @@ public static void exerciseLiveShifter() { shifter.setPitchScale(0.8); - System.err.println + System.out.println (String.format("Channel count: %d\n" + "Pitch scale: %f\n" + "Block size: %d\n" + @@ -171,17 +171,20 @@ public static void exerciseLiveShifter() { } } - System.err.println + 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"); } }