Audacity  3.0.3
Public Member Functions | Private Member Functions | List of all members
ExportPCM Class Referencefinal
Inheritance diagram for ExportPCM:
[legend]
Collaboration diagram for ExportPCM:
[legend]

Public Member Functions

 ExportPCM ()
 
void OptionsCreate (ShuttleGui &S, int format) override
 
ProgressResult Export (AudacityProject *project, std::unique_ptr< ProgressDialog > &pDialog, unsigned channels, const wxFileNameWrapper &fName, bool selectedOnly, double t0, double t1, MixerSpec *mixerSpec=NULL, const Tags *metadata=NULL, int subformat=0) override
 
wxString GetFormat (int index) override
 
FileExtension GetExtension (int index) override
 Return the (first) file name extension for the sub-format. More...
 
unsigned GetMaxChannels (int index) override
 
- Public Member Functions inherited from ExportPlugin
 ExportPlugin ()
 
virtual ~ExportPlugin ()
 
int AddFormat ()
 Add a NEW entry to the list of formats this plug-in can export. More...
 
void SetFormat (const wxString &format, int index)
 
void SetDescription (const TranslatableString &description, int index)
 
void AddExtension (const FileExtension &extension, int index)
 
void SetExtensions (FileExtensions extensions, int index)
 
void SetMask (FileNames::FileTypes mask, int index)
 
void SetMaxChannels (unsigned maxchannels, unsigned index)
 
void SetCanMetaData (bool canmetadata, int index)
 
virtual int GetFormatCount ()
 
TranslatableString GetDescription (int index)
 
virtual FileExtensions GetExtensions (int index=0)
 Return all the file name extensions used for the sub-format. More...
 
FileNames::FileTypes GetMask (int index)
 
virtual bool GetCanMetaData (int index)
 
virtual bool IsExtension (const FileExtension &ext, int index)
 
virtual bool DisplayOptions (wxWindow *parent, int format=0)
 
virtual bool CheckFileName (wxFileName &filename, int format=0)
 
virtual int SetNumExportChannels ()
 Exporter plug-ins may override this to specify the number of channels in exported file. -1 for unspecified. More...
 

Private Member Functions

void ReportTooBigError (wxWindow *pParent)
 
ArrayOf< char > AdjustString (const wxString &wxStr, int sf_format)
 
bool AddStrings (AudacityProject *project, SNDFILE *sf, const Tags *tags, int sf_format)
 
bool AddID3Chunk (const wxFileNameWrapper &fName, const Tags *tags, int sf_format)
 

Additional Inherited Members

- Public Types inherited from ExportPlugin
using ProgressResult = BasicUI::ProgressResult
 
- Protected Member Functions inherited from ExportPlugin
std::unique_ptr< MixerCreateMixer (const TrackList &tracks, bool selectionOnly, double startTime, double stopTime, unsigned numOutChannels, size_t outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, MixerSpec *mixerSpec)
 
- Static Protected Member Functions inherited from ExportPlugin
static void InitProgress (std::unique_ptr< ProgressDialog > &pDialog, const TranslatableString &title, const TranslatableString &message)
 
static void InitProgress (std::unique_ptr< ProgressDialog > &pDialog, const wxFileNameWrapper &title, const TranslatableString &message)
 

Detailed Description

Definition at line 382 of file ExportPCM.cpp.

Constructor & Destructor Documentation

◆ ExportPCM()

ExportPCM::ExportPCM ( )

Definition at line 415 of file ExportPCM.cpp.

416  : ExportPlugin()
417 {
418  int selformat; // the index of the format we are setting up at the moment
419 
420  // Add the "special" formats first
421  for (size_t i = 0; i < WXSIZEOF(kFormats); ++i)
422  {
423  selformat = AddFormat() - 1;
425  SetFormat(kFormats[i].name, selformat);
426  SetDescription(kFormats[i].desc, selformat);
427  SetCanMetaData(true, selformat);
428  SetMaxChannels(255, selformat);
429  }
430 
431  // Then add the generic libsndfile "format"
432  selformat = AddFormat() - 1; // Matches FMT_OTHER
433  SetExtensions(sf_get_all_extensions(), selformat);
434  SetFormat(wxT("LIBSNDFILE"), selformat);
435  SetDescription(XO("Other uncompressed files"), selformat);
436  SetCanMetaData(true, selformat);
437  SetMaxChannels(255, selformat);
438 }

References ExportPlugin::AddExtension(), ExportPlugin::AddFormat(), desc, format, kFormats, name, ExportPlugin::SetCanMetaData(), ExportPlugin::SetDescription(), ExportPlugin::SetExtensions(), ExportPlugin::SetFormat(), ExportPlugin::SetMaxChannels(), sf_get_all_extensions(), sf_header_extension(), and XO.

Here is the call graph for this function:

Member Function Documentation

◆ AddID3Chunk()

bool ExportPCM::AddID3Chunk ( const wxFileNameWrapper fName,
const Tags tags,
int  sf_format 
)
private

Definition at line 890 of file ExportPCM.cpp.

