Compare commits

...

14 Commits

Author SHA1 Message Date
Dave Griffiths 7a02dcfa3c switched (back) to qt6 2022-10-29 13:49:15 +01:00
Dave Griffiths b983408bd7 Merge branch 'samplebrain-parallel-synapse-generation' into main 2022-10-29 13:14:34 +01:00
Dave Griffiths fa44d2b4c7 added openmp to cmake 2022-10-29 13:13:16 +01:00
Dave Griffiths 8e7a9abe1a removed pro file 2022-10-29 13:00:53 +01:00
Dave Griffiths 65ea2080be Merge remote-tracking branch 'origin/development' into main 2022-10-29 12:53:34 +01:00
Dave Griffiths edf2071467 added reddit 2022-10-29 12:39:18 +01:00
Dave Griffiths 2e517e3bff changelog update 2022-10-29 08:48:13 +01:00
Dave Griffiths 1b766822f6 new version README update 2022-10-29 08:35:34 +01:00
Dave Griffiths bc8002a9f0 added target filename to GUI - not adding brain session or directory as less need for it/more clutter... partially fixes: #76 2022-10-23 10:06:23 +01:00
Dave Griffiths f9f3557b79 sorted file paths so we have separate ones for each dialog - fixes: #75 2022-10-23 09:34:46 +01:00
Dave Griffiths c4499ce7c9 commented out unused and confusing old code - fixes: #28 2022-10-20 21:22:23 +01:00
Dave Griffiths 46ab0a131a optimisation from Claude - fixes: #27 2022-10-20 21:15:27 +01:00
Dave Griffiths 884c8ad91f resources location 2022-10-20 21:12:58 +01:00
Claude Heiland-Allen 8aa0dee3d1 parallelize synapse generation using OpenMP
- only tested on Debian Linux with a 16-thread amd64 CPU
- Windows may need DLLs to be shipped with the EXE
- using `-fopenmp` should be made optional via qmake somehow
  (without the flag, parallelization pragmas are ignored
  and it reverts to serial operation)
- assumes `status::update()` among other code is thread-safe,
  it seems to work on my machine without issues...
2022-09-27 13:19:04 +01:00
12 changed files with 177 additions and 107 deletions

View File

@ -3,7 +3,7 @@ build-job:
image: ubuntu:22.04
script:
- apt-get update
- apt-get install -y git cmake g++ freeglut3-dev qtbase5-dev qt5-qmake qtbase5-dev-tools
- apt-get install -y git cmake g++ freeglut3-dev qt6-base-dev qt5-qmake qtbase5-dev-tools
- mkdir -p build
- cd build
- cmake ..

View File

