未验证 提交 00f737e6 编写于 作者: S Sam Aaron 提交者: GitHub

Merge pull request #2684 from sonic-pi-net/v3.3.1-dev

V3.3.1 dev
# History
* [v3.3.1 'Beamer'](#v3.3.1), 1st Feb, 2021
* [v3.3 'Beam'](#v3.3), 28th Jan, 2021
* [v3.2.2 'Tau3'](#v3.2.2), 5th April, 2020
* [v3.2.1 'Take Tau'](#v3.2.1), 3rd April, 2020
......@@ -22,6 +23,22 @@
* [v2.0.1](#v2.0.1), 9th Sept, 2014
* [v2.0 'Phoenix'](#v2.0), 2nd Sept, 2014
<a name="v3.3.1"></a>
## Version 3.3.1 - 'Beamer'
1st Feb, 2021
[(view commits)](https://github.com/sonic-pi-net/sonic-pi/commits/v3.3.1):
This is a compatibility release addressing two issues with macOS Big Sur:
1. Correct language translations now used based on user's current locale
2. The language server is no longer killed by macOS Gatekeeper when a
sample's onset times are queried.
This release also includes minor translation updates.
<a name="v3.3"></a>
## Version 3.3.0 - 'Beam'
......
......@@ -224,19 +224,18 @@ In addition to the Sonic Pi Core Team, we would like to extend our
thanks to the following developers who have donated their time and
effort to help grow and improve the Sonic Pi code base:
* [Sam Aaron](https://github.com/samaaron/sonic-pi/commits?author=samaaron) (5546)
* [Ethan Crawford](https://github.com/samaaron/sonic-pi/commits?author=ethancrawford) (259)
* [Sam Aaron](https://github.com/samaaron/sonic-pi/commits?author=samaaron) (5565)
* [Ethan Crawford](https://github.com/samaaron/sonic-pi/commits?author=ethancrawford) (262)
* [Joseph Wilk](https://github.com/samaaron/sonic-pi/commits?author=josephwilk) (160)
* [Hanno Zulla](https://github.com/samaaron/sonic-pi/commits?author=hzulla) (127)
* [Jeremy Weatherford](https://github.com/samaaron/sonic-pi/commits?author=jweather) (127)
* [Xavier Riley](https://github.com/samaaron/sonic-pi/commits?author=xavriley) (106)
* [Adrian Cheater](https://github.com/samaaron/sonic-pi/commits?author=factoid) (74)
* [Robin Newman](https://github.com/samaaron/sonic-pi/commits?author=rbnpi) (64)
* [Robin Newman](https://github.com/samaaron/sonic-pi/commits?author=rbnpi) (68)
* [Emlyn Corrin](https://github.com/samaaron/sonic-pi/commits?author=emlyn) (62)
* [SunderB](https://github.com/samaaron/sonic-pi/commits?author=SunderB) (56)
* [Martin Keegan](https://github.com/samaaron/sonic-pi/commits?author=mk270) (48)
* [Chris Maughan](https://github.com/samaaron/sonic-pi/commits?author=cmaughan) (44)
* [Chris Maughan](https://github.com/samaaron/sonic-pi/commits?author=cmaughan) (46)
* [Luis Lloret](https://github.com/samaaron/sonic-pi/commits?author=llloret) (39)
* [Nicolas Dermine](https://github.com/samaaron/sonic-pi/commits?author=nicoder) (37)
* [Nikolaus Gradwohl](https://github.com/samaaron/sonic-pi/commits?author=ngradwohl) (30)
......@@ -379,8 +378,8 @@ The following volunteers have generously helped to translate Sonic Pi into addit
* Hanno Zulla (119)
* Sam Aaron (87)
* Nico Staelens (83)
* Nicolas Dermine (80)
* Olivier Humbert (50)
* Nicolas Dermine (81)
* Olivier Humbert (51)
* Cindy Dallaire (48)
* Pau Monfort (39)
* Olympia Brikis (37)
......@@ -392,22 +391,22 @@ The following volunteers have generously helped to translate Sonic Pi into addit
* Ricardo Pozo (23)
* Yann Pavlenko (22)
* Giovanni Mori (21)
* Emlyn Corrin (18)
* Ruben Tobalina (18)
* Emlyn Corrin (16)
* Nicolas Le Bellier (15)
* Wolfgang Werner (14)
* Allan Nordhøy (13)
* G. Martin Butz (12)
* Nicolas Le Bellier (12)
* Álvaro Cáceres Muñoz (12)
* Hong Lees (11)
* Ole Friis Østergaard (11)
* Nicolas Limage (10)
* Elena Ogaderova (9)
* Kristjan Räts (9)
* Oto Popis (9)
* Tiago Morais Morgado (9)
* Yaron Shahrabani (9)
* Daniele Paradiso (8)
* Kristjan Räts (8)
* Ole Erik Yrvin (8)
* Rekc@h (8)
* Yang Wanjun (8)
......@@ -446,6 +445,7 @@ The following volunteers have generously helped to translate Sonic Pi into addit
* Nur Sodik (5)
* Sebastian Stahn (5)
* Suleyman Poyraz (5)
* Alex Esc (4)
* André Klöpfel (4)
* ArtyomIsFlash (4)
* Dahlia Sager (4)
......@@ -582,7 +582,6 @@ The following volunteers have generously helped to translate Sonic Pi into addit
* Ai Doan (1)
* Alejandro Sánchez Medina (1)
* Alessandro Kiefer (1)
* Alex Esc (1)
* Anderson Freitas B. da Silva (1)
* Andras Nemeth (1)
* Andrey Raspopov (1)
......@@ -599,6 +598,7 @@ The following volunteers have generously helped to translate Sonic Pi into addit
* Bruno Arakaki (1)
* Burak Tufekci (1)
* Carlos David Perales Cejudo (1)
* Cattice (1)
* Cedric Frossard (1)
* Cem Kaan Kösali (1)
* Cem Olcay (1)
......
......@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.12)
project(aubio-5)
set(SOURCE_ROOT ${CMAKE_CURRENT_LIST_DIR}/src)
set(EXAMPLES_ROOT ${CMAKE_CURRENT_LIST_DIR}/examples)
set(SOURCES
${SOURCE_ROOT}/exports.def
......@@ -122,11 +123,23 @@ set(SOURCES
${SOURCE_ROOT}/utils/windll.c
)
add_library(${PROJECT_NAME} SHARED ${SOURCES} ${RESOURCES}) # Win32 ignored on non-windows
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${RESOURCES}) # Win32 ignored on non-windows
target_include_directories(${PROJECT_NAME}
PRIVATE
src
${LIBSNDFILE_INCLUDE_DIR}
)
add_executable(aubio_onset
${EXAMPLES_ROOT}/aubioonset.c
${EXAMPLES_ROOT}/utils.c)
target_link_libraries(aubio_onset PRIVATE ${PROJECT_NAME})
target_include_directories(aubio_onset
PRIVATE
src
${LIBSNDFILE_INCLUDE_DIR}
)
# 'lib' is appended to the library name automatically on most non-Windows platforms
......@@ -206,6 +219,7 @@ target_compile_definitions(${PROJECT_NAME}
-DHAVE_SWRESAMPLE
-DHAVE_MEMCPY_HACKS
-DHAVE_SNDFILE
-DHAVE_CONFIG
#-DHAVE_ACCELERATE
#-DHAVE_INTEL_IPP (needs intel lib)
#-DHAVE_SAMPLERATE (needs extra lib in path)
......
#pragma once
#define HAVE_STDLIB_H 1
#define HAVE_STDIO_H 1
#define HAVE_STRING_H 1
#define HAVE_MATH_H 1
#define HAVE_ERRNO_H 1
#define HAVE_LIMITS_H 1
#define HAVE_STDARG_H 1
#define HAVE_MEMCPY_HACKS 1
#define HAVE_C99_VARARGS_MACROS 1
#ifdef WIN32
#define HAVE_WIN_HACKS 1
#else
#define HAVE_UNISTD_H 1
#endif
......@@ -38,13 +38,22 @@ int usejack = 0;
char_t *sink_uri = NULL;
char_t *source_uri = NULL;
// general stuff
// Use defaults as found in the original aubio Ruby gem
// to match existing behaviour:
// [:window_size] 1024
// [:hop_size] 512
// [:onset_threshold] 0.3
// [:minioi_ms] 12.0 (ms)
uint_t samplerate = 0;
uint_t buffer_size = 512;
uint_t hop_size = 256;
uint_t buffer_size = 1024;
uint_t hop_size = 512;
// onset stuff
char_t * onset_method = "default";
smpl_t onset_threshold = 0.0; // will be set if != 0.
smpl_t onset_minioi = 0.0; // will be set if != 0.
smpl_t onset_threshold = 0.3; // will be set if != 0.
smpl_t onset_minioi = 0.012; // will be set if != 0.
// pitch stuff
char_t * pitch_unit = "default";
char_t * pitch_method = "default";
......
......@@ -12,10 +12,6 @@ cmake -DERLANG_INCLUDE_PATH=${ERLANG_INCLUDE_PATH} -G "Unix Makefiles" ..
echo "Building sp_midi..."
cmake --build . --target sp_midi
if [ "$1" = "--build-aubio" ]; then
echo "Building aubio..."
cmake --build . --target aubio
fi
cmake --build . --target aubio
cd "${SCRIPT_DIR}"
......@@ -11,11 +11,7 @@ cmake -G "Unix Makefiles" -D ERLANG_INCLUDE_PATH="${SCRIPT_DIR}/../../prebuilt/m
echo "Building sp_midi..."
cmake --build . --target sp_midi
if [ "$1" = "--build-aubio" ]; then
echo "Building aubio..."
cmake --build . --target aubio
fi
echo "Building aubio onset..."
cmake --build . --target aubio
cd "${SCRIPT_DIR}"
......@@ -5,7 +5,7 @@ message(STATUS " CMakeLists: Sonic Pi")
project("Sonic Pi"
LANGUAGES CXX C
DESCRIPTION "A code-based music creation and performance tool"
VERSION 3.3.0
VERSION 3.3.1
HOMEPAGE_URL "https://sonic-pi.net"
)
......
......@@ -15,7 +15,7 @@
code_as <span class="highlight">:art</span></pre>
</p>
<p class="version">v3.3.0</p>
<p class="version">v3.3.1</p>
</center>
......
......@@ -29,6 +29,6 @@ code_as <span class="highlight">:art</span></pre>
<br/>
<p class="version">v3.3.0</p>
<p class="version">v3.3.1</p>
</center>
</body>
app/gui/qt/images/splash.png

61.9 KB | W: | H:

app/gui/qt/images/splash.png

61.9 KB | W: | H:

app/gui/qt/images/splash.png
app/gui/qt/images/splash.png
app/gui/qt/images/splash.png
app/gui/qt/images/splash.png
  • 2-up
  • Swipe
  • Onion skin
app/gui/qt/images/splash2x.png

147.9 KB | W: | H:

app/gui/qt/images/splash2x.png

147.8 KB | W: | H:

app/gui/qt/images/splash2x.png
app/gui/qt/images/splash2x.png
app/gui/qt/images/splash2x.png
app/gui/qt/images/splash2x.png
  • 2-up
  • Swipe
  • Onion skin
app/gui/qt/images/splash@2x.png

147.9 KB | W: | H:

app/gui/qt/images/splash@2x.png

147.8 KB | W: | H:

app/gui/qt/images/splash@2x.png
app/gui/qt/images/splash@2x.png
app/gui/qt/images/splash@2x.png
app/gui/qt/images/splash@2x.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -430,7 +430,7 @@ Lo sentimos, un error crítico ha ocurrido durante el inicio</translation>
<message>
<location filename="../mainwindow.cpp" line="2634"/>
<source>Enforce Timing Guarantees</source>
<translation type="unfinished">Imponer Garantías de Tiempo</translation>
<translation>Imponer Garantías de Tiempo</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="2639"/>
......@@ -475,7 +475,7 @@ Lo sentimos, un error crítico ha ocurrido durante el inicio</translation>
<message>
<location filename="../mainwindow.cpp" line="2679"/>
<source>Clear Logs on Run</source>
<translation>Eliminar registros al ejecutar</translation>
<translation>Eliminar Registros al Ejecutar código</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="2684"/>
......@@ -485,12 +485,12 @@ Lo sentimos, un error crítico ha ocurrido durante el inicio</translation>
<message>
<location filename="../mainwindow.cpp" line="2689"/>
<source>Auto-Scroll Log</source>
<translation>Desplazar hasta el final</translation>
<translation>Desplazar Registro Automáticamente</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="2699"/>
<source>Live</source>
<translation>En directo</translation>
<translation>En Vivo</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="2711"/>
......@@ -535,7 +535,7 @@ Lo sentimos, un error crítico ha ocurrido durante el inicio</translation>
<message>
<location filename="../mainwindow.cpp" line="2761"/>
<source>Show Scope Labels</source>
<translation>Mostrar nombres de visualizadores</translation>
<translation>Mostrar Nombres de Visualizadores</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="2766"/>
......@@ -694,7 +694,7 @@ Lo sentimos, un error crítico ha ocurrido durante el inicio</translation>
<message>
<location filename="../mainwindow.cpp" line="2952"/>
<source>Show Log</source>
<translation>Mostrar registros</translation>
<translation>Mostrar Registro</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="2957"/>
......
......@@ -49,15 +49,14 @@ int main(int argc, char *argv[])
qRegisterMetaType<SonicPiLog::MultiMessage>("SonicPiLog::MultiMessage");
QString systemLocale = QLocale::system().name();
QString systemLocale = QLocale::system().uiLanguages()[0].replace("-", "_");
QTranslator qtTranslator;
qtTranslator.load("qt_" + systemLocale, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
app.installTranslator(&qtTranslator);
QTranslator translator;
bool i18n = translator.load("sonic-pi_" + systemLocale, ":/lang/") || systemLocale.startsWith("en") || systemLocale == "C";
bool i18n = translator.load(QLatin1String("sonic-pi_") + systemLocale, QLatin1String(":/lang")) || systemLocale.startsWith("en") || systemLocale == "C";
app.installTranslator(&translator);
app.setApplicationName(QObject::tr("Sonic Pi"));
......
......@@ -128,7 +128,7 @@ MainWindow::MainWindow(QApplication &app, bool i18n, QSplashScreen* splash)
show_rec_icon_a = false;
restoreDocPane = false;
focusMode = false;
version = "3.3.0";
version = "3.3.1";
latest_version = "";
version_num = 0;
latest_version_num = 0;
......@@ -148,11 +148,22 @@ MainWindow::MainWindow(QApplication &app, bool i18n, QSplashScreen* splash)
// Throw all stdout into ~/.sonic-pi/log/gui.log
setupLogPathAndRedirectStdOut();
std::cout << "[GUI] - " << std::endl;
std::cout << "[GUI] - " << std::endl;
std::cout << "[GUI] - " << std::endl;
std::cout << "[GUI] - Welcome to the Sonic Pi GUI" << std::endl;
std::cout << "[GUI] - ===========================" << std::endl;
std::cout << "[GUI] - " << std::endl;
std::cout << "[GUI] - " << guiID.toStdString() << std::endl;
std::cout << "[GUI] - ui locale: " << QLocale::system().uiLanguages()[0].toStdString() << std::endl;
std::cout << "[GUI] - sys locale: " << QLocale::system().name().toStdString() << std::endl;
if(i18n) {
std::cout << "[GUI] - translations available " << std::endl;
} else {
std::cout << "[GUI] - translations unavailable (using EN)" << std::endl;
}
// dynamically discover port numbers and then check them this will
// show an error dialogue to the user and then kill the app if any of
......
......@@ -3,22 +3,17 @@ set -e # Quit script on error
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
echo "Warning: Unix build scripts are still a work in progress!"
# Build external dependencies
if [ "$1" = "--build-aubio" ]; then
"${SCRIPT_DIR}/external/linux_build_externals.sh" --build-aubio
else
"${SCRIPT_DIR}/external/linux_build_externals.sh"
fi
"${SCRIPT_DIR}/external/linux_build_externals.sh"
# Install dependencies to server
echo "Copying external dependencies to the server..."
mkdir -p "${SCRIPT_DIR}/server/erlang/sonic_pi_server/priv/"
cp ${SCRIPT_DIR}/external/build/sp_midi-prefix/src/sp_midi-build/*.so ${SCRIPT_DIR}/server/erlang/sonic_pi_server/priv/
if [ "$1" = "--build-aubio" ]; then
mkdir -p "${SCRIPT_DIR}/server/native/lib"
cp "${SCRIPT_DIR}/external/build/aubio-prefix/src/aubio-build/libaubio-5.so" "${SCRIPT_DIR}/server/native/lib/"
fi
cp "${SCRIPT_DIR}/external/build/aubio-prefix/src/aubio-build/aubio_onset" "${SCRIPT_DIR}/server/native/"
#dont remove ruby-aubio-prerelease, as needed in linux build
#it is removed in the windows-prebuild
......
......@@ -2,16 +2,25 @@
set -e # Quit script on error
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Check to see if we have a bundled Ruby and if so, use that
# Otherwise use system ruby
# Build external dependencies
if [ "$1" = "--without-aubio" ]; then
"${SCRIPT_DIR}/external/mac_build_externals.sh"
BUNDLED_RUBY="${SCRIPT_DIR}/server/native/ruby/bin/ruby"
if [ -f "$BUNDLED_RUBY" ]; then
echo "Found bundled Ruby: ${BUNDLED_RUBY}"
RUBY=$BUNDLED_RUBY
else
"${SCRIPT_DIR}/external/mac_build_externals.sh" --build-aubio
mkdir -p "${SCRIPT_DIR}/server/native/lib"
cp "${SCRIPT_DIR}/external/build/aubio-prefix/src/aubio-build/libaubio-5.dylib" "${SCRIPT_DIR}/server/native/lib/"
echo "Using system Ruby"
RUBY=ruby
fi
# Build external dependencies
"${SCRIPT_DIR}/external/mac_build_externals.sh"
# mkdir -p "${SCRIPT_DIR}/server/native/lib"
cp "${SCRIPT_DIR}/external/build/aubio-prefix/src/aubio-build/aubio_onset" "${SCRIPT_DIR}/server/native/"
# Install dependencies to server
echo "Copying external dependencies to the server..."
mkdir -p "${SCRIPT_DIR}/server/erlang/sonic_pi_server/priv/"
......@@ -33,16 +42,15 @@ mv supercollider/extra-plugins/* supercollider/plugins/
rm -rf supercollider/extra-plugins
echo "Compiling native ruby extensions..."
ruby "${SCRIPT_DIR}/server/ruby/bin/compile-extensions.rb"
$RUBY "${SCRIPT_DIR}/server/ruby/bin/compile-extensions.rb"
echo "Translating tutorial..."
#assumes linux uses system ruby
#so dont use prefix server/native/ruby/bin/ruby, as unnecessary to set this up
ruby "${SCRIPT_DIR}/server/ruby/bin/i18n-tool.rb" -t
$RUBY "${SCRIPT_DIR}/server/ruby/bin/i18n-tool.rb" -t
echo "Generating docs for the Qt GUI..."
cp "${SCRIPT_DIR}/gui/qt/utils/ruby_help.tmpl" "${SCRIPT_DIR}/gui/qt/utils/ruby_help.h"
ruby "${SCRIPT_DIR}/server/ruby/bin/qt-doc.rb" -o "${SCRIPT_DIR}/gui/qt/utils/ruby_help.h"
$RUBY "${SCRIPT_DIR}/server/ruby/bin/qt-doc.rb" -o "${SCRIPT_DIR}/gui/qt/utils/ruby_help.h"
echo "Updating GUI translation files..."
# Use lrelease on PATH if available otherwise assume Qt was installed via homebrew
......
......@@ -216,7 +216,7 @@ languages =
map { |p| File.basename(p).gsub(/sonic-pi-tutorial-(.*?).po/, '\1') }.
sort_by {|n| -n.length}
docs << "\n QString systemLocale = QLocale::system().name();\n\n" unless languages.empty?
docs << "\n QString systemLocale = QLocale::system().uiLanguages()[0];\n\n" unless languages.empty?
# first, try to match all non-default languages (those that aren't "en")
languages.each do |lang|
......
......@@ -50,10 +50,6 @@ os = case RUBY_PLATFORM
$:.unshift "#{File.expand_path("../rb-native", __FILE__)}/#{ruby_api}/"
## Add aubio native library to ENV if not present (the aubio library needs to be told the location)
native_lib_path = File.expand_path("../../native/", __FILE__)
ENV["AUBIO_LIB"] ||= Dir[native_lib_path + "/lib/libaubio*.{*dylib,so*,dll}"].first
module SonicPi
module Core
class ThreadLocal
......
......@@ -1332,7 +1332,7 @@ module SonicPi
end
@scsynth_clobber_args = @audio_settings.scsynth_opts_override
@scsynth_opts = @audio_settings.scsynth_opts
@version = Version.new(3, 3, 0)
@version = Version.new(3, 3, 1)
@server_version = __server_version
@life_hooks = LifeCycleHooks.new
@msg_queue = msg_queue
......
......@@ -14,7 +14,6 @@
require_relative "buffer"
require_relative "util"
require_relative "sox"
require 'aubio'
module SonicPi
......@@ -87,14 +86,31 @@ module SonicPi
end
def onset_data
return @aubio_onset_data if @aubio_onset_data
return @aubio_onset_data if @aubio_onset_data
@aubio_sem.synchronize do
return @aubio_onset_data if @aubio_onset_data
return @aubio_onset_data if @aubio_onset_data
__no_kill_block do
aubio_file = Aubio.open(@path, {sample_rate: sample_rate})
native_onsets = aubio_file.onsets.to_a.ring
aubio_file.close
@aubio_onset_data = native_onsets
# These are the aubio defaults set by old gem and now
# hard-coded into the aubio_onset binary: (this was worth
# maintaining to preserve backwards compatibility. Might also
# be nice to let users tweak these values in the future)
# [:window_size] 1024
# [:hop_size] 512
# [:onset_threshold] 0.3
# [:minioi_ms] 12.0 (ms)
begin
aubio_onsets_command = "\"#{aubio_onset_path}\" \"#{@path}\""
onsets_str = `#{aubio_onsets_command}`
onsets = onsets_str.split.map(&:to_f)
rescue Exception => e
log_exception e
onsets = []
end
@aubio_onset_data = onsets.ring
end
end
return @aubio_onset_data
......@@ -106,7 +122,7 @@ module SonicPi
@aubio_sem.synchronize do
return @aubio_onsets[stretch] if @aubio_onsets[stretch]
onset_times = data.map do |el|
[1, (el[:s].to_f / duration)].min * stretch
[1, (el / duration)].min * stretch
end
@aubio_onsets[stretch] = onset_times
end
......
......@@ -416,7 +416,7 @@ module SonicPi
puts "Jackd already running. Not starting another server..."
end
block_size = raspberry_pi_1? ? 512 : 128
block_size = 128
local_scsynth_opts = {
"-c" => "128",
......@@ -429,10 +429,11 @@ module SonicPi
boot_and_wait(scsynth_path, scsynth_opts)
`jack_connect SuperCollider:in_1 system_capture_1`
`jack_connect SuperCollider:in_2 system_capture_2`
`pactl load-module module-jack-source connect=0 client_name=JACK_to_PulseAudio`
`pactl load-module module-loopback source=jack_in`
`pactl load-module module-jack-sink channels=2 connect=0 client_name=PulseAudio_to_JACK`
`jack_connect PulseAudio_to_JACK:front-left SuperCollider:in_1`
`jack_connect PulseAudio_to_JACK:front-right SuperCollider:in_2`
`jack_connect SuperCollider:out_1 JACK_to_PulseAudio:front-left`
`jack_connect SuperCollider:out_2 JACK_to_PulseAudio:front-right`
......@@ -440,6 +441,7 @@ module SonicPi
end
def boot_server_linux
#to do -- needs further work
log_boot_msg
puts "Booting on Linux"
#Start Jack if not already running
......
......@@ -565,7 +565,7 @@ module SonicPi
# set_mixer! :basic
# set_mixer! :default
log_message "Starting mixer"
mixer_synth = raspberry_pi_1? ? "sonic-pi-basic_mixer" : "sonic-pi-mixer"
mixer_synth = "sonic-pi-mixer"
@mixer = @server.trigger_synth(:head, @mixer_group, mixer_synth, {"in_bus" => @mixer_bus.to_i}, nil, true)
end
......
......@@ -56,7 +56,6 @@ module SonicPi
@@current_uuid = nil
@@home_dir = nil
@@util_lock = Mutex.new
@@raspberry_pi_1 = RUBY_PLATFORM.match(/.*arm.*-linux.*/) && File.exist?('/proc/cpuinfo') && !(`cat /proc/cpuinfo | grep BCM2708`).empty?
@@raspberry_pi_2 = RUBY_PLATFORM.match(/.*arm.*-linux.*/) && ['a01040','a01041','a22042'].include?(`awk '/^Revision/ { print $3}' /proc/cpuinfo`.delete!("\n"))
@@raspberry_pi_3 = RUBY_PLATFORM.match(/.*arm.*-linux.*/) && ['a02082','a22082','a32082'].include?(`awk '/^Revision/ { print $3}' /proc/cpuinfo`.delete!("\n"))
@@raspberry_pi_3bplus = RUBY_PLATFORM.match(/.*arm.*-linux.*/) && ['a020d3'].include?(`awk '/^Revision/ { print $3}' /proc/cpuinfo`.delete!("\n"))
......@@ -118,10 +117,6 @@ module SonicPi
os == :raspberry
end
def raspberry_pi_1?
os == :raspberry && @@raspberry_pi_1
end
def raspberry_pi_2?
os == :raspberry && @@raspberry_pi_2
end
......@@ -181,7 +176,7 @@ module SonicPi
def raspberry_pi_400_64?
os == :raspberry && @@raspberry_pi_400_64
end
def unify_tilde_dir(path)
if os == :windows
path
......@@ -195,17 +190,15 @@ module SonicPi
end
def num_audio_busses_for_current_os
if os == :raspberry && @@raspberry_pi_1
64
else
1024
end
end
def default_sched_ahead_time
if raspberry_pi_1?
1
if raspberry_pi_2?
2
elsif raspberry_pi_3? or raspberry_pi_3bplus? \
or raspberry_pi_3_64? or raspberry_pi_3bplus_64?
1.5
else
0.5
end
......@@ -214,9 +207,7 @@ module SonicPi
def host_platform_desc
case os
when :raspberry
if raspberry_pi_1?
"Raspberry Pi 1"
elsif raspberry_pi_2?
if raspberry_pi_2?
"Raspberry Pi 2B"
elsif raspberry_pi_3?
"Raspberry Pi 3B"
......@@ -260,11 +251,7 @@ module SonicPi
def default_control_delta
if raspberry_pi?
if raspberry_pi_1?
0.02
else
0.013
end
else
0.005
end
......@@ -417,16 +404,17 @@ module SonicPi
File.absolute_path("#{server_path}/native/")
end
def sox_path
File.join(native_path, "sox", __exe_fix("sox"))
end
def osmid_o2m_path
File.join(native_path, "osmid", __exe_fix("o2m"))
def aubio_onset_path
case os
when :windows
File.absolute_path("#{native_path}/aubio_onset.exe")
else
File.absolute_path("#{native_path}/aubio_onset")
end
end
def osmid_m2o_path
File.join(native_path, "osmid", __exe_fix("m2o"))
def sox_path
File.join(native_path, "sox", __exe_fix("sox"))
end
def scsynth_log_path
......@@ -437,14 +425,6 @@ module SonicPi
log_path + '/erlang.log'
end
def osmid_m2o_log_path
log_path + '/osmid_m2o.log'
end
def osmid_o2m_log_path
log_path + '/osmid_o2m.log'
end
def ruby_path
case os
when :windows
......
/.bundle/
/.yardoc
/Gemfile.lock
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
*.gem
tags
language: ruby
rvm:
- 2.2.1
before_install: gem install bundler -v 1.11.2
# Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of
fostering an open and welcoming community, we pledge to respect all people who
contribute through reporting issues, posting feature requests, updating
documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free
experience for everyone, regardless of level of experience, gender, gender
identity and expression, sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic
addresses, without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to
fairly and consistently applying these principles to every aspect of managing
this project. Project maintainers who do not follow or enforce the Code of
Conduct may be permanently removed from the project team.
This code of conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting a project maintainer at xavriley@hotmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. Maintainers are
obligated to maintain confidentiality with regard to the reporter of an
incident.
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.3.0, available at
[http://contributor-covenant.org/version/1/3/0/][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/3/0/
\ No newline at end of file
source 'https://rubygems.org'
# Specify your gem's dependencies in aubio.gemspec
gemspec
The MIT License (MIT)
Copyright (c) 2016 Xavier Riley
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# Aubio
## warning: pre-alpha subject to change
A Ruby binding for the `aubio` library.
# What is aubio?
In their own words...
> aubio is a tool designed for the extraction of annotations from audio signals. Its features include segmenting a sound file before each of its attacks, performing pitch detection, tapping the beat and producing midi streams from live audio.
- http://aubio.org/
## Prerequisites
`brew install aubio --with-libsndfile --with-fftw --with-libsamplerate`
## Using `ffi_gen` to autogenerate bindings
```
brew install aubio --with-libsndfile --with-fftw --with-libsamplerate
brew install llvm --with-clang --enable-shared
# clone this repo and cd into the root folder, then run
LD_LIBRARY_PATH="/usr/local/opt/llvm35/lib/llvm-3.5/lib" ruby aubio-ffi-generator.rb
```
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'aubio'
```
And then execute:
$ bundle
Or install it yourself as:
$ gem install aubio
## Usage
`Aubio` just needs a path to a sound file:
```
my_file = Aubio.open("/path/to/file")
```
From there you can access the following:
```
my_file.onsets # list of extracted onsets
my_file.pitches # list of extracted pitches
my_file.beats # where one would tap their foot
# NOT YET IMPLEMENTED # my_file.notes # list of onsets with pitches
# NOT YET IMPLEMENTED # my_file.silences # list of silent regions
# NOT YET IMPLEMENTED # my_file.mel_frequency_cepstral_coefficients # list of mfccs
```
All of these are Ruby `Enumerator`s which means they'll respond to `.next`, and so on. If you want the whole list all at once call `.to_a` on it.
### Avoiding memory leaks
The underlying C library allocates some memory so to clean this up you'll need to run
my_file.close
to free it up.
## Data format
Each "event" that `aubio` describes is represented as a `Hash` like so:
```
my_file.onsets.first #=> {s: 0.0, ms: 0.0, start: 1, end: 0}
```
`s` and `ms` refer to seconds and milliseconds respectively.
`start: 1` and `end: 1` are special events that describe the start and the end of the audio file respectively. Whilst the `start` event at `0.0s` is usually an onset, the `end` is a convenience added by the Ruby wrapper to allow for easier slicing of sound files into samples.
### Still to implement
`relative` is the point at which the event occurs relative to the overall length of the original sound file, scaled between `0` and `1` (i.e. `relative: 0.5` is exactly halfway through).
`confidence` is a score returned from `aubio` which may be useful for threshold type operations.
## Optional params
The `Aubio#open` method can take a list of optional params like so:
```
:sample_rate
# Fetch the input source, resampled at the given sampling rate. The rate should be specified in Hertz as an integer. If 0, the sampling rate of the original source will be used. Defaults to 0.
:bufsize
The size of the buffer to analyze, that is the length of the window used for spectral and temporal computations. Defaults to 512.
:hopsize
```
e.g.
```
Aubio.open("/path/to/audio/file", sample_rate: 44100)
```
## Bugs / Still to do
* better tests
* use `Onsets` class as a basis to implement the other functions
* improve accuracy of bpm - seems to be consistently too fast on things I've tried
* look into streaming results for live inputs
## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/xavriley/aubio. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
## License
The gem is available as open source under the terms of the [MIT](http://opensource.org/licenses/MIT).
require "bundler/gem_tasks"
require "rake/testtask"
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.libs << "lib"
t.test_files = FileList['test/**/*_test.rb']
end
task :default => :spec
# this needs to be set on the command line when running this script
# ENV['LD_LIBRARY_PATH'] = "/usr/local/opt/llvm35/lib/llvm-3.5/lib"
require 'ffi_gen'
FFIGen.generate(
module_name: "Aubio::Api",
ffi_lib: "/usr/local/Cellar/aubio/0.4.4/lib/libaubio.dylib",
headers: Dir["/usr/local/Cellar/aubio/0.4.4/include/aubio/**/*.h"],
cflags: `/usr/local/opt/llvm35/bin/llvm-config-3.5 --cflags`.split(" "),
# the following can be used to trim the aubio_ from the generated function names
# prefixes: ["aubio_", "CX"],
prefixes: [],
output: "lib/aubio/aubio-ffi.rb"
)
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'aubio/version'
Gem::Specification.new do |spec|
spec.name = "aubio"
spec.version = Aubio::VERSION
spec.authors = ["Xavier Riley"]
spec.email = ["xavriley@hotmail.com"]
spec.summary = %q{Ruby bindings for the aubio audio library}
spec.description = %q{Aubio is a tool designed for the extraction of annotations from audio signals. Its features include segmenting a sound file before each of its attacks, performing pitch detection, tapping the beat and producing midi streams from live audio.}
spec.homepage = "https://github.com/xavriley/ruby-aubio"
spec.license = "MIT"
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
spec.add_dependency "ffi", "~> 1.9"
spec.add_development_dependency "bundler"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "minitest", "~> 5.0"
spec.add_development_dependency "pry"
spec.add_development_dependency "ffi_gen"
end
#!/usr/bin/env ruby
require "bundler/setup"
require "aubio"
# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.
# (If you use this, don't forget to add pry to your Gemfile!)
# require "pry"
# Pry.start
require "irb"
IRB.start
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx
bundle install
# Do any other automated setup that you need to do here
require_relative "aubio/version"
require_relative "aubio/aubio-ffi"
require_relative "aubio/onsets"
require_relative "aubio/pitches"
require_relative "aubio/beats"
module Aubio
class AubioException < Exception; end
class FileNotFound < AubioException; end
class AlreadyClosed < AubioException; end
class InvalidAudioInput < AubioException; end
class Base
def initialize(path, params)
raise FileNotFound unless File.file?(path)
sample_rate = params[:sample_rate] || 44100
hop_size = params[:hop_size] || 512
@is_closed = false
@source = Api.new_aubio_source(path, sample_rate, hop_size)
@params = params
check_for_valid_audio_source(path)
end
def close
Api.del_aubio_source(@source)
@is_closed = true
end
def onsets
check_for_closed
Onsets.new(@source, @params).each
end
def pitches
check_for_closed
Pitches.new(@source, @params).each
end
def enum_beats
check_for_closed
Beats.new(@source, @params).each
end
def beats
check_for_closed
beats = Beats.new(@source, @params).each.to_a
# fill in the zero beat
beats = beats.unshift(
beats.first.merge({
confidence: 1,
s: 0.0,
ms: 0.0,
sample_no: 0,
rel_start: 0.0
})
)
# fetch the rel_end from the next beat
# using 1.0 for the last beat
beats = beats.each_cons(2).map {|a,b|
a.merge({
rel_end: (b[:rel_start] || 1.0)
})
}
# set minimum inter-onset interval in seconds
# allows for 4/4 at 400bpm (faster than most music)
# filters beats detected too closely together
minioi = @params[:minioi] || 0.15
filtered_beats = [beats.first]
beats.each do |b|
if (b[:s] - filtered_beats.last[:s]) > minioi
filtered_beats << b
end
end
# TODO: are there other smoothing methods that would be useful here?
filtered_beats
end
def bpm
check_for_closed
beat_locations = self.beats
beat_periods = beat_locations.each_cons(2).map {|a,b| b[:s] - a[:s] }
return 60.0 if beat_locations.length == 1
# use interquartile median to discourage outliers
s = beat_periods.length
qrt_lower_idx = (s/4.0).floor
qrt_upper_idx = qrt_lower_idx * 3
interquartile_beat_periods = beat_periods[qrt_lower_idx..qrt_upper_idx]
# Calculate median
iqs = interquartile_beat_periods.length
iq_median_beat_period = interquartile_beat_periods.sort[(iqs/2.0).floor() - 1]
60.0 / iq_median_beat_period
end
private
def check_for_closed
raise AlreadyClosed if @is_closed
end
def check_for_valid_audio_source(path)
begin
@source.read_pointer
rescue FFI::NullPointerError
raise InvalidAudioInput.new(%Q{
Couldn't read file at #{path}
Did you install aubio with libsndfile support?
})
end
end
end
end
module Aubio
def self.open(path, params = {})
Base.new(path, params)
end
end
module Aubio
class Beats
def initialize(aubio_source, params)
# TODO: cleanup param dups
@sample_rate = params[:sample_rate] || 44100
@window_size = params[:window_size] || 1024
@hop_size = params[:hop_size] || 512
@source = aubio_source
@tempo = Api.new_aubio_tempo('specdiff', @window_size, @hop_size, @sample_rate)
# create output for source
@sample_buffer = Api.new_fvec(@hop_size)
# create output for beat
@out_fvec = Api.new_fvec(1)
end
def each
return enum_for(:each) unless block_given?
total_frames_counter = 0
read_buffer = FFI::MemoryPointer.new(:int)
total_samples = Api.aubio_source_get_duration(@source).to_f
loop do
# Perform tempo calculation
Api.aubio_source_do(@source, @sample_buffer, read_buffer)
Api.aubio_tempo_do(@tempo, @sample_buffer, @out_fvec)
# Retrieve result
is_beat = Api.fvec_get_sample(@out_fvec, 0)
no_of_bytes_read = read_buffer.read_int
total_frames_counter += no_of_bytes_read
if is_beat > 0.0
tempo_samples = Api.aubio_tempo_get_last(@tempo)
tempo_seconds = Api.aubio_tempo_get_last_s(@tempo)
tempo_milliseconds = Api.aubio_tempo_get_last_ms(@tempo)
tempo_confidence = Api.aubio_tempo_get_confidence(@tempo)
output = {
:confidence => tempo_confidence,
:s => tempo_seconds,
:ms => tempo_milliseconds,
:sample_no => tempo_samples,
:total_samples => total_samples,
:rel_start => tempo_samples/total_samples
}
yield output
end
if no_of_bytes_read != @hop_size
# there's no more audio to look at
# Let's output one last tempo to mark the end of the file
total_time = total_frames_counter.to_f / @sample_rate.to_f
output = {
:confidence => 1.0,
:s => total_time,
:ms => total_time/1000.0,
:sample_no => total_samples,
:total_samples => total_samples
}
yield output
# clean up
Api.del_aubio_tempo(@tempo)
Api.del_fvec(@sample_buffer)
Api.del_fvec(@out_fvec)
break
end
end
end
end
end
require 'ffi'
module Aubio
module LegacyApi #:nodoc
extend FFI::Library
# idea inspired by https://github.com/qoobaa/magic/blob/master/lib/magic/api.rb
lib_paths = Array(ENV["AUBIO_LIB"] || Dir["/{opt,usr}/{,local/}{lib,lib64,Cellar/aubio**,lib/arm-linux-gnueabihf}/libaubio.{*.dylib,so.*}"])
fallback_names = %w(libaubio.4.2.2.dylib libaubio.so.1 aubio1.dll)
ffi_lib(lib_paths + fallback_names)
# tempo
attach_function :new_aubio_tempo, [ :string, :int, :int, :int ], :pointer
attach_function :aubio_tempo_do, [:pointer, :pointer, :pointer], :void
attach_function :aubio_tempo_get_last, [:pointer], :int
attach_function :aubio_tempo_get_last_s, [:pointer], :float
attach_function :aubio_tempo_get_last_ms, [:pointer], :float
attach_function :aubio_tempo_set_silence, [:pointer, :float], :int
attach_function :aubio_tempo_get_silence, [:pointer], :float
attach_function :aubio_tempo_set_threshold, [:pointer, :float], :int
attach_function :aubio_tempo_get_threshold, [:pointer], :float
attach_function :aubio_tempo_get_bpm, [:pointer], :float
attach_function :aubio_tempo_get_confidence, [:pointer], :float
attach_function :del_aubio_tempo, [:pointer], :void
# beattracking / misc
attach_function :new_aubio_beattracking, [:int, :int, :int], :pointer
attach_function :aubio_beattracking_do, [:pointer, :pointer, :pointer], :void
attach_function :aubio_beattracking_get_bpm, [:pointer], :float
attach_function :aubio_filter_do, [:pointer, :pointer], :void
attach_function :new_aubio_filter_a_weighting, [:int], :pointer
# source
attach_function :new_aubio_source, [:string, :int, :int], :pointer
attach_function :aubio_source_do, [:pointer, :pointer, :pointer], :void
attach_function :aubio_source_do_multi, [:pointer, :pointer, :pointer], :void
attach_function :aubio_source_get_samplerate, [:pointer], :int
attach_function :aubio_source_get_channels, [:pointer], :int
attach_function :aubio_source_seek, [:pointer, :int], :int
attach_function :aubio_source_close, [:pointer], :int
attach_function :del_aubio_source, [:pointer], :void
# sink
attach_function :new_aubio_sink, [:string, :int], :pointer
attach_function :aubio_sink_preset_samplerate, [:pointer, :int], :void
attach_function :aubio_sink_preset_channels, [:pointer, :int], :void
attach_function :aubio_sink_get_samplerate, [:pointer], :int
attach_function :aubio_sink_get_channels, [:pointer], :int
attach_function :aubio_sink_do, [:pointer, :pointer, :int], :void
attach_function :aubio_sink_do_multi, [:pointer, :pointer, :int], :void
attach_function :aubio_sink_close, [:pointer], :int
attach_function :del_aubio_sink, [:pointer], :void
# onset
attach_function :new_aubio_onset, [:string, :int, :int, :int], :pointer
attach_function :aubio_onset_do, [:pointer, :pointer, :pointer], :void
attach_function :aubio_onset_get_last, [:pointer], :int
attach_function :aubio_onset_get_last_s, [:pointer], :float
attach_function :aubio_onset_get_last_ms, [:pointer], :float
attach_function :aubio_onset_set_silence, [:pointer, :float], :int
attach_function :aubio_onset_get_silence, [:pointer], :float
attach_function :aubio_onset_get_descriptor, [:pointer], :float
attach_function :aubio_onset_get_thresholded_descriptor, [:pointer], :float
attach_function :aubio_onset_set_threshold, [:pointer, :float], :int
attach_function :aubio_onset_set_minioi, [:pointer, :int], :int
attach_function :aubio_onset_set_minioi_s, [:pointer, :int], :int
attach_function :aubio_onset_set_minioi_ms, [:pointer, :float], :int
attach_function :aubio_onset_set_delay, [:pointer, :int], :int
attach_function :aubio_onset_set_delay_s, [:pointer, :int], :int
attach_function :aubio_onset_set_delay_ms, [:pointer, :float], :int
attach_function :aubio_onset_get_minioi, [:pointer], :int
attach_function :aubio_onset_get_minioi_s, [:pointer], :float
attach_function :aubio_onset_get_minioi_ms, [:pointer], :float
attach_function :aubio_onset_get_delay, [:pointer], :int
attach_function :aubio_onset_get_delay_s, [:pointer], :float
attach_function :aubio_onset_get_delay_ms, [:pointer], :float
attach_function :aubio_onset_get_threshold, [:pointer], :float
attach_function :del_aubio_onset, [:pointer], :void
# pitch
attach_function :new_aubio_pitch, [:string, :int, :int, :int], :pointer
attach_function :aubio_pitch_do, [:pointer, :pointer, :pointer], :void
attach_function :aubio_pitch_set_tolerance, [:pointer, :int], :int
attach_function :aubio_pitch_set_unit, [:pointer, :string], :int
attach_function :aubio_pitch_set_silence, [:pointer, :float], :int
attach_function :aubio_pitch_get_silence, [:pointer], :float
attach_function :aubio_pitch_get_confidence, [:pointer], :float
attach_function :del_aubio_pitch, [:pointer], :void
# new fvec
attach_function :new_fvec, [:int], :pointer
attach_function :del_fvec, [:pointer], :void
attach_function :fvec_get_sample, [:pointer, :int], :float
attach_function :fvec_set_sample, [:pointer, :float, :int], :void
attach_function :fvec_get_data, [:pointer], :float
attach_function :fvec_print, [:pointer], :void
attach_function :fvec_set_all, [:pointer, :float], :void
attach_function :fvec_zeros, [:pointer], :void
attach_function :fvec_rev, [:pointer], :void
attach_function :fvec_weight, [:pointer, :pointer], :void
attach_function :fvec_copy, [:pointer, :pointer], :void
attach_function :fvec_ones, [:pointer], :void
end
end
module Aubio
class Onsets
def initialize(aubio_source, params)
# TODO: cleanup param dups
@sample_rate = params[:sample_rate] || 44100
@window_size = params[:window_size] || 1024
@hop_size = params[:hop_size] || 512
@source = aubio_source
@onset = Api.new_aubio_onset('default', @window_size, @hop_size, @sample_rate)
Api.aubio_onset_set_minioi_ms(@onset, 12.0)
Api.aubio_onset_set_threshold(@onset, 0.3)
# create output for source
@sample_buffer = Api.new_fvec(@hop_size)
# create output for pitch and beat
@out_fvec = Api.new_fvec(1)
end
def each
return enum_for(:each) unless block_given?
total_frames_counter = 0
read_buffer = FFI::MemoryPointer.new(:int)
loop do
# Perform onset calculation
Api.aubio_source_do(@source, @sample_buffer, read_buffer)
Api.aubio_onset_do(@onset, @sample_buffer, @out_fvec)
# Retrieve result
onset_new_peak = Api.fvec_get_sample(@out_fvec, 0)
no_of_bytes_read = read_buffer.read_int
total_frames_counter += no_of_bytes_read
if onset_new_peak > 0.0
onset_seconds = Api.aubio_onset_get_last_s(@onset)
onset_milliseconds = Api.aubio_onset_get_last_ms(@onset)
output = {
:s => onset_seconds,
:ms => onset_milliseconds,
:start => (onset_seconds == 0.0 ? 1 : 0),
:end => 0
}
yield output
end
if no_of_bytes_read != @hop_size
# there's no more audio to look at
# Let's output one last onset to mark the end of the file
total_time = total_frames_counter.to_f / @sample_rate.to_f
output = {
:s => total_time,
:ms => total_time/1000.0,
:start => 0,
:end => 1
}
yield output
# clean up
Api.del_aubio_onset(@onset)
Api.del_fvec(@sample_buffer)
Api.del_fvec(@out_fvec)
break
end
end
end
end
end
module Aubio
class Pitches
def initialize(aubio_source, params)
# TODO: cleanup param dups
@sample_rate = params[:sample_rate] || 44100
@window_size = params[:window_size] || 1024
@hop_size = params[:hop_size] || 512
# Set the tolerance for the pitch detection algorithm.
# Typical values range between 0.2 and 0.9.
# Pitch candidates found with a confidence less than this threshold will not be selected.
# The higher the threshold, the more confidence in the candidates.
@confidence_thresh = params[:confidence_thresh] || 0.9
@pitch_method = params[:pitch_method] || "yinfast"
@source = aubio_source
@pitch = Api.new_aubio_pitch(@pitch_method, @window_size, @hop_size, @sample_rate)
Api.aubio_pitch_set_unit(@pitch, 'midi')
Api.aubio_pitch_set_tolerance(@pitch, @confidence_thresh)
# create output for source
@sample_buffer = Api.new_fvec(@hop_size)
# create output for pitch and beat
@out_fvec = Api.new_fvec(1)
end
def each
return enum_for(:each) unless block_given?
total_frames_counter = 0
read_buffer = FFI::MemoryPointer.new(:int)
last_pitch = 0
loop do
# Perform pitch calculation
Api.aubio_source_do(@source, @sample_buffer, read_buffer)
Api.aubio_pitch_do(@pitch, @sample_buffer, @out_fvec)
# Retrieve result
pitch = Api.fvec_get_sample(@out_fvec, 0)
confidence = Api.aubio_pitch_get_confidence(@pitch)
no_of_bytes_read = read_buffer.read_int
total_frames_counter += no_of_bytes_read
if (last_pitch - pitch).abs >= 1 and confidence > @confidence_thresh
output = {
:pitch => pitch,
:confidence => confidence,
:start => (total_frames_counter == 0 ? 1 : 0),
:end => 0
}
yield output
end
last_pitch = pitch
if no_of_bytes_read != @hop_size
# there's no more audio to look at
# Let's output one last pitch to mark the end of the file
total_time = total_frames_counter.to_f / @sample_rate.to_f
output = {
:pitch => pitch,
:confidence => confidence,
:start => 0,
:end => 1
}
yield output
# clean up
Api.del_aubio_pitch(@pitch)
Api.del_fvec(@sample_buffer)
Api.del_fvec(@out_fvec)
break
end
end
end
end
end
require 'test_helper'
class AubioTest < Minitest::Test
def setup
@not_audio_file_path = File.expand_path("../sounds/not_an_audio_file.txt", __FILE__)
@loop_amen_path = File.expand_path("../sounds/loop_amen.wav", __FILE__)
@loop_amen_full_path = File.expand_path("../sounds/loop_amen_full.wav", __FILE__)
@french_house_path = File.expand_path("../sounds/french_house_thing.wav", __FILE__)
@bobby_mcferrin_path = File.expand_path("../sounds/bobby.wav", __FILE__)
end
def test_that_it_has_a_version_number
refute_nil ::Aubio::VERSION
end
def test_it_checks_file_existence
assert_raises Aubio::FileNotFound do
result = Aubio.open(@loop_amen_path + "e")
end
end
def test_it_checks_file_is_readable_audio
assert_raises Aubio::InvalidAudioInput do
Aubio.open(@not_audio_file_path)
end
end
def test_it_checks_if_file_has_been_closed
source = Aubio.open(@loop_amen_path)
source.close
assert_raises Aubio::AlreadyClosed do
source.onsets
end
end
def test_it_calculates_onsets
result = Aubio.open(@loop_amen_path).onsets.to_a
pairs = result.take(3).zip([
{:s=>0.0, :ms=>0.0, :start=> 1, :end=>0},
{:s=>0.21174603700637817, :ms=>211.74603271484375, :start=> 0, :end=>0},
{:s=>0.4404761791229248, :ms=>440.4761657714844, :start=> 0, :end=>0},
])
pairs.each do |expected, actual|
assert_in_delta expected[:s], actual[:s], 0.25
end
end
def test_it_calculates_pitches
result = Aubio.open(@bobby_mcferrin_path, pitch_method: 'yinfast', confidence_thresh: 0.9).pitches.to_a
assert_in_delta 50.0, result[0][:pitch], 1.0
end
def test_it_calculates_beats
result = Aubio.open(@loop_amen_full_path).beats
assert_equal [
{:confidence=>1, :s=>0.0, :ms=>0.0, :sample_no=>0, :total_samples=>302400.0, :rel_start=>0.0, :rel_end=>0.2202149470899471}
], result.first(1)
assert_equal 13, result.length
end
def test_it_filters_beats_based_on_minimum_interval
result = Aubio.open(@loop_amen_full_path, minioi: 0.5).beats
assert_equal 8, result.length
end
def test_it_calculates_bpm
# TODO: see if this can be improved
# the actual bpm of that sample is 125
result = Aubio.open(@french_house_path).bpm
assert_equal 126.66357966281865, result
end
def test_it_provides_default_bpm_as_fallback
result = Aubio.open(@loop_amen_path).bpm
assert_equal 60, result
end
end
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
require 'aubio'
require 'minitest/autorun'
......@@ -10,7 +10,7 @@ rmdir server\erlang\sonic_pi_server\priv /s /q
rmdir server\native\plugins /s /q
@echo Copying aubio to the server...
copy external\build\aubio-prefix\src\aubio-build\Release\libaubio-5.dll server\native\ruby\bin
copy external\build\aubio-prefix\src\aubio-build\Release\aubio_onset.exe server\native\
@echo Copying all other native files to server...
xcopy /Y /I /R /E ..\prebuilt\windows\x64\*.* server\native
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册