892 {
893 #ifdef USE_LIBID3TAG
894  id3_tag_holder tp { id3_tag_new() };
895 
896  for (const auto &pair : tags->GetRange()) {
897  const auto &n = pair.first;
898  const auto &v = pair.second;
899  const char *name = "TXXX";
900 
901  if (n.CmpNoCase(TAG_TITLE) == 0) {
902  name = ID3_FRAME_TITLE;
903  }
904  else if (n.CmpNoCase(TAG_ARTIST) == 0) {
905  name = ID3_FRAME_ARTIST;
906  }
907  else if (n.CmpNoCase(TAG_ALBUM) == 0) {
908  name = ID3_FRAME_ALBUM;
909  }
910  else if (n.CmpNoCase(TAG_YEAR) == 0) {
911  name = ID3_FRAME_YEAR;
912  }
913  else if (n.CmpNoCase(TAG_GENRE) == 0) {
914  name = ID3_FRAME_GENRE;
915  }
916  else if (n.CmpNoCase(TAG_COMMENTS) == 0) {
917  name = ID3_FRAME_COMMENT;
918  }
919  else if (n.CmpNoCase(TAG_TRACK) == 0) {
920  name = ID3_FRAME_TRACK;
921  }
922  else if (n.CmpNoCase(wxT("composer")) == 0) {
923  name = "TCOM";
924  }
925 
926  struct id3_frame *frame = id3_frame_new(name);
927 
928  if (!n.IsAscii() || !v.IsAscii()) {
929  id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_UTF_16);
930  }
931  else {
932  id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_ISO_8859_1);
933  }
934 
936  id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) v.mb_str(wxConvUTF8)) };
937 
938  if (strcmp(name, ID3_FRAME_COMMENT) == 0) {
939  // A hack to get around iTunes not recognizing the comment. The
940  // language defaults to XXX and, since it's not a valid language,
941  // iTunes just ignores the tag. So, either set it to a valid language
942  // (which one???) or just clear it. Unfortunately, there's no supported
943  // way of clearing the field, so do it directly.
944  id3_field *f = id3_frame_field(frame, 1);
945  memset(f->immediate.value, 0, sizeof(f->immediate.value));
946  id3_field_setfullstring(id3_frame_field(frame, 3), ucs4.get());
947  }
948  else if (strcmp(name, "TXXX") == 0) {
949  id3_field_setstring(id3_frame_field(frame, 2), ucs4.get());
950 
951  ucs4.reset(id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) n.mb_str(wxConvUTF8)));
952 
953  id3_field_setstring(id3_frame_field(frame, 1), ucs4.get());
954  }
955  else {
956  auto addr = ucs4.get();
957  id3_field_setstrings(id3_frame_field(frame, 1), 1, &addr);
958  }
959 
960  id3_tag_attachframe(tp.get(), frame);
961  }
962 
963  tp->options &= (~ID3_TAG_OPTION_COMPRESSION); // No compression
964 
965  // If this version of libid3tag supports it, use v2.3 ID3
966  // tags instead of the newer, but less well supported, v2.4
967  // that libid3tag uses by default.
968 #ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3
969  tp->options |= ID3_TAG_OPTION_ID3V2_3;
970 #endif
971 
972  id3_length_t len;
973 
974  len = id3_tag_render(tp.get(), 0);
975  if (len == 0)
976  return true;
977 
978  if ((len % 2) != 0) len++; // Length must be even.
979  ArrayOf<id3_byte_t> buffer { len, true };
980  if (buffer == NULL)
981  return false;
982 
983  // Zero all locations, for ending odd UTF16 content
984  // correctly, i.e., two '\0's at the end.
985 
986  id3_tag_render(tp.get(), buffer.get());
987 
988  wxFFile f(fName.GetFullPath(), wxT("r+b"));
989  if (f.IsOpened()) {
990  wxUint32 sz;
991 
992  sz = (wxUint32) len;
993  if (!f.SeekEnd(0))
994  return false;
995  if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)
996  {
997  if (4 != f.Write("id3 ", 4))// Must be lower case for foobar2000.
998  return false ;
999  }
1000  else {
1001  if (4 != f.Write("ID3 ", 4))
1002  return false;
1003  sz = wxUINT32_SWAP_ON_LE(sz);
1004  }
1005  if (4 != f.Write(&sz, 4))
1006  return false;
1007 
1008  if (len != f.Write(buffer.get(), len))
1009  return false;
1010 
1011  sz = (wxUint32) f.Tell() - 8;
1012  if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
1013  sz = wxUINT32_SWAP_ON_LE(sz);
1014 
1015  if (!f.Seek(4))
1016  return false;
1017  if (4 != f.Write(&sz, 4))
1018  return false;
1019 
1020  if (!f.Flush())
1021  return false;
1022 
1023  if (!f.Close())
1024  return false;
1025  }
1026  else
1027  return false;
1028 #endif
1029  return true;
1030 }

