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