72 Commits

Author SHA1 Message Date
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
f9f3557b79 sorted file paths so we have separate ones for each dialog - fixes: #75 2022-10-23 09:34:46 +01:00
c4499ce7c9 commented out unused and confusing old code - fixes: #28 2022-10-20 21:22:23 +01:00
46ab0a131a optimisation from Claude - fixes: #27 2022-10-20 21:15:27 +01:00
385563e64e forgot to add flac 2022-10-14 21:24:56 +01:00
e1c8e0ea67 added more soundfile formats to the loading dialog and new error report when it can't load 2022-10-14 20:30:12 +01:00
b5fd6dd3e0 added osc ports to the settings interface, bumped the version num 2022-10-14 19:23:07 +01:00
5e347f7f13 made the 3 osc ports configurable via (cross-platform) config scripts and move them outside of well used ranges for UDP 2022-10-14 18:27:59 +01:00
5469a8f253 added build instructions for ubuntu 22.04 fixes: #66 2022-10-07 08:39:45 +01:00
ac83efce3b readme tweak 2022-10-06 20:55:23 +01:00
7b41c94610 readme tweak 2022-10-06 20:54:40 +01:00
07ea3e63ef readme tweak 2022-10-06 20:51:42 +01:00
182baa59c3 readme tweak 2022-10-06 20:50:47 +01:00
ed0cf2eaac readme tweak 2022-10-06 20:47:48 +01:00
a9755b3f72 readme tweak 2022-10-06 20:46:44 +01:00
a30779b759 separated building and changelog 2022-10-06 20:44:28 +01:00
1c912d3977 readme tweaks 2022-10-06 20:43:06 +01:00
004d9970c8 Merge branch 'tooltip-fixup' into 'main'
fix some tooltips in the GUI

See merge request then-try-this/samplebrain!13
2022-10-05 11:32:45 +00:00
0a309ef8cd Merge branch 'theme' into 'main'
set dark foreground when using light background

See merge request then-try-this/samplebrain!14
2022-10-05 11:31:29 +00:00
28a8bd31f8 set dark foreground when using light background
fixes hard-to-read light text on light background
when using a dark system theme enabled for QT apps
2022-10-05 11:06:18 +01:00
b569c47d40 soundcard text top aligned 2022-10-04 13:55:12 +01:00
ad2a70a8d6 fix some tooltips in the GUI
- some were incorrect, now corrected
- some spinboxes were missing, now added (same as slider)
- still not all widgets have tooltips (notably blocksize + overlap)
2022-10-04 12:44:52 +01:00
014becd12f version bump and added settings cog icon 2022-10-04 10:33:11 +01:00
78aa2b2b3e Merge branch 'main' of gitlab.com:then-try-this/samplebrain into main 2022-10-03 09:56:38 +01:00
4a4ab8b41a added audio settings options for selection of device, samplrate and buffer size, also fixed: #65 & fixed: #64 2022-10-03 09:56:25 +01:00
21522f38e0 Merge branch 'main' into 'main'
update url of catart in readme

See merge request then-try-this/samplebrain!12
2022-10-01 17:36:12 +00:00
729edb25cd update url of catart in readme 2022-10-01 09:08:00 +00:00
fb8d607e2d started making samplerate and buffer size configurable for audio device selection 2022-09-30 21:08:53 +01:00
a2b77951ca new version links 2022-09-30 12:48:36 +01:00
524cd58945 version bump 2022-09-30 12:02:56 +01:00
43890c905e Merge branch 'packaging/flatpak' into 'main'
Add Flathub download badge to README

See merge request then-try-this/samplebrain!10
2022-09-30 10:45:19 +00:00
99e59a6f11 readme: add Flathub download badge 2022-09-29 17:54:25 -04:00
e3619746a4 Update README.md 2022-09-29 11:36:21 +00:00
74eb5b0b9b Update README.md 2022-09-29 11:30:07 +00:00
287ba85c14 Update README.md 2022-09-29 11:29:41 +00:00
a7b866b4fc Update README.md 2022-09-29 10:44:56 +00:00
7bda8195c9 Merge branch 'packaging/flatpak' into 'main'
linux: Prepare for Flathub submission

See merge request then-try-this/samplebrain!9
2022-09-29 07:23:43 +00:00
dc0c047ad6 linux: add AppStream metadata
Adds an AppStream metainfo file that is used to display the app in
graphical app stores on Linux. This is also required for submission to
Flathub.
2022-09-28 18:19:48 -04:00
672fb1868f linux: add a screenshot for the metadata
AppStream metadata requires a screenshot. This screenshot will be used
in the graphical app stores.
2022-09-28 18:12:32 -04:00
30d93be49c linux: update desktop file
Remove unnecessary keys, tweak the comment, and add categories.
2022-09-28 18:12:31 -04:00
297972cd5b Merge branch 'increase-command-size' into 'main'
Increase command data size to 1024. (Fixes #54)

Closes #54

See merge request then-try-this/samplebrain!7
2022-09-28 17:50:33 +00:00
d783b8c16a fixed formatting 2022-09-28 12:42:48 +01:00
cdb6a912c0 windows version bump 2022-09-28 12:38:20 +01:00
1374c47afd Increase command data size to 1024. (Fixes #54) 2022-09-28 04:15:10 -04:00
79fef7cd66 README words 2022-09-27 08:35:44 +01:00
b4c72412f3 more info in README 2022-09-27 08:33:08 +01:00
95de445d9e more info in README 2022-09-27 08:32:16 +01:00
defee25db7 added ICON 2022-09-26 18:32:40 +02:00
2cd44183b9 further mac updates 2022-09-26 18:20:56 +02:00
108029b6f9 macos build updates 2022-09-26 18:15:58 +02:00
43bd1eff3e added liblo to mac build instructions 2022-09-26 12:14:13 +01:00
f521ccafc9 removed ci script 2022-09-26 12:03:38 +01:00
11ed7413b3 fixed broken link duh fixes: #35 2022-09-26 12:01:40 +01:00
0af0d8e20c new binaries added 2022-09-26 10:46:24 +01:00
dcdf481a02 Merge branch 'packaging/flatpak' into 'main'
packaging: Add support for building a Flatpak

See merge request then-try-this/samplebrain!4
2022-09-26 07:45:38 +00:00
e741246b1f turned off ci runner thing 2022-09-26 08:42:13 +01:00
6241b1030d Merge branch 'development' into 'main'
fix build instructions and add mac deps list

See merge request then-try-this/samplebrain!5
2022-09-26 07:41:02 +00:00
70a277afb8 fix build instructions and add mac deps list 2022-09-25 22:07:30 -06:00
b965130cd9 Merge branch 'main' of gitlab.com:then-try-this/samplebrain into main 2022-09-25 22:02:00 +01:00
79b1c1aeda new mac m1 version link 2022-09-25 21:57:38 +01:00
129b6ea416 linux: add a Flatpak manifest
This manifest will build a Flatpak from the current local source
directory.
2022-09-25 16:43:21 -04:00
395ff352e3 Merge branch 'main' into 'main'
added homebrew arm64 paths

See merge request then-try-this/samplebrain!3
2022-09-25 20:37:28 +00:00
dd92364be6 added homebrew arm64 paths 2022-09-25 22:30:39 +02:00
9d7931c8c2 linux: don't use hardcoded path for Exec and Icon
Desktop files respect $PATH and $XDG_DATA_DIRS, so the full path does
not need to be specified to either.

This fixes the Flatpak build by allowing Flatpak to rewrite these to
match the app ID.
2022-09-25 16:21:17 -04:00
74b00570dd build: allow setting installation prefix
Allow $PREFIX to be set during compilation, changing where the app is
installed.

This is used to change where the app is installed inside the Flatpak
sandbox.
2022-09-25 16:21:07 -04:00
493209c28d add .gitlab-ci.yml file 2022-09-25 08:58:05 +00:00
6bc1cbeec3 Merge branch 'upgrade-to-qt6' into 'main'
Update project to Qt6

See merge request then-try-this/samplebrain!2
2022-09-24 20:56:12 +00:00
ff1d4cafd2 it's -> its 2022-09-24 21:51:20 +01:00
a831638171 Update project to Qt6:
* loaded and saved `samplebrain.ui` in Qt6 Designer
* generate `ui_samplebrain.h` on the fly (via `FORMS +=`)
* remove example files `sample.ui` and `ui_sample.h`
* remove `setMargin()` calls, these methods have been removed
* use `QFont::Bold` for `setWeight()` calls, numeric values aren't accepted anymore
* qualify `spiralcore::sample` in `block.cpp`'s `normalise()`
2022-09-24 22:50:43 +02:00
0cfacdc8a8 turned off input device 2022-09-24 21:23:20 +01:00
df0fd84b48 mac binary note 2022-09-24 18:54:00 +01:00
b6dcb229d5 removed mic input as the worker threads were causing high cpu and it's not reliable anyway fixes #12 2022-09-24 17:49:27 +01:00
49 changed files with 2080 additions and 2323 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.*~
*.o

100
README.md
View File

@ -5,7 +5,7 @@ A custom sample mashing app designed by Aphex Twin.
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
in the same way, and tries to match each block with one in its brain
to play in realtime.
This allows you to interpret a sound with a different one. As we
@ -14,41 +14,54 @@ 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 samples into the brain
1. Load a bunch of short wav files into the brain
2. Click (re)generate brain
3. Load a loop sample into the target
4. Click (re)generate target
3. Load a short loop sample into the target
4. Click (re)generate blocks
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.
# [Demo brain session](https://static.thentrythis.org/samplebrain/demo.samplebrain)
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/).
Load this file using "load session" not "load brain" (sessions contain
both the target and brain samples).
# Download
# [Manual](docs/manual.md)
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.
Full description of all the parameters and a bit of the thinking
behind it.
* **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)
# Binaries
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/).
* **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.dmg](https://static.thentrythis.org/samplebrain/samplebrain_0.18_m1.dmg) (thank you to [Nik Gaffney](http://fo.am) for building)
For old versions see the [changelog](changelog.md)
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
<a href='https://flathub.org/apps/details/org.thentrythis.Samplebrain'><img width='200' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/></a>
#### Ubuntu
$ sudo add-apt-repository ppa:thentrythis/samplebrain
$ sudo apt update
$ sudo apt install samplebrain
@ -57,52 +70,13 @@ If you'd like the right font, optionally:
$ sudo apt install ttf-mscorefonts-installer
# Building from source
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 dependancies for the interface:
$ sudo apt install build-essential qtcreator qt5-default
Build & run it:
$ cd app
$ qmake
$ make
$ sudo make install
$ samplebrain
# Mac build additions
To make a mac app bundle:
* Run `macdeployqt` to copy all dependancies inside the app.
* Copy desktop/samplebrain.icns (the icon) to the Resources directory in the bundle.
* Edit Info.plist to add samplebrain.icns to CFBundleIconFile.
## 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.

View File

@ -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 <QtGui>
#include <iostream>
#include <list>
@ -24,9 +27,16 @@
using namespace std;
MainWindow::MainWindow() :
m_last_file("."),
m_feedback("8890")
MainWindow::MainWindow(const string &port, const string &audio_port, const string &process_port, QSettings *settings) :
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),
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);
@ -40,14 +50,14 @@ MainWindow::MainWindow() :
setUnifiedTitleAndToolBarOnMac(true);
m_Ui.verticalLayout_5->setSpacing(0);
m_Ui.verticalLayout_5->setMargin(0);
m_Ui.verticalLayout_5->setContentsMargins(0,0,0,0);
m_Ui.brain_contents->setAlignment(Qt::AlignTop);
m_Ui.brain_contents->setSpacing(0);
m_Ui.brain_contents->setMargin(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
@ -56,8 +66,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);
@ -151,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());
@ -185,3 +201,5 @@ void MainWindow::init_from_session(const string &filename) {
}
#endif

