123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- /*
- 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 "ecantorix_synth.h"
- #include "../editor/ustjkeys.h"
- #include <QFileInfo>
- #include <QDebug>
- #include <QDir>
- #include <QFile>
- #include <QTextStream>
- #include <QDebug>
- #include <QDirIterator>
- #include <QStringList>
- #include <QJsonDocument>
- #include <math.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <QProcess>
- #include <QtConcurrent/QtConcurrent>
- #include <sndfile.h>
- #include <unistd.h>
- #include <sekai/midi.h>
- #include <assert.h>
- #define __devloglevel__ 4
- //utilityfunctions -- move to libkawaii
- bool fileExists(QString path) {
- QFileInfo check_file(path);
- // check if file exists and if yes: Is it really a file and no directory?
- return check_file.exists() && check_file.isFile();
- }
- //ecantorix synth specific
- /// manifest
- QString eCantorixSynth::name() { return "eCantorix2"; }
- QString eCantorixSynth::description() { return "A multilingual KTH-style singing synthesizer"; }
- QString eCantorixSynth::version() { return "18.04"; }
- /// setup
- void eCantorixSynth::setup(IController* ctrl) {
- this->_ctrl = ctrl;
- this->_jack_samplerate = ctrl->sampleRate();
- _wavtool.setOutputRate(this->_jack_samplerate);
- //thread queue signaling
- connect(this,&eCantorixSynth::logDebug,this,&eCantorixSynth::on_logDebug);
- connect(this,&eCantorixSynth::logError,this,&eCantorixSynth::on_logError);
- connect(this,&eCantorixSynth::logSuccess,this,&eCantorixSynth::on_logSuccess);
- // _voices["Mikulas"]="cs";
- _voices["Kurogane"]="en1";
- _voices["Anna"]="de1";
- // _voices["Lukas"]="sv";
- // _voices["Mika"]="fi";
- _phoTypes["a"]={0,VOWEL};
- _phoTypes["A"]={0,VOWEL};
- _phoTypes["{"]={0,VOWEL};
- _phoTypes["6"]={0,VOWEL};
- _phoTypes["q"]={0,VOWEL};
- _phoTypes["Q"]={0,VOWEL};
- _phoTypes["e"]={0,VOWEL};
- _phoTypes["E"]={0,VOWEL};
- _phoTypes["@"]={0,VOWEL};
- _phoTypes["3"]={0,VOWEL};
- _phoTypes["i"]={0,VOWEL};
- _phoTypes["I"]={0,VOWEL};
- _phoTypes["o"]={0,VOWEL};
- _phoTypes["O"]={0,VOWEL};
- _phoTypes["O:"]={0,VOWEL};
- _phoTypes["2"]={0,VOWEL};
- _phoTypes["9"]={0,VOWEL};
- _phoTypes["&"]={0,VOWEL};
- _phoTypes["u"]={0,VOWEL};
- _phoTypes["U"]={0,VOWEL};
- _phoTypes["}"]={0,VOWEL};
- _phoTypes["v"]={0,VOWEL};
- _phoTypes["V"]={0,VOWEL};
- _phoTypes["y"]={0,VOWEL};
- _phoTypes["Y"]={0,VOWEL};
- _phoTypes["p"]={100,HCONSONANT};
- _phoTypes["b"]={80,VCONSONANT};
- _phoTypes["t"]={100,HCONSONANT};
- _phoTypes["d"]={80,VCONSONANT};
- _phoTypes["k"]={100,HCONSONANT};
- _phoTypes["g"]={80,VCONSONANT};
- _phoTypes["pf"]={160,HCONSONANT};
- _phoTypes["ts"]={160,HCONSONANT};
- _phoTypes["tS"]={160,HCONSONANT};
- _phoTypes["f"]={50,HCONSONANT};
- _phoTypes["v"]={50,VCONSONANT};
- _phoTypes["s"]={60,HCONSONANT};
- _phoTypes["z"]={60,VCONSONANT};
- _phoTypes["S"]={60,HCONSONANT};
- _phoTypes["Z"]={60,VCONSONANT};
- _phoTypes["C"]={60,HCONSONANT};
- _phoTypes["j"]={60,VCONSONANT};
- _phoTypes["x"]={700,VCONSONANT};
- _phoTypes["h"]={100,HCONSONANT};
- _phoTypes["m"]={60,NASAL};
- _phoTypes["n"]={60,NASAL};
- _phoTypes["N"]={60,NASAL};
- _phoTypes["l"]={60,LIQUID};
- _phoTypes["R"]={60,LIQUID};
- }
- bool eCantorixSynth::setCacheDir(QString cacheDir)
- {
- //FIX this
- DEVLOG_DEBUG("cacheDir: "+cacheDir);
- return true;
- }
- bool eCantorixSynth::synthIsRealtime()
- {
- return false;
- }
- void eCantorixSynth::lookupPho(QString pho)
- {
- phoEvent e;
- e.type = _phoTypes[pho];
- e.symbol = pho;
- _eventList.push_back(e);
- }
- void eCantorixSynth::lyricize(QString lyric)
- {
- _eventList.clear();
- _wavtool.clear();
- bool inPho=false;
- QString pho;
- for(int i = 0; i< lyric.length(); i++)
- {
- QString i0 = lyric.at(i);
- if(i0=="["){
- inPho=true;
- continue;
- }
- if(i0=="]"){
- inPho=false;
- continue;
- }
- if(inPho)
- {
- pho+=i0;
- }
- }
- QStringList phoList = pho.split(" ");
- for(int i=0;i<phoList.count();i++)
- {
- pho = phoList[i];
- lookupPho(pho);
- //lookup pho, store result in list
- }
- }
- float eCantorixSynth::getPreutter()
- {
- int preutter=0;
- for(int i=0;i<_eventList.count();i++)
- {
- phoEvent e=_eventList[i];
- preutter += e.type.defaultLength;
- //DEVLOG_DEBUG("l: "+e.symbol);
- if(e.type.defaultLength==0) break;
- }
- return 0.001*preutter;
- }
- QString genPitch(float pitch)
- {
- int p=pitch;
- return "0 "+STR(p)+ " 100 "+STR(p);
- }
- bool eCantorixSynth::synthesize(IScore *score)
- {
- _notes.clear();
- for(int i=0;i<score->getNoteCount();i++)
- {
- auto n = score->getNote(i);
- //DEVLOG_DEBUG("note "+STR(i));
- //DEVLOG_DEBUG("rest "+STR(n.rest)+" length "+STR(n.lenght));
- lyricize(n.lyric);
- eCantorixNote en;
- en.vstart=n.start;
- en.rlength=n.lenght;
- en.f0=frequencyFromNote(n.pitch);
- en.preutter = getPreutter();
- DEVLOG_DEBUG("preutter "+STR(en.preutter));
- en.eventList= _eventList;
- _notes.push_back(en);
- }
- for(int i=1;i<score->getNoteCount();i++)
- {
- float a = _notes[i-1].vstart + _notes[i-1].rlength;
- float b = _notes[i].vstart - _notes[i].preutter;
- if(b<a)
- {
- _notes[i-1].rlength -= _notes[i].preutter;
- }
- }
- for(int i=0;i<score->getNoteCount();i++)
- {
- _eventList = _notes[i].eventList;
- float f0 = _notes[i].f0;
- float fixedLength=0;
- int zeros=0;
- DEVLOG_DEBUG("rlength: "+STR(_notes[i].rlength));
- for(int j=0;j<_eventList.count();j++)
- {
- phoEvent e=_eventList[j];
- fixedLength += e.type.defaultLength;
- if(e.type.defaultLength==0) zeros++;
- }
- fixedLength /= 1000.0;
- float totalLength = _notes[i].preutter+_notes[i].rlength;
- if(totalLength>fixedLength && zeros>0)
- {
- QString pho;
- for(int j=0;j<_eventList.count();j++)
- {
- phoEvent e=_eventList[j];
- int len = e.type.defaultLength;
- bool sustained=false;
- if(len==0)
- {
- len=(totalLength-fixedLength)*1000/zeros;
- sustained=true;
- }
- int lmax=150;
- if(sustained && len>lmax)
- {
- int len2 = len % lmax;
- int len3 = len2/2;
- int count = len/lmax;
- len2-=len3;
- DEVLOG_DEBUG("len2 "+STR(len2));
- DEVLOG_DEBUG("len3 "+STR(len3));
- DEVLOG_DEBUG("count "+STR(count));
- assert(count*lmax+len2+len3==len);
- pho += e.symbol;
- pho += "\t";
- pho += STR(len2);
- pho += "\t";
- pho += genPitch(f0);
- pho += "\n";
- for(int k=0;k<count;k++)
- {
- pho += e.symbol;
- pho += "\t";
- pho += STR(lmax);
- pho += "\t";
- pho += genPitch(f0);
- pho += "\n";
- }
- pho += e.symbol;
- pho += "\t";
- pho += STR(len3);
- pho += "\t";
- pho += genPitch(f0);
- pho += "\n";
- }
- else
- {
- pho += e.symbol;
- pho += "\t";
- pho += STR(len);
- pho += "\t";
- pho += genPitch(f0);
- pho += "\n";
- }
- }
- _notes[i].mbrolaPho = pho;
- }
- }
- DEVLOG_DEBUG("voice "+_internalVoice);
- QString cacheDir="/tmp/ecantorix_mbrola_"+_internalVoice;
- QDir dir(cacheDir);
- if (!dir.exists())
- dir.mkpath(".");
- chdir(cacheDir.toUtf8());
- for(int i=0;i<score->getNoteCount();i++)
- {
- QString basename = STR(i) +"_"+ QString(QCryptographicHash::hash(_notes[i].mbrolaPho.toUtf8(),QCryptographicHash::Md5).toHex());
- //DEVLOG_DEBUG("send_to_mbrola\n"+_notes[i].mbrolaPho);
- DEVLOG_DEBUG("basename \n"+basename);
- {
- QString phoFileName = basename+".pho";
- QString wavFileName = basename+".wav";
- QString tmpFileName = STR(i)+".wav";
- QFile file (phoFileName);
- if(file.open(QIODevice::WriteOnly))
- {
- QTextStream outstream(&file);
- outstream << _notes[i].mbrolaPho;
- }
- else
- abort();
- QString voice = "/home/isengaara/Hacking/Audio/ecantorix-sinsy-ng/mbrola-voices/data/"+_internalVoice+"/"+_internalVoice;
- QString mbrola = "mbrola -e "+voice+" "+phoFileName+" "+wavFileName+" 2> mbrola.log";
- DEVLOG_DEBUG(mbrola);
- system(mbrola.toUtf8());
- QFile logfile("mbrola.log");
- if(logfile.open(QIODevice::ReadOnly))
- {
- QTextStream instream(&logfile);
- QString text;
- do
- {
- text = instream.readLine();
- emit logError(text);
- } while(text.length());
- }
- else
- abort();
- _wavtool.addNote(wavFileName.toUtf8(),_notes[i].vstart-_notes[i].preutter,_notes[i].preutter+_notes[i].rlength);
- //agc(,tmpFileName.toUtf8());
- }
- //store pho files in cache dir
- }
- _wavtool.mix();
- _ctrl->startOfflinePlayback("/tmp/wavtool.wav");
- return true;
- }
- int eCantorixSynth::readData(float *data, int size)
- {
- abort();
- (void)data;
- return size;
- }
- /// phoneme transformation
- QString eCantorixSynth::getTranscription(QString txt)
- {
- return txt;
- }
- bool eCantorixSynth::doPhonemeTransformation(QStringList& list)
- {
- //TODO lyrizer
- DEVLOG_DEBUG(STR(list.count()));
- return false;
- }
- /// voice list (fix this there is only one)
- bool eCantorixSynth::setVoice(QString voiceName)
- {
- if(_voices.keys().contains(voiceName))
- {
- _internalVoice = _voices[voiceName];
- return true;
- }
- else
- {
- _internalVoice = "";
- return false;
- }
- }
- QStringList eCantorixSynth::listVoices()
- {
- return _voices.keys();
- }
- /// logging (helper) (refactor this)
- void eCantorixSynth::on_logDebug(QString debug)
- {
- _ctrl->logDebug(debug);
- }
- void eCantorixSynth::on_logError(QString error)
- {
- _ctrl->logError(error);
- }
- void eCantorixSynth::on_logSuccess(QString success)
- {
- _ctrl->logSuccess(success);
- }
- //remove cacheDir support
- //FIX no sound
|