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