Skip to content

Commit 5e1077f

Browse files
committed
update audio plugin
1 parent 17b3237 commit 5e1077f

File tree

11 files changed

+753
-44
lines changed

11 files changed

+753
-44
lines changed

src/plugins/audioplugin/audio/exporter/audioexporter.cpp

Lines changed: 134 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
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

src/plugins/audioplugin/audio/exporter/audioexporter.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ namespace Core {
1111
}
1212

1313
namespace Audio {
14+
15+
16+
namespace Internal {
17+
class AudioExportDialog;
18+
}
19+
1420
class AudioExporter;
1521

1622
class AudioExporterConfigData;
@@ -39,6 +45,9 @@ namespace Audio {
3945
bool formatMono() const;
4046
void setFormatMono(bool);
4147

48+
[[nodiscard]] static QStringList formatOptionsOfType(FileType type);
49+
[[nodiscard]] static QString extensionOfType(FileType type);
50+
4251
int formatOption() const;
4352
void setFormatOption(int);
4453

@@ -80,6 +89,8 @@ namespace Audio {
8089
QVariantMap toVariantMap() const;
8190
[[nodiscard]] static AudioExporterConfig fromVariantMap(const QVariantMap &map);
8291

92+
bool operator==(const AudioExporterConfig &other) const;
93+
8394
private:
8495
QSharedDataPointer<AudioExporterConfigData> d;
8596
};
@@ -93,6 +104,7 @@ namespace Audio {
93104
class AudioExporter : public QObject, public talcs::ErrorStringProvider {
94105
Q_OBJECT
95106
Q_DECLARE_PRIVATE(AudioExporter)
107+
friend class Internal::AudioExportDialog;
96108
public:
97109

98110
explicit AudioExporter(Core::IProjectWindow *window, QObject *parent = nullptr);
@@ -132,10 +144,12 @@ namespace Audio {
132144
Result exec();
133145

134146
void cancel(bool isFail = false, const QString &message = {});
147+
void addWarning(const QString &message, int sourceIndex = -1);
135148

136149
signals:
137150
void progressChanged(double progressRatio, int sourceIndex);
138151
void clippingDetected(int sourceIndex);
152+
void warningAdded(const QString &message, int sourceIndex);
139153

140154
private:
141155
QScopedPointer<AudioExporterPrivate> d_ptr;

src/plugins/audioplugin/audio/exporter/audioexporter_p.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ namespace Audio {
4545
bool calculateTemplate(QString &templateString) const;
4646
bool calculateTemplate(QString &templateString, const QString &trackName, int trackIndex) const;
4747

48+
int calculateFormat() const;
49+
4850
AudioExporter::Warning warning;
4951
QStringList fileList;
5052
void updateFileListAndWarnings();

src/plugins/audioplugin/internal/addon/projectaddon.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,13 @@ namespace Audio::Internal {
8383
m_failedAudioClipsToAlert.clear();
8484
return ret;
8585
}
86+
void ProjectAddOn::setData(const QString &key, const QVariant &value) {
87+
m_dataDict.insert(key, value);
88+
}
89+
QVariant ProjectAddOn::data(const QString &key) const {
90+
return m_dataDict.value(key);
91+
}
92+
bool ProjectAddOn::hasData(const QString &key) const {
93+
return m_dataDict.contains(key);
94+
}
8695
}

src/plugins/audioplugin/internal/addon/projectaddon.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,19 @@ namespace Audio::Internal {
5050
void addFailedAudioClipToAlert(QDspx::AudioClipEntity *entity);
5151
QList<QDspx::AudioClipEntity *> takeFailedAudioClipsToAlert();
5252

53+
void setData(const QString &key, const QVariant &value);
54+
QVariant data(const QString &key) const;
55+
bool hasData(const QString &key) const;
56+
5357
private:
5458
AudioContextInterface *m_audioContextInterface;
5559
talcs::DspxProjectContext *m_projectContext;
5660

5761
QHash<QDspx::AudioClipEntity *, QString> m_audioClipsToOpenFile;
5862

5963
QList<QDspx::AudioClipEntity *> m_failedAudioClipsToAlert;
64+
65+
QHash<QString, QVariant> m_dataDict;
6066
};
6167

6268
}

0 commit comments

Comments
 (0)