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#include <wx/defs.h>
27
28#include "Import.h"
29#include "ImportPlugin.h"
31
32#include "Tags.h"
33
34#define FLAC_HEADER "fLaC"
35
36#define DESC XO("FLAC files")
37
38static const auto exts = {
39 wxT("flac"),
40 wxT("flc")
41};
42
43#include <wx/file.h>
44#include <wx/ffile.h>
45
46#include "FLAC++/decoder.h"
47
48#include "WaveTrack.h"
49#include "ImportUtils.h"
50
51#ifdef USE_LIBID3TAG
52extern "C" {
53#include <id3tag.h>
54}
55#endif
56
57/* FLACPP_API_VERSION_CURRENT is 6 for libFLAC++ from flac-1.1.3 (see <FLAC++/export.h>) */
58#if !defined FLACPP_API_VERSION_CURRENT || FLACPP_API_VERSION_CURRENT < 6
59#define LEGACY_FLAC
60#else
61#undef LEGACY_FLAC
62#endif
63
64
66
67class MyFLACFile final : public FLAC::Decoder::File
68{
69 public:
71 {
72 mWasError = false;
73 set_metadata_ignore_all();
74 set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT);
75 set_metadata_respond(FLAC__METADATA_TYPE_STREAMINFO);
76 }
77
78 bool get_was_error() const
79 {
80 return mWasError;
81 }
82
84
85 private:
89 wxArrayString mComments;
90 protected:
91 FLAC__StreamDecoderWriteStatus write_callback(const FLAC__Frame *frame,
92 const FLAC__int32 * const buffer[]) override;
93 void metadata_callback(const FLAC__StreamMetadata *metadata) override;
94 void error_callback(FLAC__StreamDecoderErrorStatus status) override;
95};
96
97
98class FLACImportPlugin final : public ImportPlugin
99{
100 public:
103 {
104 }
105
107
108 wxString GetPluginStringID() override { return wxT("libflac"); }
110 std::unique_ptr<ImportFileHandle> Open(
111 const FilePath &Filename, AudacityProject*) override;
112};
113
114
116{
117 friend class MyFLACFile;
118public:
121
122 bool Init();
123
126 void Import(
127 ImportProgressListener& progressListener, WaveTrackFactory* trackFactory,
128 TrackHolders& outTracks, Tags* tags,
129 std::optional<LibFileFormats::AcidizerTags>& outAcidTags) override;
130
131 wxInt32 GetStreamCount() override { return 1; }
132
134 {
135 static TranslatableStrings empty;
136 return empty;
137 }
138
139 void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
140 {}
141
142private:
144 std::unique_ptr<MyFLACFile> mFile;
145 wxFFile mHandle;
146 unsigned long mSampleRate;
147 unsigned long mNumChannels;
148 unsigned long mBitsPerSample;
149 FLAC__uint64 mNumSamples;
150 FLAC__uint64 mSamplesDone;
153};
154
155
156void MyFLACFile::metadata_callback(const FLAC__StreamMetadata *metadata)
157{
158 switch (metadata->type)
159 {
160 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
161 for (FLAC__uint32 i = 0; i < metadata->data.vorbis_comment.num_comments; i++) {
162 mComments.push_back(UTF8CTOWX((char *)metadata->data.vorbis_comment.comments[i].entry));
163 }
164 break;
165
166 case FLAC__METADATA_TYPE_STREAMINFO:
167 mFile->mSampleRate=metadata->data.stream_info.sample_rate;
168 mFile->mNumChannels=metadata->data.stream_info.channels;
169 mFile->mBitsPerSample=metadata->data.stream_info.bits_per_sample;
170 mFile->mNumSamples=metadata->data.stream_info.total_samples;
171
172 // Widen mFormat after examining the file header
173 if (mFile->mBitsPerSample<=16) {
175 } else if (mFile->mBitsPerSample<=24) {
177 } else {
179 }
181 break;
182 // handle the other types we do nothing with to avoid a warning
183 case FLAC__METADATA_TYPE_PADDING: // do nothing with padding
184 case FLAC__METADATA_TYPE_APPLICATION: // no idea what to do with this
185 case FLAC__METADATA_TYPE_SEEKTABLE: // don't need a seektable here
186 case FLAC__METADATA_TYPE_CUESHEET: // convert this to labels?
187 case FLAC__METADATA_TYPE_PICTURE: // ignore pictures
188 case FLAC__METADATA_TYPE_UNDEFINED: // do nothing with this either
189
190 // FIXME: not declared when compiling on Ubuntu.
191 //case FLAC__MAX_METADATA_TYPE: // quiet compiler warning with this line
192 default:
193 break;
194 }
195}
196
197void MyFLACFile::error_callback(FLAC__StreamDecoderErrorStatus WXUNUSED(status))
198{
199 mWasError = true;
200
201 /*
202 switch (status)
203 {
204 case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
205 wxPrintf(wxT("Flac Error: Lost sync\n"));
206 break;
207 case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
208 wxPrintf(wxT("Flac Error: Crc mismatch\n"));
209 break;
210 case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
211 wxPrintf(wxT("Flac Error: Bad Header\n"));
212 break;
213 default:
214 wxPrintf(wxT("Flac Error: Unknown error code\n"));
215 break;
216 }*/
217}
218
219FLAC__StreamDecoderWriteStatus MyFLACFile::write_callback(const FLAC__Frame *frame,
220 const FLAC__int32 * const buffer[])
221{
222 // Don't let C++ exceptions propagate through libflac
223 return GuardedCall< FLAC__StreamDecoderWriteStatus > ( [&] {
224 auto tmp = ArrayOf< short >{ frame->header.blocksize };
225
226 unsigned chn = 0;
227 ImportUtils::ForEachChannel(*mFile->mTrack, [&](auto& channel)
228 {
229 if (frame->header.bits_per_sample <= 16) {
230 if (frame->header.bits_per_sample == 8) {
231 for (unsigned int s = 0; s < frame->header.blocksize; s++) {
232 tmp[s] = buffer[chn][s] << 8;
233 }
234 } else /* if (frame->header.bits_per_sample == 16) */ {
235 for (unsigned int s = 0; s < frame->header.blocksize; s++) {
236 tmp[s] = buffer[chn][s];
237 }
238 }
239
240 channel.AppendBuffer((samplePtr)tmp.get(),
241 int16Sample,
242 frame->header.blocksize, 1,
243 int16Sample);
244 }
245 else {
246 channel.AppendBuffer((samplePtr)buffer[chn],
247 int24Sample,
248 frame->header.blocksize, 1,
249 int24Sample);
250 }
251 ++chn;
252 });
253
254 mFile->mSamplesDone += frame->header.blocksize;
255
256 if(mFile->mNumSamples > 0)
258 static_cast<double>(mFile->mNumSamples));
259
260 if (mFile->IsCancelled() || mFile->IsStopped())
261 {
262 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
263 }
264
265 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
266 }, MakeSimpleGuard(FLAC__STREAM_DECODER_WRITE_STATUS_ABORT) );
267}
268
270{
271 return DESC;
272}
273
274
275std::unique_ptr<ImportFileHandle> FLACImportPlugin::Open(
276 const FilePath &filename, AudacityProject*)
277{
278 // First check if it really is a FLAC file
279
280 int cnt;
281 wxFile binaryFile;
282 if (!binaryFile.Open(filename)) {
283 return nullptr; // File not found
284 }
285
286 // FIXME: TRAP_ERR wxFILE ops in FLAC Import could fail.
287 // Seek() return value is not examined, for example.
288#ifdef USE_LIBID3TAG
289 // Skip any ID3 tags that might be present
290 id3_byte_t query[ID3_TAG_QUERYSIZE];
291 cnt = binaryFile.Read(query, sizeof(query));
292 cnt = id3_tag_query(query, cnt);
293 binaryFile.Seek(cnt);
294#endif
295
296 char buf[5];
297 cnt = binaryFile.Read(buf, 4);
298 binaryFile.Close();
299
300 if (cnt == wxInvalidOffset || strncmp(buf, FLAC_HEADER, 4) != 0) {
301 // File is not a FLAC file
302 return nullptr;
303 }
304
305 // Open the file for import
306 auto handle = std::make_unique<FLACImportFileHandle>(filename);
307
308 bool success = handle->Init();
309 if (!success) {
310 return nullptr;
311 }
312
313 // This std::move is needed to "upcast" the pointer type
314 return std::move(handle);
315}
316
318 std::make_unique< FLACImportPlugin >()
319};
320
323 mSamplesDone(0),
324 mStreamInfoDone(false)
325{
326 // Initialize mFormat as narrowest
328 mFile = std::make_unique<MyFLACFile>(this);
329}
330
332{
333#ifdef LEGACY_FLAC
334 bool success = mFile->set_filename(OSINPUT(mFilename));
335 if (!success) {
336 return false;
337 }
338 mFile->set_metadata_respond(FLAC__METADATA_TYPE_STREAMINFO);
339 mFile->set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT);
340 FLAC::Decoder::File::State state = mFile->init();
341 if (state != FLAC__FILE_DECODER_OK) {
342 return false;
343 }
344#else
345 if (!mHandle.Open(GetFilename(), wxT("rb"))) {
346 return false;
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 //
353 // Responsibility for closing the file is passed to libflac.
354 // (it happens when mFile->finish() is called)
355 bool result = mFile->init(mHandle.fp())?true:false;
356 mHandle.Detach();
357
358 if (result != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
359 return false;
360 }
361#endif
362 mFile->process_until_end_of_metadata();
363
364#ifdef LEGACY_FLAC
365 state = mFile->get_state();
366 if (state != FLAC__FILE_DECODER_OK) {
367 return false;
368 }
369#else
370 // not necessary to check state, error callback will catch errors, but here's how:
371 if (mFile->get_state() > FLAC__STREAM_DECODER_READ_FRAME) {
372 return false;
373 }
374#endif
375
376 if (!mFile->is_valid() || mFile->get_was_error()) {
377 // This probably is not a FLAC file at all
378 return false;
379 }
380 return true;
381}
382
384{
385 return DESC;
386}
387
388
390{
391 // TODO: Get Uncompressed byte count.
392 return 0;
393}
394
396 ImportProgressListener& progressListener, WaveTrackFactory* trackFactory,
397 TrackHolders& outTracks, Tags* tags,
398 std::optional<LibFileFormats::AcidizerTags>&)
399{
400 BeginImport();
401
402 outTracks.clear();
403
404 auto cleanup = finally([&]{ mFile->mImportProgressListener = nullptr; });
405
406 wxASSERT(mStreamInfoDone);
407
409
410 mFile->mImportProgressListener = &progressListener;
411
412 // TODO: Vigilant Sentry: Variable res unused after assignment (error code DA1)
413 // Should check the result.
414 #ifdef LEGACY_FLAC
415 bool res = (mFile->process_until_end_of_file() != 0);
416 #else
417 bool res = (mFile->process_until_end_of_stream() != 0);
418 #endif
419
420 if(IsCancelled())
421 {
423 return;
424 }
425
427
428 wxString comment;
429 wxString description;
430
431 size_t cnt = mFile->mComments.size();
432 if (cnt > 0) {
433 tags->Clear();
434 for (size_t c = 0; c < cnt; c++) {
435 wxString name = mFile->mComments[c].BeforeFirst(wxT('='));
436 wxString value = mFile->mComments[c].AfterFirst(wxT('='));
437 wxString upper = name.Upper();
438 if (upper == wxT("DATE") && !tags->HasTag(TAG_YEAR)) {
439 long val;
440 if (value.length() == 4 && value.ToLong(&val)) {
441 name = TAG_YEAR;
442 }
443 }
444 else if (upper == wxT("COMMENT") || upper == wxT("COMMENTS")) {
445 comment = value;
446 continue;
447 }
448 else if (upper == wxT("DESCRIPTION")) {
449 description = value;
450 continue;
451 }
452 tags->SetTag(name, value);
453 }
454
455 if (comment.empty()) {
456 comment = description;
457 }
458 if (!comment.empty()) {
459 tags->SetTag(TAG_COMMENTS, comment);
460 }
461 }
462
463 progressListener.OnImportResult(IsStopped()
466}
467
469{
470 mFile->finish();
471}
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>
#define OSINPUT(X)
static const auto exts
Definition: ImportFLAC.cpp:38
#define DESC
Definition: ImportFLAC.cpp:36
static Importer::RegisteredImportPlugin registered
Definition: ImportFLAC.cpp:317
#define FLAC_HEADER
Definition: ImportFLAC.cpp:34
The interface that all file import "plugins" (if you want to call them that) must implement....
std::vector< std::shared_ptr< Track > > TrackHolders
Definition: ImportRaw.h:24
#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.
#define TAG_COMMENTS
Definition: Tags.h:64
#define TAG_YEAR
Definition: Tags.h:62
wxString name
Definition: TagsEditor.cpp:166
std::vector< TranslatableString > TranslatableStrings
This simplifies arrays of arrays, each array separately allocated with NEW[] But it might be better t...
Definition: MemoryX.h:29
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:116
void Import(ImportProgressListener &progressListener, WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags, std::optional< LibFileFormats::AcidizerTags > &outAcidTags) override
Definition: ImportFLAC.cpp:395
FLACImportFileHandle(const FilePath &name)
Definition: ImportFLAC.cpp:321
WaveTrack::Holder mTrack
Definition: ImportFLAC.cpp:152
FLAC__uint64 mNumSamples
Definition: ImportFLAC.cpp:149
void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
Definition: ImportFLAC.cpp:139
FLAC__uint64 mSamplesDone
Definition: ImportFLAC.cpp:150
std::unique_ptr< MyFLACFile > mFile
Definition: ImportFLAC.cpp:144
const TranslatableStrings & GetStreamInfo() override
Definition: ImportFLAC.cpp:133
TranslatableString GetFileDescription() override
Definition: ImportFLAC.cpp:383
ByteCount GetFileUncompressedBytes() override
Definition: ImportFLAC.cpp:389
unsigned long mBitsPerSample
Definition: ImportFLAC.cpp:148
unsigned long mNumChannels
Definition: ImportFLAC.cpp:147
unsigned long mSampleRate
Definition: ImportFLAC.cpp:146
wxInt32 GetStreamCount() override
Definition: ImportFLAC.cpp:131
sampleFormat mFormat
Definition: ImportFLAC.cpp:143
An ImportPlugin for FLAC data.
Definition: ImportFLAC.cpp:99
wxString GetPluginStringID() override
Definition: ImportFLAC.cpp:108
std::unique_ptr< ImportFileHandle > Open(const FilePath &Filename, AudacityProject *) override
Definition: ImportFLAC.cpp:275
TranslatableString GetPluginFormatDescription() override
Definition: ImportFLAC.cpp:269
bool IsStopped() const noexcept
FilePath GetFilename() const override
bool IsCancelled() const noexcept
unsigned long long ByteCount
Definition: ImportPlugin.h:114
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:67
Interface used to report on import state and progress.
virtual void OnImportResult(ImportResult result)=0
Used to report on import result for file handle passed as argument to OnImportFileOpened.
virtual void OnImportProgress(double progress)=0
static std::shared_ptr< WaveTrack > NewWaveTrack(WaveTrackFactory &trackFactory, unsigned nChannels, sampleFormat effectiveFormat, double rate)
Definition: ImportUtils.cpp:35
static void ForEachChannel(TrackList &trackList, const std::function< void(WaveChannel &)> &op)
Iterates over channels in each wave track from the list.
Definition: ImportUtils.cpp:73
static void FinalizeImport(TrackHolders &outTracks, const std::vector< std::shared_ptr< WaveTrack > > &importedStreams)
Flushes the given channels and moves them to outTracks.
Definition: ImportUtils.cpp:49
void error_callback(FLAC__StreamDecoderErrorStatus status) override
Definition: ImportFLAC.cpp:197
MyFLACFile(FLACImportFileHandle *handle)
Definition: ImportFLAC.cpp:70
FLAC__StreamDecoderWriteStatus write_callback(const FLAC__Frame *frame, const FLAC__int32 *const buffer[]) override
Definition: ImportFLAC.cpp:219
bool get_was_error() const
Definition: ImportFLAC.cpp:78
FLACImportFileHandle * mFile
Definition: ImportFLAC.cpp:87
ImportProgressListener * mImportProgressListener
Definition: ImportFLAC.cpp:83
wxArrayString mComments
Definition: ImportFLAC.cpp:89
bool mWasError
Definition: ImportFLAC.cpp:88
void metadata_callback(const FLAC__StreamMetadata *metadata) override
Definition: ImportFLAC.cpp:156
ID3 Tags (for MP3)
Definition: Tags.h:73
void Clear()
Definition: Tags.cpp:293
bool HasTag(const wxString &name) const
Definition: Tags.cpp:397
void SetTag(const wxString &name, const wxString &value, const bool bSpecialTag=false)
Definition: Tags.cpp:431
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:871
std::shared_ptr< WaveTrack > Holder
Definition: WaveTrack.h:247
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
const char * begin(const char *str) noexcept
Definition: StringUtils.h:101