Controller.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. /*
  2. This file is part of QTau
  3. Copyright (C) 2013-2018 Tobias "Tomoko" Platen <tplaten@posteo.de>
  4. Copyright (C) 2013 digited <https://github.com/digited>
  5. Copyright (C) 2010-2013 HAL@ShurabaP <https://github.com/haruneko>
  6. QTau is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. SPDX-License-Identifier: GPL-3.0+
  17. */
  18. #include "mainwindow.h"
  19. #include "Session.h"
  20. #include "Controller.h"
  21. #include "PluginInterfaces.h"
  22. #include "Utils.h"
  23. #include <QJsonDocument>
  24. #include "audio/jackaudio.h"
  25. #include "audio/audioengine.h"
  26. #include <QApplication>
  27. #include <QPluginLoader>
  28. #include <QDebug>
  29. #include <QTimer>
  30. #include <qsettings.h>
  31. #define __devloglevel__ 4
  32. qtauController::qtauController(QString dir,QObject *parent) :
  33. QObject(parent), _jack(nullptr), _mainWindow(nullptr), _activeSession(nullptr)
  34. {
  35. QDir diru(dir);
  36. diru.cdUp();
  37. _prefix = diru.absolutePath();
  38. QSettings settings;
  39. bool autoconnect = settings.value("jackAutoConnect").toBool();
  40. autoconnect = true;//FIXME: hack until we have setting panel
  41. _jack = new JackAudio(autoconnect);
  42. _audio = new AudioEngine(_jack,this);
  43. _audio->setUseJackTransport(true);
  44. _outbuf = new OutputBuffer(_jack);
  45. _audio->setOutputBuffer(_outbuf);
  46. _jackSampleRate=_jack->getSamplerate();
  47. connect(_outbuf,&OutputBuffer::startPlayback,this,&qtauController::outbuf_startPlayback);
  48. connect(_outbuf,&OutputBuffer::stopPlayback,this,&qtauController::outbuf_stopPlayback);
  49. QTimer *timer = new QTimer(this);
  50. connect(timer, SIGNAL(timeout()), this, SLOT(jackTimer()));
  51. timer->start(10);
  52. _activeSynth = nullptr;
  53. _selectedSynth = nullptr;
  54. float bpm=120; //TODO hardcoded in editor, change settings on UST load
  55. int numerator=4;
  56. int denominator=4;
  57. _samplesToMeasures = (bpm*numerator)/(denominator*_jackSampleRate*60);
  58. setupTranslations();
  59. setupPlugins();
  60. }
  61. int qtauController::sampleRate()
  62. {
  63. return _jack->sampleRate();
  64. }
  65. void qtauController::startOfflinePlayback(const QString &fileName)
  66. {
  67. _outbuf->openReadFile(fileName);
  68. _lastPlay = fileName;
  69. startPlayback(0);
  70. }
  71. void qtauController::jackTimer()
  72. {
  73. char midi[1024];
  74. if(_jack->readMidiData(midi,1024))
  75. {
  76. int event_type = midi[0] & 0xF0;
  77. int note_num = midi[1];
  78. if(event_type==144)
  79. {
  80. pianoKeyPressed(note_num);
  81. }
  82. else if(event_type==128)
  83. {
  84. pianoKeyReleased(note_num);
  85. }
  86. }
  87. if(_jack->stateChanged())
  88. {
  89. switch(_jack->transportState())
  90. {
  91. case JackTransportStopped:
  92. DEVLOG_DEBUG("state_changed to JackTransportStopped");
  93. //XXX if(_activeSynth) _activeSynth->stopThread();
  94. transportPositionChanged(-1);
  95. break;
  96. case JackTransportStarting:
  97. DEVLOG_DEBUG("state_changed to JackTransportStarting");
  98. break;
  99. case JackTransportLooping:
  100. DEVLOG_DEBUG("not supported JackTransportLooping");
  101. break;
  102. #ifdef TRISQUEL7
  103. case JackTransportNetStarting:
  104. DEVLOG_DEBUG("not supported JackTransportNetStarting");
  105. #endif
  106. default:
  107. break;
  108. }
  109. }
  110. int pos = _jack->positionChange();
  111. if(_nonzeroStart>0) {
  112. pos = -1;
  113. _nonzeroStart--;
  114. DEVLOG_DEBUG("NONZERO START "+STR(_nonzeroStart));
  115. }
  116. if(pos!=-1 && _audio->transportPosition()!=pos)
  117. {
  118. _audio->setTransportPosition(pos);
  119. //XXX if(_activeSynth) _activeSynth->stopThread();
  120. _localRequestStartPlayback = false;
  121. }
  122. if(_jack->isRolling() || (_audio->useJackTransport()==false && _audio->localTransportRolling())) transportPositionChanged(_samplesToMeasures*_audio->transportPosition());
  123. }
  124. void qtauController::outbuf_startPlayback()
  125. {
  126. // startPlayback(0);
  127. DEVLOG_DEBUG("playback is stable");
  128. }
  129. void qtauController::outbuf_stopPlayback()
  130. {
  131. stopPlayback();
  132. }
  133. qtauController::~qtauController()
  134. {
  135. delete _jack;
  136. //TODO --> shutdown audio -> crash here
  137. delete _audio;
  138. delete _mainWindow;
  139. }
  140. //------------------------------------------
  141. static qtauController* singleton=0;
  142. void qtauController::shutdown(int rc)
  143. {
  144. (void) rc;
  145. _jack->shutdown();
  146. _audio->shutdown();//kind of CopyEngine
  147. }
  148. bool qtauController::run()
  149. {
  150. _mainWindow = new MainWindow();
  151. _mainWindow->show();
  152. newEmptySession();
  153. _mainWindow->setController(*this, *this->_activeSession);
  154. singleton = this;
  155. return true;
  156. }
  157. qtauController* qtauController::instance()
  158. {
  159. return singleton;
  160. }
  161. bool qtauController::setupTranslations()
  162. {
  163. //FIXME: english only
  164. return false;
  165. }
  166. bool qtauController::setupPlugins()
  167. {
  168. //FIXME plugins dir, should not be hardcoded
  169. _pluginsDir = QDir(qApp->applicationDirPath());
  170. if(_pluginsDir.cd("plugins")==false) {
  171. _pluginsDir = QDir(_prefix+"/lib/qtau/");
  172. if(_pluginsDir.cd("plugins")==false) return false;
  173. }
  174. foreach (QString fileName, _pluginsDir.entryList(QDir::Files))
  175. {
  176. QPluginLoader loader(_pluginsDir.absoluteFilePath(fileName));
  177. QObject *plugin = loader.instance();
  178. if (plugin)
  179. {
  180. ISynth* s = qobject_cast<ISynth*>(plugin);
  181. if (s)
  182. initSynth(s);
  183. else
  184. {
  185. IPreviewSynth* ps = qobject_cast<IPreviewSynth*>(plugin);
  186. if(ps)
  187. {
  188. initPreviewSynth(ps);
  189. }
  190. }
  191. }
  192. else DEVLOG_INFO("Incompatible plugin: " + fileName+ "reason: "+loader.errorString());
  193. }
  194. return false;
  195. }
  196. void qtauController::initPreviewSynth(IPreviewSynth *ps)
  197. {
  198. ps->setup(this);
  199. _audio->setPreviewSynth(ps);
  200. _preview=ps;
  201. }
  202. void qtauController::initSynth(ISynth *s)
  203. {
  204. if (!_synths.contains(s->name()))
  205. {
  206. s->setup(this);
  207. DEVLOG_INFO("Adding synthesizer " + s->name());
  208. _synths[s->name()] = s;
  209. _voices.append(s->listVoices());
  210. }
  211. else DEVLOG_INFO("Synthesizer " + s->name() + " is already registered!");
  212. }
  213. void qtauController::selectSinger(QString singerName)
  214. {
  215. foreach (QString synthName, _synths.keys()) {
  216. if(_synths[synthName]->setVoice(singerName))
  217. {
  218. DEVLOG_DEBUG("setSynth: "+synthName)
  219. _selectedSynth = _synths[synthName];
  220. }
  221. }
  222. }
  223. void qtauController::newEmptySession()
  224. {
  225. _activeSession = new qtauSession(this);
  226. }
  227. //------------------------------------------
  228. void qtauController::addFileToRecentFiles(QString fileName)
  229. {
  230. DEVLOG_DEBUG("addFileToRecentFiles: "+fileName);
  231. QSettings settings;
  232. QStringList files = settings.value("recentFileList").toStringList();
  233. files.removeAll(fileName);
  234. files.prepend(fileName);
  235. while (files.size() > MAXRECENTFILES)
  236. files.removeLast();
  237. foreach(QString file, files)
  238. DEVLOG_DEBUG("recent: "+file);
  239. settings.setValue("recentFileList", files);
  240. _mainWindow->updateRecentFileActions();
  241. }
  242. void qtauController::onLoadUST(QString fileName)
  243. {
  244. if (!fileName.isEmpty())
  245. {
  246. if (!_activeSession)
  247. newEmptySession();
  248. _activeSession->loadUST(fileName);
  249. addFileToRecentFiles(fileName);
  250. }
  251. else DEVLOG_WARNING("empty UST file name");
  252. }
  253. void qtauController::onSaveUST(QString fileName, bool rewrite)
  254. {
  255. if (_activeSession && !_activeSession->isSessionEmpty())
  256. {
  257. QFile uf(fileName);
  258. if (uf.open(QFile::WriteOnly))
  259. {
  260. addFileToRecentFiles(fileName);
  261. if (uf.size() == 0 || rewrite)
  262. {
  263. uf.reset(); // maybe it's redundant?..
  264. QJsonArray array;
  265. _activeSession->ustJson(array);
  266. QJsonDocument doc(array);
  267. uf.write(doc.toJson());
  268. uf.close();
  269. _activeSession->setFilePath(fileName);
  270. _activeSession->setSaved();
  271. DEVLOG_DEBUG("UST saved to " + fileName);
  272. }
  273. else DEVLOG_ERROR("File " + fileName + " is not empty, rewriting cancelled");
  274. }
  275. else DEVLOG_ERROR("Could not open file " + fileName + " to save UST");
  276. }
  277. else DEVLOG_ERROR("Trying to save ust from empty session!");
  278. }
  279. //new synth api required (madde)
  280. void qtauController::pianoKeyPressed(int keyNum)
  281. {
  282. _previewRunning=true;
  283. if(_preview) _preview->start(keyNum);
  284. }
  285. void qtauController::pianoKeyReleased(int keyNum)
  286. {
  287. DEVLOG_DEBUG("piano key released "+STR(keyNum));
  288. _previewRunning=false;
  289. if(_preview) _preview->stop();
  290. }
  291. bool qtauController::validateScore(const QJsonArray& ust)
  292. {
  293. // struct selectionRange sel = _mainWindow->getSelectionRange();
  294. //XXX if(_activeSynth) _activeSynth->setPlaybackSelection(sel);
  295. int tempo = 0;
  296. int lastNoteEnd=0;
  297. int noteCount = 0;
  298. for (int i = 0; i < ust.count(); ++i)
  299. {
  300. auto o = ust[i].toObject();
  301. if(!o.contains(NOTE_KEY_NUMBER)) {
  302. QJsonDocument doc(o);
  303. tempo=o[TEMPO].toInt();
  304. continue;
  305. }
  306. noteCount++;
  307. int noteOffset = o[NOTE_PULSE_OFFSET].toInt();
  308. int noteLength = o[NOTE_PULSE_LENGTH].toInt();
  309. int notenum = o[NOTE_KEY_NUMBER].toInt();
  310. QString lyric = o[NOTE_LYRIC].toString();
  311. int rest = noteOffset-lastNoteEnd;
  312. if(rest<0)
  313. {
  314. DEVLOG_ERROR("overlapping notes "+STR(i)+" "+STR(i-1));
  315. //SET color to red
  316. auto o = ust[i].toObject();
  317. auto o2 = ust[i-1].toObject();
  318. _mainWindow->markOverlappingNotes(_activeSession->getNote(o),_activeSession->getNote(o2));
  319. return false;
  320. }
  321. if(notenum<0 && notenum>127) return false;
  322. lastNoteEnd = noteOffset+noteLength;
  323. }
  324. if(noteCount==0) return false; //empty score
  325. if(tempo==0) return false;
  326. _mainWindow->updateNoteColors();
  327. return true;
  328. }
  329. void qtauController::onRequestStartPlayback()
  330. {
  331. _localRequestStartPlayback=true;
  332. QJsonArray ust;
  333. _activeSession->ustJson(ust);
  334. foreach (QString synthName, _synths.keys()) {
  335. //XXX _synths[synthName]->stopThread();
  336. //only one synth can be scheduled
  337. }
  338. if(_activeSynth!=_selectedSynth)
  339. {
  340. //XXX if(_activeSynth) _activeSynth->stopThread();
  341. _activeSynth = _selectedSynth;
  342. DEVLOG_DEBUG("synth selection");
  343. }
  344. if(validateScore(ust))
  345. {
  346. //if(!_synthrunning)
  347. {
  348. QJsonArray ust;
  349. _activeSession->ustJson(ust);
  350. int noteCount=0;
  351. for (int i = 0; i < ust.count(); ++i)
  352. {
  353. auto o = ust[i].toObject();
  354. if(!o.contains(NOTE_KEY_NUMBER)) {
  355. continue;
  356. }
  357. noteCount++;
  358. }
  359. if(noteCount==0)
  360. {
  361. _mainWindow->onLog("empty session - nothing to do",ELog::error);
  362. _jack->changeTranportState(TRANSPORT_STOP);
  363. }
  364. else
  365. {
  366. _activeSynth->setScore(ust);
  367. DEVLOG_ERROR("synth start set seek\n");
  368. if(_audio->useJackTransport()) _jack->changeTranportState(TRANSPORT_STOP);
  369. else _audio->setLocalTransportRolling(false);
  370. QString tmp = _activeSession->documentFile();
  371. tmp.replace(".ustj",".cache");
  372. if(tmp.length()==0) tmp="/tmp";
  373. _activeSynth->setCacheDir(tmp);
  374. bool ready = _activeSynth->synthesize();//start synth
  375. if(ready)
  376. {
  377. if(_activeSynth->synthIsRealtime())
  378. {
  379. DEVLOG_DEBUG("schedule synth");
  380. _outbuf->scheduleSynth(_activeSynth);
  381. startPlayback(0);
  382. }
  383. else
  384. {
  385. DEVLOG_DEBUG("TODO:: synth must start playback");
  386. _mainWindow->onLog("synth running offline",ELog::info);
  387. }
  388. }
  389. }
  390. }
  391. }
  392. }
  393. void qtauController::onRequestStopPlayback()
  394. {
  395. _localRequestStartPlayback = false;
  396. if(_audio->useJackTransport()) _jack->changeTranportState(TRANSPORT_STOP);
  397. else {
  398. _audio->setLocalTransportRolling(false);
  399. //XXX if(_activeSynth) _activeSynth->stopThread();
  400. }
  401. }
  402. void qtauController::onRequestResetPlayback()
  403. {
  404. _localRequestStartPlayback = false;
  405. // XXX if(_activeSynth) _activeSynth->stopThread();
  406. _audio->setTransportPosition(0);
  407. if(_audio->useJackTransport())
  408. {
  409. _jack->changeTranportState(TRANSPORT_ZERO);
  410. }
  411. }
  412. //from synth plugin
  413. void qtauController::startPlayback(float startPos)
  414. {
  415. //_synthrunning = true;
  416. _mainWindow->onLog("start playback",ELog::info);
  417. if(_audio->useJackTransport()==false)
  418. {
  419. _audio->setLocalTransportRolling(true);
  420. float _sampleRate_ = _jack->sampleRate(); //FIXXME
  421. if(startPos) _audio->setTransportPosition(_sampleRate_*startPos);
  422. }
  423. else
  424. {
  425. if(startPos==0)
  426. {
  427. _nonzeroStart=0;
  428. _jack->changeTranportState(TRANSPORT_START);
  429. }
  430. else
  431. {
  432. _nonzeroStart=30;
  433. float _sampleRate_ = _jack->sampleRate();
  434. _audio->setTransportPosition(_sampleRate_*startPos);
  435. _jack->transportStartPos(startPos);
  436. }
  437. }
  438. }
  439. void qtauController::stopPlayback()
  440. {
  441. _localRequestStartPlayback=false;
  442. //_synthrunning = false;
  443. _mainWindow->onLog("stop playback",ELog::info);
  444. if(_audio->useJackTransport()==false)
  445. {
  446. _audio->setLocalTransportRolling(false);
  447. transportPositionChanged(-1);
  448. }
  449. else
  450. _jack->changeTranportState(TRANSPORT_STOP);
  451. }
  452. void qtauController::setJackTranportEnabled(bool enabled)
  453. {
  454. if(enabled==false)
  455. _jack->changeTranportState(TRANSPORT_STOP);
  456. _audio->setLocalTransportRolling(false);
  457. _jack->setUseTransport(enabled);
  458. _audio->setUseJackTransport(enabled);
  459. }
  460. void qtauController::updateTempoTimeSignature(int tempo)
  461. {
  462. float bpm=tempo;
  463. _samplesToMeasures = bpm/(_jackSampleRate*60);
  464. }
  465. void qtauController::logError(const QString &error)
  466. {
  467. _mainWindow->onLog(error,ELog::error);
  468. }
  469. void qtauController::logDebug(const QString &debug)
  470. {
  471. _mainWindow->onLog(debug,ELog::debug);
  472. }
  473. void qtauController::logSuccess(const QString &success)
  474. {
  475. _mainWindow->onLog(success,ELog::success);
  476. }
  477. void qtauController::addPluginAction(QAction *action)
  478. {
  479. _mainWindow->addPluginAction(action);
  480. }
  481. #if 0
  482. void qtauController::startThread(IThreaded *threaded)
  483. {
  484. WorkerThread *workerThread = new WorkerThread(threaded);
  485. workerThread->start();
  486. connect(workerThread,&WorkerThread::resultReady,workerThread,&WorkerThread::threadEnd);
  487. connect(workerThread,&WorkerThread::finished,workerThread,&WorkerThread::threadEnd2);
  488. }
  489. #endif