@ -15,8 +15,9 @@ 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)
find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Widgets)
find_package(OpenMP)
add_executable(samplebrain WIN32 MACOSX_BUNDLE
app/MainWindow.cpp app/MainWindow.h
@ -55,15 +56,20 @@ target_include_directories(samplebrain PRIVATE
)
target_link_libraries(samplebrain PRIVATE
Qt5::Core
Qt5::Gui
Qt5::Widgets
Qt::Core
Qt::Gui
Qt::Widgets
fftw3
lo_shared
portaudio
sndfile
)
if(OpenMP_CXX_FOUND)
target_link_libraries(samplebrain PUBLIC OpenMP::OpenMP_CXX)
endif()
# Resources:
set(samplebrain_resource_files
"app/images/at.png"
@ -74,14 +80,14 @@ set(samplebrain_resource_files
"app/images/stop.png"
)
#qt5_add_resources(samplebrain "samplebrain"
# PREFIX
# "/images"
# BASE
# "app"
# FILES
# ${samplebrain_resource_files}
#)
qt_add_resources(samplebrain "samplebrain"
PREFIX
"/images"
BASE
"app"
FILES
${samplebrain_resource_files}
)
install(TARGETS samplebrain
BUNDLE DESTINATION .

View File

@ -34,6 +34,10 @@ 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/).
# Community
* https://www.reddit.com/r/samplebrain/
# Download
As this is experimental non-commercial software (only originally
@ -41,12 +45,16 @@ 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)
* **Windows**: [samplebrain_0.18.5_win.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.5_win.zip)
* **Mac (intel/m1)**: [samplebrain_0.18.5_macintel.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.5_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/).
Changes in 0.18.5 (relased 28/10/22):
* Target sound filename shown (and tells you if you don't have one)
* More soundfile formats supported (aiff,aifc,au,snd,fasttracker xi,flac)
* New configurable OSC ports in settings
* Warning boxes if the OSC network connection fails
* File path memory per-dialog rather than global
For old versions see the [changelog](changelog.md)

View File

@ -28,7 +28,11 @@
using namespace std;
MainWindow::MainWindow(const string &port, const string &audio_port, const string &process_port, QSettings *settings) :
m_last_file("."),
m_last_sound_file("."),
m_last_target_file("."),
m_last_brain_file("."),
m_last_session_file("."),
m_last_recording_file("."),
m_feedback(port),
m_audio_port(audio_port),
m_process_port(process_port),
@ -157,6 +161,12 @@ void MainWindow::init_from_session(const string &filename) {
m_Ui.spinBoxSlideError->setValue(r.get_slide_error());
// target
if (t.get_samples().size()>0) {
// extract target filename from brain sample
string fn = t.get_samples().begin()->m_filename;
m_Ui.labelTargetSound->setText("loaded: "+QFileInfo(QString::fromStdString(fn)).fileName());
}
m_Ui.spinBoxBlockSizeTarget->setValue(t.get_block_size());
m_Ui.doubleSpinBoxBlockOverlapTarget->setValue(t.get_overlap()/(float)t.get_block_size());

View File

@ -67,7 +67,7 @@ public:
}
}
}
void send_process_osc(const char *name, const char *types) {
for (auto dest:m_destinations) {
if (dest.m_enabled) {
@ -206,12 +206,15 @@ private slots:
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());
QString path=QFileDialog::getOpenFileName(this,
QString("Select an audio file"),
m_last_target_file,
m_format_string);
if (m_last_target_file!="") {
m_last_target_file=path;
m_Ui.labelTargetSound->setText("loaded: "+QFileInfo(path).fileName());
send_process_osc("/load_target","s",m_last_target_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); }
@ -221,14 +224,15 @@ private slots:
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);
QString path=QFileDialog::getOpenFileName(this,
QString("Select a wav file"),
m_last_sound_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);
if (path!="") {
m_last_sound_file=path;
send_process_osc("/load_sample","s",m_last_sound_file.toStdString().c_str());
sound_items::sound_item &si = m_sound_items.add(m_Ui.brain_contents, m_last_sound_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()));
@ -238,25 +242,27 @@ private slots:
void load_sounds() {
m_last_file=QFileDialog::getExistingDirectory(this,
QString("Select a directory of wav files"),
m_last_file);
QString path=QFileDialog::getExistingDirectory(this,
QString("Select a directory of wav files"),
m_last_directory_file);
if (path!="") {
m_last_directory_file=path;
QDirIterator dirIt(m_last_directory_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());
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);
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);
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);
}
}
}
}
@ -313,12 +319,11 @@ private slots:
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();
m_last_recording_file=QFileDialog::getSaveFileName(this,
QString("Select a wav file"),
m_last_recording_file,
QString("Sounds (*.wav);;All files (*.*)"));
m_save_wav = m_last_recording_file.toStdString();
// chop off .wav
size_t pos = m_save_wav.find_last_of(".");
if (pos!=string::npos) {
@ -339,43 +344,48 @@ private slots:
}
void load_brain() {
m_last_file=QFileDialog::getOpenFileName(
this,
QString("Select a brain file"),
m_last_file,
QString("Brains (*.brain);;All files (*.*)"));
QString path=QFileDialog::getOpenFileName(this,
QString("Select a brain file"),
m_last_brain_file,
QString("Brains (*.brain);;All files (*.*)"));
send_process_osc("/load_brain","s",m_last_file.toStdString().c_str());
if (path!="") {
m_last_brain_file=path;
send_process_osc("/load_brain","s",m_last_brain_file.toStdString().c_str());
}
}
void save_brain() {
m_last_file=QFileDialog::getSaveFileName(
this,
QString("Select a brain file"),
m_last_file,
QString("Brains (*.brain);;All files (*.*)"));
send_process_osc("/save_brain","s",m_last_file.toStdString().c_str());
QString path=QFileDialog::getSaveFileName(this,
QString("Select a brain file"),
m_last_brain_file,
QString("Brains (*.brain);;All files (*.*)"));
if (path!="") {
m_last_brain_file=path;
send_process_osc("/save_brain","s",m_last_brain_file.toStdString().c_str());
}
}
void load_session() {
m_last_file=QFileDialog::getOpenFileName(
this,
QString("Select a session file"),
m_last_file,
QString("Sessions *.samplebrain (*.samplebrain);;All files (*.*)"));
send_process_osc("/load_session","s",m_last_file.toStdString().c_str());
init_from_session(m_last_file.toStdString());
QString path=QFileDialog::getOpenFileName(this,
QString("Select a session file"),
m_last_session_file,
QString("Sessions *.samplebrain (*.samplebrain);;All files (*.*)"));
if (path!="") {
m_last_session_file=path;
send_process_osc("/load_session","s",m_last_session_file.toStdString().c_str());
init_from_session(m_last_session_file.toStdString());
}
}
void save_session() {
m_last_file=QFileDialog::getSaveFileName(
this,
QString("Select a session file"),
m_last_file,
QString("Sessions *.samplebrain (*.samplebrain)"));
send_process_osc("/save_session","s",m_last_file.toStdString().c_str());
QString path=QFileDialog::getSaveFileName(this,
QString("Select a session file"),
m_last_session_file,
QString("Sessions *.samplebrain (*.samplebrain)"));
if (path!="") {
m_last_session_file=path;
send_process_osc("/save_session","s",m_last_session_file.toStdString().c_str());
}
}
void update_status() {
@ -440,7 +450,12 @@ private:
QSignalMapper* enable_mapper);
string m_save_wav;
QString m_last_file;
QString m_last_sound_file;
QString m_last_target_file;
QString m_last_directory_file;
QString m_last_brain_file;
QString m_last_session_file;
QString m_last_recording_file;
unsigned int m_record_id;
Ui_MainWindow m_Ui;
feedback m_feedback;

