Audacity 3.2.0
Public Member Functions | Static Private Member Functions | Private Attributes | Static Private Attributes | List of all members
PCMExportProcessor Class Referencefinal
Inheritance diagram for PCMExportProcessor:
[legend]
Collaboration diagram for PCMExportProcessor:
[legend]

Public Member Functions

 PCMExportProcessor (int subformat)
 
 ~PCMExportProcessor () override
 
bool Initialize (AudacityProject &project, const Parameters &parameters, const wxFileNameWrapper &filename, double t0, double t1, bool selectedOnly, double sampleRate, unsigned channels, MixerOptions::Downmix *mixerSpec, const Tags *tags) override
 Called before start processing. More...
 
ExportResult Process (ExportProcessorDelegate &delegate) override
 
- Public Member Functions inherited from ExportProcessor
 ExportProcessor (const ExportProcessor &)=delete
 
ExportProcessoroperator= (const ExportProcessor &)=delete
 
 ExportProcessor ()=default
 
virtual ~ExportProcessor ()
 
virtual bool Initialize (AudacityProject &project, const Parameters &parameters, const wxFileNameWrapper &filename, double t0, double t1, bool selectedOnly, double rate, unsigned channels, MixerOptions::Downmix *mixerSpec=nullptr, const Tags *tags=nullptr)=0
 Called before start processing. More...
 
virtual ExportResult Process (ExportProcessorDelegate &delegate)=0
 

Static Private Member Functions

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

Private Attributes

struct {
   int   subformat
 
   double   t0
 
   double   t1
 
   std::unique_ptr< Mixer >   mixer
 
   TranslatableString   status
 
   SF_INFO   info
 
   sampleFormat   format
 
   wxFile   f
 
   SNDFILE *   sf
 
   int   sf_format
 
   wxFileNameWrapper   fName
 
   int   fileFormat
 
