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)
553#if defined(__WXMAC__)
597 info.channels = numChannels;
603 if( (numChannels != 1) && ((
sf_format & SF_FORMAT_SUBMASK) == SF_FORMAT_GSM610) )
608 if (
sf_format == SF_FORMAT_WAVEX + SF_FORMAT_GSM610) {
618 if (!sf_format_check(&
info))
619 info.format = (
info.format & SF_FORMAT_TYPEMASK);
620 if (!sf_format_check(&
info)) {
623 const auto path =
fName.GetFullPath();
624 if (
f.Open(path, wxFile::write)) {
628 sf = sf_open_fd(
f.fd(), SFM_WRITE, &
info, FALSE);
663 if( byteCount > 4.295e9)
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.");
670 wxT(
"Size_limits_for_WAV_and_AIFF_files"));
675 context.status = (selectionOnly
676 ?
XO(
"Exporting the selected audio as %s")
677 :
XO(
"Exporting the audio as %s")).
Format( formatStr );
680 wxASSERT(
info.channels >= 0);
696 std::vector<char> dither;
697 if ((
context.info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
702 sf_count_t samplesWritten;
703 size_t numSamples =
context.mixer->Process();
707 auto mixed =
context.mixer->GetBuffer();
710 if ((
context.info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) {
711 for (
int c = 0; c <
context.info.channels; ++c) {
727 samplesWritten = SFCall<sf_count_t>(sf_writef_short,
context.sf, (
const short *)mixed, numSamples);
729 samplesWritten = SFCall<sf_count_t>(sf_writef_float,
context.sf, (
const float *)mixed, numSamples);
731 if (
static_cast<size_t>(samplesWritten) != numSamples) {
733 sf_error_str(
context.sf, buffer2, 1000);
741"Error while writing %s file (disk full?).\nLibsndfile says \"%s\"")
742 .
Format( formatStr, wxString::FromAscii(buffer2) ));
763 if (
context.fileFormat == SF_FORMAT_WAV ||
764 context.fileFormat == SF_FORMAT_WAVEX) {
769 if (0 != sf_close(
context.sf)) {
779 if ((
context.fileFormat == SF_FORMAT_AIFF) ||
780 (
context.fileFormat == SF_FORMAT_WAV))
793 if ((
sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
797 size_t sz = wxStr.length();
801 size_t sr = (sz+4) * 2;
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);
817 char *pD = pDest.get();
818 char *pS = pSrc.get();
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
858 for(i = 0; i < sr; i++) {
859 c = (
unsigned char) *pS++;
860 *pD++ = aASCII7Table[c];
867 int len = (int)strlen(pDest.get());
870 strcat(pDest.get(),
" ");
882 sf_set_string(
sf, SF_STR_TITLE, ascii7Str.get());
889 sf_set_string(
sf, SF_STR_ALBUM, ascii7Str.get());
896 sf_set_string(
sf, SF_STR_ARTIST, ascii7Str.get());
903 sf_set_string(
sf, SF_STR_COMMENT, ascii7Str.get());
910 sf_set_string(
sf, SF_STR_DATE, ascii7Str.get());
917 sf_set_string(
sf, SF_STR_GENRE, ascii7Str.get());
924 sf_set_string(
sf, SF_STR_COPYRIGHT, ascii7Str.get());
931 sf_set_string(
sf, SF_STR_SOFTWARE, ascii7Str.get());
938 sf_set_string(
sf, SF_STR_TRACKNUMBER, ascii7Str.get());
944struct id3_tag_deleter {
945 void operator () (id3_tag *p)
const {
if (p) id3_tag_delete(p); }
947using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>;
954 id3_tag_holder tp { id3_tag_new() };
956 for (
const auto &pair : tags->
GetRange()) {
957 const auto &n = pair.first;
958 const auto &v = pair.second;
959 const char *
name =
"TXXX";
962 name = ID3_FRAME_TITLE;
965 name = ID3_FRAME_ARTIST;
968 name = ID3_FRAME_ALBUM;
970 else if (n.CmpNoCase(
TAG_YEAR) == 0) {
971 name = ID3_FRAME_YEAR;
974 name = ID3_FRAME_GENRE;
977 name = ID3_FRAME_COMMENT;
980 name = ID3_FRAME_TRACK;
982 else if (n.CmpNoCase(
wxT(
"composer")) == 0) {
986 struct id3_frame *frame = id3_frame_new(
name);
988 if (!n.IsAscii() || !v.IsAscii()) {
989 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_UTF_16);
992 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_ISO_8859_1);
996 id3_utf8_ucs4duplicate((id3_utf8_t *) (
const char *) v.mb_str(wxConvUTF8)) };
998 if (strcmp(
name, ID3_FRAME_COMMENT) == 0) {
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());
1008 else if (strcmp(
name,
"TXXX") == 0) {
1009 id3_field_setstring(id3_frame_field(frame, 2), ucs4.get());
1011 ucs4.reset(id3_utf8_ucs4duplicate((id3_utf8_t *) (
const char *) n.mb_str(wxConvUTF8)));
1013 id3_field_setstring(id3_frame_field(frame, 1), ucs4.get());
1016 auto addr = ucs4.get();
1017 id3_field_setstrings(id3_frame_field(frame, 1), 1, &addr);
1020 id3_tag_attachframe(tp.get(), frame);
1023 tp->options &= (~ID3_TAG_OPTION_COMPRESSION);
1028#ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3
1029 tp->options |= ID3_TAG_OPTION_ID3V2_3;
1034 len = id3_tag_render(tp.get(), 0);
1038 if ((len % 2) != 0) len++;
1046 id3_tag_render(tp.get(), buffer.get());
1048 wxFFile
f(
fName.GetFullPath(),
wxT(
"r+b"));
1052 sz = (wxUint32) len;
1055 if ((
sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)
1057 if (4 !=
f.Write(
"id3 ", 4))
1061 if (4 !=
f.Write(
"ID3 ", 4))
1063 sz = wxUINT32_SWAP_ON_LE(sz);
1065 if (4 !=
f.Write(&sz, 4))
1068 if (len !=
f.Write(buffer.get(), len))
1071 sz = (wxUint32)
f.Tell() - 8;
1072 if ((
sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
1073 sz = wxUINT32_SWAP_ON_LE(sz);
1077 if (4 !=
f.Write(&sz, 4))
1093 []{
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 AudacityProject &project, 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::@184 context
static constexpr size_t maxBlockLen
std::unique_ptr< Mixer > mixer
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)
const TranslatableString desc
int LoadOtherFormat(const audacity::BasicSettings &config, int def)
struct anonymous_namespace{ExportPCM.cpp}::@181 kFormats[]
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.