View File

@ -18,7 +18,9 @@
#include <QDirIterator>
#include <QFileDialog>
#include <QLineEdit>
#include "generated/ui_samplebrain.h"
#include <QMessageBox>
#include "ui_samplebrain.h"
#include "SettingsDialog.h"
#include <iostream>
#include <lo/lo.h>
@ -26,17 +28,86 @@
#include <list>
#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
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 <class T>
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 <class T>
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:
@ -135,13 +206,15 @@ private slots:
void run_slot() {}
void load_target() {
m_last_file=QFileDialog::getOpenFileName(
this,
QString("Select an wav file"),
m_last_file,
QString("Sounds (*.wav)"));
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); }
@ -151,30 +224,31 @@ private slots:
void fft_spectrum_size(int) {}
void generate() { send_process_osc("/generate_brain",""); }
void load_sound() {
m_last_file=QFileDialog::getOpenFileName(
this,
QString path=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);
m_last_sound_file,
m_format_string);
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()));
m_sound_item_delete_mapper->setMapping(si.m_del, si.m_id);
}
}
void load_sounds() {
m_last_file=QFileDialog::getExistingDirectory(this,
QString("Select a directory"),
m_last_file);
QString path=QFileDialog::getExistingDirectory(this,
QString("Select a directory of wav files"),
m_last_directory_file);
QDirIterator dirIt(m_last_file,QDirIterator::Subdirectories);
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() &&
@ -191,6 +265,7 @@ private slots:
}
}
}
}
void select_all() {
for (auto &si:m_sound_items.m_sound_items) {
@ -244,12 +319,11 @@ private slots:
void record() {
if (m_save_wav=="") {
m_last_file=QFileDialog::getSaveFileName(
this,
m_last_recording_file=QFileDialog::getSaveFileName(this,
QString("Select a wav file"),
m_last_file,
QString("Sounds (*.wav)"));
m_save_wav = m_last_file.toStdString();
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) {
@ -270,47 +344,52 @@ private slots:
}
void load_brain() {
m_last_file=QFileDialog::getOpenFileName(
this,
QString path=QFileDialog::getOpenFileName(this,
QString("Select a brain file"),
m_last_file,
QString("Brains (*.brain)"));
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 path=QFileDialog::getSaveFileName(this,
QString("Select a brain file"),
m_last_file,
QString("Brains (*.brain)"));
send_process_osc("/save_brain","s",m_last_file.toStdString().c_str());
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 path=QFileDialog::getOpenFileName(this,
QString("Select a session file"),
m_last_file,
QString("Sessions *.samplebrain (*.samplebrain)"));
send_process_osc("/load_session","s",m_last_file.toStdString().c_str());
init_from_session(m_last_file.toStdString());
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 path=QFileDialog::getSaveFileName(this,
QString("Select a session file"),
m_last_file,
m_last_session_file,
QString("Sessions *.samplebrain (*.samplebrain)"));
send_process_osc("/save_session","s",m_last_file.toStdString().c_str());
if (path!="") {
m_last_session_file=path;
send_process_osc("/save_session","s",m_last_session_file.toStdString().c_str());
}
}
void update_status() {
m_feedback.poll(m_Ui.statusbar,&m_sound_items);
m_feedback.poll(m_Ui.statusbar,&m_sound_items,m_settings_dialog);
}
void stereo_mode(bool s) {
@ -325,8 +404,8 @@ private slots:
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());
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 {
@ -336,6 +415,9 @@ private slots:
}
void settings() {
m_settings_dialog->show();
}
private:
@ -362,48 +444,18 @@ private:
vector<osc_destination> 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 <class T>
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 <class T>
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 init_from_session(const string &filename);
void add_gui_address(osc_destination &dest,
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;
@ -411,5 +463,10 @@ private:
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;
};

43
app/SettingsDialog.cpp Normal file
View File

@ -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 <QtGui>
#include <iostream>
#include <list>
#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());
}

89
app/SettingsDialog.h Normal file
View File

@ -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 <QtGui>
#include <QDialog>
#include <QLineEdit>
#include <QSettings>
#include "ui_settings.h"
#include <iostream>
#include <lo/lo.h>
#include <string>
#include <list>
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

View File

@ -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,11 +50,21 @@ 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;
@ -168,7 +180,9 @@ void audio_thread::process(sample &left_in, sample &right_in, sample &left_out,
m_right_renderer->reset();
}
if (name=="/mic") {
m_mic_mode = cmd.get_int(0);
//m_mic_mode = cmd.get_int(0);
//if (m_mic_mode==1) m_block_stream->start();
//else m_block_stream->stop();
}
}

View File

@ -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;
};
}

View File

@ -16,6 +16,7 @@
#include "feedback.h"
#include "sound_items.h"
#include "SettingsDialog.h"
#include <iostream>
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)) {

View File

@ -17,6 +17,7 @@
#include <string>
#include <QtGui>
#include <QStatusBar>
#include "SettingsDialog.h"
#include "spiralcore/OSC_server.h"
#pragma once
@ -27,7 +28,8 @@ class sound_items;
class feedback {
public:
feedback(std::string address);
void poll(QStatusBar *s, sound_items *sound_items);
void poll(QStatusBar *s, sound_items *sound_items, SettingsDialog *settings);
bool ok() { return m_osc.ok(); }
private:

View File

@ -1,93 +0,0 @@
/********************************************************************************
** Form generated from reading UI file 'sampleSy5241.ui'
**
** Created: Sat Jul 11 10:24:07 2015
** by: Qt User Interface Compiler version 4.8.1
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef SAMPLESY5241_H
#define SAMPLESY5241_H
#include <QtCore/QVariant>
#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QButtonGroup>
#include <QtGui/QCheckBox>
#include <QtGui/QHBoxLayout>
#include <QtGui/QHeaderView>
#include <QtGui/QLabel>
#include <QtGui/QPushButton>
#include <QtGui/QWidget>
QT_BEGIN_NAMESPACE
class Ui_FormSample
{
public:
QHBoxLayout *horizontalLayout;
QCheckBox *checkBox;
QLabel *labelSampleName;
QLabel *labelSampleInfo;
QPushButton *pushButton;
void setupUi(QWidget *FormSample)
{
if (FormSample->objectName().isEmpty())
FormSample->setObjectName(QString::fromUtf8("FormSample"));
FormSample->resize(400, 46);
horizontalLayout = new QHBoxLayout(FormSample);
horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
checkBox = new QCheckBox(FormSample);
checkBox->setObjectName(QString::fromUtf8("checkBox"));
QFont font;
font.setFamily(QString::fromUtf8("Comic Sans MS"));
font.setBold(true);
font.setWeight(75);
checkBox->setFont(font);
horizontalLayout->addWidget(checkBox);
labelSampleName = new QLabel(FormSample);
labelSampleName->setObjectName(QString::fromUtf8("labelSampleName"));
labelSampleName->setFont(font);
horizontalLayout->addWidget(labelSampleName);
labelSampleInfo = new QLabel(FormSample);
labelSampleInfo->setObjectName(QString::fromUtf8("labelSampleInfo"));
labelSampleInfo->setFont(font);
horizontalLayout->addWidget(labelSampleInfo);
pushButton = new QPushButton(FormSample);
pushButton->setObjectName(QString::fromUtf8("pushButton"));
pushButton->setFont(font);
horizontalLayout->addWidget(pushButton);
retranslateUi(FormSample);
QMetaObject::connectSlotsByName(FormSample);
} // setupUi
void retranslateUi(QWidget *FormSample)
{
FormSample->setWindowTitle(QApplication::translate("FormSample", "Form", 0, QApplication::UnicodeUTF8));
checkBox->setText(QApplication::translate("FormSample", "active", 0, QApplication::UnicodeUTF8));
labelSampleName->setText(QApplication::translate("FormSample", "TextLabel", 0, QApplication::UnicodeUTF8));
labelSampleInfo->setText(QApplication::translate("FormSample", "TextLabel", 0, QApplication::UnicodeUTF8));
pushButton->setText(QApplication::translate("FormSample", "delete", 0, QApplication::UnicodeUTF8));
} // retranslateUi
};
namespace Ui {
class FormSample: public Ui_FormSample {};
} // namespace Ui
QT_END_NAMESPACE
#endif // SAMPLESY5241_H

File diff suppressed because it is too large Load Diff

BIN
app/images/settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@ -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)
@ -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 "<<m_target_window_type<<endl;
ifs||m_source;
ifs||m_left_target;
ifs||m_right_target;
@ -200,9 +197,6 @@ void process_thread::save_session(const std::string &filename) {
ofs||(*m_right_renderer);
ofs||m_source_block_size||m_source_overlap;
ofs||m_target_block_size||m_target_overlap;
cerr<<"saving window type session "<<m_target_window_type<<endl;
ofs||m_window_type||m_target_window_type;
ofs||m_source;
ofs||m_left_target;

View File

@ -25,9 +25,9 @@
namespace spiralcore {
class process_thread {
public:
process_thread();
class process_thread {
public:
process_thread(const string &port);
~process_thread();
pthread_mutex_t* m_brain_mutex;
@ -47,10 +47,13 @@ public:
void load_session(const std::string &filename);
void save_session(const std::string &filename);
bool ok() { return m_osc.ok(); }
// only for use in mutex
brain m_source, m_left_target, m_right_target;
private:
private:
OSC_server m_osc;
u32 m_source_block_size;
float m_source_overlap;
@ -64,6 +67,6 @@ private:
renderer *m_left_renderer;
renderer *m_right_renderer;
block_stream *m_block_stream;
};
};
}

View File

@ -19,23 +19,54 @@
#include <iostream>
#include <unistd.h>
#include <QtGui>
#include <QSettings>
#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;
QSettings settings("thentrythis", "samplebrain");
// slight over-use of OSC servers here, but the are packaged nicely for
// threadsafe (nonblocking) communication, and it's useful to expose all
// moving parts for external control (i.e. the processing and the audio)
if (!settings.contains("gui_port")) settings.setValue("gui_port", "62345");
if (!settings.contains("audio_port")) settings.setValue("audio_port", "62346");
if (!settings.contains("process_port")) settings.setValue("process_port", "62347");
string gui_port = settings.value("gui_port").toByteArray().constData();
string audio_port = settings.value("audio_port").toByteArray().constData();
string process_port = settings.value("process_port").toByteArray().constData();
status::set_port(gui_port);
MainWindow mainWin(gui_port,audio_port,process_port,&settings);
mainWin.show();
process_thread pt(process_port);
audio_thread at(audio_port,pt);
cerr<<"Qt version: "<<qVersion()<<endl;
process_thread pt;
audio_thread at(pt);
pt.register_renderer(at.m_left_renderer, at.m_right_renderer, at.m_block_stream);
mainWin.set_audio_thread(&at);
if (!at.ok()) {
mainWin.message("problem starting audio thread on port "+audio_port);
}
if (!pt.ok()) {
mainWin.message("problem starting process thread on port "+process_port);
}
if (!mainWin.ok()) {
mainWin.message("problem starting gui thread on port "+gui_port);
}
return app.exec();
}

View File

@ -1,5 +1,6 @@
<RCC>
<qresource prefix="images">
<file>images/settings.png</file>
<file>images/at.png</file>
<file>images/pause.png</file>
<file>images/play.png</file>

View File

@ -33,11 +33,10 @@ 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);
si.m_container->setMargin(0);
si.m_container->setContentsMargins(0,0,0,0);
si.m_enable = new QCheckBox();
@ -56,7 +55,7 @@ sound_items::sound_item &sound_items::add(QVBoxLayout *container, const string &
QFont font;
font.setFamily(QString::fromUtf8("Comic Sans MS"));
font.setBold(true);
font.setWeight(75);
font.setWeight(QFont::Bold);
si.m_label->setFont(font);
si.m_label->setContentsMargins(0,0,0,0);
@ -90,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);
@ -102,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);

View File

@ -36,7 +36,7 @@ double square(double a) {
return a*a;
}
void normalise(sample &in) {
void normalise(spiralcore::sample &in) {
// find min/max
float max = 0;
float min = FLT_MAX;
@ -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);
}
}
@ -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

@ -38,7 +38,7 @@ namespace spiralcore {
// returns distance based on ratio of fft-mfcc values
double compare(const block &other, const search_params &params) 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; }

View File

@ -16,6 +16,7 @@
#include <unistd.h>
#include <iostream>
#include <signal.h>
#include "block_stream.h"
using namespace spiralcore;
@ -34,15 +35,36 @@ block_stream::block_stream() :
m_block_index_offset(0),
m_sent_block_index(0)
{
}
block_stream::~block_stream() {}
void block_stream::start() {
cerr<<"block stream starting up"<<endl;
for (u32 i=0; i<NUM_WORKERS; ++i) {
m_workers.push_back(new worker(i,&m_window));
// ????
#ifndef WIN32
usleep(1);
#endif
}
}
block_stream::~block_stream() {}
void block_stream::stop() {
cerr<<"block stream ending"<<endl;
bool killcount = 0;
for (auto &w : m_workers) {
pthread_join(*w->m_thread,NULL);
}
usleep(500);
for (u32 i=0; i<NUM_WORKERS; ++i) {
delete m_workers[i];
}
m_workers.clear();
}
void block_stream::init(u32 block_size, u32 overlap, window::type t, bool ditchpcm) {
m_block_size=block_size;
@ -57,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));
@ -93,7 +116,6 @@ void block_stream::process(const sample &left, const sample &right) {
m_block_position=0;
if (m_blocks.size()>MAX_BLOCKS) {
m_blocks.erase(m_blocks.begin());
m_block_index_offset++;
@ -131,8 +153,12 @@ block_stream::worker::worker(u32 id, window *w) :
pthread_create(m_thread,NULL,(void*(*)(void*))_run_worker,this);
}
block_stream::worker::~worker() {
delete m_worker_mutex;
delete m_thread;
}
void block_stream::worker::run() {
//cerr<<"worker "<<m_id<<" started..."<<endl;
while (true) {
pthread_mutex_lock(m_worker_mutex);
if (m_status==ACTIVATE) {
@ -142,6 +168,7 @@ void block_stream::worker::run() {
m_status=FINISHED;
cerr<<"ending "<<m_id<<endl;
}
pthread_mutex_unlock(m_worker_mutex);
#ifndef WIN32
usleep(5);
@ -149,11 +176,10 @@ void block_stream::worker::run() {
}
}
void block_stream::scatter_gather(u32 block_index, const sample &region) {
while(true) {
for (auto &w : m_workers) {
if (pthread_mutex_trylock(w->m_worker_mutex)) {
if (pthread_mutex_trylock(w->m_worker_mutex)==0) {
if (w->m_status == worker::FINISHED) {
//cerr<<"adding finished block "<<w->m_block_index<<endl;
m_blocks[w->m_block_index]=*w->m_output;
@ -166,6 +192,7 @@ void block_stream::scatter_gather(u32 block_index, const sample &region) {
w->m_block_index = block_index;
return;
}
pthread_mutex_unlock(w->m_worker_mutex);
}
}

View File

@ -41,11 +41,15 @@ class block_stream : public block_source {
virtual const block &get_block(u32 index) const;
virtual u32 get_num_blocks() const { return UINT_MAX; }
void start();
void stop();
u32 last_block_index() const { return m_block_index_offset+m_blocks.size()-1; }
class worker {
public:
worker(u32 id, window *w);
~worker();
enum worker_status { READY=0, ACTIVATE, WORKING, FINISHED };

View File

@ -69,6 +69,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());
}
}
@ -226,6 +228,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;
@ -259,6 +262,7 @@ void brain::build_synapses_thresh(search_params &params, double thresh) {
++outer_index;
}
}
*/
void brain::build_synapses_fixed(search_params &params) {
//m_average_error = calc_average_diff(params)*thresh;
@ -377,34 +381,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 &params){
sample out((m_block_size-m_overlap)*m_blocks.size());
out.zero();
u32 pos = 0;
u32 count = 0;
cerr<<other.m_blocks.size()<<" brain blocks..."<<endl;
cerr<<endl;
for (vector<block>::iterator i=m_blocks.begin(); i!=m_blocks.end(); ++i) {
cerr<<'\r';
cerr<<"searching: "<<count/float(m_blocks.size())*100;
u32 index = other.search(*i, params);
//cerr<<index<<endl;
out.mul_mix(other.get_block_pcm(index),pos,0.2);
if (count%1000==0) {
audio_device::save_sample(filename,out);
}
++count;
pos += (m_block_size-m_overlap);
}
audio_device::save_sample(filename,out);
}
*/
ios &spiralcore::operator||(ios &s, brain::sound &b) {
u32 version=1;
string id("brain::sound");

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

@ -24,8 +24,9 @@ using namespace std;
static const int MAX_FFT_LENGTH = 4096;
FFT::FFT(u32 length, u32 bins) :
FFT::FFT(u32 length, u32 rate, u32 bins) :
m_length(length),
m_rate(rate),
m_num_bins(bins),
m_in(new double[length]),
m_spectrum(new fftw_complex[length]),
@ -35,26 +36,19 @@ FFT::FFT(u32 length, u32 bins) :
m_plan = fftw_plan_dft_r2c_1d(m_length, m_in, m_spectrum, FFTW_ESTIMATE);
}
FFT::~FFT()
{
FFT::~FFT() {
delete[] m_in;
fftw_destroy_plan(m_plan);
}
void FFT::impulse2freq(const float *imp)
{
void FFT::impulse2freq(const float *imp) {
unsigned int i;
for (i=0; i<m_length; i++)
{
for (i=0; i<m_length; i++) {
m_in[i] = imp[i];
}
fftw_execute(m_plan);
}
static const float SRATE = 44100;
float FFT::calculate_dominant_freq() {
double highest = 0;
u32 index = 0;
@ -65,7 +59,7 @@ float FFT::calculate_dominant_freq() {
highest=t;
}
}
float freq = index * (SRATE/(float)m_length);
float freq = index * (m_rate/(float)m_length);
if (freq<0.01) freq=0.01;
return freq;
}

View File

@ -27,7 +27,7 @@ namespace spiralcore {
class FFT
{
public:
FFT(u32 length, u32 num_bins);
FFT(u32 length, u32 rate, u32 num_bins);
~FFT();
void impulse2freq(const float *imp);
void calculate_bins();
@ -35,6 +35,7 @@ namespace spiralcore {
fftw_plan m_plan;
u32 m_length;
u32 m_rate;
u32 m_num_bins;
double *m_in;
fftw_complex *m_spectrum;

View File

@ -26,7 +26,7 @@ namespace Aquila
{
//auto spectrum = m_fft->fft(source);
Aquila::MelFilterBank bank(44100, m_inputSize);
Aquila::MelFilterBank bank(m_sampleRate, m_inputSize);
auto filterOutput = bank.applyAll(spectrum);
Aquila::Dct dct;

View File

@ -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,6 +72,7 @@ namespace Aquila
* Number of samples in each processed input.
*/
const std::size_t m_inputSize;
const unsigned int m_sampleRate;
/**
* FFT calculator.

View File

@ -31,41 +31,41 @@ extern "C" {
#endif
OSC_server::OSC_server(const string &port) :
m_port(port),
m_exit(false),
m_command_ring_buffer(262144)
{
//cerr<<"using port: ["<<port<<"]"<<endl;
m_port(port),
m_exit(false),
m_command_ring_buffer(262144) {
cerr<<"OSC using port: ["<<port<<"]"<<endl;
m_server = lo_server_thread_new(port.c_str(), error_handler);
if (m_server) {
lo_server_thread_add_method(m_server, NULL, NULL, default_handler, this);
} else {
cerr<<"error opening OSC port"<<endl;
}
}
OSC_server::~OSC_server()
{
OSC_server::~OSC_server() {
m_exit=true;
lo_server_thread_stop(m_server);
}
void OSC_server::run()
{
void OSC_server::run() {
if (!m_server) return;
lo_server_thread_start(m_server);
// while (!m_exit) usleep(1000);
// while (!m_exit) usleep(1000);
}
void OSC_server::error_handler(int num, const char *msg, const char *path)
{
void OSC_server::error_handler(int num, const char *msg, const char *path) {
//cerr<<"liblo server error "<<num<<" in path "<<path<<": "<<msg<<endl;
cerr<<"liblo server error "<<num<<endl;
}
int OSC_server::default_handler(const char *path, const char *types, lo_arg **argv,
int argc, void *data, void *user_data)
{
int argc, void *data, void *user_data) {
OSC_server *server = (OSC_server*)user_data;
if (!server) return -1;
unsigned int size = 0;
for (int i=0; i<argc; i++)
{
for (int i=0; i<argc; i++) {
size+=lo_arg_size((lo_type)types[i],argv[i]);
// add one for the null terminator
if (types[i]=='s') size++;
@ -73,14 +73,10 @@ int OSC_server::default_handler(const char *path, const char *types, lo_arg **ar
char *newdata=new char[size];
unsigned int pos=0;
for (int i=0; i<argc; i++)
{
switch (types[i])
{
case LO_INT32:
{
if (pos+4>COMMAND_DATA_SIZE)
{
for (int i=0; i<argc; i++) {
switch (types[i]) {
case LO_INT32: {
if (pos+4>COMMAND_DATA_SIZE) {
cerr<<"osc data too big for ringbuffer command"<<endl;
delete[] newdata;
return 1;
@ -90,10 +86,8 @@ int OSC_server::default_handler(const char *path, const char *types, lo_arg **ar
pos+=4;
}
break;
case LO_FLOAT:
{
if (pos+4>COMMAND_DATA_SIZE)
{
case LO_FLOAT: {
if (pos+4>COMMAND_DATA_SIZE) {
cerr<<"osc data too big for ringbuffer command"<<endl;
delete[] newdata;
return 1;
@ -103,12 +97,10 @@ int OSC_server::default_handler(const char *path, const char *types, lo_arg **ar
pos+=4;
}
break;
case LO_STRING:
{
case LO_STRING: {
int size=strlen(&argv[i]->s);
if (pos+size+1>COMMAND_DATA_SIZE)
{
if (pos+size+1>COMMAND_DATA_SIZE) {
cerr<<"osc data too big for ringbuffer command"<<endl;
delete[] newdata;
return 1;
@ -119,8 +111,7 @@ int OSC_server::default_handler(const char *path, const char *types, lo_arg **ar
pos+=size+1;
}
break;
default:
{
default: {
cerr<<"unsupported type: "<<types[i]<<endl;
delete[] newdata;
return 1;
@ -129,16 +120,14 @@ int OSC_server::default_handler(const char *path, const char *types, lo_arg **ar
}
}
if (1)//pos==size) hmm
{
if (1) { //pos==size) hmm
command_ring_buffer::command command(path,types,newdata,pos);
if (!server->m_command_ring_buffer.send(command))
{
if (!server->m_command_ring_buffer.send(command)) {
//cerr<<"OSC_server - ringbuffer full!"<<endl;
}
}
else
{
else {
cerr<<"OSC_server::default_handler: size mismatch ["<<pos<<":"<<size<<"], not sending message"<<endl;
}

View File

@ -29,6 +29,7 @@ public:
void run();
bool get(command_ring_buffer::command& command) { return m_command_ring_buffer.get(command);}
bool ok() { return m_server!=NULL; }
private:
static int default_handler(const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data);

View File

@ -22,13 +22,23 @@ using namespace std;
using namespace spiralcore;
audio_device::audio_device(const string &clientname, u32 samplerate, u32 buffer_size) :
left_out(buffer_size),
right_out(buffer_size),
left_in(buffer_size),
right_in(buffer_size),
m_recording(false),
m_record_filename("")
{
m_record_filename(""),
m_samplerate(samplerate) {
// connect to default device
m_client.init();
connect("", clientname, samplerate, buffer_size);
}
void audio_device::connect(const string &output_device_name, const string &clientname, u32 samplerate, u32 buffer_size) {
m_client.detach();
left_out.allocate(buffer_size);
right_out.allocate(buffer_size);
left_in.allocate(buffer_size);
right_in.allocate(buffer_size);
m_samplerate = samplerate;
portaudio_client::device_options opt;
opt.buffer_size = buffer_size;
opt.num_buffers = 2;
@ -38,15 +48,14 @@ audio_device::audio_device(const string &clientname, u32 samplerate, u32 buffer_
m_client.set_outputs(left_out.get_buffer(), right_out.get_buffer());
m_client.set_inputs(left_in.get_non_const_buffer(), right_in.get_non_const_buffer());
m_client.attach(clientname,opt);
m_client.attach(output_device_name,clientname,opt);
}
void audio_device::save_sample(const string &filename, const sample s) {
SF_INFO sfinfo;
sfinfo.format=SF_FORMAT_WAV | SF_FORMAT_FLOAT;
sfinfo.frames=s.get_length();
sfinfo.samplerate=44100;
sfinfo.samplerate=m_samplerate;
sfinfo.channels=1;
sfinfo.sections=1;
sfinfo.seekable=0;
@ -82,5 +91,4 @@ void audio_device::maybe_record() {
}
}
void audio_device::start_graph(graph *graph) {
}

View File

@ -24,11 +24,11 @@ class graph;
namespace spiralcore {
class audio_device {
public:
class audio_device {
public:
audio_device(const string &clientname, u32 samplerate, u32 buffer_size);
void start_graph(graph *graph);
void connect(const string &output_device, const string &clientname, u32 samplerate, u32 buffer_size);
void start_recording(std::string filename);
void stop_recording();
@ -42,15 +42,15 @@ public:
portaudio_client m_client;
static void save_sample(const std::string &filename, const sample s);
void save_sample(const std::string &filename, const sample s);
private:
private:
bool m_recording;
std::string m_record_filename;
sample m_record_buffer_left;
sample m_record_buffer_right;
u32 m_record_counter;
};
u32 m_samplerate;
};
}

View File

@ -19,7 +19,7 @@
#ifndef COMMAND_RING_BUFFER
#define COMMAND_RING_BUFFER
static const unsigned int COMMAND_DATA_SIZE = 128;
static const unsigned int COMMAND_DATA_SIZE = 1024;
class command_ring_buffer : public ring_buffer
{

View File

@ -19,15 +19,16 @@
#include "portaudio_client.h"
bool portaudio_client::m_attached = false;
long unsigned int portaudio_client::m_buffer_size = 0;
long unsigned int portaudio_client::m_sample_rate = 44100;
bool portaudio_client::m_initialised = false;
int portaudio_client::m_attached_device = -1;
void (*portaudio_client::run_callback)(void*, unsigned int buf_size)=NULL;
void *portaudio_client::run_context = NULL;
const float *portaudio_client::m_right_data=NULL;
const float *portaudio_client::m_left_data=NULL;
float *portaudio_client::m_right_in_data=NULL;
float *portaudio_client::m_left_in_data=NULL;
PaStream *portaudio_client::m_stream=NULL;
///////////////////////////////////////////////////////
@ -40,55 +41,103 @@ portaudio_client::~portaudio_client() {
detach();
}
/////////////////////////////////////////////////////////////////////////////////////////////
bool portaudio_client::attach(const string &client_name, const device_options &dopt) {
if (m_attached) return true;
bool portaudio_client::init() {
m_initialised=false;
if (!m_initialised) {
PaError err;
err = Pa_Initialize();
if( err != paNoError ) {
cerr<<"could not init portaudio_client"<<endl;
Pa_Terminate();
fprintf( stderr, "an error occured while using the portaudio stream\n" );
fprintf( stderr, "error number: %d\n", err );
fprintf( stderr, "error message: %s\n", Pa_GetErrorText( err ) );
m_status="error initialising portaudio: "+string(Pa_GetErrorText(err))+"\n";
return false;
}
PaDeviceIndex output_device_num = Pa_GetDefaultOutputDevice();
PaDeviceIndex input_device_num = Pa_GetDefaultInputDevice();
// load all the devices we have
PaDeviceIndex default_output_num = Pa_GetDefaultOutputDevice();
PaDeviceIndex default_input_num = Pa_GetDefaultInputDevice();
m_devices.clear();
// get all devices we have available
int numDevices = Pa_GetDeviceCount();
if(numDevices <= 0) {
m_status="portaudio error: no audio devices found";
return false;
}
const PaDeviceInfo *deviceInfo;
for(int i=0; i<numDevices; i++) {
deviceInfo = Pa_GetDeviceInfo(i);
if (deviceInfo->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 &requested_output_device, const string &client_name, const device_options &dopt) {
if (!init()) return false;
detach();
int requested_output_id = device_name_to_id(requested_output_device);
PaStreamParameters output_parameters;
output_parameters.device = output_device_num;
if (output_parameters.device == paNoDevice) {
cerr<<"error: no default output device."<<endl;
if (requested_output_device=="" || requested_output_id==-1) {
// start up by connecting to the default one
PaDeviceIndex default_output_num = Pa_GetDefaultOutputDevice();
if (default_output_num == paNoDevice) {
m_status="error: no default output device.";
return false;
} else {
output_parameters.device = default_output_num;
}
} else {
output_parameters.device = requested_output_id;
}
output_parameters.channelCount = 2; /* stereo output */
output_parameters.sampleFormat = paFloat32; /* 32 bit floating point output */
output_parameters.suggestedLatency = Pa_GetDeviceInfo( output_parameters.device )->defaultLowOutputLatency;
output_parameters.hostApiSpecificStreamInfo = NULL;
cerr<<"Connecting to "<<Pa_GetDeviceInfo( output_parameters.device )->name<<" for output"<<endl;
}
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_parameters;
PaStreamParameters *input_p=&input_parameters;
input_parameters.device = input_device_num;
if (input_parameters.device == paNoDevice) {
if (true || input_parameters.device == paNoDevice) {
cerr<<"error: no default input device."<<endl;
input_p=0;
} else {
input_parameters.channelCount = 2; /* stereo output */
input_parameters.sampleFormat = paFloat32; /* 32 bit floating point output */
input_parameters.channelCount = 2;
input_parameters.sampleFormat = paFloat32;
input_parameters.suggestedLatency = Pa_GetDeviceInfo( input_parameters.device )->defaultLowInputLatency;
input_parameters.hostApiSpecificStreamInfo = NULL;
cerr<<"Connecting to "<<Pa_GetDeviceInfo( input_parameters.device )->name<<" for input"<<endl;
}
} */
PaStream *stream;
err = Pa_OpenStream(&stream,
input_p,
PaError err = Pa_OpenStream(&m_stream,
NULL,
&output_parameters,
dopt.samplerate,
dopt.buffer_size,
@ -96,31 +145,38 @@ bool portaudio_client::attach(const string &client_name, const device_options &d
process,
NULL);
m_attached_device=output_parameters.device;
if(err != paNoError) {
cerr<<"could not attach portaudio_client: "<<Pa_GetErrorText( err )<<endl;
Pa_Terminate();
m_status+="could not attach: "+string(Pa_GetErrorText(err))+"\n";
detach();
return false;
}
err = Pa_StartStream(stream);
err = Pa_StartStream(m_stream);
if(err != paNoError) {
cerr<<"could not start stream: "<<Pa_GetErrorText( err )<<endl;
Pa_Terminate();
m_status+="could not start stream: "+string(Pa_GetErrorText(err))+"\n";
detach();
return false;
}
m_attached=true;
cerr<<"connected to portaudio..."<<endl;
m_status+="we are connected to portaudio!\n";
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////
void portaudio_client::detach() {
cerr<<"detaching from portaudio"<<endl;
if (m_attached_device!=-1) {
if (m_stream!=NULL) {
Pa_CloseStream(m_stream);
}
m_stream=NULL;
m_status+="detaching from portaudio\n";
Pa_Terminate();
m_attached=false;
m_attached_device=-1;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -30,9 +30,9 @@ class portaudio_client
public:
portaudio_client();
~portaudio_client();
bool init();
class device_options
{
class device_options {
public:
enum type {READ,WRITE,READWRITE};
unsigned int buffer_size;
@ -42,13 +42,25 @@ class portaudio_client
unsigned int out_channels;
};
bool attach(const string &client_name, const device_options &dopt);
class device_desc {
public:
string name;
int id;
bool default_input;
bool default_output;
};
vector<portaudio_client::device_desc> 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,9 +71,11 @@ 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;
@ -70,6 +84,8 @@ class portaudio_client
static void(*run_callback)(void *, unsigned int);
static void *run_context;
static PaStream *m_stream;
};
#endif

View File

@ -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());
}

View File

@ -24,10 +24,13 @@ namespace spiralcore {
class status {
public:
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;
};

View File

@ -0,0 +1,31 @@
# Flatpak Build Instructions
## Prerequisites
To build the Flatpak, install the `flatpak` and `flatpak-builder` packages
from your repositories. Then, [add Flathub](https://flatpak.org/setup/).
Now, install the KDE SDK:
```sh
flatpak install flathub org.kde.Sdk//5.15-22.08
```
## Building and installing
To build and install the app locally:
```sh
flatpak-builder --force-clean --install --user builddir build-aux/flatpak/org.thentrythis.Samplebrain.yaml
```
## Building and exporting
To build and export a Flatpak bundle:
```sh
flatpak-builder --force-clean --repo=repo builddir build-aux/flatpak/org.thentrythis.Samplebrain.yaml
flatpak build-bundle repo org.thentrythis.Samplebrain.flatpak org.thentrythis.Samplebrain
```
To install the bundle:
```sh
flatpak install org.thentrythis.Samplebrain.flatpak
```

View File

@ -0,0 +1,73 @@
id: org.thentrythis.Samplebrain
runtime: org.kde.Platform
runtime-version: "5.15-22.08"
sdk: org.kde.Sdk
rename-desktop-file: samplebrain.desktop
rename-icon: samplebrain
command: samplebrain
finish-args:
- "--share=ipc"
- "--socket=wayland"
- "--socket=fallback-x11"
- "--socket=pulseaudio"
- "--device=dri"
- "--share=network"
cleanup:
- "/include"
- "/lib/cmake"
- "/lib/pkgconfig"
- "/share/doc"
modules:
- name: libsndfile
buildsystem: cmake-ninja
config-opts:
- "-DCMAKE_BUILD_TYPE=Release"
- "-DBUILD_SHARED_LIBS=ON"
- "-DENABLE_EXTERNAL_LIBS=NO"
- "-DENABLE_CPACK=NO"
- "-DBUILD_PROGRAMS=OFF"
- "-DBUILD_EXAMPLES=OFF"
sources:
- type: archive
url: https://github.com/libsndfile/libsndfile/releases/download/1.1.0/libsndfile-1.1.0.tar.xz
sha256: 0f98e101c0f7c850a71225fb5feaf33b106227b3d331333ddc9bacee190bcf41
- name: portaudio
buildsystem: autotools
config-opts:
- "--with-alsa"
- "--without-jack"
- "--without-oss"
- "--without-asihpi"
sources:
- type: archive
url: http://files.portaudio.com/archives/pa_stable_v190700_20210406.tgz
sha256: 47efbf42c77c19a05d22e627d42873e991ec0c1357219c0d74ce6a2948cb2def
- name: liblo
buildsystem: autotools
config-opts:
- "--disable-tests"
- "--disable-network-tests"
- "--disable-tools"
- "--disable-examples"
sources:
- type: archive
url: https://versaweb.dl.sourceforge.net/project/liblo/liblo/0.31/liblo-0.31.tar.gz
sha256: 2b4f446e1220dcd624ecd8405248b08b7601e9a0d87a0b94730c2907dbccc750
- name: samplebrain
buildsystem: qmake
build-options:
env:
PREFIX: /app
config-opts:
- "LIBS+=-L/app/lib"
sources:
- type: dir
path: ../../

72
building.md Normal file
View File

@ -0,0 +1,72 @@
# 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
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
$ 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:
<key>CFBundleIconFile</key>
<string>samplebrain.icns</string>
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

27
changelog.md Normal file
View File

@ -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)

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>org.thentrythis.Samplebrain</id>
<name>Samplebrain</name>
<summary>A custom sample mashing app designed by Aphex Twin</summary>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0-only</project_license>
<description>
<p>
Samplebrain chops samples up into a &apos;brain&apos; 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&apos;s brain to play in realtime.
</p>
<p>
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.
</p>
</description>
<launchable type="desktop-id">samplebrain.desktop</launchable>
<categories>
<category>AudioVideo</category>
<category>Music</category>
</categories>
<content_rating type="oars-1.0"></content_rating>
<url type="homepage">https://thentrythis.org/projects/samplebrain/</url>
<url type="bugtracker">https://gitlab.com/then-try-this/samplebrain/-/issues</url>
<url type="vcs-browser">https://gitlab.com/then-try-this/samplebrain</url>
<screenshots>
<screenshot type="default">
<image type="source" width="1280" height="720">https://gitlab.com/then-try-this/samplebrain/-/raw/main/desktop/screenshots/01-main-with-project.png</image>
</screenshot>
</screenshots>
<releases>
<release version="0.18" date="2022-09-22"/>
</releases>
</component>

View File

@ -1,10 +1,8 @@
[Desktop Entry]
Encoding=UTF-8
Type=Application
Name=Samplebrain
Comment=A sample masher designed by Aphex Twin
Exec=/usr/bin/samplebrain
Icon=/usr/share/icons/hicolor/scalable/apps/samplebrain.svg
Terminal=false
Categories=GNOME;Application;
Comment=A custom sample mashing app designed by Aphex Twin
Icon=samplebrain
Exec=samplebrain
Categories=AudioVideo;Music
StartupNotify=true

View File

@ -1,7 +0,0 @@
[Desktop Entry]
Type=Application
Version=1.0
Name=Samplebrain
GenericName="Samplebrain"
Exec=samplebrain
Icon=samplebrain

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

View File

@ -1,77 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FormSample</class>
<widget class="QWidget" name="FormSample">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>46</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="checkBox">
<property name="font">
<font>
<family>Comic Sans MS</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>active</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelSampleName">
<property name="font">
<font>
<family>Comic Sans MS</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelSampleInfo">
<property name="font">
<font>
<family>Comic Sans MS</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="font">
<font>
<family>Comic Sans MS</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>delete</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
<string>samplebrain 0.18</string>
<string>samplebrain 0.18.5</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_4">
@ -96,6 +96,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>plain fft match vs mfcc values </string>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
@ -156,6 +159,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>match original or normalised blocks</string>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
@ -298,6 +304,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>use new blocks rather than similar ones</string>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
@ -361,6 +370,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>how long it takes for the novelty to wear off</string>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
@ -403,7 +415,7 @@
</sizepolicy>
</property>
<property name="toolTip">
<string>how long it takes for the novelty to wear off</string>
<string>likelihood of playing the next block rather than the closest</string>
</property>
<property name="maximum">
<number>100</number>
@ -424,6 +436,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>likelihood of playing the next block rather than the closest</string>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
@ -491,7 +506,7 @@
</sizepolicy>
</property>
<property name="toolTip">
<string>how many connections to search (ordered in closeness)</string>
<string>repeat search (set novelty to 0 for dodgy jungle timessssstretch)</string>
</property>
<property name="minimum">
<number>1</number>
@ -666,7 +681,7 @@
</sizepolicy>
</property>
<property name="toolTip">
<string>how many connections to search (ordered in closeness)</string>
<string>block difference higher than this causes a new search, skipping the target</string>
</property>
<property name="maximum">
<number>10000</number>
@ -713,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">
@ -752,7 +774,7 @@
<number>99999</number>
</property>
<property name="value">
<number>3000</number>
<number>1000</number>
</property>
</widget>
</item>
@ -783,7 +805,7 @@
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.800000000000000</double>
<double>0.750000000000000</double>
</property>
</widget>
</item>
@ -867,14 +889,22 @@
</item>
<item>
<widget class="QCheckBox" name="mic">
<property name="enabled">
<bool>false</bool>
</property>
<property name="font">
<font>
<family>Comic Sans MS</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>use mic input</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
@ -936,6 +966,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>amount to match the frequency</string>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
@ -993,6 +1026,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>mix in the normalised blocks</string>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
@ -1053,6 +1089,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>mix in the original blocks</string>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
@ -1080,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>
@ -1228,7 +1267,7 @@
<number>99999</number>
</property>
<property name="value">
<number>3000</number>
<number>1000</number>
</property>
</widget>
</item>
@ -1259,7 +1298,7 @@
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
<double>0.750000000000000</double>
</property>
</widget>
</item>
@ -1554,6 +1593,33 @@
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="font">
<font>
<family>Comic Sans MS</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../app/samplebrain.qrc">
<normaloff>:/images/images/settings.png</normaloff>:/images/images/settings.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
@ -1585,8 +1651,6 @@
</widget>
<resources>
<include location="../app/samplebrain.qrc"/>
<include location="../app/samplebrain.qrc"/>
<include location="../app/samplebrain.qrc"/>
</resources>
<connections>
<connection>
@ -1596,8 +1660,8 @@
<slot>play_slot()</slot>
<hints>
<hint type="sourcelabel">
<x>64</x>
<y>62</y>
<x>78</x>
<y>830</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
@ -1612,8 +1676,8 @@
<slot>stop_slot()</slot>
<hints>
<hint type="sourcelabel">
<x>155</x>
<y>62</y>
<x>180</x>
<y>830</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
@ -1628,8 +1692,8 @@
<slot>volume_slot(int)</slot>
<hints>
<hint type="sourcelabel">
<x>189</x>
<y>480</y>
<x>465</x>
<y>852</y>
</hint>
<hint type="destinationlabel">
<x>361</x>
@ -1644,8 +1708,8 @@
<slot>stop_record()</slot>
<hints>
<hint type="sourcelabel">
<x>328</x>
<y>543</y>
<x>356</x>
<y>840</y>
</hint>
<hint type="destinationlabel">
<x>361</x>
@ -1660,8 +1724,8 @@
<slot>record()</slot>
<hints>
<hint type="sourcelabel">
<x>236</x>
<y>543</y>
<x>268</x>
<y>840</y>
</hint>
<hint type="destinationlabel">
<x>361</x>
@ -1676,8 +1740,8 @@
<slot>fft1_end_slot(int)</slot>
<hints>
<hint type="sourcelabel">
<x>154</x>
<y>310</y>
<x>421</x>
<y>293</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
@ -1692,8 +1756,8 @@
<slot>ratio_slot(double)</slot>
<hints>
<hint type="sourcelabel">
<x>109</x>
<y>223</y>
<x>421</x>
<y>185</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
@ -1708,12 +1772,12 @@
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>237</x>
<y>289</y>
<x>319</x>
<y>603</y>
</hint>
<hint type="destinationlabel">
<x>341</x>
<y>289</y>
<x>421</x>
<y>614</y>
</hint>
</hints>
</connection>
@ -1724,8 +1788,8 @@
<slot>target_mix_slot(double)</slot>
<hints>
<hint type="sourcelabel">
<x>330</x>
<y>446</y>
<x>720</x>
<y>669</y>
</hint>
<hint type="destinationlabel">
<x>361</x>
@ -1740,8 +1804,8 @@
<slot>generate_target_blocks()</slot>
<hints>
<hint type="sourcelabel">
<x>277</x>
<y>202</y>
<x>670</x>
<y>395</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
@ -1756,8 +1820,8 @@
<slot>target_block_size(int)</slot>
<hints>
<hint type="sourcelabel">
<x>313</x>
<y>136</y>
<x>720</x>
<y>237</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
@ -1772,8 +1836,8 @@
<slot>load_target()</slot>
<hints>
<hint type="sourcelabel">
<x>277</x>
<y>103</y>
<x>670</x>
<y>184</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
@ -1788,8 +1852,8 @@
<slot>fft1_start_slot(int)</slot>
<hints>
<hint type="sourcelabel">
<x>154</x>
<y>277</y>
<x>298</x>
<y>293</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
@ -1804,8 +1868,8 @@
<slot>target_block_overlap(double)</slot>
<hints>
<hint type="sourcelabel">
<x>619</x>
<y>196</y>
<x>720</x>
<y>291</y>
</hint>
<hint type="destinationlabel">
<x>361</x>
@ -1820,12 +1884,12 @@
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>341</x>
<y>289</y>
<x>421</x>
<y>614</y>
</hint>
<hint type="destinationlabel">
<x>237</x>
<y>289</y>
<x>319</x>
<y>603</y>
</hint>
</hints>
</connection>
@ -1836,8 +1900,8 @@
<slot>synapses(int)</slot>
<hints>
<hint type="sourcelabel">
<x>341</x>
<y>289</y>
<x>421</x>
<y>614</y>
</hint>
<hint type="destinationlabel">
<x>566</x>
@ -1852,8 +1916,8 @@
<slot>ratio_slot(int)</slot>
<hints>
<hint type="sourcelabel">
<x>189</x>
<y>141</y>
<x>323</x>
<y>174</y>
</hint>
<hint type="destinationlabel">
<x>361</x>
@ -1868,8 +1932,8 @@
<slot>n_ratio_slot(int)</slot>
<hints>
<hint type="sourcelabel">
<x>159</x>
<y>211</y>
<x>323</x>
<y>228</y>
</hint>
<hint type="destinationlabel">
<x>361</x>
@ -1884,8 +1948,8 @@
<slot>n_ratio_slot(double)</slot>
<hints>
<hint type="sourcelabel">
<x>330</x>
<y>211</y>
<x>421</x>
<y>239</y>
</hint>
<hint type="destinationlabel">
<x>361</x>
@ -1900,8 +1964,8 @@
<slot>target_mix_slot(int)</slot>
<hints>
<hint type="sourcelabel">
<x>159</x>
<y>446</y>
<x>622</x>
<y>658</y>
</hint>
<hint type="destinationlabel">
<x>361</x>
@ -1916,8 +1980,8 @@
<slot>n_mix_slot(int)</slot>
<hints>
<hint type="sourcelabel">
<x>159</x>
<y>386</y>
<x>622</x>
<y>604</y>
</hint>
<hint type="destinationlabel">
<x>361</x>
@ -1932,8 +1996,8 @@
<slot>n_mix_slot(double)</slot>
<hints>
<hint type="sourcelabel">
<x>330</x>
<y>386</y>
<x>720</x>
<y>615</y>
</hint>
<hint type="destinationlabel">
<x>361</x>
@ -1948,8 +2012,8 @@
<slot>load_brain()</slot>
<hints>
<hint type="sourcelabel">
<x>615</x>
<y>357</y>
<x>941</x>
<y>661</y>
</hint>
<hint type="destinationlabel">
<x>566</x>
@ -1964,8 +2028,8 @@
<slot>boredom_slot(double)</slot>
<hints>
<hint type="sourcelabel">
<x>411</x>
<y>253</y>
<x>421</x>
<y>401</y>
</hint>
<hint type="destinationlabel">
<x>321</x>
@ -1980,8 +2044,8 @@
<slot>novelty_slot(double)</slot>
<hints>
<hint type="sourcelabel">
<x>411</x>
<y>217</y>
<x>421</x>
<y>347</y>
</hint>
<hint type="destinationlabel">
<x>321</x>
@ -1996,8 +2060,8 @@
<slot>novelty_slot(int)</slot>
<hints>
<hint type="sourcelabel">
<x>291</x>
<y>217</y>
<x>323</x>
<y>336</y>
</hint>
<hint type="destinationlabel">
<x>321</x>
@ -2012,8 +2076,8 @@
<slot>boredom_slot(int)</slot>
<hints>
<hint type="sourcelabel">
<x>291</x>
<y>253</y>
<x>323</x>
<y>390</y>
</hint>
<hint type="destinationlabel">
<x>321</x>
@ -2028,8 +2092,8 @@
<slot>synapses(int)</slot>
<hints>
<hint type="sourcelabel">
<x>237</x>
<y>289</y>
<x>319</x>
<y>603</y>
</hint>
<hint type="destinationlabel">
<x>566</x>
@ -2044,8 +2108,8 @@
<slot>block_size(int)</slot>
<hints>
<hint type="sourcelabel">
<x>728</x>
<y>109</y>
<x>1157</x>
<y>450</y>
</hint>
<hint type="destinationlabel">
<x>566</x>
@ -2060,8 +2124,8 @@
<slot>block_overlap(double)</slot>
<hints>
<hint type="sourcelabel">
<x>728</x>
<y>145</y>
<x>1157</x>
<y>504</y>
</hint>
<hint type="destinationlabel">
<x>566</x>
@ -2076,8 +2140,8 @@
<slot>save_brain()</slot>
<hints>
<hint type="sourcelabel">
<x>728</x>
<y>357</y>
<x>1157</x>
<y>661</y>
</hint>
<hint type="destinationlabel">
<x>566</x>
@ -2092,8 +2156,8 @@
<slot>clear_brain()</slot>
<hints>
<hint type="sourcelabel">
<x>1061</x>
<y>550</y>
<x>1157</x>
<y>396</y>
</hint>
<hint type="destinationlabel">
<x>566</x>
@ -2108,8 +2172,8 @@
<slot>generate()</slot>
<hints>
<hint type="sourcelabel">
<x>671</x>
<y>322</y>
<x>1158</x>
<y>608</y>
</hint>
<hint type="destinationlabel">
<x>566</x>
@ -2124,8 +2188,8 @@
<slot>load_sound()</slot>
<hints>
<hint type="sourcelabel">
<x>841</x>
<y>550</y>
<x>868</x>
<y>396</y>
</hint>
<hint type="destinationlabel">
<x>566</x>
@ -2140,12 +2204,12 @@
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>241</x>
<y>282</y>
<x>346</x>
<y>498</y>
</hint>
<hint type="destinationlabel">
<x>343</x>
<y>282</y>
<x>421</x>
<y>509</y>
</hint>
</hints>
</connection>
@ -2156,12 +2220,12 @@
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>343</x>
<y>282</y>
<x>421</x>
<y>509</y>
</hint>
<hint type="destinationlabel">
<x>241</x>
<y>282</y>
<x>346</x>
<y>498</y>
</hint>
</hints>
</connection>
@ -2172,8 +2236,8 @@
<slot>search_stretch(int)</slot>
<hints>
<hint type="sourcelabel">
<x>241</x>
<y>282</y>
<x>346</x>
<y>498</y>
</hint>
<hint type="destinationlabel">
<x>566</x>
@ -2188,12 +2252,12 @@
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>231</x>
<y>410</y>
<x>305</x>
<y>657</y>
</hint>
<hint type="destinationlabel">
<x>337</x>
<y>410</y>
<x>421</x>
<y>668</y>
</hint>
</hints>
</connection>
@ -2204,12 +2268,12 @@
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>337</x>
<y>410</y>
<x>421</x>
<y>668</y>
</hint>
<hint type="destinationlabel">
<x>231</x>
<y>410</y>
<x>305</x>
<y>657</y>
</hint>
</hints>
</connection>
@ -2220,8 +2284,8 @@
<slot>slide_error(int)</slot>
<hints>
<hint type="sourcelabel">
<x>231</x>
<y>410</y>
<x>305</x>
<y>657</y>
</hint>
<hint type="destinationlabel">
<x>566</x>
@ -2236,8 +2300,8 @@
<slot>stickyness_slot(double)</slot>
<hints>
<hint type="sourcelabel">
<x>385</x>
<y>296</y>
<x>421</x>
<y>455</y>
</hint>
<hint type="destinationlabel">
<x>609</x>
@ -2252,8 +2316,8 @@
<slot>stickyness_slot(int)</slot>
<hints>
<hint type="sourcelabel">
<x>266</x>
<y>296</y>
<x>323</x>
<y>444</y>
</hint>
<hint type="destinationlabel">
<x>609</x>
@ -2268,8 +2332,8 @@
<slot>autotune(int)</slot>
<hints>
<hint type="sourcelabel">
<x>531</x>
<y>477</y>
<x>622</x>
<y>550</y>
</hint>
<hint type="destinationlabel">
<x>454</x>
@ -2284,8 +2348,8 @@
<slot>autotune(double)</slot>
<hints>
<hint type="sourcelabel">
<x>616</x>
<y>477</y>
<x>720</x>
<y>561</y>
</hint>
<hint type="destinationlabel">
<x>454</x>
@ -2300,8 +2364,8 @@
<slot>load_session()</slot>
<hints>
<hint type="sourcelabel">
<x>535</x>
<y>707</y>
<x>616</x>
<y>799</y>
</hint>
<hint type="destinationlabel">
<x>454</x>
@ -2316,8 +2380,8 @@
<slot>save_session()</slot>
<hints>
<hint type="sourcelabel">
<x>535</x>
<y>744</y>
<x>616</x>
<y>851</y>
</hint>
<hint type="destinationlabel">
<x>454</x>
@ -2332,8 +2396,8 @@
<slot>stereo_mode(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>539</x>
<y>594</y>
<x>721</x>
<y>717</y>
</hint>
<hint type="destinationlabel">
<x>454</x>
@ -2348,8 +2412,8 @@
<slot>algo(int)</slot>
<hints>
<hint type="sourcelabel">
<x>273</x>
<y>395</y>
<x>421</x>
<y>560</y>
</hint>
<hint type="destinationlabel">
<x>454</x>
@ -2364,8 +2428,8 @@
<slot>target_shape(int)</slot>
<hints>
<hint type="sourcelabel">
<x>557</x>
<y>233</y>
<x>720</x>
<y>342</y>
</hint>
<hint type="destinationlabel">
<x>454</x>
@ -2380,8 +2444,8 @@
<slot>brain_shape(int)</slot>
<hints>
<hint type="sourcelabel">
<x>824</x>
<y>432</y>
<x>1157</x>
<y>555</y>
</hint>
<hint type="destinationlabel">
<x>454</x>
@ -2396,8 +2460,8 @@
<slot>load_sounds()</slot>
<hints>
<hint type="sourcelabel">
<x>754</x>
<y>321</y>
<x>1013</x>
<y>396</y>
</hint>
<hint type="destinationlabel">
<x>454</x>
@ -2412,8 +2476,8 @@
<slot>select_all()</slot>
<hints>
<hint type="sourcelabel">
<x>813</x>
<y>74</y>
<x>1073</x>
<y>124</y>
</hint>
<hint type="destinationlabel">
<x>454</x>
@ -2428,8 +2492,8 @@
<slot>select_none()</slot>
<hints>
<hint type="sourcelabel">
<x>862</x>
<y>74</y>
<x>1157</x>
<y>124</y>
</hint>
<hint type="destinationlabel">
<x>454</x>
@ -2444,8 +2508,8 @@
<slot>mic(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>404</x>
<y>601</y>
<x>721</x>
<y>442</y>
</hint>
<hint type="destinationlabel">
<x>454</x>
@ -2453,6 +2517,22 @@
</hint>
</hints>
</connection>
<connection>
<sender>pushButton</sender>
<signal>clicked()</signal>
<receiver>MainWindow</receiver>
<slot>settings()</slot>
<hints>
<hint type="sourcelabel">
<x>680</x>
<y>809</y>
</hint>
<hint type="destinationlabel">
<x>776</x>
<y>849</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>play_slot()</slot>
@ -2510,5 +2590,6 @@
<slot>select_all()</slot>
<slot>select_none()</slot>
<slot>mic(bool)</slot>
<slot>settings()</slot>
</slots>
</ui>

490
gui/settings.ui Normal file
View File

@ -0,0 +1,490 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsDialog</class>
<widget class="QDialog" name="SettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>438</width>
<height>656</height>
</rect>
</property>
<property name="windowTitle">
<string>settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
<property name="text">
<string>device</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="deviceComboBox">
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
<property name="text">
<string>sample rate</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="samplerateLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
<property name="text">
<string>44100</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<family>Comic Sans MS</family>
<pointsize>8</pointsize>
</font>
</property>
<property name="text">
<string>note: this currently should probably match your sample file's input rate as no conversion is run on them - yet</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
<property name="text">
<string>buffer size</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="buffersizeComboBox">
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
<property name="currentIndex">
<number>5</number>
</property>
<item>
<property name="text">
<string>64</string>
</property>
</item>
<item>
<property name="text">
<string>128</string>
</property>
</item>
<item>
<property name="text">
<string>256</string>
</property>
</item>
<item>
<property name="text">
<string>512</string>
</property>
</item>
<item>
<property name="text">
<string>1024</string>
</property>
</item>
<item>
<property name="text">
<string>2048</string>
</property>
</item>
<item>
<property name="text">
<string>4096</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
<property name="text">
<string>messages</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="messagesLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Comic Sans MS</family>
<pointsize>8</pointsize>
</font>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">border: 1px solid;</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_9">
<property name="font">
<font>
<family>Comic Sans MS</family>
<pointsize>8</pointsize>
</font>
</property>
<property name="text">
<string>restart needed for the ports below to update</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_6">
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
<property name="text">
<string>gui osc port</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="guiOSCLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_7">
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
<property name="text">
<string>process osc port</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="processOSCLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_8">
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
<property name="text">
<string>audio osc port</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="audioOSCLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="applyPushButton">
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
<property name="text">
<string>apply settings</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="font">
<font>
<family>Comic Sans MS</family>
</font>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>SettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>244</x>
<y>642</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>SettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>312</x>
<y>642</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>samplerateLineEdit</sender>
<signal>textEdited(QString)</signal>
<receiver>SettingsDialog</receiver>
<slot>samplerate(QString)</slot>
<hints>
<hint type="sourcelabel">
<x>270</x>
<y>71</y>
</hint>
<hint type="destinationlabel">
<x>394</x>
<y>156</y>
</hint>
</hints>
</connection>
<connection>
<sender>applyPushButton</sender>
<signal>clicked()</signal>
<receiver>SettingsDialog</receiver>
<slot>apply()</slot>
<hints>
<hint type="sourcelabel">
<x>125</x>
<y>592</y>
</hint>
<hint type="destinationlabel">
<x>4</x>
<y>315</y>
</hint>
</hints>
</connection>
<connection>
<sender>deviceComboBox</sender>
<signal>currentTextChanged(QString)</signal>
<receiver>SettingsDialog</receiver>
<slot>output_device(QString)</slot>
<hints>
<hint type="sourcelabel">
<x>323</x>
<y>45</y>
</hint>
<hint type="destinationlabel">
<x>393</x>
<y>64</y>
</hint>
</hints>
</connection>
<connection>
<sender>buffersizeComboBox</sender>
<signal>currentTextChanged(QString)</signal>
<receiver>SettingsDialog</receiver>
<slot>buffersize(QString)</slot>
<hints>
<hint type="sourcelabel">
<x>390</x>
<y>215</y>
</hint>
<hint type="destinationlabel">
<x>395</x>
<y>208</y>
</hint>
</hints>
</connection>
<connection>
<sender>guiOSCLineEdit</sender>
<signal>textChanged(QString)</signal>
<receiver>SettingsDialog</receiver>
<slot>gui_port(QString)</slot>
<hints>
<hint type="sourcelabel">
<x>346</x>
<y>432</y>
</hint>
<hint type="destinationlabel">
<x>401</x>
<y>419</y>
</hint>
</hints>
</connection>
<connection>
<sender>processOSCLineEdit</sender>
<signal>textChanged(QString)</signal>
<receiver>SettingsDialog</receiver>
<slot>process_port(QString)</slot>
<hints>
<hint type="sourcelabel">
<x>356</x>
<y>482</y>
</hint>
<hint type="destinationlabel">
<x>398</x>
<y>479</y>
</hint>
</hints>
</connection>
<connection>
<sender>audioOSCLineEdit</sender>
<signal>textChanged(QString)</signal>
<receiver>SettingsDialog</receiver>
<slot>audio_port(QString)</slot>
<hints>
<hint type="sourcelabel">
<x>374</x>
<y>511</y>
</hint>
<hint type="destinationlabel">
<x>398</x>
<y>517</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>output_device(QString)</slot>
<slot>samplerate(QString)</slot>
<slot>buffersize(QString)</slot>
<slot>apply()</slot>
<slot>gui_port(QString)</slot>
<slot>audio_port(QString)</slot>
<slot>process_port(QString)</slot>
</slots>
</ui>

View File

@ -11,9 +11,13 @@ QT += core gui widgets
# Input
HEADERS += app/MainWindow.h \
app/generated/ui_samplebrain.h \
app/SettingsDialog.h
FORMS += gui/samplebrain.ui \
gui/settings.ui
SOURCES += app/MainWindow.cpp \
app/SettingsDialog.cpp \
app/sound_items.cpp \
app/audio_thread.cpp \
app/process_thread.cpp \
@ -42,19 +46,26 @@ SOURCES += app/MainWindow.cpp \
INCLUDEPATH += brain/src
INCLUDEPATH += /usr/local/include
LIBS += -L.. -L/usr/local/lib -lportaudio -lfftw3 -lsndfile -llo -ldl -lpthread -lm
INCLUDEPATH += /opt/homebrew/include
LIBS += -L.. -L/usr/local/lib -L/opt/homebrew/lib -lportaudio -lfftw3 -lsndfile -llo -ldl -lpthread -lm
QMAKE_CXXFLAGS += -O3 -Wall -Wno-unused -std=c++11
# assets
RESOURCES = app/samplebrain.qrc
ICON = desktop/samplebrain.icns
unix:desktopfile.path = /usr/share/applications/
PREFIX = $$(PREFIX)
isEmpty(PREFIX) {
PREFIX = /usr
}
unix:desktopfile.path = $$PREFIX/share/applications/
unix:desktopfile.files = desktop/samplebrain.desktop
unix:iconfile.path = /usr/share/icons/hicolor/scalable/apps
unix:iconfile.path = $$PREFIX/share/icons/hicolor/scalable/apps
unix:iconfile.files = desktop/samplebrain.svg
unix:metainfofile.path = $$PREFIX/share/metainfo
unix:metainfofile.files = desktop/org.thentrythis.Samplebrain.metainfo.xml
target.path = /usr/bin
INSTALLS += target desktopfile iconfile
target.path = $$PREFIX/bin
INSTALLS += target desktopfile iconfile metainfofile