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