11#include " audioexporter.h"
22#include " audioexporter_p.h"
33
4+ #include < QApplication>
45#include < QDir>
56#include < QFileInfo>
67#include < QVariant>
@@ -51,6 +52,41 @@ namespace Audio {
5152 void AudioExporterConfig::setFormatMono (bool a_) {
5253 d->formatMono = a_;
5354 }
55+ QStringList AudioExporterConfig::formatOptionsOfType (FileType type) {
56+ switch (type) {
57+ case FT_Wav:
58+ return {
59+ QApplication::translate (" Audio::AudioExporterConfig" ,
60+ " 32-bit float (IEEE 754)" ),
61+ QApplication::translate (" Audio::AudioExporterConfig" , " 24-bit PCM" ),
62+ QApplication::translate (" Audio::AudioExporterConfig" , " 16-bit PCM" ),
63+ QApplication::translate (" Audio::AudioExporterConfig" , " Unsigned 8-bit PCM" ),
64+ };
65+ case FT_Flac:
66+ return {
67+ QApplication::translate (" Audio::AudioExporterConfig" , " 24-bit PCM" ),
68+ QApplication::translate (" Audio::AudioExporterConfig" , " 16-bit PCM" ),
69+ QApplication::translate (" Audio::AudioExporterConfig" , " 8-bit PCM" ),
70+ };
71+ default :
72+ return {};
73+ }
74+ }
75+
76+ QString AudioExporterConfig::extensionOfType (FileType type) {
77+ switch (type) {
78+ case FT_Wav:
79+ return QStringLiteral (" wav" );
80+ case FT_Flac:
81+ return QStringLiteral (" flac" );
82+ case FT_OggVorbis:
83+ return QStringLiteral (" ogg" );
84+ case FT_Mp3:
85+ return QStringLiteral (" mp3" );
86+ }
87+ return {};
88+ }
89+
5490 int AudioExporterConfig::formatOption () const {
5591 return d->formatOption ;
5692 }
@@ -132,6 +168,19 @@ namespace Audio {
132168 config.d ->timeRange = static_cast <TimeRange>(map.value (" timeRange" ).toInt ());
133169 return config;
134170 }
171+ bool AudioExporterConfig::operator ==(const AudioExporterConfig &other) const {
172+ return d->fileName == other.d ->fileName
173+ && d->fileDirectory == other.d ->fileDirectory
174+ && d->fileType == other.d ->fileType
175+ && d->formatMono == other.d ->formatMono
176+ && d->formatOption == other.d ->formatOption
177+ && d->formatQuality == other.d ->formatQuality
178+ && d->formatSampleRate == other.d ->formatSampleRate
179+ && d->mixingOption == other.d ->mixingOption
180+ && d->isMuteSoloEnabled == other.d ->isMuteSoloEnabled
181+ && d->sourceOption == other.d ->sourceOption
182+ && d->timeRange == other.d ->timeRange ;
183+ }
135184
136185 QString AudioExporterPrivate::projectName () const {
137186 // project file's base name
@@ -173,7 +222,7 @@ namespace Audio {
173222 if (templateName == " projectName" ) {
174223 replacedText = projectName ();
175224 } else if (templateName == " sampleRate" ) {
176- replacedText = QString::number (config.formatSampleRate ());
225+ replacedText = QString::number (config.formatSampleRate ()). replace ( ' . ' , ' _ ' ) ;
177226 } else if (templateName == " today" ) {
178227 replacedText = QDate::currentDate ().toString (" yyyyMMdd" );
179228 } else if (templateName == " $" ) {
@@ -183,6 +232,8 @@ namespace Audio {
183232 replacedText = trackName;
184233 } else if (templateName == " trackIndex" ) {
185234 replacedText = QString::number (trackIndex + 1 );
235+ } else {
236+ allTemplatesMatch = false ;
186237 }
187238 } else {
188239 allTemplatesMatch = false ;
@@ -195,19 +246,64 @@ namespace Audio {
195246 templateString = result;
196247 return allTemplatesMatch;
197248 }
249+
250+ int AudioExporterPrivate::calculateFormat () const {
251+ int format = 0 ;
252+ switch (config.fileType ()) {
253+ case AudioExporterConfig::FT_Wav:
254+ format |= talcs::AudioFormatIO::WAV;
255+ switch (config.formatOption ()) {
256+ case 0 :
257+ format |= talcs::AudioFormatIO::FLOAT;
258+ return format;
259+ case 1 :
260+ format |= talcs::AudioFormatIO::PCM_24;
261+ return format;
262+ case 2 :
263+ format |= talcs::AudioFormatIO::PCM_16;
264+ return format;
265+ case 3 :
266+ format |= talcs::AudioFormatIO::PCM_U8;
267+ return format;
268+ }
269+ break ;
270+ case AudioExporterConfig::FT_Flac:
271+ format |= talcs::AudioFormatIO::FLAC;
272+ switch (config.formatOption ()) {
273+ case 0 :
274+ format |= talcs::AudioFormatIO::PCM_24;
275+ return format;
276+ case 1 :
277+ format |= talcs::AudioFormatIO::PCM_16;
278+ return format;
279+ case 2 :
280+ format |= talcs::AudioFormatIO::PCM_S8;
281+ return format;
282+ }
283+ break ;
284+ case AudioExporterConfig::FT_OggVorbis:
285+ format |= (talcs::AudioFormatIO::OGG | talcs::AudioFormatIO::VORBIS);
286+ return format;
287+ case AudioExporterConfig::FT_Mp3:
288+ format |= (talcs::AudioFormatIO::MPEG | talcs::AudioFormatIO::MPEG_LAYER_III);
289+ return format;
290+ }
291+ return 0 ;
292+ }
293+
198294 void AudioExporterPrivate::updateFileListAndWarnings () {
199295 warning = {};
200296 if (config.fileType () == AudioExporterConfig::FT_Mp3 || config.fileType () == AudioExporterConfig::FT_OggVorbis)
201297 warning |= AudioExporter::W_LossyFormat;
202298 fileList.clear ();
203- if (config.sourceOption () == AudioExporterConfig::SO_All ) {
299+ if (config.mixingOption () == AudioExporterConfig::MO_Mixed ) {
204300 auto calculatedFileName = config.fileName ();
205301 if (!calculateTemplate (calculatedFileName))
206302 warning |= AudioExporter::W_UnrecognizedTemplate;
207- auto fileInfo = QFileInfo (QDir (projectDirectory ()).absoluteFilePath (calculatedFileName));
303+ auto fileInfo = QFileInfo (QDir (QDir ( projectDirectory ()). absoluteFilePath (config. fileDirectory () )).absoluteFilePath (calculatedFileName));
208304 if (fileInfo.exists ())
209305 warning |= AudioExporter::W_WillOverwrite;
210- fileList.append (fileInfo.canonicalFilePath ());
306+ fileList.append (fileInfo.absoluteFilePath ());
211307 } else {
212308 if (config.source ().isEmpty ())
213309 warning |= AudioExporter::W_NoFile;
@@ -216,13 +312,13 @@ namespace Audio {
216312 auto calculatedFileName = config.fileName ();
217313 if (!calculateTemplate (calculatedFileName, trackName (index), index))
218314 warning |= AudioExporter::W_UnrecognizedTemplate;
219- auto fileInfo = QFileInfo (QDir (projectDirectory ()).absoluteFilePath (calculatedFileName));
315+ auto fileInfo = QFileInfo (QDir (QDir ( projectDirectory ()). absoluteFilePath (config. fileDirectory () )).absoluteFilePath (calculatedFileName));
220316 if (fileInfo.exists ())
221317 warning |= AudioExporter::W_WillOverwrite;
222- if (fileSet.contains (fileInfo.canonicalFilePath ()))
318+ if (fileSet.contains (fileInfo.absoluteFilePath ()))
223319 warning |= AudioExporter::W_DuplicatedFile;
224- fileSet.insert (fileInfo.canonicalFilePath ());
225- fileList.append (fileInfo.canonicalFilePath ());
320+ fileSet.insert (fileInfo.absoluteFilePath ());
321+ fileList.append (fileInfo.absoluteFilePath ());
226322 }
227323 }
228324 }
@@ -234,6 +330,11 @@ namespace Audio {
234330 }
235331 AudioExporter::~AudioExporter () = default ;
236332
333+ Core::IProjectWindow *AudioExporter::windowHandle () const {
334+ Q_D (const AudioExporter);
335+ return d->windowHandle ;
336+ }
337+
237338 QStringList AudioExporter::presets () {
238339 return AudioSettings::audioExporterPresets ().toObject ().keys ();
239340 }
@@ -261,7 +362,7 @@ namespace Audio {
261362 {" formatOption" , 0 },
262363 {" formatQuality" , 100 },
263364 {" formatSampleRate" , 48000 },
264- {" mixingOption" , AudioExporterConfig::MO_Separated },
365+ {" mixingOption" , AudioExporterConfig::MO_SeparatedThruMaster },
265366 {" isMuteSoloEnabled" , true },
266367 {" sourceOption" , AudioExporterConfig::SO_All},
267368 {" source" , {}},
@@ -289,7 +390,7 @@ namespace Audio {
289390 {" formatOption" , 0 },
290391 {" formatQuality" , 100 },
291392 {" formatSampleRate" , 48000 },
292- {" mixingOption" , AudioExporterConfig::MO_Separated },
393+ {" mixingOption" , AudioExporterConfig::MO_SeparatedThruMaster },
293394 {" isMuteSoloEnabled" , true },
294395 {" sourceOption" , AudioExporterConfig::SO_All},
295396 {" source" , {}},
@@ -317,7 +418,7 @@ namespace Audio {
317418 {" formatOption" , 0 },
318419 {" formatQuality" , 100 },
319420 {" formatSampleRate" , 48000 },
320- {" mixingOption" , AudioExporterConfig::MO_Separated },
421+ {" mixingOption" , AudioExporterConfig::MO_SeparatedThruMaster },
321422 {" isMuteSoloEnabled" , true },
322423 {" sourceOption" , AudioExporterConfig::SO_All},
323424 {" source" , {}},
@@ -382,13 +483,13 @@ namespace Audio {
382483 list.append (tr (" The files to be exported contain files with duplicate names. Please check if the file name template is unique for each source." ));
383484 }
384485 if (warning & W_WillOverwrite) {
385- list.append (tr (" The files to be exported contain files with the same name as existing files." ));
486+ list.append (tr (" The files to be exported contain files with the same name as existing files. If continue, the existing files will be overwritten. " ));
386487 }
387488 if (warning & W_UnrecognizedTemplate) {
388489 list.append (tr (" Unrecognized file name template. Please check the syntax of the file name template." ));
389490 }
390491 if (warning & W_LossyFormat) {
391- list.append (tr (" Currently selected file type is a lossy format. Please use WAV or FLAC format to avoid loss of sound quality ." ));
492+ list.append (tr (" The currently selected file type is a lossy format. To avoid loss of sound quality, please use WAV or FLAC format ." ));
392493 }
393494 return list;
394495 }
@@ -419,18 +520,18 @@ namespace Audio {
419520 io.setStream (file);
420521 io.setSampleRate (config.formatSampleRate ());
421522 io.setChannelCount (config.formatMono () ? 1 : 2 );
422- io.setFormat (talcs::AudioFormatIO::WAV | talcs::AudioFormatIO::FLOAT); // TODO calculte AudioFormatIO format from config
423- io.setCompressionLevel (0.01 * (100 - config.formatQuality ()));
523+ io.setFormat (d->calculateFormat ());
424524 if (!io.open (talcs::AbstractAudioFormatIO::Write)) {
425525 setErrorString (tr (" Format not supported: %1" ).arg (io.errorString ()));
426526 return R_Fail;
427527 }
528+ io.setCompressionLevel (0.01 * (100 - config.formatQuality ()));
428529 }
429530
430531 // create and configure talcs::DspxProjectAudioExporter
431532 talcs::DspxProjectAudioExporter exporter (projectContext);
432533 auto cleanup = [=](void *) {d->currentExporter = nullptr ;};
433- std::unique_ptr<void , decltype (cleanup)> _1 (nullptr , cleanup);
534+ std::unique_ptr<void , decltype (cleanup)> _1 (this , cleanup);
434535 d->currentExporter = &exporter;
435536 exporter.setMonoChannel (config.formatMono ());
436537 exporter.setThruMaster (config.mixingOption () == AudioExporterConfig::MO_SeparatedThruMaster);
@@ -471,16 +572,26 @@ namespace Audio {
471572 if (!projectContext->preMixer ()->open (currentBufferSize, currentSampleRate))
472573 qDebug () << " AudioExporter: Cannot reopen pre-mixer after exported" ;
473574 };
474- std::unique_ptr<void , decltype (reopenMixer)> _2 (nullptr , reopenMixer);
575+ std::unique_ptr<void , decltype (reopenMixer)> _2 (this , reopenMixer);
475576 if (!projectContext->preMixer ()->open (1024 , config.formatSampleRate ())) { // TODO let user configure buffer size in settings
476577 setErrorString (tr (" Cannot start audio exporting" ));
477578 return R_Fail;
478579 }
479580
480581 // call listeners
582+ QList<AudioExporterListener *> listenerToCallFinishList;
583+ auto callFinish = [&](void *) {
584+ for (auto listener : listenerToCallFinishList) {
585+ listener->willFinishCallback (this );
586+ }
587+ };
588+ std::unique_ptr<void , decltype (callFinish)> _3 (this , callFinish);
589+ // Note: order of destruction: call AudioExporterListener::willFinish after mixer reopened
590+ std::unique_ptr<void , decltype (reopenMixer)> _4 = std::move (_2);
481591 for (auto listener : m_listeners) {
482592 if (!listener->willStartCallback (this ))
483593 return R_Fail;
594+ listenerToCallFinishList.prepend (listener);
484595 }
485596
486597 // start exporting
@@ -491,9 +602,9 @@ namespace Audio {
491602 emit clippingDetected (sourceIndexMap.value (track));
492603 });
493604 auto ret = exporter.exec ();
494- if (ret & talcs::DspxProjectAudioExporter::OK)
605+ if (ret == talcs::DspxProjectAudioExporter::OK)
495606 return R_OK;
496- if (ret & talcs::DspxProjectAudioExporter::Interrupted)
607+ if (ret == talcs::DspxProjectAudioExporter::Interrupted)
497608 return R_Abort;
498609
499610 if (errorString ().isEmpty ())
@@ -509,4 +620,8 @@ namespace Audio {
509620 setErrorString (message.isEmpty () ? tr (" Unknown error" ) : message);
510621 d->currentExporter ->interrupt (isFail);
511622 }
623+
624+ void AudioExporter::addWarning (const QString &message, int sourceIndex) {
625+ emit warningAdded (message, sourceIndex);
626+ }
512627} // Audio
0 commit comments