Audacity 3.2.0
ExportFLAC.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5ExportFLAC.cpp
6
7Frederik M.J.V
8
9This program is distributed under the GNU General Public License, version 2.
10A copy of this license is included with this source.
11
12Based on ExportOGG.cpp by:
13Joshua Haberman
14
15**********************************************************************/
16
17
18
19#ifdef USE_LIBFLAC
20
21#include "Export.h"
22
23#include <wx/ffile.h>
24#include <wx/log.h>
25
26#include "FLAC++/encoder.h"
27
28#include "float_cast.h"
29#include "ProjectRate.h"
30#include "Mix.h"
31#include "Prefs.h"
32#include "../ShuttleGui.h"
33
34#include "../Tags.h"
35#include "Track.h"
36
37#include "../widgets/AudacityMessageBox.h"
38#include "../widgets/ProgressDialog.h"
39#include "wxFileNameWrapper.h"
40
41//----------------------------------------------------------------------------
42// ExportFLACOptions Class
43//----------------------------------------------------------------------------
44
46{
47public:
48
49 ExportFLACOptions(wxWindow *parent, int format);
50 virtual ~ExportFLACOptions();
51
53 bool TransferDataToWindow() override;
54 bool TransferDataFromWindow() override;
55};
56
59ExportFLACOptions::ExportFLACOptions(wxWindow *parent, int WXUNUSED(format))
60: wxPanelWrapper(parent, wxID_ANY)
61{
64
66}
67
71{
73}
74
76 wxT("/FileFormats/FLACBitDepth"),
77 {
79 { XO("16 bit") , XO("24 bit") , },
80 { wxT("16") , wxT("24") , }
81 },
82 0 // "16",
83};
84
86 wxT("/FileFormats/FLACLevel"),
87 {
89 {
90 XO("0 (fastest)") ,
91 XO("1") ,
92 XO("2") ,
93 XO("3") ,
94 XO("4") ,
95 XO("5") ,
96 XO("6") ,
97 XO("7") ,
98 XO("8 (best)") ,
99 },
100 {
101 wxT("0") ,
102 wxT("1") ,
103 wxT("2") ,
104 wxT("3") ,
105 wxT("4") ,
106 wxT("5") ,
107 wxT("6") ,
108 wxT("7") ,
109 wxT("8") ,
110 }
111 },
112 5 //"5"
113};
114
118{
119 S.StartVerticalLay();
120 {
121 S.StartHorizontalLay(wxCENTER);
122 {
123 S.StartMultiColumn(2, wxCENTER);
124 {
125 S.TieChoice( XXO("Level:"), FLACLevel);
126 S.TieChoice( XXO("Bit depth:"), FLACBitDepth);
127 }
128 S.EndMultiColumn();
129 }
130 S.EndHorizontalLay();
131 }
132 S.EndVerticalLay();
133
134 return;
135}
136
140{
141 return true;
142}
143
147{
150
151 gPrefs->Flush();
152
153 return true;
154}
155
156//----------------------------------------------------------------------------
157// ExportFLAC Class
158//----------------------------------------------------------------------------
159
160#define SAMPLES_PER_RUN 8192u
161
162/* FLACPP_API_VERSION_CURRENT is 6 for libFLAC++ from flac-1.1.3 (see <FLAC++/export.h>) */
163#if !defined FLACPP_API_VERSION_CURRENT || FLACPP_API_VERSION_CURRENT < 6
164#define LEGACY_FLAC
165#else
166#undef LEGACY_FLAC
167#endif
168
169static struct
170{
180} flacLevels[] = {
181 { false, false, false, false, 0, 2, 2, 0, 0 },
182 { false, false, true, true, 0, 2, 2, 0, 0 },
183 { false, false, true, false, 0, 0, 3, 0, 0 },
184 { false, false, false, false, 0, 3, 3, 0, 6 },
185 { false, false, true, true, 0, 3, 3, 0, 8 },
186 { false, false, true, false, 0, 3, 3, 0, 8 },
187 { false, false, true, false, 0, 0, 4, 0, 8 },
188 { true, false, true, false, 0, 0, 6, 0, 8 },
189 { true, false, true, false, 0, 0, 6, 0, 12 },
191
192//----------------------------------------------------------------------------
193
195 void operator () (FLAC__StreamMetadata *p) const
196 { if (p) ::FLAC__metadata_object_delete(p); }
197};
198using FLAC__StreamMetadataHandle = std::unique_ptr<
199 FLAC__StreamMetadata, FLAC__StreamMetadataDeleter
200>;
201
202class ExportFLAC final : public ExportPlugin
203{
204public:
205
206 ExportFLAC();
207
208 // Required
209
210 void OptionsCreate(ShuttleGui &S, int format) override;
212 std::unique_ptr<ProgressDialog> &pDialog,
213 unsigned channels,
214 const wxFileNameWrapper &fName,
215 bool selectedOnly,
216 double t0,
217 double t1,
218 MixerSpec *mixerSpec = NULL,
219 const Tags *metadata = NULL,
220 int subformat = 0) override;
221
222private:
223
224 bool GetMetadata(AudacityProject *project, const Tags *tags);
225
226 // Should this be a stack variable instead in Export?
228};
229
230//----------------------------------------------------------------------------
231
233: ExportPlugin()
234{
235 AddFormat();
236 SetFormat(wxT("FLAC"),0);
237 AddExtension(wxT("flac"),0);
238 SetMaxChannels(FLAC__MAX_CHANNELS,0);
239 SetCanMetaData(true,0);
240 SetDescription(XO("FLAC Files"),0);
241}
242
244 std::unique_ptr<ProgressDialog> &pDialog,
245 unsigned numChannels,
246 const wxFileNameWrapper &fName,
247 bool selectionOnly,
248 double t0,
249 double t1,
250 MixerSpec *mixerSpec,
251 const Tags *metadata,
252 int WXUNUSED(subformat))
253{
254 double rate = ProjectRate::Get(*project).GetRate();
255 const auto &tracks = TrackList::Get( *project );
256
257 wxLogNull logNo; // temporarily disable wxWidgets error messages
258 auto updateResult = ProgressResult::Success;
259
260 long levelPref;
261 FLACLevel.Read().ToLong( &levelPref );
262
263 auto bitDepthPref = FLACBitDepth.Read();
264
265 FLAC::Encoder::File encoder;
266
267 bool success = true;
268 success = success &&
269#ifdef LEGACY_FLAC
270 encoder.set_filename(OSOUTPUT(fName)) &&
271#endif
272 encoder.set_channels(numChannels) &&
273 encoder.set_sample_rate(lrint(rate));
274
275 // See note in GetMetadata() about a bug in libflac++ 1.1.2
276 if (success && !GetMetadata(project, metadata)) {
277 // TODO: more precise message
278 ShowExportErrorDialog("FLAC:283");
280 }
281
282 if (success && mMetadata) {
283 // set_metadata expects an array of pointers to metadata and a size.
284 // The size is 1.
285 FLAC__StreamMetadata *p = mMetadata.get();
286 success = encoder.set_metadata(&p, 1);
287 }
288
289 auto cleanup1 = finally( [&] {
290 mMetadata.reset(); // need this?
291 } );
292
294 if (bitDepthPref == wxT("24")) {
296 success = success && encoder.set_bits_per_sample(24);
297 } else { //convert float to 16 bits
299 success = success && encoder.set_bits_per_sample(16);
300 }
301
302
303 // Duplicate the flac command line compression levels
304 if (levelPref < 0 || levelPref > 8) {
305 levelPref = 5;
306 }
307 success = success &&
308 encoder.set_do_exhaustive_model_search(flacLevels[levelPref].do_exhaustive_model_search) &&
309 encoder.set_do_escape_coding(flacLevels[levelPref].do_escape_coding);
310
311 if (numChannels != 2) {
312 success = success &&
313 encoder.set_do_mid_side_stereo(false) &&
314 encoder.set_loose_mid_side_stereo(false);
315 }
316 else {
317 success = success &&
318 encoder.set_do_mid_side_stereo(flacLevels[levelPref].do_mid_side_stereo) &&
319 encoder.set_loose_mid_side_stereo(flacLevels[levelPref].loose_mid_side_stereo);
320 }
321
322 success = success &&
323 encoder.set_qlp_coeff_precision(flacLevels[levelPref].qlp_coeff_precision) &&
324 encoder.set_min_residual_partition_order(flacLevels[levelPref].min_residual_partition_order) &&
325 encoder.set_max_residual_partition_order(flacLevels[levelPref].max_residual_partition_order) &&
326 encoder.set_rice_parameter_search_dist(flacLevels[levelPref].rice_parameter_search_dist) &&
327 encoder.set_max_lpc_order(flacLevels[levelPref].max_lpc_order);
328
329 if (!success) {
330 // TODO: more precise message
331 ShowExportErrorDialog("FLAC:336");
333 }
334
335#ifdef LEGACY_FLAC
336 encoder.init();
337#else
338 wxFFile f; // will be closed when it goes out of scope
339 const auto path = fName.GetFullPath();
340 if (!f.Open(path, wxT("w+b"))) {
341 AudacityMessageBox( XO("FLAC export couldn't open %s").Format( path ) );
343 }
344
345 // Even though there is an init() method that takes a filename, use the one that
346 // takes a file handle because wxWidgets can open a file with a Unicode name and
347 // libflac can't (under Windows).
348 int status = encoder.init(f.fp());
349 if (status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
351 XO("FLAC encoder failed to initialize\nStatus: %d")
352 .Format( status ) );
354 }
355#endif
356
357 mMetadata.reset();
358
359 auto cleanup2 = finally( [&] {
360 if (!(updateResult == ProgressResult::Success ||
361 updateResult == ProgressResult::Stopped)) {
362#ifndef LEGACY_FLAC
363 f.Detach(); // libflac closes the file
364#endif
365 encoder.finish();
366 }
367 } );
368
369 auto mixer = CreateMixer(tracks, selectionOnly,
370 t0, t1,
371 numChannels, SAMPLES_PER_RUN, false,
372 rate, format, mixerSpec);
373
374 ArraysOf<FLAC__int32> tmpsmplbuf{ numChannels, SAMPLES_PER_RUN, true };
375
376 InitProgress( pDialog, fName,
377 selectionOnly
378 ? XO("Exporting the selected audio as FLAC")
379 : XO("Exporting the audio as FLAC") );
380 auto &progress = *pDialog;
381
382 while (updateResult == ProgressResult::Success) {
383 auto samplesThisRun = mixer->Process(SAMPLES_PER_RUN);
384 if (samplesThisRun == 0) { //stop encoding
385 break;
386 }
387 else {
388 for (size_t i = 0; i < numChannels; i++) {
389 auto mixed = mixer->GetBuffer(i);
390 if (format == int24Sample) {
391 for (decltype(samplesThisRun) j = 0; j < samplesThisRun; j++) {
392 tmpsmplbuf[i][j] = ((const int *)mixed)[j];
393 }
394 }
395 else {
396 for (decltype(samplesThisRun) j = 0; j < samplesThisRun; j++) {
397 tmpsmplbuf[i][j] = ((const short *)mixed)[j];
398 }
399 }
400 }
401 if (! encoder.process(
402 reinterpret_cast<FLAC__int32**>( tmpsmplbuf.get() ),
403 samplesThisRun) ) {
404 // TODO: more precise message
406 updateResult = ProgressResult::Cancelled;
407 break;
408 }
409 if (updateResult == ProgressResult::Success)
410 updateResult =
411 progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
412 }
413 }
414
415 if (updateResult == ProgressResult::Success ||
416 updateResult == ProgressResult::Stopped) {
417#ifndef LEGACY_FLAC
418 f.Detach(); // libflac closes the file
419#endif
420 if (!encoder.finish())
421 // Do not reassign updateResult, see cleanup2
422 return ProgressResult::Failed;
423#ifdef LEGACY_FLAC
424 if (!f.Flush() || !f.Close())
425 return ProgressResult::Failed;
426#endif
427 }
428
429 return updateResult;
430}
431
433{
434 S.AddWindow( safenew ExportFLACOptions{ S.GetParent(), format } );
435}
436
437// LL: There's a bug in libflac++ 1.1.2 that prevents us from using
438// FLAC::Metadata::VorbisComment directly. The set_metadata()
439// function allocates an array on the stack, but the base library
440// expects that array to be valid until the stream is initialized.
441//
442// This has been fixed in 1.1.4.
444{
445 // Retrieve tags if needed
446 if (tags == NULL)
447 tags = &Tags::Get( *project );
448
449 mMetadata.reset(::FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT));
450
451 wxString n;
452 for (const auto &pair : tags->GetRange()) {
453 n = pair.first;
454 const auto &v = pair.second;
455 if (n == TAG_YEAR) {
456 n = wxT("DATE");
457 }
458 else if (n == TAG_COMMENTS) {
459 // Some apps like Foobar use COMMENT and some like Windows use DESCRIPTION,
460 // so add both to try and make everyone happy.
461 n = wxT("COMMENT");
462 FLAC::Metadata::VorbisComment::Entry entry(n.mb_str(wxConvUTF8),
463 v.mb_str(wxConvUTF8));
464 if (! ::FLAC__metadata_object_vorbiscomment_append_comment(mMetadata.get(),
465 entry.get_entry(),
466 true) ) {
467 return false;
468 }
469 n = wxT("DESCRIPTION");
470 }
471 FLAC::Metadata::VorbisComment::Entry entry(n.mb_str(wxConvUTF8),
472 v.mb_str(wxConvUTF8));
473 if (! ::FLAC__metadata_object_vorbiscomment_append_comment(mMetadata.get(),
474 entry.get_entry(),
475 true) ) {
476 return false;
477 }
478 }
479
480 return true;
481}
482
484 []{ return std::make_unique< ExportFLAC >(); }
485};
486
487#endif // USE_LIBFLAC
488
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
void ShowExportErrorDialog(wxString ErrorCode, TranslatableString message, const TranslatableString &caption, bool allowReporting)
Definition: Export.cpp:1520
void ShowDiskFullExportErrorDialog(const wxFileNameWrapper &fileName)
Definition: Export.cpp:1533
bool do_escape_coding
Definition: ExportFLAC.cpp:172
unsigned max_lpc_order
Definition: ExportFLAC.cpp:179
static struct @42 flacLevels[]
#define SAMPLES_PER_RUN
Definition: ExportFLAC.cpp:160
unsigned min_residual_partition_order
Definition: ExportFLAC.cpp:176
unsigned rice_parameter_search_dist
Definition: ExportFLAC.cpp:178
bool do_exhaustive_model_search
Definition: ExportFLAC.cpp:171
bool do_mid_side_stereo
Definition: ExportFLAC.cpp:173
static Exporter::RegisteredExportPlugin sRegisteredPlugin
Definition: ExportFLAC.cpp:483
unsigned max_residual_partition_order
Definition: ExportFLAC.cpp:177
bool loose_mid_side_stereo
Definition: ExportFLAC.cpp:174
ChoiceSetting FLACLevel
Definition: ExportFLAC.cpp:85
std::unique_ptr< FLAC__StreamMetadata, FLAC__StreamMetadataDeleter > FLAC__StreamMetadataHandle
Definition: ExportFLAC.cpp:200
unsigned qlp_coeff_precision
Definition: ExportFLAC.cpp:175
ChoiceSetting FLACBitDepth
Definition: ExportFLAC.cpp:75
int format
Definition: ExportPCM.cpp:56
#define XXO(s)
Definition: Internat.h:44
#define XO(s)
Definition: Internat.h:31
#define safenew
Definition: MemoryX.h:10
FileConfig * gPrefs
Definition: Prefs.cpp:71
ByColumns_t ByColumns
Definition: Prefs.cpp:465
an object holding per-project preferred sample rate
static ProjectFileIORegistry::AttributeWriterEntry entry
sampleFormat
Definition: SampleFormat.h:29
@ int16Sample
Definition: SampleFormat.h:32
@ int24Sample
Definition: SampleFormat.h:33
#define OSOUTPUT(X)
Definition: SelectFile.h:48
@ eIsCreatingFromPrefs
Definition: ShuttleGui.h:48
@ eIsSavingToPrefs
Definition: ShuttleGui.h:49
#define TAG_COMMENTS
Definition: Tags.h:64
#define TAG_YEAR
Definition: Tags.h:62
#define S(N)
Definition: ToChars.cpp:64
declares abstract base class Track, TrackList, and iterators over TrackList
memory.h template class for making an array of arrays.
Definition: MemoryX.h:81
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
wxString Read() const
Definition: Prefs.cpp:327
ProgressResult Export(AudacityProject *project, std::unique_ptr< ProgressDialog > &pDialog, unsigned channels, const wxFileNameWrapper &fName, bool selectedOnly, double t0, double t1, MixerSpec *mixerSpec=NULL, const Tags *metadata=NULL, int subformat=0) override
called to export audio into a file.
Definition: ExportFLAC.cpp:243
FLAC__StreamMetadataHandle mMetadata
Definition: ExportFLAC.cpp:227
void OptionsCreate(ShuttleGui &S, int format) override
Definition: ExportFLAC.cpp:432
bool GetMetadata(AudacityProject *project, const Tags *tags)
Definition: ExportFLAC.cpp:443
void PopulateOrExchange(ShuttleGui &S)
Definition: ExportFLAC.cpp:117
bool TransferDataFromWindow() override
Definition: ExportFLAC.cpp:146
ExportFLACOptions(wxWindow *parent, int format)
Definition: ExportFLAC.cpp:59
bool TransferDataToWindow() override
Definition: ExportFLAC.cpp:139
virtual ~ExportFLACOptions()
Definition: ExportFLAC.cpp:70
void AddExtension(const FileExtension &extension, int index)
Definition: Export.cpp:127
int AddFormat()
Add a NEW entry to the list of formats this plug-in can export.
Definition: Export.cpp:101
void SetFormat(const wxString &format, int index)
Definition: Export.cpp:117
std::unique_ptr< Mixer > CreateMixer(const TrackList &tracks, bool selectionOnly, double startTime, double stopTime, unsigned numOutChannels, size_t outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, MixerSpec *mixerSpec)
Definition: Export.cpp:223
void SetDescription(const TranslatableString &description, int index)
Definition: Export.cpp:122
void SetCanMetaData(bool canmetadata, int index)
Definition: Export.cpp:147
static void InitProgress(std::unique_ptr< ProgressDialog > &pDialog, const TranslatableString &title, const TranslatableString &message)
Definition: Export.cpp:251
void SetMaxChannels(unsigned maxchannels, unsigned index)
Definition: Export.cpp:142
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
Abstract base class used in importing a file.
Class used with Mixer.
Definition: Mix.h:37
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
double GetRate() const
Definition: ProjectRate.cpp:53
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:631
ID3 Tags (for MP3)
Definition: Tags.h:73
Iterators GetRange() const
Definition: Tags.cpp:436
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:214
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:467
#define lrint(dbl)
Definition: float_cast.h:169
ProgressResult
Definition: BasicUI.h:145
void operator()(FLAC__StreamMetadata *p) const
Definition: ExportFLAC.cpp:195