View File

@ -728,6 +728,13 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelTargetSound">
<property name="text">
<string>no target sound loaded</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonLoadTarget">
<property name="font">
@ -1112,8 +1119,8 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<width>17</width>
<height>13</height>
</size>
</property>
</spacer>
@ -1466,7 +1473,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="../app/samplebrain.qrc">
<iconset resource="../samplebrain.qrc">
<normaloff>:/images/images/play.png</normaloff>:/images/images/play.png</iconset>
</property>
<property name="iconSize">
@ -1493,7 +1500,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="../app/samplebrain.qrc">
<iconset resource="../samplebrain.qrc">
<normaloff>:/images/images/pause.png</normaloff>:/images/images/pause.png</iconset>
</property>
<property name="iconSize">
@ -1513,7 +1520,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="../app/samplebrain.qrc">
<iconset resource="../samplebrain.qrc">
<normaloff>:/images/images/record.png</normaloff>:/images/images/record.png</iconset>
</property>
<property name="iconSize">
@ -1533,7 +1540,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="../app/samplebrain.qrc">
<iconset resource="../samplebrain.qrc">
<normaloff>:/images/images/stop.png</normaloff>:/images/images/stop.png</iconset>
</property>
<property name="iconSize">
@ -1599,7 +1606,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="../app/samplebrain.qrc">
<iconset resource="../samplebrain.qrc">
<normaloff>:/images/images/settings.png</normaloff>:/images/images/settings.png</iconset>
</property>
<property name="iconSize">
@ -1632,7 +1639,7 @@
<string/>
</property>
<property name="pixmap">
<pixmap resource="../app/samplebrain.qrc">:/images/images/at.png</pixmap>
<pixmap resource="../samplebrain.qrc">:/images/images/at.png</pixmap>
</property>
</widget>
</item>
@ -1643,6 +1650,7 @@
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources>
<include location="../samplebrain.qrc"/>
<include location="../app/samplebrain.qrc"/>
</resources>
<connections>

View File