   std::unique_ptr< Tags >   metadata
 
context
 

Static Private Attributes

static constexpr size_t maxBlockLen = 44100 * 5
 

Additional Inherited Members

- Public Types inherited from ExportProcessor
using Parameters = std::vector< std::tuple< ExportOptionID, ExportValue > >
 

Detailed Description

Definition at line 378 of file ExportPCM.cpp.

Constructor & Destructor Documentation

◆ PCMExportProcessor()

PCMExportProcessor::PCMExportProcessor ( int  subformat)
inline

Definition at line 401 of file ExportPCM.cpp.

402 {
403 context.sf = nullptr;
404 context.subformat = subformat;
405 }
struct PCMExportProcessor::@183 context

References context, and subformat.

◆ ~PCMExportProcessor()

PCMExportProcessor::~PCMExportProcessor ( )
inlineoverride

Definition at line 407 of file ExportPCM.cpp.

408 {
409 if(context.f.IsOpened())
410 {
411 if(context.sf != nullptr)
412 sf_close(context.sf);
413 context.f.Close();
414 }
415 }

References context.

Member Function Documentation

◆ AddID3Chunk()

bool PCMExportProcessor::AddID3Chunk ( const wxFileNameWrapper fName,
const Tags tags,
int  sf_format 
)
staticprivate

Definition at line 953 of file ExportPCM.cpp.

955{
956#ifdef USE_LIBID3TAG
957 id3_tag_holder tp { id3_tag_new() };
958
959 for (const auto &pair : tags->GetRange()) {
960 const auto &n = pair.first;
961 const auto &v = pair.second;
962 const char *name = "TXXX";
963
964 if (n.CmpNoCase(TAG_TITLE) == 0) {
965 name = ID3_FRAME_TITLE;
966 }
967 else if (n.CmpNoCase(TAG_ARTIST) == 0) {
968 name = ID3_FRAME_ARTIST;
969 }
970 else if (n.CmpNoCase(TAG_ALBUM) == 0) {
971 name = ID3_FRAME_ALBUM;
972 }
973 else if (n.CmpNoCase(TAG_YEAR) == 0) {
974 name = ID3_FRAME_YEAR;
975 }
976 else if (n.CmpNoCase(TAG_GENRE) == 0) {
977 name = ID3_FRAME_GENRE;
978 }
979 else if (n.CmpNoCase(TAG_COMMENTS) == 0) {
980 name = ID3_FRAME_COMMENT;
981 }
982 else if (n.CmpNoCase(TAG_TRACK) == 0) {
983 name = ID3_FRAME_TRACK;
984 }
985 else if (n.CmpNoCase(wxT("composer")) == 0) {
986 name = "TCOM";
987 }
988
989 struct id3_frame *frame = id3_frame_new(name);
990
991 if (!n.IsAscii() || !v.IsAscii()) {
992 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_UTF_16);
993 }
994 else {
995 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_ISO_8859_1);
996 }
997
999 id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) v.mb_str(wxConvUTF8)) };
1000
1001 if (strcmp(name, ID3_FRAME_COMMENT) == 0) {
1002 // A hack to get around iTunes not recognizing the comment. The
1003 // language defaults to XXX and, since it's not a valid language,
1004 // iTunes just ignores the tag. So, either set it to a valid language
1005 // (which one???) or just clear it. Unfortunately, there's no supported
1006 // way of clearing the field, so do it directly.
1007 id3_field *f = id3_frame_field(frame, 1);
1008 memset(f->immediate.value, 0, sizeof(f->immediate.value));
1009 id3_field_setfullstring(id3_frame_field(frame, 3), ucs4.get());
1010 }
1011 else if (strcmp(name, "TXXX") == 0) {
1012 id3_field_setstring(id3_frame_field(frame, 2), ucs4.get());
1013
1014 ucs4.reset(id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) n.mb_str(wxConvUTF8)));
1015
1016 id3_field_setstring(id3_frame_field(frame, 1), ucs4.get());
1017 }
1018 else {
1019 auto addr = ucs4.get();
1020 id3_field_setstrings(id3_frame_field(frame, 1), 1, &addr);
1021 }
1022
1023 id3_tag_attachframe(tp.get(), frame);
1024 }
1025
1026 tp->options &= (~ID3_TAG_OPTION_COMPRESSION); // No compression
1027
1028 // If this version of libid3tag supports it, use v2.3 ID3
1029 // tags instead of the newer, but less well supported, v2.4
1030 // that libid3tag uses by default.
1031#ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3
1032 tp->options |= ID3_TAG_OPTION_ID3V2_3;
1033#endif
1034
1035 id3_length_t len;
1036
1037 len = id3_tag_render(tp.get(), 0);
1038 if (len == 0)
1039 return true;
1040
1041 if ((len % 2) != 0) len++; // Length must be even.
1042 ArrayOf<id3_byte_t> buffer { len, true };
1043 if (buffer == NULL)
1044 return false;
1045
1046 // Zero all locations, for ending odd UTF16 content
1047 // correctly, i.e., two '\0's at the end.
1048
1049 id3_tag_render(tp.get(), buffer.get());
1050
1051 wxFFile f(fName.GetFullPath(), wxT("r+b"));
1052 if (f.IsOpened()) {
1053 wxUint32 sz;
1054
1055 sz = (wxUint32) len;
1056 if (!f.SeekEnd(0))
1057 return false;
1058 if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)
1059 {
1060 if (4 != f.Write("id3 ", 4))// Must be lower case for foobar2000.
1061 return false ;
1062 }
1063 else {
1064 if (4 != f.Write("ID3 ", 4))
1065 return false;
1066 sz = wxUINT32_SWAP_ON_LE(sz);
1067 }
1068 if (4 != f.Write(&sz, 4))
1069 return false;
1070
1071 if (len != f.Write(buffer.get(), len))
1072 return false;
1073
1074 sz = (wxUint32) f.Tell() - 8;
1075 if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
1076 sz = wxUINT32_SWAP_ON_LE(sz);
1077
1078 if (!f.Seek(4))
1079 return false;
1080 if (4 != f.Write(&sz, 4))
1081 return false;
1082
1083 if (!f.Flush())
1084 return false;
1085
1086 if (!f.Close())
1087 return false;
1088 }
1089 else
1090 return false;
1091#endif
1092 return true;
1093}
wxT("CloseDown"))
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:148
#define TAG_TRACK
Definition: Tags.h:61
#define TAG_COMMENTS
Definition: Tags.h:64
#define TAG_GENRE
Definition: Tags.h:63
#define TAG_ALBUM
Definition: Tags.h:60
#define TAG_YEAR
Definition: Tags.h:62
#define TAG_TITLE
Definition: Tags.h:58
#define TAG_ARTIST
Definition: Tags.h:59
This simplifies arrays of arrays, each array separately allocated with NEW[] But it might be better t...
Definition: MemoryX.h:29
wxFileNameWrapper fName
Definition: ExportPCM.cpp:394
Iterators GetRange() const
Definition: Tags.cpp:426

