Skip to content

Commit f3ba0ee

Browse files
committed
Sample Onsets - move from Ruby FFI to external binary
Previously we called the sample onset detection functionality in the aubio library via a Ruby -> C FFI bridge. This approach unfortunately appears to cause problems when executing in a "hardened runtime" on macOS Big Sur. This patch moves this FFI-based Ruby gem to a static binary which uses stdout to return the onset values. Onset times should remain identical as the binary has been tuned in the same way as the original Ruby gem: # [:window_size] 1024 # [:hop_size] 512 # [:onset_threshold] 0.3 # [:minioi_ms] 12.0 (ms)
1 parent b1c717f commit f3ba0ee

37 files changed

+107
-5196
lines changed

app/external/aubio/CMakeLists.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.12)
22

33
project(aubio-5)
44
set(SOURCE_ROOT ${CMAKE_CURRENT_LIST_DIR}/src)
5+
set(EXAMPLES_ROOT ${CMAKE_CURRENT_LIST_DIR}/examples)
56

67
set(SOURCES
78
${SOURCE_ROOT}/exports.def
@@ -122,11 +123,23 @@ set(SOURCES
122123
${SOURCE_ROOT}/utils/windll.c
123124
)
124125

125-
add_library(${PROJECT_NAME} SHARED ${SOURCES} ${RESOURCES}) # Win32 ignored on non-windows
126+
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${RESOURCES}) # Win32 ignored on non-windows
126127
target_include_directories(${PROJECT_NAME}
127128
PRIVATE
128129
src
129130
${LIBSNDFILE_INCLUDE_DIR}
131+
)
132+
133+
add_executable(aubio_onset
134+
${EXAMPLES_ROOT}/aubioonset.c
135+
${EXAMPLES_ROOT}/utils.c)
136+
137+
target_link_libraries(aubio_onset PRIVATE ${PROJECT_NAME})
138+
139+
target_include_directories(aubio_onset
140+
PRIVATE
141+
src
142+
${LIBSNDFILE_INCLUDE_DIR}
130143
)
131144