@ -112,6 +112,7 @@ void block::process(const sample &pcm, sample &fft, sample &mfcc, float &freq) {
// calculate fft
std::vector<std::complex<double> > mfspec;
mfspec.reserve(m_block_size);
for (u32 i=0; i<m_block_size; ++i) {
mfspec.push_back(std::complex<double>(m_fftw->m_spectrum[i][0],
m_fftw->m_spectrum[i][1]));

View File

@ -79,6 +79,7 @@ void block_stream::init(u32 block_size, u32 overlap, window::type t, bool ditchp
m_window.set_current_type(t);
m_blocks.clear();
m_blocks.reserve(MAX_BLOCKS);
sample dummy(block_size);
for (u32 i=0; i<MAX_BLOCKS; i++) {
m_blocks.push_back(block(0,"dummy",dummy,44100,m_window));

View File

@ -16,6 +16,7 @@
#include <iostream>
#include <algorithm>
#include <atomic>
#include <sndfile.h>
#include <float.h>
#include <spiralcore/audio.h>
@ -228,6 +229,7 @@ u32 brain::rev_search(const block &target, const search_params &params) {
return furthest_index;
}
/*
// really slow - every to every comparison of blocks calculating average distance
double brain::calc_average_diff(search_params &params) {
double diff=0;
@ -244,10 +246,12 @@ void brain::build_synapses_thresh(search_params &params, double thresh) {
m_average_error = calc_average_diff(params)*thresh;
double err = m_average_error*thresh;
u32 brain_size = m_blocks.size();
u32 outer_index = 0;
for (auto &i : m_blocks) {
std::atomic<u32> progress{0};
#pragma omp parallel for
for (u32 outer_index = 0; outer_index < brain_size; ++outer_index) {
auto &i = m_blocks[outer_index];
u32 index = 0;
status::update("building synapses %d%%",(int)(outer_index/(float)brain_size*100));
status::update("building synapses %d%%",(int)(progress/(float)brain_size*100));
for (auto &j : m_blocks) {
if (index!=outer_index) {
// collect connections that are under threshold in closeness
@ -258,30 +262,33 @@ void brain::build_synapses_thresh(search_params &params, double thresh) {
}
++index;
}
++outer_index;
++progress;
}
}
*/
void brain::build_synapses_fixed(search_params &params) {
//m_average_error = calc_average_diff(params)*thresh;
u32 brain_size = m_blocks.size();
u32 outer_index = 0;
u32 num_synapses = NUM_FIXED_SYNAPSES;
if (num_synapses>=m_blocks.size()) num_synapses=m_blocks.size()-1;
// need to stop the progress updates flooding osc
u32 update_period = 100;
u32 update_tick = 0;
for (auto &i:m_blocks) {
std::atomic<u32> update_tick{0};
std::atomic<u32> progress{0};
#pragma omp parallel for
for (u32 outer_index = 0; outer_index < brain_size; ++outer_index) {
auto &i = m_blocks[outer_index];
if (update_tick>update_period) {
status::update("building synapses %d%%",(int)(outer_index/(float)brain_size*100));
status::update("building synapses %d%%",(int)(progress/(float)brain_size*100));
update_tick=0;
}
update_tick++;
u32 index = 0;
vector<pair<u32,double>> collect;
collect.reserve(brain_size);
// collect comparisons to all other blocks
for (auto &j:m_blocks) {
@ -306,7 +313,7 @@ void brain::build_synapses_fixed(search_params &params) {
i.get_synapse().push_back(collect[n].first);
}
++outer_index;
++progress;
}
status::update("Done: %d synapses grown for %d blocks",num_synapses*brain_size,brain_size);
}

View File

@ -62,8 +62,8 @@ public:
u32 rev_search(const block &target, const search_params &params);
// synaptic search
double calc_average_diff(search_params &params);
void build_synapses_thresh(search_params &params, double threshold);
// double calc_average_diff(search_params &params);
// void build_synapses_thresh(search_params &params, double threshold);
void build_synapses_fixed(search_params &params);
u32 search_synapses(const block &target, search_params &params);
double get_current_error() { return m_current_error; }

View File

@ -1,5 +1,14 @@
# Changlog
0.18.4
* **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/).
0.18.3
* **Windows**: [samplebrain_0.18.3_win.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.3_win.zip)

11
debian/changelog vendored
View File

@ -1,6 +1,11 @@
samplebrain (0.18rc2-1ubuntu0~bionic4) bionic; urgency=medium
samplebrain (0.18.5rc1-1ubuntu0~bionic1) bionic; urgency=medium
* Initial release
* Target sound filename shown (and tells you if you don't have one)
* More soundfile formats supported (aiff,aifc,au,snd,fasttracker xi,flac)
* New configurable OSC ports in settings
* Warning boxes if the OSC network connection fails
* File path memory per-dialog rather than global
-- Dave Griffiths <dave@thentrythis.org> Thu, 29 Oct 2022 08:47:10 +0100
-- Dave Griffiths <dave@thentrythis.org> Thu, 08 Sep 2022 13:08:26 +0100