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 image: ubuntu:22.04
script: script:
- apt-get update - 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 - mkdir -p build
- cd build - cd build
- cmake .. - cmake ..

View File

@ -15,8 +15,9 @@ set(CMAKE_AUTOUIC ON)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt5 REQUIRED COMPONENTS Core) find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core)
find_package(Qt5 REQUIRED COMPONENTS Gui Widgets) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Widgets)
find_package(OpenMP)
add_executable(samplebrain WIN32 MACOSX_BUNDLE add_executable(samplebrain WIN32 MACOSX_BUNDLE
app/MainWindow.cpp app/MainWindow.h app/MainWindow.cpp app/MainWindow.h
@ -55,15 +56,20 @@ target_include_directories(samplebrain PRIVATE
) )
target_link_libraries(samplebrain PRIVATE target_link_libraries(samplebrain PRIVATE
Qt5::Core Qt::Core
Qt5::Gui Qt::Gui
Qt5::Widgets Qt::Widgets
fftw3 fftw3
lo_shared lo_shared
portaudio portaudio
sndfile sndfile
) )
if(OpenMP_CXX_FOUND)
target_link_libraries(samplebrain PUBLIC OpenMP::OpenMP_CXX)
endif()
# Resources: # Resources:
set(samplebrain_resource_files set(samplebrain_resource_files
"app/images/at.png" "app/images/at.png"
@ -74,14 +80,14 @@ set(samplebrain_resource_files
"app/images/stop.png" "app/images/stop.png"
) )
#qt5_add_resources(samplebrain "samplebrain" qt_add_resources(samplebrain "samplebrain"
# PREFIX PREFIX
# "/images" "/images"
# BASE BASE
# "app" "app"
# FILES FILES
# ${samplebrain_resource_files} ${samplebrain_resource_files}
#) )
install(TARGETS samplebrain install(TARGETS samplebrain
BUNDLE DESTINATION . 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 create the demo session [can be found here for
testing](https://static.thentrythis.org/samplebrain/samples/). testing](https://static.thentrythis.org/samplebrain/samples/).
# Community
* https://www.reddit.com/r/samplebrain/
# Download # Download
As this is experimental non-commercial software (only originally 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 us as we gradually stabilise things based on your feedback. There
might currently be problems running it on 64bit Windows. 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) * **Windows**: [samplebrain_0.18.5_win.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.5_win.zip)
* **Mac (intel/m1)**: [samplebrain_0.18.4_macintel.zip](https://static.thentrythis.org/samplebrain/samplebrain_0.18.4_macintel.app.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 Changes in 0.18.5 (relased 28/10/22):
windows build. Better default block size, tool tip tweaks and fixes
for dark themes by [Claude Heiland-Allen](https://mathr.co.uk/). * 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) For old versions see the [changelog](changelog.md)

View File

@ -28,7 +28,11 @@
using namespace std; using namespace std;
MainWindow::MainWindow(const string &port, const string &audio_port, const string &process_port, QSettings *settings) : 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_feedback(port),
m_audio_port(audio_port), m_audio_port(audio_port),
m_process_port(process_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()); m_Ui.spinBoxSlideError->setValue(r.get_slide_error());
// target // 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.spinBoxBlockSizeTarget->setValue(t.get_block_size());
m_Ui.doubleSpinBoxBlockOverlapTarget->setValue(t.get_overlap()/(float)t.get_block_size()); m_Ui.doubleSpinBoxBlockOverlapTarget->setValue(t.get_overlap()/(float)t.get_block_size());

View File

@ -206,12 +206,15 @@ private slots:
void run_slot() {} void run_slot() {}
void load_target() { void load_target() {
m_last_file=QFileDialog::getOpenFileName(this, QString path=QFileDialog::getOpenFileName(this,
QString("Select an audio file"), QString("Select an audio file"),
m_last_file, m_last_target_file,
m_format_string); m_format_string);
if (m_last_target_file!="") {
send_process_osc("/load_target","s",m_last_file.toStdString().c_str()); 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_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 target_block_overlap(double s) { send_process_osc("/target_overlap","f",s); }
@ -221,14 +224,15 @@ private slots:
void fft_spectrum_size(int) {} void fft_spectrum_size(int) {}
void generate() { send_process_osc("/generate_brain",""); } void generate() { send_process_osc("/generate_brain",""); }
void load_sound() { void load_sound() {
m_last_file=QFileDialog::getOpenFileName(this, QString path=QFileDialog::getOpenFileName(this,
QString("Select a wav file"), QString("Select a wav file"),
m_last_file, m_last_sound_file,
m_format_string); m_format_string);
if (m_last_file!="") { if (path!="") {
send_process_osc("/load_sample","s",m_last_file.toStdString().c_str()); m_last_sound_file=path;
sound_items::sound_item &si = m_sound_items.add(m_Ui.brain_contents, m_last_file.toStdString(),true); 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())); 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); 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())); QObject::connect(si.m_del, SIGNAL(clicked()), m_sound_item_delete_mapper, SLOT(map()));
@ -238,12 +242,13 @@ private slots:
void load_sounds() { void load_sounds() {
m_last_file=QFileDialog::getExistingDirectory(this, QString path=QFileDialog::getExistingDirectory(this,
QString("Select a directory of wav files"), QString("Select a directory of wav files"),
m_last_file); m_last_directory_file);
if (path!="") {
QDirIterator dirIt(m_last_file,QDirIterator::Subdirectories); m_last_directory_file=path;
QDirIterator dirIt(m_last_directory_file,QDirIterator::Subdirectories);
while (dirIt.hasNext()) { while (dirIt.hasNext()) {
dirIt.next(); dirIt.next();
if (QFileInfo(dirIt.filePath()).isFile() && if (QFileInfo(dirIt.filePath()).isFile() &&
@ -260,6 +265,7 @@ private slots:
} }
} }
} }
}
void select_all() { void select_all() {
for (auto &si:m_sound_items.m_sound_items) { for (auto &si:m_sound_items.m_sound_items) {
@ -313,12 +319,11 @@ private slots:
void record() { void record() {
if (m_save_wav=="") { if (m_save_wav=="") {
m_last_file=QFileDialog::getSaveFileName( m_last_recording_file=QFileDialog::getSaveFileName(this,
this,
QString("Select a wav file"), QString("Select a wav file"),
m_last_file, m_last_recording_file,
QString("Sounds (*.wav);;All files (*.*)")); QString("Sounds (*.wav);;All files (*.*)"));
m_save_wav = m_last_file.toStdString(); m_save_wav = m_last_recording_file.toStdString();
// chop off .wav // chop off .wav
size_t pos = m_save_wav.find_last_of("."); size_t pos = m_save_wav.find_last_of(".");
if (pos!=string::npos) { if (pos!=string::npos) {
@ -339,43 +344,48 @@ private slots:
} }
void load_brain() { void load_brain() {
m_last_file=QFileDialog::getOpenFileName( QString path=QFileDialog::getOpenFileName(this,
this,
QString("Select a brain file"), QString("Select a brain file"),
m_last_file, m_last_brain_file,
QString("Brains (*.brain);;All files (*.*)")); 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() { void save_brain() {
m_last_file=QFileDialog::getSaveFileName( QString path=QFileDialog::getSaveFileName(this,
this,
QString("Select a brain file"), QString("Select a brain file"),
m_last_file, m_last_brain_file,
QString("Brains (*.brain);;All files (*.*)")); QString("Brains (*.brain);;All files (*.*)"));
if (path!="") {
send_process_osc("/save_brain","s",m_last_file.toStdString().c_str()); m_last_brain_file=path;
send_process_osc("/save_brain","s",m_last_brain_file.toStdString().c_str());
}
} }
void load_session() { void load_session() {
m_last_file=QFileDialog::getOpenFileName( QString path=QFileDialog::getOpenFileName(this,
this,
QString("Select a session file"), QString("Select a session file"),
m_last_file, m_last_session_file,
QString("Sessions *.samplebrain (*.samplebrain);;All files (*.*)")); QString("Sessions *.samplebrain (*.samplebrain);;All files (*.*)"));
if (path!="") {
send_process_osc("/load_session","s",m_last_file.toStdString().c_str()); m_last_session_file=path;
init_from_session(m_last_file.toStdString()); send_process_osc("/load_session","s",m_last_session_file.toStdString().c_str());
init_from_session(m_last_session_file.toStdString());
}
} }
void save_session() { void save_session() {
m_last_file=QFileDialog::getSaveFileName( QString path=QFileDialog::getSaveFileName(this,
this,
QString("Select a session file"), QString("Select a session file"),
m_last_file, m_last_session_file,
QString("Sessions *.samplebrain (*.samplebrain)")); QString("Sessions *.samplebrain (*.samplebrain)"));
if (path!="") {
send_process_osc("/save_session","s",m_last_file.toStdString().c_str()); m_last_session_file=path;
send_process_osc("/save_session","s",m_last_session_file.toStdString().c_str());
}
} }
void update_status() { void update_status() {
@ -440,7 +450,12 @@ private:
QSignalMapper* enable_mapper); QSignalMapper* enable_mapper);
string m_save_wav; 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; unsigned int m_record_id;
Ui_MainWindow m_Ui; Ui_MainWindow m_Ui;
feedback m_feedback; feedback m_feedback;

View File

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

View File

@ -112,6 +112,7 @@ void block::process(const sample &pcm, sample &fft, sample &mfcc, float &freq) {
// calculate fft // calculate fft
std::vector<std::complex<double> > mfspec; std::vector<std::complex<double> > mfspec;
mfspec.reserve(m_block_size);
for (u32 i=0; i<m_block_size; ++i) { for (u32 i=0; i<m_block_size; ++i) {
mfspec.push_back(std::complex<double>(m_fftw->m_spectrum[i][0], mfspec.push_back(std::complex<double>(m_fftw->m_spectrum[i][0],
m_fftw->m_spectrum[i][1])); 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_window.set_current_type(t);
m_blocks.clear(); m_blocks.clear();
m_blocks.reserve(MAX_BLOCKS);
sample dummy(block_size); sample dummy(block_size);
for (u32 i=0; i<MAX_BLOCKS; i++) { for (u32 i=0; i<MAX_BLOCKS; i++) {
m_blocks.push_back(block(0,"dummy",dummy,44100,m_window)); m_blocks.push_back(block(0,"dummy",dummy,44100,m_window));

View File

@ -16,6 +16,7 @@
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
#include <atomic>
#include <sndfile.h> #include <sndfile.h>
#include <float.h> #include <float.h>
#include <spiralcore/audio.h> #include <spiralcore/audio.h>
@ -228,6 +229,7 @@ u32 brain::rev_search(const block &target, const search_params &params) {
return furthest_index; return furthest_index;
} }
/*
// really slow - every to every comparison of blocks calculating average distance // really slow - every to every comparison of blocks calculating average distance
double brain::calc_average_diff(search_params &params) { double brain::calc_average_diff(search_params &params) {
double diff=0; 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; m_average_error = calc_average_diff(params)*thresh;
double err = m_average_error*thresh; double err = m_average_error*thresh;
u32 brain_size = m_blocks.size(); u32 brain_size = m_blocks.size();
u32 outer_index = 0; std::atomic<u32> progress{0};
for (auto &i : m_blocks) { #pragma omp parallel for
for (u32 outer_index = 0; outer_index < brain_size; ++outer_index) {
auto &i = m_blocks[outer_index];
u32 index = 0; 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) { for (auto &j : m_blocks) {
if (index!=outer_index) { if (index!=outer_index) {
// collect connections that are under threshold in closeness // collect connections that are under threshold in closeness
@ -258,30 +262,33 @@ void brain::build_synapses_thresh(search_params &params, double thresh) {
} }
++index; ++index;
} }
++outer_index; ++progress;
} }
} }
*/
void brain::build_synapses_fixed(search_params &params) { void brain::build_synapses_fixed(search_params &params) {
//m_average_error = calc_average_diff(params)*thresh; //m_average_error = calc_average_diff(params)*thresh;
u32 brain_size = m_blocks.size(); u32 brain_size = m_blocks.size();
u32 outer_index = 0;
u32 num_synapses = NUM_FIXED_SYNAPSES; u32 num_synapses = NUM_FIXED_SYNAPSES;
if (num_synapses>=m_blocks.size()) num_synapses=m_blocks.size()-1; if (num_synapses>=m_blocks.size()) num_synapses=m_blocks.size()-1;
// need to stop the progress updates flooding osc // need to stop the progress updates flooding osc
u32 update_period = 100; u32 update_period = 100;
u32 update_tick = 0; std::atomic<u32> update_tick{0};
std::atomic<u32> progress{0};
for (auto &i:m_blocks) { #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) { 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=0;
} }
update_tick++; update_tick++;
u32 index = 0; u32 index = 0;
vector<pair<u32,double>> collect; vector<pair<u32,double>> collect;
collect.reserve(brain_size);
// collect comparisons to all other blocks // collect comparisons to all other blocks
for (auto &j:m_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); 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); 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); u32 rev_search(const block &target, const search_params &params);
// synaptic search // synaptic search
double calc_average_diff(search_params &params); // double calc_average_diff(search_params &params);
void build_synapses_thresh(search_params &params, double threshold); // void build_synapses_thresh(search_params &params, double threshold);
void build_synapses_fixed(search_params &params); void build_synapses_fixed(search_params &params);
u32 search_synapses(const block &target, search_params &params); u32 search_synapses(const block &target, search_params &params);
double get_current_error() { return m_current_error; } double get_current_error() { return m_current_error; }

View File

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