Rubber Band with JUCE and CMake

Is there an easy way to integrate the Rubber Band TimeStretch library in a JUCE plugin built with CMake?

Unfortunately, it looks like they are using another build system and have configurable external libraries. I would prefer to build always from the source instead of using the compiled library. I want to avoid building it for all platforms and install the required tools on every platform (maybe they are already available in compiled form?).

It hasn’t to be Rubber Band, but it looks like this is the most popular after zplane’s elastique that may also is an option.

Every input is welcome, thanks.

I have been been working on a project on the side which integrates Rubberband and had a situation similar to yours. I had just about got to grips with CMake, so didn’t want to start trying to grapple with yet another build system.

I found a CMakeLists file which integrates well with my project. I really wanted to provide details of the author, but honestly I can’t find the source again when trying to Google it, so unfortunately our hero must remain nameless :frowning:

Honestly, my CMake knowledge is still a bit sketchy, so I can’t help much beyond this - but for me it was as simple as adding this to my Rubberband subdir, and adding the line add_subdirectory(dependencies/rubberband) to my main CMakeLists.

Hope this helps!

cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(rubberband VERSION 2.1.1)

option(BUILD_EXECUTABLE "Build the Rubberband executable" OFF)
option(BUILD_JNI "Build the Java Native Interface (JNI)" OFF)
option(OPTIMIZE "Build Rubberband optimized for the build machine's CPU" OFF)
option(USE_FFTW "Use FFTW instead of KISS FFT" OFF)
option(USE_LIBSAMPLERATE "Use libsamplerate instead of Speex" ON)

