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 #include "../prefs/QualityPrefs.h"
46 
47 #include "../Experimental.h"
48 
49 #define FLAC_HEADER "fLaC"
50 
51 #define DESC _("FLAC files")
52 
53 static const wxChar *exts[] =
54 {
55  wxT("flac"),
56  wxT("flc")
57 };
58 
59 #ifndef USE_LIBFLAC
60 
61 void GetFLACImportPlugin(ImportPluginList &importPluginList,
62  UnusableImportPluginList &unusableImportPluginList)
63 {
64  unusableImportPluginList.push_back(
65  std::make_unique<UnusableImportPlugin>
66  (DESC, wxArrayString(WXSIZEOF(exts), exts))
67  );
68 }
69 
70 #else /* USE_LIBFLAC */
71 
72 #include "../Internat.h"
73 
74 #include <wx/string.h>
75 #include <wx/utils.h>
76 #include <wx/file.h>
77 #include <wx/ffile.h>
78 
79 #include "FLAC++/decoder.h"
80 
81 #include "../FileFormats.h"
82 #include "../Prefs.h"
83 #include "../WaveTrack.h"
84 #include "ImportPlugin.h"
85 #include "../ondemand/ODDecodeFlacTask.h"
86 #include "../ondemand/ODManager.h"
87 
88 #ifdef USE_LIBID3TAG
89 extern "C" {
90 #include <id3tag.h>
91 }
92 #endif
93 
94 /* FLACPP_API_VERSION_CURRENT is 6 for libFLAC++ from flac-1.1.3 (see <FLAC++/export.h>) */
95 #if !defined FLACPP_API_VERSION_CURRENT || FLACPP_API_VERSION_CURRENT < 6
96 #define LEGACY_FLAC
97 #else
98 #undef LEGACY_FLAC
99 #endif
100 
101 
103 
104 class MyFLACFile final : public FLAC::Decoder::File
105 {
106  public:
107  MyFLACFile(FLACImportFileHandle *handle) : mFile(handle)
108  {
109  mWasError = false;
110  set_metadata_ignore_all();
111  set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT);
112  set_metadata_respond(FLAC__METADATA_TYPE_STREAMINFO);
113  }
114 
115  bool get_was_error() const
116  {
117  return mWasError;
118  }
119  private:
120  friend class FLACImportFileHandle;
121  FLACImportFileHandle *mFile;
122  bool mWasError;
123  wxArrayString mComments;
124  protected:
125  FLAC__StreamDecoderWriteStatus write_callback(const FLAC__Frame *frame,
126  const FLAC__int32 * const buffer[]) override;
127  void metadata_callback(const FLAC__StreamMetadata *metadata) override;
128  void error_callback(FLAC__StreamDecoderErrorStatus status) override;
129 };
130 
131 
132 class FLACImportPlugin final : public ImportPlugin
133 {
134  public:
136  ImportPlugin(wxArrayString(WXSIZEOF(exts), exts))
137  {
138  }
139 
140  ~FLACImportPlugin() { }
141 
142  wxString GetPluginStringID() override { return wxT("libflac"); }
143  wxString GetPluginFormatDescription() override;
144  std::unique_ptr<ImportFileHandle> Open(const wxString &Filename) override;
145 };
146 
147 
148 class FLACImportFileHandle final : public ImportFileHandle
149 {
150  friend class MyFLACFile;
151 public:
152  FLACImportFileHandle(const wxString & name);
154 
155  bool Init();
156 
157  wxString GetFileDescription() override;
158  ByteCount GetFileUncompressedBytes() override;
159  ProgressResult Import(TrackFactory *trackFactory, TrackHolders &outTracks,
160  Tags *tags) override;
161 
162  wxInt32 GetStreamCount() override { return 1; }
163 
164  const wxArrayString &GetStreamInfo() override
165  {
166  static wxArrayString empty;
167  return empty;
168  }
169 
170  void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
171  {}
172 
173 private:
174  sampleFormat mFormat;
175  std::unique_ptr<MyFLACFile> mFile;
176  wxFFile mHandle;
177  unsigned long mSampleRate;
178  unsigned long mNumChannels;
179  unsigned long mBitsPerSample;
180  FLAC__uint64 mNumSamples;
181  FLAC__uint64 mSamplesDone;
182  bool mStreamInfoDone;
183  ProgressResult mUpdateResult;
184  TrackHolders mChannels;
185  std::unique_ptr<ODDecodeFlacTask> mDecoderTask;
186 };
187 
188 
189 void MyFLACFile::metadata_callback(const FLAC__StreamMetadata *metadata)
190 {
191  switch (metadata->type)
192  {
193  case FLAC__METADATA_TYPE_VORBIS_COMMENT:
194  for (FLAC__uint32 i = 0; i < metadata->data.vorbis_comment.num_comments; i++) {
195  mComments.Add(UTF8CTOWX((char *)metadata->data.vorbis_comment.comments[i].entry));
196  }
197  break;
198 
199  case FLAC__METADATA_TYPE_STREAMINFO:
200  mFile->mSampleRate=metadata->data.stream_info.sample_rate;
201  mFile->mNumChannels=metadata->data.stream_info.channels;
202  mFile->mBitsPerSample=metadata->data.stream_info.bits_per_sample;
203  mFile->mNumSamples=metadata->data.stream_info.total_samples;
204 
205  if (mFile->mBitsPerSample<=16) {
206  if (mFile->mFormat<int16Sample) {
207  mFile->mFormat=int16Sample;
208  }
209  } else if (mFile->mBitsPerSample<=24) {
210  if (mFile->mFormat<int24Sample) {
211  mFile->mFormat=int24Sample;
212  }
213  } else {
214  mFile->mFormat=floatSample;
215  }
216  mFile->mStreamInfoDone=true;
217  break;
218  // handle the other types we do nothing with to avoid a warning
219  case FLAC__METADATA_TYPE_PADDING: // do nothing with padding
220  case FLAC__METADATA_TYPE_APPLICATION: // no idea what to do with this
221  case FLAC__METADATA_TYPE_SEEKTABLE: // don't need a seektable here
222  case FLAC__METADATA_TYPE_CUESHEET: // convert this to labels?
223  case FLAC__METADATA_TYPE_PICTURE: // ignore pictures
224  case FLAC__METADATA_TYPE_UNDEFINED: // do nothing with this either
225 
226  // FIXME: not declared when compiling on Ubuntu.
227  //case FLAC__MAX_METADATA_TYPE: // quiet compiler warning with this line
228  default:
229  break;
230  }
231 }
232 
233 void MyFLACFile::error_callback(FLAC__StreamDecoderErrorStatus WXUNUSED(status))
234 {
235  mWasError = true;
236 
237  /*
238  switch (status)
239  {
240  case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
241  wxPrintf(wxT("Flac Error: Lost sync\n"));
242  break;
243  case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
244  wxPrintf(wxT("Flac Error: Crc mismatch\n"));
245  break;
246  case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
247  wxPrintf(wxT("Flac Error: Bad Header\n"));
248  break;
249  default:
250  wxPrintf(wxT("Flac Error: Unknown error code\n"));
251  break;
252  }*/
253 }
254 
255 FLAC__StreamDecoderWriteStatus MyFLACFile::write_callback(const FLAC__Frame *frame,
256  const FLAC__int32 * const buffer[])
257 {
258  // Don't let C++ exceptions propagate through libflac
259  return GuardedCall< FLAC__StreamDecoderWriteStatus > ( [&] {
260  auto tmp = ArrayOf< short >{ frame->header.blocksize };
261 
262  auto iter = mFile->mChannels.begin();
263  for (unsigned int chn=0; chn<mFile->mNumChannels; ++iter, ++chn) {
264  if (frame->header.bits_per_sample == 16) {
265  for (unsigned int s=0; s<frame->header.blocksize; s++) {
266  tmp[s]=buffer[chn][s];
267  }
268 
269  iter->get()->Append((samplePtr)tmp.get(),
270  int16Sample,
271  frame->header.blocksize);
272  }
273  else {
274  iter->get()->Append((samplePtr)buffer[chn],
275  int24Sample,
276  frame->header.blocksize);
277  }
278  }
279 
280  mFile->mSamplesDone += frame->header.blocksize;
281 
282  mFile->mUpdateResult = mFile->mProgress->Update((wxULongLong_t) mFile->mSamplesDone, mFile->mNumSamples != 0 ? (wxULongLong_t)mFile->mNumSamples : 1);
283  if (mFile->mUpdateResult != ProgressResult::Success)
284  {
285  return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
286  }
287 
288  return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
289  }, MakeSimpleGuard(FLAC__STREAM_DECODER_WRITE_STATUS_ABORT) );
290 }
291 
292 
293 void GetFLACImportPlugin(ImportPluginList &importPluginList,
294  UnusableImportPluginList &WXUNUSED(unusableImportPluginList))
295 {
296  importPluginList.push_back( std::make_unique<FLACImportPlugin>() );
297 }
298 
299 
300 wxString FLACImportPlugin::GetPluginFormatDescription()
301 {
302  return DESC;
303 }
304 
305 
306 std::unique_ptr<ImportFileHandle> FLACImportPlugin::Open(const wxString &filename)
307 {
308  // First check if it really is a FLAC file
309 
310  int cnt;
311  wxFile binaryFile;
312  if (!binaryFile.Open(filename)) {
313  return nullptr; // File not found
314  }
315 
316  // FIXME: TRAP_ERR wxFILE ops in FLAC Import could fail.
317  // Seek() return value is not examined, for example.
318 #ifdef USE_LIBID3TAG
319  // Skip any ID3 tags that might be present
320  id3_byte_t query[ID3_TAG_QUERYSIZE];
321  cnt = binaryFile.Read(query, sizeof(query));
322  cnt = id3_tag_query(query, cnt);
323  binaryFile.Seek(cnt);
324 #endif
325 
326  char buf[5];
327  cnt = binaryFile.Read(buf, 4);
328  binaryFile.Close();
329 
330  if (cnt == wxInvalidOffset || strncmp(buf, FLAC_HEADER, 4) != 0) {
331  // File is not a FLAC file
332  return nullptr;
333  }
334 
335  // Open the file for import
336  auto handle = std::make_unique<FLACImportFileHandle>(filename);
337 
338  bool success = handle->Init();
339  if (!success) {
340  return nullptr;
341  }
342 
343  // This std::move is needed to "upcast" the pointer type
344  return std::move(handle);
345 }
346 
347 
348 FLACImportFileHandle::FLACImportFileHandle(const wxString & name)
349 : ImportFileHandle(name),
350  mSamplesDone(0),
351  mStreamInfoDone(false),
352  mUpdateResult(ProgressResult::Success)
353 {
355  mFile = std::make_unique<MyFLACFile>(this);
356 }
357 
358 bool FLACImportFileHandle::Init()
359 {
360 #ifdef EXPERIMENTAL_OD_FLAC
361  mDecoderTask = std::make_unique<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 = std::make_unique<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 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:53
ProgressResult
An ImportFileHandle for data.
Definition: ImportPlugin.h:119
void GetFLACImportPlugin(ImportPluginList &importPluginList, UnusableImportPluginList &unusableImportPluginList)
Definition: ImportFLAC.cpp:61
#define UTF8CTOWX(X)
Definition: Internat.h:179
unsigned long mSampleRate
#define FLAC_HEADER
Definition: ImportFLAC.cpp:49
SimpleGuard< R > MakeSimpleGuard(R value)
void AddNewTask(std::unique_ptr< ODTask > &&mtask, bool lockMutex=true)
Adds a wavetrack, creates a queue member.
Definition: ODManager.cpp:145
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: Types.h:178
Used to create a WaveTrack, or a LabelTrack.. Implementation of the functions of this class are dispe...
Definition: Track.h:862
void Clear()
Definition: Tags.cpp:300
An ImportFileHandle for FLAC data.
#define OSINPUT(X)
Definition: Internat.h:174
sampleFormat
Definition: Types.h:188
char * samplePtr
Definition: Types.h:203
The interface that all file import "plugins" (if you want to call them that) must implement...
ID3 Tags (for MP3)
Definition: Tags.h:70
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:86
An UnusableImportPlugin list.
An ImportPlugin list.
static sampleFormat SampleFormatChoice()
#define DESC
Definition: ImportFLAC.cpp:51
std::vector< std::unique_ptr< WaveTrack >> TrackHolders
Definition: ImportRaw.h:24
#define TAG_YEAR
Definition: Tags.h:64
bool HasTag(const wxString &name) const
Definition: Tags.cpp:415