References f, fName, Tags::GetRange(), anonymous_namespace{ExportPCM.cpp}::name, sf_format, TAG_ALBUM, TAG_ARTIST, TAG_COMMENTS, TAG_GENRE, TAG_TITLE, TAG_TRACK, TAG_YEAR, and wxT().

Referenced by Process().

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

◆ AddStrings()

void PCMExportProcessor::AddStrings ( SNDFILE *  sf,
const Tags tags,
int  sf_format 
)
staticprivate

Definition at line 880 of file ExportPCM.cpp.

881{
882 if (tags->HasTag(TAG_TITLE)) {
883 auto ascii7Str = AdjustString(tags->GetTag(TAG_TITLE), sf_format);
884 if (ascii7Str) {
885 sf_set_string(sf, SF_STR_TITLE, ascii7Str.get());
886 }
887 }
888
889 if (tags->HasTag(TAG_ALBUM)) {
890 auto ascii7Str = AdjustString(tags->GetTag(TAG_ALBUM), sf_format);
891 if (ascii7Str) {
892 sf_set_string(sf, SF_STR_ALBUM, ascii7Str.get());
893 }
894 }
895
896 if (tags->HasTag(TAG_ARTIST)) {
897 auto ascii7Str = AdjustString(tags->GetTag(TAG_ARTIST), sf_format);
898 if (ascii7Str) {
899 sf_set_string(sf, SF_STR_ARTIST, ascii7Str.get());
900 }
901 }
902
903 if (tags->HasTag(TAG_COMMENTS)) {
904 auto ascii7Str = AdjustString(tags->GetTag(TAG_COMMENTS), sf_format);
905 if (ascii7Str) {
906 sf_set_string(sf, SF_STR_COMMENT, ascii7Str.get());
907 }
908 }
909
910 if (tags->HasTag(TAG_YEAR)) {
911 auto ascii7Str = AdjustString(tags->GetTag(TAG_YEAR), sf_format);
912 if (ascii7Str) {
913 sf_set_string(sf, SF_STR_DATE, ascii7Str.get());
914 }
915 }
916
917 if (tags->HasTag(TAG_GENRE)) {
918 auto ascii7Str = AdjustString(tags->GetTag(TAG_GENRE), sf_format);
919 if (ascii7Str) {
920 sf_set_string(sf, SF_STR_GENRE, ascii7Str.get());
921 }
922 }
923
924 if (tags->HasTag(TAG_COPYRIGHT)) {
925 auto ascii7Str = AdjustString(tags->GetTag(TAG_COPYRIGHT), sf_format);
926 if (ascii7Str) {
927 sf_set_string(sf, SF_STR_COPYRIGHT, ascii7Str.get());
928 }
929 }
930
931 if (tags->HasTag(TAG_SOFTWARE)) {
932 auto ascii7Str = AdjustString(tags->GetTag(TAG_SOFTWARE), sf_format);
933 if (ascii7Str) {
934 sf_set_string(sf, SF_STR_SOFTWARE, ascii7Str.get());
935 }
936 }
937
938 if (tags->HasTag(TAG_TRACK)) {
939 auto ascii7Str = AdjustString(tags->GetTag(TAG_TRACK), sf_format);
940 if (ascii7Str) {
941 sf_set_string(sf, SF_STR_TRACKNUMBER, ascii7Str.get());
942 }
943 }
944}
#define TAG_SOFTWARE
Definition: Tags.h:65
#define TAG_COPYRIGHT
Definition: Tags.h:66
static ArrayOf< char > AdjustString(const wxString &wxStr, int sf_format)
Definition: ExportPCM.cpp:793
bool HasTag(const wxString &name) const
Definition: Tags.cpp:397
wxString GetTag(const wxString &name) const
Definition: Tags.cpp:406

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