References Tags::GetRange(), name, TAG_ALBUM, TAG_ARTIST, TAG_COMMENTS, TAG_GENRE, TAG_TITLE, TAG_TRACK, and TAG_YEAR.

Referenced by Export().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AddStrings()

bool ExportPCM::AddStrings ( AudacityProject project,
SNDFILE *  sf,
const Tags tags,
int  sf_format 
)
private

Definition at line 815 of file ExportPCM.cpp.

816 {
817  if (tags->HasTag(TAG_TITLE)) {
818  auto ascii7Str = AdjustString(tags->GetTag(TAG_TITLE), sf_format);
819  if (ascii7Str) {
820  sf_set_string(sf, SF_STR_TITLE, ascii7Str.get());
821  }
822  }
823 
824  if (tags->HasTag(TAG_ALBUM)) {
825  auto ascii7Str = AdjustString(tags->GetTag(TAG_ALBUM), sf_format);
826  if (ascii7Str) {
827  sf_set_string(sf, SF_STR_ALBUM, ascii7Str.get());
828  }
829  }
830 
831  if (tags->HasTag(TAG_ARTIST)) {
832  auto ascii7Str = AdjustString(tags->GetTag(TAG_ARTIST), sf_format);
833  if (ascii7Str) {
834  sf_set_string(sf, SF_STR_ARTIST, ascii7Str.get());
835  }
836  }
837 
838  if (tags->HasTag(TAG_COMMENTS)) {
839  auto ascii7Str = AdjustString(tags->GetTag(TAG_COMMENTS), sf_format);
840  if (ascii7Str) {
841  sf_set_string(sf, SF_STR_COMMENT, ascii7Str.get());
842  }
843  }
844 
845  if (tags->HasTag(TAG_YEAR)) {
846  auto ascii7Str = AdjustString(tags->GetTag(TAG_YEAR), sf_format);
847  if (ascii7Str) {
848  sf_set_string(sf, SF_STR_DATE, ascii7Str.get());
849  }
850  }
851 
852  if (tags->HasTag(TAG_GENRE)) {
853  auto ascii7Str = AdjustString(tags->GetTag(TAG_GENRE), sf_format);
854  if (ascii7Str) {
855  sf_set_string(sf, SF_STR_GENRE, ascii7Str.get());
856  }
857  }
858 
859  if (tags->HasTag(TAG_COPYRIGHT)) {
860  auto ascii7Str = AdjustString(tags->GetTag(TAG_COPYRIGHT), sf_format);
861  if (ascii7Str) {
862  sf_set_string(sf, SF_STR_COPYRIGHT, ascii7Str.get());
863  }
864  }
865 
866  if (tags->HasTag(TAG_SOFTWARE)) {
867  auto ascii7Str = AdjustString(tags->GetTag(TAG_SOFTWARE), sf_format);
868  if (ascii7Str) {
869  sf_set_string(sf, SF_STR_SOFTWARE, ascii7Str.get());
870  }
871  }
872 
873  if (tags->HasTag(TAG_TRACK)) {
874  auto ascii7Str = AdjustString(tags->GetTag(TAG_TRACK), sf_format);
875  if (ascii7Str) {
876  sf_set_string(sf, SF_STR_TRACKNUMBER, ascii7Str.get());
877  }
878  }
879 
880  return true;
881 }

References AdjustString(), Tags::GetTag(), Tags::HasTag(), TAG_ALBUM, TAG_ARTIST, TAG_COMMENTS, TAG_COPYRIGHT, TAG_GENRE, TAG_SOFTWARE, TAG_TITLE, TAG_TRACK, and TAG_YEAR.

Referenced by Export().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AdjustString()

ArrayOf< char > ExportPCM::AdjustString ( const wxString &  wxStr,
int  sf_format 
)
private

Definition at line 728 of file ExportPCM.cpp.

