Audacity  2.2.2
Public Member Functions | Private Attributes | List of all members
PCMImportFileHandle Class Referencefinal

An ImportFileHandle for PCM data. More...

Inheritance diagram for PCMImportFileHandle:
ImportFileHandle

Public Member Functions

 PCMImportFileHandle (wxString name, SFFile &&file, SF_INFO info)
 
 ~PCMImportFileHandle ()
 
wxString GetFileDescription () override
 
ByteCount GetFileUncompressedBytes () override
 
ProgressResult Import (TrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) override
 
wxInt32 GetStreamCount () override
 
const wxArrayString & GetStreamInfo () override
 
void SetStreamUsage (wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
 
- Public Member Functions inherited from ImportFileHandle
 ImportFileHandle (const wxString &filename)
 
virtual ~ImportFileHandle ()
 
void CreateProgress ()
 
virtual void SetStreamUsage (wxInt32 StreamID, bool Use)=0
 

Private Attributes

SFFile mFile
 
const SF_INFO mInfo
 
sampleFormat mFormat
 

Additional Inherited Members

- Public Types inherited from ImportFileHandle
using ByteCount = unsigned long long
 
- Protected Attributes inherited from ImportFileHandle
wxString mFilename
 
Maybe< ProgressDialogmProgress
 

Detailed Description

An ImportFileHandle for PCM data.

Definition at line 89 of file ImportPCM.cpp.

Constructor & Destructor Documentation

PCMImportFileHandle::PCMImportFileHandle ( wxString  name,
SFFile &&  file,
SF_INFO  info 
)

Definition at line 192 of file ImportPCM.cpp.

References floatSample, mFormat, mInfo, QualityPrefs::SampleFormatChoice(), and sf_subtype_more_than_16_bits().

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 }
bool sf_subtype_more_than_16_bits(unsigned int format)
sampleFormat mFormat
Definition: ImportPCM.cpp:114
ImportFileHandle(const wxString &filename)
Definition: ImportPlugin.h:122
const wxChar * name
Definition: Distortion.cpp:94
static sampleFormat SampleFormatChoice()
const SF_INFO mInfo
Definition: ImportPCM.cpp:113
PCMImportFileHandle::~PCMImportFileHandle ( )

Definition at line 730 of file ImportPCM.cpp.

731 {
732 }

Member Function Documentation

wxString PCMImportFileHandle::GetFileDescription ( )
overridevirtual

Implements ImportFileHandle.

Definition at line 215 of file ImportPCM.cpp.

References mInfo, and sf_header_name().

216 {
217  return SFCall<wxString>(sf_header_name, mInfo.format);
218 }
wxString sf_header_name(int format)
Get the string name of the specified container format.
const SF_INFO mInfo
Definition: ImportPCM.cpp:113
auto PCMImportFileHandle::GetFileUncompressedBytes ( )
overridevirtual

Implements ImportFileHandle.

Definition at line 220 of file ImportPCM.cpp.

References SAMPLE_SIZE.

221 {
222  return mInfo.frames * mInfo.channels * SAMPLE_SIZE(mFormat);
223 }
#define SAMPLE_SIZE(SampleFormat)
Definition: Types.h:198
sampleFormat mFormat
Definition: ImportPCM.cpp:114
const SF_INFO mInfo
Definition: ImportPCM.cpp:113
wxInt32 PCMImportFileHandle::GetStreamCount ( )
inlineoverridevirtual

Implements ImportFileHandle.

Definition at line 100 of file ImportPCM.cpp.

100 { return 1; }
const wxArrayString& PCMImportFileHandle::GetStreamInfo ( )
inlineoverridevirtual

Implements ImportFileHandle.

Definition at line 102 of file ImportPCM.cpp.

103  {
104  static wxArrayString empty;
105  return empty;
106  }
ProgressResult PCMImportFileHandle::Import ( TrackFactory trackFactory,
TrackHolders outTracks,
Tags tags 
)
overridevirtual

Implements ImportFileHandle.

Definition at line 345 of file ImportPCM.cpp.

