18#include <wx/filename.h>
19#include <wx/textctrl.h>
25#include "../FileFormats.h"
45 struct id3_frame *id3_frame_new(
char const *);
46 id3_length_t id3_latin1_length(id3_latin1_t
const *);
47 void id3_latin1_decode(id3_latin1_t
const *, id3_ucs4_t *);
60 {SF_FORMAT_AIFF | SF_FORMAT_PCM_16,
wxT(
"AIFF"),
XO(
"AIFF (Apple/SGI)")},
62 {SF_FORMAT_WAV | SF_FORMAT_PCM_16,
wxT(
"WAV"),
XO(
"WAV (Microsoft)")},
80 return gPrefs->Read(
wxT(
"/FileFormats/ExportFormat_SF1"),
86 gPrefs->Write(
wxT(
"/FileFormats/ExportFormat_SF1"), val);
92 return gPrefs->Read(wxString::Format(
wxT(
"/FileFormats/ExportFormat_SF1_Type/%s_%x"),
98 gPrefs->Write(wxString::Format(
wxT(
"/FileFormats/ExportFormat_SF1_Type/%s_%x"),
107#define ID_HEADER_CHOICE 7102
108#define ID_ENCODING_CHOICE 7103
119 void OnShow(wxShowEvent & evt);
144 DECLARE_EVENT_TABLE()
156 mSelFormat = selformat;
159 mHeaderFromChoice = 0;
160 mEncodingFromChoice = 0;
164 mType =
kFormats[selformat].format & SF_FORMAT_TYPEMASK;
165 GetEncodings(mType & SF_FORMAT_SUBMASK);
174 PopulateOrExchange(
S);
188 S.StartVerticalLay();
190 S.StartHorizontalLay(wxCENTER);
192 S.StartMultiColumn(2, wxCENTER);
198 .AddChoice(
XXO(
"Header:"),
203 .AddChoice(
XXO(
"Encoding:"),
209 S.EndHorizontalLay();
289#if defined(__WXMAC__)
320 info.samplerate = 44100;
329 if (!(enc & SF_FORMAT_SUBMASK))
333 enc &= SF_FORMAT_SUBMASK;
336 if (
mType == SF_FORMAT_AIFF && enc == 0)
338 enc = SF_FORMAT_PCM_16;
349 info.format =
mType | sub;
350 if (sf_format_check(&info))
369 wxCommandEvent evt(AUDACITY_FILE_SUFFIX_EVENT, GetId());
370 evt.SetEventObject(
this);
372 ProcessWindowEvent(evt);
389 std::unique_ptr<BasicUI::ProgressDialog> &pDialog,
396 const Tags *metadata = NULL,
397 int subformat = 0)
override;
418 for (
size_t i = 0; i < WXSIZEOF(
kFormats); ++i)
441 XO(
"You have attempted to Export a WAV or AIFF file which would be greater than 4GB.\n"
442 "Audacity cannot do this, the Export was abandoned.");
445 XO(
"Error Exporting"), message,
446 wxT(
"Size_limits_for_WAV_and_AIFF_files"));
452 XO(
"Error Exporting"),
453 XO(
"Your exported WAV file has been truncated as Audacity cannot export WAV\n"
454 "files bigger than 4GB."),
455 wxT(
"Size_limits_for_WAV_files"));
465 std::unique_ptr<BasicUI::ProgressDialog> &pDialog,
466 unsigned numChannels,
472 const Tags *metadata,
483#if defined(__WXMAC__)
485 sf_format = SF_FORMAT_AIFF;
490 sf_format = SF_FORMAT_WAV;
501 if (!(sf_format & SF_FORMAT_SUBMASK))
507 if (!(sf_format & SF_FORMAT_SUBMASK))
509 sf_format |= SF_FORMAT_PCM_16;
512 int fileFormat = sf_format & SF_FORMAT_TYPEMASK;
530 info.samplerate = (
unsigned int)(rate + 0.5);
531 info.frames = (
unsigned int)((t1 - t0)*rate + 0.5);
532 info.channels = numChannels;
533 info.format = sf_format;
538 if( (numChannels != 1) && ((sf_format & SF_FORMAT_SUBMASK) == SF_FORMAT_GSM610) )
544 if (sf_format == SF_FORMAT_WAVEX + SF_FORMAT_GSM610) {
546 XO(
"WAVEX and GSM 6.10 formats are not compatible") );
556 if (!sf_format_check(&info))
557 info.format = (info.format & SF_FORMAT_TYPEMASK);
558 if (!sf_format_check(&info)) {
562 const auto path = fName.GetFullPath();
563 if (f.Open(path, wxFile::write)) {
567 sf.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_WRITE, &info, FALSE));
577 if (metadata == NULL)
582 if (fileFormat != SF_FORMAT_WAV &&
583 fileFormat != SF_FORMAT_WAVEX) {
584 if (!
AddStrings(project, sf.get(), metadata, sf_format)) {
598 if( (fileFormat == SF_FORMAT_WAV) ||
599 (fileFormat == SF_FORMAT_WAVEX) ||
600 (fileFormat == SF_FORMAT_AIFF ))
602 float sampleCount = (float)(t1-t0)*rate*info.channels;
605 if( byteCount > 4.295e9)
608 return ProgressResult::Failed;
611 size_t maxBlockLen = 44100 * 5;
614 std::vector<char> dither;
615 if ((info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
619 wxASSERT(info.channels >= 0);
622 info.channels, maxBlockLen,
true,
627 ?
XO(
"Exporting the selected audio as %s")
628 :
XO(
"Exporting the audio as %s"))
630 auto &progress = *pDialog;
633 sf_count_t samplesWritten;
634 size_t numSamples = mixer->Process();
638 auto mixed = mixer->GetBuffer();
641 if ((info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
642 for (
int c = 0; c < info.channels; ++c) {
658 samplesWritten = SFCall<sf_count_t>(sf_writef_short, sf.get(), (
const short *)mixed, numSamples);
660 samplesWritten = SFCall<sf_count_t>(sf_writef_float, sf.get(), (
const float *)mixed, numSamples);
662 if (
static_cast<size_t>(samplesWritten) != numSamples) {
664 sf_error_str(sf.get(), buffer2, 1000);
672"Error while writing %s file (disk full?).\nLibsndfile says \"%s\"")
673 .
Format( formatStr, wxString::FromAscii(buffer2) ));
687 updateResult = progress.Poll(mixer->MixGetCurrentTime() - t0, t1 - t0);
693 updateResult == ProgressResult::Stopped) {
694 if (fileFormat == SF_FORMAT_WAV ||
695 fileFormat == SF_FORMAT_WAVEX) {
696 if (!
AddStrings(project, sf.get(), metadata, sf_format)) {
702 if (0 != sf.
close()) {
711 updateResult == ProgressResult::Stopped)
712 if ((fileFormat == SF_FORMAT_AIFF) ||
713 (fileFormat == SF_FORMAT_WAV))
727 if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
731 size_t sz = wxStr.length();
735 size_t sr = (sz+4) * 2;
744 if(wxStr.mb_str(wxConvISO8859_1))
745 strncpy(pSrc.get(), wxStr.mb_str(wxConvISO8859_1), sz);
746 else if(wxStr.mb_str())
747 strncpy(pSrc.get(), wxStr.mb_str(), sz);
751 char *pD = pDest.get();
752 char *pS = pSrc.get();
756 static char aASCII7Table[256] = {
757 0x00, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
758 0x5f, 0x09, 0x0a, 0x5f, 0x0d, 0x5f, 0x5f, 0x5f,
759 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
760 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
761 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
762 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
763 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
764 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
765 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
766 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
767 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
768 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
769 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
770 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
771 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
772 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
773 0x45, 0x20, 0x2c, 0x53, 0x22, 0x2e, 0x2b, 0x2b,
774 0x5e, 0x25, 0x53, 0x28, 0x4f, 0x20, 0x5a, 0x20,
775 0x20, 0x27, 0x27, 0x22, 0x22, 0x2e, 0x2d, 0x5f,
776 0x22, 0x54, 0x73, 0x29, 0x6f, 0x20, 0x7a, 0x59,
777 0x20, 0x21, 0x63, 0x4c, 0x6f, 0x59, 0x7c, 0x53,
778 0x22, 0x43, 0x61, 0x22, 0x5f, 0x2d, 0x43, 0x2d,
779 0x6f, 0x7e, 0x32, 0x33, 0x27, 0x75, 0x50, 0x27,
780 0x2c, 0x31, 0x6f, 0x22, 0x5f, 0x5f, 0x5f, 0x3f,
781 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x43,
782 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
783 0x44, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x78,
784 0x4f, 0x55, 0x55, 0x55, 0x55, 0x59, 0x70, 0x53,
785 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x63,
786 0x65, 0x65, 0x65, 0x65, 0x69, 0x69, 0x69, 0x69,
787 0x64, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x2f,
788 0x6f, 0x75, 0x75, 0x75, 0x75, 0x79, 0x70, 0x79
792 for(i = 0; i < sr; i++) {
793 c = (
unsigned char) *pS++;
794 *pD++ = aASCII7Table[c];
801 int len = (int)strlen(pDest.get());
804 strcat(pDest.get(),
" ");
816 sf_set_string(sf, SF_STR_TITLE, ascii7Str.get());
823 sf_set_string(sf, SF_STR_ALBUM, ascii7Str.get());
830 sf_set_string(sf, SF_STR_ARTIST, ascii7Str.get());
837 sf_set_string(sf, SF_STR_COMMENT, ascii7Str.get());
844 sf_set_string(sf, SF_STR_DATE, ascii7Str.get());
851 sf_set_string(sf, SF_STR_GENRE, ascii7Str.get());
858 sf_set_string(sf, SF_STR_COPYRIGHT, ascii7Str.get());
865 sf_set_string(sf, SF_STR_SOFTWARE, ascii7Str.get());
872 sf_set_string(sf, SF_STR_TRACKNUMBER, ascii7Str.get());
880struct id3_tag_deleter {
881 void operator () (id3_tag *p)
const {
if (p) id3_tag_delete(p); }
883using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>;
890 id3_tag_holder tp { id3_tag_new() };
892 for (
const auto &pair : tags->
GetRange()) {
893 const auto &n = pair.first;
894 const auto &v = pair.second;
895 const char *
name =
"TXXX";
898 name = ID3_FRAME_TITLE;
901 name = ID3_FRAME_ARTIST;
904 name = ID3_FRAME_ALBUM;
906 else if (n.CmpNoCase(
TAG_YEAR) == 0) {
907 name = ID3_FRAME_YEAR;
910 name = ID3_FRAME_GENRE;
913 name = ID3_FRAME_COMMENT;
916 name = ID3_FRAME_TRACK;
918 else if (n.CmpNoCase(
wxT(
"composer")) == 0) {
922 struct id3_frame *frame = id3_frame_new(
name);
924 if (!n.IsAscii() || !v.IsAscii()) {
925 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_UTF_16);
928 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_ISO_8859_1);
932 id3_utf8_ucs4duplicate((id3_utf8_t *) (
const char *) v.mb_str(wxConvUTF8)) };
934 if (strcmp(
name, ID3_FRAME_COMMENT) == 0) {
940 id3_field *f = id3_frame_field(frame, 1);
941 memset(f->immediate.value, 0,
sizeof(f->immediate.value));
942 id3_field_setfullstring(id3_frame_field(frame, 3), ucs4.get());
944 else if (strcmp(
name,
"TXXX") == 0) {
945 id3_field_setstring(id3_frame_field(frame, 2), ucs4.get());
947 ucs4.reset(id3_utf8_ucs4duplicate((id3_utf8_t *) (
const char *) n.mb_str(wxConvUTF8)));
949 id3_field_setstring(id3_frame_field(frame, 1), ucs4.get());
952 auto addr = ucs4.get();
953 id3_field_setstrings(id3_frame_field(frame, 1), 1, &addr);
956 id3_tag_attachframe(tp.get(), frame);
959 tp->options &= (~ID3_TAG_OPTION_COMPRESSION);
964#ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3
965 tp->options |= ID3_TAG_OPTION_ID3V2_3;
970 len = id3_tag_render(tp.get(), 0);
974 if ((len % 2) != 0) len++;
982 id3_tag_render(tp.get(), buffer.get());
984 wxFFile f(fName.GetFullPath(),
wxT(
"r+b"));
991 if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)
993 if (4 != f.Write(
"id3 ", 4))
997 if (4 != f.Write(
"ID3 ", 4))
999 sz = wxUINT32_SWAP_ON_LE(sz);
1001 if (4 != f.Write(&sz, 4))
1004 if (len != f.Write(buffer.get(), len))
1007 sz = (wxUint32) f.Tell() - 8;
1008 if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
1009 sz = wxUINT32_SWAP_ON_LE(sz);
1013 if (4 != f.Write(&sz, 4))
1032#if defined(__WXMAC__)
1080 si.format =
kFormats[index].format;
1089 for (si.channels = 1; sf_format_check(&si); si.channels++)
1095 return si.channels - 1;
1099 []{
return std::make_unique< ExportPCM >(); }
1102#ifdef HAS_CLOUD_UPLOAD
1127 [](
const AudacityProject&) {
return std::make_unique<PCMCloudHelper>(); });
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)
void ShowExportErrorDialog(wxString ErrorCode, TranslatableString message, const TranslatableString &caption, bool allowReporting)
static int LoadEncoding(int type)
const TranslatableString desc
static void SaveEncoding(int type, int val)
static void SaveOtherFormat(int val)
static Exporter::RegisteredExportPlugin sRegisteredPlugin
#define ID_ENCODING_CHOICE
static int LoadOtherFormat(int def=0)
XXO("&Cut/Copy/Paste Toolbar")
wxString FileExtension
File extension, not including any leading dot.
std::unique_ptr< Character[], freer > MallocString
an object holding per-project preferred sample rate
declares abstract base class Track, TrackList, and iterators over TrackList
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< TranslatableString > TranslatableStrings
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
ProgressResult Export(AudacityProject *project, std::unique_ptr< BasicUI::ProgressDialog > &pDialog, unsigned channels, const wxFileNameWrapper &fName, bool selectedOnly, double t0, double t1, MixerSpec *mixerSpec=NULL, const Tags *metadata=NULL, int subformat=0) override
bool AddID3Chunk(const wxFileNameWrapper &fName, const Tags *tags, int sf_format)
unsigned GetMaxChannels(int index) override
wxString GetFormat(int index) override
void OptionsCreate(ShuttleGui &S, int format) override
bool AddStrings(AudacityProject *project, SNDFILE *sf, const Tags *tags, int sf_format)
void ReportTooBigError(wxWindow *pParent)
ArrayOf< char > AdjustString(const wxString &wxStr, int sf_format)
FileExtension GetExtension(int index) override
Return the (first) file name extension for the sub-format.
void OnShow(wxShowEvent &evt)
ExportPCMOptions(wxWindow *parent, int format)
virtual ~ExportPCMOptions()
TranslatableStrings mEncodingNames
std::vector< int > mHeaderIndexes
std::vector< int > mEncodingIndexes
void OnEncodingChoice(wxCommandEvent &evt)
void PopulateOrExchange(ShuttleGui &S)
TranslatableStrings mHeaderNames
void GetEncodings(int enc=0)
void OnHeaderChoice(wxCommandEvent &evt)
wxChoice * mEncodingChoice
virtual void OptionsCreate(ShuttleGui &S, int format)=0
virtual FileExtension GetExtension(int index=0)
Return the (first) file name extension for the sub-format.
void AddExtension(const FileExtension &extension, int index)
int AddFormat()
Add a NEW entry to the list of formats this plug-in can export.
virtual wxString GetFormat(int index)
static void InitProgress(std::unique_ptr< BasicUI::ProgressDialog > &pDialog, const TranslatableString &title, const TranslatableString &message)
void SetFormat(const wxString &format, int index)
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)
void SetDescription(const TranslatableString &description, int index)
void SetCanMetaData(bool canmetadata, int index)
void SetMaxChannels(unsigned maxchannels, unsigned index)
void SetExtensions(FileExtensions extensions, int index)
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Thrown for failure of file or database operations in deeply nested places.
@ Write
most important to detect when storage space is exhausted
A matrix of booleans, one row per input channel, column per output.
static ProjectRate & Get(AudacityProject &project)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
static TrackList & Get(AudacityProject &project)
Holds a msgid for the translation catalog; may also bind format arguments.
Helper interface, that allows to setup the desired export format on the ExportPlugin.
virtual wxString GetExporterID() const =0
Identifier of the ExportPlugin to be used.
virtual FileExtension GetFileExtension() const =0
File extension that is expected with this plugin.
virtual void OnBeforeExport()=0
Setup the preferred format for the export.
Positions or offsets within audio files need a wide type.
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.
bool RegisterCloudExporter(MimeType mimeType, CloudExporterPluginFactory factory)
Registers a factory for a specific mime type.