15#include <wx/filename.h>
39 struct id3_frame *id3_frame_new(
char const *);
40 id3_length_t id3_latin1_length(id3_latin1_t
const *);
41 void id3_latin1_decode(id3_latin1_t
const *, id3_ucs4_t *);
56 {SF_FORMAT_AIFF | SF_FORMAT_PCM_16,
wxT(
"AIFF"),
XO(
"AIFF (Apple/SGI)")},
58 {SF_FORMAT_WAV | SF_FORMAT_PCM_16,
wxT(
"WAV"),
XO(
"WAV (Microsoft)")},
72 return config.
Read(wxString(
"/FileFormats/ExportFormat_SF1"), def);
77 config.
Write(
wxT(
"/FileFormats/ExportFormat_SF1"), val);
82 return config.
Read(wxString::Format(
wxT(
"/FileFormats/ExportFormat_SF1_Type/%s_%x"),
88 config.
Write(wxString::Format(
wxT(
"/FileFormats/ExportFormat_SF1_Type/%s_%x"),
96 info.samplerate = 44100;
106 info.format = type | sub;
107 if (sf_format_check(&info))
133 mEncodingOption.
id = type;
134 mEncodingOption.
title =
XO(
"Encoding");
138 mEncoding = *std::get_if<int>(&mEncodingOption.
defaultValue);
148 option = mEncodingOption;
160 if(std::find(mEncodingOption.
values.begin(),
161 mEncodingOption.
values.end(), value) != mEncodingOption.
values.end())
163 mEncoding = *std::get_if<int>(&value);
195 if(std::holds_alternative<int>(typeValue))
197 const auto& typeOption = mOptions.front();
198 return std::find(typeOption.values.begin(),
199 typeOption.values.end(),
200 typeValue) != typeOption.values.end();
209 : mListener(listener)
217 auto hasDefaultType =
false;
224#if defined(__WXMAC__)
225 case SF_FORMAT_AIFF:
break;
228 case SF_FORMAT_WAV:
break;
231 typeOption.values.emplace_back(type);
239 GetEncodings(type, encodingOption.values, encodingOption.names);
240 encodingOption.defaultValue = encodingOption.values[0];
244 typeOption.defaultValue = type;
245 hasDefaultType =
true;
249 mOptions.push_back(std::move(encodingOption));
250 mEncodings[type] = *std::get_if<int>(&encodingOption.defaultValue);
254 typeOption.defaultValue = typeOption.values[0];
255 mOptions.insert(mOptions.begin(), std::move(typeOption));
260 return static_cast<int>(mOptions.size());
265 if (index >= 0 && index <
static_cast<int>(mOptions.size()))
267 option = mOptions[index];
280 auto it = mEncodings.find(
id);
281 if(it != mEncodings.end())
293 const auto newType = *std::get_if<int>(&value);
300 for(
auto& option : mOptions)
302 if(option.id == mType)
308 else if(option.id == newType)
325 auto it = mEncodings.find(
id);
326 if(it != mEncodings.end() && std::holds_alternative<int>(value))
328 it->second = *std::get_if<int>(&value);
343 for(
auto& p : mEncodings)
347 if (mType & SF_FORMAT_SUBMASK)
349 const auto type = mType & SF_FORMAT_TYPEMASK;
350 const auto enc = mType & SF_FORMAT_SUBMASK;
351 mEncodings[type] = enc;
355 for(
auto& option : mOptions)
357 const auto it = mEncodings.find(option.id);
358 if(it == mEncodings.end())
361 if(mType == it->first)
371 for(
auto& [type, encoding] : mEncodings)
420 double t0,
double t1,
bool selectedOnly,
423 const Tags* tags)
override;
444 std::vector<std::string>
GetMimeTypes(
int formatIndex)
const override;
448 std::unique_ptr<ExportOptionsEditor>
476 for (si.channels = 1; sf_format_check(&si); si.channels++)
484 XO(
"Other uncompressed files"),
486 static_cast<unsigned>(si.channels),
506 return {
"audio/x-wav" };
521std::unique_ptr<ExportOptionsEditor>
525 return std::make_unique<ExportOptionsSFTypedEditor>(
527 return std::make_unique<ExportOptionsSFEditor>(listener);
532 return std::make_unique<PCMExportProcessor>(
format);
539 double t0,
double t1,
bool selectionOnly,
542 const Tags* metadata)
555#if defined(__WXMAC__)
599 info.channels = numChannels;
605 if( (numChannels != 1) && ((
sf_format & SF_FORMAT_SUBMASK) == SF_FORMAT_GSM610) )
610 if (
sf_format == SF_FORMAT_WAVEX + SF_FORMAT_GSM610) {
620 if (!sf_format_check(&
info))
621 info.format = (
info.format & SF_FORMAT_TYPEMASK);
622 if (!sf_format_check(&
info)) {
625 const auto path =
fName.GetFullPath();
626 if (
f.Open(path, wxFile::write)) {
630 sf = sf_open_fd(
f.fd(), SFM_WRITE, &
info, FALSE);
665 if( byteCount > 4.295e9)
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.");
672 wxT(
"Size_limits_for_WAV_and_AIFF_files"));
677 context.status = (selectionOnly
678 ?
XO(
"Exporting the selected audio as %s")
679 :
XO(
"Exporting the audio as %s")).
Format( formatStr );
682 wxASSERT(
info.channels >= 0);
699 std::vector<char> dither;
700 if ((
context.info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
705 sf_count_t samplesWritten;
706 size_t numSamples =
context.mixer->Process();
710 auto mixed =
context.mixer->GetBuffer();
713 if ((
context.info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
714 for (
int c = 0; c <
context.info.channels; ++c) {
730 samplesWritten = SFCall<sf_count_t>(sf_writef_short,
context.sf, (
const short *)mixed, numSamples);
732 samplesWritten = SFCall<sf_count_t>(sf_writef_float,
context.sf, (
const float *)mixed, numSamples);
734 if (
static_cast<size_t>(samplesWritten) != numSamples) {
736 sf_error_str(
context.sf, buffer2, 1000);
744"Error while writing %s file (disk full?).\nLibsndfile says \"%s\"")
745 .
Format( formatStr, wxString::FromAscii(buffer2) ));
766 if (
context.fileFormat == SF_FORMAT_WAV ||
767 context.fileFormat == SF_FORMAT_WAVEX) {
772 if (0 != sf_close(
context.sf)) {
782 if ((
context.fileFormat == SF_FORMAT_AIFF) ||
783 (
context.fileFormat == SF_FORMAT_WAV))
796 if ((
sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
800 size_t sz = wxStr.length();
804 size_t sr = (sz+4) * 2;
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);
820 char *pD = pDest.get();
821 char *pS = pSrc.get();
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
861 for(i = 0; i < sr; i++) {
862 c = (
unsigned char) *pS++;
863 *pD++ = aASCII7Table[c];
870 int len = (int)strlen(pDest.get());
873 strcat(pDest.get(),
" ");
885 sf_set_string(
sf, SF_STR_TITLE, ascii7Str.get());
892 sf_set_string(
sf, SF_STR_ALBUM, ascii7Str.get());
899 sf_set_string(
sf, SF_STR_ARTIST, ascii7Str.get());
906 sf_set_string(
sf, SF_STR_COMMENT, ascii7Str.get());
913 sf_set_string(
sf, SF_STR_DATE, ascii7Str.get());
920 sf_set_string(
sf, SF_STR_GENRE, ascii7Str.get());
927 sf_set_string(
sf, SF_STR_COPYRIGHT, ascii7Str.get());
934 sf_set_string(
sf, SF_STR_SOFTWARE, ascii7Str.get());
941 sf_set_string(
sf, SF_STR_TRACKNUMBER, ascii7Str.get());
947struct id3_tag_deleter {
948 void operator () (id3_tag *p)
const {
if (p) id3_tag_delete(p); }
950using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>;
957 id3_tag_holder tp { id3_tag_new() };
959 for (
const auto &pair : tags->
GetRange()) {
960 const auto &n = pair.first;
961 const auto &v = pair.second;
962 const char *
name =
"TXXX";
965 name = ID3_FRAME_TITLE;
968 name = ID3_FRAME_ARTIST;
971 name = ID3_FRAME_ALBUM;
973 else if (n.CmpNoCase(
TAG_YEAR) == 0) {
974 name = ID3_FRAME_YEAR;
977 name = ID3_FRAME_GENRE;
980 name = ID3_FRAME_COMMENT;
983 name = ID3_FRAME_TRACK;
985 else if (n.CmpNoCase(
wxT(
"composer")) == 0) {
989 struct id3_frame *frame = id3_frame_new(
name);
991 if (!n.IsAscii() || !v.IsAscii()) {
992 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_UTF_16);
995 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_ISO_8859_1);
999 id3_utf8_ucs4duplicate((id3_utf8_t *) (
const char *) v.mb_str(wxConvUTF8)) };
1001 if (strcmp(
name, ID3_FRAME_COMMENT) == 0) {
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());
1011 else if (strcmp(
name,
"TXXX") == 0) {
1012 id3_field_setstring(id3_frame_field(frame, 2), ucs4.get());
1014 ucs4.reset(id3_utf8_ucs4duplicate((id3_utf8_t *) (
const char *) n.mb_str(wxConvUTF8)));
1016 id3_field_setstring(id3_frame_field(frame, 1), ucs4.get());
1019 auto addr = ucs4.get();
1020 id3_field_setstrings(id3_frame_field(frame, 1), 1, &addr);
1023 id3_tag_attachframe(tp.get(), frame);
1026 tp->options &= (~ID3_TAG_OPTION_COMPRESSION);
1031#ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3
1032 tp->options |= ID3_TAG_OPTION_ID3V2_3;
1037 len = id3_tag_render(tp.get(), 0);
1041 if ((len % 2) != 0) len++;
1049 id3_tag_render(tp.get(), buffer.get());
1051 wxFFile
f(
fName.GetFullPath(),
wxT(
"r+b"));
1055 sz = (wxUint32) len;
1058 if ((
sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)
1060 if (4 !=
f.Write(
"id3 ", 4))
1064 if (4 !=
f.Write(
"ID3 ", 4))
1066 sz = wxUINT32_SWAP_ON_LE(sz);
1068 if (4 !=
f.Write(&sz, 4))
1071 if (len !=
f.Write(buffer.get(), len))
1074 sz = (wxUint32)
f.Tell() - 8;
1075 if ((
sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
1076 sz = wxUINT32_SWAP_ON_LE(sz);
1080 if (4 !=
f.Write(&sz, 4))
1096 []{
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.
struct PCMExportProcessor::@174 context
ExportResult Process(ExportProcessorDelegate &delegate) override
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)
struct anonymous_namespace{ExportPCM.cpp}::@171 kFormats[]
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)
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.