References ODManager::AddNewTask(), SampleBuffer::Allocate(), AskCopyOrEdit(), Cancelled, ImportFileHandle::CreateProgress(), Failed, floatSample, Tags::GetGenre(), Tags::GetTag(), Tags::HasTag(), ODManager::Instance, int16Sample, kMinimumODFileSampleSize, Track::LeftChannel, limitSampleBufferSize(), mFile, ImportFileHandle::mFilename, mFormat, min(), mInfo, Track::MonoChannel, ImportFileHandle::mProgress, TrackFactory::NewWaveTrack(), SampleBuffer::ptr(), Track::RightChannel, SAMPLE_SIZE, Tags::SetTag(), Success, TAG_ALBUM, TAG_ARTIST, TAG_COMMENTS, TAG_COPYRIGHT, TAG_GENRE, TAG_SOFTWARE, TAG_TITLE, TAG_TRACK, TAG_YEAR, ProgressDialog::Update(), and UTF8CTOWX.

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  TrackHolders channels(mInfo.channels);
368 
369  auto iter = channels.begin();
370  for (int c = 0; c < mInfo.channels; ++iter, ++c) {
371  *iter = trackFactory->NewWaveTrack(mFormat, mInfo.samplerate);
372 
373  if (mInfo.channels > 1)
374  switch (c) {
375  case 0:
376  iter->get()->SetChannel(Track::LeftChannel);
377  break;
378  case 1:
379  iter->get()->SetChannel(Track::RightChannel);
380  break;
381  default:
382  iter->get()->SetChannel(Track::MonoChannel);
383  }
384  }
385 
386  if (mInfo.channels == 2) {
387  channels.begin()->get()->SetLinked(true);
388  }
389 
390  auto fileTotalFrames =
391  (sampleCount)mInfo.frames; // convert from sf_count_t
392  auto maxBlockSize = channels.begin()->get()->GetMaxBlockSize();
393  auto updateResult = ProgressResult::Cancelled;
394 
395  // If the format is not seekable, we must use 'copy' mode,
396  // because 'edit' mode depends on the ability to seek to an
397  // arbitrary location in the file.
398  if (!mInfo.seekable)
399  doEdit = false;
400 
401  if (doEdit) {
402  // If this mode has been selected, we form the tracks as
403  // aliases to the files we're editing, i.e. ("foo.wav", 12000-18000)
404  // instead of actually making fresh copies of the samples.
405 
406  // lets use OD only if the file is longer than 30 seconds. Otherwise, why wake up extra threads.
407  //todo: make this a user pref.
408  bool useOD =fileTotalFrames>kMinimumODFileSampleSize;
409  int updateCounter = 0;
410 
411  for (decltype(fileTotalFrames) i = 0; i < fileTotalFrames; i += maxBlockSize) {
412 
413  const auto blockLen =
414  limitSampleBufferSize( maxBlockSize, fileTotalFrames - i );
415 
416  auto iter = channels.begin();
417  for (int c = 0; c < mInfo.channels; ++iter, ++c)
418  iter->get()->AppendAlias(mFilename, i, blockLen, c,useOD);
419 
420  if (++updateCounter == 50) {
421  updateResult = mProgress->Update(
422  i.as_long_long(),
423  fileTotalFrames.as_long_long()
424  );
425  updateCounter = 0;
426  if (updateResult != ProgressResult::Success)
427  break;
428  }
429  }
430 
431  // One last update for completion
432  updateResult = mProgress->Update(
433  fileTotalFrames.as_long_long(),
434  fileTotalFrames.as_long_long()
435  );
436 
437  if(useOD)
438  {
439  auto computeTask = std::make_unique<ODComputeSummaryTask>();
440  bool moreThanStereo = mInfo.channels>2;
441  for (const auto &channel : channels)
442  {
443  computeTask->AddWaveTrack(channel.get());
444  if(moreThanStereo)
445  {
446  //if we have 3 more channels, they get imported on seperate tracks, so we add individual tasks for each.
447  ODManager::Instance()->AddNewTask(std::move(computeTask));
448  computeTask = std::make_unique<ODComputeSummaryTask>();
449  }
450  }
451  //if we have a linked track, we add ONE task.
452  if(!moreThanStereo)
453  ODManager::Instance()->AddNewTask(std::move(computeTask));
454  }
455  }
456  else {
457  // Otherwise, we're in the "copy" mode, where we read in the actual
458  // samples from the file and store our own local copy of the
459  // samples in the tracks.
460 
461  // PRL: guard against excessive memory buffer allocation in case of many channels
462  using type = decltype(maxBlockSize);
463  if (mInfo.channels < 1)
464  return ProgressResult::Failed;
465  auto maxBlock = std::min(maxBlockSize,
466  std::numeric_limits<type>::max() /
467  (mInfo.channels * SAMPLE_SIZE(mFormat))
468  );
469  if (maxBlock < 1)
470  return ProgressResult::Failed;
471 
472  SampleBuffer srcbuffer, buffer;
473  wxASSERT(mInfo.channels >= 0);
474  while (NULL == srcbuffer.Allocate(maxBlock * mInfo.channels, mFormat).ptr() ||
475  NULL == buffer.Allocate(maxBlock, mFormat).ptr())
476  {
477  maxBlock /= 2;
478  if (maxBlock < 1)
479  return ProgressResult::Failed;
480  }
481 
482  decltype(fileTotalFrames) framescompleted = 0;
483 
484  long block;
485  do {
486  block = maxBlock;
487 
488  if (mFormat == int16Sample)
489  block = SFCall<sf_count_t>(sf_readf_short, mFile.get(), (short *)srcbuffer.ptr(), block);
490  //import 24 bit int as float and have the append function convert it. This is how PCMAliasBlockFile works too.
491  else
492  block = SFCall<sf_count_t>(sf_readf_float, mFile.get(), (float *)srcbuffer.ptr(), block);
493 
494  if(block < 0 || block > (long)maxBlock) {
495  wxASSERT(false);
496  block = maxBlock;
497  }
498 
499  if (block) {
500  auto iter = channels.begin();
501  for(int c=0; c<mInfo.channels; ++iter, ++c) {
502  if (mFormat==int16Sample) {
503  for(int j=0; j<block; j++)
504  ((short *)buffer.ptr())[j] =
505  ((short *)srcbuffer.ptr())[mInfo.channels*j+c];
506  }
507  else {
508  for(int j=0; j<block; j++)
509  ((float *)buffer.ptr())[j] =
510  ((float *)srcbuffer.ptr())[mInfo.channels*j+c];
511  }
512 
513  iter->get()->Append(buffer.ptr(), (mFormat == int16Sample)?int16Sample:floatSample, block);
514  }
515  framescompleted += block;
516  }
517 
518  updateResult = mProgress->Update(
519  framescompleted.as_long_long(),
520  fileTotalFrames.as_long_long()
521  );
522  if (updateResult != ProgressResult::Success)
523  break;
524 
525  } while (block > 0);
526  }
527 
528  if (updateResult == ProgressResult::Failed || updateResult == ProgressResult::Cancelled) {
529  return updateResult;
530  }
531 
532  for(const auto &channel : channels) {
533  channel->Flush();
534  }
535  outTracks.swap(channels);
536 
537  const char *str;
538 
539  str = sf_get_string(mFile.get(), SF_STR_TITLE);
540  if (str) {
541  tags->SetTag(TAG_TITLE, UTF8CTOWX(str));
542  }
543 
544  str = sf_get_string(mFile.get(), SF_STR_ALBUM);
545  if (str) {
546  tags->SetTag(TAG_ALBUM, UTF8CTOWX(str));
547  }
548 
549  str = sf_get_string(mFile.get(), SF_STR_ARTIST);
550  if (str) {
551  tags->SetTag(TAG_ARTIST, UTF8CTOWX(str));
552  }
553 
554  str = sf_get_string(mFile.get(), SF_STR_COMMENT);
555  if (str) {
556  tags->SetTag(TAG_COMMENTS, UTF8CTOWX(str));
557  }
558 
559  str = sf_get_string(mFile.get(), SF_STR_DATE);
560  if (str) {
561  tags->SetTag(TAG_YEAR, UTF8CTOWX(str));
562  }
563 
564  str = sf_get_string(mFile.get(), SF_STR_COPYRIGHT);
565  if (str) {
566  tags->SetTag(TAG_COPYRIGHT, UTF8CTOWX(str));
567  }
568 
569  str = sf_get_string(mFile.get(), SF_STR_SOFTWARE);
570  if (str) {
571  tags->SetTag(TAG_SOFTWARE, UTF8CTOWX(str));
572  }
573 
574  str = sf_get_string(mFile.get(), SF_STR_TRACKNUMBER);
575  if (str) {
576  tags->SetTag(TAG_TRACK, UTF8CTOWX(str));
577  }
578 
579  str = sf_get_string(mFile.get(), SF_STR_GENRE);
580  if (str) {
581  tags->SetTag(TAG_GENRE, UTF8CTOWX(str));
582  }
583 
584 #if defined(USE_LIBID3TAG)
585  if (((mInfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) ||
586  ((mInfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)) {
587  wxFFile f(mFilename, wxT("rb"));
588  if (f.IsOpened()) {
589  char id[5];
590  wxUint32 len;
591 
592  id[4] = '\0';
593 
594  f.Seek(12); // Skip filetype, length, and formtype
595 
596  while (!f.Error()) {
597  f.Read(id, 4); // Get chunk type
598  if (f.Eof()) {
599  break;
600  }
601  f.Read(&len, 4);
602  if((mInfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
603  len = wxUINT32_SWAP_ON_LE(len);
604 
605  if (wxStricmp(id, "ID3 ") != 0) { // must be case insensitive
606  f.Seek(len + (len & 0x01), wxFromCurrent);
607  continue;
608  }
609 
610 
611  id3_tag_holder tp;
612  {
613  ArrayOf<id3_byte_t> buffer{ len };
614  if (!buffer) {
615  break;
616  }
617 
618  f.Read(buffer.get(), len);
619  tp.reset( id3_tag_parse(buffer.get(), len) );
620  }
621 
622  if (!tp) {
623  break;
624  }
625 
626  // Loop through all frames
627  bool have_year = false;
628  for (int i = 0; i < (int) tp->nframes; i++) {
629  struct id3_frame *frame = tp->frames[i];
630 
631  // wxPrintf("ID: %08x '%4s'\n", (int) *(int *)frame->id, frame->id);
632  // wxPrintf("Desc: %s\n", frame->description);
633  // wxPrintf("Num fields: %d\n", frame->nfields);
634 
635  // for (int j = 0; j < (int) frame->nfields; j++) {
636  // wxPrintf("field %d type %d\n", j, frame->fields[j].type );
637  // if (frame->fields[j].type == ID3_FIELD_TYPE_STRINGLIST) {
638  // wxPrintf("num strings %d\n", frame->fields[j].stringlist.nstrings);
639  // }
640  // }
641 
642  wxString n, v;
643 
644  // Determine the tag name
645  if (strcmp(frame->id, ID3_FRAME_TITLE) == 0) {
646  n = TAG_TITLE;
647  }
648  else if (strcmp(frame->id, ID3_FRAME_ARTIST) == 0) {
649  n = TAG_ARTIST;
650  }
651  else if (strcmp(frame->id, ID3_FRAME_ALBUM) == 0) {
652  n = TAG_ALBUM;
653  }
654  else if (strcmp(frame->id, ID3_FRAME_TRACK) == 0) {
655  n = TAG_TRACK;
656  }
657  else if (strcmp(frame->id, ID3_FRAME_YEAR) == 0) {
658  // LLL: When libid3tag encounters the "TYER" tag, it converts it to a
659  // "ZOBS" (obsolete) tag and adds a "TDRC" tag at the end of the
660  // list of tags using the first 4 characters of the "TYER" tag.
661  // Since we write both the "TDRC" and "TYER" tags, the "TDRC" tag
662  // will always be encountered first in the list. We want use it
663  // since the converted "TYER" tag may have been truncated.
664  if (have_year) {
665  continue;
666  }
667  n = TAG_YEAR;
668  have_year = true;
669  }
670  else if (strcmp(frame->id, ID3_FRAME_COMMENT) == 0) {
671  n = TAG_COMMENTS;
672  }
673  else if (strcmp(frame->id, ID3_FRAME_GENRE) == 0) {
674  n = TAG_GENRE;
675  }
676  else {
677  // Use frame description as default tag name. The descriptions
678  // may include several "meanings" separated by "/" characters, so
679  // we just use the first meaning
680  n = UTF8CTOWX(frame->description).BeforeFirst(wxT('/'));
681  }
682 
683  const id3_ucs4_t *ustr = NULL;
684 
685  if (n == TAG_COMMENTS) {
686  ustr = id3_field_getfullstring(&frame->fields[3]);
687  }
688  else if (frame->nfields == 3) {
689  ustr = id3_field_getstring(&frame->fields[1]);
690  if (ustr) {
691  // Is this duplication really needed?
692  MallocString<> str{ (char *)id3_ucs4_utf8duplicate(ustr) };
693  n = UTF8CTOWX(str.get());
694  }
695 
696  ustr = id3_field_getstring(&frame->fields[2]);
697  }
698  else if (frame->nfields >= 2) {
699  ustr = id3_field_getstrings(&frame->fields[1], 0);
700  }
701 
702  if (ustr) {
703  // Is this duplication really needed?
704  MallocString<> str{ (char *)id3_ucs4_utf8duplicate(ustr) };
705  v = UTF8CTOWX(str.get());
706  }
707 
708  if (!n.IsEmpty() && !v.IsEmpty()) {
709  tags->SetTag(n, v);
710  }
711  }
712 
713  // Convert v1 genre to name
714  if (tags->HasTag(TAG_GENRE)) {
715  long g = -1;
716  if (tags->GetTag(TAG_GENRE).ToLong(&g)) {
717  tags->SetTag(TAG_GENRE, tags->GetGenre(g));
718  }
719  }
720 
721  break;
722  }
723  }
724  }
725 #endif
726 
727  return updateResult;
728 }
void SetTag(const wxString &name, const wxString &value)
Definition: Tags.cpp:449
static ODManager *(* Instance)()
Definition: ODManager.h:49
#define TAG_TRACK
Definition: Tags.h:63
#define UTF8CTOWX(X)
Definition: Internat.h:179
#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
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:417
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: Types.h:178
#define TAG_SOFTWARE
Definition: Tags.h:67
sampleFormat mFormat
Definition: ImportPCM.cpp:114
ProgressResult Update(int value, const wxString &message=wxEmptyString)
#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:174
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
#define TAG_COPYRIGHT
Definition: Tags.h:68
Memory.h template class for making an array of float, bool, etc.
Definition: MemoryX.h:86
#define TAG_COMMENTS
Definition: Tags.h:66
#define TAG_GENRE
Definition: Tags.h:65
std::vector< std::unique_ptr< WaveTrack >> TrackHolders
Definition: ImportRaw.h:24
const SF_INFO mInfo
Definition: ImportPCM.cpp:113
wxString mFilename
Definition: ImportPlugin.h:173
#define TAG_ALBUM
Definition: Tags.h:62
#define TAG_YEAR
Definition: Tags.h:64
bool HasTag(const wxString &name) const
Definition: Tags.cpp:415
void PCMImportFileHandle::SetStreamUsage ( wxInt32   WXUNUSEDStreamID,
bool   WXUNUSEDUse 
)
inlineoverride

Definition at line 108 of file ImportPCM.cpp.

109  {}

Member Data Documentation

SFFile PCMImportFileHandle::mFile
private

Definition at line 112 of file ImportPCM.cpp.

Referenced by Import().

sampleFormat PCMImportFileHandle::mFormat
private

Definition at line 114 of file ImportPCM.cpp.

Referenced by Import(), and PCMImportFileHandle().

const SF_INFO PCMImportFileHandle::mInfo
private

Definition at line 113 of file ImportPCM.cpp.

Referenced by GetFileDescription(), Import(), and PCMImportFileHandle().


The documentation for this class was generated from the following file: