Audacity 3.2.0
ImportPCM.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ImportPCM.cpp
6
7 Dominic Mazzoni
8 Leland Lucius
9
10*//****************************************************************//****************************************************************//*******************************************************************/
21
22
23
24#include "Import.h"
25#include "Tags.h"
26
27#include <wx/wx.h>
28#include <wx/ffile.h>
29
30#include "sndfile.h"
31
32#ifndef SNDFILE_1
33#error Requires libsndfile 1.0 or higher
34#endif
35
36#include "FileFormats.h"
37#include "WaveTrack.h"
38#include "ImportPlugin.h"
39#include "ImportUtils.h"
41
42#include <algorithm>
43
44#ifdef USE_LIBID3TAG
45 #include <id3tag.h>
46 // DM: the following functions were supposed to have been
47 // included in id3tag.h - should be fixed in the next release
48 // of mad.
49 extern "C" {
50 struct id3_frame *id3_frame_new(char const *);
51 id3_length_t id3_latin1_length(id3_latin1_t const *);
52 void id3_latin1_decode(id3_latin1_t const *, id3_ucs4_t *);
53 }
54#endif
55
56#define DESC XO("WAV, AIFF, and other uncompressed types")
57
58class PCMImportPlugin final : public ImportPlugin
59{
60public:
63 {
64 }
65
67
68 wxString GetPluginStringID() override { return wxT("libsndfile"); }
70 std::unique_ptr<ImportFileHandle> Open(
71 const FilePath &Filename, AudacityProject*) override;
72};
73
74
76{
77public:
78 PCMImportFileHandle(const FilePath &name, SFFile &&file, SF_INFO info);
80
83 void Import(ImportProgressListener &progressListener,
84 WaveTrackFactory *trackFactory,
85 TrackHolders &outTracks,
86 Tags *tags) override;
87
88 wxInt32 GetStreamCount() override { return 1; }
89
91 {
92 static TranslatableStrings empty;
93 return empty;
94 }
95
96 void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
97 {}
98
99private:
101 const SF_INFO mInfo;
104};
105
107{
108 return DESC;
109}
110
111std::unique_ptr<ImportFileHandle> PCMImportPlugin::Open(
112 const FilePath &filename, AudacityProject*)
113{
114 SF_INFO info;
115 wxFile f; // will be closed when it goes out of scope
116 SFFile file;
117
118 memset(&info, 0, sizeof(info));
119
120
121#ifdef __WXGTK__
122 if (filename.Lower().EndsWith(wxT("mp3"))) {
123 // There is a bug in libsndfile where mp3s with duplicated metadata tags
124 // will crash libsndfile and thus audacity.
125 // We have patched the lib-src version of libsndfile, but
126 // for linux the user can build against the system libsndfile which
127 // still has this bug.
128 // This happens in sf_open_fd, which is the very first point of
129 // interaction with libsndfile, so the only workaround is to hardcode
130 // ImportPCM to not handle .mp3. Of course, this will still fail for mp3s
131 // that are mislabeled with a .wav or other extension.
132 // So, in the future we may want to write a simple parser to detect mp3s here.
133 return NULL;
134 }
135#endif
136
137
138 if (f.Open(filename)) {
139 // Even though there is an sf_open() that takes a filename, use the one that
140 // takes a file descriptor since wxWidgets can open a file with a Unicode name and
141 // libsndfile can't (under Windows).
142 file.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &info, TRUE));
143 }
144
145 // The file descriptor is now owned by "file", so we must tell "f" to leave
146 // it alone. The file descriptor is closed by the destructor of file even if an error
147 // occurs.
148 f.Detach();
149
150 if (!file) {
151 // TODO: Handle error
152 //char str[1000];
153 //sf_error_str((SNDFILE *)NULL, str, 1000);
154
155 return nullptr;
156 } else if (file &&
157 (info.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
158 // mchinen 15.1.2012 - disallowing libsndfile to handle
159 // ogg files because seeking is broken at this date (very slow,
160 // seeks from beginning of file each seek).
161 // This was said by Erik (libsndfile maintainer).
162 // Note that this won't apply to our local libsndfile, so only
163 // linux builds that use --with-libsndfile=system are affected,
164 // as our local libsndfile doesn't do OGG.
165 // In particular ubuntu 10.10 and 11.04 are known to be affected
166 // When the bug is fixed, we can check version to avoid only
167 // the broken builds.
168
169 return nullptr;
170 }
171
172 // Success, so now transfer the duty to close the file from "file".
173 return std::make_unique<PCMImportFileHandle>(filename, std::move(file), info);
174}
175
177 std::make_unique< PCMImportPlugin >()
178};
179
181 SFFile &&file, SF_INFO info)
183 mFile(std::move(file)),
184 mInfo(info)
185{
186 wxASSERT(info.channels >= 0);
187
188 //
189 // Figure out the format to use.
190 //
191 // In general, go with the user's preferences. However, if
192 // the file is higher-quality, go with a format which preserves
193 // the quality of the original file.
194 //
195
196 // Effective format
198 // But maybe different storage format
200}
201
203{
204 // Library strings
205 // See the major_formats and subtype_formats tables in command.c in
206 // libsndfile for this list of possibilities
207
208using Unevaluated = decltype(
209 /* major_formats */
210 XO("AIFF (Apple/SGI)")
211 , XO("AU (Sun/NeXT)")
212 , XO("AVR (Audio Visual Research)")
213 , XO("CAF (Apple Core Audio File)")
214 /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
215 , XO("FLAC (FLAC Lossless Audio Codec)")
216 , XO("HTK (HMM Tool Kit)")
217 , XO("IFF (Amiga IFF/SVX8/SV16)")
218 , XO("MAT4 (GNU Octave 2.0 / Matlab 4.2)")
219 , XO("MAT5 (GNU Octave 2.1 / Matlab 5.0)")
220 , XO("MPC (Akai MPC 2k)")
221 , XO("OGG (OGG Container format)")
222 , XO("PAF (Ensoniq PARIS)")
223 , XO("PVF (Portable Voice Format)")
224 , XO("RAW (header-less)")
225 , XO("RF64 (RIFF 64)")
226 , XO("SD2 (Sound Designer II)")
227 , XO("SDS (Midi Sample Dump Standard)")
228 , XO("SF (Berkeley/IRCAM/CARL)")
229 , XO("VOC (Creative Labs)")
230 , XO("W64 (SoundFoundry WAVE 64)")
231 , XO("WAV (Microsoft)")
232 , XO("WAV (NIST Sphere)")
233 , XO("WAVEX (Microsoft)")
234 , XO("WVE (Psion Series 3)")
235 , XO("XI (FastTracker 2)")
236);
237
238using Unevaluated2 = decltype(
239 /* subtype_formats */
240 XO("Signed 8 bit PCM")
241 , XO("Signed 16 bit PCM")
242 , XO("Signed 24 bit PCM")
243 , XO("Signed 32 bit PCM")
244 , XO("Unsigned 8 bit PCM")
245 , XO("32 bit float")
246 , XO("64 bit float")
247 , XO("U-Law")
248 , XO("A-Law")
249 , XO("IMA ADPCM")
250 , XO("Microsoft ADPCM")
251 , XO("GSM 6.10")
252 , XO("32kbs G721 ADPCM")
253 , XO("24kbs G723 ADPCM")
254 , XO("12 bit DWVW")
255 , XO("16 bit DWVW")
256 , XO("24 bit DWVW")
257 , XO("VOX ADPCM")
258 , XO("16 bit DPCM")
259 , XO("8 bit DPCM")
260 , XO("Vorbis")
261);
262
263 auto untranslated = SFCall<wxString>(sf_header_name, mInfo.format);
264 return TranslatableString{
265 untranslated, {} };
266}
267
269{
270 return mInfo.frames * mInfo.channels * SAMPLE_SIZE(mFormat);
271}
272
273#ifdef USE_LIBID3TAG
274struct id3_tag_deleter {
275 void operator () (id3_tag *p) const { if (p) id3_tag_delete(p); }
276};
277using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>;
278#endif
279
281 WaveTrackFactory *trackFactory,
282 TrackHolders &outTracks,
283 Tags *tags)
284{
285 BeginImport();
286
287 outTracks.clear();
288
289 wxASSERT(mFile.get());
290
291 auto trackList = ImportUtils::NewWaveTrack(
292 *trackFactory,
293 mInfo.channels,
294 mFormat,
295 mInfo.samplerate);
296
297 auto fileTotalFrames =
298 (sampleCount)mInfo.frames; // convert from sf_count_t
299 auto maxBlockSize = (*trackList->Any<WaveTrack>().begin())->GetMaxBlockSize();
300
301 {
302 // Otherwise, we're in the "copy" mode, where we read in the actual
303 // samples from the file and store our own local copy of the
304 // samples in the tracks.
305
306 // PRL: guard against excessive memory buffer allocation in case of many channels
307 using type = decltype(maxBlockSize);
308 if (mInfo.channels < 1)
309 {
311 return;
312 }
313 auto maxBlock = std::min(maxBlockSize,
314 std::numeric_limits<type>::max() /
315 (mInfo.channels * SAMPLE_SIZE(mFormat))
316 );
317 if (maxBlock < 1)
318 {
320 return;
321 }
322
323 SampleBuffer srcbuffer, buffer;
324 wxASSERT(mInfo.channels >= 0);
325 while (NULL == srcbuffer.Allocate(maxBlock * mInfo.channels, mFormat).ptr() ||
326 NULL == buffer.Allocate(maxBlock, mFormat).ptr())
327 {
328 maxBlock /= 2;
329 if (maxBlock < 1)
330 {
332 return;
333 }
334 }
335
336 decltype(fileTotalFrames) framescompleted = 0;
337
338 long block;
339 do {
340 block = maxBlock;
341
342 if (mFormat == int16Sample)
343 block = SFCall<sf_count_t>(sf_readf_short, mFile.get(), (short *)srcbuffer.ptr(), block);
344 //import 24 bit int as float and have the append function convert it. This is how PCMAliasBlockFile worked too.
345 else
346 block = SFCall<sf_count_t>(sf_readf_float, mFile.get(), (float *)srcbuffer.ptr(), block);
347
348 if(block < 0 || block > (long)maxBlock) {
349 wxASSERT(false);
350 block = maxBlock;
351 }
352
353 if (block) {
354 unsigned c = 0;
355 ImportUtils::ForEachChannel(*trackList, [&](auto& channel)
356 {
357 if (mFormat==int16Sample) {
358 for(int j=0; j<block; j++)
359 ((short *)buffer.ptr())[j] =
360 ((short *)srcbuffer.ptr())[mInfo.channels*j+c];
361 }
362 else {
363 for(int j=0; j<block; j++)
364 ((float *)buffer.ptr())[j] =
365 ((float *)srcbuffer.ptr())[mInfo.channels*j+c];
366 }
367
368 channel.AppendBuffer(
369 buffer.ptr(),
371 block, 1, mEffectiveFormat
372 );
373 ++c;
374 });
375 framescompleted += block;
376 }
377 if(fileTotalFrames > 0)
378 progressListener.OnImportProgress(framescompleted.as_double() / fileTotalFrames.as_double());
379 } while (block > 0 && !IsCancelled() && !IsStopped());
380 }
381
382 if(IsCancelled())
383 {
385 return;
386 }
387
388 ImportUtils::FinalizeImport(outTracks, trackList);
389
390 const char *str;
391
392 str = sf_get_string(mFile.get(), SF_STR_TITLE);
393 if (str) {
394 tags->SetTag(TAG_TITLE, UTF8CTOWX(str));
395 }
396
397 str = sf_get_string(mFile.get(), SF_STR_ALBUM);
398 if (str) {
399 tags->SetTag(TAG_ALBUM, UTF8CTOWX(str));
400 }
401
402 str = sf_get_string(mFile.get(), SF_STR_ARTIST);
403 if (str) {
405 }
406
407 str = sf_get_string(mFile.get(), SF_STR_COMMENT);
408 if (str) {
410 }
411
412 str = sf_get_string(mFile.get(), SF_STR_DATE);
413 if (str) {
414 tags->SetTag(TAG_YEAR, UTF8CTOWX(str));
415 }
416
417 str = sf_get_string(mFile.get(), SF_STR_COPYRIGHT);
418 if (str) {
420 }
421
422 str = sf_get_string(mFile.get(), SF_STR_SOFTWARE);
423 if (str) {
425 }
426
427 str = sf_get_string(mFile.get(), SF_STR_TRACKNUMBER);
428 if (str) {
429 tags->SetTag(TAG_TRACK, UTF8CTOWX(str));
430 }
431
432 str = sf_get_string(mFile.get(), SF_STR_GENRE);
433 if (str) {
434 tags->SetTag(TAG_GENRE, UTF8CTOWX(str));
435 }
436
437#if defined(USE_LIBID3TAG)
438 if (((mInfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) ||
439 ((mInfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)) {
440 wxFFile f(GetFilename(), wxT("rb"));
441 if (f.IsOpened()) {
442 char id[5];
443 wxUint32 len;
444
445 id[4] = '\0';
446
447 f.Seek(12); // Skip filetype, length, and formtype
448
449 while (!f.Error()) {
450 f.Read(id, 4); // Get chunk type
451 if (f.Eof()) {
452 break;
453 }
454 f.Read(&len, 4);
455 if((mInfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
456 len = wxUINT32_SWAP_ON_LE(len);
457
458 if (wxStricmp(id, "ID3 ") != 0) { // must be case insensitive
459 f.Seek(len + (len & 0x01), wxFromCurrent);
460 continue;
461 }
462
463
464 id3_tag_holder tp;
465 {
466 ArrayOf<id3_byte_t> buffer{ len };
467 if (!buffer) {
468 break;
469 }
470
471 f.Read(buffer.get(), len);
472 tp.reset( id3_tag_parse(buffer.get(), len) );
473 }
474
475 if (!tp) {
476 break;
477 }
478
479 // Loop through all frames
480 bool have_year = false;
481 for (int i = 0; i < (int) tp->nframes; i++) {
482 struct id3_frame *frame = tp->frames[i];
483
484 // wxPrintf("ID: %08x '%4s'\n", (int) *(int *)frame->id, frame->id);
485 // wxPrintf("Desc: %s\n", frame->description);
486 // wxPrintf("Num fields: %d\n", frame->nfields);
487
488 // for (int j = 0; j < (int) frame->nfields; j++) {
489 // wxPrintf("field %d type %d\n", j, frame->fields[j].type );
490 // if (frame->fields[j].type == ID3_FIELD_TYPE_STRINGLIST) {
491 // wxPrintf("num strings %d\n", frame->fields[j].stringlist.nstrings);
492 // }
493 // }
494
495 wxString n, v;
496
497 // Determine the tag name
498 if (strcmp(frame->id, ID3_FRAME_TITLE) == 0) {
499 n = TAG_TITLE;
500 }
501 else if (strcmp(frame->id, ID3_FRAME_ARTIST) == 0) {
502 n = TAG_ARTIST;
503 }
504 else if (strcmp(frame->id, ID3_FRAME_ALBUM) == 0) {
505 n = TAG_ALBUM;
506 }
507 else if (strcmp(frame->id, ID3_FRAME_TRACK) == 0) {
508 n = TAG_TRACK;
509 }
510 else if (strcmp(frame->id, ID3_FRAME_YEAR) == 0) {
511 // LLL: When libid3tag encounters the "TYER" tag, it converts it to a
512 // "ZOBS" (obsolete) tag and adds a "TDRC" tag at the end of the
513 // list of tags using the first 4 characters of the "TYER" tag.
514 // Since we write both the "TDRC" and "TYER" tags, the "TDRC" tag
515 // will always be encountered first in the list. We want use it
516 // since the converted "TYER" tag may have been truncated.
517 if (have_year) {
518 continue;
519 }
520 n = TAG_YEAR;
521 have_year = true;
522 }
523 else if (strcmp(frame->id, ID3_FRAME_COMMENT) == 0) {
524 n = TAG_COMMENTS;
525 }
526 else if (strcmp(frame->id, ID3_FRAME_GENRE) == 0) {
527 n = TAG_GENRE;
528 }
529 else {
530 // Use frame description as default tag name. The descriptions
531 // may include several "meanings" separated by "/" characters, so
532 // we just use the first meaning
533 n = UTF8CTOWX(frame->description).BeforeFirst(wxT('/'));
534 }
535
536 const id3_ucs4_t *ustr = NULL;
537
538 if (n == TAG_COMMENTS) {
539 ustr = id3_field_getfullstring(&frame->fields[3]);
540 }
541 else if (frame->nfields == 3) {
542 ustr = id3_field_getstring(&frame->fields[1]);
543 if (ustr) {
544 // Is this duplication really needed?
545 MallocString<> convStr{ (char *)id3_ucs4_utf8duplicate(ustr) };
546 n = UTF8CTOWX(convStr.get());
547 }
548
549 ustr = id3_field_getstring(&frame->fields[2]);
550 }
551 else if (frame->nfields >= 2) {
552 ustr = id3_field_getstrings(&frame->fields[1], 0);
553 }
554
555 if (ustr) {
556 // Is this duplication really needed?
557 MallocString<> convStr{ (char *)id3_ucs4_utf8duplicate(ustr) };
558 v = UTF8CTOWX(convStr.get());
559 }
560
561 if (!n.empty() && !v.empty()) {
562 tags->SetTag(n, v);
563 }
564 }
565
566 // Convert v1 genre to name
567 if (tags->HasTag(TAG_GENRE)) {
568 long g = -1;
569 if (tags->GetTag(TAG_GENRE).ToLong(&g)) {
570 tags->SetTag(TAG_GENRE, tags->GetGenre(g));
571 }
572 }
573
574 break;
575 }
576 }
577 }
578#endif
579
580 progressListener.OnImportResult(IsStopped()
583}
584
586{
587}
wxT("CloseDown"))
int min(int a, int b)
#define str(a)
const TranslatableString name
Definition: Distortion.cpp:76
FileExtensions sf_get_all_extensions()
wxString sf_header_name(int format)
Get the string name of the specified container format.
sampleFormat sf_subtype_to_effective_format(unsigned int format)
Choose the narrowest value in the sampleFormat enumeration for a given libsndfile format.
XO("Cut/Copy/Paste")
#define DESC
Definition: ImportPCM.cpp:56
static Importer::RegisteredImportPlugin registered
Definition: ImportPCM.cpp:176
The interface that all file import "plugins" (if you want to call them that) must implement....
std::vector< std::shared_ptr< TrackList > > TrackHolders
Definition: ImportRaw.h:24
#define UTF8CTOWX(X)
Definition: Internat.h:157
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:146
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
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:50
#define TAG_TRACK
Definition: Tags.h:61
#define TAG_COMMENTS
Definition: Tags.h:64
#define TAG_GENRE
Definition: Tags.h:63
#define TAG_SOFTWARE
Definition: Tags.h:65
#define TAG_COPYRIGHT
Definition: Tags.h:66
#define TAG_ALBUM
Definition: Tags.h:60
#define TAG_YEAR
Definition: Tags.h:62
#define TAG_TITLE
Definition: Tags.h:58
#define TAG_ARTIST
Definition: Tags.h:59
std::vector< TranslatableString > TranslatableStrings
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
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 TrackListHolder NewWaveTrack(WaveTrackFactory &trackFactory, unsigned nChannels, sampleFormat effectiveFormat, double rate)
Definition: ImportUtils.cpp:35
static sampleFormat ChooseFormat(sampleFormat effectiveFormat)
Choose appropriate format, which will not be narrower than the specified one.
Definition: ImportUtils.cpp:19
static void ForEachChannel(TrackList &trackList, const std::function< void(WaveChannel &)> &op)
Iterates over channels in each wave track from the list.
Definition: ImportUtils.cpp:66
static void FinalizeImport(TrackHolders &outTracks, const std::vector< TrackListHolder > &importedStreams)
Flushes the given channels and moves them to outTracks.
Definition: ImportUtils.cpp:49
An ImportFileHandle for PCM data.
Definition: ImportPCM.cpp:76
const TranslatableStrings & GetStreamInfo() override
Definition: ImportPCM.cpp:90
void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
Definition: ImportPCM.cpp:96
wxInt32 GetStreamCount() override
Definition: ImportPCM.cpp:88
TranslatableString GetFileDescription() override
Definition: ImportPCM.cpp:202
void Import(ImportProgressListener &progressListener, WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) override
Definition: ImportPCM.cpp:280
ByteCount GetFileUncompressedBytes() override
Definition: ImportPCM.cpp:268
PCMImportFileHandle(const FilePath &name, SFFile &&file, SF_INFO info)
Definition: ImportPCM.cpp:180
sampleFormat mFormat
Definition: ImportPCM.cpp:103
const SF_INFO mInfo
Definition: ImportPCM.cpp:101
sampleFormat mEffectiveFormat
Definition: ImportPCM.cpp:102
An ImportPlugin for PCM data.
Definition: ImportPCM.cpp:59
wxString GetPluginStringID() override
Definition: ImportPCM.cpp:68
TranslatableString GetPluginFormatDescription() override
Definition: ImportPCM.cpp:106
std::unique_ptr< ImportFileHandle > Open(const FilePath &Filename, AudacityProject *) override
Definition: ImportPCM.cpp:111
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:149
samplePtr ptr() const
Definition: SampleFormat.h:163
ID3 Tags (for MP3)
Definition: Tags.h:73
bool HasTag(const wxString &name) const
Definition: Tags.cpp:397
wxString GetGenre(int value)
Definition: Tags.cpp:373
void SetTag(const wxString &name, const wxString &value, const bool bSpecialTag=false)
Definition: Tags.cpp:431
wxString GetTag(const wxString &name) const
Definition: Tags.cpp:406
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:1166
A Track that contains audio waveform data.
Definition: WaveTrack.h:220
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
STL namespace.