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::@184 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 950 of file ExportPCM.cpp.

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

878{
879 if (tags->HasTag(TAG_TITLE)) {
880 auto ascii7Str = AdjustString(tags->GetTag(TAG_TITLE), sf_format);
881 if (ascii7Str) {
882 sf_set_string(sf, SF_STR_TITLE, ascii7Str.get());
883 }
884 }
885
886 if (tags->HasTag(TAG_ALBUM)) {
887 auto ascii7Str = AdjustString(tags->GetTag(TAG_ALBUM), sf_format);
888 if (ascii7Str) {
889 sf_set_string(sf, SF_STR_ALBUM, ascii7Str.get());
890 }
891 }
892
893 if (tags->HasTag(TAG_ARTIST)) {
894 auto ascii7Str = AdjustString(tags->GetTag(TAG_ARTIST), sf_format);
895 if (ascii7Str) {
896 sf_set_string(sf, SF_STR_ARTIST, ascii7Str.get());
897 }
898 }
899
900 if (tags->HasTag(TAG_COMMENTS)) {
901 auto ascii7Str = AdjustString(tags->GetTag(TAG_COMMENTS), sf_format);
902 if (ascii7Str) {
903 sf_set_string(sf, SF_STR_COMMENT, ascii7Str.get());
904 }
905 }
906
907 if (tags->HasTag(TAG_YEAR)) {
908 auto ascii7Str = AdjustString(tags->GetTag(TAG_YEAR), sf_format);
909 if (ascii7Str) {
910 sf_set_string(sf, SF_STR_DATE, ascii7Str.get());
911 }
912 }
913
914 if (tags->HasTag(TAG_GENRE)) {
915 auto ascii7Str = AdjustString(tags->GetTag(TAG_GENRE), sf_format);
916 if (ascii7Str) {
917 sf_set_string(sf, SF_STR_GENRE, ascii7Str.get());
918 }
919 }
920
921 if (tags->HasTag(TAG_COPYRIGHT)) {
922 auto ascii7Str = AdjustString(tags->GetTag(TAG_COPYRIGHT), sf_format);
923 if (ascii7Str) {
924 sf_set_string(sf, SF_STR_COPYRIGHT, ascii7Str.get());
925 }
926 }
927
928 if (tags->HasTag(TAG_SOFTWARE)) {
929 auto ascii7Str = AdjustString(tags->GetTag(TAG_SOFTWARE), sf_format);
930 if (ascii7Str) {
931 sf_set_string(sf, SF_STR_SOFTWARE, ascii7Str.get());
932 }
933 }
934
935 if (tags->HasTag(TAG_TRACK)) {
936 auto ascii7Str = AdjustString(tags->GetTag(TAG_TRACK), sf_format);
937 if (ascii7Str) {
938 sf_set_string(sf, SF_STR_TRACKNUMBER, ascii7Str.get());
939 }
940 }
941}
#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:790
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 790 of file ExportPCM.cpp.

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

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

Here is the call graph for this function:

◆ Process()

ExportResult PCMExportProcessor::Process ( ExportProcessorDelegate delegate)
overridevirtual

Implements ExportProcessor.

Definition at line 689 of file ExportPCM.cpp.

690{
691 delegate.SetStatusString(context.status);
692
693 auto exportResult = ExportResult::Success;
694
695 {
696 std::vector<char> dither;
697 if ((context.info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
698 dither.reserve(maxBlockLen * context.info.channels * SAMPLE_SIZE(int24Sample));
699 }
700
701 while (exportResult == ExportResult::Success) {
702 sf_count_t samplesWritten;
703 size_t numSamples = context.mixer->Process();
704 if (numSamples == 0)
705 break;
706
707 auto mixed = context.mixer->GetBuffer();
708
709 // Bug 1572: Not ideal, but it does add the desired dither
710 if ((context.info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
711 for (int c = 0; c < context.info.channels; ++c) {
713 mixed + (c * SAMPLE_SIZE(context.format)), context.format,
714 dither.data() + (c * SAMPLE_SIZE(int24Sample)), int24Sample,
715 numSamples, gHighQualityDither, context.info.channels, context.info.channels
716 );
717 // Copy back without dither
719 dither.data() + (c * SAMPLE_SIZE(int24Sample)), int24Sample,
720 const_cast<samplePtr>(mixed) // PRL fix this!
721 + (c * SAMPLE_SIZE(context.format)), context.format,
722 numSamples, DitherType::none, context.info.channels, context.info.channels);
723 }
724 }
725
726 if (context.format == int16Sample)
727 samplesWritten = SFCall<sf_count_t>(sf_writef_short, context.sf, (const short *)mixed, numSamples);
728 else
729 samplesWritten = SFCall<sf_count_t>(sf_writef_float, context.sf, (const float *)mixed, numSamples);
730
731 if (static_cast<size_t>(samplesWritten) != numSamples) {
732 char buffer2[1000];
733 sf_error_str(context.sf, buffer2, 1000);
734 //Used to give this error message
735#if 0
737 XO(
738 /* i18n-hint: %s will be the error message from libsndfile, which
739 * is usually something unhelpful (and untranslated) like "system
740 * error" */
741"Error while writing %s file (disk full?).\nLibsndfile says \"%s\"")
742 .Format( formatStr, wxString::FromAscii(buffer2) ));
743#else
744 // But better to give the same error message as for
745 // other cases of disk exhaustion.
746 // The thrown exception doesn't escape but GuardedCall
747 // will enqueue a message.
748 GuardedCall([&]{
749 throw FileException{
751#endif
752 exportResult = ExportResult::Error;
753 break;
754 }
755 if(exportResult == ExportResult::Success)
757 delegate, *context.mixer, context.t0, context.t1);
758 }
759 }
760
761 // Install the WAV metata in a "LIST" chunk at the end of the file
762 if (exportResult != ExportResult::Cancelled && exportResult != ExportResult::Error) {
763 if (context.fileFormat == SF_FORMAT_WAV ||
764 context.fileFormat == SF_FORMAT_WAVEX) {
765 AddStrings(context.sf, context.metadata.get(), context.sf_format);
766 }
767 }
768
769 if (0 != sf_close(context.sf)) {
770 // TODO: more precise message
771 throw ExportErrorException("PCM:681");
772 }
773
774 context.sf = nullptr;
775 context.f.Close();
776
777 if (exportResult != ExportResult::Cancelled && exportResult != ExportResult::Error)
778 {
779 if ((context.fileFormat == SF_FORMAT_AIFF) ||
780 (context.fileFormat == SF_FORMAT_WAV))
781 // Note: file has closed, and gets reopened and closed again here:
782 if (!AddID3Chunk(context.fName, context.metadata.get(), context.sf_format) ) {
783 // TODO: more precise message
784 throw ExportErrorException("PCM:694");
785 }
786 }
787 return exportResult;
788}
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:950

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: