diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..bd6c18b --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,10 @@ +build-job: + stage: build + image: ubuntu:22.04 + script: + - apt-get update + - apt-get install -y git cmake g++ freeglut3-dev qtbase5-dev qt5-qmake qtbase5-dev-tools + - mkdir -p build + - cd build + - cmake .. + - cmake --build . diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..71a5d5e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,93 @@ +cmake_minimum_required(VERSION 3.16) +project(samplebrain VERSION 1.0 LANGUAGES C CXX) + +include(Dependencies.cmake) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +# Set up AUTOMOC and some sensible defaults for runtime execution +# When using Qt 6.3, you can replace the code block below with +# qt_standard_project_setup() +set(CMAKE_AUTOMOC ON) +include(GNUInstallDirs) +set(CMAKE_AUTOUIC ON) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(QT NAMES Qt5 REQUIRED COMPONENTS Core) +find_package(Qt5 REQUIRED COMPONENTS Gui Widgets) + +add_executable(samplebrain WIN32 MACOSX_BUNDLE + app/MainWindow.cpp app/MainWindow.h + app/SettingsDialog.cpp app/SettingsDialog.h + app/audio_thread.cpp + app/feedback.cpp + app/process_thread.cpp + app/qtmain.cpp + app/sound_items.cpp + brain/src/aquila/filter/MelFilter.cpp + brain/src/aquila/filter/MelFilterBank.cpp + brain/src/aquila/transform/Dct.cpp + brain/src/block.cpp + brain/src/block_stream.cpp + brain/src/brain.cpp + brain/src/fft.cpp + brain/src/mfcc.cpp + brain/src/renderer.cpp + brain/src/search_params.cpp + brain/src/spiralcore/OSC_server.cpp + brain/src/spiralcore/allocator.cpp + brain/src/spiralcore/audio.cpp + brain/src/spiralcore/command_ring_buffer.cpp + brain/src/spiralcore/portaudio_client.cpp + brain/src/spiralcore/ring_buffer.cpp + brain/src/spiralcore/sample.cpp + brain/src/spiralcore/stream.cpp + brain/src/status.cpp + brain/src/window.cpp + app/gui/samplebrain.ui + app/gui/settings.ui +) +target_include_directories(samplebrain PRIVATE + . + brain/src +) + +target_link_libraries(samplebrain PRIVATE + Qt5::Core + Qt5::Gui + Qt5::Widgets + fftw3 + lo_shared + portaudio + sndfile +) + +# Resources: +set(samplebrain_resource_files + "app/images/at.png" + "app/images/pause.png" + "app/images/play.png" + "app/images/record.png" + "app/images/settings.png" + "app/images/stop.png" +) + +qt5_add_resources(samplebrain "samplebrain" + PREFIX + "/images" +# BASE +# "app" + FILES + ${samplebrain_resource_files} +) + +install(TARGETS samplebrain + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +# Consider using qt_generate_deploy_app_script() for app deployment if +# the project can use Qt 6.3. In that case rerun qmake2cmake with +# --min-qt-version=6.3. diff --git a/Dependencies.cmake b/Dependencies.cmake new file mode 100644 index 0000000..848c56a --- /dev/null +++ b/Dependencies.cmake @@ -0,0 +1,49 @@ +################################################################################ +# FetchContent +################################################################################ + +include(FetchContent) + +################################################################################ +# fftw3 +################################################################################ + +FetchContent_Declare( + fftw3 + URL http://fftw.org/fftw-3.3.10.tar.gz + URL_HASH MD5=8ccbf6a5ea78a16dbc3e1306e234cc5c) +FetchContent_MakeAvailable(fftw3) + +include_directories(${fftw3_SOURCE_DIR}/api) + +################################################################################ +# liblo +################################################################################ + +FetchContent_Declare( + liblo + URL http://downloads.sourceforge.net/liblo/liblo-0.31.tar.gz + URL_HASH MD5=14378c1e74c58e777fbb4fcf33ac5315) +FetchContent_MakeAvailable(liblo) + +add_subdirectory(${liblo_SOURCE_DIR}/cmake) + +################################################################################ +# PortAudio +################################################################################ + +FetchContent_Declare( + portaudio + URL http://files.portaudio.com/archives/pa_stable_v190700_20210406.tgz + URL_HASH MD5=ad319249932c6794b551d954b8844402) +FetchContent_MakeAvailable(portaudio) + +################################################################################ +# libsndfile +################################################################################ + +FetchContent_Declare( + libsndfile + GIT_REPOSITORY https://github.com/libsndfile/libsndfile + GIT_TAG 1.1.0) +FetchContent_MakeAvailable(libsndfile) diff --git a/README.md b/README.md index 75c25b0..824ef4b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ tweakable parameters until it became slightly out of control. ![](docs/pics/screenshot.png) -Quick start: +# How do I use this thing? 1. Load a bunch of short wav files into the brain 2. Click (re)generate brain @@ -23,42 +23,45 @@ Quick start: 5. Press play 6. Tweak brain -The default block size (3000) is really high to prevent CPU glitches - -500 to 1000 is a better range. Larger wav files like whole tracks can -be used, but take a long time to process, after which they can be -saved as "brain" files and instantly reloaded. +Larger wav files like whole tracks can be used, but take a long time +to process, after which they can be saved as "brain" files and +instantly reloaded. -# [Demo brain session](https://static.thentrythis.org/samplebrain/demo.samplebrain) - -Load this file using "load session" not "load brain" (sessions contain +Check the [Manual](docs/manual.md) here for the details on all the +parameters and try out the [demo brain session](https://static.thentrythis.org/samplebrain/demo.samplebrain). +Load the demo using "load session" not "load brain" (sessions contain both the target and brain samples). The original samples used to create the demo session [can be found here for testing](https://static.thentrythis.org/samplebrain/samples/). -# [Manual](docs/manual.md) - -Full description of all the parameters and a bit of the thinking -behind it. - # Download As this is experimental non-commercial software (only originally written to run on a couple of computers!) you will have to bear with us as we gradually stabilise things based on your feedback. There might currently be problems running it on 64bit Windows. + +* **Windows**: [samplebrain_0.18.4_win.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.4_win.zip) +* **Mac (intel/m1)**: [samplebrain_0.18.4_macintel.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.4_macintel.app.zip) + +Changes in 0.18.4: New audio device settings window and updated +windows build. Better default block size, tool tip tweaks and fixes +for dark themes by [Claude Heiland-Allen](https://mathr.co.uk/). + +For old versions see the [changelog](changelog.md) -* **Windows**: [samplebrain_0.18.1_win.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.1_win.zip) -* **Mac (intel/m1)**: [samplebrain_0.18.1_macintel.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.1_macintel.app.zip) - -Thank you to [Nik Gaffney](http://fo.am) for help with the Apple builds - Mac note: As this software is not on the apple store, to run the binary you need to tell your mac it's ok: Go to System Preferences > Security & Privacy > General. At the bottom of the window, select "Allow apps to be downloaded from Anywhere". -**Linux install (Ubuntu)** +Thank you to [Nik Gaffney](http://fo.am) for help with the Apple +builds. + +# Linux install +Download on Flathub +#### Ubuntu $ sudo add-apt-repository ppa:thentrythis/samplebrain $ sudo apt update $ sudo apt install samplebrain @@ -67,92 +70,13 @@ If you'd like the right font, optionally: $ sudo apt install ttf-mscorefonts-installer -# Old/broken/spurious binaries - -* **Windows* *: [samplebrain_0.18_win.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18_win.zip) -* **Mac (intel)**: [samplebrain_0.18_macintel.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18_macintel.zip) -* **Mac (m1)**: [samplebrain_0.18_m1_v2.dmg](https://static.thentrythis.org/samplebrain/samplebrain_0.18_m1_v2.dmg) - -# Building from source -## Linux (Ubuntu) -Install libraries for the sample engine (use brew on mac, MinGW on win): - - $ sudo apt install libsndfile1-dev portaudio19-dev liblo-dev libfftw3-dev - -Install dependencies for the interface: - - $ sudo apt install build-essential qtcreator qt5-default - -Build & run it: - - $ mkdir build - $ cd build - $ qmake .. - $ make - $ sudo make install - $ samplebrain - -## Mac -Install libraries for sample engine: - - $ brew install fftw portaudio liblo libsndfile - -Install dependencies for the interface: - - $ brew install qt - $ brew link qt - -Build & run it: - - $ mkdir build - $ cd build - $ qmake .. - $ make - -`samplebrain.app` should then be in the app folder for you to run. - -# Mac build additions - -To make a mac app bundle: - -Run `macdeployqt` which copies all dependencies inside the app. - - $ cd build - $ macdeployqt - -If the icon is not visible, you might need to copy desktop/samplebrain.icns (the icon) to the Resources directory in the app bundle. - - $ cp ../desktop/samplebrain.icns samplebrain.app/Contents/Resources - -Then edit Info.plist to add samplebrain.icns to CFBundleIconFile. Key `CFBundleIconFile` should match: - - CFBundleIconFile - samplebrain.icns - -You might also need to resign the app bundle after making any changes - - $ codesign --force --deep --sign - samplebrain.app - -## What's here - -1. brain: - * samplebrain engine code -2. app: - * code to build the Qt GUI app -3. gui: - * qt designer project files -4. desktop: - * various icon files etc -4. cooking: - * some sketches and ideas - * proof of concept written in python - * brief initial (abandoned) attempt at clojure version +# [Building from source](building.md) MFCC algo courtesy of the Aquila library by Zbigniew Siciarz MIT/X11 -licence 2007-2014 (see brain/src/aquila/LICENCE) +licence 2007-2014 (see brain/src/aquila/LICENCE). This program is free +software licenced under GNU General Public License version 2 (see +LICENCE). Written by [Dave Griffiths at Then Try This](http://thentrythis.org). -This program is free software licenced under GNU General Public -License version 2 (see LICENCE). - -Written by [Dave Griffiths at Then Try This](http://thentrythis.org). +## Links +To find related tech like [CataRT](https://ircam-ismm.github.io/max-msp/catart.html), [bbcut2](https://composerprogrammer.com/bbcut2.html), [eargram](https://sites.google.com/site/eargram/) and [sCrAmBlEd?HaCkZ!](https://www.youtube.com/watch?v=eRlhKaxcKpA) search up [granular synthesis](http://granularsynthesis.com/guide.php), [concatenative synthesis](https://hal.archives-ouvertes.fr/hal-01161337), [neural audio synthesis](https://github.com/acids-ircam/RAVE), [sinewave speech](http://www.lifesci.sussex.ac.uk/home/Chris_Darwin/SWS/), automated breakbeat cutting, audio mosaicing and plunderphonics/plundermatics. diff --git a/app/MainWindow.cpp b/app/MainWindow.cpp index 850d0de..623f583 100644 --- a/app/MainWindow.cpp +++ b/app/MainWindow.cpp @@ -14,6 +14,9 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#ifndef MAIN_WINDOW +#define MAIN_WINDOW + #include #include #include @@ -24,9 +27,12 @@ using namespace std; -MainWindow::MainWindow() : +MainWindow::MainWindow(const string &port, const string &audio_port, const string &process_port, QSettings *settings) : m_last_file("."), - m_feedback("8890") + m_feedback(port), + m_audio_port(audio_port), + m_process_port(process_port), + m_format_string("Microsoft WAV (*.wav);;SGI/Apple AIFF (*.aiff);;SGI/Apple AIFC (*.aifc);;Sun/DEC/NeXT AU (*.au);;Sun/DEC/NeXT SND (*.snd);;Fasttracker 2 XI (*.xi);;Free Lossless Audio Codec FLAC (*.flac);;All files (*.*)") { m_sound_item_enable_mapper = new QSignalMapper(this); m_sound_item_delete_mapper = new QSignalMapper(this); @@ -45,7 +51,9 @@ MainWindow::MainWindow() : m_Ui.brain_contents->setAlignment(Qt::AlignTop); m_Ui.brain_contents->setSpacing(0); m_Ui.brain_contents->setContentsMargins(0,0,0,0); - + + m_settings_dialog = new SettingsDialog(this,settings); + // add default local dest // turn on first one @@ -54,8 +62,8 @@ MainWindow::MainWindow() : for (int i=0; i<10; i++) { osc_destination d; d.m_id=i; - d.m_audio_address = lo_address_new_from_url("osc.udp://localhost:8888"); - d.m_process_address = lo_address_new_from_url("osc.udp://localhost:8889"); + d.m_audio_address = lo_address_new_from_url(string("osc.udp://localhost:"+m_audio_port).c_str()); + d.m_process_address = lo_address_new_from_url(string("osc.udp://localhost:"+m_process_port).c_str()); if (i==0) d.m_enabled=true; else d.m_enabled=false; add_gui_address(d,enable_mapper); @@ -183,3 +191,5 @@ void MainWindow::init_from_session(const string &filename) { } + +#endif diff --git a/app/MainWindow.h b/app/MainWindow.h index 90abd9d..9b97127 100644 --- a/app/MainWindow.h +++ b/app/MainWindow.h @@ -18,7 +18,9 @@ #include #include #include -#include "ui_samplebrain.h" +#include +#include "gui/ui_samplebrain.h" +#include "SettingsDialog.h" #include #include @@ -26,390 +28,430 @@ #include #include "feedback.h" #include "sound_items.h" +#include "audio_thread.h" using namespace std; using namespace spiralcore; +class QSettings; + class MainWindow : public QMainWindow { - Q_OBJECT + Q_OBJECT public: - MainWindow(); + MainWindow(const string &port, const string &audio_port, const string &process_port, QSettings *settings); + + void message(const string &msg) { + QMessageBox::information(this,"problem",msg.c_str(), QMessageBox::Ok); + } + bool ok() { + return m_feedback.ok(); + } + + // all this to work around liblo's use of varargs... + void send_audio_osc(const char *name, const char *types) { + for (auto dest:m_destinations) { + if (dest.m_enabled) { + lo_send(dest.m_audio_address,name,types); + } + } + } + template + void send_audio_osc(const char *name, const char *types, T val) { + for (auto dest:m_destinations) { + if (dest.m_enabled) { + lo_send(dest.m_audio_address,name,types,val); + } + } + } + + void send_process_osc(const char *name, const char *types) { + for (auto dest:m_destinations) { + if (dest.m_enabled) { + lo_send(dest.m_process_address,name,types); + } + } + } + + template + void send_process_osc(const char *name, const char *types, T val) { + for (auto dest:m_destinations) { + if (dest.m_enabled) { + lo_send(dest.m_process_address,name,types,val); + } + } + } + + void set_audio_thread(audio_thread *at) { + m_audio_thread=at; + m_audio_thread->start_audio(); + + for (auto &d:m_audio_thread->m_audio_device->m_client.m_devices) { + if (d.id==0) { + m_settings_dialog->m_Ui.deviceComboBox->clear(); + } + m_settings_dialog->m_Ui.deviceComboBox->addItem(d.name.c_str()); + + if (d.default_output==1) { + m_settings_dialog->m_device=d.name; + m_settings_dialog->m_Ui.deviceComboBox->setCurrentText(d.name.c_str()); + } + + } + + m_settings_dialog->m_Ui.messagesLabel->setText(m_audio_thread->m_audio_device->m_client.m_status.c_str()); + } + + audio_thread *get_audio_thread() { + return m_audio_thread; + } + protected: private slots: - void play_slot() { send_audio_osc("/start",""); } - void stop_slot() { send_audio_osc("/pause",""); } + void play_slot() { send_audio_osc("/start",""); } + void stop_slot() { send_audio_osc("/pause",""); } - void ratio_slot(int s) { - send_audio_osc("/ratio","f",s/100.0f); - m_Ui.doubleSpinBoxRatio->setValue(s/100.0f); - } - void ratio_slot(double s) { - send_audio_osc("/ratio","f",s); - m_Ui.sliderRatio->setValue(s*100); - } + void ratio_slot(int s) { + send_audio_osc("/ratio","f",s/100.0f); + m_Ui.doubleSpinBoxRatio->setValue(s/100.0f); + } + void ratio_slot(double s) { + send_audio_osc("/ratio","f",s); + m_Ui.sliderRatio->setValue(s*100); + } - void n_ratio_slot(int s) { - send_audio_osc("/n_ratio","f",s/100.0f); - m_Ui.doubleSpinBoxNRatio->setValue(s/100.0f); - } - void n_ratio_slot(double s) { - send_audio_osc("/n_ratio","f",s); - m_Ui.sliderNRatio->setValue(s*100); - } + void n_ratio_slot(int s) { + send_audio_osc("/n_ratio","f",s/100.0f); + m_Ui.doubleSpinBoxNRatio->setValue(s/100.0f); + } + void n_ratio_slot(double s) { + send_audio_osc("/n_ratio","f",s); + m_Ui.sliderNRatio->setValue(s*100); + } - void autotune(int s) { - send_audio_osc("/autotune","f",s/100.0f); - m_Ui.doubleSpinBoxAutotune->setValue(s/100.0f); - } - void autotune(double s) { - send_audio_osc("/autotune","f",s); - m_Ui.sliderAutotune->setValue(s*100); - } + void autotune(int s) { + send_audio_osc("/autotune","f",s/100.0f); + m_Ui.doubleSpinBoxAutotune->setValue(s/100.0f); + } + void autotune(double s) { + send_audio_osc("/autotune","f",s); + m_Ui.sliderAutotune->setValue(s*100); + } - void fft1_start_slot(int s) { send_audio_osc("/fft1_start","i",s); } - void fft1_end_slot(int s) { send_audio_osc("/fft1_end","i",s); } - void fft2_start_slot(int s){} // { m_renderer->get_params()->m_fft2_start=s; } - void fft2_end_slot(int s){} // { m_renderer->get_params()->m_fft2_end=s; } + void fft1_start_slot(int s) { send_audio_osc("/fft1_start","i",s); } + void fft1_end_slot(int s) { send_audio_osc("/fft1_end","i",s); } + void fft2_start_slot(int s){} // { m_renderer->get_params()->m_fft2_start=s; } + void fft2_end_slot(int s){} // { m_renderer->get_params()->m_fft2_end=s; } - void n_mix_slot(int s) { - send_audio_osc("/n_mix","f",s/100.0f); - m_Ui.doubleSpinBoxNMix->setValue(s/100.0f); - } - void n_mix_slot(double s) { - send_audio_osc("/n_mix","f",s); - m_Ui.sliderNMix->setValue(s*100); - } + void n_mix_slot(int s) { + send_audio_osc("/n_mix","f",s/100.0f); + m_Ui.doubleSpinBoxNMix->setValue(s/100.0f); + } + void n_mix_slot(double s) { + send_audio_osc("/n_mix","f",s); + m_Ui.sliderNMix->setValue(s*100); + } - void novelty_slot(int s) { - send_audio_osc("/novelty","f",s/100.0f); - m_Ui.doubleSpinBoxNovelty->setValue(s/100.0f); - } - void novelty_slot(double s) { - send_audio_osc("/novelty","f",s); - m_Ui.sliderNovelty->setValue(s*100); - } + void novelty_slot(int s) { + send_audio_osc("/novelty","f",s/100.0f); + m_Ui.doubleSpinBoxNovelty->setValue(s/100.0f); + } + void novelty_slot(double s) { + send_audio_osc("/novelty","f",s); + m_Ui.sliderNovelty->setValue(s*100); + } - void boredom_slot(int s) { - float v=s/100.0f; - send_audio_osc("/boredom","f",v); - m_Ui.doubleSpinBoxBoredom->setValue(v); - } - void boredom_slot(double s) { - send_audio_osc("/boredom","f",s); - m_Ui.sliderBoredom->setValue(s*100); - } - void synapses(int s) { - send_audio_osc("/synapses","i",s); - } - void target_mix_slot(int s) { - send_audio_osc("/target_mix","f",s/100.0f); - m_Ui.doubleSpinBoxTargetMix->setValue(s/100.0f); - } - void target_mix_slot(double s) { - send_audio_osc("/target_mix","f",s); - m_Ui.sliderTargetMix->setValue(s*100); - } - void search_stretch(int s) { - send_audio_osc("/search-stretch","i",s); - } - void slide_error(int s) { - send_audio_osc("/slide-error","i",s); - } - void stickyness_slot(int s) { - send_audio_osc("/stickyness","f",s/100.0f); - m_Ui.doubleSpinBoxStickyness->setValue(s/100.0f); - } - void stickyness_slot(double s) { - send_audio_osc("/stickyness","f",s); - m_Ui.sliderStickyness->setValue(s*100); - } + void boredom_slot(int s) { + float v=s/100.0f; + send_audio_osc("/boredom","f",v); + m_Ui.doubleSpinBoxBoredom->setValue(v); + } + void boredom_slot(double s) { + send_audio_osc("/boredom","f",s); + m_Ui.sliderBoredom->setValue(s*100); + } + void synapses(int s) { + send_audio_osc("/synapses","i",s); + } + void target_mix_slot(int s) { + send_audio_osc("/target_mix","f",s/100.0f); + m_Ui.doubleSpinBoxTargetMix->setValue(s/100.0f); + } + void target_mix_slot(double s) { + send_audio_osc("/target_mix","f",s); + m_Ui.sliderTargetMix->setValue(s*100); + } + void search_stretch(int s) { + send_audio_osc("/search-stretch","i",s); + } + void slide_error(int s) { + send_audio_osc("/slide-error","i",s); + } + void stickyness_slot(int s) { + send_audio_osc("/stickyness","f",s/100.0f); + m_Ui.doubleSpinBoxStickyness->setValue(s/100.0f); + } + void stickyness_slot(double s) { + send_audio_osc("/stickyness","f",s); + m_Ui.sliderStickyness->setValue(s*100); + } - void volume_slot(int s) { send_audio_osc("/volume","f",s/100.0f); } - void algo(int n) { send_audio_osc("/search_algo","i",n); } + void volume_slot(int s) { send_audio_osc("/volume","f",s/100.0f); } + void algo(int n) { send_audio_osc("/search_algo","i",n); } - void run_slot() {} - void load_target() { - m_last_file=QFileDialog::getOpenFileName( - this, - QString("Select an wav file"), - m_last_file, - QString("Sounds (*.wav)")); + void run_slot() {} + void load_target() { + m_last_file=QFileDialog::getOpenFileName(this, + QString("Select an audio file"), + m_last_file, + m_format_string); - send_process_osc("/load_target","s",m_last_file.toStdString().c_str()); + send_process_osc("/load_target","s",m_last_file.toStdString().c_str()); + } + void target_block_size(int s) { send_process_osc("/target_block_size","i",s); } + void target_block_overlap(double s) { send_process_osc("/target_overlap","f",s); } + void generate_target_blocks() { send_process_osc("/generate_target",""); } + void block_size(int s) { send_process_osc("/source_block_size","i",s); } + void block_overlap(double s) { send_process_osc("/source_overlap","f",s); } + void fft_spectrum_size(int) {} + void generate() { send_process_osc("/generate_brain",""); } + void load_sound() { + m_last_file=QFileDialog::getOpenFileName(this, + QString("Select a wav file"), + m_last_file, + m_format_string); + + if (m_last_file!="") { + send_process_osc("/load_sample","s",m_last_file.toStdString().c_str()); + sound_items::sound_item &si = m_sound_items.add(m_Ui.brain_contents, m_last_file.toStdString(),true); + QObject::connect(si.m_enable, SIGNAL(clicked()), m_sound_item_enable_mapper, SLOT(map())); + m_sound_item_enable_mapper->setMapping(si.m_enable, si.m_id); + QObject::connect(si.m_del, SIGNAL(clicked()), m_sound_item_delete_mapper, SLOT(map())); + m_sound_item_delete_mapper->setMapping(si.m_del, si.m_id); } - void target_block_size(int s) { send_process_osc("/target_block_size","i",s); } - void target_block_overlap(double s) { send_process_osc("/target_overlap","f",s); } - void generate_target_blocks() { send_process_osc("/generate_target",""); } - void block_size(int s) { send_process_osc("/source_block_size","i",s); } - void block_overlap(double s) { send_process_osc("/source_overlap","f",s); } - void fft_spectrum_size(int) {} - void generate() { send_process_osc("/generate_brain",""); } - void load_sound() { - m_last_file=QFileDialog::getOpenFileName( - this, - QString("Select a wav file"), - m_last_file, - QString("Sounds (*.wav)")); + } - send_process_osc("/load_sample","s",m_last_file.toStdString().c_str()); - sound_items::sound_item &si = m_sound_items.add(m_Ui.brain_contents, m_last_file.toStdString(),true); + void load_sounds() { + m_last_file=QFileDialog::getExistingDirectory(this, + QString("Select a directory of wav files"), + m_last_file); + + + QDirIterator dirIt(m_last_file,QDirIterator::Subdirectories); + while (dirIt.hasNext()) { + dirIt.next(); + if (QFileInfo(dirIt.filePath()).isFile() && + QFileInfo(dirIt.filePath()).suffix() == "wav") { + send_process_osc("/load_sample","s",dirIt.filePath().toStdString().c_str()); + + sound_items::sound_item &si = m_sound_items.add(m_Ui.brain_contents, dirIt.filePath().toStdString(),true); QObject::connect(si.m_enable, SIGNAL(clicked()), m_sound_item_enable_mapper, SLOT(map())); m_sound_item_enable_mapper->setMapping(si.m_enable, si.m_id); QObject::connect(si.m_del, SIGNAL(clicked()), m_sound_item_delete_mapper, SLOT(map())); m_sound_item_delete_mapper->setMapping(si.m_del, si.m_id); + + } } + } + void select_all() { + for (auto &si:m_sound_items.m_sound_items) { + si.m_enable->setChecked(true); + send_process_osc("/activate_sound","s",si.m_filename.c_str()); + } + } - void load_sounds() { - m_last_file=QFileDialog::getExistingDirectory(this, - QString("Select a directory"), - m_last_file); + void select_none() { + for (auto &si:m_sound_items.m_sound_items) { + si.m_enable->setChecked(false); + send_process_osc("/deactivate_sound","s",si.m_filename.c_str()); + } + } - - QDirIterator dirIt(m_last_file,QDirIterator::Subdirectories); - while (dirIt.hasNext()) { - dirIt.next(); - if (QFileInfo(dirIt.filePath()).isFile() && - QFileInfo(dirIt.filePath()).suffix() == "wav") { - send_process_osc("/load_sample","s",dirIt.filePath().toStdString().c_str()); - - sound_items::sound_item &si = m_sound_items.add(m_Ui.brain_contents, dirIt.filePath().toStdString(),true); - - QObject::connect(si.m_enable, SIGNAL(clicked()), m_sound_item_enable_mapper, SLOT(map())); - m_sound_item_enable_mapper->setMapping(si.m_enable, si.m_id); - QObject::connect(si.m_del, SIGNAL(clicked()), m_sound_item_delete_mapper, SLOT(map())); - m_sound_item_delete_mapper->setMapping(si.m_del, si.m_id); - - } - } - } - - void select_all() { - for (auto &si:m_sound_items.m_sound_items) { - si.m_enable->setChecked(true); - send_process_osc("/activate_sound","s",si.m_filename.c_str()); - } - } - - void select_none() { - for (auto &si:m_sound_items.m_sound_items) { - si.m_enable->setChecked(false); - send_process_osc("/deactivate_sound","s",si.m_filename.c_str()); - } - } - - void sound_enable(int id) { - // search for this id... - for (auto &si:m_sound_items.m_sound_items) { - if (si.m_id==id) { - if (si.m_enable->isChecked()) { - send_process_osc("/activate_sound","s",si.m_filename.c_str()); - } else { - send_process_osc("/deactivate_sound","s",si.m_filename.c_str()); - } + void sound_enable(int id) { + // search for this id... + for (auto &si:m_sound_items.m_sound_items) { + if (si.m_id==id) { + if (si.m_enable->isChecked()) { + send_process_osc("/activate_sound","s",si.m_filename.c_str()); + } else { + send_process_osc("/deactivate_sound","s",si.m_filename.c_str()); } } } + } - void delete_sound(int id) { - // search for this id... - for (auto &si:m_sound_items.m_sound_items) { - if (si.m_id==id) { - send_process_osc("/delete_sample","s",si.m_filename.c_str()); - m_sound_items.remove(si.m_filename); - // iterator is now invalidated... - return; - } - } - } - void clear_brain() { - for (auto &si:m_sound_items.m_sound_items) { + void delete_sound(int id) { + // search for this id... + for (auto &si:m_sound_items.m_sound_items) { + if (si.m_id==id) { send_process_osc("/delete_sample","s",si.m_filename.c_str()); + m_sound_items.remove(si.m_filename); + // iterator is now invalidated... + return; } - m_sound_items.clear(); } - void restart_audio() { send_audio_osc("/restart_audio",""); } - - void brain_shape(int n) { send_process_osc("/window_type","i",n); } - void target_shape(int n) { send_process_osc("/target_window_type","i",n); } - void mic(bool n) { send_audio_osc("/mic","i",(int)n); } - - void record() { - if (m_save_wav=="") { - m_last_file=QFileDialog::getSaveFileName( - this, - QString("Select a wav file"), - m_last_file, - QString("Sounds (*.wav)")); - m_save_wav = m_last_file.toStdString(); - // chop off .wav - size_t pos = m_save_wav.find_last_of("."); - if (pos!=string::npos) { - m_save_wav = m_save_wav.substr(0,pos); - } - - } - - char fn[1024]; - snprintf(fn,1024,"%s-%i",m_save_wav.c_str(),m_record_id); - send_audio_osc("/record","s",fn); - cerr<isChecked()) { - // reconnect - string url = d.m_address->text().toUtf8().constData(); - lo_address_free(d.m_audio_address); - lo_address_free(d.m_process_address); - d.m_audio_address = lo_address_new_from_url(string(url+":8888").c_str()); - d.m_process_address = lo_address_new_from_url(string(url+":8889").c_str()); - // start sending messages here - d.m_enabled=true; - } else { - // stop sending messages here - d.m_enabled=false; + void record() { + if (m_save_wav=="") { + m_last_file=QFileDialog::getSaveFileName( + this, + QString("Select a wav file"), + m_last_file, + QString("Sounds (*.wav);;All files (*.*)")); + m_save_wav = m_last_file.toStdString(); + // chop off .wav + size_t pos = m_save_wav.find_last_of("."); + if (pos!=string::npos) { + m_save_wav = m_save_wav.substr(0,pos); } } + char fn[1024]; + snprintf(fn,1024,"%s-%i",m_save_wav.c_str(),m_record_id); + send_audio_osc("/record","s",fn); + cerr<isChecked()) { + // reconnect + string url = d.m_address->text().toUtf8().constData(); + lo_address_free(d.m_audio_address); + lo_address_free(d.m_process_address); + d.m_audio_address = lo_address_new_from_url(string(url+":"+m_audio_port).c_str()); + d.m_process_address = lo_address_new_from_url(string(url+":"+m_process_port).c_str()); + // start sending messages here + d.m_enabled=true; + } else { + // stop sending messages here + d.m_enabled=false; + } + + } + + void settings() { + m_settings_dialog->show(); + } + private: - /////////////////////////////////////////////// + /////////////////////////////////////////////// - // we want to be able to send out to - // multiple addresses over the network + // we want to be able to send out to + // multiple addresses over the network - //////////////////////////////////////////////// + //////////////////////////////////////////////// - // we want to be able to send out to - // multiple addresses over the network - class osc_destination { - public: - int m_id; - lo_address m_audio_address; - lo_address m_process_address; - // can't find a way to address these via qt - QLineEdit *m_address; - QCheckBox *m_enable; - bool m_enabled; - }; + // we want to be able to send out to + // multiple addresses over the network + class osc_destination { + public: + int m_id; + lo_address m_audio_address; + lo_address m_process_address; + // can't find a way to address these via qt + QLineEdit *m_address; + QCheckBox *m_enable; + bool m_enabled; + }; - vector m_destinations; - - // all this to work around liblo's use of varargs... - void send_audio_osc(const char *name, const char *types) { - for (auto dest:m_destinations) { - if (dest.m_enabled) { - lo_send(dest.m_audio_address,name,types); - } - } - } - - template - void send_audio_osc(const char *name, const char *types, T val) { - for (auto dest:m_destinations) { - if (dest.m_enabled) { - lo_send(dest.m_audio_address,name,types,val); - } - } - } - - void send_process_osc(const char *name, const char *types) { - for (auto dest:m_destinations) { - if (dest.m_enabled) { - lo_send(dest.m_process_address,name,types); - } - } - } - - template - void send_process_osc(const char *name, const char *types, T val) { - for (auto dest:m_destinations) { - if (dest.m_enabled) { - lo_send(dest.m_process_address,name,types,val); - } - } - } + vector m_destinations; - void init_from_session(const string &filename); - void add_gui_address(osc_destination &dest, - QSignalMapper* enable_mapper); - - string m_save_wav; - QString m_last_file; - unsigned int m_record_id; - Ui_MainWindow m_Ui; - feedback m_feedback; - QSignalMapper* m_sound_item_enable_mapper; - QSignalMapper* m_sound_item_delete_mapper; - sound_items m_sound_items; + void init_from_session(const string &filename); + void add_gui_address(osc_destination &dest, + QSignalMapper* enable_mapper); + string m_save_wav; + QString m_last_file; + unsigned int m_record_id; + Ui_MainWindow m_Ui; + feedback m_feedback; + QSignalMapper* m_sound_item_enable_mapper; + QSignalMapper* m_sound_item_delete_mapper; + sound_items m_sound_items; + SettingsDialog *m_settings_dialog; + audio_thread *m_audio_thread; + + string m_audio_port; + string m_process_port; + QString m_format_string; }; diff --git a/app/SettingsDialog.cpp b/app/SettingsDialog.cpp new file mode 100644 index 0000000..d6e0cd5 --- /dev/null +++ b/app/SettingsDialog.cpp @@ -0,0 +1,43 @@ +// Copyright (C) 2022 Then Try This +// +// 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include + +#include "MainWindow.h" +#include "SettingsDialog.h" +#include "feedback.h" + +using namespace std; + +SettingsDialog::SettingsDialog(MainWindow *parent, QSettings *settings): + m_device(""), + m_parent(parent), + m_buffersize(2048), + m_samplerate(44100), + m_settings(settings) { + m_Ui.setupUi(this); + m_Ui.guiOSCLineEdit->setText(settings->value("gui_port").toByteArray().constData()); + m_Ui.processOSCLineEdit->setText(settings->value("process_port").toByteArray().constData()); + m_Ui.audioOSCLineEdit->setText(settings->value("audio_port").toByteArray().constData()); +} + +void SettingsDialog::connect() { + audio_thread *at = m_parent->get_audio_thread(); + at->restart_audio(m_device,m_samplerate,m_buffersize); + m_Ui.messagesLabel->setText(at->m_audio_device->m_client.m_status.c_str()); +} diff --git a/app/SettingsDialog.h b/app/SettingsDialog.h new file mode 100644 index 0000000..ebb9666 --- /dev/null +++ b/app/SettingsDialog.h @@ -0,0 +1,89 @@ +// Copyright (C) 2022 Then Try This +// +// 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. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SETTINGS_DIALOG +#define SETTINGS_DIALOG + +#include +#include +#include +#include +#include "gui/ui_settings.h" + +#include +#include +#include +#include + +using namespace std; + + +class MainWindow; + +class SettingsDialog : public QDialog +{ + Q_OBJECT + public: + SettingsDialog(MainWindow *parent, QSettings *settings); + + Ui_SettingsDialog m_Ui; + string m_device; + + protected: + + private slots: + + void samplerate(QString str) { + try { m_samplerate=stoi(str.toStdString()); } + catch(std::exception const & e) { + m_samplerate=44100; + } + } + void output_device(QString v) { m_device=v.toStdString(); } + void buffersize(QString str) { + try { m_buffersize=stoi(str.toStdString()); } + catch(std::exception const & e) { + m_buffersize=44100; + } + } + void accept() { connect(); hide(); } + void reject() { hide(); } + void apply() { connect(); } + + void gui_port(QString str) { + m_settings->setValue("gui_port",str); + } + + void process_port(QString str) { + m_settings->setValue("process_port",str); + } + + void audio_port(QString str) { + m_settings->setValue("audio_port",str); + } + + private: + + void connect(); + MainWindow *m_parent; + + unsigned int m_buffersize; + unsigned int m_samplerate; + + QSettings *m_settings; +}; + +#endif diff --git a/app/audio_thread.cpp b/app/audio_thread.cpp index 8e87fe3..7da0aa0 100644 --- a/app/audio_thread.cpp +++ b/app/audio_thread.cpp @@ -20,15 +20,17 @@ using namespace spiralcore; using namespace std; -audio_thread::audio_thread(process_thread &p) : +audio_thread::audio_thread(const string &port, process_thread &p) : m_audio_device(NULL), - m_osc("8888"), + m_osc(port), m_process_thread(p), m_brain_mutex(p.m_brain_mutex), m_stereo_mode(false), - m_mic_mode(false) -{ - start_audio(); + m_mic_mode(false), + m_bufsize(2048), + m_samplerate(44100), + m_device("") { + pthread_mutex_lock(m_brain_mutex); m_left_renderer = new renderer(p.m_source,p.m_left_target); m_right_renderer = new renderer(p.m_source,p.m_right_target); @@ -48,18 +50,28 @@ audio_thread::~audio_thread() { void audio_thread::start_audio() { if (m_audio_device!=NULL) delete m_audio_device; - m_audio_device = new audio_device("samplebrain",48000,2048); - //m_audio_device = new audio_device("samplebrain",48000,2048*4); + m_audio_device = new audio_device("samplebrain",m_samplerate,m_bufsize); m_audio_device->m_client.set_callback(run_audio, this); } +void audio_thread::restart_audio(const string device, unsigned int samplerate, unsigned int bufsize) { + m_samplerate = samplerate; + m_bufsize = bufsize; + m_device = device; + m_audio_device->connect(m_device, + "samplebrain", + m_samplerate, + m_bufsize); +} + + void audio_thread::run_audio(void* c, unsigned int frames) { if (state) { audio_thread *at = (audio_thread*)c; at->m_audio_device->left_out.zero(); at->process(at->m_audio_device->left_in, - at->m_audio_device->right_in, - at->m_audio_device->left_out, + at->m_audio_device->right_in, + at->m_audio_device->left_out, at->m_audio_device->right_out); at->m_audio_device->maybe_record(); } @@ -186,12 +198,12 @@ void audio_thread::process(sample &left_in, sample &right_in, sample &left_out, } m_left_renderer->process(left_out.get_length(), - left_out.get_non_const_buffer(), - bs); + left_out.get_non_const_buffer(), + bs); if (m_stereo_mode) { m_right_renderer->process(right_out.get_length(), - right_out.get_non_const_buffer(), - bs); + right_out.get_non_const_buffer(), + bs); } else { right_out=left_out; } diff --git a/app/audio_thread.h b/app/audio_thread.h index dcb3b81..66ccd40 100644 --- a/app/audio_thread.h +++ b/app/audio_thread.h @@ -24,11 +24,14 @@ namespace spiralcore { -class audio_thread { -public: - audio_thread(process_thread &p); + class audio_thread { + public: + audio_thread(const string &port, process_thread &p); ~audio_thread(); + void start_audio(); + void restart_audio(const string device, unsigned int samplerate, unsigned int bufsize); + bool ok() { return m_osc.ok(); } void process(sample &left_in, sample &right_in, sample &left_out, sample &right_out); static void run_audio(void* c, unsigned int frames); @@ -38,14 +41,16 @@ public: renderer *m_right_renderer; block_stream *m_block_stream; -private: - void start_audio(); + private: OSC_server m_osc; process_thread &m_process_thread; pthread_mutex_t* m_brain_mutex; bool m_stereo_mode; bool m_mic_mode; -}; + u32 m_bufsize; + u32 m_samplerate; + string m_device; + }; } diff --git a/app/feedback.cpp b/app/feedback.cpp index 30d4887..582329c 100644 --- a/app/feedback.cpp +++ b/app/feedback.cpp @@ -16,6 +16,7 @@ #include "feedback.h" #include "sound_items.h" +#include "SettingsDialog.h" #include using namespace spiralcore; @@ -28,7 +29,7 @@ feedback::feedback(string address) : } -void feedback::poll(QStatusBar *s, sound_items *sound_items) { +void feedback::poll(QStatusBar *s, sound_items *sound_items, SettingsDialog *settings) { command_ring_buffer::command cmd; while (m_osc.get(cmd)) { diff --git a/app/feedback.h b/app/feedback.h index 9638f91..1c52949 100644 --- a/app/feedback.h +++ b/app/feedback.h @@ -17,6 +17,7 @@ #include #include #include +#include "SettingsDialog.h" #include "spiralcore/OSC_server.h" #pragma once @@ -26,12 +27,13 @@ class sound_items; class feedback { public: - feedback(std::string address); - void poll(QStatusBar *s, sound_items *sound_items); - + feedback(std::string address); + void poll(QStatusBar *s, sound_items *sound_items, SettingsDialog *settings); + bool ok() { return m_osc.ok(); } + private: - OSC_server m_osc; + OSC_server m_osc; }; } diff --git a/gui/buttons.svg b/app/gui/buttons.svg similarity index 100% rename from gui/buttons.svg rename to app/gui/buttons.svg diff --git a/gui/samplebrain.ui b/app/gui/samplebrain.ui similarity index 91% rename from gui/samplebrain.ui rename to app/gui/samplebrain.ui index ff82d63..e3626eb 100644 --- a/gui/samplebrain.ui +++ b/app/gui/samplebrain.ui @@ -11,7 +11,7 @@ - samplebrain 0.18.1 + samplebrain 0.18.5 @@ -20,6 +20,7 @@ Comic Sans MS + 75 true @@ -39,6 +40,7 @@ Comic Sans MS 20 + 75 true @@ -55,6 +57,7 @@ Comic Sans MS 9 + 75 true @@ -93,6 +96,9 @@ 0 + + plain fft match vs mfcc values + 1.000000000000000 @@ -114,6 +120,7 @@ Comic Sans MS 9 + 75 true @@ -152,6 +159,9 @@ 0 + + match original or normalised blocks + 1.000000000000000 @@ -173,6 +183,7 @@ Comic Sans MS 9 + 75 true @@ -195,6 +206,7 @@ Comic Sans MS + 75 true @@ -221,6 +233,7 @@ Comic Sans MS + 75 true @@ -249,6 +262,7 @@ Comic Sans MS 9 + 75 true @@ -290,6 +304,9 @@ 0 + + use new blocks rather than similar ones + 1.000000000000000 @@ -311,6 +328,7 @@ Comic Sans MS 9 + 75 true @@ -352,6 +370,9 @@ 0 + + how long it takes for the novelty to wear off + 1.000000000000000 @@ -373,6 +394,7 @@ Comic Sans MS 9 + 75 true @@ -393,7 +415,7 @@ - how long it takes for the novelty to wear off + likelihood of playing the next block rather than the closest 100 @@ -414,6 +436,9 @@ 0 + + likelihood of playing the next block rather than the closest + 1.000000000000000 @@ -435,6 +460,7 @@ Comic Sans MS 9 + 75 true @@ -480,7 +506,7 @@ - how many connections to search (ordered in closeness) + repeat search (set novelty to 0 for dodgy jungle timessssstretch) 1 @@ -503,6 +529,7 @@ Comic Sans MS 9 + 75 true @@ -545,6 +572,7 @@ Comic Sans MS 9 + 75 true @@ -607,6 +635,7 @@ Comic Sans MS 9 + 75 true @@ -652,7 +681,7 @@ - how many connections to search (ordered in closeness) + block difference higher than this causes a new search, skipping the target 10000 @@ -690,6 +719,7 @@ Comic Sans MS 20 + 75 true @@ -703,6 +733,7 @@ Comic Sans MS + 75 true @@ -718,6 +749,7 @@ Comic Sans MS + 75 true @@ -735,7 +767,7 @@ 99999 - 3000 + 1000 @@ -748,6 +780,7 @@ Comic Sans MS + 75 true @@ -765,7 +798,7 @@ 0.010000000000000 - 0.800000000000000 + 0.750000000000000 @@ -778,6 +811,7 @@ Comic Sans MS + 75 true @@ -837,6 +871,7 @@ Comic Sans MS + 75 true @@ -853,6 +888,7 @@ Comic Sans MS + 75 true @@ -870,6 +906,7 @@ Comic Sans MS 20 + 75 true @@ -886,6 +923,7 @@ Comic Sans MS 9 + 75 true @@ -921,6 +959,9 @@ 0 + + amount to match the frequency + 1.000000000000000 @@ -942,6 +983,7 @@ Comic Sans MS 9 + 75 true @@ -977,6 +1019,9 @@ 0 + + mix in the normalised blocks + 1.000000000000000 @@ -998,6 +1043,7 @@ Comic Sans MS 9 + 75 true @@ -1036,6 +1082,9 @@ 0 + + mix in the original blocks + 1.000000000000000 @@ -1081,6 +1130,7 @@ Comic Sans MS 20 + 75 true @@ -1127,7 +1177,7 @@ 0 0 - 600 + 424 198 @@ -1146,6 +1196,7 @@ Comic Sans MS + 75 true @@ -1159,6 +1210,7 @@ Comic Sans MS + 75 true @@ -1172,6 +1224,7 @@ Comic Sans MS + 75 true @@ -1189,6 +1242,7 @@ Comic Sans MS + 75 true @@ -1206,7 +1260,7 @@ 99999 - 3000 + 1000 @@ -1219,6 +1273,7 @@ Comic Sans MS + 75 true @@ -1236,7 +1291,7 @@ 0.010000000000000 - 0.000000000000000 + 0.750000000000000 @@ -1249,6 +1304,7 @@ Comic Sans MS + 75 true @@ -1308,6 +1364,7 @@ Comic Sans MS + 75 true @@ -1323,6 +1380,7 @@ Comic Sans MS + 75 true @@ -1336,6 +1394,7 @@ Comic Sans MS + 75 true @@ -1399,6 +1458,7 @@ Comic Sans MS + 75 true @@ -1406,7 +1466,7 @@ - + :/images/images/play.png:/images/images/play.png @@ -1425,6 +1485,7 @@ Comic Sans MS + 75 true @@ -1432,7 +1493,7 @@ - + :/images/images/pause.png:/images/images/pause.png @@ -1452,7 +1513,7 @@ - + :/images/images/record.png:/images/images/record.png @@ -1472,7 +1533,7 @@ - + :/images/images/stop.png:/images/images/stop.png @@ -1500,6 +1561,7 @@ Comic Sans MS + 75 true @@ -1513,6 +1575,7 @@ Comic Sans MS + 75 true @@ -1523,6 +1586,33 @@ + + + + + Comic Sans MS + 75 + true + + + + + + + + :/images/images/settings.png:/images/images/settings.png + + + + 32 + 32 + + + + true + + + @@ -1542,7 +1632,7 @@ - :/images/images/at.png + :/images/images/at.png @@ -1553,8 +1643,7 @@ - - + @@ -1565,8 +1654,8 @@ play_slot() - 64 - 62 + 78 + 830 399 @@ -1581,8 +1670,8 @@ stop_slot() - 155 - 62 + 180 + 830 399 @@ -1597,8 +1686,8 @@ volume_slot(int) - 189 - 480 + 465 + 852 361 @@ -1613,8 +1702,8 @@ stop_record() - 328 - 543 + 356 + 840 361 @@ -1629,8 +1718,8 @@ record() - 236 - 543 + 268 + 840 361 @@ -1645,8 +1734,8 @@ fft1_end_slot(int) - 154 - 310 + 421 + 293 399 @@ -1661,8 +1750,8 @@ ratio_slot(double) - 109 - 223 + 421 + 185 399 @@ -1677,12 +1766,12 @@ setValue(int) - 237 - 289 + 319 + 603 - 341 - 289 + 421 + 614 @@ -1693,8 +1782,8 @@ target_mix_slot(double) - 330 - 446 + 720 + 669 361 @@ -1709,8 +1798,8 @@ generate_target_blocks() - 277 - 202 + 670 + 395 399 @@ -1725,8 +1814,8 @@ target_block_size(int) - 313 - 136 + 720 + 237 399 @@ -1741,8 +1830,8 @@ load_target() - 277 - 103 + 670 + 184 399 @@ -1757,8 +1846,8 @@ fft1_start_slot(int) - 154 - 277 + 298 + 293 399 @@ -1773,8 +1862,8 @@ target_block_overlap(double) - 619 - 196 + 720 + 291 361 @@ -1789,12 +1878,12 @@ setValue(int) - 341 - 289 + 421 + 614 - 237 - 289 + 319 + 603 @@ -1805,8 +1894,8 @@ synapses(int) - 341 - 289 + 421 + 614 566 @@ -1821,8 +1910,8 @@ ratio_slot(int) - 189 - 141 + 323 + 174 361 @@ -1837,8 +1926,8 @@ n_ratio_slot(int) - 159 - 211 + 323 + 228 361 @@ -1853,8 +1942,8 @@ n_ratio_slot(double) - 330 - 211 + 421 + 239 361 @@ -1869,8 +1958,8 @@ target_mix_slot(int) - 159 - 446 + 622 + 658 361 @@ -1885,8 +1974,8 @@ n_mix_slot(int) - 159 - 386 + 622 + 604 361 @@ -1901,8 +1990,8 @@ n_mix_slot(double) - 330 - 386 + 720 + 615 361 @@ -1917,8 +2006,8 @@ load_brain() - 615 - 357 + 941 + 661 566 @@ -1933,8 +2022,8 @@ boredom_slot(double) - 411 - 253 + 421 + 401 321 @@ -1949,8 +2038,8 @@ novelty_slot(double) - 411 - 217 + 421 + 347 321 @@ -1965,8 +2054,8 @@ novelty_slot(int) - 291 - 217 + 323 + 336 321 @@ -1981,8 +2070,8 @@ boredom_slot(int) - 291 - 253 + 323 + 390 321 @@ -1997,8 +2086,8 @@ synapses(int) - 237 - 289 + 319 + 603 566 @@ -2013,8 +2102,8 @@ block_size(int) - 728 - 109 + 1157 + 450 566 @@ -2029,8 +2118,8 @@ block_overlap(double) - 728 - 145 + 1157 + 504 566 @@ -2045,8 +2134,8 @@ save_brain() - 728 - 357 + 1157 + 661 566 @@ -2061,8 +2150,8 @@ clear_brain() - 1061 - 550 + 1157 + 396 566 @@ -2077,8 +2166,8 @@ generate() - 671 - 322 + 1158 + 608 566 @@ -2093,8 +2182,8 @@ load_sound() - 841 - 550 + 868 + 396 566 @@ -2109,12 +2198,12 @@ setValue(int) - 241 - 282 + 346 + 498 - 343 - 282 + 421 + 509 @@ -2125,12 +2214,12 @@ setValue(int) - 343 - 282 + 421 + 509 - 241 - 282 + 346 + 498 @@ -2141,8 +2230,8 @@ search_stretch(int) - 241 - 282 + 346 + 498 566 @@ -2157,12 +2246,12 @@ setValue(int) - 231 - 410 + 305 + 657 - 337 - 410 + 421 + 668 @@ -2173,12 +2262,12 @@ setValue(int) - 337 - 410 + 421 + 668 - 231 - 410 + 305 + 657 @@ -2189,8 +2278,8 @@ slide_error(int) - 231 - 410 + 305 + 657 566 @@ -2205,8 +2294,8 @@ stickyness_slot(double) - 385 - 296 + 421 + 455 609 @@ -2221,8 +2310,8 @@ stickyness_slot(int) - 266 - 296 + 323 + 444 609 @@ -2237,8 +2326,8 @@ autotune(int) - 531 - 477 + 622 + 550 454 @@ -2253,8 +2342,8 @@ autotune(double) - 616 - 477 + 720 + 561 454 @@ -2269,8 +2358,8 @@ load_session() - 535 - 707 + 616 + 799 454 @@ -2285,8 +2374,8 @@ save_session() - 535 - 744 + 616 + 851 454 @@ -2301,8 +2390,8 @@ stereo_mode(bool) - 539 - 594 + 721 + 717 454 @@ -2317,8 +2406,8 @@ algo(int) - 273 - 395 + 421 + 560 454 @@ -2333,8 +2422,8 @@ target_shape(int) - 557 - 233 + 720 + 342 454 @@ -2349,8 +2438,8 @@ brain_shape(int) - 824 - 432 + 1157 + 555 454 @@ -2365,8 +2454,8 @@ load_sounds() - 754 - 321 + 1013 + 396 454 @@ -2381,8 +2470,8 @@ select_all() - 813 - 74 + 1073 + 124 454 @@ -2397,8 +2486,8 @@ select_none() - 862 - 74 + 1157 + 124 454 @@ -2413,8 +2502,8 @@ mic(bool) - 404 - 601 + 721 + 442 454 @@ -2422,6 +2511,22 @@ + + pushButton + clicked() + MainWindow + settings() + + + 680 + 809 + + + 776 + 849 + + + play_slot() @@ -2479,5 +2584,6 @@ select_all() select_none() mic(bool) + settings() diff --git a/app/gui/settings.ui b/app/gui/settings.ui new file mode 100644 index 0000000..e8f8835 --- /dev/null +++ b/app/gui/settings.ui @@ -0,0 +1,490 @@ + + + SettingsDialog + + + + 0 + 0 + 438 + 656 + + + + settings + + + + + + + + + + + Comic Sans MS + + + + device + + + + + + + + Comic Sans MS + + + + + + + + + + + + + Comic Sans MS + + + + sample rate + + + + + + + + 0 + 0 + + + + + Comic Sans MS + + + + 44100 + + + + + + + + + + Comic Sans MS + 8 + + + + note: this currently should probably match your sample file's input rate as no conversion is run on them - yet + + + true + + + + + + + + + + + + Comic Sans MS + + + + buffer size + + + + + + + + Comic Sans MS + + + + 5 + + + + 64 + + + + + 128 + + + + + 256 + + + + + 512 + + + + + 1024 + + + + + 2048 + + + + + 4096 + + + + + + + + + + + Comic Sans MS + + + + messages + + + + + + + + 0 + 0 + + + + + Comic Sans MS + 8 + + + + false + + + border: 1px solid; + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + Comic Sans MS + 8 + + + + restart needed for the ports below to update + + + + + + + + + + Comic Sans MS + + + + gui osc port + + + + + + + + 0 + 0 + + + + + Comic Sans MS + + + + + + + + + + + + + Comic Sans MS + + + + process osc port + + + + + + + + 0 + 0 + + + + + Comic Sans MS + + + + + + + + + + + + + Comic Sans MS + + + + audio osc port + + + + + + + + 0 + 0 + + + + + Comic Sans MS + + + + + + + + + + + Comic Sans MS + + + + apply settings + + + + + + + + Comic Sans MS + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SettingsDialog + accept() + + + 244 + 642 + + + 157 + 274 + + + + + buttonBox + rejected() + SettingsDialog + reject() + + + 312 + 642 + + + 286 + 274 + + + + + samplerateLineEdit + textEdited(QString) + SettingsDialog + samplerate(QString) + + + 270 + 71 + + + 394 + 156 + + + + + applyPushButton + clicked() + SettingsDialog + apply() + + + 125 + 592 + + + 4 + 315 + + + + + deviceComboBox + currentTextChanged(QString) + SettingsDialog + output_device(QString) + + + 323 + 45 + + + 393 + 64 + + + + + buffersizeComboBox + currentTextChanged(QString) + SettingsDialog + buffersize(QString) + + + 390 + 215 + + + 395 + 208 + + + + + guiOSCLineEdit + textChanged(QString) + SettingsDialog + gui_port(QString) + + + 346 + 432 + + + 401 + 419 + + + + + processOSCLineEdit + textChanged(QString) + SettingsDialog + process_port(QString) + + + 356 + 482 + + + 398 + 479 + + + + + audioOSCLineEdit + textChanged(QString) + SettingsDialog + audio_port(QString) + + + 374 + 511 + + + 398 + 517 + + + + + + output_device(QString) + samplerate(QString) + buffersize(QString) + apply() + gui_port(QString) + audio_port(QString) + process_port(QString) + + diff --git a/app/images/settings.png b/app/images/settings.png new file mode 100644 index 0000000..705fc79 Binary files /dev/null and b/app/images/settings.png differ diff --git a/app/process_thread.cpp b/app/process_thread.cpp index 3687da8..d09e127 100644 --- a/app/process_thread.cpp +++ b/app/process_thread.cpp @@ -32,11 +32,11 @@ static void* _process(void *c) { return NULL; } -process_thread::process_thread() : - m_osc("8889"), - m_source_block_size(3000), +process_thread::process_thread(const string &port) : + m_osc(port), + m_source_block_size(1000), m_source_overlap(0.75), - m_target_block_size(3000), + m_target_block_size(1000), m_target_overlap(0.75), m_window_type(window::DODGY), m_target_window_type(window::DODGY) @@ -63,82 +63,82 @@ void process_thread::process() { string name = cmd.m_name; //cerr<reset(); - m_right_renderer->reset(); - pthread_mutex_unlock(m_brain_mutex); + pthread_mutex_lock(m_brain_mutex); + m_source.init(m_source_block_size, m_source_overlap, m_window_type); + search_params p(1,0,0,100,0); + m_source.build_synapses_fixed(p); + m_left_renderer->reset(); + m_right_renderer->reset(); + pthread_mutex_unlock(m_brain_mutex); } if (name=="/load_target") { - pthread_mutex_lock(m_brain_mutex); - m_left_target.clear_sounds(); - m_left_target.load_sound(cmd.get_string(0),brain::LEFT); - m_right_target.clear_sounds(); - m_right_target.load_sound(cmd.get_string(0),brain::RIGHT); - pthread_mutex_unlock(m_brain_mutex); + pthread_mutex_lock(m_brain_mutex); + m_left_target.clear_sounds(); + m_left_target.load_sound(cmd.get_string(0),brain::LEFT); + m_right_target.clear_sounds(); + m_right_target.load_sound(cmd.get_string(0),brain::RIGHT); + pthread_mutex_unlock(m_brain_mutex); } if (name=="/target_block_size") { - m_target_block_size = cmd.get_int(0); - m_block_stream->init(m_target_block_size, m_target_overlap, m_target_window_type); + m_target_block_size = cmd.get_int(0); + m_block_stream->init(m_target_block_size, m_target_overlap, m_target_window_type); } if (name=="/target_overlap") { - m_target_overlap = m_target_block_size*cmd.get_float(0); - m_block_stream->init(m_target_block_size, m_target_overlap, m_target_window_type); + m_target_overlap = m_target_block_size*cmd.get_float(0); + m_block_stream->init(m_target_block_size, m_target_overlap, m_target_window_type); } if (name=="/generate_target") { - pthread_mutex_lock(m_brain_mutex); - m_left_target.init(m_target_block_size, m_target_overlap, m_target_window_type); - m_right_target.init(m_target_block_size, m_target_overlap, m_target_window_type); - // probably elsewhere - m_block_stream->init(m_target_block_size, m_target_overlap, m_target_window_type); - pthread_mutex_unlock(m_brain_mutex); + pthread_mutex_lock(m_brain_mutex); + m_left_target.init(m_target_block_size, m_target_overlap, m_target_window_type); + m_right_target.init(m_target_block_size, m_target_overlap, m_target_window_type); + // probably elsewhere + m_block_stream->init(m_target_block_size, m_target_overlap, m_target_window_type); + pthread_mutex_unlock(m_brain_mutex); } if (name=="/window_type") { - m_window_type=(window::type)cmd.get_int(0); + m_window_type=(window::type)cmd.get_int(0); } if (name=="/target_window_type") { - m_target_window_type=(window::type)cmd.get_int(0); - m_block_stream->init(m_target_block_size, m_target_overlap, m_target_window_type); + m_target_window_type=(window::type)cmd.get_int(0); + m_block_stream->init(m_target_block_size, m_target_overlap, m_target_window_type); } if (name=="/load_brain") { - load_source(cmd.get_string(0)); + load_source(cmd.get_string(0)); } if (name=="/save_brain") { - save_source(cmd.get_string(0)); + save_source(cmd.get_string(0)); } if (name=="/load_session") { - load_session(cmd.get_string(0)); + load_session(cmd.get_string(0)); } if (name=="/save_session") { - save_session(cmd.get_string(0)); + save_session(cmd.get_string(0)); } } #ifdef WIN32 @@ -181,9 +181,6 @@ void process_thread::load_session(const std::string &filename) { ifs||m_source_block_size||m_source_overlap; ifs||m_target_block_size||m_target_overlap; ifs||m_window_type||m_target_window_type; - - cerr<<"loading window type session "< #include #include +#include + #include "MainWindow.h" #include "process_thread.h" #include "audio_thread.h" +#include "status.h" using namespace std; int main( int argc , char *argv[] ){ - QApplication app(argc, argv); - MainWindow mainWin; - mainWin.show(); - cerr<<"Qt version: "< + images/settings.png images/at.png images/pause.png images/play.png diff --git a/app/sound_items.cpp b/app/sound_items.cpp index ce3704c..340c56c 100644 --- a/app/sound_items.cpp +++ b/app/sound_items.cpp @@ -33,7 +33,7 @@ sound_items::sound_item &sound_items::add(QVBoxLayout *container, const string & sound_item si; si.m_filename = name; si.m_id = m_current_sound_id++; - QString style("background-color:lightgrey;"); + QString style("color:black;background-color:lightgrey;"); si.m_container = new QHBoxLayout(); si.m_container->setSpacing(10); @@ -89,8 +89,8 @@ void sound_items::clear() { void sound_items::recolour() { u32 c=0; for (auto &si:m_sound_items) { - QString style("background-color:lightblue;"); - if (c%2==0) style="background-color:pink;"; + QString style("color:black;background-color:lightblue;"); + if (c%2==0) style="color:black;background-color:pink;"; si.m_enable->setStyleSheet(style); si.m_del->setStyleSheet(style); si.m_label->setStyleSheet(style); @@ -101,7 +101,7 @@ void sound_items::recolour() { void sound_items::change_colour(const std::string &name, const std::string &colour) { for (auto &si:m_sound_items) { if (si.m_filename==name) { - QString style("background-color:"+QString::fromStdString(colour)+";"); + QString style("color:black;background-color:"+QString::fromStdString(colour)+";"); si.m_enable->setStyleSheet(style); si.m_del->setStyleSheet(style); si.m_label->setStyleSheet(style); diff --git a/brain/src/block.cpp b/brain/src/block.cpp index c959188..149e399 100644 --- a/brain/src/block.cpp +++ b/brain/src/block.cpp @@ -77,7 +77,7 @@ block::block(u64 id, const string &filename, const sample &pcm, u32 rate, const m_orig_filename(filename), m_usage(0) { - init_fft(m_pcm.get_length()); + init_fft(m_pcm.get_length(),rate); assert(m_mfcc_proc!=NULL); assert(m_fftw!=NULL); @@ -97,12 +97,12 @@ block::block(u64 id, const string &filename, const sample &pcm, u32 rate, const } -void block::init_fft(u32 block_size) { +void block::init_fft(u32 block_size, u32 rate) { if (m_fftw == NULL || m_fftw->m_length!=block_size) { if (m_fftw == NULL) delete m_fftw; - m_fftw = new FFT(block_size,100); + m_fftw = new FFT(block_size,rate,100); if (m_mfcc_proc == NULL) delete m_mfcc_proc; - m_mfcc_proc = new Aquila::Mfcc(block_size); + m_mfcc_proc = new Aquila::Mfcc(block_size,rate); } } diff --git a/brain/src/block.h b/brain/src/block.h index a10858f..3aae3a2 100644 --- a/brain/src/block.h +++ b/brain/src/block.h @@ -38,7 +38,7 @@ namespace spiralcore { // returns distance based on ratio of fft-mfcc values double compare(const block &other, const search_params ¶ms) const; - static void init_fft(u32 block_size); + static void init_fft(u32 block_size, u32 rate); static bool unit_test(); const sample &get_pcm() const { return m_pcm; } diff --git a/brain/src/brain.cpp b/brain/src/brain.cpp index 0f4d2e8..64f5d56 100644 --- a/brain/src/brain.cpp +++ b/brain/src/brain.cpp @@ -70,6 +70,8 @@ void brain::load_sound(std::string filename, stereo_mode mode) { delete[] temp; m_samples.push_back(sound(filename,s)); status::update("loaded %s",filename.c_str()); + } else { + status::update("problem loading %s",filename.c_str()); } } @@ -382,34 +384,6 @@ void brain::deplete_usage() { } } - -// take another brain and rebuild this brain from bits of that one -// (presumably this one is made from a single sample) -/*void brain::resynth(const string &filename, const brain &other, const search_params ¶ms){ - sample out((m_block_size-m_overlap)*m_blocks.size()); - out.zero(); - u32 pos = 0; - u32 count = 0; - cerr<::iterator i=m_blocks.begin(); i!=m_blocks.end(); ++i) { - cerr<<'\r'; - cerr<<"searching: "<fft(source); - Aquila::MelFilterBank bank(44100, m_inputSize); + Aquila::MelFilterBank bank(m_sampleRate, m_inputSize); auto filterOutput = bank.applyAll(spectrum); Aquila::Dct dct; diff --git a/brain/src/mfcc.h b/brain/src/mfcc.h index 03b2c4b..c203d5a 100644 --- a/brain/src/mfcc.h +++ b/brain/src/mfcc.h @@ -59,8 +59,9 @@ namespace Aquila * * @param inputSize input length (common to all inputs) */ - Mfcc(std::size_t inputSize): - m_inputSize(inputSize)//, m_fft(FftFactory::getFft(inputSize)) + Mfcc(std::size_t inputSize, unsigned int sampleRate): + m_inputSize(inputSize), // m_fft(FftFactory::getFft(inputSize)) + m_sampleRate(sampleRate) { } @@ -71,7 +72,8 @@ namespace Aquila * Number of samples in each processed input. */ const std::size_t m_inputSize; - + const unsigned int m_sampleRate; + /** * FFT calculator. */ diff --git a/brain/src/spiralcore/OSC_server.cpp b/brain/src/spiralcore/OSC_server.cpp index cfd1590..523e17f 100644 --- a/brain/src/spiralcore/OSC_server.cpp +++ b/brain/src/spiralcore/OSC_server.cpp @@ -31,117 +31,106 @@ extern "C" { #endif OSC_server::OSC_server(const string &port) : -m_port(port), -m_exit(false), -m_command_ring_buffer(262144) -{ - //cerr<<"using port: ["<COMMAND_DATA_SIZE) - { - cerr<<"osc data too big for ringbuffer command"<COMMAND_DATA_SIZE) - { - cerr<<"osc data too big for ringbuffer command"<s); - - if (pos+size+1>COMMAND_DATA_SIZE) - { - cerr<<"osc data too big for ringbuffer command"<s,size); - newdata[pos+size]='\0'; - pos+=size+1; - } - break; - default: - { - cerr<<"unsupported type: "<m_command_ring_buffer.send(command)) - { - //cerr<<"OSC_server - ringbuffer full!"<COMMAND_DATA_SIZE) { + cerr<<"osc data too big for ringbuffer command"<COMMAND_DATA_SIZE) { + cerr<<"osc data too big for ringbuffer command"<s); + + if (pos+size+1>COMMAND_DATA_SIZE) { + cerr<<"osc data too big for ringbuffer command"<s,size); + newdata[pos+size]='\0'; + pos+=size+1; + } + break; + default: { + cerr<<"unsupported type: "<m_command_ring_buffer.send(command)) { + //cerr<<"OSC_server - ringbuffer full!"<maxOutputChannels>=2) { + device_desc desc; + desc.name = deviceInfo->name; + desc.id = i; + desc.default_input = i==default_input_num; + desc.default_output = i==default_output_num; + m_devices.push_back(desc); + } + } + + m_initialised=true; + return true; + } + return true; +} + +int portaudio_client::device_name_to_id(const string &name) { + for (auto &d:m_devices) { + if (d.name==name) return d.id; + } + return -1; } ///////////////////////////////////////////////////////////////////////////////////////////// -bool portaudio_client::attach(const string &client_name, const device_options &dopt) { - if (m_attached) return true; - - PaError err; - err = Pa_Initialize(); - if( err != paNoError ) { - cerr<<"could not init portaudio_client"<defaultLowOutputLatency; - output_parameters.hostApiSpecificStreamInfo = NULL; - cerr<<"Connecting to "<name<<" for output"<defaultLowOutputLatency; + output_parameters.hostApiSpecificStreamInfo = NULL; + m_status="connecting to "+string(Pa_GetDeviceInfo(output_parameters.device)->name)+" for output\n"; + + // turn off input for the moment, it's causing + // too many cross platform security issues + + /*PaStreamParameters input_parameters; PaStreamParameters *input_p=&input_parameters; input_parameters.device = input_device_num; if (true || input_parameters.device == paNoDevice) { cerr<<"error: no default input device."<defaultLowInputLatency; input_parameters.hostApiSpecificStreamInfo = NULL; cerr<<"Connecting to "<name<<" for input"< m_devices; + + bool attach(const string &requested_output_device, const string &client_name, const device_options &dopt); void detach(); - bool is_attached() { return m_attached; } + bool is_attached() { return m_attached_device!=-1; } void set_callback(void(*run)(void*, unsigned int),void *context) { run_callback=run; run_context=context; } void set_outputs(const float *l, const float *r) { m_left_data=l; m_right_data=r; } void set_inputs(float *l, float *r) { m_left_in_data=l; m_right_in_data=r; } + string m_status; + protected: static int process(const void *input_buffer, void *output_buffer, @@ -59,17 +71,21 @@ class portaudio_client private: + int device_name_to_id(const string &name); + static long unsigned int m_buffer_size; - static long unsigned int m_sample_rate; - static bool m_attached; - + static bool m_initialised; + static int m_attached_device; + static const float *m_right_data; static const float *m_left_data; static float *m_right_in_data; static float *m_left_in_data; - + static void(*run_callback)(void *, unsigned int); static void *run_context; + static PaStream *m_stream; + }; #endif diff --git a/brain/src/status.cpp b/brain/src/status.cpp index 4fc07ec..de90912 100644 --- a/brain/src/status.cpp +++ b/brain/src/status.cpp @@ -22,6 +22,10 @@ using namespace std; lo_address status::m_address = lo_address_new_from_url("osc.udp://localhost:8890"); +void status::set_port(const std::string &port) { + status::m_address = lo_address_new_from_url(string("osc.udp://localhost:"+port).c_str()); +} + void status::_update(const std::string &msg) { lo_send(m_address,"/report","s",msg.c_str()); } diff --git a/brain/src/status.h b/brain/src/status.h index 4a78a1e..ea5c05c 100644 --- a/brain/src/status.h +++ b/brain/src/status.h @@ -24,11 +24,14 @@ namespace spiralcore { class status { public: - static void _update(const std::string &msg); - static void update(const char *msg, ...); - static void sound_item(const std::string &name, const std::string &colour); - static void sound_item_refresh(); - static lo_address m_address; + static void set_port(const std::string &port); + static void _update(const std::string &msg); + static void update(const char *msg, ...); + static void sound_item(const std::string &name, const std::string &colour); + static void sound_item_refresh(); + static void add_audio_device(int id, const std::string &name, bool default_output); + static void audio_device_status(const std::string &status); + static lo_address m_address; }; } diff --git a/building.md b/building.md new file mode 100644 index 0000000..90c068e --- /dev/null +++ b/building.md @@ -0,0 +1,71 @@ +# Building from source +## Linux (Ubuntu) +Install cmake: + + $ sudo apt install cmake + +Install dependencies for the interface: + + $ sudo apt install build-essential qtcreator qt5-default + +On ubuntu 22.04 it's: + + $ apt install build-essential qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools + +Build & run it: + + $ mkdir build + $ cd build + $ cmake .. + $ cmake --build . + $ ./samplebrain + +## Mac +Install cmake: + + $ brew install cmake + +Install dependencies for the interface: + + $ brew install qt + $ brew link qt + +Build & run it: + + $ mkdir build + $ cd build + $ cmake .. + $ cmake --build . + +`samplebrain.app` should then be in the build folder for you to run. + +# Mac build additions + +To make a mac app bundle: + +Run `macdeployqt` which copies all dependencies inside the app. + + $ cd build + $ macdeployqt + +If the icon is not visible, you might need to copy desktop/samplebrain.icns (the icon) to the Resources directory in the app bundle. + + $ cp ../desktop/samplebrain.icns samplebrain.app/Contents/Resources + +Then edit Info.plist to add samplebrain.icns to CFBundleIconFile. Key `CFBundleIconFile` should match: + + CFBundleIconFile + samplebrain.icns + +You might also need to resign the app bundle after making any changes + + $ codesign --force --deep --sign - samplebrain.app + +# Windows (Work in progress) + +* Install [MSYS2](https://www.msys2.org/) +* Install dependances via pacman +* Build with qmake as usual +* Run `windeployqt` and copy missing .dll files into release directory + + \ No newline at end of file diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..f49a480 --- /dev/null +++ b/changelog.md @@ -0,0 +1,27 @@ +# Changlog + +0.18.3 + +* **Windows**: [samplebrain_0.18.3_win.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.3_win.zip) +* **Mac (intel/m1)**: [samplebrain_0.18.3_macintel.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.3_macintel.app.zip) + +Changes: Release fixes loading samples from paths longer than 255 characters + +0.18.2 + +* **Windows**: [samplebrain_0.18.2_win.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.2_win.zip) + +Changes: Crash fix when closing load session file dialog + +0.18.1 + +* **Windows**: [samplebrain_0.18.1_win.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.1_win.zip) +* **Mac (intel/m1)**: [samplebrain_0.18.1_macintel.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.1_macintel.app.zip) + +Changes: Turned off microphone input to prevent security problems + +0.18 (initial release) + +* **Windows**: [samplebrain_0.18_win.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18_win.zip) +* **Mac (intel)**: [samplebrain_0.18_macintel.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18_macintel.zip) +* **Mac (m1)**: [samplebrain_0.18_m1_v2.dmg](https://static.thentrythis.org/samplebrain/samplebrain_0.18_m1_v2.dmg) diff --git a/desktop/org.thentrythis.Samplebrain.metainfo.xml b/desktop/org.thentrythis.Samplebrain.metainfo.xml new file mode 100644 index 0000000..63babad --- /dev/null +++ b/desktop/org.thentrythis.Samplebrain.metainfo.xml @@ -0,0 +1,42 @@ + + + org.thentrythis.Samplebrain + + Samplebrain + A custom sample mashing app designed by Aphex Twin + + CC0-1.0 + GPL-2.0-only + + +