Referenced by Initialize(), and Process().

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

◆ AdjustString()

ArrayOf< char > PCMExportProcessor::AdjustString ( const wxString &  wxStr,
int  sf_format 
)
staticprivate

Definition at line 793 of file ExportPCM.cpp.

794{
795 bool b_aiff = false;
796 if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
797 b_aiff = true; // Apple AIFF file
798
799 // We must convert the string to 7 bit ASCII
800 size_t sz = wxStr.length();
801 if(sz == 0)
802 return {};
803 // Size for secure allocation in case of local wide char usage
804 size_t sr = (sz+4) * 2;
805
806 ArrayOf<char> pDest{ sr, true };
807 if (!pDest)
808 return {};
809 ArrayOf<char> pSrc{ sr, true };
810 if (!pSrc)
811 return {};
812
813 if(wxStr.mb_str(wxConvISO8859_1))
814 strncpy(pSrc.get(), wxStr.mb_str(wxConvISO8859_1), sz);
815 else if(wxStr.mb_str())
816 strncpy(pSrc.get(), wxStr.mb_str(), sz);
817 else
818 return {};
819
820 char *pD = pDest.get();
821 char *pS = pSrc.get();
822 unsigned char c;
823
824 // ISO Latin to 7 bit ascii conversion table (best approximation)
825 static char aASCII7Table[256] = {
826 0x00, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
827 0x5f, 0x09, 0x0a, 0x5f, 0x0d, 0x5f, 0x5f, 0x5f,
828 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
829 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
830 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
831 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
832 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
833 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
834 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
835 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
836 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
837 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
838 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
839 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
840 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
841 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
842 0x45, 0x20, 0x2c, 0x53, 0x22, 0x2e, 0x2b, 0x2b,
843 0x5e, 0x25, 0x53, 0x28, 0x4f, 0x20, 0x5a, 0x20,
844 0x20, 0x27, 0x27, 0x22, 0x22, 0x2e, 0x2d, 0x5f,
845 0x22, 0x54, 0x73, 0x29, 0x6f, 0x20, 0x7a, 0x59,
846 0x20, 0x21, 0x63, 0x4c, 0x6f, 0x59, 0x7c, 0x53,
847 0x22, 0x43, 0x61, 0x22, 0x5f, 0x2d, 0x43, 0x2d,
848 0x6f, 0x7e, 0x32, 0x33, 0x27, 0x75, 0x50, 0x27,
849 0x2c, 0x31, 0x6f, 0x22, 0x5f, 0x5f, 0x5f, 0x3f,
850 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x43,
851 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
852 0x44, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x78,
853 0x4f, 0x55, 0x55, 0x55, 0x55, 0x59, 0x70, 0x53,
854 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x63,
855 0x65, 0x65, 0x65, 0x65, 0x69, 0x69, 0x69, 0x69,
856 0x64, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x2f,
857 0x6f, 0x75, 0x75, 0x75, 0x75, 0x79, 0x70, 0x79
858 };
859
860 size_t i;
861 for(i = 0; i < sr; i++) {
862 c = (unsigned char) *pS++;
863 *pD++ = aASCII7Table[c];
864 if(c == 0)
865 break;
866 }
867 *pD = '\0';
868
869 if(b_aiff) {
870 int len = (int)strlen(pDest.get());
871 if((len % 2) != 0) {
872 // In case of an odd length string, add a space char
873 strcat(pDest.get(), " ");
874 }
875 }
876
877 return pDest;
878}