729 {
730  bool b_aiff = false;
731  if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
732  b_aiff = true; // Apple AIFF file
733 
734  // We must convert the string to 7 bit ASCII
735  size_t sz = wxStr.length();
736  if(sz == 0)
737  return {};
738  // Size for secure allocation in case of local wide char usage
739  size_t sr = (sz+4) * 2;
740 
741  ArrayOf<char> pDest{ sr, true };
742  if (!pDest)
743  return {};
744  ArrayOf<char> pSrc{ sr, true };
745  if (!pSrc)
746  return {};
747 
748  if(wxStr.mb_str(wxConvISO8859_1))
749  strncpy(pSrc.get(), wxStr.mb_str(wxConvISO8859_1), sz);
750  else if(wxStr.mb_str())
751  strncpy(pSrc.get(), wxStr.mb_str(), sz);
752  else
753  return {};
754 
755  char *pD = pDest.get();
756  char *pS = pSrc.get();
757  unsigned char c;
758 
759  // ISO Latin to 7 bit ascii conversion table (best approximation)
760  static char aASCII7Table[256] = {
761  0x00, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
762  0x5f, 0x09, 0x0a, 0x5f, 0x0d, 0x5f, 0x5f, 0x5f,
763  0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
764  0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
765  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
766  0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
767  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
768  0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
769  0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
770  0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
771  0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
772  0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
773  0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
774  0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
775  0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
776  0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
777  0x45, 0x20, 0x2c, 0x53, 0x22, 0x2e, 0x2b, 0x2b,
778  0x5e, 0x25, 0x53, 0x28, 0x4f, 0x20, 0x5a, 0x20,
779  0x20, 0x27, 0x27, 0x22, 0x22, 0x2e, 0x2d, 0x5f,
780  0x22, 0x54, 0x73, 0x29, 0x6f, 0x20, 0x7a, 0x59,
781  0x20, 0x21, 0x63, 0x4c, 0x6f, 0x59, 0x7c, 0x53,
782  0x22, 0x43, 0x61, 0x22, 0x5f, 0x2d, 0x43, 0x2d,
783  0x6f, 0x7e, 0x32, 0x33, 0x27, 0x75, 0x50, 0x27,
784  0x2c, 0x31, 0x6f, 0x22, 0x5f, 0x5f, 0x5f, 0x3f,
785  0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x43,
786  0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
787  0x44, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x78,
788  0x4f, 0x55, 0x55, 0x55, 0x55, 0x59, 0x70, 0x53,
789  0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x63,
790  0x65, 0x65, 0x65, 0x65, 0x69, 0x69, 0x69, 0x69,
791  0x64, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x2f,
792  0x6f, 0x75, 0x75, 0x75, 0x75, 0x79, 0x70, 0x79
793  };
794 
795  size_t i;
796  for(i = 0; i < sr; i++) {
797  c = (unsigned char) *pS++;
798  *pD++ = aASCII7Table[c];
799  if(c == 0)
800  break;
801  }
802  *pD = '\0';
803 
804  if(b_aiff) {
805  int len = (int)strlen(pDest.get());
806  if((len % 2) != 0) {
807  // In case of an odd length string, add a space char
808  strcat(pDest.get(), " ");
809  }
810  }
811 
812  return pDest;
813 }

Referenced by AddStrings().

Here is the caller graph for this function:

◆ Export()

ProgressResult ExportPCM::Export ( AudacityProject project,
std::unique_ptr< ProgressDialog > &  pDialog,
unsigned  numChannels,
const wxFileNameWrapper fName,
bool  selectionOnly,
double  t0,
double  t1,
MixerSpec mixerSpec = NULL,
const Tags metadata = NULL,
int  subformat = 0 
)
overridevirtual
Parameters
subformatControl whether we are doing a "preset" export to a popular file type, or giving the user full control over libsndfile.

Implements ExportPlugin.

Definition at line 467 of file ExportPCM.cpp.

