123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- /*
- This file is part of QTau
- Copyright (C) 2013-2018 Tobias "Tomoko" Platen <tplaten@posteo.de>
- Copyright (C) 2013 digited <https://github.com/digited>
- Copyright (C) 2010-2013 HAL@ShurabaP <https://github.com/haruneko>
- QTau 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 3 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, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: GPL-3.0+
- */
- #include "mainwindow.h"
- #include <QJsonDocument>
- #include "Controller.h"
- #include "PluginInterfaces.h"
- #include "Session.h"
- #include "Utils.h"
- #include "audio/audioengine.h"
- #include "audio/jackaudio.h"
- #include <assert.h>
- #include <qsettings.h>
- #include <score.h>
- #include <tempomap.h>
- #include <QApplication>
- #include <QDebug>
- #include <QDirIterator>
- #include <QPluginLoader>
- #include <QTimer>
- #include <QProcess>
- #include "archivedownloader.h"
- #include <QMessageBox>
- static QString defa = "https://archive.org/download/defa-vault-cv-japanese.tar/Defa%20Vault%20CV%20Japanese.tar.bz2";
- #define __devloglevel__ 4
- qtauController::qtauController(QString dir, QObject *parent)
- : QObject(parent),
- _jack(nullptr),
- _mainWindow(nullptr),
- _activeSession(nullptr) {
- QDir diru(dir);
- diru.cdUp();
- _prefix = diru.absolutePath();
- QSettings settings("QTau", c_qtau_name);
- bool autoconnect = settings.value("jack_auto_connect",true).toBool();
- _jack = new JackAudio(autoconnect);
- _audio = new AudioEngine(_jack, this);
- _audio->setUseJackTransport(true);
- _outbuf = new OutputBuffer(_jack);
- _audio->setOutputBuffer(_outbuf);
- _jackSampleRate = _jack->getSamplerate();
- connect(_outbuf, &OutputBuffer::startPlayback, this,
- &qtauController::outbuf_startPlayback);
- connect(_outbuf, &OutputBuffer::stopPlayback, this,
- &qtauController::outbuf_stopPlayback);
- QTimer *timer = new QTimer(this);
- connect(timer, SIGNAL(timeout()), this, SLOT(jackTimer()));
- timer->start(10);
- //_activeSynth = nullptr;
- //_selectedSynth = nullptr;
- setupPlugins();
- if(setupVoicebanks()==false)
- {
- QString userpath = QDir::home().filePath(".local/share/utau/voice");
- _needDownload = true;
- ArchiveDownloader* dl = new ArchiveDownloader(defa,userpath,this);
- connect(dl,&ArchiveDownloader::done,this,&qtauController::rescanVoiceDirectory);
- //FIXME: store dl -> delete after download done
- }
- }
- int qtauController::sampleRate() {
- return _jack->sampleRate();
- }
- void qtauController::startOfflinePlayback(const QString &fileName) {
- _outbuf->openReadFile(fileName);
- _lastPlay = fileName;
- startPlayback(0);
- }
- void qtauController::jackTimer() {
- char midi[1024];
- if (_jack->readMidiData(midi, 1024)) {
- int event_type = midi[0] & 0xF0;
- int note_num = midi[1];
- if (event_type == 144) {
- pianoKeyPressed(note_num);
- } else if (event_type == 128) {
- pianoKeyReleased(note_num);
- }
- }
- if (_jack->stateChanged()) {
- switch (_jack->transportState()) {
- case JackTransportStopped:
- DEVLOG_DEBUG("state_changed to JackTransportStopped");
- // XXX if(_activeSynth) _activeSynth->stopThread();
- transportPositionChanged(-1);
- break;
- case JackTransportStarting:
- DEVLOG_DEBUG("state_changed to JackTransportStarting");
- break;
- case JackTransportLooping:
- DEVLOG_DEBUG("not supported JackTransportLooping");
- break;
- #if 0
- case JackTransportNetStarting:
- DEVLOG_DEBUG("not supported JackTransportNetStarting");
- #endif
- default:
- break;
- }
- }
- int pos = _jack->positionChange();
- if (_nonzeroStart > 0) {
- pos = -1;
- _nonzeroStart--;
- DEVLOG_DEBUG("NONZERO START " + STR(_nonzeroStart));
- }
- if (pos != -1 && _audio->transportPosition() != pos) {
- _audio->setTransportPosition(pos);
- // XXX if(_activeSynth) _activeSynth->stopThread();
- _localRequestStartPlayback = false;
- }
- // FIXME: if(_jack->isRolling() || (_audio->useJackTransport()==false &&
- // _audio->localTransportRolling()))
- // transportPositionChanged(_samplesToMeasures*_audio->transportPosition());
- }
- void qtauController::outbuf_startPlayback() {
- // startPlayback(0);
- DEVLOG_DEBUG("playback is stable");
- }
- void qtauController::outbuf_stopPlayback() {
- stopPlayback();
- }
- qtauController::~qtauController() {
- delete _jack;
- delete _audio;
- delete _mainWindow;
- }
- //------------------------------------------
- static qtauController *singleton = 0;
- void qtauController::shutdown(int rc) {
- (void)rc;
- _jack->shutdown();
- _audio->shutdown(); // kind of CopyEngine
- }
- bool qtauController::run() {
- _mainWindow = new MainWindow();
- _mainWindow->show();
- newEmptySession();
- _mainWindow->setController(*this, *this->_activeSession);
- singleton = this;
- return true;
- }
- qtauController *qtauController::instance() {
- return singleton;
- }
- bool qtauController::setupVoicebanks() {
- bool found = false;
- QStringList ret;
- QStringList searchPaths;
- QString userpath = QDir::home().filePath(".local/share/utau/voice");
- searchPaths << userpath;
- if(!QFile(userpath).exists()) {
- QDir::home().mkpath(".local/share/utau/voice");
- //first start -- voices dir is empty
- }
- searchPaths << "/usr/share/utau/voice";
- searchPaths << "/usr/local/share/utau/voice";
- foreach (QString searchPath, searchPaths) {
- QDir dir(searchPath);
- QDirIterator it(dir);
- while (it.hasNext()) {
- QString vdir = it.next();
- QString file = it.fileName();
- if (file == "." || file == "..") continue;
- _voicesMap[file] = vdir;
- DEVLOG_DEBUG(vdir);
- found = true;
- }
- }
- return found;
- }
- bool qtauController::setupPlugins() {
- // FIXME plugins dir, should not be hardcoded
- _pluginsDir = QDir(qApp->applicationDirPath());
- if (_pluginsDir.cd("plugins") == false) {
- _pluginsDir = QDir(_prefix + "/lib/qtau/");
- if (_pluginsDir.cd("plugins") == false) return false;
- }
- foreach (QString fileName, _pluginsDir.entryList(QDir::Files)) {
- QPluginLoader loader(_pluginsDir.absoluteFilePath(fileName));
- QObject *plugin = loader.instance();
- if (plugin) {
- ISynth *s = qobject_cast<ISynth *>(plugin);
- if (s)
- initSynth(s);
- else {
- IPreviewSynth *ps = qobject_cast<IPreviewSynth *>(plugin);
- if (ps) {
- initPreviewSynth(ps);
- }
- }
- } else
- DEVLOG_INFO("Incompatible plugin: " + fileName +
- "reason: " + loader.errorString());
- }
- return false;
- }
- void qtauController::initPreviewSynth(IPreviewSynth *ps) {
- ps->setup(this);
- _audio->setPreviewSynth(ps);
- _preview = ps;
- }
- void qtauController::initSynth(ISynth *s) {
- if (!_synths.contains(s->name())) {
- s->setup(this);
- DEVLOG_INFO("Adding synthesizer " + s->name());
- _synths[s->name()] = s;
- _synth = s;
- } else
- DEVLOG_INFO("Synthesizer " + s->name() + " is already registered!");
- }
- void qtauController::selectSinger(QString singerName) {
- DEVLOG_DEBUG("selectSinger " + singerName);
- foreach (QString synthName, _synths.keys()) {
- _synths[synthName]->setVoicePath(_voicesMap[singerName]);
- }
- }
- void qtauController::newEmptySession() {
- _activeSession = new qtauSession(this);
- }
- //------------------------------------------
- void qtauController::addFileToRecentFiles(QString fileName) {
- DEVLOG_DEBUG("addFileToRecentFiles: " + fileName);
- QSettings settings("QTau", c_qtau_name);
- QStringList files = settings.value("recentFileList").toStringList();
- files.removeAll(fileName);
- files.prepend(fileName);
- while (files.size() > MAXRECENTFILES) files.removeLast();
- foreach (QString file, files)
- DEVLOG_DEBUG("recent: " + file);
- settings.setValue("recentFileList", files);
- _mainWindow->updateRecentFileActions();
- }
- void qtauController::onLoadUST(QString fileName) {
- if (!fileName.isEmpty()) {
- if (!_activeSession) newEmptySession();
- _activeSession->loadUST(fileName);
- addFileToRecentFiles(fileName);
- } else
- DEVLOG_WARNING("empty UST file name");
- }
- void qtauController::onSaveUST(QString fileName, bool rewrite) {
- if (_activeSession && !_activeSession->isSessionEmpty()) {
- QFile uf(fileName);
- if (uf.open(QFile::WriteOnly)) {
- addFileToRecentFiles(fileName);
- if (uf.size() == 0 || rewrite) {
- uf.reset(); // maybe it's redundant?..
- QJsonArray array;
- _activeSession->ustJson(array);
- QJsonDocument doc(array);
- uf.write(doc.toJson());
- uf.close();
- _activeSession->setFilePath(fileName);
- _activeSession->setSaved();
- DEVLOG_DEBUG("UST saved to " + fileName);
- } else
- DEVLOG_ERROR("File " + fileName + " is not empty, rewriting cancelled");
- } else
- DEVLOG_ERROR("Could not open file " + fileName + " to save UST");
- } else
- DEVLOG_ERROR("Trying to save ust from empty session!");
- }
- // new synth api required (madde)
- void qtauController::pianoKeyPressed(int keyNum) {
- _previewRunning = true;
- if (_preview) _preview->start(keyNum);
- }
- void qtauController::pianoKeyReleased(int keyNum) {
- DEVLOG_DEBUG("piano key released " + STR(keyNum));
- _previewRunning = false;
- if (_preview) _preview->stop();
- }
- void qtauController::onRequestStartPlayback() {
- _localRequestStartPlayback = true;
- QJsonArray ust;
- _activeSession->ustJson(ust);
- DEVLOG_DEBUG("create score");
- TempoMap *tempoMap = _mainWindow->getTempoMap();
- QtauScore *score = new QtauScore(ust, tempoMap);
- if (score->getNoteCount() == 0) {
- _mainWindow->onLog("Empty session - nothing to do", ELog::error);
- _jack->changeTranportState(TRANSPORT_STOP);
- return;
- }
- if (_synth == nullptr) {
- _mainWindow->onLog("No synth", ELog::error);
- _jack->changeTranportState(TRANSPORT_STOP);
- return;
- }
- if (_voicesMap.size() == 0) {
- _mainWindow->onLog("No voice source", ELog::error);
- _jack->changeTranportState(TRANSPORT_STOP);
- return;
- }
- bool result = _synth->synthesize(score);
- if (result) {
- if (0) { // synth: [refactor] cache and async transport
- QString tmp = _activeSession->documentFile();
- tmp.replace(".ustj", ".cache");
- if (tmp.length() == 0) tmp = "/tmp";
- _synth->setCacheDir(tmp);
- if (_audio->useJackTransport())
- _jack->changeTranportState(TRANSPORT_STOP);
- else
- _audio->setLocalTransportRolling(false);
- }
- if (_synth->synthIsRealtime()) {
- DEVLOG_DEBUG("schedule synth");
- _outbuf->scheduleSynth(_synth);
- startPlayback(0);
- }
- }
- }
- void qtauController::onRequestStopPlayback() {
- _localRequestStartPlayback = false;
- if (_audio->useJackTransport())
- _jack->changeTranportState(TRANSPORT_STOP);
- else {
- _audio->setLocalTransportRolling(false);
- // XXX if(_activeSynth) _activeSynth->stopThread();
- }
- }
- void qtauController::onRequestResetPlayback() {
- _localRequestStartPlayback = false;
- // XXX if(_activeSynth) _activeSynth->stopThread();
- _audio->setTransportPosition(0);
- if (_audio->useJackTransport()) {
- _jack->changeTranportState(TRANSPORT_ZERO);
- }
- }
- // from synth plugin
- void qtauController::startPlayback(float startPos) {
- //_synthrunning = true;
- // _mainWindow->onLog("start playback",ELog::info);
- if (_audio->useJackTransport() == false) {
- _audio->setLocalTransportRolling(true);
- float _sampleRate_ = _jack->sampleRate(); // FIXXME
- if (startPos) _audio->setTransportPosition(_sampleRate_ * startPos);
- } else {
- if (startPos == 0) {
- _nonzeroStart = 0;
- _jack->changeTranportState(TRANSPORT_START);
- } else {
- _nonzeroStart = 30;
- float _sampleRate_ = _jack->sampleRate();
- _audio->setTransportPosition(_sampleRate_ * startPos);
- _jack->transportStartPos(startPos);
- }
- }
- }
- void qtauController::stopPlayback() {
- _localRequestStartPlayback = false;
- //_synthrunning = false;
- //_mainWindow->onLog("stop playback",ELog::info);
- if (_audio->useJackTransport() == false) {
- _audio->setLocalTransportRolling(false);
- transportPositionChanged(-1);
- } else
- _jack->changeTranportState(TRANSPORT_STOP);
- }
- void qtauController::setJackTranportEnabled(bool enabled) {
- if (enabled == false) _jack->changeTranportState(TRANSPORT_STOP);
- _audio->setLocalTransportRolling(false);
- _jack->setUseTransport(enabled);
- _audio->setUseJackTransport(enabled);
- }
- void qtauController::updateTempoTimeSignature(int tempo) {
- (void)tempo; // FIXME
- //REMOVE
- }
- void qtauController::logError(const QString &error) {
- _mainWindow->onLog(error, ELog::error);
- }
- void qtauController::logDebug(const QString &debug) {
- _mainWindow->onLog(debug, ELog::debug);
- }
- void qtauController::logSuccess(const QString &success) {
- _mainWindow->onLog(success, ELog::success);
- }
- void qtauController::addPluginAction(QAction *action) {
- _mainWindow->addPluginAction(action);
- }
- void qtauController::openVoiceDirectory()
- {
- QString path = QDir::home().filePath(".local/share/utau/voice");
- QProcess xdg;
- xdg.start("xdg-open", QStringList() << path);
- if (!xdg.waitForStarted())
- return;
- if (!xdg.waitForFinished())
- return;
- }
- void qtauController::rescanVoiceDirectory()
- {
- if(setupVoicebanks())
- {
- emit voiceListChanged();
- }
- }
|