ecantorix_synth.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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 "ecantorix_synth.h"
  19. #include "../editor/ustjkeys.h"
  20. #include <QFileInfo>
  21. #include <QDebug>
  22. #include <QDir>
  23. #include <QFile>
  24. #include <QTextStream>
  25. #include <QDebug>
  26. #include <QDirIterator>
  27. #include <QStringList>
  28. #include <QJsonDocument>
  29. #include <math.h>
  30. #include <stdlib.h>
  31. #include <stdio.h>
  32. #include <QProcess>
  33. #include <QtConcurrent/QtConcurrent>
  34. #include <sndfile.h>
  35. #include <unistd.h>
  36. #define __devloglevel__ 4
  37. //utilityfunctions -- move to libkawaii
  38. bool fileExists(QString path) {
  39. QFileInfo check_file(path);
  40. // check if file exists and if yes: Is it really a file and no directory?
  41. return check_file.exists() && check_file.isFile();
  42. }
  43. //ecantorix synth specific
  44. /// manifest
  45. QString eCantorixSynth::name() { return "eCantorix2"; }
  46. QString eCantorixSynth::description() { return "A multilingual KTH-style singing synthesizer"; }
  47. QString eCantorixSynth::version() { return "18.04"; }
  48. /// setup
  49. void eCantorixSynth::setup(IController* ctrl) {
  50. this->_ctrl = ctrl;
  51. this->_jack_samplerate = ctrl->sampleRate();
  52. //thread queue signaling
  53. connect(this,&eCantorixSynth::logDebug,this,&eCantorixSynth::on_logDebug);
  54. connect(this,&eCantorixSynth::logError,this,&eCantorixSynth::on_logError);
  55. connect(this,&eCantorixSynth::logSuccess,this,&eCantorixSynth::on_logSuccess);
  56. _voices["Mikulas"]="cs";
  57. _voices["Lenny"]="m1";
  58. _voices["Lukas"]="sv";
  59. _voices["Mika"]="fi";
  60. }
  61. /// input
  62. bool eCantorixSynth::setScore(const QJsonArray &s)
  63. {
  64. if(_synthprocess)
  65. {
  66. DEVLOG_ERROR("threadRunning - cannot set score");
  67. return false;
  68. }
  69. _score = s;
  70. return true;
  71. }
  72. //have resampling output engine
  73. bool eCantorixSynth::synthesize()// refactor interace
  74. {
  75. if(_synthprocess)
  76. {
  77. DEVLOG_ERROR("synthprocess already running\n");
  78. return false;
  79. }
  80. builduScore();
  81. QString program = "sinsyNG";
  82. QStringList arguments;
  83. arguments << "-u" << "/tmp/uscore.sinsy";
  84. if(_isRealtime)
  85. {
  86. arguments << "-q" << "/tmp/sinsy.rbuf";
  87. _rb = new RingBuffer((char*)"/tmp/sinsy.rbuf",8*1024);
  88. }
  89. else
  90. {
  91. arguments << "-o" << "/tmp/ecantorix_sinsy.wav";
  92. }
  93. _synthprocess = new QProcess(this);
  94. connect(_synthprocess,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(synthprocess_finished(int,QProcess::ExitStatus)));
  95. _synthprocess->start(program, arguments);
  96. usleep(250*1000);
  97. return true;
  98. }
  99. bool eCantorixSynth::setCacheDir(QString cacheDir)
  100. {
  101. (void) cacheDir;
  102. return true;
  103. }
  104. void eCantorixSynth::builduScore()
  105. {
  106. int lastNoteEnd=0;
  107. QString uscore;
  108. uscore += "voice "+_internalVoice+"\n";
  109. uscore += "resample "+STR(_jack_samplerate)+"\n";
  110. DEVLOG_DEBUG("jack_samplerate "+STR(_jack_samplerate));
  111. for (int i = 0; i < _score.count(); ++i)
  112. {
  113. auto o = _score[i].toObject();
  114. if(!o.contains(NOTE_KEY_NUMBER)) {
  115. //tempo=o[TEMPO].toInt();
  116. continue;
  117. }
  118. //verfy int keyNumber=o[NOTE_KEY_NUMBER].toInt();
  119. int noteOffset = o[NOTE_PULSE_OFFSET].toInt();
  120. int noteLength = o[NOTE_PULSE_LENGTH].toInt();
  121. QString lyric = o[NOTE_LYRIC].toString();
  122. int notenum = o[NOTE_KEY_NUMBER].toInt();
  123. int rest = noteOffset-lastNoteEnd;
  124. if(rest>0)
  125. {
  126. uscore += "rest "+STR(rest)+"\n";
  127. }
  128. uscore += "note "+STR(noteLength)+" \""+lyric+"\" "+STR(notenum)+" 0 0 0 0 0\n";
  129. lastNoteEnd = noteOffset+noteLength;
  130. }
  131. QFile file("/tmp/uscore.sinsy");
  132. file.open(QFile::WriteOnly);
  133. file.write(uscore.toUtf8());
  134. }
  135. void eCantorixSynth::synthprocess_finished(int exitCode, QProcess::ExitStatus exitStatus)
  136. {
  137. _synthprocess->deleteLater();
  138. _synthprocess=nullptr;
  139. DEVLOG_DEBUG("process end "+STR(exitCode)+" "+STR(int(exitStatus)));
  140. if(!_isRealtime)
  141. {
  142. _ctrl->startOfflinePlayback("/tmp/ecantorix_sinsy.wav");
  143. }
  144. }
  145. bool eCantorixSynth::synthIsRealtime()
  146. {
  147. return _isRealtime;
  148. }
  149. int eCantorixSynth::readData(float *data, int size)
  150. {
  151. int data_length=size*sizeof(float);
  152. memset(data,0,data_length);
  153. int count = _rb->read((char*)data,data_length);
  154. if(!_synthprocess && count==0) return 1;
  155. return 0;
  156. }
  157. /// phoneme transformation
  158. QString eCantorixSynth::getTranscription(QString txt)
  159. {
  160. if(txt.split(" [").length()==2)
  161. return txt;
  162. QString program = "espeak-ng";
  163. QStringList arguments;
  164. arguments << "-v" << _internalVoice;
  165. arguments << "-x" << txt;
  166. QProcess myProcess(this);
  167. myProcess.start(program, arguments);
  168. if (!myProcess.waitForStarted())
  169. return txt;
  170. if (!myProcess.waitForFinished())
  171. return txt;
  172. QByteArray result = myProcess.readAll();
  173. QString tmp = QString::fromUtf8(result.data());
  174. return txt+"["+tmp+"]";
  175. }
  176. bool eCantorixSynth::doPhonemeTransformation(QStringList& list)
  177. {
  178. //TODO lyrizer
  179. DEVLOG_DEBUG(STR(list.count()));
  180. return false;
  181. }
  182. /// voice list (fix this there is only one)
  183. bool eCantorixSynth::setVoice(QString voiceName)
  184. {
  185. if(_voices.keys().contains(voiceName))
  186. {
  187. _internalVoice = _voices[voiceName];
  188. return true;
  189. }
  190. else
  191. {
  192. _internalVoice = "";
  193. return false;
  194. }
  195. }
  196. QStringList eCantorixSynth::listVoices()
  197. {
  198. return _voices.keys();
  199. }
  200. /// logging (helper) (refactor this)
  201. void eCantorixSynth::on_logDebug(QString debug)
  202. {
  203. _ctrl->logDebug(debug);
  204. }
  205. void eCantorixSynth::on_logError(QString error)
  206. {
  207. _ctrl->logError(error);
  208. }
  209. void eCantorixSynth::on_logSuccess(QString success)
  210. {
  211. _ctrl->logSuccess(success);
  212. }
  213. //remove cacheDir support
  214. //FIX no sound