Controller.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  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 <QJsonDocument>
  20. #include "Controller.h"
  21. #include "PluginInterfaces.h"
  22. #include "Session.h"
  23. #include "Utils.h"
  24. #include "audio/audioengine.h"
  25. #include "audio/jackaudio.h"
  26. #include <assert.h>
  27. #include <qsettings.h>
  28. #include <score.h>
  29. #include <tempomap.h>
  30. #include <QApplication>
  31. #include <QDebug>
  32. #include <QDirIterator>
  33. #include <QPluginLoader>
  34. #include <QTimer>
  35. #include <QProcess>
  36. #include "archivedownloader.h"
  37. #include <QMessageBox>
  38. static QString defa = "https://archive.org/download/defa-vault-cv-japanese.tar/Defa%20Vault%20CV%20Japanese.tar.bz2";
  39. #define __devloglevel__ 4
  40. qtauController::qtauController(QString dir, QObject *parent)
  41. : QObject(parent),
  42. _jack(nullptr),
  43. _mainWindow(nullptr),
  44. _activeSession(nullptr) {
  45. QDir diru(dir);
  46. diru.cdUp();
  47. _prefix = diru.absolutePath();
  48. QSettings settings("QTau", c_qtau_name);
  49. bool autoconnect = settings.value("jack_auto_connect",true).toBool();
  50. _jack = new JackAudio(autoconnect);
  51. _audio = new AudioEngine(_jack, this);
  52. _audio->setUseJackTransport(true);
  53. _outbuf = new OutputBuffer(_jack);
  54. _audio->setOutputBuffer(_outbuf);
  55. _jackSampleRate = _jack->getSamplerate();
  56. connect(_outbuf, &OutputBuffer::startPlayback, this,
  57. &qtauController::outbuf_startPlayback);
  58. connect(_outbuf, &OutputBuffer::stopPlayback, this,
  59. &qtauController::outbuf_stopPlayback);
  60. QTimer *timer = new QTimer(this);
  61. connect(timer, SIGNAL(timeout()), this, SLOT(jackTimer()));
  62. timer->start(10);
  63. //_activeSynth = nullptr;
  64. //_selectedSynth = nullptr;
  65. setupPlugins();
  66. if(setupVoicebanks()==false)
  67. {
  68. QString userpath = QDir::home().filePath(".local/share/utau/voice");
  69. _needDownload = true;
  70. ArchiveDownloader* dl = new ArchiveDownloader(defa,userpath,this);
  71. connect(dl,&ArchiveDownloader::done,this,&qtauController::rescanVoiceDirectory);
  72. //FIXME: store dl -> delete after download done
  73. }
  74. }
  75. int qtauController::sampleRate() {
  76. return _jack->sampleRate();
  77. }
  78. void qtauController::startOfflinePlayback(const QString &fileName) {
  79. _outbuf->openReadFile(fileName);
  80. _lastPlay = fileName;
  81. startPlayback(0);
  82. }
  83. void qtauController::jackTimer() {
  84. char midi[1024];
  85. if (_jack->readMidiData(midi, 1024)) {
  86. int event_type = midi[0] & 0xF0;
  87. int note_num = midi[1];
  88. if (event_type == 144) {
  89. pianoKeyPressed(note_num);
  90. } else if (event_type == 128) {
  91. pianoKeyReleased(note_num);
  92. }
  93. }
  94. if (_jack->stateChanged()) {
  95. switch (_jack->transportState()) {
  96. case JackTransportStopped:
  97. DEVLOG_DEBUG("state_changed to JackTransportStopped");
  98. // XXX if(_activeSynth) _activeSynth->stopThread();
  99. transportPositionChanged(-1);
  100. break;
  101. case JackTransportStarting:
  102. DEVLOG_DEBUG("state_changed to JackTransportStarting");
  103. break;
  104. case JackTransportLooping:
  105. DEVLOG_DEBUG("not supported JackTransportLooping");
  106. break;
  107. #if 0
  108. case JackTransportNetStarting:
  109. DEVLOG_DEBUG("not supported JackTransportNetStarting");
  110. #endif
  111. default:
  112. break;
  113. }
  114. }
  115. int pos = _jack->positionChange();
  116. if (_nonzeroStart > 0) {
  117. pos = -1;
  118. _nonzeroStart--;
  119. DEVLOG_DEBUG("NONZERO START " + STR(_nonzeroStart));
  120. }
  121. if (pos != -1 && _audio->transportPosition() != pos) {
  122. _audio->setTransportPosition(pos);
  123. // XXX if(_activeSynth) _activeSynth->stopThread();
  124. _localRequestStartPlayback = false;
  125. }
  126. // FIXME: if(_jack->isRolling() || (_audio->useJackTransport()==false &&
  127. // _audio->localTransportRolling()))
  128. // transportPositionChanged(_samplesToMeasures*_audio->transportPosition());
  129. }
  130. void qtauController::outbuf_startPlayback() {
  131. // startPlayback(0);
  132. DEVLOG_DEBUG("playback is stable");
  133. }
  134. void qtauController::outbuf_stopPlayback() {
  135. stopPlayback();
  136. }
  137. qtauController::~qtauController() {
  138. delete _jack;
  139. delete _audio;
  140. delete _mainWindow;
  141. }
  142. //------------------------------------------
  143. static qtauController *singleton = 0;
  144. void qtauController::shutdown(int rc) {
  145. (void)rc;
  146. _jack->shutdown();
  147. _audio->shutdown(); // kind of CopyEngine
  148. }
  149. bool qtauController::run() {
  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. return singleton;
  159. }
  160. bool qtauController::setupVoicebanks() {
  161. bool found = false;
  162. QStringList ret;
  163. QStringList searchPaths;
  164. QString userpath = QDir::home().filePath(".local/share/utau/voice");
  165. searchPaths << userpath;
  166. if(!QFile(userpath).exists()) {
  167. QDir::home().mkpath(".local/share/utau/voice");
  168. //first start -- voices dir is empty
  169. }
  170. searchPaths << "/usr/share/utau/voice";
  171. searchPaths << "/usr/local/share/utau/voice";
  172. foreach (QString searchPath, searchPaths) {
  173. QDir dir(searchPath);
  174. QDirIterator it(dir);
  175. while (it.hasNext()) {
  176. QString vdir = it.next();
  177. QString file = it.fileName();
  178. if (file == "." || file == "..") continue;
  179. _voicesMap[file] = vdir;
  180. DEVLOG_DEBUG(vdir);
  181. found = true;
  182. }
  183. }
  184. return found;
  185. }
  186. bool qtauController::setupPlugins() {
  187. // FIXME plugins dir, should not be hardcoded
  188. _pluginsDir = QDir(qApp->applicationDirPath());
  189. if (_pluginsDir.cd("plugins") == false) {
  190. _pluginsDir = QDir(_prefix + "/lib/qtau/");
  191. if (_pluginsDir.cd("plugins") == false) return false;
  192. }
  193. foreach (QString fileName, _pluginsDir.entryList(QDir::Files)) {
  194. QPluginLoader loader(_pluginsDir.absoluteFilePath(fileName));
  195. QObject *plugin = loader.instance();
  196. if (plugin) {
  197. ISynth *s = qobject_cast<ISynth *>(plugin);
  198. if (s)
  199. initSynth(s);
  200. else {
  201. IPreviewSynth *ps = qobject_cast<IPreviewSynth *>(plugin);
  202. if (ps) {
  203. initPreviewSynth(ps);
  204. }
  205. }
  206. } else
  207. DEVLOG_INFO("Incompatible plugin: " + fileName +
  208. "reason: " + loader.errorString());
  209. }
  210. return false;
  211. }
  212. void qtauController::initPreviewSynth(IPreviewSynth *ps) {
  213. ps->setup(this);
  214. _audio->setPreviewSynth(ps);
  215. _preview = ps;
  216. }
  217. void qtauController::initSynth(ISynth *s) {
  218. if (!_synths.contains(s->name())) {
  219. s->setup(this);
  220. DEVLOG_INFO("Adding synthesizer " + s->name());
  221. _synths[s->name()] = s;
  222. _synth = s;
  223. } else
  224. DEVLOG_INFO("Synthesizer " + s->name() + " is already registered!");
  225. }
  226. void qtauController::selectSinger(QString singerName) {
  227. DEVLOG_DEBUG("selectSinger " + singerName);
  228. foreach (QString synthName, _synths.keys()) {
  229. _synths[synthName]->setVoicePath(_voicesMap[singerName]);
  230. }
  231. }
  232. void qtauController::newEmptySession() {
  233. _activeSession = new qtauSession(this);
  234. }
  235. //------------------------------------------
  236. void qtauController::addFileToRecentFiles(QString fileName) {
  237. DEVLOG_DEBUG("addFileToRecentFiles: " + fileName);
  238. QSettings settings("QTau", c_qtau_name);
  239. QStringList files = settings.value("recentFileList").toStringList();
  240. files.removeAll(fileName);
  241. files.prepend(fileName);
  242. while (files.size() > MAXRECENTFILES) files.removeLast();
  243. foreach (QString file, files)
  244. DEVLOG_DEBUG("recent: " + file);
  245. settings.setValue("recentFileList", files);
  246. _mainWindow->updateRecentFileActions();
  247. }
  248. void qtauController::onLoadUST(QString fileName) {
  249. if (!fileName.isEmpty()) {
  250. if (!_activeSession) newEmptySession();
  251. _activeSession->loadUST(fileName);
  252. addFileToRecentFiles(fileName);
  253. } else
  254. DEVLOG_WARNING("empty UST file name");
  255. }
  256. void qtauController::onSaveUST(QString fileName, bool rewrite) {
  257. if (_activeSession && !_activeSession->isSessionEmpty()) {
  258. QFile uf(fileName);
  259. if (uf.open(QFile::WriteOnly)) {
  260. addFileToRecentFiles(fileName);
  261. if (uf.size() == 0 || rewrite) {
  262. uf.reset(); // maybe it's redundant?..
  263. QJsonArray array;
  264. _activeSession->ustJson(array);
  265. QJsonDocument doc(array);
  266. uf.write(doc.toJson());
  267. uf.close();
  268. _activeSession->setFilePath(fileName);
  269. _activeSession->setSaved();
  270. DEVLOG_DEBUG("UST saved to " + fileName);
  271. } else
  272. DEVLOG_ERROR("File " + fileName + " is not empty, rewriting cancelled");
  273. } else
  274. DEVLOG_ERROR("Could not open file " + fileName + " to save UST");
  275. } else
  276. DEVLOG_ERROR("Trying to save ust from empty session!");
  277. }
  278. // new synth api required (madde)
  279. void qtauController::pianoKeyPressed(int keyNum) {
  280. _previewRunning = true;
  281. if (_preview) _preview->start(keyNum);
  282. }
  283. void qtauController::pianoKeyReleased(int keyNum) {
  284. DEVLOG_DEBUG("piano key released " + STR(keyNum));
  285. _previewRunning = false;
  286. if (_preview) _preview->stop();
  287. }
  288. void qtauController::onRequestStartPlayback() {
  289. _localRequestStartPlayback = true;
  290. QJsonArray ust;
  291. _activeSession->ustJson(ust);
  292. DEVLOG_DEBUG("create score");
  293. TempoMap *tempoMap = _mainWindow->getTempoMap();
  294. QtauScore *score = new QtauScore(ust, tempoMap);
  295. if (score->getNoteCount() == 0) {
  296. _mainWindow->onLog("Empty session - nothing to do", ELog::error);
  297. _jack->changeTranportState(TRANSPORT_STOP);
  298. return;
  299. }
  300. if (_synth == nullptr) {
  301. _mainWindow->onLog("No synth", ELog::error);
  302. _jack->changeTranportState(TRANSPORT_STOP);
  303. return;
  304. }
  305. if (_voicesMap.size() == 0) {
  306. _mainWindow->onLog("No voice source", ELog::error);
  307. _jack->changeTranportState(TRANSPORT_STOP);
  308. return;
  309. }
  310. bool result = _synth->synthesize(score);
  311. if (result) {
  312. if (0) { // synth: [refactor] cache and async transport
  313. QString tmp = _activeSession->documentFile();
  314. tmp.replace(".ustj", ".cache");
  315. if (tmp.length() == 0) tmp = "/tmp";
  316. _synth->setCacheDir(tmp);
  317. if (_audio->useJackTransport())
  318. _jack->changeTranportState(TRANSPORT_STOP);
  319. else
  320. _audio->setLocalTransportRolling(false);
  321. }
  322. if (_synth->synthIsRealtime()) {
  323. DEVLOG_DEBUG("schedule synth");
  324. _outbuf->scheduleSynth(_synth);
  325. startPlayback(0);
  326. }
  327. }
  328. }
  329. void qtauController::onRequestStopPlayback() {
  330. _localRequestStartPlayback = false;
  331. if (_audio->useJackTransport())
  332. _jack->changeTranportState(TRANSPORT_STOP);
  333. else {
  334. _audio->setLocalTransportRolling(false);
  335. // XXX if(_activeSynth) _activeSynth->stopThread();
  336. }
  337. }
  338. void qtauController::onRequestResetPlayback() {
  339. _localRequestStartPlayback = false;
  340. // XXX if(_activeSynth) _activeSynth->stopThread();
  341. _audio->setTransportPosition(0);
  342. if (_audio->useJackTransport()) {
  343. _jack->changeTranportState(TRANSPORT_ZERO);
  344. }
  345. }
  346. // from synth plugin
  347. void qtauController::startPlayback(float startPos) {
  348. //_synthrunning = true;
  349. // _mainWindow->onLog("start playback",ELog::info);
  350. if (_audio->useJackTransport() == false) {
  351. _audio->setLocalTransportRolling(true);
  352. float _sampleRate_ = _jack->sampleRate(); // FIXXME
  353. if (startPos) _audio->setTransportPosition(_sampleRate_ * startPos);
  354. } else {
  355. if (startPos == 0) {
  356. _nonzeroStart = 0;
  357. _jack->changeTranportState(TRANSPORT_START);
  358. } else {
  359. _nonzeroStart = 30;
  360. float _sampleRate_ = _jack->sampleRate();
  361. _audio->setTransportPosition(_sampleRate_ * startPos);
  362. _jack->transportStartPos(startPos);
  363. }
  364. }
  365. }
  366. void qtauController::stopPlayback() {
  367. _localRequestStartPlayback = false;
  368. //_synthrunning = false;
  369. //_mainWindow->onLog("stop playback",ELog::info);
  370. if (_audio->useJackTransport() == false) {
  371. _audio->setLocalTransportRolling(false);
  372. transportPositionChanged(-1);
  373. } else
  374. _jack->changeTranportState(TRANSPORT_STOP);
  375. }
  376. void qtauController::setJackTranportEnabled(bool enabled) {
  377. if (enabled == false) _jack->changeTranportState(TRANSPORT_STOP);
  378. _audio->setLocalTransportRolling(false);
  379. _jack->setUseTransport(enabled);
  380. _audio->setUseJackTransport(enabled);
  381. }
  382. void qtauController::updateTempoTimeSignature(int tempo) {
  383. (void)tempo; // FIXME
  384. //REMOVE
  385. }
  386. void qtauController::logError(const QString &error) {
  387. _mainWindow->onLog(error, ELog::error);
  388. }
  389. void qtauController::logDebug(const QString &debug) {
  390. _mainWindow->onLog(debug, ELog::debug);
  391. }
  392. void qtauController::logSuccess(const QString &success) {
  393. _mainWindow->onLog(success, ELog::success);
  394. }
  395. void qtauController::addPluginAction(QAction *action) {
  396. _mainWindow->addPluginAction(action);
  397. }
  398. void qtauController::openVoiceDirectory()
  399. {
  400. QString path = QDir::home().filePath(".local/share/utau/voice");
  401. QProcess xdg;
  402. xdg.start("xdg-open", QStringList() << path);
  403. if (!xdg.waitForStarted())
  404. return;
  405. if (!xdg.waitForFinished())
  406. return;
  407. }
  408. void qtauController::rescanVoiceDirectory()
  409. {
  410. if(setupVoicebanks())
  411. {
  412. emit voiceListChanged();
  413. }
  414. }