477 {
478  double rate = ProjectRate::Get( *project ).GetRate();
479  const auto &tracks = TrackList::Get( *project );
480 
481  // Set a default in case the settings aren't found
482  int sf_format;
483 
484  switch (subformat)
485  {
486 #if defined(__WXMAC__)
487  case FMT_AIFF:
488  sf_format = SF_FORMAT_AIFF;
489  break;
490 #endif
491 
492  case FMT_WAV:
493  sf_format = SF_FORMAT_WAV;
494  break;
495 
496  default:
497  // Retrieve the current format.
498  sf_format = LoadOtherFormat();
499  break;
500  }
501 
502  // Prior to v2.4.0, sf_format will include the subtype. If not present,
503  // check for the format specific preference.
504  if (!(sf_format & SF_FORMAT_SUBMASK))
505  {
506  sf_format |= LoadEncoding(sf_format);
507  }
508 
509  // If subtype is still not specified, supply a default.
510  if (!(sf_format & SF_FORMAT_SUBMASK))
511  {
512  sf_format |= SF_FORMAT_PCM_16;
513  }
514 
515  int fileFormat = sf_format & SF_FORMAT_TYPEMASK;
516 
517  auto updateResult = ProgressResult::Success;
518  {
519  wxFile f; // will be closed when it goes out of scope
520  SFFile sf; // wraps f
521 
522  wxString formatStr;
523  SF_INFO info;
524  //int err;
525 
526  //This whole operation should not occur while a file is being loaded on OD,
527  //(we are worried about reading from a file being written to,) so we block.
528  //Furthermore, we need to do this because libsndfile is not threadsafe.
529  formatStr = SFCall<wxString>(sf_header_name, fileFormat);
530 
531  // Use libsndfile to export file
532 
533  info.samplerate = (unsigned int)(rate + 0.5);
534  info.frames = (unsigned int)((t1 - t0)*rate + 0.5);
535  info.channels = numChannels;
536  info.format = sf_format;
537  info.sections = 1;
538  info.seekable = 0;
539 
540  // Bug 46. Trap here, as sndfile.c does not trap it properly.
541  if( (numChannels != 1) && ((sf_format & SF_FORMAT_SUBMASK) == SF_FORMAT_GSM610) )
542  {
543  AudacityMessageBox( XO("GSM 6.10 requires mono") );
545  }
546 
547  if (sf_format == SF_FORMAT_WAVEX + SF_FORMAT_GSM610) {
549  XO("WAVEX and GSM 6.10 formats are not compatible") );
551  }
552 
553  // If we can't export exactly the format they requested,
554  // try the default format for that header type...
555  //
556  // LLL: I don't think this is valid since libsndfile checks
557  // for all allowed subtypes explicitly and doesn't provide
558  // for an unspecified subtype.
559  if (!sf_format_check(&info))
560  info.format = (info.format & SF_FORMAT_TYPEMASK);
561  if (!sf_format_check(&info)) {
562  AudacityMessageBox( XO("Cannot export audio in this format.") );
564  }
565  const auto path = fName.GetFullPath();
566  if (f.Open(path, wxFile::write)) {
567  // Even though there is an sf_open() that takes a filename, use the one that
568  // takes a file descriptor since wxWidgets can open a file with a Unicode name and
569  // libsndfile can't (under Windows).
570  sf.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_WRITE, &info, FALSE));
571  //add clipping for integer formats. We allow floats to clip.
572  sf_command(sf.get(), SFC_SET_CLIPPING, NULL, sf_subtype_is_integer(sf_format)?SF_TRUE:SF_FALSE) ;
573  }
574 
575  if (!sf) {
576  AudacityMessageBox( XO("Cannot export audio to %s").Format( path ) );
578  }
579  // Retrieve tags if not given a set
580  if (metadata == NULL)
581  metadata = &Tags::Get( *project );
582 
583  // Install the meta data at the beginning of the file (except for
584  // WAV and WAVEX formats)
585  if (fileFormat != SF_FORMAT_WAV &&
586  fileFormat != SF_FORMAT_WAVEX) {
587  if (!AddStrings(project, sf.get(), metadata, sf_format)) {
589  }
590  }
591 
593  if (sf_subtype_more_than_16_bits(info.format))
595  else
597 
598  // Bug 2200
599  // Only trap size limit for file types we know have an upper size limit.
600  // The error message mentions aiff and wav.
601  if( (fileFormat == SF_FORMAT_WAV) ||
602  (fileFormat == SF_FORMAT_WAVEX) ||
603  (fileFormat == SF_FORMAT_AIFF ))
604  {
605  float sampleCount = (float)(t1-t0)*rate*info.channels;
606  float byteCount = sampleCount * sf_subtype_bytes_per_sample( info.format);
607  // Test for 4 Gibibytes, rather than 4 Gigabytes
608  if( byteCount > 4.295e9)
609  {
610  ReportTooBigError( wxTheApp->GetTopWindow() );
611  return ProgressResult::Failed;
612  }
613  }
614  size_t maxBlockLen = 44100 * 5;
615 
616  {
617  std::vector<char> dither;
618  if ((info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
619  dither.reserve(maxBlockLen * info.channels * SAMPLE_SIZE(int24Sample));
620  }
621 
622  wxASSERT(info.channels >= 0);
623  auto mixer = CreateMixer(tracks, selectionOnly,
624  t0, t1,
625  info.channels, maxBlockLen, true,
626  rate, format, mixerSpec);
627 
628  InitProgress( pDialog, fName,
629  (selectionOnly
630  ? XO("Exporting the selected audio as %s")
631  : XO("Exporting the audio as %s"))
632  .Format( formatStr ) );
633  auto &progress = *pDialog;
634 
635  while (updateResult == ProgressResult::Success) {
636  sf_count_t samplesWritten;
637  size_t numSamples = mixer->Process(maxBlockLen);
638 
639  if (numSamples == 0)
640  break;
641 
642  auto mixed = mixer->GetBuffer();
643 
644  // Bug 1572: Not ideal, but it does add the desired dither
645  if ((info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
646  for (int c = 0; c < info.channels; ++c) {
647  CopySamples(
648  mixed + (c * SAMPLE_SIZE(format)), format,
649  dither.data() + (c * SAMPLE_SIZE(int24Sample)), int24Sample,
650  numSamples, gHighQualityDither, info.channels, info.channels
651  );
652  // Copy back without dither
653  CopySamples(
654  dither.data() + (c * SAMPLE_SIZE(int24Sample)), int24Sample,
655  const_cast<samplePtr>(mixed) // PRL fix this!
656  + (c * SAMPLE_SIZE(format)), format,
657  numSamples, DitherType::none, info.channels, info.channels);
658  }
659  }
660 
661  if (format == int16Sample)
662  samplesWritten = SFCall<sf_count_t>(sf_writef_short, sf.get(), (const short *)mixed, numSamples);
663  else
664  samplesWritten = SFCall<sf_count_t>(sf_writef_float, sf.get(), (const float *)mixed, numSamples);
665 
666  if (static_cast<size_t>(samplesWritten) != numSamples) {
667  char buffer2[1000];
668  sf_error_str(sf.get(), buffer2, 1000);
669  //Used to give this error message
670 #if 0
672  XO(
673  /* i18n-hint: %s will be the error message from libsndfile, which
674  * is usually something unhelpful (and untranslated) like "system
675  * error" */
676 "Error while writing %s file (disk full?).\nLibsndfile says \"%s\"")
677  .Format( formatStr, wxString::FromAscii(buffer2) ));
678 #else
679  // But better to give the same error message as for
680  // other cases of disk exhaustion.
681  // The thrown exception doesn't escape but GuardedCall
682  // will enqueue a message.
683  GuardedCall([&fName]{
684  throw FileException{
685  FileException::Cause::Write, fName }; });
686 #endif
687  updateResult = ProgressResult::Cancelled;
688  break;
689  }
690 
691  updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
692  }
693  }
694 
695  // Install the WAV metata in a "LIST" chunk at the end of the file
696  if (updateResult == ProgressResult::Success ||
697  updateResult == ProgressResult::Stopped) {
698  if (fileFormat == SF_FORMAT_WAV ||
699  fileFormat == SF_FORMAT_WAVEX) {
700  if (!AddStrings(project, sf.get(), metadata, sf_format)) {
701  // TODO: more precise message
702  ShowExportErrorDialog("PCM:675");
704  }
705  }
706  if (0 != sf.close()) {
707  // TODO: more precise message
708  ShowExportErrorDialog("PCM:681");
710  }
711  }
712  }
713 
714  if (updateResult == ProgressResult::Success ||
715  updateResult == ProgressResult::Stopped)
716  if ((fileFormat == SF_FORMAT_AIFF) ||
717  (fileFormat == SF_FORMAT_WAV))
718  // Note: file has closed, and gets reopened and closed again here:
719  if (!AddID3Chunk(fName, metadata, sf_format) ) {
720  // TODO: more precise message
721  ShowExportErrorDialog("PCM:694");
723  }
724 
725  return updateResult;
726 }

