Audacity  2.2.2
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 #include "../Audacity.h"
26 #include "ImportFLAC.h"
27 
28 // For compilers that support precompilation, includes "wx/wx.h".
29 #include <wx/wxprec.h>
30 
31 #ifndef WX_PRECOMP
32 // Include your minimal set of headers here, or wx.h
33 #include <wx/window.h>
34 #endif
35 
36 #include <wx/defs.h>
37 #include <wx/intl.h> // needed for _("translated stings") even if we
38  // don't have libflac available
39 
40 #include "../AudacityException.h"
41 #include "Import.h"
42 #include "ImportPlugin.h"
43 
44 #include "../Tags.h"
45 
46 #include "../Experimental.h"
47 
48 #define FLAC_HEADER "fLaC"
49 
50 #define DESC _("FLAC files")
51 
52 static const wxChar *exts[] =
53 {
54  wxT("flac"),
55  wxT("flc")
56 };
57 
58 #ifndef USE_LIBFLAC
59 
60 void GetFLACImportPlugin(ImportPluginList &importPluginList,
61  UnusableImportPluginList &unusableImportPluginList)
62 {
63  unusableImportPluginList.push_back(
64  make_movable<UnusableImportPlugin>
65  (DESC, wxArrayString(WXSIZEOF(exts), exts))
66  );
67 }
68 
69 #else /* USE_LIBFLAC */
70 
71 #include "../Internat.h"
72 
73 #include <wx/string.h>
74 #include <wx/utils.h>
75 #include <wx/file.h>
76 #include <wx/ffile.h>
77 
78 #include "FLAC++/decoder.h"
79 
80 #include "../FileFormats.h"
81 #include "../Prefs.h"
82 #include "../WaveTrack.h"
83 #include "ImportPlugin.h"
84 #include "../ondemand/ODDecodeFlacTask.h"
85 #include "../ondemand/ODManager.h"
86 
87 #ifdef USE_LIBID3TAG
88 extern "C" {
89 #include <id3tag.h>
90 }
91 #endif
92 
93 /* FLACPP_API_VERSION_CURRENT is 6 for libFLAC++ from flac-1.1.3 (see <FLAC++/export.h>) */
94 #if !defined FLACPP_API_VERSION_CURRENT || FLACPP_API_VERSION_CURRENT < 6
95 #define LEGACY_FLAC
96 #else
97 #undef LEGACY_FLAC
98 #endif
99 
100 
102 
103 class MyFLACFile final : public FLAC::Decoder::File
104 {
105  public:
106  MyFLACFile(FLACImportFileHandle *handle) : mFile(handle)
107  {
108  mWasError = false;
109  set_metadata_ignore_all();
110  set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT);
111  set_metadata_respond(FLAC__METADATA_TYPE_STREAMINFO);
112  }
113 
114  bool get_was_error() const
115  {
116  return mWasError;
117  }
118  private:
119  friend class FLACImportFileHandle;
120  FLACImportFileHandle *mFile;
121  bool mWasError;
122  wxArrayString mComments;
123  protected:
124  FLAC__StreamDecoderWriteStatus write_callback(const FLAC__Frame *frame,
125  const FLAC__int32 * const buffer[]) override;
126  void metadata_callback(const FLAC__StreamMetadata *metadata) override;
127  void error_callback(FLAC__StreamDecoderErrorStatus status) override;
128 };
129 
130 
131 class FLACImportPlugin final : public ImportPlugin
132 {
133  public:
135  ImportPlugin(wxArrayString(WXSIZEOF(exts), exts))
136  {
137  }
138 
139  ~FLACImportPlugin() { }
140 
141  wxString GetPluginStringID() { return wxT("libflac"); }
142  wxString GetPluginFormatDescription();
143  std::unique_ptr<ImportFileHandle> Open(const wxString &Filename) override;
144 };
145 
146 
147 class FLACImportFileHandle final : public ImportFileHandle
148 {
149  friend class MyFLACFile;
150 public:
151  FLACImportFileHandle(const wxString & name);
153 
154  bool Init();
155 
156  wxString GetFileDescription() override;
157  ByteCount GetFileUncompressedBytes() override;
158  ProgressResult Import(TrackFactory *trackFactory, TrackHolders &outTracks,
159  Tags *tags) override;
160 
161  wxInt32 GetStreamCount() override { return 1; }
162 
163  const wxArrayString &GetStreamInfo() override
164  {
165  static wxArrayString empty;
166  return empty;
167  }
168 
169  void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
170  {}
171 
172 private:
173  sampleFormat mFormat;
174  std::unique_ptr<MyFLACFile> mFile;
175  wxFFile mHandle;
176  unsigned long mSampleRate;
177  unsigned long mNumChannels;
178  unsigned long mBitsPerSample;
179  FLAC__uint64 mNumSamples;
180  FLAC__uint64 mSamplesDone;
181  bool mStreamInfoDone;
182  ProgressResult mUpdateResult;
183  TrackHolders mChannels;
184  movable_ptr<ODDecodeFlacTask> mDecoderTask;
185 };
186 
187 
188 void MyFLACFile::metadata_callback(const FLAC__StreamMetadata *metadata)
189 {
190  switch (metadata->type)
191  {
192  case FLAC__METADATA_TYPE_VORBIS_COMMENT:
193  for (FLAC__uint32 i = 0; i < metadata->data.vorbis_comment.num_comments; i++) {
194  mComments.Add(UTF8CTOWX((char *)metadata->data.vorbis_comment.comments[i].entry));
195  }
196  break;
197 
198  case FLAC__METADATA_TYPE_STREAMINFO:
199  mFile->mSampleRate=metadata->data.stream_info.sample_rate;
200  mFile->mNumChannels=metadata->data.stream_info.channels;
201  mFile->mBitsPerSample=metadata->data.stream_info.bits_per_sample;
202  mFile->mNumSamples=metadata->data.stream_info.total_samples;
203 
204  if (mFile->mBitsPerSample<=16) {
205  if (mFile->mFormat<int16Sample) {
206  mFile->mFormat=int16Sample;
207  }
208  } else if (mFile->mBitsPerSample<=24) {
209  if (mFile->mFormat<int24Sample) {
210  mFile->mFormat=int24Sample;
211  }
212  } else {
213  mFile->mFormat=floatSample;
214  }
215  mFile->mStreamInfoDone=true;
216  break;
217  // handle the other types we do nothing with to avoid a warning
218  case FLAC__METADATA_TYPE_PADDING: // do nothing with padding
219  case FLAC__METADATA_TYPE_APPLICATION: // no idea what to do with this
220  case FLAC__METADATA_TYPE_SEEKTABLE: // don't need a seektable here
221  case FLAC__METADATA_TYPE_CUESHEET: // convert this to labels?
222  case FLAC__METADATA_TYPE_PICTURE: // ignore pictures
223  case FLAC__METADATA_TYPE_UNDEFINED: // do nothing with this either
224 
225  // FIXME: not declared when compiling on Ubuntu.
226  //case FLAC__MAX_METADATA_TYPE: // quiet compiler warning with this line
227 
228  break;
229  }
230 }
231 
232 void MyFLACFile::error_callback(FLAC__StreamDecoderErrorStatus WXUNUSED(status))
233 {
234  mWasError = true;
235 
236  /*
237  switch (status)
238  {
239  case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
240  wxPrintf(wxT("Flac Error: Lost sync\n"));
241  break;
242  case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
243  wxPrintf(wxT("Flac Error: Crc mismatch\n"));
244  break;
245  case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
246  wxPrintf(wxT("Flac Error: Bad Header\n"));
247  break;
248  default:
249  wxPrintf(wxT("Flac Error: Unknown error code\n"));
250  break;
251  }*/
252 }
253 
254 FLAC__StreamDecoderWriteStatus MyFLACFile::write_callback(const FLAC__Frame *frame,
255  const FLAC__int32 * const buffer[])
256 {
257  // Don't let C++ exceptions propagate through libflac
258  return GuardedCall< FLAC__StreamDecoderWriteStatus > ( [&] {
259  auto tmp = ArrayOf< short >{ frame->header.blocksize };
260 
261  auto iter = mFile->mChannels.begin();
262  for (unsigned int chn=0; chn<mFile->mNumChannels; ++iter, ++chn) {
263  if (frame->header.bits_per_sample == 16) {
264  for (unsigned int s=0; s<frame->header.blocksize; s++) {
265  tmp[s]=buffer[chn][s];
266  }
267 
268  iter->get()->Append((samplePtr)tmp.get(),
269  int16Sample,
270  frame->header.blocksize);
271  }
272  else {
273  iter->get()->Append((samplePtr)buffer[chn],
274  int24Sample,
275  frame->header.blocksize);
276  }
277  }
278 
279  mFile->mSamplesDone += frame->header.blocksize;
280 
281  mFile->mUpdateResult = mFile->mProgress->Update((wxULongLong_t) mFile->mSamplesDone, mFile->mNumSamples != 0 ? (wxULongLong_t)mFile->mNumSamples : 1);
282  if (mFile->mUpdateResult != ProgressResult::Success)
283  {
284  return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
285  }
286 
287  return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
288  }, MakeSimpleGuard(FLAC__STREAM_DECODER_WRITE_STATUS_ABORT) );
289 }
290 
291 
292 void GetFLACImportPlugin(ImportPluginList &importPluginList,
293  UnusableImportPluginList &WXUNUSED(unusableImportPluginList))
294 {
295  importPluginList.push_back( make_movable<FLACImportPlugin>() );
296 }
297 
298 
299 wxString FLACImportPlugin::GetPluginFormatDescription()
300 {
301  return DESC;
302 }
303 
304 
305 std::unique_ptr<ImportFileHandle> FLACImportPlugin::Open(const wxString &filename)
306 {
307  // First check if it really is a FLAC file
308 
309  int cnt;
310  wxFile binaryFile;
311  if (!binaryFile.Open(filename)) {
312  return nullptr; // File not found
313  }
314 
315  // FIXME: TRAP_ERR wxFILE ops in FLAC Import could fail.
316  // Seek() return value is not examined, for example.
317 #ifdef USE_LIBID3TAG
318  // Skip any ID3 tags that might be present
319  id3_byte_t query[ID3_TAG_QUERYSIZE];
320  cnt = binaryFile.Read(query, sizeof(query));
321  cnt = id3_tag_query(query, cnt);
322  binaryFile.Seek(cnt);
323 #endif
324 
325  char buf[5];
326  cnt = binaryFile.Read(buf, 4);
327  binaryFile.Close();
328 
329  if (cnt == wxInvalidOffset || strncmp(buf, FLAC_HEADER, 4) != 0) {
330  // File is not a FLAC file
331  return nullptr;
332  }
333 
334  // Open the file for import
335  auto handle = std::make_unique<FLACImportFileHandle>(filename);
336 
337  bool success = handle->Init();
338  if (!success) {
339  return nullptr;
340  }
341 
342  // This std::move is needed to "upcast" the pointer type
343  return std::move(handle);
344 }
345 
346 
347 FLACImportFileHandle::FLACImportFileHandle(const wxString & name)
348 : ImportFileHandle(name),
349  mSamplesDone(0),
350  mStreamInfoDone(false),
351  mUpdateResult(ProgressResult::Success)
352 {
353  mFormat = (sampleFormat)
354  gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);
355  mFile = std::make_unique<MyFLACFile>(this);
356 }
357 
358 bool FLACImportFileHandle::Init()
359 {
360 #ifdef EXPERIMENTAL_OD_FLAC
361  mDecoderTask = make_movable<ODDecodeFlacTask>();
362 
363  ODFlacDecoder* odDecoder = (ODFlacDecoder*)mDecoderTask->CreateFileDecoder(mFilename);
364  if(!odDecoder || !odDecoder->ReadHeader())
365  {
366  return false;
367  }
368  //copy the meta data over to the class
369 
370  mSampleRate=odDecoder->mSampleRate;
371  mNumChannels=odDecoder->mNumChannels;
372  mBitsPerSample=odDecoder->mBitsPerSample;
373 
374  mNumSamples=odDecoder->mNumSamples;
375  mBitsPerSample=odDecoder->mBitsPerSample;
376  mFormat=odDecoder->mFormat;
377  mStreamInfoDone=true;
378 
379 
380  return true;
381 #endif
382 #ifdef LEGACY_FLAC
383  bool success = mFile->set_filename(OSINPUT(mFilename));
384  if (!success) {
385  return false;
386  }
387  mFile->set_metadata_respond(FLAC__METADATA_TYPE_STREAMINFO);
388  mFile->set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT);
389  FLAC::Decoder::File::State state = mFile->init();
390  if (state != FLAC__FILE_DECODER_OK) {
391  return false;
392  }
393 #else
394  if (!mHandle.Open(mFilename, wxT("rb"))) {
395  return false;
396  }
397 
398  // Even though there is an init() method that takes a filename, use the one that
399  // takes a file handle because wxWidgets can open a file with a Unicode name and
400  // libflac can't (under Windows).
401  //
402  // Responsibility for closing the file is passed to libflac.
403  // (it happens when mFile->finish() is called)
404  bool result = mFile->init(mHandle.fp())?true:false;
405  mHandle.Detach();
406 
407  if (result != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
408  return false;
409  }
410 #endif
411  mFile->process_until_end_of_metadata();
412 
413 #ifdef LEGACY_FLAC
414  state = mFile->get_state();
415  if (state != FLAC__FILE_DECODER_OK) {
416  return false;
417  }
418 #else
419  // not necessary to check state, error callback will catch errors, but here's how:
420  if (mFile->get_state() > FLAC__STREAM_DECODER_READ_FRAME) {
421  return false;
422  }
423 #endif
424 
425  if (!mFile->is_valid() || mFile->get_was_error()) {
426  // This probably is not a FLAC file at all
427  return false;
428  }
429  return true;
430 }
431 
432 wxString FLACImportFileHandle::GetFileDescription()
433 {
434  return DESC;
435 }
436 
437 
438 auto FLACImportFileHandle::GetFileUncompressedBytes() -> ByteCount
439 {
440  // TODO: Get Uncompressed byte count.
441  return 0;
442 }
443 
444 
445 ProgressResult FLACImportFileHandle::Import(TrackFactory *trackFactory,
446  TrackHolders &outTracks,
447  Tags *tags)
448 {
449  outTracks.clear();
450 
451  wxASSERT(mStreamInfoDone);
452 
453  CreateProgress();
454 
455  mChannels.resize(mNumChannels);
456 
457  auto iter = mChannels.begin();
458  for (size_t c = 0; c < mNumChannels; ++iter, ++c) {
459  *iter = trackFactory->NewWaveTrack(mFormat, mSampleRate);
460 
461  if (mNumChannels == 2) {
462  switch (c) {
463  case 0:
464  iter->get()->SetChannel(Track::LeftChannel);
465  iter->get()->SetLinked(true);
466  break;
467  case 1:
468  iter->get()->SetChannel(Track::RightChannel);
469  break;
470  }
471  }
472  else {
473  iter->get()->SetChannel(Track::MonoChannel);
474  }
475  }
476 
477 
478 //Start OD
479  bool useOD = false;
480 #ifdef EXPERIMENTAL_OD_FLAC
481  useOD=true;
482 #endif
483 
484  // TODO: Vigilant Sentry: Variable res unused after assignment (error code DA1)
485  // Should check the result.
486  #ifdef LEGACY_FLAC
487  bool res = (mFile->process_until_end_of_file() != 0);
488  #else
489  bool res = true;
490  if(!useOD)
491  res = (mFile->process_until_end_of_stream() != 0);
492  #endif
493  wxUnusedVar(res);
494 
495  //add the task to the ODManager
496  if(useOD)
497  {
498  auto fileTotalFrames =
499  (sampleCount)mNumSamples; // convert from FLAC__uint64
500  auto maxBlockSize = mChannels.begin()->get()->GetMaxBlockSize();
501  for (decltype(fileTotalFrames) i = 0; i < fileTotalFrames; i += maxBlockSize) {
502  const auto blockLen =
503  limitSampleBufferSize( maxBlockSize, fileTotalFrames - i );
504 
505  auto iter = mChannels.begin();
506  for (size_t c = 0; c < mNumChannels; ++c, ++iter)
507  iter->get()->AppendCoded(mFilename, i, blockLen, c, ODTask::eODFLAC);
508 
509  mUpdateResult = mProgress->Update(
510  i.as_long_long(),
511  fileTotalFrames.as_long_long()
512  );
513  if (mUpdateResult != ProgressResult::Success)
514  break;
515  }
516 
517  bool moreThanStereo = mNumChannels>2;
518  for (const auto &channel : mChannels)
519  {
520  mDecoderTask->AddWaveTrack(channel.get());
521  if(moreThanStereo)
522  {
523  //if we have 3 more channels, they get imported on seperate tracks, so we add individual tasks for each.
524  ODManager::Instance()->AddNewTask(std::move(mDecoderTask));
525  mDecoderTask = make_movable<ODDecodeFlacTask>(); //TODO: see if we need to use clone to keep the metadata.
526  }
527  }
528  //if we have mono or a linked track (stereo), we add ONE task for the one linked wave track
529  if(!moreThanStereo)
530  ODManager::Instance()->AddNewTask(std::move(mDecoderTask));
531  }
532 //END OD
533 
534 
535  if (mUpdateResult == ProgressResult::Failed || mUpdateResult == ProgressResult::Cancelled) {
536  return mUpdateResult;
537  }
538 
539  for (const auto &channel : mChannels) {
540  channel->Flush();
541  }
542  outTracks.swap(mChannels);
543 
544  tags->Clear();
545  size_t cnt = mFile->mComments.GetCount();
546  for (size_t c = 0; c < cnt; c++) {
547  wxString name = mFile->mComments[c].BeforeFirst(wxT('='));
548  wxString value = mFile->mComments[c].AfterFirst(wxT('='));
549  if (name.Upper() == wxT("DATE") && !tags->HasTag(TAG_YEAR)) {
550  long val;
551  if (value.Length() == 4 && value.ToLong(&val)) {
552  name = TAG_YEAR;
553  }
554  }
555  tags->SetTag(name, value);
556  }
557 
558  return mUpdateResult;
559 }
560 
561 
562 FLACImportFileHandle::~FLACImportFileHandle()
563 {
564  //don't finish *mFile if we are using OD,
565  //because it was not initialized in Init().
566 #ifndef EXPERIMENTAL_OD_FLAC
567  mFile->finish();
568 #endif
569 }
570 
571 #endif /* USE_LIBFLAC */
void AddNewTask(movable_ptr< ODTask > &&mtask, bool lockMutex=true)
Adds a wavetrack, creates a queue member.
Definition: ODManager.cpp:145
void SetTag(const wxString &name, const wxString &value)
Definition: Tags.cpp:449
static ODManager *(* Instance)()
Definition: ODManager.h:49
static const wxChar * exts[]
Definition: ImportFLAC.cpp:52
ProgressResult
An ImportFileHandle for data.
Definition: ImportPlugin.h:119
std::unique_ptr< T > movable_ptr
Definition: MemoryX.h:713
void GetFLACImportPlugin(ImportPluginList &importPluginList, UnusableImportPluginList &unusableImportPluginList)
Definition: ImportFLAC.cpp:60
#define UTF8CTOWX(X)
Definition: Internat.h:167
unsigned long mSampleRate
#define FLAC_HEADER
Definition: ImportFLAC.cpp:48
SimpleGuard< R > MakeSimpleGuard(R value)
Used to create a WaveTrack, or a LabelTrack.. Implementation of the functions of this class are dispe...
Definition: Track.h:850
wxFileConfig * gPrefs
Definition: Prefs.cpp:72
void Clear()
Definition: Tags.cpp:300
An ImportFileHandle for FLAC data.
#define OSINPUT(X)
Definition: Internat.h:162
The interface that all file import "plugins" (if you want to call them that) must implement...
ID3 Tags (for MP3)
Definition: Tags.h:72
std::unique_ptr< WaveTrack > NewWaveTrack(sampleFormat format=(sampleFormat) 0, double rate=0)
Definition: WaveTrack.cpp:78
An ImportPlugin for FLAC data.
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:73
const wxChar * name
Definition: Distortion.cpp:94
class to decode a particular file (one per file). Saves info such as filename and length (after the h...
Memory.h template class for making an array of float, bool, etc.
Definition: MemoryX.h:469
An UnusableImportPlugin list.
An ImportPlugin list.
#define DESC
Definition: ImportFLAC.cpp:50
std::vector< std::unique_ptr< WaveTrack >> TrackHolders
Definition: ImportRaw.h:42
#define TAG_YEAR
Definition: Tags.h:66
bool HasTag(const wxString &name) const
Definition: Tags.cpp:415