+ Samplebrain chops samples up into a 'brain' of interconnected small sections called blocks which are connected into a network by similarity. It processes a target sample, chopping it up into blocks in the same way, and tries to match each block with one in it's brain to play in realtime. +

+

+ This allows you to interpret a sound with a different one. As we worked on it (during 2015 and 2016) we gradually added more and more tweakable parameters until it became slightly out of control. +

+
+ + samplebrain.desktop + + + AudioVideo + Music + + + + + https://thentrythis.org/projects/samplebrain/ + https://gitlab.com/then-try-this/samplebrain/-/issues + https://gitlab.com/then-try-this/samplebrain + + + + https://gitlab.com/then-try-this/samplebrain/-/raw/main/desktop/screenshots/01-main-with-project.png + + + + + + +
diff --git a/desktop/samplebrain.desktop b/desktop/samplebrain.desktop index 20d400b..0b94e56 100644 --- a/desktop/samplebrain.desktop +++ b/desktop/samplebrain.desktop @@ -1,10 +1,8 @@ [Desktop Entry] -Encoding=UTF-8 Type=Application Name=Samplebrain -Comment=A sample masher designed by Aphex Twin -Exec=samplebrain +Comment=A custom sample mashing app designed by Aphex Twin Icon=samplebrain -Terminal=false -Categories=GNOME;Application; -StartupNotify=true \ No newline at end of file +Exec=samplebrain +Categories=AudioVideo;Music +StartupNotify=true diff --git a/desktop/samplebrain.desktop~ b/desktop/samplebrain.desktop~ deleted file mode 100644 index fd7a886..0000000 --- a/desktop/samplebrain.desktop~ +++ /dev/null @@ -1,7 +0,0 @@ -[Desktop Entry] -Type=Application -Version=1.0 -Name=Samplebrain -GenericName="Samplebrain" -Exec=samplebrain -Icon=samplebrain diff --git a/desktop/screenshots/01-main-with-project.png b/desktop/screenshots/01-main-with-project.png new file mode 100644 index 0000000..94b4e80 Binary files /dev/null and b/desktop/screenshots/01-main-with-project.png differ diff --git a/samplebrain.pro b/samplebrain.pro deleted file mode 100644 index 03e4e46..0000000 --- a/samplebrain.pro +++ /dev/null @@ -1,66 +0,0 @@ -###################################################################### -# Automatically generated by qmake (2.01a) Sun Jul 5 17:49:45 2015 -###################################################################### - -TEMPLATE = app -TARGET = samplebrain -DEPENDPATH += . 2 -INCLUDEPATH += . 2 - -QT += core gui widgets - -# Input -HEADERS += app/MainWindow.h - -FORMS += gui/samplebrain.ui - -SOURCES += app/MainWindow.cpp \ - app/sound_items.cpp \ - app/audio_thread.cpp \ - app/process_thread.cpp \ - app/feedback.cpp \ - app/qtmain.cpp \ - brain/src/block.cpp \ - brain/src/brain.cpp \ - brain/src/fft.cpp \ - brain/src/mfcc.cpp \ - brain/src/renderer.cpp \ - brain/src/search_params.cpp \ - brain/src/status.cpp \ - brain/src/window.cpp \ - brain/src/block_stream.cpp \ - brain/src/aquila/filter/MelFilterBank.cpp \ - brain/src/aquila/filter/MelFilter.cpp \ - brain/src/aquila/transform/Dct.cpp \ - brain/src/spiralcore/sample.cpp \ - brain/src/spiralcore/ring_buffer.cpp \ - brain/src/spiralcore/command_ring_buffer.cpp \ - brain/src/spiralcore/portaudio_client.cpp \ - brain/src/spiralcore/audio.cpp \ - brain/src/spiralcore/OSC_server.cpp \ - brain/src/spiralcore/allocator.cpp \ - brain/src/spiralcore/stream.cpp - -INCLUDEPATH += brain/src -INCLUDEPATH += /usr/local/include -INCLUDEPATH += /opt/homebrew/include -LIBS += -L.. -L/usr/local/lib -L/opt/homebrew/lib -lportaudio -lfftw3 -lsndfile -llo -ldl -lpthread -lm -fopenmp - -QMAKE_CXXFLAGS += -O3 -fopenmp -Wall -Wno-unused -std=c++11 - -# assets -RESOURCES = app/samplebrain.qrc -ICON = desktop/samplebrain.icns - -PREFIX = $$(PREFIX) -isEmpty(PREFIX) { - PREFIX = /usr -} - -unix:desktopfile.path = $$PREFIX/share/applications/ -unix:desktopfile.files = desktop/samplebrain.desktop -unix:iconfile.path = $$PREFIX/share/icons/hicolor/scalable/apps -unix:iconfile.files = desktop/samplebrain.svg - -target.path = $$PREFIX/bin -INSTALLS += target desktopfile iconfile