file(GLOB rubberband_src
    src/audiocurves/*.*
    src/base/*.*
    src/dsp/*.*
    src/float_cast/*.*
    src/kissfft/*.*
    src/pommier/*.*
    src/speex/*.*
    src/system/*.*
    src/*.*
)

add_library(rubberband ${rubberband_src})

include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src)

if(BUILD_JNI)
  add_library(rubberband-jni src/jni/RubberBandStretcherJNI.cpp)
  target_include_directories(rubberband-jni PRIVATE src)

  find_package(JNI REQUIRED)
  target_include_directories(rubberband-jni PRIVATE ${JNI_INCLUDE_DIRS})
  target_link_libraries(rubberband-jni PRIVATE ${JNI_LIBRARIES})
endif()


#
# Dependencies
#

# Threads
find_package(Threads REQUIRED)
target_link_libraries(rubberband PRIVATE Threads::Threads)

# FFTW / KissFFT
if(USE_FFTW)
  message(STATUS "Using FFT library: fftw")
  find_path(FFTW3_INCLUDEDIR fftw3.h REQUIRED)
  find_library(FFTW3_LIBRARY fftw fftw3 fftw-3.3 REQUIRED)
  target_include_directories(rubberband PRIVATE ${FFTW3_INCLUDEDIR})
  target_link_libraries(rubberband PUBLIC ${FFTW3_LIBRARY})
  target_compile_definitions(rubberband PRIVATE HAVE_FFTW3 FFTW_DOUBLE_ONLY)
else()
  message(STATUS "Using resampling library: kissfft")
  target_compile_definitions(rubberband PRIVATE USE_KISSFFT)
  target_sources(rubberband PRIVATE src/kissfft/kiss_fft.c src/kissfft/kiss_fftr.c)
endif()

# libsamplerate / Speex
if(USE_LIBSAMPLERATE)
  message(STATUS "Using resampling library: libsamplerate")
  find_path(SAMPLERATE_INCLUDEDIR samplerate.h REQUIRED)
  find_library(SAMPLERATE_LIBRARY samplerate samplerate-0 libsamplerate libsamplerate-0 REQUIRED)
  target_include_directories(rubberband PRIVATE ${SAMPLERATE_INCLUDEDIR})
  target_link_libraries(rubberband PUBLIC ${SAMPLERATE_LIBRARY})
  target_compile_definitions(rubberband PRIVATE HAVE_LIBSAMPLERATE)
else()
  message(STATUS "Using resampling library: speex")
  target_compile_definitions(rubberband PRIVATE USE_SPEEX)
  target_sources(rubberband PRIVATE src/speex/resample.c)
endif()

#
# Handle general flags, include paths, etc.
#
include(CheckCXXCompilerFlag)

if(OPTIMIZE)
  check_cxx_compiler_flag("-march=native" MARCH_NATIVE_SUPPORTED)
  if(MARCH_NATIVE_SUPPORTED AND NOT MSVC)
    target_compile_options(rubberband PRIVATE -march=native)
  endif()
endif()

include(GNUInstallDirs)
target_include_directories(rubberband
  PUBLIC
      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/rubberband>
      $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/rubberband>
  PRIVATE
      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>
      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
)

target_compile_definitions(rubberband
  PRIVATE
    $<$<BOOL:${CMAKE_USE_PTHREADS_INIT}>:USE_PTHREADS>
    MALLOC_IS_ALIGNED
    NO_THREAD_CHECKS
    NO_TIMING
)

if(APPLE)
  if(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.11)
    message(FATAL_ERROR "macOS deployment target must be >= 10.11")
  endif()
  target_compile_definitions(rubberband PRIVATE HAVE_VDSP)

  find_library(ACCELERATE_FRAMEWORK NAMES Accelerate REQUIRED)
  target_link_libraries(rubberband PUBLIC ${ACCELERATE_FRAMEWORK})
elseif(WIN32)
    target_compile_definitions(rubberband PRIVATE _WINDOWS)
    if(CMAKE_SIZEOF_VOID_P EQUAL 4)
        target_compile_definitions(rubberband PRIVATE WIN32)
    endif()

    if(MSVC)
      target_compile_definitions(rubberband PRIVATE
          __MSVC__
          _LIB
          NOMINMAX
          _USE_MATH_DEFINES
          $<$<CONFIG:Release>:NDEBUG>
          $<$<CONFIG:Debug>:_DEBUG NO_TIMING NO_THREAD_CHECKS>
      )
      set_target_properties(rubberband PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
    endif()
endif()

#
# Command-Line Program
#
if(BUILD_EXECUTABLE)
  find_library(SNDFILE_LIBRARY sndfile REQUIRED)
  add_executable(rubberband-program main/main.cpp)
  target_include_directories(rubberband-program PRIVATE src Threads::Threads)
  target_link_libraries(rubberband-program PRIVATE rubberband ${SNDFILE_LIBRARY})
endif()

#
# Installation
#
install(
  TARGETS rubberband
  EXPORT rubberband-targets
  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
  INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)

# Header files
install(
    DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/rubberband"
    TYPE INCLUDE
)

# pkg-config file
if(UNIX)
    set(PREFIX "${CMAKE_INSTALL_PREFIX}")
    configure_file("${CMAKE_CURRENT_SOURCE_DIR}/rubberband.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/rubberband.pc" @ONLY)
    install(
        FILES "${CMAKE_CURRENT_BINARY_DIR}/rubberband.pc"
        DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
    )
endif()

# CMake config
include(CMakePackageConfigHelpers)
set(RUBBERBAND_INSTALL_CMAKEDIR "lib/cmake/rubberband")
install(
  EXPORT rubberband-targets
  FILE rubberband-targets.cmake
  NAMESPACE rubberband::
  DESTINATION "${RUBBERBAND_INSTALL_CMAKEDIR}"
)
configure_package_config_file(rubberband-config.cmake.in
  "${CMAKE_CURRENT_BINARY_DIR}/rubberband-config.cmake"
  INSTALL_DESTINATION "${RUBBERBAND_INSTALL_CMAKEDIR}"
)
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/rubberband-config-version.cmake"
  VERSION "${CMAKE_PROJECT_VERSION}"
  COMPATIBILITY SameMajorVersion
)
install(
  FILES
      "${CMAKE_CURRENT_BINARY_DIR}/rubberband-config.cmake"
      "${CMAKE_CURRENT_BINARY_DIR}/rubberband-config-version.cmake"
  DESTINATION "${RUBBERBAND_INSTALL_CMAKEDIR}"
)

1 Like

Thanks a lot for the information. I also know not much about CMake. Looks like a lot of code I can’t maintain in a reasonable time :slight_smile:

I had contact with one of the people from breakfastquay.com. Looks like they will release an update in a few weeks that makes it possible to include a single C++ file with a standard configuration that compiles out of the box. This would be super great :slight_smile:

Fair enough - thats good news that they’re going to go with a single file approach, I will be interested to try that out also.

Looks like a lot of code I can’t maintain in a reasonable time :slight_smile:

I understand that maybe it just makes a bit more sense to wait for the simpler single file method, but really this is also quite simple too. I don’t think I had to make any modifications to the CMakeLists file itself to get Rubberband to compile / link correctly.

If you’re already using CMake I would recommend cloning the Rubberband repo into your project & just taking the code I provided as is and saving it in the root directory of Rubberband. Really couldn’t be simpler in terms of CMake integration in my opinion. Granted I’ve only tested it on MacOS but I don’t see why other platforms should cause any more difficulty.

Another possibility you could explore is to install the compiled library somewhere on your machine (e.g. for MacOS brew install rubberband) and use find_library(RUBBERBAND NAMES rubberband) in your main CMake file. This is the simplest way to get started in my opinion, but at the expense of introducing dependencies that others have to install manually

Thanks for the information. I really hope they do that single file solution. Otherwise i have no choice and i will try the CMake solution.

Version 2.0 is here and you can include the source directly into your project in 5 minutes without configuring anything and without external libraries :slight_smile:

https://breakfastquay.com/rubberband/

@kunz could you maybe give an example how to integrate the rubberband library with CMake (or Projucer)? Or a hint how to do it, because rubberband doesnt provide a CMakeList build/integration and the rubberband examples dont help me. Thanks in adavance!

In Projucer:

External libraries to link → rubberband
Header search paths → /rubberband/src
Extra library search paths → /rubberband/lib

This works for me

1 Like

I have added the path to the rubberband cpp file in the CMake config:


target_sources(AudioPlugin PRIVATE
...
        ../shared/rubberband/single/RubberBandSingle.cpp
...
)

Then i have added following line in my processor cpp file:

#if JUCE_WINDOWS
#define NOMINMAX
#endif

This was required to make it compile on windows.

In the code I’m including following header:

#include "../../../shared/rubberband/rubberband/RubberBandStretcher.h"

That should be all you have to do.

2 Likes

If I want to use a library without building it separately, where the path to RubberBandSingle.cpp should go in Projucer?

thanks @losslessgo & @kunz!

I was able to compile a JUCE Projekt + rubberband with your CMake hints! :slight_smile: I had to use C++14 to get rubberband running ( C++17 & 20 will cause some std::byte ambiguous symbol errors)

1 Like

@struppel Did you use CMake or Projucer?

I used CMake, looks like this


cmake_minimum_required(VERSION 3.15)

project(audioPlugin VERSION 0.0.1)

add_subdirectory(modules/JUCE)

if(MSVC)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++14")
endif(MSVC)

set(CMAKE_CXX_STANDARD 14)

juce_add_plugin(audioPlugin
        COMPANY_NAME "yourcompany"                           # Specify the name of the plugin's author
        PLUGIN_MANUFACTURER_CODE Comp               # A four-character manufacturer id with at least one upper-case character
        PLUGIN_CODE SPIT                            # A unique four-character plugin id with exactly one upper-case character
        FORMATS VST3 Standalone                  # The formats to build. Other valid formats are: AAX Unity VST AU AUv3
        PRODUCT_NAME "audioPlugin")        # The name of the final executable, which can differ from the target name

juce_generate_juce_header(audioPlugin)

target_sources(audioPlugin
        PRIVATE
        source/PluginEditor.cpp
        source/PluginProcessor.cpp
        modules/rubberband/single/RubberBandSingle.cpp)

target_compile_definitions(audioPlugin
        PUBLIC
        JUCE_WEB_BROWSER=0
        JUCE_USE_CURL=0
        JUCE_VST3_CAN_REPLACE_VST2=0
        NOMINMAX #you should only use this on windows
        )

target_link_libraries(audioPlugin
        PRIVATE
        juce::juce_core
        juce::juce_audio_utils
        PUBLIC
        juce::juce_recommended_config_flags
        juce::juce_recommended_lto_flags
        juce::juce_recommended_warning_flags)


2 Likes