References AddID3Chunk(), AddStrings(), AudacityMessageBox(), RefreshCode::Cancelled, SFFile::close(), CopySamples(), ExportPlugin::CreateMixer(), floatSample, FMT_WAV, format, ProjectRate::Get(), Tags::Get(), TrackList::Get(), ProjectRate::GetRate(), gHighQualityDither, GuardedCall(), ExportPlugin::InitProgress(), int16Sample, int24Sample, LoadEncoding(), LoadOtherFormat(), none, ReportTooBigError(), SAMPLE_SIZE, sf_header_name(), sf_subtype_bytes_per_sample(), sf_subtype_is_integer(), sf_subtype_more_than_16_bits(), ShowExportErrorDialog(), BasicUI::Success, FileException::Write, and XO.

Here is the call graph for this function:

◆ GetExtension()

FileExtension ExportPCM::GetExtension ( int  index)
overridevirtual

Return the (first) file name extension for the sub-format.

Parameters
indexThe sub-format for which the extension is wanted

Reimplemented from ExportPlugin.

Definition at line 1064 of file ExportPCM.cpp.

1065 {
1066  if (index != FMT_OTHER)
1067  {
1068  return ExportPlugin::GetExtension(index);
1069  }
1070 
1071  // Get the saved type
1072  int typ = LoadOtherFormat() & SF_FORMAT_TYPEMASK;
1073 
1074  // Return the extension for that type
1075  return sf_header_extension(typ);
1076 }

References FMT_OTHER, ExportPlugin::GetExtension(), LoadOtherFormat(), and sf_header_extension().

Here is the call graph for this function:

◆ GetFormat()

wxString ExportPCM::GetFormat ( int  index)
overridevirtual

Reimplemented from ExportPlugin.

Definition at line 1050 of file ExportPCM.cpp.

1051 {
1052  if (index != FMT_OTHER)
1053  {
1054  return ExportPlugin::GetFormat(index);
1055  }
1056 
1057  // Get the saved type
1058  int typ = LoadOtherFormat() & SF_FORMAT_TYPEMASK;
1059 
1060  // Return the format name for that type
1061  return sf_header_shortname(typ);
1062 }

References FMT_OTHER, ExportPlugin::GetFormat(), LoadOtherFormat(), and sf_header_shortname().

Here is the call graph for this function:

◆ GetMaxChannels()

unsigned ExportPCM::GetMaxChannels ( int  index)
overridevirtual

Reimplemented from ExportPlugin.

Definition at line 1078 of file ExportPCM.cpp.

1079 {
1080  SF_INFO si = {};
1081 
1082  if (index < FMT_OTHER)
1083  {
1084  si.format = kFormats[index].format;
1085  }
1086  else
1087  {
1088  // Get the saved type
1089  si.format = LoadOtherFormat() & SF_FORMAT_TYPEMASK;
1090  si.format |= LoadEncoding(si.format);
1091  }
1092 
1093  for (si.channels = 1; sf_format_check(&si); si.channels++)
1094  {
1095  // just counting
1096  }
1097 
1098  // Return the max number of channels
1099  return si.channels - 1;
1100 }

References FMT_OTHER, kFormats, LoadEncoding(), and LoadOtherFormat().

Here is the call graph for this function:

◆ OptionsCreate()