132145
# 'lib' is appended to the library name automatically on most non-Windows platforms
@@ -206,6 +219,7 @@ target_compile_definitions(${PROJECT_NAME}
206219
-DHAVE_SWRESAMPLE
207220
-DHAVE_MEMCPY_HACKS
208221
-DHAVE_SNDFILE
222+
-DHAVE_CONFIG
209223
#-DHAVE_ACCELERATE
210224
#-DHAVE_INTEL_IPP (needs intel lib)
211225
#-DHAVE_SAMPLERATE (needs extra lib in path)

app/external/aubio/examples/config.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#pragma once
2+
3+
#define HAVE_STDLIB_H 1
4+
#define HAVE_STDIO_H 1
5+
#define HAVE_STRING_H 1
6+
#define HAVE_MATH_H 1
7+
#define HAVE_ERRNO_H 1
8+
#define HAVE_LIMITS_H 1
9+
#define HAVE_STDARG_H 1
10+
#define HAVE_MEMCPY_HACKS 1
11+
#define HAVE_C99_VARARGS_MACROS 1
12+
13+
#ifdef WIN32
14+
#define HAVE_WIN_HACKS 1
15+
#else
16+
#define HAVE_UNISTD_H 1
17+
#endif

app/external/aubio/examples/utils.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,22 @@ int usejack = 0;
3838
char_t *sink_uri = NULL;
3939
char_t *source_uri = NULL;
4040
// general stuff
41+
42+
// Use defaults as found in the original aubio Ruby gem
43+
// to match existing behaviour:
44+
// [:window_size] 1024
45+
// [:hop_size] 512
46+
// [:onset_threshold] 0.3
47+
// [:minioi_ms] 12.0 (ms)
48+
49+
4150
uint_t samplerate = 0;
42-
uint_t buffer_size = 512;
43-
uint_t hop_size = 256;
51+
uint_t buffer_size = 1024;
52+
uint_t hop_size = 512;
4453
// onset stuff
4554
char_t * onset_method = "default";
46-
smpl_t onset_threshold = 0.0; // will be set if != 0.
47-
smpl_t onset_minioi = 0.0; // will be set if != 0.
55+
smpl_t onset_threshold = 0.3; // will be set if != 0.
56+
smpl_t onset_minioi = 0.012; // will be set if != 0.
4857
// pitch stuff
4958
char_t * pitch_unit = "default";
5059
char_t * pitch_method = "default";

app/external/linux_build_externals.sh

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ cmake -DERLANG_INCLUDE_PATH=${ERLANG_INCLUDE_PATH} -G "Unix Makefiles" ..
1212

1313
echo "Building sp_midi..."
1414
cmake --build . --target sp_midi
15-
16-
if [ "$1" = "--build-aubio" ]; then
17-
echo "Building aubio..."
18-
cmake --build . --target aubio
19-
fi
15+
cmake --build . --target aubio
2016

2117
cd "${SCRIPT_DIR}"

app/external/mac_build_externals.sh

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ cmake -G "Unix Makefiles" -D ERLANG_INCLUDE_PATH="${SCRIPT_DIR}/../../prebuilt/m
1111

1212
echo "Building sp_midi..."
1313
cmake --build . --target sp_midi
14-
15-
16-
if [ "$1" = "--build-aubio" ]; then
17-
echo "Building aubio..."
18-
cmake --build . --target aubio
19-
fi
14+
echo "Building aubio onset..."
15+
cmake --build . --target aubio
2016

2117
cd "${SCRIPT_DIR}"

app/linux-prebuild.sh

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,17 @@ set -e # Quit script on error
33
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
44
echo "Warning: Unix build scripts are still a work in progress!"
55

6-
# Build external dependencies
7-
if [ "$1" = "--build-aubio" ]; then
8-
"${SCRIPT_DIR}/external/linux_build_externals.sh" --build-aubio
9-
else
10-
"${SCRIPT_DIR}/external/linux_build_externals.sh"
11-
fi
6+
7+
8+
"${SCRIPT_DIR}/external/linux_build_externals.sh"
9+
1210

1311
# Install dependencies to server
1412
echo "Copying external dependencies to the server..."
1513
mkdir -p "${SCRIPT_DIR}/server/erlang/sonic_pi_server/priv/"
1614
cp ${SCRIPT_DIR}/external/build/sp_midi-prefix/src/sp_midi-build/*.so ${SCRIPT_DIR}/server/erlang/sonic_pi_server/priv/
1715

18-
if [ "$1" = "--build-aubio" ]; then
19-
mkdir -p "${SCRIPT_DIR}/server/native/lib"
20-
cp "${SCRIPT_DIR}/external/build/aubio-prefix/src/aubio-build/libaubio-5.so" "${SCRIPT_DIR}/server/native/lib/"
21-
fi
16+
cp "${SCRIPT_DIR}/external/build/aubio-prefix/src/aubio-build/aubio_onset" "${SCRIPT_DIR}/server/native/"
2217

2318
#dont remove ruby-aubio-prerelease, as needed in linux build
2419
#it is removed in the windows-prebuild

app/mac-prebuild.sh

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,25 @@
22
set -e # Quit script on error
33
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
44

5+
# Check to see if we have a bundled Ruby and if so, use that
6+
# Otherwise use system ruby
57

6-
# Build external dependencies
7-
if [ "$1" = "--without-aubio" ]; then
8-
"${SCRIPT_DIR}/external/mac_build_externals.sh"
8+
BUNDLED_RUBY="${SCRIPT_DIR}/server/native/ruby/bin/ruby"
9+
if [ -f "$BUNDLED_RUBY" ]; then
10+
echo "Found bundled Ruby: ${BUNDLED_RUBY}"
11+
RUBY=$BUNDLED_RUBY
912
else
10-
"${SCRIPT_DIR}/external/mac_build_externals.sh" --build-aubio
11-
mkdir -p "${SCRIPT_DIR}/server/native/lib"
12-
cp "${SCRIPT_DIR}/external/build/aubio-prefix/src/aubio-build/libaubio-5.dylib" "${SCRIPT_DIR}/server/native/lib/"
13+
echo "Using system Ruby"
14+
RUBY=ruby
1315
fi
1416

17+
18+
# Build external dependencies
19+
"${SCRIPT_DIR}/external/mac_build_externals.sh"
20+
# mkdir -p "${SCRIPT_DIR}/server/native/lib"
21+
cp "${SCRIPT_DIR}/external/build/aubio-prefix/src/aubio-build/aubio_onset" "${SCRIPT_DIR}/server/native/"
22+
23+
1524
# Install dependencies to server
1625
echo "Copying external dependencies to the server..."
1726
mkdir -p "${SCRIPT_DIR}/server/erlang/sonic_pi_server/priv/"
@@ -33,16 +42,15 @@ mv supercollider/extra-plugins/* supercollider/plugins/
3342
rm -rf supercollider/extra-plugins
3443

3544
echo "Compiling native ruby extensions..."
36-
ruby "${SCRIPT_DIR}/server/ruby/bin/compile-extensions.rb"
45+
$RUBY "${SCRIPT_DIR}/server/ruby/bin/compile-extensions.rb"
3746

3847
echo "Translating tutorial..."
39-
#assumes linux uses system ruby
40-
#so dont use prefix server/native/ruby/bin/ruby, as unnecessary to set this up
41-
ruby "${SCRIPT_DIR}/server/ruby/bin/i18n-tool.rb" -t
48+
49+
$RUBY "${SCRIPT_DIR}/server/ruby/bin/i18n-tool.rb" -t
4250

4351
echo "Generating docs for the Qt GUI..."
4452
cp "${SCRIPT_DIR}/gui/qt/utils/ruby_help.tmpl" "${SCRIPT_DIR}/gui/qt/utils/ruby_help.h"
45-
ruby "${SCRIPT_DIR}/server/ruby/bin/qt-doc.rb" -o "${SCRIPT_DIR}/gui/qt/utils/ruby_help.h"
53+
$RUBY "${SCRIPT_DIR}/server/ruby/bin/qt-doc.rb" -o "${SCRIPT_DIR}/gui/qt/utils/ruby_help.h"
4654

4755
echo "Updating GUI translation files..."
4856
# Use lrelease on PATH if available otherwise assume Qt was installed via homebrew

app/server/ruby/core.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,6 @@
5050

5151
$:.unshift "#{File.expand_path("../rb-native", __FILE__)}/#{ruby_api}/"
5252

53-
## Add aubio native library to ENV if not present (the aubio library needs to be told the location)
54-
native_lib_path = File.expand_path("../../native/", __FILE__)
55-
ENV["AUBIO_LIB"] ||= Dir[native_lib_path + "/lib/libaubio*.{*dylib,so*,dll}"].first
56-
5753
module SonicPi
5854
module Core
5955
class ThreadLocal

app/server/ruby/lib/sonicpi/samplebuffer.rb

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
require_relative "buffer"
1515
require_relative "util"
1616
require_relative "sox"
17-
require 'aubio'
1817

1918

2019
module SonicPi
@@ -87,14 +86,31 @@ def info
8786
end
8887

8988
def onset_data
90-
return @aubio_onset_data if @aubio_onset_data
89+
return @aubio_onset_data if @aubio_onset_data
9190
@aubio_sem.synchronize do
92-
return @aubio_onset_data if @aubio_onset_data
91+
return @aubio_onset_data if @aubio_onset_data
9392
__no_kill_block do
94-
aubio_file = Aubio.open(@path, {sample_rate: sample_rate})
95-
native_onsets = aubio_file.onsets.to_a.ring
96-
aubio_file.close
97-
@aubio_onset_data = native_onsets
93+
94+
# These are the aubio defaults set by old gem and now
95+
# hard-coded into the aubio_onset binary: (this was worth
96+
# maintaining to preserve backwards compatibility. Might also
97+
# be nice to let users tweak these values in the future)
98+
99+
# [:window_size] 1024
100+
# [:hop_size] 512
101+
# [:onset_threshold] 0.3
102+
# [:minioi_ms] 12.0 (ms)
103+
104+
begin
105+
aubio_onsets_command = "'#{aubio_onset_path}' '#{@path}'"
106+
onsets_str = `#{aubio_onsets_command}`
107+
onsets = onsets_str.split.map(&:to_f)
108+
rescue Exception => e
109+
log_exception e
110+
onsets = []
111+
end
112+
113+
@aubio_onset_data = onsets.ring
98114
end
99115
end
100116
return @aubio_onset_data
@@ -106,7 +122,7 @@ def onsets(stretch=1)
106122
@aubio_sem.synchronize do
107123
return @aubio_onsets[stretch] if @aubio_onsets[stretch]
108124
onset_times = data.map do |el|
109-
[1, (el[:s].to_f / duration)].min * stretch
125+
[1, (el / duration)].min * stretch
110126
end
111127
@aubio_onsets[stretch] = onset_times
112128
end

app/server/ruby/lib/sonicpi/util.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def raspberry_pi_400?
181181
def raspberry_pi_400_64?
182182
os == :raspberry && @@raspberry_pi_400_64
183183
end
184-
184+
185185
def unify_tilde_dir(path)
186186
if os == :windows
187187
path
@@ -417,6 +417,15 @@ def native_path
417417
File.absolute_path("#{server_path}/native/")
418418
end
419419

420+
def aubio_onset_path
421+
case os
422+
when :windows
423+
File.absolute_path("#{native_path}/aubio_onset.exe")
424+
else
425+
File.absolute_path("#{native_path}/aubio_onset")
426+
end
427+
end
428+
420429
def sox_path
421430
File.join(native_path, "sox", __exe_fix("sox"))
422431
end

0 commit comments

Comments
 (0)