15#include <wx/filename.h>
40 struct id3_frame *id3_frame_new(
char const *);
41 id3_length_t id3_latin1_length(id3_latin1_t
const *);
42 void id3_latin1_decode(id3_latin1_t
const *, id3_ucs4_t *);
57 {SF_FORMAT_AIFF | SF_FORMAT_PCM_16,
wxT(
"AIFF"),
XO(
"AIFF (Apple/SGI)")},
59 {SF_FORMAT_WAV | SF_FORMAT_PCM_16,
wxT(
"WAV"),
XO(
"WAV (Microsoft)")},
73 return config.
Read(wxString(
"/FileFormats/ExportFormat_SF1"), def);
78 config.
Write(
wxT(
"/FileFormats/ExportFormat_SF1"), val);
83 return config.
Read(wxString::Format(
wxT(
"/FileFormats/ExportFormat_SF1_Type/%s_%x"),
89 config.
Write(wxString::Format(
wxT(
"/FileFormats/ExportFormat_SF1_Type/%s_%x"),
97 info.samplerate = 44100;
107 info.format = type | sub;
108 if (sf_format_check(&info))
134 mEncodingOption.
id = type;
135 mEncodingOption.
title =
XO(
"Encoding");
139 mEncoding = *std::get_if<int>(&mEncodingOption.
defaultValue);
149 option = mEncodingOption;
161 if(std::find(mEncodingOption.
values.begin(),
162 mEncodingOption.
values.end(), value) != mEncodingOption.
values.end())
164 mEncoding = *std::get_if<int>(&value);
196 if(std::holds_alternative<int>(typeValue))
198 const auto& typeOption = mOptions.front();
199 return std::find(typeOption.values.begin(),
200 typeOption.values.end(),
201 typeValue) != typeOption.values.end();
210 : mListener(listener)
218 auto hasDefaultType =
false;
225#if defined(__WXMAC__)
226 case SF_FORMAT_AIFF:
break;
229 case SF_FORMAT_WAV:
break;
232 typeOption.values.emplace_back(type);
240 GetEncodings(type, encodingOption.values, encodingOption.names);
241 encodingOption.defaultValue = encodingOption.values[0];
245 typeOption.defaultValue = type;
246 hasDefaultType =
true;
250 mOptions.push_back(std::move(encodingOption));
251 mEncodings[type] = *std::get_if<int>(&encodingOption.defaultValue);
255 typeOption.defaultValue = typeOption.values[0];
256 mOptions.insert(mOptions.begin(), std::move(typeOption));
261 return static_cast<int>(mOptions.size());
266 if (index >= 0 && index <
static_cast<int>(mOptions.size()))
268 option = mOptions[index];
281 auto it = mEncodings.find(
id);
282 if(it != mEncodings.end())
294 const auto newType = *std::get_if<int>(&value);
301 for(
auto& option : mOptions)
303 if(option.id == mType)
309 else if(option.id == newType)
326 auto it = mEncodings.find(
id);
327 if(it != mEncodings.end() && std::holds_alternative<int>(value))
329 it->second = *std::get_if<int>(&value);
344 for(
auto& p : mEncodings)
348 if (mType & SF_FORMAT_SUBMASK)
350 const auto type = mType & SF_FORMAT_TYPEMASK;
351 const auto enc = mType & SF_FORMAT_SUBMASK;
352 mEncodings[type] = enc;
356 for(
auto& option : mOptions)
358 const auto it = mEncodings.find(option.id);
359 if(it == mEncodings.end())
362 if(mType == it->first)
372 for(
auto& [type, encoding] : mEncodings)
421 double t0,
double t1,
bool selectedOnly,
424 const Tags* tags)
override;
445 std::vector<std::string>
GetMimeTypes(
int formatIndex)
const override;
449 std::unique_ptr<ExportOptionsEditor>
477 for (si.channels = 1; sf_format_check(&si); si.channels++)
485 XO(
"Other uncompressed files"),
487 static_cast<unsigned>(si.channels),
507 return {
"audio/x-wav" };
522std::unique_ptr<ExportOptionsEditor>
526 return std::make_unique<ExportOptionsSFTypedEditor>(
528 return std::make_unique<ExportOptionsSFEditor>(listener);
533 return std::make_unique<PCMExportProcessor>(
format);
540 double t0,
double t1,
bool selectionOnly,
543 const Tags* metadata)
556#if defined(__WXMAC__)
600 info.channels = numChannels;
606 if( (numChannels != 1) && ((
sf_format & SF_FORMAT_SUBMASK) == SF_FORMAT_GSM610) )
611 if (
sf_format == SF_FORMAT_WAVEX + SF_FORMAT_GSM610) {
621 if (!sf_format_check(&
info))
622 info.format = (
info.format & SF_FORMAT_TYPEMASK);
623 if (!sf_format_check(&
info)) {
626 const auto path =
fName.GetFullPath();
627 if (
f.Open(path, wxFile::write)) {
631 sf = sf_open_fd(
f.fd(), SFM_WRITE, &
info, FALSE);
666 if( byteCount > 4.295e9)
670 XO(
"You have attempted to Export a WAV or AIFF file which would be greater than 4GB.\n"
671 "Audacity cannot do this, the Export was abandoned.");
673 wxT(
"Size_limits_for_WAV_and_AIFF_files"));
678 context.status = (selectionOnly
679 ?
XO(
"Exporting the selected audio as %s")
680 :
XO(
"Exporting the audio as %s")).
Format( formatStr );
683 wxASSERT(
info.channels >= 0);
700 std::vector<char> dither;
701 if ((
context.info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
706 sf_count_t samplesWritten;
707 size_t numSamples =
context.mixer->Process();
711 auto mixed =
context.mixer->GetBuffer();
714 if ((
context.info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
715 for (
int c = 0; c <
context.info.channels; ++c) {
731 samplesWritten = SFCall<sf_count_t>(sf_writef_short,
context.sf, (
const short *)mixed, numSamples);
733 samplesWritten = SFCall<sf_count_t>(sf_writef_float,
context.sf, (
const float *)mixed, numSamples);
735 if (
static_cast<size_t>(samplesWritten) != numSamples) {
737 sf_error_str(
context.sf, buffer2, 1000);
745"Error while writing %s file (disk full?).\nLibsndfile says \"%s\"")
746 .
Format( formatStr, wxString::FromAscii(buffer2) ));
767 if (
context.fileFormat == SF_FORMAT_WAV ||
768 context.fileFormat == SF_FORMAT_WAVEX) {
773 if (0 != sf_close(
context.sf)) {
783 if ((
context.fileFormat == SF_FORMAT_AIFF) ||
784 (
context.fileFormat == SF_FORMAT_WAV))
797 if ((
sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
801 size_t sz = wxStr.length();
805 size_t sr = (sz+4) * 2;
814 if(wxStr.mb_str(wxConvISO8859_1))
815 strncpy(pSrc.get(), wxStr.mb_str(wxConvISO8859_1), sz);
816 else if(wxStr.mb_str())
817 strncpy(pSrc.get(), wxStr.mb_str(), sz);
821 char *pD = pDest.get();
822 char *pS = pSrc.get();
826 static char aASCII7Table[256] = {
827 0x00, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
828 0x5f, 0x09, 0x0a, 0x5f, 0x0d, 0x5f, 0x5f, 0x5f,
829 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
830 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
831 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
832 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
833 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
834 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
835 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
836 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
837 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
838 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
839 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
840 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
841 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
842 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
843 0x45, 0x20, 0x2c, 0x53, 0x22, 0x2e, 0x2b, 0x2b,
844 0x5e, 0x25, 0x53, 0x28, 0x4f, 0x20, 0x5a, 0x20,
845 0x20, 0x27, 0x27, 0x22, 0x22, 0x2e, 0x2d, 0x5f,
846 0x22, 0x54, 0x73, 0x29, 0x6f, 0x20, 0x7a, 0x59,
847 0x20, 0x21, 0x63, 0x4c, 0x6f, 0x59, 0x7c, 0x53,
848 0x22, 0x43, 0x61, 0x22, 0x5f, 0x2d, 0x43, 0x2d,
849 0x6f, 0x7e, 0x32, 0x33, 0x27, 0x75, 0x50, 0x27,
850 0x2c, 0x31, 0x6f, 0x22, 0x5f, 0x5f, 0x5f, 0x3f,
851 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x43,
852 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
853 0x44, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x78,
854 0x4f, 0x55, 0x55, 0x55, 0x55, 0x59, 0x70, 0x53,
855 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x63,
856 0x65, 0x65, 0x65, 0x65, 0x69, 0x69, 0x69, 0x69,
857 0x64, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x2f,
858 0x6f, 0x75, 0x75, 0x75, 0x75, 0x79, 0x70, 0x79
862 for(i = 0; i < sr; i++) {
863 c = (
unsigned char) *pS++;
864 *pD++ = aASCII7Table[c];
871 int len = (int)strlen(pDest.get());
874 strcat(pDest.get(),
" ");
886 sf_set_string(
sf, SF_STR_TITLE, ascii7Str.get());
893 sf_set_string(
sf, SF_STR_ALBUM, ascii7Str.get());
900 sf_set_string(
sf, SF_STR_ARTIST, ascii7Str.get());
907 sf_set_string(
sf, SF_STR_COMMENT, ascii7Str.get());
914 sf_set_string(
sf, SF_STR_DATE, ascii7Str.get());
921 sf_set_string(
sf, SF_STR_GENRE, ascii7Str.get());
928 sf_set_string(
sf, SF_STR_COPYRIGHT, ascii7Str.get());
935 sf_set_string(
sf, SF_STR_SOFTWARE, ascii7Str.get());
942 sf_set_string(
sf, SF_STR_TRACKNUMBER, ascii7Str.get());
948struct id3_tag_deleter {
949 void operator () (id3_tag *p)
const {
if (p) id3_tag_delete(p); }
951using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>;
958 id3_tag_holder tp { id3_tag_new() };
960 for (
const auto &pair : tags->
GetRange()) {
961 const auto &n = pair.first;
962 const auto &v = pair.second;
963 const char *
name =
"TXXX";
966 name = ID3_FRAME_TITLE;
969 name = ID3_FRAME_ARTIST;
972 name = ID3_FRAME_ALBUM;
974 else if (n.CmpNoCase(
TAG_YEAR) == 0) {
975 name = ID3_FRAME_YEAR;
978 name = ID3_FRAME_GENRE;
981 name = ID3_FRAME_COMMENT;
984 name = ID3_FRAME_TRACK;
986 else if (n.CmpNoCase(
wxT(
"composer")) == 0) {
990 struct id3_frame *frame = id3_frame_new(
name);
992 if (!n.IsAscii() || !v.IsAscii()) {
993 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_UTF_16);
996 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_ISO_8859_1);
1000 id3_utf8_ucs4duplicate((id3_utf8_t *) (
const char *) v.mb_str(wxConvUTF8)) };
1002 if (strcmp(
name, ID3_FRAME_COMMENT) == 0) {
1008 id3_field *
f = id3_frame_field(frame, 1);
1009 memset(
f->immediate.value, 0,
sizeof(
f->immediate.value));
1010 id3_field_setfullstring(id3_frame_field(frame, 3), ucs4.get());
1012 else if (strcmp(
name,
"TXXX") == 0) {
1013 id3_field_setstring(id3_frame_field(frame, 2), ucs4.get());
1015 ucs4.reset(id3_utf8_ucs4duplicate((id3_utf8_t *) (
const char *) n.mb_str(wxConvUTF8)));
1017 id3_field_setstring(id3_frame_field(frame, 1), ucs4.get());
1020 auto addr = ucs4.get();
1021 id3_field_setstrings(id3_frame_field(frame, 1), 1, &addr);
1024 id3_tag_attachframe(tp.get(), frame);
1027 tp->options &= (~ID3_TAG_OPTION_COMPRESSION);
1032#ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3
1033 tp->options |= ID3_TAG_OPTION_ID3V2_3;
1038 len = id3_tag_render(tp.get(), 0);
1042 if ((len % 2) != 0) len++;
1050 id3_tag_render(tp.get(), buffer.get());
1052 wxFFile
f(
fName.GetFullPath(),
wxT(
"r+b"));
1056 sz = (wxUint32) len;
1059 if ((
sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)
1061 if (4 !=
f.Write(
"id3 ", 4))
1065 if (4 !=
f.Write(
"ID3 ", 4))
1067 sz = wxUINT32_SWAP_ON_LE(sz);
1069 if (4 !=
f.Write(&sz, 4))
1072 if (len !=
f.Write(buffer.get(), len))
1075 sz = (wxUint32)
f.Tell() - 8;
1076 if ((
sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
1077 sz = wxUINT32_SWAP_ON_LE(sz);
1081 if (4 !=
f.Write(&sz, 4))
1097 []{
return std::make_unique< ExportPCM >(); }
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)
static ExportPluginRegistry::RegisteredPlugin sRegisteredPlugin
std::variant< bool, int, double, std::string > ExportValue
A type of option values (parameters) used by exporting plugins.
std::unique_ptr< Character[], freer > MallocString
audacity::BasicSettings * gPrefs
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...
Listener object that is used to report on option changes.
virtual void OnExportOptionChangeEnd()=0
Called after OnExportOptionChange
virtual void OnFormatInfoChange()=0
Called when format extension change (usually in response parameter change)
virtual void OnExportOptionChangeBegin()=0
Called before OnExportOptionChange
virtual void OnExportOptionChange(const ExportOption &option)=0
Called when option change.
Editor objects are used to retrieve a set of export options, and configure exporting parameters accor...
std::vector< int > SampleRateList
FormatInfo GetFormatInfo(int index) const override
Returns FormatInfo structure for given index if it's valid, or a default one. FormatInfo::format isn'...
int GetFormatCount() const override
std::unique_ptr< ExportProcessor > CreateProcessor(int format) const override
std::vector< std::string > GetMimeTypes(int formatIndex) const override
std::unique_ptr< ExportOptionsEditor > CreateOptionsEditor(int, ExportOptionsEditor::Listener *) const override
Creates format-dependent options editor, that is used to create a valid set of parameters to be used ...
bool ParseConfig(int formatIndex, const rapidjson::Value &, ExportProcessor::Parameters ¶meters) const override
Attempt to parse configuration JSON object and produce a suitable set of parameters....
static T GetParameterValue(const ExportProcessor::Parameters ¶meters, int id, T defaultValue=T())
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...
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)
virtual void SetStatusString(const TranslatableString &str)=0
std::vector< std::tuple< ExportOptionID, ExportValue > > Parameters
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.
~PCMExportProcessor() override
static ArrayOf< char > AdjustString(const wxString &wxStr, int sf_format)
static void AddStrings(SNDFILE *sf, const Tags *tags, int sf_format)
PCMExportProcessor(int subformat)
TranslatableString status
static bool AddID3Chunk(const wxFileNameWrapper &fName, const Tags *tags, int sf_format)
std::unique_ptr< Tags > metadata
bool Initialize(AudacityProject &project, const Parameters ¶meters, const wxFileNameWrapper &filename, double t0, double t1, bool selectedOnly, double sampleRate, unsigned channels, MixerOptions::Downmix *mixerSpec, const Tags *tags) override
Called before start processing.
ExportResult Process(ExportProcessorDelegate &delegate) override
struct PCMExportProcessor::@177 context
static constexpr size_t maxBlockLen
std::unique_ptr< Mixer > mixer
static TrackList & Get(AudacityProject &project)
Holds a msgid for the translation catalog; may also bind format arguments.
void Load(const audacity::BasicSettings &config) override
SampleRateList GetSampleRateList() const override
ExportOptionsSFEditor(Listener *listener)
bool SetValue(ExportOptionID id, const ExportValue &value) override
std::vector< ExportOption > mOptions
void Store(audacity::BasicSettings &config) const override
int GetOptionsCount() const override
bool GetValue(ExportOptionID id, ExportValue &value) const override
Listener *const mListener
bool IsValidType(const ExportValue &typeValue) const
std::unordered_map< int, int > mEncodings
bool GetOption(int index, ExportOption &option) const override
void Store(audacity::BasicSettings &config) const override
bool GetValue(ExportOptionID, ExportValue &value) const override
ExportOptionsSFTypedEditor(int type)
int GetOptionsCount() const override
ExportOption mEncodingOption
bool GetOption(int, ExportOption &option) const override
SampleRateList GetSampleRateList() const override
void Load(const audacity::BasicSettings &config) override
bool SetValue(ExportOptionID, const ExportValue &value) override
Base class for objects that provide facility to store data persistently, and access it with string ke...
virtual bool Write(const wxString &key, bool value)=0
virtual bool Read(const wxString &key, bool *value) const =0
Positions or offsets within audio files need a wide type.
constexpr auto sampleRate
void GetEncodings(int type, std::vector< ExportValue > &values, TranslatableStrings &names)
void SaveEncoding(audacity::BasicSettings &config, int type, int val)
int LoadEncoding(const audacity::BasicSettings &config, int type, int def)
void SaveOtherFormat(audacity::BasicSettings &config, int val)
struct anonymous_namespace{ExportPCM.cpp}::@174 kFormats[]
const TranslatableString desc
int LoadOtherFormat(const audacity::BasicSettings &config, int def)
A type that provides a description of an exporting option. Isn't allowed to change except non-type re...
ExportOptionID id
Internal option id.
@ TypeEnum
List/enum option. values holds items, and names text to be displayed.
@ Hidden
Option is not used and may be hidden from the user.
ExportValue defaultValue
Default valid value for the parameter.
int flags
A set of flag that desc.
std::vector< ExportValue > values
Interpretation depends on type.
TranslatableString title
Name of an option in a human-readable form.
TranslatableStrings names
Interpretation depends on type.