void ExportPCM::OptionsCreate ( ShuttleGui S,
int  format 
)
overridevirtual

Implements ExportPlugin.

Definition at line 1032 of file ExportPCM.cpp.

1033 {
1034  switch (format)
1035  {
1036 #if defined(__WXMAC__)
1037  case FMT_AIFF:
1038 #endif
1039  case FMT_WAV:
1040  case FMT_OTHER:
1042  break;
1043 
1044  default:
1046  break;
1047  }
1048 }

References ShuttleGuiBase::AddWindow(), FMT_OTHER, FMT_WAV, format, ShuttleGuiBase::GetParent(), ExportPlugin::OptionsCreate(), and safenew.

Here is the call graph for this function:

◆ ReportTooBigError()

void ExportPCM::ReportTooBigError ( wxWindow *  pParent)
private

Definition at line 440 of file ExportPCM.cpp.

441 {
442  //Temporary translation hack, to say 'WAV or AIFF' rather than 'WAV'
443  auto message =
444  XO("You have attempted to Export a WAV or AIFF file which would be greater than 4GB.\n"
445  "Audacity cannot do this, the Export was abandoned.");
446 
448  XO("Error Exporting"), message,
449  wxT("Size_limits_for_WAV_and_AIFF_files"));
450 
451 // This alternative error dialog was to cover the possibility we could not
452 // compute the size in advance.
453 #if 0
455  XO("Error Exporting"),
456  XO("Your exported WAV file has been truncated as Audacity cannot export WAV\n"
457  "files bigger than 4GB."),
458  wxT("Size_limits_for_WAV_files"));
459 #endif
460 }

References BasicUI::ShowErrorDialog(), and XO.

Referenced by Export().

Here is the call graph for this function:
Here is the caller graph for this function:

