diff --git a/src/MemoryX.h b/src/MemoryX.h index f882bdb3d..b87af6cdd 100644 --- a/src/MemoryX.h +++ b/src/MemoryX.h @@ -349,12 +349,74 @@ namespace std { #endif /* -* template class Maybe -* Can be used for monomorphic objects that are stack-allocable, but only conditionally constructed. -* You might also use it as a member. -* Initialize with create(), then use like a smart pointer, -* with *, ->, get(), reset(), or in if() -*/ + * ArrayOf + * Not to be confused with std::array (which takes a fixed size) or std::vector + * This maintains a pointer allocated by NEW X[]. It's cheap: only one pointer, + * with no size and capacity information for resizing as for vector, and if X is + * a built-in numeric or pointer type, by default there is no zero filling at + * allocation time. + */ + +template +class ArrayOf : public std::unique_ptr +{ +public: + ArrayOf() {} + explicit ArrayOf(size_t count, bool initialize = false) + { + if (initialize) + reset(safenew X[count]{}); + else + reset(safenew X[count]); + } + ArrayOf(const ArrayOf&) = delete; + ArrayOf& operator= (ArrayOf &&that) + { + std::unique_ptr::operator=(std::move(that)); + return *this; + } + ArrayOf& operator= (std::unique_ptr &&that) + { + std::unique_ptr::operator=(std::move(that)); + return *this; + } +}; + +/* + * ArraysOf + * This simplifies arrays of arrays, each array separately allocated with NEW[] + * But it might be better to use std::Array, N> for some small constant N + * Or use just one array when sub-arrays have a common size and are not large. + */ +template +class ArraysOf : public ArrayOf> +{ +public: + ArraysOf() {} + explicit ArraysOf(size_t N) + : ArrayOf>( N ) + {} + ArraysOf(size_t N, size_t M, bool initialize = false) + : ArrayOf>( N ) + { + for (size_t ii = 0; ii < N; ++ii) + (*this)[ii] = ArrayOf{ M, initialize }; + } + ArraysOf(const ArraysOf&) = delete; + ArraysOf& operator= (ArraysOf&& that) + { + ArrayOf>::operator=(std::move(that)); + return *this; + } +}; + +/* + * template class Maybe + * Can be used for monomorphic objects that are stack-allocable, but only conditionally constructed. + * You might also use it as a member. + * Initialize with create(), then use like a smart pointer, + * with *, ->, get(), reset(), or in if() + */ // Placement-new is used below, and that does not cooperate with the DEBUG_NEW for Visual Studio #ifdef _DEBUG diff --git a/src/export/ExportMP3.cpp b/src/export/ExportMP3.cpp index e519f7e2f..f1307b654 100644 --- a/src/export/ExportMP3.cpp +++ b/src/export/ExportMP3.cpp @@ -65,6 +65,7 @@ #include #include +#include #include #include #include @@ -110,6 +111,7 @@ #define CHANNEL_JOINT 0 #define CHANNEL_STEREO 1 +#define CHANNEL_MONO 2 #define QUALITY_0 0 #define QUALITY_1 1 @@ -211,6 +213,7 @@ static CHOICES sampRates[] = #define ID_ABR 7002 #define ID_CBR 7003 #define ID_QUALITY 7004 +#define ID_MONO 7005 static void InitMP3_Statics() { @@ -275,6 +278,7 @@ public: void OnABR(wxCommandEvent& evt); void OnCBR(wxCommandEvent& evt); void OnQuality(wxCommandEvent& evt); + void OnMono(wxCommandEvent& evt); void LoadNames(CHOICES *choices, int count); wxArrayString GetNames(CHOICES *choices, int count); @@ -285,6 +289,7 @@ private: wxRadioButton *mStereo; wxRadioButton *mJoint; + wxCheckBox *mMono; wxRadioButton *mSET; wxRadioButton *mVBR; wxRadioButton *mABR; @@ -306,6 +311,7 @@ BEGIN_EVENT_TABLE(ExportMP3Options, wxPanel) EVT_RADIOBUTTON(ID_ABR, ExportMP3Options::OnABR) EVT_RADIOBUTTON(ID_CBR, ExportMP3Options::OnCBR) EVT_CHOICE(wxID_ANY, ExportMP3Options::OnQuality) + EVT_CHECKBOX(ID_MONO, ExportMP3Options::OnMono) END_EVENT_TABLE() /// @@ -403,14 +409,21 @@ void ExportMP3Options::PopulateOrExchange(ShuttleGui & S) mMode->Enable(enable); S.AddPrompt(_("Channel Mode:")); - S.StartTwoColumn(); + S.StartMultiColumn(3, wxEXPAND); { + bool mono = false; + gPrefs->Read(wxT("/FileFormats/MP3ForceMono"), &mono, 0); + S.StartRadioButtonGroup(wxT("/FileFormats/MP3ChannelMode"), CHANNEL_JOINT); { mJoint = S.TieRadioButton(_("Joint Stereo"), CHANNEL_JOINT); mStereo = S.TieRadioButton(_("Stereo"), CHANNEL_STEREO); + mJoint->Enable(!mono); + mStereo->Enable(!mono); } S.EndRadioButtonGroup(); + + mMono = S.Id(ID_MONO).AddCheckBox(_("Force export to mono"), mono? wxT("true") : wxT("false")); } S.EndTwoColumn(); } @@ -506,6 +519,17 @@ void ExportMP3Options::OnQuality(wxCommandEvent& WXUNUSED(event)) } } +void ExportMP3Options::OnMono(wxCommandEvent& evt) +{ + bool mono = false; + mono = mMono->GetValue(); + mJoint->Enable(!mono); + mStereo->Enable(!mono); + + gPrefs->Write(wxT("/FileFormats/MP3ForceMono"), mono); + gPrefs->Flush(); +} + void ExportMP3Options::LoadNames(CHOICES *choices, int count) { mRate->Clear(); @@ -1265,7 +1289,8 @@ int MP3Exporter::InitializeStream(int channels, int sampleRate) // Set the channel mode MPEG_mode mode; - if (channels == 1) { + + if (channels == 1 || mChannel == CHANNEL_MONO) { mode = MONO; } else if (mChannel == CHANNEL_JOINT) { @@ -1668,11 +1693,13 @@ int ExportMP3::Export(AudacityProject *project, int rmode; int vmode; int cmode; + bool forceMono; gPrefs->Read(wxT("/FileFormats/MP3Bitrate"), &brate, 128); gPrefs->Read(wxT("/FileFormats/MP3RateMode"), &rmode, MODE_CBR); gPrefs->Read(wxT("/FileFormats/MP3VarMode"), &vmode, ROUTINE_FAST); gPrefs->Read(wxT("/FileFormats/MP3ChannelMode"), &cmode, CHANNEL_STEREO); + gPrefs->Read(wxT("/FileFormats/MP3ForceMono"), &forceMono, 0); // Set the bitrate/quality and mode if (rmode == MODE_SET) { @@ -1722,7 +1749,10 @@ int ExportMP3::Export(AudacityProject *project, } // Set the channel mode - if (cmode == CHANNEL_JOINT) { + if (forceMono) { + exporter.SetChannel(CHANNEL_MONO); + } + else if (cmode == CHANNEL_JOINT) { exporter.SetChannel(CHANNEL_JOINT); } else {