Audacity  3.0.3
ImportFLAC.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  ImportFLAC.cpp
6 
7  Copyright 2004 Sami Liedes
8  Leland Lucius
9 
10  Based on ImportPCM.cpp by Dominic Mazzoni
11  Licensed under the GNU General Public License v2 or later
12 
13 *//****************************************************************//****************************************************************//*******************************************************************/
24 
25 
26 
27 // For compilers that support precompilation, includes "wx/wx.h".
28 #include <wx/wxprec.h>
29 
30 #ifndef WX_PRECOMP
31 // Include your minimal set of headers here, or wx.h
32 #include <wx/window.h>
33 #endif
34 
35 #include <wx/defs.h>
36 #include <wx/intl.h> // needed for _("translated strings") even if we
37  // don't have libflac available
38 
39 #include "Import.h"
40 #include "ImportPlugin.h"
41 
42 #include "../SelectFile.h"
43 #include "../Tags.h"
44 #include "../widgets/ProgressDialog.h"
45 
46 #define FLAC_HEADER "fLaC"
47 
48 #define DESC XO("FLAC files")
49 
50 static const auto exts = {
51  wxT("flac"),
52  wxT("flc")
53 };
54 
55 #ifndef USE_LIBFLAC
56 
58  std::make_unique<UnusableImportPlugin>
59  (DESC, FileExtensions( exts.begin(), exts.end() ) )
60 };
61 
62 #else /* USE_LIBFLAC */
63 
64 #include <wx/string.h>
65 #include <wx/utils.h>
66 #include <wx/file.h>
67 #include <wx/ffile.h>
68 
69 #include "FLAC++/decoder.h"
70 
71 #include "Prefs.h"
72 #include "../WaveTrack.h"
73 #include "ImportPlugin.h"
74 
75 #ifdef USE_LIBID3TAG
76 extern "C" {
77 #include <id3tag.h>
78 }
79 #endif
80 
81 /* FLACPP_API_VERSION_CURRENT is 6 for libFLAC++ from flac-1.1.3 (see <FLAC++/export.h>) */
82 #if !defined FLACPP_API_VERSION_CURRENT || FLACPP_API_VERSION_CURRENT < 6
83 #define LEGACY_FLAC
84 #else
85 #undef LEGACY_FLAC
86 #endif
87 
88 
90 using NewChannelGroup = std::vector< std::shared_ptr<WaveTrack> >;
91 
92 class MyFLACFile final : public FLAC::Decoder::File
93 {
94  public:
96  {
97  mWasError = false;
98  set_metadata_ignore_all();
99  set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT);
100  set_metadata_respond(FLAC__METADATA_TYPE_STREAMINFO);
101  }
102 
103  bool get_was_error() const
104  {
105  return mWasError;
106  }
107  private:
108  friend class FLACImportFileHandle;
110  bool mWasError;
111  wxArrayString mComments;
112  protected:
113  FLAC__StreamDecoderWriteStatus write_callback(const FLAC__Frame *frame,
114  const FLAC__int32 * const buffer[]) override;
115  void metadata_callback(const FLAC__StreamMetadata *metadata) override;
116  void error_callback(FLAC__StreamDecoderErrorStatus status) override;
117 };
118 
119 
120 class FLACImportPlugin final : public ImportPlugin
121 {
122  public:
124  ImportPlugin( FileExtensions( exts.begin(), exts.end() ) )
125  {
126  }
127 
129 
130  wxString GetPluginStringID() override { return wxT("libflac"); }
132  std::unique_ptr<ImportFileHandle> Open(
133  const FilePath &Filename, AudacityProject*) override;
134 };
135 
136 
138 {
139  friend class MyFLACFile;
140 public:
143 
144  bool Init();
145 
148  ProgressResult Import(WaveTrackFactory *trackFactory, TrackHolders &outTracks,
149  Tags *tags) override;
150 
151  wxInt32 GetStreamCount() override { return 1; }
152 
154  {
155  static TranslatableStrings empty;
156  return empty;
157  }
158 
159  void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
160  {}
161 
162 private:
164  std::unique_ptr<MyFLACFile> mFile;
165  wxFFile mHandle;
166  unsigned long mSampleRate;
167  unsigned long mNumChannels;
168  unsigned long mBitsPerSample;
169  FLAC__uint64 mNumSamples;
170  FLAC__uint64 mSamplesDone;
174 };
175 
176 
177 void MyFLACFile::metadata_callback(const FLAC__StreamMetadata *metadata)
178 {
179  switch (metadata->type)
180  {
181  case FLAC__METADATA_TYPE_VORBIS_COMMENT:
182  for (FLAC__uint32 i = 0; i < metadata->data.vorbis_comment.num_comments; i++) {
183  mComments.push_back(UTF8CTOWX((char *)metadata->data.vorbis_comment.comments[i].entry));
184  }
185  break;
186 
187  case FLAC__METADATA_TYPE_STREAMINFO:
188  mFile->mSampleRate=metadata->data.stream_info.sample_rate;
189  mFile->mNumChannels=metadata->data.stream_info.channels;
190  mFile->mBitsPerSample=metadata->data.stream_info.bits_per_sample;
191  mFile->mNumSamples=metadata->data.stream_info.total_samples;
192 
193  // Widen mFormat after examining the file header
194  if (mFile->mBitsPerSample<=16) {
196  } else if (mFile->mBitsPerSample<=24) {
198  } else {
200  }
201  mFile->mStreamInfoDone=true;
202  break;
203  // handle the other types we do nothing with to avoid a warning
204  case FLAC__METADATA_TYPE_PADDING: // do nothing with padding
205  case FLAC__METADATA_TYPE_APPLICATION: // no idea what to do with this
206  case FLAC__METADATA_TYPE_SEEKTABLE: // don't need a seektable here
207  case FLAC__METADATA_TYPE_CUESHEET: // convert this to labels?
208  case FLAC__METADATA_TYPE_PICTURE: // ignore pictures
209  case FLAC__METADATA_TYPE_UNDEFINED: // do nothing with this either
210 
211  // FIXME: not declared when compiling on Ubuntu.
212  //case FLAC__MAX_METADATA_TYPE: // quiet compiler warning with this line
213  default:
214  break;
215  }
216 }
217 
218 void MyFLACFile::error_callback(FLAC__StreamDecoderErrorStatus WXUNUSED(status))
219 {
220  mWasError = true;
221 
222  /*
223  switch (status)
224  {
225  case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
226  wxPrintf(wxT("Flac Error: Lost sync\n"));
227  break;
228  case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
229  wxPrintf(wxT("Flac Error: Crc mismatch\n"));
230  break;
231  case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
232  wxPrintf(wxT("Flac Error: Bad Header\n"));
233  break;
234  default:
235  wxPrintf(wxT("Flac Error: Unknown error code\n"));
236  break;
237  }*/
238 }
239 
240 FLAC__StreamDecoderWriteStatus MyFLACFile::write_callback(const FLAC__Frame *frame,
241  const FLAC__int32 * const buffer[])
242 {
243  // Don't let C++ exceptions propagate through libflac
244  return GuardedCall< FLAC__StreamDecoderWriteStatus > ( [&] {
245  auto tmp = ArrayOf< short >{ frame->header.blocksize };
246 
247  auto iter = mFile->mChannels.begin();
248  for (unsigned int chn=0; chn<mFile->mNumChannels; ++iter, ++chn) {
249  if (frame->header.bits_per_sample <= 16) {
250  if (frame->header.bits_per_sample == 8) {
251  for (unsigned int s = 0; s < frame->header.blocksize; s++) {
252  tmp[s] = buffer[chn][s] << 8;
253  }
254  } else /* if (frame->header.bits_per_sample == 16) */ {
255  for (unsigned int s = 0; s < frame->header.blocksize; s++) {
256  tmp[s] = buffer[chn][s];
257  }
258  }
259 
260  iter->get()->Append((samplePtr)tmp.get(),
261  int16Sample,
262  frame->header.blocksize);
263  }
264  else {
265  iter->get()->Append((samplePtr)buffer[chn],
266  int24Sample,
267  frame->header.blocksize);
268  }
269  }
270 
271  mFile->mSamplesDone += frame->header.blocksize;
272 
273  mFile->mUpdateResult = mFile->mProgress->Update((wxULongLong_t) mFile->mSamplesDone, mFile->mNumSamples != 0 ? (wxULongLong_t)mFile->mNumSamples : 1);
275  {
276  return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
277  }
278 
279  return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
280  }, MakeSimpleGuard(FLAC__STREAM_DECODER_WRITE_STATUS_ABORT) );
281 }
282 
284 {
285  return DESC;
286 }
287 
288 
289 std::unique_ptr<ImportFileHandle> FLACImportPlugin::Open(
290  const FilePath &filename, AudacityProject*)
291 {
292  // First check if it really is a FLAC file
293 
294  int cnt;
295  wxFile binaryFile;
296  if (!binaryFile.Open(filename)) {
297  return nullptr; // File not found
298  }
299 
300  // FIXME: TRAP_ERR wxFILE ops in FLAC Import could fail.
301  // Seek() return value is not examined, for example.
302 #ifdef USE_LIBID3TAG
303  // Skip any ID3 tags that might be present
304  id3_byte_t query[ID3_TAG_QUERYSIZE];
305  cnt = binaryFile.Read(query, sizeof(query));
306  cnt = id3_tag_query(query, cnt);
307  binaryFile.Seek(cnt);
308 #endif
309 
310  char buf[5];
311  cnt = binaryFile.Read(buf, 4);
312  binaryFile.Close();
313 
314  if (cnt == wxInvalidOffset || strncmp(buf, FLAC_HEADER, 4) != 0) {
315  // File is not a FLAC file
316  return nullptr;
317  }
318 
319  // Open the file for import
320  auto handle = std::make_unique<FLACImportFileHandle>(filename);
321 
322  bool success = handle->Init();
323  if (!success) {
324  return nullptr;
325  }
326 
327  // This std::move is needed to "upcast" the pointer type
328  return std::move(handle);
329 }
330 
332  std::make_unique< FLACImportPlugin >()
333 };
334 
337  mSamplesDone(0),
338  mStreamInfoDone(false),
339  mUpdateResult(ProgressResult::Success)
340 {
341  // Initialize mFormat as narrowest
343  mFile = std::make_unique<MyFLACFile>(this);
344 }
345 
347 {
348 #ifdef LEGACY_FLAC
349  bool success = mFile->set_filename(OSINPUT(mFilename));
350  if (!success) {
351  return false;
352  }
353  mFile->set_metadata_respond(FLAC__METADATA_TYPE_STREAMINFO);
354  mFile->set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT);
355  FLAC::Decoder::File::State state = mFile->init();
356  if (state != FLAC__FILE_DECODER_OK) {
357  return false;
358  }
359 #else
360  if (!mHandle.Open(mFilename, wxT("rb"))) {
361  return false;
362  }
363 
364  // Even though there is an init() method that takes a filename, use the one that
365  // takes a file handle because wxWidgets can open a file with a Unicode name and
366  // libflac can't (under Windows).
367  //
368  // Responsibility for closing the file is passed to libflac.
369  // (it happens when mFile->finish() is called)
370  bool result = mFile->init(mHandle.fp())?true:false;
371  mHandle.Detach();
372 
373  if (result != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
374  return false;
375  }
376 #endif
377  mFile->process_until_end_of_metadata();
378 
379 #ifdef LEGACY_FLAC
380  state = mFile->get_state();
381  if (state != FLAC__FILE_DECODER_OK) {
382  return false;
383  }
384 #else
385  // not necessary to check state, error callback will catch errors, but here's how:
386  if (mFile->get_state() > FLAC__STREAM_DECODER_READ_FRAME) {
387  return false;
388  }
389 #endif
390 
391  if (!mFile->is_valid() || mFile->get_was_error()) {
392  // This probably is not a FLAC file at all
393  return false;
394  }
395  return true;
396 }
397 
399 {
400  return DESC;
401 }
402 
403 
405 {
406  // TODO: Get Uncompressed byte count.
407  return 0;
408 }
409 
410 
412  TrackHolders &outTracks,
413  Tags *tags)
414 {
415  outTracks.clear();
416 
417  wxASSERT(mStreamInfoDone);
418 
419  CreateProgress();
420 
421  mChannels.resize(mNumChannels);
422 
423  {
424  auto iter = mChannels.begin();
425  for (size_t c = 0; c < mNumChannels; ++iter, ++c)
426  *iter = NewWaveTrack(*trackFactory, mFormat, mSampleRate);
427  }
428 
429  // TODO: Vigilant Sentry: Variable res unused after assignment (error code DA1)
430  // Should check the result.
431  #ifdef LEGACY_FLAC
432  bool res = (mFile->process_until_end_of_file() != 0);
433  #else
434  bool res = (mFile->process_until_end_of_stream() != 0);
435  #endif
436  wxUnusedVar(res);
437 
438  if (mUpdateResult == ProgressResult::Failed || mUpdateResult == ProgressResult::Cancelled) {
439  return mUpdateResult;
440  }
441 
442  for (const auto &channel : mChannels)
443  channel->Flush();
444 
445  if (!mChannels.empty())
446  outTracks.push_back(std::move(mChannels));
447 
448  wxString comment;
449  wxString description;
450 
451  size_t cnt = mFile->mComments.size();
452  if (cnt > 0) {
453  tags->Clear();
454  for (size_t c = 0; c < cnt; c++) {
455  wxString name = mFile->mComments[c].BeforeFirst(wxT('='));
456  wxString value = mFile->mComments[c].AfterFirst(wxT('='));
457  wxString upper = name.Upper();
458  if (upper == wxT("DATE") && !tags->HasTag(TAG_YEAR)) {
459  long val;
460  if (value.length() == 4 && value.ToLong(&val)) {
461  name = TAG_YEAR;
462  }
463  }
464  else if (upper == wxT("COMMENT") || upper == wxT("COMMENTS")) {
465  comment = value;
466  continue;
467  }
468  else if (upper == wxT("DESCRIPTION")) {
469  description = value;
470  continue;
471  }
472  tags->SetTag(name, value);
473  }
474 
475  if (comment.empty()) {
476  comment = description;
477  }
478  if (!comment.empty()) {
479  tags->SetTag(TAG_COMMENTS, comment);
480  }
481  }
482 
483  return mUpdateResult;
484 }
485 
486 
488 {
489  mFile->finish();
490 }
491 
492 #endif /* USE_LIBFLAC */
narrowestSampleFormat
@ narrowestSampleFormat
Two synonyms for previous values that might change if more values were added.
Definition: SampleFormat.h:37
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
MyFLACFile::mComments
wxArrayString mComments
Definition: ImportFLAC.cpp:111
MyFLACFile::mFile
FLACImportFileHandle * mFile
Definition: ImportFLAC.cpp:109
OSINPUT
#define OSINPUT(X)
Definition: SelectFile.h:55
TrackHolders
std::vector< std::vector< std::shared_ptr< WaveTrack > > > TrackHolders
Definition: Import.h:39
BasicUI::ProgressResult::Success
@ Success
MyFLACFile::metadata_callback
void metadata_callback(const FLAC__StreamMetadata *metadata) override
Definition: ImportFLAC.cpp:177
FLACImportFileHandle::GetFileUncompressedBytes
ByteCount GetFileUncompressedBytes() override
Definition: ImportFLAC.cpp:404
MyFLACFile::error_callback
void error_callback(FLAC__StreamDecoderErrorStatus status) override
Definition: ImportFLAC.cpp:218
FLACImportFileHandle::GetStreamCount
wxInt32 GetStreamCount() override
Definition: ImportFLAC.cpp:151
TranslatableStrings
std::vector< TranslatableString > TranslatableStrings
Definition: TranslatableString.h:295
ImportPlugin
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:67
FLACImportPlugin::GetPluginFormatDescription
TranslatableString GetPluginFormatDescription() override
Definition: ImportFLAC.cpp:283
Import.h
FLACImportFileHandle::GetFileDescription
TranslatableString GetFileDescription() override
Definition: ImportFLAC.cpp:398
FLACImportPlugin::FLACImportPlugin
FLACImportPlugin()
Definition: ImportFLAC.cpp:123
Tags::HasTag
bool HasTag(const wxString &name) const
Definition: Tags.cpp:452
FLACImportFileHandle
An ImportFileHandle for FLAC data.
Definition: ImportFLAC.cpp:138
Tags
ID3 Tags (for MP3)
Definition: Tags.h:74
FLACImportFileHandle::mChannels
NewChannelGroup mChannels
Definition: ImportFLAC.cpp:173
BasicUI::ProgressResult
ProgressResult
Definition: BasicUI.h:145
MakeSimpleGuard
SimpleGuard< R > MakeSimpleGuard(R value)
Convert a value to a handler function returning that value, suitable for GuardedCall<R>
Definition: AudacityException.h:167
RefreshCode::Cancelled
@ Cancelled
Definition: RefreshCode.h:23
int24Sample
@ int24Sample
Definition: SampleFormat.h:33
FLACImportFileHandle::mStreamInfoDone
bool mStreamInfoDone
Definition: ImportFLAC.cpp:171
FLACImportFileHandle::Init
bool Init()
Definition: ImportFLAC.cpp:346
FLACImportFileHandle::GetStreamInfo
const TranslatableStrings & GetStreamInfo() override
Definition: ImportFLAC.cpp:153
MyFLACFile
Definition: ImportFLAC.cpp:93
FLACImportFileHandle::mFormat
sampleFormat mFormat
Definition: ImportFLAC.cpp:163
ImportFileHandle::mProgress
std::unique_ptr< ProgressDialog > mProgress
Definition: ImportPlugin.h:159
wxArrayStringEx
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
Definition: wxArrayStringEx.h:18
exts
static const auto exts
Definition: ImportFLAC.cpp:50
floatSample
@ floatSample
Definition: SampleFormat.h:34
ImportFileHandle::CreateProgress
void CreateProgress()
Definition: ImportPlugin.cpp:45
ImportFileHandle::NewWaveTrack
std::shared_ptr< WaveTrack > NewWaveTrack(WaveTrackFactory &trackFactory, sampleFormat effectiveFormat, double rate)
Build a wave track with appropriate format, which will not be narrower than the specified one.
Definition: ImportPlugin.cpp:69
FilePath
wxString FilePath
Definition: Project.h:20
Tags::SetTag
void SetTag(const wxString &name, const wxString &value, const bool bSpecialTag=false)
Definition: Tags.cpp:486
DESC
#define DESC
Definition: ImportFLAC.cpp:48
int16Sample
@ int16Sample
Definition: SampleFormat.h:32
FLACImportFileHandle::mNumChannels
unsigned long mNumChannels
Definition: ImportFLAC.cpp:167
Append
Append([](My &table) -> Registry::BaseItemPtr { if(WaveTrackSubViews::slots() > 1) return std::make_unique< Entry >("MultiView", Entry::CheckItem, OnMultiViewID, XXO("&Multi-view"), POPUP_MENU_FN(OnMultiView), table, [](PopupMenuHandler &handler, wxMenu &menu, int id){ auto &table=static_cast< WaveTrackMenuTable & >(handler);auto &track=table.FindWaveTrack();const auto &view=WaveTrackView::Get(track);menu.Check(id, view.GetMultiView());});else return nullptr;})
name
const TranslatableString name
Definition: Distortion.cpp:98
UTF8CTOWX
#define UTF8CTOWX(X)
Definition: Internat.h:159
FLACImportPlugin::Open
std::unique_ptr< ImportFileHandle > Open(const FilePath &Filename, AudacityProject *) override
Definition: ImportFLAC.cpp:289
Tags::Clear
void Clear()
Definition: Tags.cpp:337
FLACImportPlugin::~FLACImportPlugin
~FLACImportPlugin()
Definition: ImportFLAC.cpp:128
registered
static Importer::RegisteredImportPlugin registered
Definition: ImportFLAC.cpp:331
ImportFileHandle::mFilename
FilePath mFilename
Definition: ImportPlugin.h:158
TAG_YEAR
#define TAG_YEAR
Definition: Tags.h:64
FLACImportFileHandle::mUpdateResult
ProgressResult mUpdateResult
Definition: ImportFLAC.cpp:172
MyFLACFile::get_was_error
bool get_was_error() const
Definition: ImportFLAC.cpp:103
ImportFileHandle
An ImportFileHandle for data.
Definition: ImportPlugin.h:107
FileExtensions
wxArrayStringEx FileExtensions
Definition: Identifier.h:225
FLACImportFileHandle::~FLACImportFileHandle
~FLACImportFileHandle()
Definition: ImportFLAC.cpp:487
FLACImportFileHandle::mBitsPerSample
unsigned long mBitsPerSample
Definition: ImportFLAC.cpp:168
WaveTrackFactory
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:713
sampleFormat
sampleFormat
Definition: SampleFormat.h:29
samplePtr
char * samplePtr
Definition: SampleFormat.h:49
FLACImportPlugin
An ImportPlugin for FLAC data.
Definition: ImportFLAC.cpp:121
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
ImportPlugin.h
The interface that all file import "plugins" (if you want to call them that) must implement....
MyFLACFile::write_callback
FLAC__StreamDecoderWriteStatus write_callback(const FLAC__Frame *frame, const FLAC__int32 *const buffer[]) override
Definition: ImportFLAC.cpp:240
Importer::RegisteredImportPlugin
Definition: Import.h:85
FLACImportFileHandle::mHandle
wxFFile mHandle
Definition: ImportFLAC.cpp:165
FLACImportPlugin::GetPluginStringID
wxString GetPluginStringID() override
Definition: ImportFLAC.cpp:130
TAG_COMMENTS
#define TAG_COMMENTS
Definition: Tags.h:66
FLACImportFileHandle::mNumSamples
FLAC__uint64 mNumSamples
Definition: ImportFLAC.cpp:169
FLACImportFileHandle::SetStreamUsage
void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
Definition: ImportFLAC.cpp:159
MyFLACFile::MyFLACFile
MyFLACFile(FLACImportFileHandle *handle)
Definition: ImportFLAC.cpp:95
Prefs.h
FLACImportFileHandle::Import
ProgressResult Import(WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) override
Definition: ImportFLAC.cpp:411
NewChannelGroup
std::vector< std::shared_ptr< WaveTrack > > NewChannelGroup
Definition: Import.cpp:62
FLACImportFileHandle::mSampleRate
unsigned long mSampleRate
Definition: ImportFLAC.cpp:166
ImportFileHandle::ByteCount
unsigned long long ByteCount
Definition: ImportPlugin.h:127
MyFLACFile::mWasError
bool mWasError
Definition: ImportFLAC.cpp:110
FLACImportFileHandle::FLACImportFileHandle
FLACImportFileHandle(const FilePath &name)
Definition: ImportFLAC.cpp:335
FLAC_HEADER
#define FLAC_HEADER
Definition: ImportFLAC.cpp:46
FLACImportFileHandle::mFile
std::unique_ptr< MyFLACFile > mFile
Definition: ImportFLAC.cpp:164
ArrayOf
Memory.h template class for making an array of float, bool, etc.
Definition: MemoryX.h:27
FLACImportFileHandle::mSamplesDone
FLAC__uint64 mSamplesDone
Definition: ImportFLAC.cpp:170
Importer::RegisteredUnusableImportPlugin
Definition: Import.h:94