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