123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- /*
- 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 "Session.h"
- #include "Controller.h"
- #include "PluginInterfaces.h"
- #include "Utils.h"
- #include <QJsonDocument>
- #include "audio/jackaudio.h"
- #include "audio/audioengine.h"
- #include <QApplication>
- #include <QPluginLoader>
- #include <QDebug>
- #include <QTimer>
- #include <qsettings.h>
- #include <tempomap.h>
- #include <score.h>
- #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;
- bool autoconnect = settings.value("jack_auto_connect").toBool();
- QString startup_script = settings.value("startup_script").toString();
- if(startup_script.length())
- system(startup_script.toUtf8().data());
- //DO once
- //settings.setValue("startup_script","/home/isengaara/Hacking/Audio/Ongakunix/scripts/connect_qtau &");
- //settings.setValue("jack_auto_connect",false);
- //settings.sync();
- _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;
- setupTranslations();
- setupPlugins();
- }
- 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;
- case JackTransportNetStarting:
- DEVLOG_DEBUG("not supported JackTransportNetStarting");
- 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;
- //TODO --> shutdown audio -> crash here
- 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::setupTranslations()
- {
- //FIXME: english only
- return false;
- }
- 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;
- _voices.append(s->listVoices());
- }
- else DEVLOG_INFO("Synthesizer " + s->name() + " is already registered!");
- }
- void qtauController::selectSinger(QString singerName)
- {
- foreach (QString synthName, _synths.keys()) {
- if(_synths[synthName]->setVoice(singerName))
- {
- // DEVLOG_DEBUG("setSynth: "+synthName)
- _selectedSynth = _synths[synthName];
- }
- }
- }
- void qtauController::newEmptySession()
- {
- _activeSession = new qtauSession(this);
- }
- //------------------------------------------
- void qtauController::addFileToRecentFiles(QString fileName)
- {
- DEVLOG_DEBUG("addFileToRecentFiles: "+fileName);
- QSettings settings;
- 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();
- }
- #if 0
- bool qtauController::validateScore(const QJsonArray& ust)
- {
- // struct selectionRange sel = _mainWindow->getSelectionRange();
- //XXX if(_activeSynth) _activeSynth->setPlaybackSelection(sel);
- int tempo = 0;
- int lastNoteEnd=0;
- int noteCount = 0;
- for (int i = 0; i < ust.count(); ++i)
- {
- auto o = ust[i].toObject();
- if(!o.contains(NOTE_KEY_NUMBER)) {
- QJsonDocument doc(o);
- tempo=o[TEMPO].toInt();
- continue;
- }
- noteCount++;
- int noteOffset = o[NOTE_PULSE_OFFSET].toInt();
- int noteLength = o[NOTE_PULSE_LENGTH].toInt();
- int notenum = o[NOTE_KEY_NUMBER].toInt();
- QString lyric = o[NOTE_LYRIC].toString();
- int rest = noteOffset-lastNoteEnd;
- if(rest<0)
- {
- DEVLOG_ERROR("overlapping notes "+STR(i)+" "+STR(i-1));
- //SET color to red
- auto o = ust[i].toObject();
- auto o2 = ust[i-1].toObject();
- _mainWindow->markOverlappingNotes(_activeSession->getNote(o),_activeSession->getNote(o2));
- return false;
- }
- if(notenum<0 && notenum>127) return false;
- lastNoteEnd = noteOffset+noteLength;
- }
- if(noteCount==0) return false; //empty score
- if(tempo==0) return false;
- _mainWindow->updateNoteColors();
- return true;
- }
- #endif
- void qtauController::onRequestStartPlayback()
- {
- _localRequestStartPlayback=true;
- QJsonArray ust;
- _activeSession->ustJson(ust);
- if(_activeSynth!=_selectedSynth)
- {
- //XXX if(_activeSynth) _activeSynth->stopThread();
- _activeSynth = _selectedSynth;
- DEVLOG_DEBUG("synth selection");
- }
- 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;
- }
- bool result = _activeSynth->synthesize(score);
- if(result)
- {
- #if 0
- QString tmp = _activeSession->documentFile();
- tmp.replace(".ustj",".cache");
- if(tmp.length()==0) tmp="/tmp";
- _activeSynth->setCacheDir(tmp);
- if(_audio->useJackTransport()) _jack->changeTranportState(TRANSPORT_STOP);
- else _audio->setLocalTransportRolling(false);
- #endif
- if(_activeSynth->synthIsRealtime())
- {
- DEVLOG_DEBUG("schedule synth");
- _outbuf->scheduleSynth(_activeSynth);
- startPlayback(0);
- }
- else
- {
- DEVLOG_DEBUG("TODO:: synth must start playback");
- //_mainWindow->onLog("synth running offline",ELog::info);
- }
- }
- }
- 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
- //float bpm=tempo;
- //_samplesToMeasures = bpm/(_jackSampleRate*60);
- }
- 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);
- }
- #if 0
- void qtauController::startThread(IThreaded *threaded)
- {
- WorkerThread *workerThread = new WorkerThread(threaded);
- workerThread->start();
- connect(workerThread,&WorkerThread::resultReady,workerThread,&WorkerThread::threadEnd);
- connect(workerThread,&WorkerThread::finished,workerThread,&WorkerThread::threadEnd2);
- }
- #endif
|