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