/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * instrumentwidget.cc * * Sat May 12 15:38:38 CEST 2018 * Copyright 2018 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ /* * This file is part of DrumGizmo. * * DrumGizmo 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. * * DrumGizmo 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 DrumGizmo; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "instrumentwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sleep.h" #include "canvastool.h" #include "canvastoolthreshold.h" #include "volumefader.h" #include "selectioneditor.h" #include "zoomslider.h" #include "settings.h" #include "project.h" #define MAXVAL 10000000L static void addTool(QToolBar* toolbar, Canvas* canvas, CanvasTool* tool) { QAction* action = new QAction(tool->name(), toolbar); action->setCheckable(true); toolbar->addAction(action); tool->connect(action, SIGNAL(toggled(bool)), tool, SLOT(setActive(bool))); tool->setActive(false); canvas->tools.push_back(tool); } InstrumentWidget::InstrumentWidget(Settings& settings, Instrument& instrument) : settings(settings) , instrument(instrument) { setProperty("id", instrument.getId()); { int start = 44100 * 60; Selection p(start, start + 44100 * 60, 0, 0); // one minute selection selections_preview.add(p); } QWidget* central = new QWidget(); QHBoxLayout* lh = new QHBoxLayout(); QVBoxLayout* lv = new QVBoxLayout(); central->setLayout(lv); setCentralWidget(central); extractor = new AudioExtractor(instrument, this); canvaswidget = new CanvasWidget(this); QToolBar* toolbar = addToolBar(tr("Tools")); listen = new CanvasToolListen(canvaswidget->canvas, player); addTool(toolbar, canvaswidget->canvas, listen); threshold = new CanvasToolThreshold(canvaswidget->canvas, instrument); canvaswidget->canvas->tools.push_back(threshold); tool_selections = new CanvasToolSelections(canvaswidget->canvas, selections, selections_preview); connect(threshold, SIGNAL(thresholdChanged(double)), tool_selections, SLOT(thresholdChanged(double))); connect(threshold, SIGNAL(thresholdChanging(double)), tool_selections, SLOT(thresholdChanged(double))); connect(&selections, SIGNAL(activeChanged(sel_id_t)), canvaswidget->canvas, SLOT(update())); connect(&selections, SIGNAL(updated(sel_id_t)), canvaswidget->canvas, SLOT(update())); addTool(toolbar, canvaswidget->canvas, tool_selections); sorter = new SampleSorter(selections, selections_preview, instrument); connect(&selections, SIGNAL(added(sel_id_t)), sorter, SLOT(addSelection(sel_id_t))); connect(&selections_preview, SIGNAL(added(sel_id_t)), sorter, SLOT(addSelectionPreview(sel_id_t))); connect(&selections, SIGNAL(updated(sel_id_t)), sorter, SLOT(relayout())); connect(&selections_preview, SIGNAL(updated(sel_id_t)), sorter, SLOT(relayout())); connect(&selections, SIGNAL(removed(sel_id_t)), sorter, SLOT(relayout())); connect(&selections_preview, SIGNAL(removed(sel_id_t)), sorter, SLOT(relayout())); connect(&selections, SIGNAL(activeChanged(sel_id_t)), sorter, SLOT(relayout())); QPushButton* btn_playsamples = new QPushButton(tr("Play samples")); connect(btn_playsamples, SIGNAL(clicked()), this, SLOT(playSamples())); sb_playsamples = new QScrollBar(Qt::Horizontal); sb_playsamples->setRange(100, 4000); // ms lh->addWidget(canvaswidget); lv->addLayout(lh, 100); lv->addWidget(sorter, 15); lv->addWidget(btn_playsamples); lv->addWidget(sb_playsamples); QDockWidget* dockWidget = new QDockWidget(tr("Process"), this); dockWidget->setAllowedAreas(Qt::RightDockWidgetArea); dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures); dockWidget->setWidget(new QWidget()); dockWidget->widget()->setLayout(new QVBoxLayout()); tabs = new QTabWidget(this); tabs->setMinimumWidth(350); tabs->addTab(createFilesTab(), tr("Files")); generateTabId = tabs->addTab(createGenerateTab(), tr("Generate")); tabs->addTab(createEditTab(), tr("Edit")); //tabs->addTab(createExportTab(), tr("Export")); connect(tabs, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); tabChanged(tabs->currentIndex()); dockWidget->widget()->layout()->addWidget(tabs); VolumeFader* vol = new VolumeFader(); connect(vol, SIGNAL(volumeChangedDb(double)), &player, SLOT(setGainDB(double))); connect(&player, SIGNAL(peakUpdate(double)), vol, SLOT(updatePeakPower(double))); vol->setVolumeDb(0); dockWidget->widget()->layout()->addWidget(vol); addDockWidget(Qt::RightDockWidgetArea, dockWidget); canvaswidget->yscale->setValue(0.9); canvaswidget->yoffset->setValue(MAXVAL/2); canvaswidget->xscale->setValue(0); canvaswidget->xoffset->setValue(0); // Update selections according to threshold tool_selections->thresholdChanged(instrument.getThreshold()); } InstrumentWidget::~InstrumentWidget() { } void InstrumentWidget::tabChanged(int tabid) { tool_selections->setShowPreview(tabid == generateTabId); sorter->setShowPreview(tabid == generateTabId); tool_selections->autoCreateSelectionsPreview(); threshold->setActive(tabid == generateTabId); } QWidget* InstrumentWidget::createFilesTab() { QWidget* w = new QWidget(); QVBoxLayout* l = new QVBoxLayout(); w->setLayout(l); l->addWidget(new QLabel(tr("Files: (double-click to set as master)"))); QPushButton* loadbtn = new QPushButton(); loadbtn->setText(tr("Add files...")); l->addWidget(loadbtn); filelist = new FileList(instrument); connect(filelist, SIGNAL(masterFileChanged(QString)), this, SLOT(loadFile(QString))); connect(loadbtn, SIGNAL(clicked()), filelist, SLOT(addFiles())); l->addWidget(filelist); if(!instrument.getMasterFile().isEmpty()) { loadFile(instrument.getMasterFile()); } return w; } QWidget* InstrumentWidget::createEditTab() { selections = instrument.getSelections(); SelectionEditor* se = new SelectionEditor(selections, instrument); connect(&selections, SIGNAL(added(sel_id_t)), se, SLOT(added(sel_id_t))); connect(&selections, SIGNAL(updated(sel_id_t)), se, SLOT(updated(sel_id_t))); connect(&selections, SIGNAL(removed(sel_id_t)), se, SLOT(removed(sel_id_t))); connect(&selections, SIGNAL(activeChanged(sel_id_t)), se, SLOT(activeChanged(sel_id_t))); connect(&selections, SIGNAL(added(sel_id_t)), this, SLOT(selectionChanged())); connect(&selections, SIGNAL(updated(sel_id_t)), this, SLOT(selectionChanged())); connect(&selections, SIGNAL(removed(sel_id_t)), this, SLOT(selectionChanged())); connect(&selections, SIGNAL(activeChanged(sel_id_t)), this, SLOT(selectionChanged())); return se; } void InstrumentWidget::selectionChanged() { instrument.setSelections(selections); } static QSlider* createAttribute(QWidget* parent, QString name, int range_from, int range_to) { QSlider* slider; QGridLayout* l = new QGridLayout(); l->addWidget(new QLabel(name), 0, 0, 1, 2); QSpinBox* spin = new QSpinBox(); spin->setRange(range_from, range_to); l->addWidget(spin, 1, 0, 1, 1); slider = new QSlider(Qt::Horizontal); slider->setRange(range_from, range_to); l->addWidget(slider, 1, 1, 1,1); QObject::connect(spin, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int))); QObject::connect(slider, SIGNAL(valueChanged(int)), spin, SLOT(setValue(int))); ((QBoxLayout*)parent->layout())->addLayout(l); return slider; } QWidget* InstrumentWidget::createGenerateTab() { QWidget* w = new QWidget(); QVBoxLayout* l = new QVBoxLayout(); w->setLayout(l); QHBoxLayout* btns = new QHBoxLayout(); QPushButton* autosel = new QPushButton(); autosel->setText(tr("Generate")); connect(autosel, SIGNAL(clicked()), tool_selections, SLOT(clearSelections())); connect(autosel, SIGNAL(clicked()), tool_selections, SLOT(autoCreateSelections())); connect(threshold, SIGNAL(thresholdChanged(double)), tool_selections, SLOT(autoCreateSelectionsPreview())); connect(threshold, SIGNAL(thresholdChanging(double)), tool_selections, SLOT(autoCreateSelectionsPreview())); QPushButton* clearsel = new QPushButton(); clearsel->setText(tr("Clear")); connect(clearsel, SIGNAL(clicked()), tool_selections, SLOT(clearSelections())); btns->addWidget(autosel); btns->addWidget(clearsel); l->addLayout(btns); slider_attacklength = createAttribute(w, tr("Attack length:"), 10, 1000); connect(slider_attacklength, SIGNAL(valueChanged(int)), sorter, SLOT(setAttackLength(int))); connect(slider_attacklength, SIGNAL(valueChanged(int)), tool_selections, SLOT(autoCreateSelectionsPreview())); slider_attacklength->setValue(instrument.getAttackLength()); connect(slider_attacklength, SIGNAL(valueChanged(int)), this, SLOT(generateSlidersChanged())); slider_spread = createAttribute(w, tr("Power spread:"), 1, 2000); connect(slider_spread, SIGNAL(valueChanged(int)), sorter, SLOT(setSpreadFactor(int))); connect(slider_spread, SIGNAL(valueChanged(int)), tool_selections, SLOT(autoCreateSelectionsPreview())); slider_spread->setValue(instrument.getPowerSpread()); connect(slider_spread, SIGNAL(valueChanged(int)), this, SLOT(generateSlidersChanged())); slider_hold = createAttribute(w, tr("Minimum size (samples):"), 0, 200000); connect(slider_hold, SIGNAL(valueChanged(int)), tool_selections, SLOT(holdChanged(int))); connect(slider_hold, SIGNAL(valueChanged(int)), tool_selections, SLOT(autoCreateSelectionsPreview())); slider_hold->setValue(instrument.getMinimumSize()); connect(slider_hold, SIGNAL(valueChanged(int)), this, SLOT(generateSlidersChanged())); slider_falloff = createAttribute(w, tr("Falloff:"), 10, 5000); connect(slider_falloff, SIGNAL(valueChanged(int)), tool_selections, SLOT(noiseFloorChanged(int))); connect(slider_falloff, SIGNAL(valueChanged(int)), tool_selections, SLOT(autoCreateSelectionsPreview())); slider_falloff->setValue(instrument.getFalloff()); connect(slider_falloff, SIGNAL(valueChanged(int)), this, SLOT(generateSlidersChanged())); slider_fadelength = createAttribute(w, tr("Fadelength:"), 0, 2000); connect(slider_fadelength, SIGNAL(valueChanged(int)), tool_selections, SLOT(fadeoutChanged(int))); connect(slider_fadelength, SIGNAL(valueChanged(int)), tool_selections, SLOT(autoCreateSelectionsPreview())); slider_fadelength->setValue(instrument.getFadeLength()); connect(slider_fadelength, SIGNAL(valueChanged(int)), this, SLOT(generateSlidersChanged())); l->addStretch(); return w; } void InstrumentWidget::generateSlidersChanged() { Project::RAIIBulkUpdate bulkUpdate(instrument.getProject()); instrument.setAttackLength(slider_attacklength->value()); instrument.setPowerSpread(slider_spread->value()); instrument.setMinimumSize(slider_hold->value()); instrument.setFalloff(slider_falloff->value()); instrument.setFadeLength(slider_fadelength->value()); } QWidget* InstrumentWidget::createExportTab() { QWidget* w = new QWidget(); QVBoxLayout* l = new QVBoxLayout(); w->setLayout(l); l->addWidget(new QLabel(tr("Prefix:"))); prefix = new QLineEdit(); prefix->setText(instrument.getPrefix()); connect(prefix, SIGNAL(textChanged(const QString &)), this, SLOT(prefixChanged())); l->addWidget(prefix); l->addWidget(new QLabel(tr("Export path:"))); QHBoxLayout* lo_exportp = new QHBoxLayout(); lineed_exportp = new QLineEdit(); lineed_exportp->setText(instrument.getProject().getExportPath()); connect(lineed_exportp, SIGNAL(textChanged(const QString &)), this, SLOT(exportPathChanged())); lo_exportp->addWidget(lineed_exportp); QPushButton* btn_browse = new QPushButton(tr("...")); connect(btn_browse, SIGNAL(clicked()), this, SLOT(browse())); lo_exportp->addWidget(btn_browse); l->addLayout(lo_exportp); QPushButton* exportsel = new QPushButton(); exportsel->setText(tr("Export")); connect(exportsel, SIGNAL(clicked()), this, SLOT(doExport())); l->addWidget(exportsel); QProgressBar* bar = new QProgressBar(); connect(extractor, SIGNAL(progressUpdate(int)), bar, SLOT(setValue(int))); connect(extractor, SIGNAL(setMaximumProgress(int)), bar, SLOT(setMaximum(int))); l->addWidget(bar); l->addStretch(); return w; } void InstrumentWidget::prefixChanged() { instrument.setPrefix(prefix->text()); } void InstrumentWidget::exportPathChanged() { instrument.getProject().setExportPath(lineed_exportp->text()); } void InstrumentWidget::playSamples() { Selections* sels = &selections; if(tabs->currentIndex() == generateTabId) { sels = &selections_preview; } QVector ids = sels->ids(); for(int v1 = 0; v1 < ids.size(); v1++) { for(int v2 = 0; v2 < ids.size(); v2++) { Selection sel1 = sels->get(ids[v1]); Selection sel2 = sels->get(ids[v2]); if(sel1.energy < sel2.energy) { sel_id_t vtmp = ids[v1]; ids[v1] = ids[v2]; ids[v2] = vtmp; } } } QVector::iterator i = ids.begin(); while(i != ids.end()) { Selection sel = sels->get(*i); unsigned int length = sb_playsamples->value() * 44100 / 1000; unsigned int sample_length = sel.to - sel.from; unsigned int to = sel.to; if(sample_length > length) { to = sel.from + length; } sels->setActive(*i); connect(&player, SIGNAL(positionUpdate(size_t)), listen, SLOT(update(size_t))); player.playSelection(sel, to - sel.from); QTime t; t.start(); while(t.elapsed() < sb_playsamples->value()) { qApp->processEvents(); q_usleep(25 * 1000); } player.stop(); disconnect(&player, SIGNAL(positionUpdate(size_t)), listen, SLOT(update(size_t))); i++; } } void InstrumentWidget::doExport() { extractor->exportSelections(); } void InstrumentWidget::loadFile(QString filename) { setCursor(Qt::WaitCursor); statusBar()->showMessage(tr("Loading...")); qApp->processEvents(); sorter->setWavData(NULL, 0); player.setPcmData(NULL, 0, 0); canvaswidget->canvas->load(filename); sorter->setWavData(canvaswidget->canvas->data, canvaswidget->canvas->size); player.setPcmData(canvaswidget->canvas->data, canvaswidget->canvas->size, canvaswidget->canvas->samplerate); statusBar()->showMessage(tr("Ready")); setCursor(Qt::ArrowCursor); instrument.setMasterFile(filename); } void InstrumentWidget::browse() { QString path = QFileDialog::getExistingDirectory(this, tr("Select export path"), lineed_exportp->text()); lineed_exportp->setText(path); }