The documentation for this class was generated from the following file:
sf_header_shortname
wxString sf_header_shortname(int format)
Get an abbreviated form of the string name of the specified format.
Definition: FileFormats.cpp:122
GuardedCall
R GuardedCall(const F1 &body, const F2 &handler=F2::Default(), std::function< void(AudacityException *)> delayedHandler=DefaultDelayedHandlerAction{})
Execute some code on any thread; catch any AudacityException; enqueue error report on the main thread...
Definition: AudacityException.h:202
BasicUI::ProgressResult::Success
@ Success
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
Definition: AudacityMessageBox.cpp:17
BasicUI::ShowErrorDialog
void ShowErrorDialog(const WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const ManualPageID &helpPage, const ErrorDialogOptions &options={})
Show an error dialog with a link to the manual for further help.
Definition: BasicUI.h:233
ExportPlugin::AddExtension
void AddExtension(const FileExtension &extension, int index)
Definition: Export.cpp:127
ShowExportErrorDialog
void ShowExportErrorDialog(wxString ErrorCode, TranslatableString message, const TranslatableString &caption)
Definition: Export.cpp:1519
ProjectRate::Get
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:42
FileException::Cause::Write
@ Write
most important to detect when storage space is exhausted
Tags::HasTag
bool HasTag(const wxString &name) const
Definition: Tags.cpp:452
Format
Abstract base class used in importing a file.
TAG_TRACK
#define TAG_TRACK
Definition: Tags.h:63
Tags::GetRange
Iterators GetRange() const
Definition: Tags.cpp:481
FileException
Thrown for failure of file or database operations in deeply nested places.
Definition: FileException.h:19
sf_header_name
wxString sf_header_name(int format)
Get the string name of the specified container format.
Definition: FileFormats.cpp:111
SAMPLE_SIZE
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:44
RefreshCode::Cancelled
@ Cancelled
Definition: RefreshCode.h:23
MallocString
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:274
XO
#define XO(s)
Definition: Internat.h:31
int24Sample
@ int24Sample
Definition: SampleFormat.h:33
ExportPlugin::ExportPlugin
ExportPlugin()
Definition: Export.cpp:81
TAG_SOFTWARE
#define TAG_SOFTWARE
Definition: Tags.h:67
LoadEncoding
static int LoadEncoding(int type)
Definition: ExportPCM.cpp:93
Tags::GetTag
wxString GetTag(const wxString &name) const
Definition: Tags.cpp:461
desc
const TranslatableString desc
Definition: ExportPCM.cpp:58
ExportPlugin::GetFormat
virtual wxString GetFormat(int index)
Definition: Export.cpp:152
ExportPlugin::GetExtension
virtual FileExtension GetExtension(int index=0)
Return the (first) file name extension for the sub-format.
Definition: Export.cpp:162
Tags::Get
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:237
wxWidgetsWindowPlacement
Window placement information for wxWidgetsBasicUI can be constructed from a wxWindow pointer.
Definition: wxWidgetsBasicUI.h:20
floatSample
@ floatSample
Definition: SampleFormat.h:34
ExportPlugin::SetExtensions
void SetExtensions(FileExtensions extensions, int index)
Definition: Export.cpp:132
ExportPlugin::InitProgress
static void InitProgress(std::unique_ptr< ProgressDialog > &pDialog, const TranslatableString &title, const TranslatableString &message)
Definition: Export.cpp:251
SFFile::close
int close()
Definition: FileFormats.h:150
CopySamples
void CopySamples(constSamplePtr src, sampleFormat srcFormat, samplePtr dst, sampleFormat dstFormat, size_t len, DitherType ditherType, unsigned int srcStride, unsigned int dstStride)
Copy samples from any format to any other format; apply dithering only if narrowing the format.
Definition: SampleFormat.cpp:111
ExportPlugin::SetFormat
void SetFormat(const wxString &format, int index)
Definition: Export.cpp:117
int16Sample
@ int16Sample
Definition: SampleFormat.h:32
none
@ none
Definition: Dither.h:20
ExportPCMOptions
Definition: ExportPCM.cpp:114
TAG_GENRE
#define TAG_GENRE
Definition: Tags.h:65
format
int format
Definition: ExportPCM.cpp:56
ShuttleGuiBase::GetParent
wxWindow * GetParent()
Definition: ShuttleGui.h:496
ShuttleGuiBase::AddWindow
wxWindow * AddWindow(wxWindow *pWindow, int PositionFlags=wxALIGN_CENTRE)
Definition: ShuttleGui.cpp:299
TAG_YEAR
#define TAG_YEAR
Definition: Tags.h:64
ExportPlugin::SetDescription
void SetDescription(const TranslatableString &description, int index)
Definition: Export.cpp:122
SFFile
Definition: FileFormats.h:143
TAG_COPYRIGHT
#define TAG_COPYRIGHT
Definition: Tags.h:68
ProjectRate::GetRate
double GetRate() const
Definition: ProjectRate.cpp:68
sampleFormat
sampleFormat
Definition: SampleFormat.h:29
samplePtr
char * samplePtr
Definition: SampleFormat.h:49
ExportPlugin::SetMaxChannels
void SetMaxChannels(unsigned maxchannels, unsigned index)
Definition: Export.cpp:142
LoadOtherFormat
static int LoadOtherFormat(int def=0)
Definition: ExportPCM.cpp:81
sf_subtype_bytes_per_sample
int sf_subtype_bytes_per_sample(unsigned int format)
Definition: FileFormats.cpp:207
name
const wxChar * name
Definition: ExportPCM.cpp:57
sf_subtype_more_than_16_bits
bool sf_subtype_more_than_16_bits(unsigned int format)
Definition: FileFormats.cpp:190
kFormats
struct @0 kFormats[]
FMT_WAV
@ FMT_WAV
Definition: ExportPCM.cpp:73
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:506
ExportPCM::AddStrings
bool AddStrings(AudacityProject *project, SNDFILE *sf, const Tags *tags, int sf_format)
Definition: ExportPCM.cpp:815
sampleCount
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
ExportPCM::AdjustString
ArrayOf< char > AdjustString(const wxString &wxStr, int sf_format)
Definition: ExportPCM.cpp:728
FMT_OTHER
@ FMT_OTHER
Definition: ExportPCM.cpp:74
ExportPlugin::CreateMixer
std::unique_ptr< Mixer > CreateMixer(const TrackList &tracks, bool selectionOnly, double startTime, double stopTime, unsigned numOutChannels, size_t outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, MixerSpec *mixerSpec)
Definition: Export.cpp:223
ExportPlugin::SetCanMetaData
void SetCanMetaData(bool canmetadata, int index)
Definition: Export.cpp:147
ExportPlugin::OptionsCreate
virtual void OptionsCreate(ShuttleGui &S, int format)=0
Definition: Export.cpp:209
TAG_COMMENTS
#define TAG_COMMENTS
Definition: Tags.h:66
TAG_ARTIST
#define TAG_ARTIST
Definition: Tags.h:61
ExportPlugin::AddFormat
int AddFormat()
Add a NEW entry to the list of formats this plug-in can export.
Definition: Export.cpp:101
gHighQualityDither
DitherType gHighQualityDither
Definition: SampleFormat.cpp:50
sf_get_all_extensions
FileExtensions sf_get_all_extensions()
Definition: FileFormats.cpp:241
sf_subtype_is_integer
bool sf_subtype_is_integer(unsigned int format)
Definition: FileFormats.cpp:199
sf_header_extension
wxString sf_header_extension(int format)
Get the most common file extension for the given format.
Definition: FileFormats.cpp:146
safenew
#define safenew
Definition: MemoryX.h:10
ExportPCM::ReportTooBigError
void ReportTooBigError(wxWindow *pParent)
Definition: ExportPCM.cpp:440
ArrayOf
Memory.h template class for making an array of float, bool, etc.
Definition: MemoryX.h:27
TAG_TITLE
#define TAG_TITLE
Definition: Tags.h:60
ExportPCM::AddID3Chunk
bool AddID3Chunk(const wxFileNameWrapper &fName, const Tags *tags, int sf_format)
Definition: ExportPCM.cpp:890
TAG_ALBUM
#define TAG_ALBUM
Definition: Tags.h:62