References sf_format.

Referenced by AddStrings().

Here is the caller graph for this function:

◆ Initialize()

bool PCMExportProcessor::Initialize ( AudacityProject project,
const Parameters parameters,
const wxFileNameWrapper filename,
double  t0,
double  t1,
bool  selectedOnly,
double  rate,
unsigned  channels,
MixerOptions::Downmix mixerSpec,
const Tags tags 
)
overridevirtual

Called before start processing.

Parameters
projectProcessor may access project data, take care to exclude any data race
parametersA format-dependent set of parameters used in exporting
selectedOnlySet to true if all tracks should be mixed, to false if only the selected tracks should be mixed and exported.
tagsA Tags object that will over-ride the one in *project and be used to tag the file that is exported. @retern Implementations may simply return false without any error reporting. This is to temporarily preserve old behavior, which is to be removed in the nearest future.

Implements ExportProcessor.

Definition at line 536 of file ExportPCM.cpp.

543{
544 context.t0 = t0;
545 context.t1 = t1;
546 context.fName = fName;
547
548 const auto &tracks = TrackList::Get( project );
549
550 // Set a default in case the settings aren't found
551 int& sf_format = context.sf_format;
552
553 switch (context.subformat)
554 {
555#if defined(__WXMAC__)
556 case FMT_AIFF:
557 sf_format = SF_FORMAT_AIFF;
558 break;
559#endif
560
561 case FMT_WAV:
562 sf_format = SF_FORMAT_WAV;
563 break;
564
565 default:
566 // Retrieve the current format.
568 break;
569 }
570
572
573 // If subtype is still not specified, supply a default.
574 if (!(sf_format & SF_FORMAT_SUBMASK))
575 {
576 sf_format |= SF_FORMAT_PCM_16;
577 }
578
579 int& fileFormat = context.fileFormat;
580 fileFormat = sf_format & SF_FORMAT_TYPEMASK;
581
582 {
583 wxFile &f = context.f;
584 SNDFILE* &sf = context.sf;
585
586 wxString formatStr;
587 SF_INFO &info = context.info;
588 //int err;
589
590 //This whole operation should not occur while a file is being loaded on OD,
591 //(we are worried about reading from a file being written to,) so we block.
592 //Furthermore, we need to do this because libsndfile is not threadsafe.
593 formatStr = SFCall<wxString>(sf_header_name, fileFormat);
594
595 // Use libsndfile to export file
596
597 info.samplerate = (unsigned int)(sampleRate + 0.5);
598 info.frames = (unsigned int)((t1 - t0)*sampleRate + 0.5);
599 info.channels = numChannels;
600 info.format = sf_format;
601 info.sections = 1;
602 info.seekable = 0;
603
604 // Bug 46. Trap here, as sndfile.c does not trap it properly.
605 if( (numChannels != 1) && ((sf_format & SF_FORMAT_SUBMASK) == SF_FORMAT_GSM610) )
606 {
607 throw ExportException(_("GSM 6.10 requires mono"));
608 }
609
610 if (sf_format == SF_FORMAT_WAVEX + SF_FORMAT_GSM610) {
611 throw ExportException(_("WAVEX and GSM 6.10 formats are not compatible"));
612 }
613
614 // If we can't export exactly the format they requested,
615 // try the default format for that header type...
616 //
617 // LLL: I don't think this is valid since libsndfile checks
618 // for all allowed subtypes explicitly and doesn't provide
619 // for an unspecified subtype.
620 if (!sf_format_check(&info))
621 info.format = (info.format & SF_FORMAT_TYPEMASK);
622 if (!sf_format_check(&info)) {
623 throw ExportException(_("Cannot export audio in this format."));
624 }
625 const auto path = fName.GetFullPath();
626 if (f.Open(path, wxFile::write)) {
627 // Even though there is an sf_open() that takes a filename, use the one that
628 // takes a file descriptor since wxWidgets can open a file with a Unicode name and
629 // libsndfile can't (under Windows).
630 sf = sf_open_fd(f.fd(), SFM_WRITE, &info, FALSE);
631 //add clipping for integer formats. We allow floats to clip.
632 sf_command(sf, SFC_SET_CLIPPING, NULL, sf_subtype_is_integer(sf_format)?SF_TRUE:SF_FALSE) ;
633 }
634
635 if (!sf) {
636 throw ExportException(_("Cannot export audio to %s").Format( path ));
637 }
638 // Retrieve tags if not given a set
639 if (metadata == NULL)
641
642 // Install the meta data at the beginning of the file (except for
643 // WAV and WAVEX formats)
644 if (fileFormat != SF_FORMAT_WAV &&
645 fileFormat != SF_FORMAT_WAVEX) {
647 }
648 context.metadata = std::make_unique<Tags>(*metadata);
649
651 context.format = floatSample;
652 else
653 context.format = int16Sample;
654
655 // Bug 2200
656 // Only trap size limit for file types we know have an upper size limit.
657 // The error message mentions aiff and wav.
658 if( (fileFormat == SF_FORMAT_WAV) ||
659 (fileFormat == SF_FORMAT_WAVEX) ||
660 (fileFormat == SF_FORMAT_AIFF ))
661 {
662 float sampleCount = (float)(t1-t0)*sampleRate*info.channels;
663 float byteCount = sampleCount * sf_subtype_bytes_per_sample( info.format);
664 // Test for 4 Gibibytes, rather than 4 Gigabytes
665 if( byteCount > 4.295e9)
666 {
667 //Temporary translation hack, to say 'WAV or AIFF' rather than 'WAV'
668 const auto message =
669 XO("You have attempted to Export a WAV or AIFF file which would be greater than 4GB.\n"
670 "Audacity cannot do this, the Export was abandoned.");
671 throw ExportErrorException(message,
672 wxT("Size_limits_for_WAV_and_AIFF_files"));
673 }
674 }
675
676
677 context.status = (selectionOnly
678 ? XO("Exporting the selected audio as %s")
679 : XO("Exporting the audio as %s")).Format( formatStr );
680
681
682 wxASSERT(info.channels >= 0);
683 context.mixer = ExportPluginHelpers::CreateMixer(tracks, selectionOnly,
684 t0, t1,
685 info.channels, maxBlockLen, true,
686 sampleRate, context.format, mixerSpec);
687 }
688
689 return true;
690}
bool sf_subtype_is_integer(unsigned int format)
bool sf_subtype_more_than_16_bits(unsigned int format)
wxString sf_header_name(int format)
Get the string name of the specified container format.
int sf_subtype_bytes_per_sample(unsigned int format)
XO("Cut/Copy/Paste")
#define _(s)
Definition: Internat.h:73
const auto tracks
const auto project
static T GetParameterValue(const ExportProcessor::Parameters &parameters, int id, T defaultValue=T())
static 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, MixerOptions::Downmix *mixerSpec)
Abstract base class used in importing a file.
static void AddStrings(SNDFILE *sf, const Tags *tags, int sf_format)
Definition: ExportPCM.cpp:880
std::unique_ptr< Tags > metadata
Definition: ExportPCM.cpp:396
static constexpr size_t maxBlockLen
Definition: ExportPCM.cpp:380
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:214
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:314
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19

