1 /***************************************************************************
2 **                                                                        **
3 **  Polyphone, a soundfont editor                                         **
4 **  Copyright (C) 2013-2019 Davy Triponney                                **
5 **                                                                        **
6 **  This program 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 **                                                                        **
11 **  This program is distributed in the hope that it will be useful,       **
12 **  but WITHOUT ANY WARRANTY; without even the implied warranty of        **
13 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          **
14 **  GNU General Public License for more details.                          **
15 **                                                                        **
16 **  You should have received a copy of the GNU General Public License     **
17 **  along with this program. If not, see http://www.gnu.org/licenses/.    **
18 **                                                                        **
19 ****************************************************************************
20 **           Author: Davy Triponney                                       **
21 **  Website/Contact: https://www.polyphone-soundfonts.com                 **
22 **             Date: 01.01.2013                                           **
23 ***************************************************************************/
24 
25 #include "conversion_sfz.h"
26 #include "soundfontmanager.h"
27 #include "sampleutils.h"
28 #include "samplewriterwav.h"
29 #include <QFile>
30 #include <QFileInfo>
31 #include <QDir>
32 #include <QDate>
33 #include "contextmanager.h"
34 #include "attribute.h"
35 #include "sfzparamlist.h"
36 
37 
ConversionSfz()38 ConversionSfz::ConversionSfz() : QObject(),
39     _sf2(SoundfontManager::getInstance())
40 {}
41 
convert(QString dirPath,EltID idSf2,bool presetPrefix,bool bankDir,bool gmSort)42 QString ConversionSfz::convert(QString dirPath, EltID idSf2, bool presetPrefix, bool bankDir, bool gmSort)
43 {
44     // Create the base directory
45     if (QDir(dirPath).exists())
46     {
47         int i = 1;
48         while (QDir(dirPath + "-" + QString::number(i)).exists())
49             i++;
50         dirPath += "-" + QString::number(i);
51     }
52     if (!QDir().mkdir(dirPath))
53         return tr("Cannot create directory \"%1\"").arg(dirPath);
54 
55     // Plusieurs banques sont utilisées ?
56     int numBankUnique = -1;
57     _bankSortEnabled = false;
58     EltID presetId(elementPrst, idSf2.indexSf2);
59     foreach (int presetNumber, _sf2->getSiblings(presetId))
60     {
61         presetId.indexElt = presetNumber;
62         if (numBankUnique == -1)
63             numBankUnique = _sf2->get(presetId, champ_wBank).wValue;
64         else
65             _bankSortEnabled |= (numBankUnique != _sf2->get(presetId, champ_wBank).wValue);
66     }
67     if (_bankSortEnabled)
68         numBankUnique = -1; // Si numBankUnique est différent de -1, il nous donne le numéro unique de banque utilisée
69     _bankSortEnabled &= bankDir;
70     _gmSortEnabled = gmSort;
71 
72     // Sample directory
73     _dirSamples = dirPath + "/samples";
74     QDir().mkdir(_dirSamples);
75 
76     // For each preset in the soundfont
77     foreach (int presetNumber, _sf2->getSiblings(presetId))
78     {
79         presetId.indexElt = presetNumber;
80 
81         // Répertoire allant contenir le fichier sfz
82         QString sourceDir = dirPath;
83 
84         int numBank = _sf2->get(presetId, champ_wBank).wValue;
85         int numPreset = _sf2->get(presetId, champ_wPreset).wValue;
86 
87         if (_bankSortEnabled)
88         {
89             QString numText;
90             numText.sprintf("%.3u", numBank);
91             sourceDir += "/" + numText;
92             if (!QDir(sourceDir).exists())
93                 QDir(sourceDir).mkdir(sourceDir);
94         }
95         if (_gmSortEnabled)
96         {
97             if (numBank == 128)
98             {
99                 if (_bankSortEnabled || numBankUnique == 128)
100                     sourceDir += "/" + getDrumCategory(numPreset);
101                 else
102                     sourceDir += "/" + getDirectoryName(128);
103             }
104             else
105                 sourceDir += "/" + getDirectoryName(numPreset);
106             if (!QDir(sourceDir).exists())
107                 QDir(sourceDir).mkdir(sourceDir);
108         }
109 
110         exportPrst(sourceDir, presetId, presetPrefix);
111     }
112 
113     return "";
114 }
115 
exportPrst(QString dir,EltID id,bool presetPrefix)116 void ConversionSfz::exportPrst(QString dir, EltID id, bool presetPrefix)
117 {
118     QString numText;
119     if (presetPrefix)
120         numText.sprintf("%.3u_", _sf2->get(id, champ_wPreset).wValue);
121     int numBank = _sf2->get(id, champ_wBank).wValue;
122 
123     QFile fichierSfz(getPathSfz(dir, numText + _sf2->getQstr(id, champ_name)) + ".sfz");
124     if (fichierSfz.open(QIODevice::WriteOnly))
125     {
126         writeEntete(&fichierSfz, id);
127         id.typeElement = elementPrstInst;
128 
129         foreach (int i, _sf2->getSiblings(id))
130         {
131             id.indexElt2 = i;
132             QList<EltID> listProcessedInstSmpl;
133 
134             // Paramètres du prst
135             SfzParamList * paramPrst = new SfzParamList(_sf2, id);
136 
137             // ID de l'instrument lié
138             EltID idInst = id;
139             idInst.typeElement = elementInst;
140             idInst.indexElt = _sf2->get(id, champ_instrument).wValue;
141 
142             // Paramètres globaux (groupe)
143             SfzParamList * paramGroupe = new SfzParamList(_sf2, paramPrst, idInst);
144             writeGroup(&fichierSfz, paramGroupe, numBank == 128);
145             delete paramGroupe;
146 
147             // Ecriture de chaque élément présent dans l'instrument
148             idInst.typeElement = elementInstSmpl;
149 
150             // Ordre des instSmpl
151             QMultiMap<int, EltID> map;
152             foreach (int j, _sf2->getSiblings(idInst))
153             {
154                 idInst.indexElt2 = j;
155                 map.insert(_sf2->get(idInst, champ_keyRange).rValue.byLo, idInst);
156             }
157 
158             QList<EltID> listInstSmpl = map.values();
159             for (int j = 0; j < listInstSmpl.count(); j++)
160             {
161                 idInst = listInstSmpl.at(j);
162                 if (!listProcessedInstSmpl.contains(idInst) && isIncluded(paramPrst, idInst))
163                 {
164                     SfzParamList * paramInstSmpl = new SfzParamList(_sf2, paramPrst, idInst);
165                     EltID idSmpl = idInst;
166                     idSmpl.typeElement = elementSmpl;
167                     idSmpl.indexElt = _sf2->get(idInst, champ_sampleID).wValue;
168 
169                     // Utilisation sample mono ou stéréo ?
170                     SFSampleLink typeSample = _sf2->get(idSmpl, champ_sfSampleType).sfLinkValue;
171                     double pan = 0;
172                     if (paramInstSmpl->findChamp(champ_pan) != -1)
173                         pan = paramInstSmpl->getValeur(paramInstSmpl->findChamp(champ_pan));
174                     bool enableStereo = ((typeSample == leftSample || typeSample == RomLeftSample) && pan == -50) ||
175                             ((typeSample == rightSample || typeSample == RomRightSample) && pan == 50);
176 
177                     bool ignorePan = false;
178                     if (enableStereo)
179                     {
180                         // Sample lié
181                         EltID idSmplLinked = idSmpl;
182                         idSmplLinked.indexElt = _sf2->get(idSmpl, champ_wSampleLink).wValue;
183 
184                         // Vérification que les paramètres liés aux samples sont identiques
185                         if (_sf2->get(idSmpl, champ_dwStartLoop).dwValue == _sf2->get(idSmplLinked, champ_dwStartLoop).dwValue &&
186                                 _sf2->get(idSmpl, champ_dwEndLoop).dwValue == _sf2->get(idSmplLinked, champ_dwEndLoop).dwValue &&
187                                 _sf2->get(idSmpl, champ_dwLength).dwValue == _sf2->get(idSmplLinked, champ_dwLength).dwValue &&
188                                 _sf2->get(idSmpl, champ_chPitchCorrection).cValue == _sf2->get(idSmplLinked, champ_chPitchCorrection).cValue &&
189                                 _sf2->get(idSmpl, champ_byOriginalPitch).bValue == _sf2->get(idSmplLinked, champ_byOriginalPitch).bValue &&
190                                 _sf2->get(idSmpl, champ_dwSampleRate).dwValue == _sf2->get(idSmplLinked, champ_dwSampleRate).dwValue)
191                         {
192                             // Recherche d'un instSmpl correspondant exactement à l'autre canal
193                             int index = -1;
194                             for (int k = j + 1; k < listInstSmpl.count(); k++)
195                             {
196                                 if (_sf2->get(listInstSmpl.at(k), champ_sampleID).wValue == idSmplLinked.indexElt)
197                                 {
198                                     // Comparaison des gen
199                                     bool isEqual = true;
200                                     EltID idInstSmplGen = idInst;
201                                     idInstSmplGen.typeElement = elementInstSmplGen;
202                                     EltID idLinkedInstSmplGen = listInstSmpl.at(k);
203                                     idLinkedInstSmplGen.typeElement = elementInstSmplGen;
204                                     QMap<AttributeType, AttributeValue> map, mapLinked;
205                                     foreach (int champ, _sf2->getSiblings(idInstSmplGen))
206                                     {
207                                         map.insert((AttributeType)champ, _sf2->get(idInstSmplGen.parent(), (AttributeType)champ));
208                                     }
209                                     foreach (int champ, _sf2->getSiblings(idLinkedInstSmplGen))
210                                     {
211                                         mapLinked.insert((AttributeType)champ, _sf2->get(idLinkedInstSmplGen.parent(), (AttributeType)champ));
212                                     }
213                                     QList<AttributeType> listeChamps = map.keys();
214                                     QList<AttributeType> listeChampsLinked = mapLinked.keys();
215                                     QList<AttributeValue> listeValeurs = map.values();
216                                     QList<AttributeValue> listeValeursLinked = mapLinked.values();
217                                     if (listeChamps.size() == listeChampsLinked.size())
218                                     {
219                                         for (int l = 0; l < listeChamps.size(); l++)
220                                         {
221                                             if (listeChamps.at(l) == champ_pan)
222                                                 isEqual &= listeChamps.at(l) == listeChampsLinked.at(l) &&
223                                                         listeValeurs.at(l).shValue == -listeValeursLinked.at(l).shValue;
224                                             else if (listeChamps.at(l) != champ_sampleID)
225                                                 isEqual &= listeChamps.at(l) == listeChampsLinked.at(l) &&
226                                                         listeValeurs.at(l).shValue == listeValeursLinked.at(l).shValue;
227                                         }
228                                     }
229                                     else
230                                         isEqual = false;
231                                     if (isEqual)
232                                         index = k;
233                                 }
234                             }
235                             if (index != -1)
236                             {
237                                 listProcessedInstSmpl << listInstSmpl.at(index);
238                                 ignorePan = true;
239                             }
240                         }
241                         else
242                             enableStereo = false;
243                     }
244                     writeRegion(&fichierSfz, paramInstSmpl, getLink(idSmpl, enableStereo), ignorePan);
245                     delete paramInstSmpl;
246                     listProcessedInstSmpl << idInst;
247                 }
248             }
249             delete paramPrst;
250         }
251         fichierSfz.close();
252     }
253 }
254 
getPathSfz(QString dir,QString name)255 QString ConversionSfz::getPathSfz(QString dir, QString name)
256 {
257     if (name.isEmpty())
258         name = tr("untitled");
259     name = escapeStr(name);
260     QFile file(dir + "/" + name + ".sfz");
261     QDir dossier(dir + "/" + name);
262     if (file.exists() || dossier.exists())
263     {
264         int i = 1;
265         while (QFile(dir + "/" + name + "-" + QString::number(i) + ".sfz").exists() ||
266                QDir(dir + "/" + name + "-" + QString::number(i)).exists())
267             i++;
268         name = name + "-" + QString::number(i);
269     }
270 
271     return dir + "/" + name;
272 }
273 
writeEntete(QFile * fichierSfz,EltID id)274 void ConversionSfz::writeEntete(QFile * fichierSfz, EltID id)
275 {
276     // Write header
277     id.typeElement = elementSf2;
278     QTextStream out(fichierSfz);
279     out << "// Sfz exported from a sf2 file with Polyphone" << endl
280         << "// Name      : " << _sf2->getQstr(id, champ_name).replace(QRegExp("[\r\n]"), " ") << endl
281         << "// Author    : " << _sf2->getQstr(id, champ_IENG).replace(QRegExp("[\r\n]"), " ") << endl
282         << "// Copyright : " << _sf2->getQstr(id, champ_ICOP).replace(QRegExp("[\r\n]"), " ") << endl
283         << "// Date      : " << QDate::currentDate().toString("yyyy/MM/dd") << endl
284         << "// Comment   : " << _sf2->getQstr(id, champ_ICMT).replace(QRegExp("[\r\n]"), " ") << endl;
285 }
286 
writeGroup(QFile * fichierSfz,SfzParamList * listeParam,bool isPercKit)287 void ConversionSfz::writeGroup(QFile * fichierSfz, SfzParamList * listeParam, bool isPercKit)
288 {
289     // Ecriture de paramètres communs à plusieurs régions
290     QTextStream out(fichierSfz);
291     out << endl << "<group>" << endl;
292     if (isPercKit)
293         out << "lochan=10 hichan=10" << endl;
294     for (int i = 0; i < listeParam->size(); i++)
295         writeElement(out, listeParam->getChamp(i), listeParam->getValeur(i));
296 }
297 
writeRegion(QFile * fichierSfz,SfzParamList * listeParam,QString pathSample,bool ignorePan)298 void ConversionSfz::writeRegion(QFile * fichierSfz, SfzParamList * listeParam, QString pathSample, bool ignorePan)
299 {
300     // Correction de volume lorsqu'un son passe en stéréo
301     double deltaVolumeIfIgnorePan = 3.;
302 
303     // Ecriture de paramètres spécifique à une région
304     QTextStream out(fichierSfz);
305 
306     out << endl << "<region>" << endl
307         << "sample=" << pathSample.replace("/", "\\") << endl;
308     if (ignorePan && listeParam->findChamp(champ_initialAttenuation) == -1)
309         writeElement(out, champ_initialAttenuation, -deltaVolumeIfIgnorePan / DB_SF2_TO_SFZ);
310 
311     for (int i = 0; i < listeParam->size(); i++)
312     {
313         if (ignorePan && listeParam->getChamp(i) == champ_initialAttenuation)
314             writeElement(out, champ_initialAttenuation, listeParam->getValeur(i) -
315                          deltaVolumeIfIgnorePan / DB_SF2_TO_SFZ);
316         else if (!ignorePan || listeParam->getChamp(i) != champ_pan)
317             writeElement(out, listeParam->getChamp(i), listeParam->getValeur(i));
318     }
319 }
320 
writeElement(QTextStream & out,AttributeType champ,double value)321 void ConversionSfz::writeElement(QTextStream &out, AttributeType champ, double value)
322 {
323     QString v2 = " // sfz v2";
324     switch (champ)
325     {
326     case champ_fineTune:                out << "tune=" << qRound(value) << endl;                    break;
327     case champ_coarseTune:              out << "transpose=" << qRound(value) << endl;               break;
328     case champ_scaleTuning:             out << "pitch_keytrack=" << qRound(value) << endl;          break;
329     case champ_startloopAddrsOffset:    out << "loop_start=" << qRound(value) << endl;              break;
330     case champ_startAddrsOffset:        out << "offset=" << qRound(value) << endl;                  break;
331     case champ_endloopAddrsOffset:      out << "loop_end=" << qRound(value) - 1 << endl;            break;
332     case champ_endAddrsOffset:          out << "end=" << qRound(value) - 1 << endl;                 break;
333     case champ_pan:                     out << "pan=" << 2 * value << endl;                         break;
334     case champ_initialAttenuation:      out << "volume=" << -value * DB_SF2_TO_SFZ << endl;         break;
335     case champ_initialFilterQ:          out << "resonance=" << value << endl;                       break;
336     case champ_sustainModEnv:           out << "fileg_sustain=" << 100. - value << endl
337                                             << "pitcheg_sustain=" << 100. - value << endl;          break;
338     case champ_delayModEnv:             out << "pitcheg_delay=" << value << endl
339                                             << "fileg_delay=" << value << endl;                     break;
340     case champ_attackModEnv:            out << "pitcheg_attack=" << value << endl
341                                             << "fileg_attack=" << value << endl;                    break;
342     case champ_holdModEnv:              out << "pitcheg_hold=" << value << endl
343                                             << "fileg_hold=" << value << endl;                      break;
344     case champ_decayModEnv:             out << "pitcheg_decay=" << value << endl
345                                             << "fileg_decay=" << value << endl;                     break;
346     case champ_releaseModEnv:           out << "pitcheg_release=" << value << endl
347                                             << "fileg_release=" << value << endl;                   break;
348     case champ_modEnvToPitch:           out << "pitcheg_depth=" << qRound(value) << endl;           break;
349     case champ_modEnvToFilterFc:        out << "fileg_depth=" << qRound(value) << endl;             break;
350     case champ_keynumToModEnvHold:      out << "pitcheg_holdcc133=" << value << v2 << endl
351                                             << "fileg_holdcc133=" << value << v2 << endl;           break;
352     case champ_keynumToModEnvDecay:     out << "pitcheg_decaycc133=" << value << v2 << endl
353                                             << "fileg_decaycc133=" << value << v2 << endl;          break;
354     case champ_delayModLFO:             out << "amplfo_delay=" << value << endl
355                                             << "fillfo_delay=" << value << endl;                    break;
356     case champ_freqModLFO:              out << "amplfo_freq=" << value << endl
357                                             << "fillfo_freq=" << value << endl;                     break;
358     case champ_modLfoToVolume:          out << "amplfo_depth=" << value << endl;                    break;
359     case champ_modLfoToFilterFc:        out << "fillfo_depth=" << value << endl;                    break;
360 
361     case champ_modLfoToPitch:           /* IMPOSSIBLE !!! */                                        break;
362     case champ_keynum:
363         out << "pitch_keycenter="
364             << ContextManager::keyName()->getKeyName(qRound(value), false, false, true) << endl
365             << "pitch_keytrack=0" << endl;
366         break;
367     case champ_reverbEffectsSend:       out << "effect1=" << value << endl;                         break;
368     case champ_chorusEffectsSend:       out << "effect2=" << value << endl;                         break;
369     case champ_delayVolEnv:             out << "ampeg_delay=" << value << endl;                     break;
370     case champ_attackVolEnv:            out << "ampeg_attack=" << value << endl;                    break;
371     case champ_sustainVolEnv:           out << "ampeg_sustain=" << dbToPercent(value) << endl;      break;
372     case champ_holdVolEnv:              out << "ampeg_hold=" << value << endl;                      break;
373     case champ_decayVolEnv:             out << "ampeg_decay=" << value << endl;                     break;
374     case champ_keynumToVolEnvHold:      out << "ampeg_holdcc133=" << value << v2 << endl;           break;
375     case champ_keynumToVolEnvDecay:     out << "ampeg_decaycc133=" << value << v2 << endl;          break;
376     case champ_releaseVolEnv:           out << "ampeg_release=" << value << endl;                   break;
377     case champ_overridingRootKey:
378         out << "pitch_keycenter="
379             << ContextManager::keyName()->getKeyName(qRound(value), false, false, true) << endl;
380         break;
381     case champ_delayVibLFO:             out << "pitchlfo_delay=" << value << endl;                  break;
382     case champ_freqVibLFO:              out << "pitchlfo_freq=" << value << endl;                   break;
383     case champ_vibLfoToPitch:           out << "pitchlfo_depth=" << qRound(value) << endl;          break;
384     case champ_velocity:                out << "amp_velcurve_1=" << value / 127. << endl
385                                             << "amp_velcurve_127=" << value / 127. << endl;         break;
386     case champ_exclusiveClass:
387         if (value != 0)
388             out << "group=" << qRound(value) << endl
389                 << "off_by=" << qRound(value) << endl;
390         break;
391     case champ_initialFilterFc:
392         out << "fil_type=lpf_2p" << endl
393             << "cutoff="   << qRound(value) << endl;
394         break;
395     case champ_keyRange:{
396         QString lokey = ContextManager::keyName()->getKeyName(qRound(value / 1000.), false, false, true);
397         QString hikey = ContextManager::keyName()->getKeyName(qRound(value - 1000. * qRound(value / 1000.)), false, false, true);
398         out << "lokey=" << lokey << " hikey=" << hikey << endl;
399     }break;
400     case champ_velRange:{
401         int lovel = qRound(value / 1000.);
402         int hivel = qRound(value - 1000. * lovel);
403         out << "lovel=" << lovel << " hivel=" << hivel << endl;
404     }break;
405     case champ_sampleModes:
406         if (value == 0.)
407             out << "loop_mode=no_loop" << endl;
408         else if (value == 1.)
409             out << "loop_mode=loop_continuous" << endl;
410         else if (value == 3.)
411             out << "loop_mode=loop_sustain" << endl;
412         break;
413     default:
414         break;
415     }
416 }
417 
getLink(EltID idSmpl,bool enableStereo)418 QString ConversionSfz::getLink(EltID idSmpl, bool enableStereo)
419 {
420     QString path = "";
421     if (!enableStereo && _mapMonoSamples.contains(idSmpl.indexElt))
422         path = _mapMonoSamples.value(idSmpl.indexElt);
423     else if (enableStereo && _mapStereoSamples.contains(idSmpl.indexElt))
424         path = _mapStereoSamples.value(idSmpl.indexElt);
425     else
426     {
427         QString name;
428         EltID idSmpl2 = idSmpl;
429         idSmpl2.indexElt = -1;
430 
431         // Stéréo ?
432         if (enableStereo && _sf2->get(idSmpl, champ_sfSampleType).wValue != monoSample &&
433                 _sf2->get(idSmpl, champ_sfSampleType).wValue != RomMonoSample)
434         {
435             idSmpl2.indexElt = _sf2->get(idSmpl, champ_wSampleLink).wValue;
436 
437             // Nom du fichier
438             QString nom1 = _sf2->getQstr(idSmpl, champ_name);
439             QString nom2 = _sf2->getQstr(idSmpl2, champ_name);
440             int nb = SampleUtils::lastLettersToRemove(nom1, nom2);
441             name = nom1.left(nom1.size() - nb);
442 
443             if (_sf2->get(idSmpl, champ_sfSampleType).wValue != rightSample &&
444                     _sf2->get(idSmpl, champ_sfSampleType).wValue != RomRightSample)
445             {
446                 // Inversion smpl1 smpl2
447                 EltID idTmp = idSmpl;
448                 idSmpl = idSmpl2;
449                 idSmpl2 = idTmp;
450             }
451         }
452         else
453             name = _sf2->getQstr(idSmpl, champ_name);
454 
455         name = escapeStr(name);
456         QFile file(_dirSamples + "/" + name + ".wav");
457         if (file.exists())
458         {
459             int i = 1;
460             while (QFile(_dirSamples + "/" + name + "-" + QString::number(i) + ".wav").exists())
461                 i++;
462             name = name + "-" + QString::number(i);
463         }
464 
465         path = QDir(_dirSamples).dirName() + "/" + name + ".wav";
466         if (_bankSortEnabled)
467             path.prepend("../");
468         if (_gmSortEnabled)
469             path.prepend("../");
470 
471         // Export et sauvegarde
472         SampleWriterWav writer(_dirSamples + "/" + name + ".wav");
473         if (idSmpl.indexElt == -1 || idSmpl2.indexElt == -1)
474         {
475             // Mono
476             if (idSmpl.indexElt2 != -1)
477             {
478                 _mapMonoSamples.insert(idSmpl.indexElt, path);
479                 writer.write(_sf2->getSound(idSmpl));
480             }
481             else if (idSmpl2.indexElt != -1)
482             {
483                 _mapMonoSamples.insert(idSmpl2.indexElt, path);
484                 writer.write(_sf2->getSound(idSmpl2));
485             }
486         }
487         else
488         {
489             // Stéréo
490             _mapStereoSamples.insert(idSmpl.indexElt, path);
491             _mapStereoSamples.insert(idSmpl2.indexElt, path);
492             writer.write(_sf2->getSound(idSmpl2), _sf2->getSound(idSmpl));
493         }
494     }
495     return path;
496 }
497 
escapeStr(QString str)498 QString ConversionSfz::escapeStr(QString str)
499 {
500     return str.replace(QRegExp(QString::fromUtf8("[`~*|:<>«»?/{}\"\\\\]")), "_");
501 }
502 
lastLettersToRemove(QString str1,QString str2)503 int ConversionSfz::lastLettersToRemove(QString str1, QString str2)
504 {
505     str1 = str1.toLower();
506     str2 = str2.toLower();
507     int nbLetters = 0;
508 
509     int size = 0;
510     if (str1.size() == str2.size())
511         size = str1.size();
512     else return 0;
513 
514     if (str1.left(size - 2).compare(str2.left(size - 2)) != 0)
515         return 0;
516 
517     QString fin1_3 = str1.right(3);
518     QString fin2_3 = str2.right(3);
519     QString fin1_2 = str1.right(2).left(1);
520     QString fin2_2 = str2.right(2).left(1);
521     QString fin1_1 = str1.right(1);
522     QString fin2_1 = str2.right(1);
523 
524     if ((fin1_3.compare("(r)") == 0 && fin2_3.compare("(l)") == 0) ||
525             (fin1_3.compare("(l)") == 0 && fin2_3.compare("(r)") == 0))
526         nbLetters = 3;
527     else if (((fin1_1.compare("r") == 0 && fin2_1.compare("l") == 0) ||
528               (fin1_1.compare("l") == 0 && fin2_1.compare("r") == 0)) &&
529              str1.left(size - 1).compare(str2.left(size - 1)) == 0)
530     {
531         nbLetters = 1;
532         if ((fin1_2.compare("-") == 0 && fin2_2.compare("-") == 0) ||
533                 (fin1_2.compare("_") == 0 && fin2_2.compare("_") == 0) ||
534                 (fin1_2.compare(".") == 0 && fin2_2.compare(".") == 0))
535             nbLetters = 2;
536     }
537 
538     return nbLetters;
539 }
540 
getDirectoryName(int numPreset)541 QString ConversionSfz::getDirectoryName(int numPreset)
542 {
543     QString strRet = tr("other");
544 
545     if (numPreset < 8)
546         strRet = "000-007 " + tr("Piano");
547     else if (numPreset < 16)
548         strRet = "008-015 " + tr("Chromatic percussion");
549     else if (numPreset < 24)
550         strRet = "016-023 " + tr("Organ");
551     else if (numPreset < 32)
552         strRet = "024-031 " + tr("Guitar");
553     else if (numPreset < 40)
554         strRet = "032-039 " + tr("Bass");
555     else if (numPreset < 48)
556         strRet = "040-047 " + tr("Strings");
557     else if (numPreset < 56)
558         strRet = "048-055 " + tr("Ensemble");
559     else if (numPreset < 64)
560         strRet = "056-063 " + tr("Brass");
561     else if (numPreset < 72)
562         strRet = "064-071 " + tr("Reed");
563     else if (numPreset < 80)
564         strRet = "072-079 " + tr("Pipe");
565     else if (numPreset < 88)
566         strRet = "080-087 " + tr("Synth lead");
567     else if (numPreset < 96)
568         strRet = "088-095 " + tr("Synth pad");
569     else if (numPreset < 104)
570         strRet = "096-103 " + tr("Synth effects");
571     else if (numPreset < 112)
572         strRet = "104-111 " + tr("Ethnic");
573     else if (numPreset < 120)
574         strRet = "112-119 " + tr("Percussive");
575     else if (numPreset < 128)
576         strRet = "120-127 " + tr("Sound effects");
577     else if (numPreset == 128)
578         strRet = tr("Percussion kit");
579 
580     return strRet;
581 }
582 
getDrumCategory(int numPreset)583 QString ConversionSfz::getDrumCategory(int numPreset)
584 {
585     QString strRet = tr("other");
586 
587     if (numPreset < 8)
588         strRet = "000-007 Standard kit";
589     else if (numPreset < 16)
590         strRet = "008-015 Room kit";
591     else if (numPreset < 24)
592         strRet = "016-023 Power kit";
593     else if (numPreset < 32)
594         strRet = "024-031 Electronic kit";
595     else if (numPreset < 40)
596         strRet = "032-039 Jazz kit";
597     else if (numPreset < 48)
598         strRet = "040-047 Brush kit";
599     else if (numPreset < 56)
600         strRet = "048-055 Orchestra kit";
601     else if (numPreset < 64)
602         strRet = "056-063 Sound FX kit";
603     else if (numPreset < 72)
604         strRet = "064-071 Additional kit";
605     else if (numPreset < 80)
606         strRet = "072-079 Additional kit";
607     else if (numPreset < 88)
608         strRet = "080-087 Additional kit";
609     else if (numPreset < 96)
610         strRet = "088-095 Additional kit";
611     else if (numPreset < 104)
612         strRet = "096-103 Additional kit";
613     else if (numPreset < 112)
614         strRet = "104-111 Additional kit";
615     else if (numPreset < 120)
616         strRet = "112-119 Additional kit";
617     else if (numPreset < 128)
618         strRet = "120-127 Additional kit";
619 
620     return strRet;
621 }
622 
isIncluded(SfzParamList * paramPrst,EltID idInstSmpl)623 bool ConversionSfz::isIncluded(SfzParamList * paramPrst, EltID idInstSmpl)
624 {
625     // Etendues de la division globale
626     int prstMinKey = 0, prstMaxKey = 127, prstMinVel = 0, prstMaxVel = 127;
627     int index = paramPrst->findChamp(champ_keyRange);
628     if (index != -1)
629     {
630         prstMinKey = qRound(paramPrst->getValeur(index) / 1000);
631         prstMaxKey = qRound(paramPrst->getValeur(index) - prstMinKey * 1000);
632     }
633     index = paramPrst->findChamp(champ_velRange);
634     if (index != -1)
635     {
636         prstMinVel = qRound(paramPrst->getValeur(index) / 1000);
637         prstMaxVel = qRound(paramPrst->getValeur(index) - prstMinVel * 1000);
638     }
639 
640     // Etendues de la division de l'instrument
641     int instMinKey = 0, instMaxKey = 127, instMinVel = 0, instMaxVel = 127;
642     if (_sf2->isSet(idInstSmpl, champ_keyRange))
643     {
644         RangesType range = _sf2->get(idInstSmpl, champ_keyRange).rValue;
645         instMinKey = range.byLo;
646         instMaxKey = range.byHi;
647     }
648     if (_sf2->isSet(idInstSmpl, champ_velRange))
649     {
650         RangesType range = _sf2->get(idInstSmpl, champ_velRange).rValue;
651         instMinVel = range.byLo;
652         instMaxVel = range.byHi;
653     }
654 
655     return instMinKey <= prstMaxKey && instMaxKey >= prstMinKey &&
656             instMinVel <= prstMaxVel && instMaxVel >= prstMinVel;
657 }
658