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