From e8af619d2231899abddc34319e03d9242e534bd2 Mon Sep 17 00:00:00 2001 From: dave griffiths Date: Fri, 16 Oct 2015 16:23:51 +0100 Subject: [PATCH] session saving --- samplebrain/interface/samplebrain.ui | Bin 78564 -> 80545 bytes samplebrain/qt/MainWindow.cpp | 115 ++++++++++++++++++++++ samplebrain/qt/MainWindow.h | 26 ++++- samplebrain/qt/generated/ui_samplebrain.h | 97 +++++++++++------- samplebrain/qt/process_thread.cpp | 38 +++++-- samplebrain/qt/process_thread.h | 5 +- samplebrain/qt/samplebrain.pro | 1 + samplebrain/src/brain.cpp | 7 ++ samplebrain/src/brain.h | 5 + samplebrain/src/renderer.cpp | 12 +++ samplebrain/src/renderer.h | 13 +++ samplebrain/src/search_params.h | 8 ++ 12 files changed, 281 insertions(+), 46 deletions(-) diff --git a/samplebrain/interface/samplebrain.ui b/samplebrain/interface/samplebrain.ui index 0f6807d97e5e1a10de115b89452e3768e9c1a781..ea36146218ef9bf654eb33cda8f347313abaa2da 100644 GIT binary patch literal 80545 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

  • P*mUfx#S7W(6@ZwaVjJy&>6L-~!w)OckzXV&r z!ryZ_l{xRLb1lL>KK_=F6*}x|*{>~RsW+YX-|rH#7m(AV3ZbJx))jhZlV;j3X$8)@ zYLid;_TY5TOxx?4-4cp36(Uzg$P$d*uJ1!rjG2JtSJA7c7*p9{vlua{>cPoIRJWjJ z_mf@3^c~Wml5;Y4r?Lf>Vk#vZF8c))H#?|q6V-VT};t540dd9{_8Y=VY- zUwKeP#;P=^A_GMlQRg+=e zSQFUsM=$~*A8P{1VrT;^!PH8{|`wb#!O2d16T>InLk8zeIgsdAy5p{Jt-bj@vt>@BK&DstxAQ}$;Q+aL}&(7JV`~$G@F1X{A#mu zg!`45JHnW%5<1NgeO@seNIAa}|7Z8~xdHrMrMraRuk*R`A%!W-_C&Y&*pRINd0{Q+ z<|uAf{J2UT7J#6tbWDmPJmYM)8Ac Ogqh$0+|e$WefR%Heyaxn literal 78564 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
    *UdFMj{}v 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 diff --git a/samplebrain/qt/MainWindow.cpp b/samplebrain/qt/MainWindow.cpp index 07736aa..34de1a6 100644 --- a/samplebrain/qt/MainWindow.cpp +++ b/samplebrain/qt/MainWindow.cpp @@ -16,9 +16,11 @@ #include #include +#include #include "MainWindow.h" #include "feedback.h" +#include "renderer.h" using namespace std; @@ -40,3 +42,116 @@ MainWindow::MainWindow() : m_save_wav=""; m_record_id=0; } + +void MainWindow::init_from_session(const string &filename) { + // pull the bits out of the file to set the interface... + // is this easier than direct access? no idea?? + ifstream ifs(filename.c_str(),ios::binary); + + brain s,t; + + renderer r(s,t); + ifs||r; + + u32 target_windowsize; + u32 source_windowsize; + window::type target_window; + window::type source_window; + float target_overlap; + float source_overlap; + int t_int; + // skip this... + ifs||target_windowsize||target_overlap; + ifs||source_windowsize||source_overlap; + ifs||target_window||source_window; + // todo: probably don't need to load all the sample data too :/ + ifs||s; + ifs||t; + + // brain tweaks + search_params * p = r.get_params(); + + m_Ui.sliderRatio->setValue(p->m_ratio*100); + m_Ui.doubleSpinBoxRatio->setValue(p->m_ratio); + m_Ui.sliderNRatio->setValue(p->m_n_ratio*100); + m_Ui.doubleSpinBoxNRatio->setValue(p->m_n_ratio); + m_Ui.spinBoxFFT1Start->setValue(p->m_fft1_start); + m_Ui.spinBoxFFT1End->setValue(p->m_fft1_end); + m_Ui.sliderNovelty->setValue(p->m_usage_importance*100); + m_Ui.doubleSpinBoxNovelty->setValue(p->m_usage_importance); + m_Ui.sliderBoredom->setValue(s.get_usage_falloff()*100); + m_Ui.doubleSpinBoxBoredom->setValue(s.get_usage_falloff()); + m_Ui.sliderStickyness->setValue(p->m_stickyness*100); + m_Ui.doubleSpinBoxStickyness->setValue(p->m_stickyness); + m_Ui.sliderSearchStretch->setValue(r.get_stretch()); + m_Ui.spinBoxSearchStretch->setValue(r.get_stretch()); + + switch(r.get_search_algo()) { + case renderer::BASIC: m_Ui.radioButtonAlgoBasic->setChecked(true); break; + case renderer::REV_BASIC: m_Ui.radioButtonAlgoRevBasic->setChecked(true); break; + case renderer::SYNAPTIC: m_Ui.radioButtonSynaptic->setChecked(true); break; + case renderer::SYNAPTIC_SLIDE: m_Ui.radioButtonSynapticSlide->setChecked(true); break; + }; + + m_Ui.sliderSynapses->setValue(p->m_num_synapses); + m_Ui.spinBoxSynapses->setValue(p->m_num_synapses); + m_Ui.sliderSlideError->setValue(r.get_slide_error()); + m_Ui.spinBoxSlideError->setValue(r.get_slide_error()); + + // target + m_Ui.spinBoxBlockSizeTarget->setValue(t.get_block_size()); + m_Ui.doubleSpinBoxBlockOverlapTarget->setValue(t.get_overlap()/(float)t.get_block_size()); + + m_Ui.radioButton_dodgyTarget->setChecked(false); + m_Ui.radioButton_bartlettTarget->setChecked(false); + m_Ui.radioButton_blackmanTarget->setChecked(false); + m_Ui.radioButton_flattopTarget->setChecked(false); + m_Ui.radioButton_gaussianTarget->setChecked(false); + m_Ui.radioButton_hammingTarget->setChecked(false); + m_Ui.radioButton_hannTarget->setChecked(false); + m_Ui.radioButton_rectangleTarget->setChecked(false); + + switch(target_window) { + case window::DODGY: m_Ui.radioButton_dodgyTarget->setChecked(true); break; + case window::BARTLETT: m_Ui.radioButton_bartlettTarget->setChecked(true); break; + case window::BLACKMAN: m_Ui.radioButton_blackmanTarget->setChecked(true); break; + case window::FLAT_TOP: m_Ui.radioButton_flattopTarget->setChecked(true); break; + case window::GAUSSIAN: m_Ui.radioButton_gaussianTarget->setChecked(true); break; + case window::HAMMING: m_Ui.radioButton_hammingTarget->setChecked(true); break; + case window::HANN: m_Ui.radioButton_hannTarget->setChecked(true); break; + case window::RECTANGLE: m_Ui.radioButton_rectangleTarget->setChecked(true); break; + }; + + // source + m_Ui.spinBoxBlockSize->setValue(s.get_block_size()); + m_Ui.doubleSpinBoxBlockOverlap->setValue(s.get_overlap()/(float)s.get_block_size()); + switch(source_window) { + case window::DODGY: m_Ui.radioButton_dodgy->setChecked(true); break; + case window::BARTLETT: m_Ui.radioButton_bartlett->setChecked(true); break; + case window::BLACKMAN: m_Ui.radioButton_blackman->setChecked(true); break; + case window::FLAT_TOP: m_Ui.radioButton_flattop->setChecked(true); break; + case window::GAUSSIAN: m_Ui.radioButton_gaussian->setChecked(true); break; + case window::HAMMING: m_Ui.radioButton_hamming->setChecked(true); break; + case window::HANN: m_Ui.radioButton_hann->setChecked(true); break; + case window::RECTANGLE: m_Ui.radioButton_rectagle->setChecked(true); break; + }; + + // brain samples + m_Ui.listWidgetSounds->clear(); + const std::list samples = s.get_samples(); + for (std::list::const_iterator i=samples.begin(); + i!=samples.end(); ++i) { + m_Ui.listWidgetSounds->addItem(QString::fromStdString(i->m_filename)); + } + + // mix + m_Ui.sliderTargetMix->setValue(r.get_target_mix()*100); + m_Ui.doubleSpinBoxTargetMix->setValue(r.get_target_mix()); + m_Ui.sliderNMix->setValue(r.get_n_mix()*100); + m_Ui.doubleSpinBoxNMix->setValue(r.get_n_mix()); + m_Ui.sliderAutotune->setValue(r.get_autotune()*100); + m_Ui.doubleSpinBoxAutotune->setValue(r.get_autotune()); + + + +} diff --git a/samplebrain/qt/MainWindow.h b/samplebrain/qt/MainWindow.h index 5080f3b..390e2e7 100644 --- a/samplebrain/qt/MainWindow.h +++ b/samplebrain/qt/MainWindow.h @@ -69,7 +69,6 @@ private slots: m_Ui.sliderAutotune->setValue(s*100); } - void fft1_start_slot(int s) { lo_send(m_audio_address,"/fft1_start","i",s); } void fft1_end_slot(int s) { lo_send(m_audio_address,"/fft1_end","i",s); } void fft2_start_slot(int s){} // { m_renderer->get_params()->m_fft2_start=s; } @@ -200,7 +199,7 @@ private slots: if (m_save_wav=="") { m_last_file=QFileDialog::getSaveFileName( this, - QString("Select an wav file"), + QString("Select a wav file"), m_last_file, QString("Sounds (*.wav)")); m_save_wav = m_last_file.toStdString(); @@ -242,12 +241,35 @@ private slots: lo_send(m_process_address,"/save_brain","s",m_last_file.toStdString().c_str()); } + void load_session() { + m_last_file=QFileDialog::getOpenFileName( + this, + QString("Select a session file"), + m_last_file, + QString("Sessions (*.samplebrain)")); + + lo_send(m_process_address,"/load_session","s",m_last_file.toStdString().c_str()); + init_from_session(m_last_file.toStdString()); + } + + void save_session() { + m_last_file=QFileDialog::getSaveFileName( + this, + QString("Select a session file"), + m_last_file, + QString("Sessions (*.samplebrain)")); + + lo_send(m_process_address,"/save_session","s",m_last_file.toStdString().c_str()); + } void update_status() { m_feedback.poll(m_Ui.statusbar); } private: + + void init_from_session(const string &filename); + string m_save_wav; QString m_last_file; u32 m_record_id; diff --git a/samplebrain/qt/generated/ui_samplebrain.h b/samplebrain/qt/generated/ui_samplebrain.h index 088642b..6f85f8c 100644 --- a/samplebrain/qt/generated/ui_samplebrain.h +++ b/samplebrain/qt/generated/ui_samplebrain.h @@ -1,13 +1,13 @@ /******************************************************************************** -** Form generated from reading UI file 'samplebrainPm4153.ui' +** Form generated from reading UI file 'samplebraing13021.ui' ** ** Created by: Qt User Interface Compiler version 4.8.6 ** ** WARNING! All changes made in this file will be lost when recompiling UI file! ********************************************************************************/ -#ifndef SAMPLEBRAINPM4153_H -#define SAMPLEBRAINPM4153_H +#ifndef SAMPLEBRAING13021_H +#define SAMPLEBRAING13021_H #include #include @@ -88,6 +88,7 @@ public: QLabel *label_29; QSlider *sliderSlideError; QSpinBox *spinBoxSlideError; + QSpacerItem *horizontalSpacer_2; QVBoxLayout *verticalLayout_6; QLabel *label_16; QPushButton *pushButtonLoadTarget; @@ -122,7 +123,7 @@ public: QSlider *sliderTargetMix; QDoubleSpinBox *doubleSpinBoxTargetMix; QSpacerItem *verticalSpacer; - QVBoxLayout *verticalLayout; + QVBoxLayout *verticalLayout_2; QLabel *label_3; QListWidget *listWidgetSounds; QHBoxLayout *horizontalLayout_2; @@ -136,8 +137,8 @@ public: QLabel *label_2; QDoubleSpinBox *doubleSpinBoxBlockOverlap; QGridLayout *gridLayout; - QRadioButton *radioButton_hamming; QRadioButton *radioButton_gaussian; + QRadioButton *radioButton_hamming; QRadioButton *radioButton_dodgy; QRadioButton *radioButton_blackman; QRadioButton *radioButton_rectagle; @@ -149,6 +150,7 @@ public: QHBoxLayout *horizontalLayout_7; QPushButton *pushButtonLoadBrain; QPushButton *pushButtonSaveBrain; + QSpacerItem *verticalSpacer_2; QWidget *logTab; QHBoxLayout *horizontalLayout_15; QPlainTextEdit *plainTextEdit; @@ -158,6 +160,9 @@ public: QPushButton *pushButtonRecord; QPushButton *pushButtonStopRecord; QDial *dialVolume; + QVBoxLayout *verticalLayout; + QPushButton *pushButtonLoadSession; + QPushButton *pushButtonSaveSession; QSpacerItem *horizontalSpacer; QLabel *label_13; QStatusBar *statusbar; @@ -169,7 +174,7 @@ public: { if (MainWindow->objectName().isEmpty()) MainWindow->setObjectName(QString::fromUtf8("MainWindow")); - MainWindow->resize(1012, 707); + MainWindow->resize(910, 795); centralwidget = new QWidget(MainWindow); centralwidget->setObjectName(QString::fromUtf8("centralwidget")); verticalLayout_4 = new QVBoxLayout(centralwidget); @@ -545,6 +550,10 @@ public: verticalLayout_3->addLayout(horizontalLayout_20); + horizontalSpacer_2 = new QSpacerItem(21, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + verticalLayout_3->addItem(horizontalSpacer_2); + horizontalLayout_5->addLayout(verticalLayout_3); @@ -685,7 +694,6 @@ public: sizePolicy.setHeightForWidth(sliderAutotune->sizePolicy().hasHeightForWidth()); sliderAutotune->setSizePolicy(sizePolicy); sliderAutotune->setValue(0); - sliderAutotune->setSliderPosition(0); sliderAutotune->setOrientation(Qt::Horizontal); horizontalLayout_22->addWidget(sliderAutotune); @@ -771,18 +779,18 @@ public: horizontalLayout_5->addLayout(verticalLayout_6); - verticalLayout = new QVBoxLayout(); - verticalLayout->setObjectName(QString::fromUtf8("verticalLayout")); + verticalLayout_2 = new QVBoxLayout(); + verticalLayout_2->setObjectName(QString::fromUtf8("verticalLayout_2")); label_3 = new QLabel(controlTab); label_3->setObjectName(QString::fromUtf8("label_3")); label_3->setFont(font1); - verticalLayout->addWidget(label_3); + verticalLayout_2->addWidget(label_3); listWidgetSounds = new QListWidget(controlTab); listWidgetSounds->setObjectName(QString::fromUtf8("listWidgetSounds")); - verticalLayout->addWidget(listWidgetSounds); + verticalLayout_2->addWidget(listWidgetSounds); horizontalLayout_2 = new QHBoxLayout(); horizontalLayout_2->setObjectName(QString::fromUtf8("horizontalLayout_2")); @@ -799,13 +807,13 @@ public: horizontalLayout_2->addWidget(pushButtonDeleteSound); - verticalLayout->addLayout(horizontalLayout_2); + verticalLayout_2->addLayout(horizontalLayout_2); pushButtonClearBrain = new QPushButton(controlTab); pushButtonClearBrain->setObjectName(QString::fromUtf8("pushButtonClearBrain")); pushButtonClearBrain->setFont(font); - verticalLayout->addWidget(pushButtonClearBrain); + verticalLayout_2->addWidget(pushButtonClearBrain); horizontalLayout_4 = new QHBoxLayout(); horizontalLayout_4->setObjectName(QString::fromUtf8("horizontalLayout_4")); @@ -823,7 +831,7 @@ public: horizontalLayout_4->addWidget(spinBoxBlockSize); - verticalLayout->addLayout(horizontalLayout_4); + verticalLayout_2->addLayout(horizontalLayout_4); horizontalLayout_6 = new QHBoxLayout(); horizontalLayout_6->setObjectName(QString::fromUtf8("horizontalLayout_6")); @@ -842,24 +850,24 @@ public: horizontalLayout_6->addWidget(doubleSpinBoxBlockOverlap); - verticalLayout->addLayout(horizontalLayout_6); + verticalLayout_2->addLayout(horizontalLayout_6); gridLayout = new QGridLayout(); gridLayout->setObjectName(QString::fromUtf8("gridLayout")); - radioButton_hamming = new QRadioButton(controlTab); + radioButton_gaussian = new QRadioButton(controlTab); buttonGroup = new QButtonGroup(MainWindow); buttonGroup->setObjectName(QString::fromUtf8("buttonGroup")); - buttonGroup->addButton(radioButton_hamming); - radioButton_hamming->setObjectName(QString::fromUtf8("radioButton_hamming")); - - gridLayout->addWidget(radioButton_hamming, 4, 1, 1, 1); - - radioButton_gaussian = new QRadioButton(controlTab); buttonGroup->addButton(radioButton_gaussian); radioButton_gaussian->setObjectName(QString::fromUtf8("radioButton_gaussian")); gridLayout->addWidget(radioButton_gaussian, 3, 1, 1, 1); + radioButton_hamming = new QRadioButton(controlTab); + buttonGroup->addButton(radioButton_hamming); + radioButton_hamming->setObjectName(QString::fromUtf8("radioButton_hamming")); + + gridLayout->addWidget(radioButton_hamming, 4, 1, 1, 1); + radioButton_dodgy = new QRadioButton(controlTab); buttonGroup->addButton(radioButton_dodgy); radioButton_dodgy->setObjectName(QString::fromUtf8("radioButton_dodgy")); @@ -903,13 +911,13 @@ public: gridLayout->addWidget(label_4, 2, 0, 1, 1); - verticalLayout->addLayout(gridLayout); + verticalLayout_2->addLayout(gridLayout); pushButtonGenerate = new QPushButton(controlTab); pushButtonGenerate->setObjectName(QString::fromUtf8("pushButtonGenerate")); pushButtonGenerate->setFont(font); - verticalLayout->addWidget(pushButtonGenerate); + verticalLayout_2->addWidget(pushButtonGenerate); horizontalLayout_7 = new QHBoxLayout(); horizontalLayout_7->setObjectName(QString::fromUtf8("horizontalLayout_7")); @@ -926,10 +934,14 @@ public: horizontalLayout_7->addWidget(pushButtonSaveBrain); - verticalLayout->addLayout(horizontalLayout_7); + verticalLayout_2->addLayout(horizontalLayout_7); + + verticalSpacer_2 = new QSpacerItem(20, 508, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayout_2->addItem(verticalSpacer_2); - horizontalLayout_5->addLayout(verticalLayout); + horizontalLayout_5->addLayout(verticalLayout_2); tabWidget->addTab(controlTab, QString()); logTab = new QWidget(); @@ -995,6 +1007,21 @@ public: horizontalLayout_12->addWidget(dialVolume); + verticalLayout = new QVBoxLayout(); + verticalLayout->setObjectName(QString::fromUtf8("verticalLayout")); + pushButtonLoadSession = new QPushButton(centralwidget); + pushButtonLoadSession->setObjectName(QString::fromUtf8("pushButtonLoadSession")); + + verticalLayout->addWidget(pushButtonLoadSession); + + pushButtonSaveSession = new QPushButton(centralwidget); + pushButtonSaveSession->setObjectName(QString::fromUtf8("pushButtonSaveSession")); + + verticalLayout->addWidget(pushButtonSaveSession); + + + horizontalLayout_12->addLayout(verticalLayout); + horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); horizontalLayout_12->addItem(horizontalSpacer); @@ -1077,8 +1104,10 @@ public: QObject::connect(sliderSlideError, SIGNAL(valueChanged(int)), MainWindow, SLOT(slide_error(int))); QObject::connect(doubleSpinBoxStickyness, SIGNAL(valueChanged(double)), MainWindow, SLOT(stickyness_slot(double))); QObject::connect(sliderStickyness, SIGNAL(valueChanged(int)), MainWindow, SLOT(stickyness_slot(int))); - QObject::connect(doubleSpinBoxAutotune, SIGNAL(valueChanged(double)), MainWindow, SLOT(autotune(double))); QObject::connect(sliderAutotune, SIGNAL(sliderMoved(int)), MainWindow, SLOT(autotune(int))); + QObject::connect(doubleSpinBoxAutotune, SIGNAL(valueChanged(double)), MainWindow, SLOT(autotune(double))); + QObject::connect(pushButtonLoadSession, SIGNAL(released()), MainWindow, SLOT(load_session())); + QObject::connect(pushButtonSaveSession, SIGNAL(released()), MainWindow, SLOT(save_session())); tabWidget->setCurrentIndex(0); @@ -1088,13 +1117,13 @@ public: void retranslateUi(QMainWindow *MainWindow) { - MainWindow->setWindowTitle(QApplication::translate("MainWindow", "samplebrain 0.11", 0, QApplication::UnicodeUTF8)); + MainWindow->setWindowTitle(QApplication::translate("MainWindow", "samplebrain 0.12", 0, QApplication::UnicodeUTF8)); label_19->setText(QApplication::translate("MainWindow", "brain tweaks", 0, QApplication::UnicodeUTF8)); label_6->setText(QApplication::translate("MainWindow", "fft / mfcc", 0, QApplication::UnicodeUTF8)); #ifndef QT_NO_TOOLTIP sliderRatio->setToolTip(QApplication::translate("MainWindow", "plain fft match vs mfcc values ", 0, QApplication::UnicodeUTF8)); #endif // QT_NO_TOOLTIP - label_20->setText(QApplication::translate("MainWindow", "dynamics / freq", 0, QApplication::UnicodeUTF8)); + label_20->setText(QApplication::translate("MainWindow", "freq & dynamics / freq only", 0, QApplication::UnicodeUTF8)); #ifndef QT_NO_TOOLTIP sliderNRatio->setToolTip(QApplication::translate("MainWindow", "match original or normalised blocks", 0, QApplication::UnicodeUTF8)); #endif // QT_NO_TOOLTIP @@ -1129,7 +1158,7 @@ public: #endif // QT_NO_TOOLTIP label_30->setText(QApplication::translate("MainWindow", "stickyness", 0, QApplication::UnicodeUTF8)); #ifndef QT_NO_TOOLTIP - sliderStickyness->setToolTip(QApplication::translate("MainWindow", "prioritise brain order over closeness", 0, QApplication::UnicodeUTF8)); + sliderStickyness->setToolTip(QApplication::translate("MainWindow", "how long it takes for the novelty to wear off", 0, QApplication::UnicodeUTF8)); #endif // QT_NO_TOOLTIP #ifndef QT_NO_TOOLTIP label_28->setToolTip(QString()); @@ -1149,7 +1178,7 @@ public: #ifndef QT_NO_TOOLTIP radioButtonAlgoRevBasic->setToolTip(QApplication::translate("MainWindow", "full brain reverse search", 0, QApplication::UnicodeUTF8)); #endif // QT_NO_TOOLTIP - radioButtonAlgoRevBasic->setText(QApplication::translate("MainWindow", "rev", 0, QApplication::UnicodeUTF8)); + radioButtonAlgoRevBasic->setText(QApplication::translate("MainWindow", "rev basic", 0, QApplication::UnicodeUTF8)); #ifndef QT_NO_TOOLTIP radioButtonSynaptic->setToolTip(QApplication::translate("MainWindow", "search based on synapse connections", 0, QApplication::UnicodeUTF8)); #endif // QT_NO_TOOLTIP @@ -1211,8 +1240,8 @@ public: pushButtonClearBrain->setText(QApplication::translate("MainWindow", "clear brain", 0, QApplication::UnicodeUTF8)); label->setText(QApplication::translate("MainWindow", "block size", 0, QApplication::UnicodeUTF8)); label_2->setText(QApplication::translate("MainWindow", "block overlap", 0, QApplication::UnicodeUTF8)); - radioButton_hamming->setText(QApplication::translate("MainWindow", "hamming", 0, QApplication::UnicodeUTF8)); radioButton_gaussian->setText(QApplication::translate("MainWindow", "gaussian", 0, QApplication::UnicodeUTF8)); + radioButton_hamming->setText(QApplication::translate("MainWindow", "hamming", 0, QApplication::UnicodeUTF8)); radioButton_dodgy->setText(QApplication::translate("MainWindow", "dodgy", 0, QApplication::UnicodeUTF8)); radioButton_blackman->setText(QApplication::translate("MainWindow", "blackman", 0, QApplication::UnicodeUTF8)); radioButton_rectagle->setText(QApplication::translate("MainWindow", "rectangle", 0, QApplication::UnicodeUTF8)); @@ -1229,6 +1258,8 @@ public: pushButtonStop->setText(QString()); pushButtonRecord->setText(QString()); pushButtonStopRecord->setText(QString()); + pushButtonLoadSession->setText(QApplication::translate("MainWindow", "load session", 0, QApplication::UnicodeUTF8)); + pushButtonSaveSession->setText(QApplication::translate("MainWindow", "save session", 0, QApplication::UnicodeUTF8)); label_13->setText(QString()); } // retranslateUi @@ -1240,4 +1271,4 @@ namespace Ui { QT_END_NAMESPACE -#endif // SAMPLEBRAINPM4153_H +#endif // SAMPLEBRAING13021_H diff --git a/samplebrain/qt/process_thread.cpp b/samplebrain/qt/process_thread.cpp index cd96302..1900a42 100644 --- a/samplebrain/qt/process_thread.cpp +++ b/samplebrain/qt/process_thread.cpp @@ -110,6 +110,12 @@ void process_thread::process() { if (name=="/save_brain") { save_source(cmd.get_string(0)); } + if (name=="/load_session") { + load_session(cmd.get_string(0)); + } + if (name=="/save_session") { + save_session(cmd.get_string(0)); + } } usleep(500); } @@ -118,20 +124,13 @@ void process_thread::process() { void process_thread::load_source(const std::string &filename) { pthread_mutex_lock(m_brain_mutex); + m_source.clear(); ifstream ifs(filename.c_str(),ios::binary); ifs||m_source; ifs.close(); pthread_mutex_unlock(m_brain_mutex); } -void process_thread::load_target(const std::string &filename) { - pthread_mutex_lock(m_brain_mutex); - ifstream ifs(filename.c_str(),ios::binary); - ifs||m_target; - ifs.close(); - pthread_mutex_unlock(m_brain_mutex); -} - void process_thread::save_source(const std::string &filename) { pthread_mutex_lock(m_brain_mutex); ofstream ofs(filename.c_str(),ios::binary); @@ -140,9 +139,30 @@ void process_thread::save_source(const std::string &filename) { pthread_mutex_unlock(m_brain_mutex); } -void process_thread::save_target(const std::string &filename) { +// remember to change GUI side to match... +void process_thread::load_session(const std::string &filename) { + pthread_mutex_lock(m_brain_mutex); + m_source.clear(); + m_target.clear(); + ifstream ifs(filename.c_str(),ios::binary); + ifs||(*m_renderer); + ifs||m_source_block_size||m_source_overlap; + ifs||m_target_block_size||m_target_overlap; + ifs||m_window_type||m_target_window_type; + ifs||m_source; + ifs||m_target; + ifs.close(); + pthread_mutex_unlock(m_brain_mutex); +} + +void process_thread::save_session(const std::string &filename) { pthread_mutex_lock(m_brain_mutex); ofstream ofs(filename.c_str(),ios::binary); + ofs||(*m_renderer); + ofs||m_source_block_size||m_source_overlap; + ofs||m_target_block_size||m_target_overlap; + ofs||m_window_type||m_target_window_type; + ofs||m_source; ofs||m_target; ofs.close(); pthread_mutex_unlock(m_brain_mutex); diff --git a/samplebrain/qt/process_thread.h b/samplebrain/qt/process_thread.h index 9745374..7d87c69 100644 --- a/samplebrain/qt/process_thread.h +++ b/samplebrain/qt/process_thread.h @@ -35,9 +35,10 @@ public: void process(); void load_source(const std::string &filename); - void load_target(const std::string &filename); void save_source(const std::string &filename); - void save_target(const std::string &filename); + + void load_session(const std::string &filename); + void save_session(const std::string &filename); // only for use in mutex brain m_source, m_target; diff --git a/samplebrain/qt/samplebrain.pro b/samplebrain/qt/samplebrain.pro index ba3cbe9..16524f0 100644 --- a/samplebrain/qt/samplebrain.pro +++ b/samplebrain/qt/samplebrain.pro @@ -23,6 +23,7 @@ SOURCES += MainWindow.cpp \ ../src/fft.cpp \ ../src/mfcc.cpp \ ../src/renderer.cpp \ + ../src/search_params.cpp \ ../src/status.cpp \ ../src/window.cpp \ ../src/aquila/filter/MelFilterBank.cpp \ diff --git a/samplebrain/src/brain.cpp b/samplebrain/src/brain.cpp index a992e01..5fa8173 100644 --- a/samplebrain/src/brain.cpp +++ b/samplebrain/src/brain.cpp @@ -61,6 +61,12 @@ void brain::delete_sound(std::string filename) { } } +void brain::clear() { + m_blocks.clear(); + m_samples.clear(); + m_active_sounds.clear(); +} + // rewrites whole brain void brain::init(u32 block_size, u32 overlap, window::type t, bool ditchpcm) { m_blocks.clear(); @@ -322,6 +328,7 @@ ios &spiralcore::operator||(ios &s, brain::sound &b) { ios &spiralcore::operator||(ios &s, brain &b) { u32 version=0; string id("brain"); + // changes here need to be reflected in interface loading s||id||version; stream_vector(s,b.m_blocks); stream_list(s,b.m_samples); diff --git a/samplebrain/src/brain.h b/samplebrain/src/brain.h index c06bfe4..52a442d 100644 --- a/samplebrain/src/brain.h +++ b/samplebrain/src/brain.h @@ -49,6 +49,8 @@ public: sample m_sample; }; + void clear(); + // load, chop up and add to brain // todo: add tags void load_sound(std::string filename); @@ -58,6 +60,8 @@ public: // (presumably this one is made from a single sample) //void resynth(const std::string &filename, const brain &other, const search_params ¶ms); + + const std::list &get_samples() { return m_samples; } const sample &get_block_pcm(u32 index) const; const sample &get_block_n_pcm(u32 index) const; const block &get_block(u32 index) const; @@ -66,6 +70,7 @@ public: u32 get_overlap() const { return m_overlap; } void set_usage_falloff(float s) { m_usage_falloff=s; } + float get_usage_falloff() { return m_usage_falloff; } // basic search u32 search(const block &target, const search_params ¶ms); diff --git a/samplebrain/src/renderer.cpp b/samplebrain/src/renderer.cpp index 627afb2..5170915 100644 --- a/samplebrain/src/renderer.cpp +++ b/samplebrain/src/renderer.cpp @@ -244,6 +244,18 @@ void renderer::clean_up() { } } +ios &spiralcore::operator||(ios &s, renderer &r) { + u32 version=0; + string id("renderer"); + s||id||version; + s||r.m_search_params; + s||r.m_volume||r.m_playing||r.m_target_index||r.m_render_index; + s||r.m_target_time||r.m_render_time||r.m_stretch; + s||r.m_n_mix||r.m_target_mix||r.m_autotune; + s||r.m_search_algo||r.m_slide_error||r.m_last_tgt_shift; + return s; +} + bool renderer::unit_test() { brain source; source.load_sound("test_data/up.wav"); diff --git a/samplebrain/src/renderer.h b/samplebrain/src/renderer.h index 8091a41..bfe2dff 100644 --- a/samplebrain/src/renderer.h +++ b/samplebrain/src/renderer.h @@ -55,10 +55,20 @@ namespace spiralcore { void set_autotune(float s) { m_autotune=s; } search_params *get_params() { return &m_search_params; } + search_algo get_search_algo() { return m_search_algo; } + float get_volume() { return m_volume; } + float get_n_mix() { return m_n_mix; } + float get_target_mix() { return m_target_mix; } + double get_slide_error() { return m_slide_error; } + u32 get_stretch() { return m_stretch; } + float get_autotune() { return m_autotune; } + brain &get_source() { return m_source; } static bool unit_test(); + friend ios &operator||(ios &s, renderer &b); + private: bool find_render_blocks(u32 nframes); @@ -104,6 +114,9 @@ namespace spiralcore { std::list m_render_blocks; }; + std::ios &operator||(std::ios &s, renderer &b); + } + #endif diff --git a/samplebrain/src/search_params.h b/samplebrain/src/search_params.h index 9ca9abd..a9e3fd2 100644 --- a/samplebrain/src/search_params.h +++ b/samplebrain/src/search_params.h @@ -17,6 +17,12 @@ #ifndef SEARCH_PARAMS #define SEARCH_PARAMS +#include "jellyfish/core/types.h" +#include "jellyfish/core/stream.h" +#include + +#pragma once + namespace spiralcore { class search_params { @@ -40,6 +46,8 @@ public: f32 m_stickyness; }; + std::ios &operator||(std::ios &s, search_params &b); + } #endif