Audacity  2.3.1
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 #include "../Audacity.h"
23 #include "ImportPCM.h"
24 #include "../Internat.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 "../ondemand/ODManager.h"
40 #include "../ondemand/ODComputeSummaryTask.h"
41 #include "../prefs/QualityPrefs.h"
42 
43 //If OD is enabled, he minimum number of samples a file has to use it.
44 //Otherwise, we use the older PCMAliasBlockFile method since it should be fast enough.
45 #define kMinimumODFileSampleSize 44100*30
46 
47 #ifndef SNDFILE_1
48 #error Requires libsndfile 1.0 or higher
49 #endif
50 
51 #include "../FileFormats.h"
52 #include "../Prefs.h"
53 #include "../WaveTrack.h"
54 #include "ImportPlugin.h"
55 
56 #include <algorithm>
57 
58 #ifdef USE_LIBID3TAG
59  #include <id3tag.h>
60  // DM: the following functions were supposed to have been
61  // included in id3tag.h - should be fixed in the next release
62  // of mad.
63  extern "C" {
64  struct id3_frame *id3_frame_new(char const *);
65  id3_length_t id3_latin1_length(id3_latin1_t const *);
66  void id3_latin1_decode(id3_latin1_t const *, id3_ucs4_t *);
67  }
68 #endif
69 
70 #define DESC _("WAV, AIFF, and other uncompressed types")
71 
72 class PCMImportPlugin final : public ImportPlugin
73 {
74 public:
76  : ImportPlugin(wxArrayString())
77  {
79  }
80 
82 
83  wxString GetPluginStringID() override { return wxT("libsndfile"); }
84  wxString GetPluginFormatDescription() override;
85  std::unique_ptr<ImportFileHandle> Open(const wxString &Filename) override;
86 };
87 
88 
90 {
91 public:
92  PCMImportFileHandle(wxString name, SFFile &&file, SF_INFO info);
94 
95  wxString GetFileDescription() override;
97  ProgressResult Import(TrackFactory *trackFactory, TrackHolders &outTracks,
98  Tags *tags) override;
99 
100  wxInt32 GetStreamCount() override { return 1; }
101 
102  const wxArrayString &GetStreamInfo() override
103  {
104  static wxArrayString empty;
105  return empty;
106  }
107 
108  void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
109  {}
110 
111 private:
113  const SF_INFO mInfo;
115 };
116 
117 void GetPCMImportPlugin(ImportPluginList & importPluginList,
118  UnusableImportPluginList & WXUNUSED(unusableImportPluginList))
119 {
120  importPluginList.push_back( std::make_unique<PCMImportPlugin>() );
121 }
122 
124 {
125  return DESC;
126 }
127 
128 std::unique_ptr<ImportFileHandle> PCMImportPlugin::Open(const wxString &filename)
129 {
130  SF_INFO info;
131  wxFile f; // will be closed when it goes out of scope
132  SFFile file;
133 
134  memset(&info, 0, sizeof(info));
135 
136 
137 #ifdef __WXGTK__
138  if (filename.Lower().EndsWith(wxT("mp3"))) {
139  // There is a bug in libsndfile where mp3s with duplicated metadata tags
140  // will crash libsndfile and thus audacity.
141  // We have patched the lib-src version of libsndfile, but
142  // for linux the user can build against the system libsndfile which
143  // still has this bug.
144  // This happens in sf_open_fd, which is the very first point of
145  // interaction with libsndfile, so the only workaround is to hardcode
146  // ImportPCM to not handle .mp3. Of couse, this will still fail for mp3s
147  // that are mislabeled with a .wav or other extension.
148  // So, in the future we may want to write a simple parser to detect mp3s here.
149  return NULL;
150  }
151 #endif
152 
153 
154  if (f.Open(filename)) {
155  // Even though there is an sf_open() that takes a filename, use the one that
156  // takes a file descriptor since wxWidgets can open a file with a Unicode name and
157  // libsndfile can't (under Windows).
158  file.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &info, TRUE));
159  }
160 
161  // The file descriptor is now owned by "file", so we must tell "f" to leave
162  // it alone. The file descriptor is closed by the destructor of file even if an error
163  // occurs.
164  f.Detach();
165 
166  if (!file) {
167  // TODO: Handle error
168  //char str[1000];
169  //sf_error_str((SNDFILE *)NULL, str, 1000);
170 
171  return nullptr;
172  } else if (file &&
173  (info.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
174  // mchinen 15.1.2012 - disallowing libsndfile to handle
175  // ogg files because seeking is broken at this date (very slow,
176  // seeks from beginning of file each seek).
177  // This was said by Erik (libsndfile maintainer).
178  // Note that this won't apply to our local libsndfile, so only
179  // linux builds that use --with-libsndfile=system are affected,
180  // as our local libsndfile doesn't do OGG.
181  // In particular ubuntu 10.10 and 11.04 are known to be affected
182  // When the bug is fixed, we can check version to avoid only
183  // the broken builds.
184 
185  return nullptr;
186  }
187 
188  // Success, so now transfer the duty to close the file from "file".
189  return std::make_unique<PCMImportFileHandle>(filename, std::move(file), info);
190 }
191 
193  SFFile &&file, SF_INFO info)
194 : ImportFileHandle(name),
195  mFile(std::move(file)),
196  mInfo(info)
197 {
198  wxASSERT(info.channels >= 0);
199 
200  //
201  // Figure out the format to use.
202  //
203  // In general, go with the user's preferences. However, if
204  // the file is higher-quality, go with a format which preserves
205  // the quality of the original file.
206  //
207 
209 
210  if (mFormat != floatSample &&
213 }
214 
216 {
217  return SFCall<wxString>(sf_header_name, mInfo.format);
218 }
219 
221 {
222  return mInfo.frames * mInfo.channels * SAMPLE_SIZE(mFormat);
223 }
224 
225 // returns "copy" or "edit" (aliased) as the user selects.
226 // if the cancel button is hit then "cancel" is returned.
227 static wxString AskCopyOrEdit()
228 {
229  wxString oldCopyPref = gPrefs->Read(wxT("/FileFormats/CopyOrEditUncompressedData"), wxT("copy"));
230  bool firstTimeAsk = gPrefs->Read(wxT("/Warnings/CopyOrEditUncompressedDataFirstAsk"), true)?true:false;
231  bool oldAskPref = gPrefs->Read(wxT("/Warnings/CopyOrEditUncompressedDataAsk"), true)?true:false;
232 
233  // The first time the user is asked we force it to 'copy'.
234  // This effectively does a one-time change to the preferences.
235  if (firstTimeAsk) {
236  if (oldCopyPref != wxT("copy")) {
237  gPrefs->Write(wxT("/FileFormats/CopyOrEditUncompressedData"), wxT("copy"));
238  oldCopyPref = wxT("copy");
239  }
240  gPrefs->Write(wxT("/Warnings/CopyOrEditUncompressedDataFirstAsk"), (long) false);
241  gPrefs->Flush();
242  }
243 
244  // check the current preferences for whether or not we should ask the user about this.
245  if (oldAskPref) {
246  wxString newCopyPref = wxT("copy");
247  wxDialogWrapper dialog(nullptr, -1, wxString(_("Warning")));
248  dialog.SetName(dialog.GetTitle());
249 
250  wxBoxSizer *vbox;
251  dialog.SetSizer(vbox = safenew wxBoxSizer(wxVERTICAL));
252 
253  wxString clause1 = _(
254 "When importing uncompressed audio files you can either copy them into the project,"
255 " or read them directly from their current location (without copying).\n\n"
256  );
257 
258  wxString clause2 = oldCopyPref == wxT("copy")
259  ? _("Your current preference is set to copy in.\n\n")
260  : _("Your current preference is set to read directly.\n\n")
261  ;
262 
263  wxString clause3 = _(
264 "Reading the files directly allows you to play or edit them almost immediately. "
265 "This is less safe than copying in, because you must retain the files with their "
266 "original names in their original locations.\n"
267 "Help > Diagnostics > Check Dependencies will show the original names and locations of any files "
268 "that you are reading directly.\n\n"
269 "How do you want to import the current file(s)?"
270  );
271 
272  wxStaticText *message =
273  safenew wxStaticText(&dialog, -1, clause1 + clause2 + clause3);
274 
275  message->Wrap(500);
276  message->SetName(message->GetLabel());
277 
278  vbox->Add(message, 1, wxALL | wxEXPAND, 10);
279 
280  wxStaticBox *box = safenew wxStaticBox(&dialog, -1, _("Choose an import method"));
281  box->SetName(box->GetLabel());
282 
283  wxRadioButton *aliasRadio;
284  wxRadioButton *copyRadio;
285  wxCheckBox *dontAskNextTimeBox;
286 
287  {
288  auto boxsizer = std::make_unique<wxStaticBoxSizer>(box, wxVERTICAL);
289 
290  copyRadio = safenew wxRadioButton(&dialog, -1, _("Make a &copy of the files before editing (safer)"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
291  boxsizer->Add(copyRadio, 0, wxALL);
292  copyRadio->SetName(wxStripMenuCodes(copyRadio->GetLabel()));
293 
294  aliasRadio = safenew wxRadioButton(&dialog, -1, _("Read the files &directly from the original (faster)"));
295  boxsizer->Add(aliasRadio, 0, wxALL);
296  aliasRadio->SetName(wxStripMenuCodes(aliasRadio->GetLabel()));
297 
298  dontAskNextTimeBox = safenew wxCheckBox(&dialog, -1, _("Don't &warn again and always use my choice above"));
299  boxsizer->Add(dontAskNextTimeBox, 0, wxALL);
300  vbox->Add(boxsizer.release(), 0, wxALL, 10);
301  }
302 
303  dontAskNextTimeBox->SetName(wxStripMenuCodes(dontAskNextTimeBox->GetLabel()));
304 
305 
306  wxRadioButton *prefsRadio = oldCopyPref == wxT("copy") ? copyRadio : aliasRadio;
307  prefsRadio->SetValue(true);
308 
309  wxSizer *buttonSizer = dialog.CreateButtonSizer(wxOK | wxCANCEL);
310  vbox->Add(buttonSizer, 0, wxALL | wxEXPAND, 10);
311 
312  dialog.SetSize(dialog.GetBestSize());
313  dialog.Layout();
314  dialog.Center();
315 
316  if (dialog.ShowModal() == wxID_OK) {
317  if (aliasRadio->GetValue()) {
318  newCopyPref = wxT("edit");
319  }
320  if (dontAskNextTimeBox->IsChecked()) {
321  gPrefs->Write(wxT("/Warnings/CopyOrEditUncompressedDataAsk"), (long) false);
322  gPrefs->Flush();
323  }
324  } else {
325  return wxT("cancel");
326  }
327 
328  // if the preference changed, save it.
329  if (newCopyPref != oldCopyPref) {
330  gPrefs->Write(wxT("/FileFormats/CopyOrEditUncompressedData"), newCopyPref);
331  gPrefs->Flush();
332  }
333  oldCopyPref = newCopyPref;
334  }
335  return oldCopyPref;
336 }
337 
338 #ifdef USE_LIBID3TAG
339 struct id3_tag_deleter {
340  void operator () (id3_tag *p) const { if (p) id3_tag_delete(p); }
341 };
342 using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>;
343 #endif
344 
346  TrackHolders &outTracks,
347  Tags *tags)
348 {
349  outTracks.clear();
350 
351  wxASSERT(mFile.get());
352 
353  // Get the preference / warn the user about aliased files.
354  wxString copyEdit = AskCopyOrEdit();
355 
356  if (copyEdit == wxT("cancel"))
358 
359  // Fall back to "copy" if it doesn't match anything else, since it is safer
360  bool doEdit = false;
361  if (copyEdit.IsSameAs(wxT("edit"), false))
362  doEdit = true;
363 
364 
365  CreateProgress();
366 
367  NewChannelGroup channels(mInfo.channels);
368 
369  {
370  // iter not used outside this scope.
371  auto iter = channels.begin();
372  for (int c = 0; c < mInfo.channels; ++iter, ++c)
373  *iter = trackFactory->NewWaveTrack(mFormat, mInfo.samplerate);
374  }
375 
376  auto fileTotalFrames =
377  (sampleCount)mInfo.frames; // convert from sf_count_t
378  auto maxBlockSize = channels.begin()->get()->GetMaxBlockSize();
379  auto updateResult = ProgressResult::Cancelled;
380 
381  // If the format is not seekable, we must use 'copy' mode,
382  // because 'edit' mode depends on the ability to seek to an
383  // arbitrary location in the file.
384  if (!mInfo.seekable)
385  doEdit = false;
386 
387  if (doEdit) {
388  // If this mode has been selected, we form the tracks as
389  // aliases to the files we're editing, i.e. ("foo.wav", 12000-18000)
390  // instead of actually making fresh copies of the samples.
391 
392  // lets use OD only if the file is longer than 30 seconds. Otherwise, why wake up extra threads.
393  //todo: make this a user pref.
394  bool useOD =fileTotalFrames>kMinimumODFileSampleSize;
395  int updateCounter = 0;
396 
397  for (decltype(fileTotalFrames) i = 0; i < fileTotalFrames; i += maxBlockSize) {
398 
399  const auto blockLen =
400  limitSampleBufferSize( maxBlockSize, fileTotalFrames - i );
401 
402  auto iter = channels.begin();
403  for (int c = 0; c < mInfo.channels; ++iter, ++c)
404  iter->get()->AppendAlias(mFilename, i, blockLen, c,useOD);
405 
406  if (++updateCounter == 50) {
407  updateResult = mProgress->Update(
408  i.as_long_long(),
409  fileTotalFrames.as_long_long()
410  );
411  updateCounter = 0;
412  if (updateResult != ProgressResult::Success)
413  break;
414  }
415  }
416 
417  // One last update for completion
418  updateResult = mProgress->Update(
419  fileTotalFrames.as_long_long(),
420  fileTotalFrames.as_long_long()
421  );
422 
423  if(useOD)
424  {
425  auto computeTask = std::make_unique<ODComputeSummaryTask>();
426  bool moreThanStereo = mInfo.channels>2;
427  for (const auto &channel : channels)
428  {
429  computeTask->AddWaveTrack(channel.get());
430  if(moreThanStereo)
431  {
432  //if we have 3 more channels, they get imported on seperate tracks, so we add individual tasks for each.
433  ODManager::Instance()->AddNewTask(std::move(computeTask));
434  computeTask = std::make_unique<ODComputeSummaryTask>();
435  }
436  }
437  //if we have a linked track, we add ONE task.
438  if(!moreThanStereo)
439  ODManager::Instance()->AddNewTask(std::move(computeTask));
440  }
441  }
442  else {
443  // Otherwise, we're in the "copy" mode, where we read in the actual
444  // samples from the file and store our own local copy of the
445  // samples in the tracks.
446 
447  // PRL: guard against excessive memory buffer allocation in case of many channels
448  using type = decltype(maxBlockSize);
449  if (mInfo.channels < 1)
450  return ProgressResult::Failed;
451  auto maxBlock = std::min(maxBlockSize,
452  std::numeric_limits<type>::max() /
453  (mInfo.channels * SAMPLE_SIZE(mFormat))
454  );
455  if (maxBlock < 1)
456  return ProgressResult::Failed;
457 
458  SampleBuffer srcbuffer, buffer;
459  wxASSERT(mInfo.channels >= 0);
460  while (NULL == srcbuffer.Allocate(maxBlock * mInfo.channels, mFormat).ptr() ||
461  NULL == buffer.Allocate(maxBlock, mFormat).ptr())
462  {
463  maxBlock /= 2;
464  if (maxBlock < 1)
465  return ProgressResult::Failed;
466  }
467 
468  decltype(fileTotalFrames) framescompleted = 0;
469 
470  long block;
471  do {
472  block = maxBlock;
473 
474  if (mFormat == int16Sample)
475  block = SFCall<sf_count_t>(sf_readf_short, mFile.get(), (short *)srcbuffer.ptr(), block);
476  //import 24 bit int as float and have the append function convert it. This is how PCMAliasBlockFile works too.
477  else
478  block = SFCall<sf_count_t>(sf_readf_float, mFile.get(), (float *)srcbuffer.ptr(), block);
479 
480  if(block < 0 || block > (long)maxBlock) {
481  wxASSERT(false);
482  block = maxBlock;
483  }
484 
485  if (block) {
486  auto iter = channels.begin();
487  for(int c=0; c<mInfo.channels; ++iter, ++c) {
488  if (mFormat==int16Sample) {
489  for(int j=0; j<block; j++)
490  ((short *)buffer.ptr())[j] =
491  ((short *)srcbuffer.ptr())[mInfo.channels*j+c];
492  }
493  else {
494  for(int j=0; j<block; j++)
495  ((float *)buffer.ptr())[j] =
496  ((float *)srcbuffer.ptr())[mInfo.channels*j+c];
497  }
498 
499  iter->get()->Append(buffer.ptr(), (mFormat == int16Sample)?int16Sample:floatSample, block);
500  }
501  framescompleted += block;
502  }
503 
504  updateResult = mProgress->Update(
505  framescompleted.as_long_long(),
506  fileTotalFrames.as_long_long()
507  );
508  if (updateResult != ProgressResult::Success)
509  break;
510 
511  } while (block > 0);
512  }
513 
514  if (updateResult == ProgressResult::Failed || updateResult == ProgressResult::Cancelled) {
515  return updateResult;
516  }
517 
518  for(const auto &channel : channels)
519  channel->Flush();
520 
521  if (!channels.empty())
522  outTracks.push_back(std::move(channels));
523 
524  const char *str;
525 
526  str = sf_get_string(mFile.get(), SF_STR_TITLE);
527  if (str) {
528  tags->SetTag(TAG_TITLE, UTF8CTOWX(str));
529  }
530 
531  str = sf_get_string(mFile.get(), SF_STR_ALBUM);
532  if (str) {
533  tags->SetTag(TAG_ALBUM, UTF8CTOWX(str));
534  }
535 
536  str = sf_get_string(mFile.get(), SF_STR_ARTIST);
537  if (str) {
538  tags->SetTag(TAG_ARTIST, UTF8CTOWX(str));
539  }
540 
541  str = sf_get_string(mFile.get(), SF_STR_COMMENT);
542  if (str) {
543  tags->SetTag(TAG_COMMENTS, UTF8CTOWX(str));
544  }
545 
546  str = sf_get_string(mFile.get(), SF_STR_DATE);
547  if (str) {
548  tags->SetTag(TAG_YEAR, UTF8CTOWX(str));
549  }
550 
551  str = sf_get_string(mFile.get(), SF_STR_COPYRIGHT);
552  if (str) {
553  tags->SetTag(TAG_COPYRIGHT, UTF8CTOWX(str));
554  }
555 
556  str = sf_get_string(mFile.get(), SF_STR_SOFTWARE);
557  if (str) {
558  tags->SetTag(TAG_SOFTWARE, UTF8CTOWX(str));
559  }
560 
561  str = sf_get_string(mFile.get(), SF_STR_TRACKNUMBER);
562  if (str) {
563  tags->SetTag(TAG_TRACK, UTF8CTOWX(str));
564  }
565 
566  str = sf_get_string(mFile.get(), SF_STR_GENRE);
567  if (str) {
568  tags->SetTag(TAG_GENRE, UTF8CTOWX(str));
569  }
570 
571 #if defined(USE_LIBID3TAG)
572  if (((mInfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) ||
573  ((mInfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)) {
574  wxFFile f(mFilename, wxT("rb"));
575  if (f.IsOpened()) {
576  char id[5];
577  wxUint32 len;
578 
579  id[4] = '\0';
580 
581  f.Seek(12); // Skip filetype, length, and formtype
582 
583  while (!f.Error()) {
584  f.Read(id, 4); // Get chunk type
585  if (f.Eof()) {
586  break;
587  }
588  f.Read(&len, 4);
589  if((mInfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
590  len = wxUINT32_SWAP_ON_LE(len);
591 
592  if (wxStricmp(id, "ID3 ") != 0) { // must be case insensitive
593  f.Seek(len + (len & 0x01), wxFromCurrent);
594  continue;
595  }
596 
597 
598  id3_tag_holder tp;
599  {
600  ArrayOf<id3_byte_t> buffer{ len };
601  if (!buffer) {
602  break;
603  }
604 
605  f.Read(buffer.get(), len);
606  tp.reset( id3_tag_parse(buffer.get(), len) );
607  }
608 
609  if (!tp) {
610  break;
611  }
612 
613  // Loop through all frames
614  bool have_year = false;
615  for (int i = 0; i < (int) tp->nframes; i++) {
616  struct id3_frame *frame = tp->frames[i];
617 
618  // wxPrintf("ID: %08x '%4s'\n", (int) *(int *)frame->id, frame->id);
619  // wxPrintf("Desc: %s\n", frame->description);
620  // wxPrintf("Num fields: %d\n", frame->nfields);
621 
622  // for (int j = 0; j < (int) frame->nfields; j++) {
623  // wxPrintf("field %d type %d\n", j, frame->fields[j].type );
624  // if (frame->fields[j].type == ID3_FIELD_TYPE_STRINGLIST) {
625  // wxPrintf("num strings %d\n", frame->fields[j].stringlist.nstrings);
626  // }
627  // }
628 
629  wxString n, v;
630 
631  // Determine the tag name
632  if (strcmp(frame->id, ID3_FRAME_TITLE) == 0) {
633  n = TAG_TITLE;
634  }
635  else if (strcmp(frame->id, ID3_FRAME_ARTIST) == 0) {
636  n = TAG_ARTIST;
637  }
638  else if (strcmp(frame->id, ID3_FRAME_ALBUM) == 0) {
639  n = TAG_ALBUM;
640  }
641  else if (strcmp(frame->id, ID3_FRAME_TRACK) == 0) {
642  n = TAG_TRACK;
643  }
644  else if (strcmp(frame->id, ID3_FRAME_YEAR) == 0) {
645  // LLL: When libid3tag encounters the "TYER" tag, it converts it to a
646  // "ZOBS" (obsolete) tag and adds a "TDRC" tag at the end of the
647  // list of tags using the first 4 characters of the "TYER" tag.
648  // Since we write both the "TDRC" and "TYER" tags, the "TDRC" tag
649  // will always be encountered first in the list. We want use it
650  // since the converted "TYER" tag may have been truncated.
651  if (have_year) {
652  continue;
653  }
654  n = TAG_YEAR;
655  have_year = true;
656  }
657  else if (strcmp(frame->id, ID3_FRAME_COMMENT) == 0) {
658  n = TAG_COMMENTS;
659  }
660  else if (strcmp(frame->id, ID3_FRAME_GENRE) == 0) {
661  n = TAG_GENRE;
662  }
663  else {
664  // Use frame description as default tag name. The descriptions
665  // may include several "meanings" separated by "/" characters, so
666  // we just use the first meaning
667  n = UTF8CTOWX(frame->description).BeforeFirst(wxT('/'));
668  }
669 
670  const id3_ucs4_t *ustr = NULL;
671 
672  if (n == TAG_COMMENTS) {
673  ustr = id3_field_getfullstring(&frame->fields[3]);
674  }
675  else if (frame->nfields == 3) {
676  ustr = id3_field_getstring(&frame->fields[1]);
677  if (ustr) {
678  // Is this duplication really needed?
679  MallocString<> convStr{ (char *)id3_ucs4_utf8duplicate(ustr) };
680  n = UTF8CTOWX(convStr.get());
681  }
682 
683  ustr = id3_field_getstring(&frame->fields[2]);
684  }
685  else if (frame->nfields >= 2) {
686  ustr = id3_field_getstrings(&frame->fields[1], 0);
687  }
688 
689  if (ustr) {
690  // Is this duplication really needed?
691  MallocString<> convStr{ (char *)id3_ucs4_utf8duplicate(ustr) };
692  v = UTF8CTOWX(convStr.get());
693  }
694 
695  if (!n.IsEmpty() && !v.IsEmpty()) {
696  tags->SetTag(n, v);
697  }
698  }
699 
700  // Convert v1 genre to name
701  if (tags->HasTag(TAG_GENRE)) {
702  long g = -1;
703  if (tags->GetTag(TAG_GENRE).ToLong(&g)) {
704  tags->SetTag(TAG_GENRE, tags->GetGenre(g));
705  }
706  }
707 
708  break;
709  }
710  }
711  }
712 #endif
713 
714  return updateResult;
715 }
716 
718 {
719 }
void SetTag(const wxString &name, const wxString &value)
Definition: Tags.cpp:449
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
wxString GetPluginFormatDescription() override
Definition: ImportPCM.cpp:123
static ODManager *(* Instance)()
Definition: ODManager.h:49
ProgressResult
#define TAG_TRACK
Definition: Tags.h:63
An ImportFileHandle for data.
Definition: ImportPlugin.h:119
std::vector< std::unique_ptr< WaveTrack > > NewChannelGroup
Definition: ImportRaw.h:26
bool sf_subtype_more_than_16_bits(unsigned int format)
unsigned long long ByteCount
Definition: ImportPlugin.h:151
#define UTF8CTOWX(X)
Definition: Internat.h:179
wxInt32 GetStreamCount() override
Definition: ImportPCM.cpp:100
#define SAMPLE_SIZE(SampleFormat)
Definition: Types.h:198
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:67
#define TAG_TITLE
Definition: Tags.h:60
static wxString AskCopyOrEdit()
Definition: ImportPCM.cpp:227
#define TAG_ARTIST
Definition: Tags.h:61
wxString GetGenre(int value)
Definition: Tags.cpp:391
wxString GetPluginStringID() override
Definition: ImportPCM.cpp:83
#define safenew
Definition: Audacity.h:230
ProgressResult Import(TrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) override
Definition: ImportPCM.cpp:345
void AddNewTask(std::unique_ptr< ODTask > &&mtask, bool lockMutex=true)
Adds a wavetrack, creates a queue member.
Definition: ODManager.cpp:145
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:345
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: Types.h:178
Used to create a WaveTrack, or a LabelTrack.. Implementation of the functions of this class are dispe...
Definition: Track.h:1617
#define DESC
Definition: ImportPCM.cpp:70
#define TAG_SOFTWARE
Definition: Tags.h:67
std::unique_ptr< ImportFileHandle > Open(const wxString &Filename) override
Definition: ImportPCM.cpp:128
An ImportFileHandle for PCM data.
Definition: ImportPCM.cpp:89
void GetPCMImportPlugin(ImportPluginList &importPluginList, UnusableImportPluginList &WXUNUSED(unusableImportPluginList))
Definition: ImportPCM.cpp:117
sampleFormat mFormat
Definition: ImportPCM.cpp:114
wxString sf_header_name(int format)
Get the string name of the specified container format.
sampleFormat
Definition: Types.h:188
ProgressResult Update(int value, const wxString &message=wxEmptyString)
The interface that all file import "plugins" (if you want to call them that) must implement...
#define kMinimumODFileSampleSize
Definition: ImportPCM.cpp:45
int min(int a, int b)
samplePtr ptr() const
Definition: SampleFormat.h:81
Maybe< ProgressDialog > mProgress
Definition: ImportPlugin.h:176
ID3 Tags (for MP3)
Definition: Tags.h:70
void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
Definition: ImportPCM.cpp:108
std::unique_ptr< WaveTrack > NewWaveTrack(sampleFormat format=(sampleFormat) 0, double rate=0)
Definition: WaveTrack.cpp:78
wxString GetTag(const wxString &name) const
Definition: Tags.cpp:424
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:73
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
ByteCount GetFileUncompressedBytes() override
Definition: ImportPCM.cpp:220
PCMImportFileHandle(wxString name, SFFile &&file, SF_INFO info)
Definition: ImportPCM.cpp:192
wxArrayString sf_get_all_extensions()
#define TAG_COPYRIGHT
Definition: Tags.h:68
wxString GetFileDescription() override
Definition: ImportPCM.cpp:215
const wxChar * name
Definition: Distortion.cpp:94
This simplifies arrays of arrays, each array separately allocated with NEW[] But it might be better t...
Definition: MemoryX.h:82
std::vector< NewChannelGroup > TrackHolders
Definition: ImportRaw.h:27
#define TAG_COMMENTS
Definition: Tags.h:66
const wxArrayString & GetStreamInfo() override
Definition: ImportPCM.cpp:102
#define TAG_GENRE
Definition: Tags.h:65
An UnusableImportPlugin list.
An ImportPlugin list.
static sampleFormat SampleFormatChoice()
An ImportPlugin for PCM data.
Definition: ImportPCM.cpp:72
const SF_INFO mInfo
Definition: ImportPCM.cpp:113
wxString mFilename
Definition: ImportPlugin.h:175
#define TAG_ALBUM
Definition: Tags.h:62
#define TAG_YEAR
Definition: Tags.h:64
bool HasTag(const wxString &name) const
Definition: Tags.cpp:415
wxArrayString mExtensions
Definition: ImportPlugin.h:115