References _, AddStrings(), context, ExportPluginHelpers::CreateMixer(), f, fileFormat, floatSample, anonymous_namespace{ExportPCM.cpp}::FMT_AIFF, anonymous_namespace{ExportPCM.cpp}::FMT_WAV, fName, Tags::Get(), TrackList::Get(), ExportPluginHelpers::GetParameterValue(), info, int16Sample, maxBlockLen, metadata, anonymous_namespace{ExportPCM.cpp}::OptionIDSFType, project, anonymous_namespace{ClipSegmentTest.cpp}::sampleRate, sf, sf_format, sf_header_name(), sf_subtype_bytes_per_sample(), sf_subtype_is_integer(), sf_subtype_more_than_16_bits(), t0, t1, tracks, wxT(), and XO().

Here is the call graph for this function:

◆ Process()

ExportResult PCMExportProcessor::Process ( ExportProcessorDelegate delegate)
overridevirtual

Implements ExportProcessor.

Definition at line 692 of file ExportPCM.cpp.

693{
694 delegate.SetStatusString(context.status);
695
696 auto exportResult = ExportResult::Success;
697
698 {
699 std::vector<char> dither;
700 if ((context.info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
701 dither.reserve(maxBlockLen * context.info.channels * SAMPLE_SIZE(int24Sample));
702 }
703
704 while (exportResult == ExportResult::Success) {
705 sf_count_t samplesWritten;
706 size_t numSamples = context.mixer->Process();
707 if (numSamples == 0)
708 break;
709
710 auto mixed = context.mixer->GetBuffer();
711
712 // Bug 1572: Not ideal, but it does add the desired dither
713 if ((context.info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
714 for (int c = 0; c < context.info.channels; ++c) {
716 mixed + (c * SAMPLE_SIZE(context.format)), context.format,
717 dither.data() + (c * SAMPLE_SIZE(int24Sample)), int24Sample,
718 numSamples, gHighQualityDither, context.info.channels, context.info.channels
719 );
720 // Copy back without dither
722 dither.data() + (c * SAMPLE_SIZE(int24Sample)), int24Sample,
723 const_cast<samplePtr>(mixed) // PRL fix this!
724 + (c * SAMPLE_SIZE(context.format)), context.format,
725 numSamples, DitherType::none, context.info.channels, context.info.channels);
726 }
727 }
728
729 if (context.format == int16Sample)
730 samplesWritten = SFCall<sf_count_t>(sf_writef_short, context.sf, (const short *)mixed, numSamples);
731 else
732 samplesWritten = SFCall<sf_count_t>(sf_writef_float, context.sf, (const float *)mixed, numSamples);
733
734 if (static_cast<size_t>(samplesWritten) != numSamples) {
735 char buffer2[1000];
736 sf_error_str(context.sf, buffer2, 1000);
737 //Used to give this error message
738#if 0
740 XO(
741 /* i18n-hint: %s will be the error message from libsndfile, which
742 * is usually something unhelpful (and untranslated) like "system
743 * error" */
744"Error while writing %s file (disk full?).\nLibsndfile says \"%s\"")
745 .Format( formatStr, wxString::FromAscii(buffer2) ));
746#else
747 // But better to give the same error message as for
748 // other cases of disk exhaustion.
749 // The thrown exception doesn't escape but GuardedCall
750 // will enqueue a message.
751 GuardedCall([&]{
752 throw FileException{
754#endif
755 exportResult = ExportResult::Error;
756 break;
757 }
758 if(exportResult == ExportResult::Success)
760 delegate, *context.mixer, context.t0, context.t1);
761 }
762 }
763
764 // Install the WAV metata in a "LIST" chunk at the end of the file
765 if (exportResult != ExportResult::Cancelled && exportResult != ExportResult::Error) {
766 if (context.fileFormat == SF_FORMAT_WAV ||
767 context.fileFormat == SF_FORMAT_WAVEX) {
768 AddStrings(context.sf, context.metadata.get(), context.sf_format);
769 }
770 }
771
772 if (0 != sf_close(context.sf)) {
773 // TODO: more precise message
774 throw ExportErrorException("PCM:681");
775 }
776
777 context.sf = nullptr;
778 context.f.Close();
779
780 if (exportResult != ExportResult::Cancelled && exportResult != ExportResult::Error)
781 {
782 if ((context.fileFormat == SF_FORMAT_AIFF) ||
783 (context.fileFormat == SF_FORMAT_WAV))
784 // Note: file has closed, and gets reopened and closed again here:
785 if (!AddID3Chunk(context.fName, context.metadata.get(), context.sf_format) ) {
786 // TODO: more precise message
787 throw ExportErrorException("PCM:694");
788 }
789 }
790 return exportResult;
791}
R GuardedCall(const F1 &body, const F2 &handler=F2::Default(), F3 delayedHandler=DefaultDelayedHandlerAction) noexcept(noexcept(handler(std::declval< AudacityException * >())) &&noexcept(handler(nullptr)) &&noexcept(std::function< void(AudacityException *)>{std::move(delayedHandler)}))
Execute some code on any thread; catch any AudacityException; enqueue error report on the main thread...
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
@ none
Definition: Dither.h:20
DitherType gHighQualityDither
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.
char * samplePtr
Definition: SampleFormat.h:57
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:52
static ExportResult UpdateProgress(ExportProcessorDelegate &delegate, Mixer &mixer, double t0, double t1)
Sends progress update to delegate and retrieves state update from it. Typically used inside each expo...
virtual void SetStatusString(const TranslatableString &str)=0
Thrown for failure of file or database operations in deeply nested places.
Definition: FileException.h:19
@ Write
most important to detect when storage space is exhausted
static bool AddID3Chunk(const wxFileNameWrapper &fName, const Tags *tags, int sf_format)
Definition: ExportPCM.cpp:953

References AddID3Chunk(), AddStrings(), AudacityMessageBox(), Cancelled, context, CopySamples(), Error, gHighQualityDither, GuardedCall(), int16Sample, int24Sample, maxBlockLen, none, SAMPLE_SIZE, ExportProcessorDelegate::SetStatusString(), Success, ExportPluginHelpers::UpdateProgress(), FileException::Write, and XO().

Here is the call graph for this function:

Member Data Documentation

◆ 

struct { ... } PCMExportProcessor::context

◆ f

wxFile PCMExportProcessor::f

Definition at line 391 of file ExportPCM.cpp.

Referenced by AddID3Chunk(), and Initialize().

◆ fileFormat

int PCMExportProcessor::fileFormat

Definition at line 395 of file ExportPCM.cpp.

Referenced by Initialize().

◆ fName

wxFileNameWrapper PCMExportProcessor::fName

Definition at line 394 of file ExportPCM.cpp.

Referenced by AddID3Chunk(), and Initialize().

◆ format

sampleFormat PCMExportProcessor::format

Definition at line 390 of file ExportPCM.cpp.

◆ info

SF_INFO PCMExportProcessor::info

Definition at line 389 of file ExportPCM.cpp.

Referenced by Initialize().

◆ maxBlockLen

constexpr size_t PCMExportProcessor::maxBlockLen = 44100 * 5
staticconstexprprivate

Definition at line 380 of file ExportPCM.cpp.

Referenced by Initialize(), and Process().

◆ metadata

std::unique_ptr<Tags> PCMExportProcessor::metadata

Definition at line 396 of file ExportPCM.cpp.

Referenced by Initialize().

◆ mixer

std::unique_ptr<Mixer> PCMExportProcessor::mixer

Definition at line 387 of file ExportPCM.cpp.

◆ sf

SNDFILE* PCMExportProcessor::sf

Definition at line 392 of file ExportPCM.cpp.

Referenced by AddStrings(), and Initialize().

◆ sf_format

int PCMExportProcessor::sf_format

Definition at line 393 of file ExportPCM.cpp.

Referenced by AddID3Chunk(), AddStrings(), AdjustString(), and Initialize().

◆ status

TranslatableString PCMExportProcessor::status

Definition at line 388 of file ExportPCM.cpp.

◆ subformat

int PCMExportProcessor::subformat

Definition at line 384 of file ExportPCM.cpp.

Referenced by PCMExportProcessor().

◆ t0

double PCMExportProcessor::t0

Definition at line 385 of file ExportPCM.cpp.

Referenced by Initialize().

◆ t1

double PCMExportProcessor::t1

Definition at line 386 of file ExportPCM.cpp.

Referenced by Initialize().


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