123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401 |
- /*
- * mod2wave.cpp
- * ------------
- * Purpose: Module to WAV conversion (dialog + conversion code).
- * Notes : (currently none)
- * Authors: OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #include "stdafx.h"
- #include "Mptrack.h"
- #include "Sndfile.h"
- #include "Dlsbank.h"
- #include "Mainfrm.h"
- #include "Mpdlgs.h"
- #include "mod2wave.h"
- #include "WAVTools.h"
- #include "../common/mptString.h"
- #include "../common/version.h"
- #include "../soundlib/MixerLoops.h"
- #include "openmpt/soundbase/Dither.hpp"
- #include "../common/Dither.h"
- #include "../soundlib/AudioReadTarget.h"
- #include "../soundlib/plugins/PlugInterface.h"
- #include "../common/mptFileIO.h"
- #include "mpt/audio/span.hpp"
- #include <variant>
- #include "mpt/io/io.hpp"
- #include "mpt/io/io_stdstream.hpp"
- OPENMPT_NAMESPACE_BEGIN
- extern const TCHAR *gszChnCfgNames[3];
- template <typename Tsample>
- static CSoundFile::samplecount_t ReadInterleaved(CSoundFile &sndFile, Tsample *outputBuffer, std::size_t channels, CSoundFile::samplecount_t count, DithersOpenMPT &dithers)
- {
- sndFile.ResetMixStat();
- MPT_ASSERT(sndFile.m_MixerSettings.gnChannels == channels);
- AudioTargetBuffer<mpt::audio_span_interleaved<Tsample>, DithersOpenMPT> target(mpt::audio_span_interleaved<Tsample>(outputBuffer, channels, count), dithers);
- return sndFile.Read(count, target);
- }
- ///////////////////////////////////////////////////
- // CWaveConvert - setup for converting a wave file
- BEGIN_MESSAGE_MAP(CWaveConvert, CDialog)
- ON_COMMAND(IDC_CHECK2, &CWaveConvert::OnCheckTimeLimit)
- ON_COMMAND(IDC_CHECK4, &CWaveConvert::OnCheckChannelMode)
- ON_COMMAND(IDC_CHECK6, &CWaveConvert::OnCheckInstrMode)
- ON_COMMAND(IDC_RADIO1, &CWaveConvert::UpdateDialog)
- ON_COMMAND(IDC_RADIO2, &CWaveConvert::UpdateDialog)
- ON_COMMAND(IDC_RADIO3, &CWaveConvert::UpdateDialog)
- ON_COMMAND(IDC_RADIO4, &CWaveConvert::OnExportModeChanged)
- ON_COMMAND(IDC_RADIO5, &CWaveConvert::OnExportModeChanged)
- ON_COMMAND(IDC_PLAYEROPTIONS, &CWaveConvert::OnPlayerOptions)
- ON_CBN_SELCHANGE(IDC_COMBO5, &CWaveConvert::OnFileTypeChanged)
- ON_CBN_SELCHANGE(IDC_COMBO1, &CWaveConvert::OnSamplerateChanged)
- ON_CBN_SELCHANGE(IDC_COMBO4, &CWaveConvert::OnChannelsChanged)
- ON_CBN_SELCHANGE(IDC_COMBO6, &CWaveConvert::OnDitherChanged)
- ON_CBN_SELCHANGE(IDC_COMBO2, &CWaveConvert::OnFormatChanged)
- ON_CBN_SELCHANGE(IDC_COMBO9, &CWaveConvert::OnSampleSlotChanged)
- END_MESSAGE_MAP()
- CWaveConvert::CWaveConvert(CWnd *parent, ORDERINDEX minOrder, ORDERINDEX maxOrder, ORDERINDEX numOrders, CSoundFile &sndFile, const std::vector<EncoderFactoryBase*> &encFactories)
- : CDialog(IDD_WAVECONVERT, parent)
- , m_Settings(theApp.GetSettings(), encFactories)
- , m_SndFile(sndFile)
- {
- ASSERT(!encFactories.empty());
- encTraits = m_Settings.GetTraits();
- m_bGivePlugsIdleTime = false;
- if(minOrder != ORDERINDEX_INVALID && maxOrder != ORDERINDEX_INVALID)
- {
- // render selection
- m_Settings.minOrder = minOrder;
- m_Settings.maxOrder = maxOrder;
- }
- m_Settings.repeatCount = 1;
- m_Settings.minSequence = m_Settings.maxSequence = m_SndFile.Order.GetCurrentSequenceIndex();
- m_nNumOrders = numOrders;
- m_dwSongLimit = 0;
- }
- void CWaveConvert::DoDataExchange(CDataExchange *pDX)
- {
- CDialog::DoDataExchange(pDX);
- DDX_Control(pDX, IDC_COMBO5, m_CbnFileType);
- DDX_Control(pDX, IDC_COMBO1, m_CbnSampleRate);
- DDX_Control(pDX, IDC_COMBO4, m_CbnChannels);
- DDX_Control(pDX, IDC_COMBO6, m_CbnDither);
- DDX_Control(pDX, IDC_COMBO2, m_CbnSampleFormat);
- DDX_Control(pDX, IDC_SPIN3, m_SpinMinOrder);
- DDX_Control(pDX, IDC_SPIN4, m_SpinMaxOrder);
- DDX_Control(pDX, IDC_SPIN5, m_SpinLoopCount);
- DDX_Control(pDX, IDC_SPIN6, m_SpinMinSequence);
- DDX_Control(pDX, IDC_SPIN7, m_SpinMaxSequence);
- DDX_Control(pDX, IDC_COMBO9, m_CbnSampleSlot);
- DDX_Control(pDX, IDC_COMBO3, m_CbnGenre);
- DDX_Control(pDX, IDC_EDIT10, m_EditGenre);
- DDX_Control(pDX, IDC_EDIT11, m_EditTitle);
- DDX_Control(pDX, IDC_EDIT6, m_EditAuthor);
- DDX_Control(pDX, IDC_EDIT7, m_EditAlbum);
- DDX_Control(pDX, IDC_EDIT8, m_EditURL);
- DDX_Control(pDX, IDC_EDIT9, m_EditYear);
- }
- BOOL CWaveConvert::OnInitDialog()
- {
- CDialog::OnInitDialog();
- CheckDlgButton(IDC_CHECK5, BST_UNCHECKED); // Normalize
- CheckDlgButton(IDC_CHECK3, BST_CHECKED); // Cue points
- CheckDlgButton(IDC_CHECK4, BST_UNCHECKED);
- CheckDlgButton(IDC_CHECK6, BST_UNCHECKED);
- const bool selection = (m_Settings.minOrder != ORDERINDEX_INVALID && m_Settings.maxOrder != ORDERINDEX_INVALID);
- CheckRadioButton(IDC_RADIO1, IDC_RADIO3, selection ? IDC_RADIO2 : IDC_RADIO1);
- if(selection)
- {
- SetDlgItemInt(IDC_EDIT3, m_Settings.minOrder);
- SetDlgItemInt(IDC_EDIT4, m_Settings.maxOrder);
- }
- m_SpinMinOrder.SetRange32(0, m_nNumOrders);
- m_SpinMaxOrder.SetRange32(0, m_nNumOrders);
- const SEQUENCEINDEX numSequences = m_SndFile.Order.GetNumSequences();
- const BOOL enableSeq = numSequences > 1 ? TRUE : FALSE;
- GetDlgItem(IDC_RADIO3)->EnableWindow(enableSeq);
- m_SpinMinSequence.SetRange32(1, numSequences);
- m_SpinMaxSequence.SetRange32(1, numSequences);
- SetDlgItemInt(IDC_EDIT12, m_Settings.minSequence + 1);
- SetDlgItemInt(IDC_EDIT13, m_Settings.maxSequence + 1);
- SetDlgItemInt(IDC_EDIT5, m_Settings.repeatCount, FALSE);
- m_SpinLoopCount.SetRange32(1, int16_max);
- FillFileTypes();
- FillSamplerates();
- FillChannels();
- FillFormats();
- FillDither();
- LoadTags();
- m_EditYear.SetLimitText(4);
- m_EditTitle.SetWindowText(mpt::ToCString(m_Settings.Tags.title));
- m_EditAuthor.SetWindowText(mpt::ToCString(m_Settings.Tags.artist));
- m_EditURL.SetWindowText(mpt::ToCString(m_Settings.Tags.url));
- m_EditAlbum.SetWindowText(mpt::ToCString(m_Settings.Tags.album));
- m_EditYear.SetWindowText(mpt::ToCString(m_Settings.Tags.year));
- m_EditGenre.SetWindowText(mpt::ToCString(m_Settings.Tags.genre));
- FillTags();
- // Plugin quirk options are only available if there are any plugins loaded.
- GetDlgItem(IDC_GIVEPLUGSIDLETIME)->EnableWindow(FALSE);
- GetDlgItem(IDC_RENDERSILENCE)->EnableWindow(FALSE);
- #ifndef NO_PLUGINS
- for(const auto &plug : m_SndFile.m_MixPlugins)
- {
- if(plug.pMixPlugin != nullptr)
- {
- GetDlgItem(IDC_GIVEPLUGSIDLETIME)->EnableWindow(TRUE);
- GetDlgItem(IDC_RENDERSILENCE)->EnableWindow(TRUE);
- break;
- }
- }
- #endif // NO_PLUGINS
- // Fill list of sample slots to render into
- if(m_SndFile.GetNextFreeSample() != SAMPLEINDEX_INVALID)
- {
- m_CbnSampleSlot.SetItemData(m_CbnSampleSlot.AddString(_T("<empty slot>")), 0);
- }
- CString s;
- for(SAMPLEINDEX smp = 1; smp <= m_SndFile.GetNumSamples(); smp++)
- {
- s.Format(_T("%02u: %s%s"), smp, m_SndFile.GetSample(smp).HasSampleData() ? _T("*") : _T(""), mpt::ToCString(m_SndFile.GetCharsetInternal(), m_SndFile.GetSampleName(smp)).GetString());
- m_CbnSampleSlot.SetItemData(m_CbnSampleSlot.AddString(s), smp);
- }
- if(m_Settings.sampleSlot > m_SndFile.GetNumSamples()) m_Settings.sampleSlot = 0;
- m_CbnSampleSlot.SetCurSel(m_Settings.sampleSlot);
- CheckRadioButton(IDC_RADIO4, IDC_RADIO5, m_Settings.outputToSample ? IDC_RADIO5 : IDC_RADIO4);
- UpdateDialog();
- return TRUE;
- }
- void CWaveConvert::LoadTags()
- {
- m_Settings.Tags.title = mpt::ToUnicode(mpt::Charset::Locale, m_SndFile.GetTitle());
- m_Settings.Tags.comments = mpt::ToUnicode(mpt::Charset::Locale, m_SndFile.m_songMessage.GetFormatted(SongMessage::leLF));
- m_Settings.Tags.artist = m_SndFile.m_songArtist;
- m_Settings.Tags.album = m_Settings.storedTags.album;
- m_Settings.Tags.trackno = m_Settings.storedTags.trackno;
- m_Settings.Tags.year = m_Settings.storedTags.year;
- m_Settings.Tags.url = m_Settings.storedTags.url;
- m_Settings.Tags.genre = m_Settings.storedTags.genre;
- }
- void CWaveConvert::SaveTags()
- {
- m_Settings.storedTags.artist = m_Settings.Tags.artist;
- m_Settings.storedTags.album = m_Settings.Tags.album;
- m_Settings.storedTags.trackno = m_Settings.Tags.trackno;
- m_Settings.storedTags.year = m_Settings.Tags.year;
- m_Settings.storedTags.url = m_Settings.Tags.url;
- m_Settings.storedTags.genre = m_Settings.Tags.genre;
- }
- void CWaveConvert::FillTags()
- {
- EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
- DWORD_PTR dwFormat = m_CbnSampleFormat.GetItemData(m_CbnSampleFormat.GetCurSel());
- Encoder::Mode mode = (Encoder::Mode)((dwFormat >> 24) & 0xff);
- CheckDlgButton(IDC_CHECK3, encTraits->canCues?encSettings.Cues?TRUE:FALSE:FALSE);
- ::EnableWindow(::GetDlgItem(m_hWnd, IDC_CHECK3), encTraits->canCues?TRUE:FALSE);
- const BOOL canTags = encTraits->canTags ? TRUE : FALSE;
- CheckDlgButton(IDC_CHECK7, encSettings.Tags ? canTags : FALSE);
- ::EnableWindow(::GetDlgItem(m_hWnd, IDC_CHECK7), canTags);
- ::EnableWindow(::GetDlgItem(m_hWnd, IDC_COMBO3), canTags);
- ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT11), canTags);
- ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT6), canTags);
- ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT7), canTags);
- ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT8), canTags);
- ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT9), canTags);
- m_CbnGenre.EnableWindow(canTags?TRUE:FALSE);
- m_EditGenre.EnableWindow(canTags?TRUE:FALSE);
- if((encTraits->modesWithFixedGenres & mode) && !encTraits->genres.empty())
- {
- m_EditGenre.ShowWindow(SW_HIDE);
- m_CbnGenre.ShowWindow(SW_SHOW);
- m_EditGenre.Clear();
- m_CbnGenre.ResetContent();
- m_CbnGenre.AddString(_T(""));
- for(const auto &genre : encTraits->genres)
- {
- m_CbnGenre.AddString(mpt::ToCString(genre));
- }
- } else
- {
- m_CbnGenre.ShowWindow(SW_HIDE);
- m_EditGenre.ShowWindow(SW_SHOW);
- m_CbnGenre.ResetContent();
- m_EditGenre.Clear();
- }
- }
- void CWaveConvert::FillFileTypes()
- {
- m_CbnFileType.ResetContent();
- int sel = 0;
- for(std::size_t i = 0; i < m_Settings.EncoderFactories.size(); ++i)
- {
- int ndx = m_CbnFileType.AddString(MPT_CFORMAT("{} ({})")(mpt::ToCString(m_Settings.EncoderFactories[i]->GetTraits().fileShortDescription), mpt::ToCString(m_Settings.EncoderFactories[i]->GetTraits().fileDescription)));
- m_CbnFileType.SetItemData(ndx, i);
- if(m_Settings.EncoderIndex == i)
- {
- sel = ndx;
- }
- }
- m_CbnFileType.SetCurSel(sel);
- }
- void CWaveConvert::FillSamplerates()
- {
- EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
- m_CbnSampleRate.CComboBox::ResetContent();
- int sel = -1;
- if(TrackerSettings::Instance().ExportDefaultToSoundcardSamplerate)
- {
- for(auto samplerate : encTraits->samplerates)
- {
- if(samplerate == TrackerSettings::Instance().MixerSamplerate)
- {
- encSettings.Samplerate = samplerate;
- }
- }
- }
- for(auto samplerate : encTraits->samplerates)
- {
- int ndx = m_CbnSampleRate.AddString(MPT_CFORMAT("{} Hz")(samplerate));
- m_CbnSampleRate.SetItemData(ndx, samplerate);
- if(samplerate == encSettings.Samplerate)
- {
- sel = ndx;
- }
- }
- if(sel == -1)
- {
- sel = 0;
- }
- m_CbnSampleRate.SetCurSel(sel);
- }
- void CWaveConvert::FillChannels()
- {
- EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
- m_CbnChannels.CComboBox::ResetContent();
- int sel = 0;
- for(int channels = 4; channels >= 1; channels /= 2)
- {
- if(channels > encTraits->maxChannels)
- {
- continue;
- }
- if(IsDlgButtonChecked(IDC_RADIO5) != BST_UNCHECKED)
- {
- if(channels > 2)
- {
- // sample export only supports 2 channels max
- continue;
- }
- }
- int ndx = m_CbnChannels.AddString(gszChnCfgNames[(channels+2)/2-1]);
- m_CbnChannels.SetItemData(ndx, channels);
- if(channels == encSettings.Channels)
- {
- sel = ndx;
- }
- }
- m_CbnChannels.SetCurSel(sel);
- }
- void CWaveConvert::FillFormats()
- {
- EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
- m_CbnSampleFormat.CComboBox::ResetContent();
- int sel = -1;
- int samplerate = static_cast<int>(m_CbnSampleRate.GetItemData(m_CbnSampleRate.GetCurSel()));
- int channels = static_cast<int>(m_CbnChannels.GetItemData(m_CbnChannels.GetCurSel()));
- if(encTraits->modes & Encoder::ModeQuality)
- {
- for(int quality = 100; quality >= 0; quality -= 10)
- {
- int ndx = m_CbnSampleFormat.AddString(mpt::ToCString(m_Settings.GetEncoderFactory()->DescribeQuality(quality * 0.01f)));
- m_CbnSampleFormat.SetItemData(ndx, (Encoder::ModeQuality<<24) | (quality<<0));
- if(encSettings.Mode == Encoder::ModeQuality && mpt::saturate_round<int>(encSettings.Quality*100.0f) == quality)
- {
- sel = ndx;
- }
- }
- }
- if(encTraits->modes & Encoder::ModeVBR)
- {
- for(int bitrate = static_cast<int>(encTraits->bitrates.size()-1); bitrate >= 0; --bitrate)
- {
- if(!m_Settings.GetEncoderFactory()->IsBitrateSupported(samplerate, channels, encTraits->bitrates[bitrate]))
- {
- continue;
- }
- int ndx = m_CbnSampleFormat.AddString(mpt::ToCString(m_Settings.GetEncoderFactory()->DescribeBitrateVBR(encTraits->bitrates[bitrate])));
- m_CbnSampleFormat.SetItemData(ndx, (Encoder::ModeVBR<<24) | (encTraits->bitrates[bitrate]<<0));
- if(encSettings.Mode == Encoder::ModeVBR && static_cast<int>(encSettings.Bitrate) == encTraits->bitrates[bitrate])
- {
- sel = ndx;
- }
- }
- }
- if(encTraits->modes & Encoder::ModeABR)
- {
- for(int bitrate = static_cast<int>(encTraits->bitrates.size()-1); bitrate >= 0; --bitrate)
- {
- if(!m_Settings.GetEncoderFactory()->IsBitrateSupported(samplerate, channels, encTraits->bitrates[bitrate]))
- {
- continue;
- }
- int ndx = m_CbnSampleFormat.AddString(mpt::ToCString(m_Settings.GetEncoderFactory()->DescribeBitrateABR(encTraits->bitrates[bitrate])));
- m_CbnSampleFormat.SetItemData(ndx, (Encoder::ModeABR<<24) | (encTraits->bitrates[bitrate]<<0));
- if(encSettings.Mode == Encoder::ModeABR && static_cast<int>(encSettings.Bitrate) == encTraits->bitrates[bitrate])
- {
- sel = ndx;
- }
- }
- }
- if(encTraits->modes & Encoder::ModeCBR)
- {
- for(int bitrate = static_cast<int>(encTraits->bitrates.size()-1); bitrate >= 0; --bitrate)
- {
- if(!m_Settings.GetEncoderFactory()->IsBitrateSupported(samplerate, channels, encTraits->bitrates[bitrate]))
- {
- continue;
- }
- int ndx = m_CbnSampleFormat.AddString(mpt::ToCString(m_Settings.GetEncoderFactory()->DescribeBitrateCBR(encTraits->bitrates[bitrate])));
- m_CbnSampleFormat.SetItemData(ndx, (Encoder::ModeCBR<<24) | (encTraits->bitrates[bitrate]<<0));
- if(encSettings.Mode == Encoder::ModeCBR && static_cast<int>(encSettings.Bitrate) == encTraits->bitrates[bitrate])
- {
- sel = ndx;
- }
- }
- }
- if(encTraits->modes & Encoder::ModeLossless)
- {
- bool allBig = true;
- bool allLittle = true;
- for(const auto &format : encTraits->formats)
- {
- if(format.endian != mpt::endian::little)
- {
- allLittle = false;
- }
- if(format.endian != mpt::endian::big)
- {
- allBig = false;
- }
- }
- bool showEndian = !(allBig || allLittle);
- for(std::size_t i = 0; i < encTraits->formats.size(); ++i)
- {
- const Encoder::Format &format = encTraits->formats[i];
- mpt::ustring description;
- switch(format.encoding)
- {
- case Encoder::Format::Encoding::Float:
- description = MPT_UFORMAT("{} Bit Floating Point")(format.bits);
- break;
- case Encoder::Format::Encoding::Integer:
- description = MPT_UFORMAT("{} Bit")(format.bits);
- break;
- case Encoder::Format::Encoding::Alaw:
- description = U_("A-law");
- break;
- case Encoder::Format::Encoding::ulaw:
- description = MPT_UTF8("\xce\xbc-law");
- break;
- case Encoder::Format::Encoding::Unsigned:
- description = MPT_UFORMAT("{} Bit (unsigned)")(format.bits);
- break;
- }
- if(showEndian && format.bits != 8 && format.encoding != Encoder::Format::Encoding::Alaw && format.encoding != Encoder::Format::Encoding::ulaw)
- {
- switch(format.endian)
- {
- case mpt::endian::big:
- description += U_(" Big-Endian");
- break;
- case mpt::endian::little:
- description += U_(" Little-Endian");
- break;
- }
- }
- int ndx = m_CbnSampleFormat.AddString(mpt::ToCString(description));
- m_CbnSampleFormat.SetItemData(ndx, format.AsInt());
- if(encSettings.Mode & Encoder::ModeLossless && format == encSettings.Format2)
- {
- sel = ndx;
- }
- }
- }
- if(sel == -1)
- {
- sel = 0;
- }
- m_CbnSampleFormat.SetCurSel(sel);
- }
- void CWaveConvert::FillDither()
- {
- EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
- m_CbnDither.CComboBox::ResetContent();
- int format = m_CbnSampleFormat.GetItemData(m_CbnSampleFormat.GetCurSel()) & 0xffffff;
- if((encTraits->modes & Encoder::ModeLossless) && Encoder::Format::FromInt(format).GetSampleFormat() != SampleFormat::Invalid && !Encoder::Format::FromInt(format).GetSampleFormat().IsFloat())
- {
- m_CbnDither.EnableWindow(TRUE);
- for(std::size_t dither = 0; dither < DithersOpenMPT::GetNumDithers(); ++dither)
- {
- int ndx = m_CbnDither.AddString(mpt::ToCString(DithersOpenMPT::GetModeName(dither) + U_(" dither")));
- m_CbnDither.SetItemData(ndx, dither);
- }
- } else
- {
- m_CbnDither.EnableWindow(FALSE);
- for(std::size_t dither = 0; dither < DithersOpenMPT::GetNumDithers(); ++dither)
- {
- int ndx = m_CbnDither.AddString(mpt::ToCString(DithersOpenMPT::GetModeName(DithersOpenMPT::GetNoDither()) + U_(" dither")));
- m_CbnDither.SetItemData(ndx, dither);
- }
- }
- m_CbnDither.SetCurSel(encSettings.Dither);
- }
- void CWaveConvert::OnFileTypeChanged()
- {
- SaveEncoderSettings();
- DWORD_PTR dwFileType = m_CbnFileType.GetItemData(m_CbnFileType.GetCurSel());
- m_Settings.SelectEncoder(dwFileType);
- encTraits = m_Settings.GetTraits();
- FillSamplerates();
- FillChannels();
- FillFormats();
- FillDither();
- FillTags();
- }
- void CWaveConvert::OnSamplerateChanged()
- {
- SaveEncoderSettings();
- FillFormats();
- FillDither();
- }
- void CWaveConvert::OnChannelsChanged()
- {
- SaveEncoderSettings();
- FillFormats();
- FillDither();
- }
- void CWaveConvert::OnDitherChanged()
- {
- SaveEncoderSettings();
- }
- void CWaveConvert::OnFormatChanged()
- {
- SaveEncoderSettings();
- FillDither();
- FillTags();
- }
- void CWaveConvert::UpdateDialog()
- {
- CheckDlgButton(IDC_CHECK2, (m_dwSongLimit) ? BST_CHECKED : 0);
- GetDlgItem(IDC_EDIT2)->EnableWindow(m_dwSongLimit ? TRUE : FALSE);
- // Repeat / selection play
- int sel = GetCheckedRadioButton(IDC_RADIO1, IDC_RADIO3);
- GetDlgItem(IDC_EDIT3)->EnableWindow(sel == IDC_RADIO2);
- GetDlgItem(IDC_EDIT4)->EnableWindow(sel == IDC_RADIO2);
- m_SpinMinOrder.EnableWindow(sel == IDC_RADIO2);
- m_SpinMaxOrder.EnableWindow(sel == IDC_RADIO2);
- GetDlgItem(IDC_EDIT5)->EnableWindow(sel == IDC_RADIO1);
- m_SpinLoopCount.EnableWindow(sel == IDC_RADIO1);
- const SEQUENCEINDEX numSequences = m_SndFile.Order.GetNumSequences();
- const BOOL enableSeq = (numSequences > 1 && sel == IDC_RADIO3) ? TRUE : FALSE;
- GetDlgItem(IDC_EDIT12)->EnableWindow(enableSeq);
- GetDlgItem(IDC_EDIT13)->EnableWindow(enableSeq);
- m_SpinMinSequence.EnableWindow(enableSeq);
- m_SpinMaxSequence.EnableWindow(enableSeq);
- // No free slots => Cannot do instrument- or channel-based export to sample
- BOOL canDoMultiExport = (IsDlgButtonChecked(IDC_RADIO4) != BST_UNCHECKED /* normal export */ || m_CbnSampleSlot.GetItemData(0) == 0 /* "free slot" is in list */) ? TRUE : FALSE;
- GetDlgItem(IDC_CHECK4)->EnableWindow(canDoMultiExport);
- GetDlgItem(IDC_CHECK6)->EnableWindow(canDoMultiExport);
- }
- void CWaveConvert::OnExportModeChanged()
- {
- SaveEncoderSettings();
- bool sampleExport = (IsDlgButtonChecked(IDC_RADIO5) != BST_UNCHECKED);
- m_CbnFileType.EnableWindow(sampleExport ? FALSE : TRUE);
- m_CbnSampleSlot.EnableWindow(sampleExport && !IsDlgButtonChecked(IDC_CHECK4) && !IsDlgButtonChecked(IDC_CHECK6));
- if(sampleExport)
- {
- // Render to sample: Always use WAV
- if(m_CbnFileType.GetCurSel() != 0)
- {
- m_CbnFileType.SetCurSel(0);
- OnFileTypeChanged();
- }
- }
- FillChannels();
- FillFormats();
- FillDither();
- FillTags();
- }
- void CWaveConvert::OnSampleSlotChanged()
- {
- CheckRadioButton(IDC_RADIO4, IDC_RADIO5, IDC_RADIO5);
- // When choosing a specific sample slot, we cannot use per-channel or per-instrument export
- int sel = m_CbnSampleSlot.GetCurSel();
- if(sel >= 0 && m_CbnSampleSlot.GetItemData(sel) > 0)
- {
- CheckDlgButton(IDC_CHECK4, BST_UNCHECKED);
- CheckDlgButton(IDC_CHECK6, BST_UNCHECKED);
- }
- UpdateDialog();
- }
- void CWaveConvert::OnPlayerOptions()
- {
- CPropertySheet dlg(_T("Mixer Settings"), this);
- COptionsMixer mixerpage;
- dlg.AddPage(&mixerpage);
- #if !defined(NO_REVERB) || !defined(NO_DSP) || !defined(NO_EQ) || !defined(NO_AGC)
- COptionsPlayer dsppage;
- dlg.AddPage(&dsppage);
- #endif
- dlg.DoModal();
- }
- void CWaveConvert::OnCheckTimeLimit()
- {
- if (IsDlgButtonChecked(IDC_CHECK2))
- {
- m_dwSongLimit = GetDlgItemInt(IDC_EDIT2, NULL, FALSE);
- if (!m_dwSongLimit)
- {
- m_dwSongLimit = 600;
- SetDlgItemText(IDC_EDIT2, _T("600"));
- }
- } else m_dwSongLimit = 0;
- UpdateDialog();
- }
- // Channel render is mutually exclusive with instrument render
- void CWaveConvert::OnCheckChannelMode()
- {
- if(IsDlgButtonChecked(IDC_CHECK4) != BST_UNCHECKED)
- {
- CheckDlgButton(IDC_CHECK6, BST_UNCHECKED);
- m_CbnSampleSlot.SetCurSel(0);
- }
- UpdateDialog();
- }
- // Channel render is mutually exclusive with instrument render
- void CWaveConvert::OnCheckInstrMode()
- {
- if(IsDlgButtonChecked(IDC_CHECK6) != BST_UNCHECKED)
- {
- CheckDlgButton(IDC_CHECK4, BST_UNCHECKED);
- m_CbnSampleSlot.SetCurSel(0);
- }
- UpdateDialog();
- }
- void CWaveConvert::OnOK()
- {
- if (m_dwSongLimit) m_dwSongLimit = GetDlgItemInt(IDC_EDIT2, NULL, FALSE);
- const bool selection = IsDlgButtonChecked(IDC_RADIO2) != BST_UNCHECKED;
- if(selection)
- {
- // Play selection
- m_Settings.minOrder = static_cast<ORDERINDEX>(GetDlgItemInt(IDC_EDIT3, NULL, FALSE));
- m_Settings.maxOrder = static_cast<ORDERINDEX>(GetDlgItemInt(IDC_EDIT4, NULL, FALSE));
- if(m_Settings.minOrder > m_Settings.maxOrder)
- std::swap(m_Settings.minOrder, m_Settings.maxOrder);
- } else
- {
- m_Settings.minOrder = m_Settings.maxOrder = ORDERINDEX_INVALID;
- }
- if(IsDlgButtonChecked(IDC_RADIO3))
- {
- const UINT maxSequence = m_SndFile.Order.GetNumSequences();
- m_Settings.minSequence = static_cast<SEQUENCEINDEX>(std::clamp(GetDlgItemInt(IDC_EDIT12, NULL, FALSE), 1u, maxSequence) - 1u);
- m_Settings.maxSequence = static_cast<SEQUENCEINDEX>(std::clamp(GetDlgItemInt(IDC_EDIT13, NULL, FALSE), 1u, maxSequence) - 1u);
- if(m_Settings.minSequence > m_Settings.maxSequence)
- std::swap(m_Settings.minSequence, m_Settings.maxSequence);
- } else
- {
- m_Settings.minSequence = m_Settings.maxSequence = m_SndFile.Order.GetCurrentSequenceIndex();
- }
- m_Settings.repeatCount = static_cast<uint16>(GetDlgItemInt(IDC_EDIT5, NULL, FALSE));
- m_Settings.normalize = IsDlgButtonChecked(IDC_CHECK5) != BST_UNCHECKED;
- m_Settings.silencePlugBuffers = IsDlgButtonChecked(IDC_RENDERSILENCE) != BST_UNCHECKED;
- m_Settings.outputToSample = IsDlgButtonChecked(IDC_RADIO5) != BST_UNCHECKED;
- m_bGivePlugsIdleTime = IsDlgButtonChecked(IDC_GIVEPLUGSIDLETIME) != BST_UNCHECKED;
- if (m_bGivePlugsIdleTime)
- {
- static bool showWarning = true;
- if(showWarning && Reporting::Confirm("You only need slow render if you are experiencing dropped notes with a Kontakt based sampler with Direct-From-Disk enabled, or buggy plugins that use the system time for parameter automation.\nIt will make rendering *very* slow.\n\nAre you sure you want to enable slow render?",
- "Really enable slow render?") == cnfNo)
- {
- m_bGivePlugsIdleTime = false;
- } else
- {
- showWarning = false;
- }
- }
- m_bChannelMode = IsDlgButtonChecked(IDC_CHECK4) != BST_UNCHECKED;
- m_bInstrumentMode= IsDlgButtonChecked(IDC_CHECK6) != BST_UNCHECKED;
- m_Settings.sampleSlot = static_cast<SAMPLEINDEX>(m_CbnSampleSlot.GetItemData(m_CbnSampleSlot.GetCurSel()));
- SaveEncoderSettings();
- EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
- m_Settings.Tags = FileTags();
- m_Settings.Tags.SetEncoder();
- if(encSettings.Tags)
- {
- CString tmp;
- m_EditTitle.GetWindowText(tmp);
- m_Settings.Tags.title = mpt::ToUnicode(tmp);
- m_EditAuthor.GetWindowText(tmp);
- m_Settings.Tags.artist = mpt::ToUnicode(tmp);
- m_EditAlbum.GetWindowText(tmp);
- m_Settings.Tags.album = mpt::ToUnicode(tmp);
- m_EditURL.GetWindowText(tmp);
- m_Settings.Tags.url = mpt::ToUnicode(tmp);
- if((encTraits->modesWithFixedGenres & encSettings.Mode) && !encTraits->genres.empty())
- {
- m_CbnGenre.GetWindowText(tmp);
- m_Settings.Tags.genre = mpt::ToUnicode(tmp);
- } else
- {
- m_EditGenre.GetWindowText(tmp);
- m_Settings.Tags.genre = mpt::ToUnicode(tmp);
- }
- m_EditYear.GetWindowText(tmp);
- m_Settings.Tags.year = mpt::ToUnicode(tmp);
- if(m_Settings.Tags.year == U_("0"))
- {
- m_Settings.Tags.year = mpt::ustring();
- }
- if(!m_SndFile.m_songMessage.empty())
- {
- m_Settings.Tags.comments = mpt::ToUnicode(mpt::Charset::Locale, m_SndFile.m_songMessage.GetFormatted(SongMessage::leLF));
- }
- m_Settings.Tags.bpm = mpt::ufmt::val(m_SndFile.GetCurrentBPM());
- SaveTags();
- }
- CDialog::OnOK();
- }
- void CWaveConvert::SaveEncoderSettings()
- {
- EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
- encSettings.Samplerate = static_cast<uint32>(m_CbnSampleRate.GetItemData(m_CbnSampleRate.GetCurSel()));
- encSettings.Channels = static_cast<uint16>(m_CbnChannels.GetItemData(m_CbnChannels.GetCurSel()));
- DWORD_PTR dwFormat = m_CbnSampleFormat.GetItemData(m_CbnSampleFormat.GetCurSel());
- if(encTraits->modes & Encoder::ModeLossless)
- {
- int format = (int)((dwFormat >> 0) & 0xffffff);
- encSettings.Dither = static_cast<int>(m_CbnDither.GetItemData(m_CbnDither.GetCurSel()));
- encSettings.Format2 = Encoder::Format::FromInt(format);
- encSettings.Mode = Encoder::ModeLossless;
- encSettings.Bitrate = 0;
- encSettings.Quality = encTraits->defaultQuality;
- } else
- {
- encSettings.Dither = static_cast<int>(m_CbnDither.GetItemData(m_CbnDither.GetCurSel()));
- Encoder::Mode mode = (Encoder::Mode)((dwFormat >> 24) & 0xff);
- int quality = (int)((dwFormat >> 0) & 0xff);
- int bitrate = (int)((dwFormat >> 0) & 0xffff);
- encSettings.Mode = mode;
- encSettings.Bitrate = bitrate;
- encSettings.Quality = static_cast<float>(quality) * 0.01f;
- encSettings.Format2 = { Encoder::Format::Encoding::Float, 32, mpt::get_endian() };
- }
-
- encSettings.Cues = IsDlgButtonChecked(IDC_CHECK3) ? true : false;
- encSettings.Tags = IsDlgButtonChecked(IDC_CHECK7) ? true : false;
- }
- std::size_t CWaveConvertSettings::FindEncoder(const mpt::ustring &name) const
- {
- for(std::size_t i = 0; i < EncoderFactories.size(); ++i)
- {
- if(EncoderFactories[i]->GetTraits().encoderSettingsName == name)
- {
- return i;
- }
- }
- return 0;
- }
- void CWaveConvertSettings::SelectEncoder(std::size_t index)
- {
- MPT_ASSERT(!EncoderFactories.empty());
- MPT_ASSERT(index < EncoderFactories.size());
- EncoderIndex = index;
- EncoderName = EncoderFactories[EncoderIndex]->GetTraits().encoderSettingsName;
- }
- EncoderFactoryBase *CWaveConvertSettings::GetEncoderFactory() const
- {
- MPT_ASSERT(!EncoderFactories.empty());
- return EncoderFactories[EncoderIndex];
- }
- const Encoder::Traits *CWaveConvertSettings::GetTraits() const
- {
- MPT_ASSERT(!EncoderFactories.empty());
- return &EncoderFactories[EncoderIndex]->GetTraits();
- }
- EncoderSettingsConf &CWaveConvertSettings::GetEncoderSettings() const
- {
- MPT_ASSERT(!EncoderSettings.empty());
- return *(EncoderSettings[EncoderIndex]);
- }
- Encoder::Settings CWaveConvertSettings::GetEncoderSettingsWithDetails() const
- {
- MPT_ASSERT(!EncoderSettings.empty());
- Encoder::Settings settings = static_cast<Encoder::Settings>(*(EncoderSettings[EncoderIndex]));
- settings.Details = static_cast<Encoder::StreamSettings>(TrackerSettings::Instance().ExportStreamEncoderSettings);
- return settings;
- }
- CWaveConvertSettings::CWaveConvertSettings(SettingsContainer &conf, const std::vector<EncoderFactoryBase*> &encFactories)
- : EncoderFactories(encFactories)
- , EncoderName(conf, U_("Export"), U_("Encoder"), U_(""))
- , EncoderIndex(FindEncoder(EncoderName))
- , storedTags(conf)
- , repeatCount(0)
- , minOrder(ORDERINDEX_INVALID), maxOrder(ORDERINDEX_INVALID)
- , sampleSlot(0)
- , normalize(false)
- , silencePlugBuffers(false)
- , outputToSample(false)
- {
- Tags.SetEncoder();
- for(const auto & factory : EncoderFactories)
- {
- const Encoder::Traits &encTraits = factory->GetTraits();
- EncoderSettings.push_back(
- std::make_unique<EncoderSettingsConf>(
- conf,
- encTraits.encoderSettingsName,
- encTraits.canCues,
- encTraits.canTags,
- encTraits.defaultSamplerate,
- encTraits.defaultChannels,
- encTraits.defaultMode,
- encTraits.defaultBitrate,
- encTraits.defaultQuality,
- encTraits.defaultFormat,
- encTraits.defaultDitherType
- )
- );
- }
- SelectEncoder(EncoderIndex);
- }
- /////////////////////////////////////////////////////////////////////////////////////////
- // CDoWaveConvert: save a mod as a wave file
- void CDoWaveConvert::Run()
- {
- UINT ok = IDOK;
- uint64 ullSamples = 0;
- std::vector<float> normalizeBufferData;
- float *normalizeBuffer = nullptr;
- float normalizePeak = 0.0f;
- const mpt::PathString normalizeFileName = mpt::CreateTempFileName(P_("OpenMPT"));
- std::optional<mpt::fstream> normalizeFile;
- if(m_Settings.normalize)
- {
- normalizeBufferData.resize(MIXBUFFERSIZE * 4);
- normalizeBuffer = normalizeBufferData.data();
- // Ensure this temporary file is marked as temporary in the file system, to increase the chance it will never be written to disk
- if(HANDLE hFile = ::CreateFile(normalizeFileName.AsNative().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL); hFile != INVALID_HANDLE_VALUE)
- {
- ::CloseHandle(hFile);
- }
- normalizeFile.emplace(normalizeFileName, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc);
- }
- const Encoder::Settings encSettings = m_Settings.GetEncoderSettingsWithDetails();
- const uint32 samplerate = encSettings.Samplerate;
- const uint16 channels = encSettings.Channels;
- ASSERT(m_Settings.GetEncoderFactory() && m_Settings.GetEncoderFactory()->IsAvailable());
- // Silence mix buffer of plugins, for plugins that don't clear their reverb buffers and similar stuff when they are reset
- #ifndef NO_PLUGINS
- if(m_Settings.silencePlugBuffers)
- {
- SetText(_T("Clearing plugin buffers"));
- for(auto &plug : m_SndFile.m_MixPlugins)
- {
- if(plug.pMixPlugin != nullptr)
- {
- // Render up to 20 seconds per plugin
- for(int j = 0; j < 20; j++)
- {
- const float maxVal = plug.pMixPlugin->RenderSilence(samplerate);
- if(maxVal <= FLT_EPSILON)
- {
- break;
- }
- }
- ProcessMessages();
- if(m_abort)
- {
- m_abort = false;
- break;
- }
- }
- }
- }
- #endif // NO_PLUGINS
- MixerSettings oldmixersettings = m_SndFile.m_MixerSettings;
- MixerSettings mixersettings = TrackerSettings::Instance().GetMixerSettings();
- mixersettings.m_nMaxMixChannels = MAX_CHANNELS; // always use max mixing channels when rendering
- mixersettings.gdwMixingFreq = samplerate;
- mixersettings.gnChannels = channels;
- m_SndFile.m_SongFlags.reset(SONG_PAUSED | SONG_STEP);
- if(m_Settings.normalize)
- {
- #ifndef NO_AGC
- mixersettings.DSPMask &= ~SNDDSP_AGC;
- #endif
- }
- DithersOpenMPT dithers(theApp.PRNG(), encSettings.Dither, encSettings.Channels);
- m_SndFile.ResetChannels();
- m_SndFile.SetMixerSettings(mixersettings);
- m_SndFile.SetResamplerSettings(TrackerSettings::Instance().GetResamplerSettings());
- m_SndFile.InitPlayer(true);
- // Tags must be known at the stream start,
- // so that the encoder class could write them before audio data if mandated by the format,
- // otherwise they should just be cached by the encoder.
- std::unique_ptr<IAudioStreamEncoder> fileEnc = m_Settings.GetEncoderFactory()->ConstructStreamEncoder(fileStream, encSettings, m_Settings.Tags);
- std::variant<
- std::unique_ptr<std::array<double, MIXBUFFERSIZE * 4>>,
- std::unique_ptr<std::array<float, MIXBUFFERSIZE * 4>>,
- std::unique_ptr<std::array<int32, MIXBUFFERSIZE * 4>>,
- std::unique_ptr<std::array<int24, MIXBUFFERSIZE * 4>>,
- std::unique_ptr<std::array<int16, MIXBUFFERSIZE * 4>>,
- std::unique_ptr<std::array<int8, MIXBUFFERSIZE * 4>>,
- std::unique_ptr<std::array<uint8, MIXBUFFERSIZE * 4>>> bufferData;
- union AnyBufferSamplePointer
- {
- double *float64;
- float *float32;
- int32 *int32;
- int24 *int24;
- int16 *int16;
- int8 *int8;
- uint8 *uint8;
- void *any;
- };
- AnyBufferSamplePointer buffer;
- buffer.any = nullptr;
- switch(fileEnc->GetSampleFormat())
- {
- case SampleFormat::Float64:
- bufferData = std::make_unique<std::array<double, MIXBUFFERSIZE * 4>>();
- buffer.float64 = std::get<0>(bufferData)->data();
- break;
- case SampleFormat::Float32:
- bufferData = std::make_unique<std::array<float, MIXBUFFERSIZE * 4>>();
- buffer.float32 = std::get<1>(bufferData)->data();
- break;
- case SampleFormat::Int32:
- bufferData = std::make_unique<std::array<int32, MIXBUFFERSIZE * 4>>();
- buffer.int32 = std::get<2>(bufferData)->data();
- break;
- case SampleFormat::Int24:
- bufferData = std::make_unique<std::array<int24, MIXBUFFERSIZE * 4>>();
- buffer.int24 = std::get<3>(bufferData)->data();
- break;
- case SampleFormat::Int16:
- bufferData = std::make_unique<std::array<int16, MIXBUFFERSIZE * 4>>();
- buffer.int16 = std::get<4>(bufferData)->data();
- break;
- case SampleFormat::Int8:
- bufferData = std::make_unique<std::array<int8, MIXBUFFERSIZE * 4>>();
- buffer.int8 = std::get<5>(bufferData)->data();
- break;
- case SampleFormat::Unsigned8:
- bufferData = std::make_unique<std::array<uint8, MIXBUFFERSIZE * 4>>();
- buffer.uint8 = std::get<6>(bufferData)->data();
- break;
- }
- uint64 ullMaxSamples = uint64_max / (channels * ((fileEnc->GetSampleFormat().GetBitsPerSample()+7) / 8));
- if (m_dwSongLimit)
- {
- LimitMax(ullMaxSamples, m_dwSongLimit * samplerate);
- }
- // Calculate maximum samples
- uint64 max = m_dwSongLimit ? ullMaxSamples : uint64_max;
- // Reset song position tracking
- m_SndFile.ResetPlayPos();
- m_SndFile.m_SongFlags.reset(SONG_PATTERNLOOP);
- ORDERINDEX startOrder = 0;
- GetLengthTarget target;
- if(m_Settings.minOrder != ORDERINDEX_INVALID && m_Settings.maxOrder != ORDERINDEX_INVALID)
- {
- m_SndFile.SetRepeatCount(0);
- startOrder = m_Settings.minOrder;
- ORDERINDEX endOrder = m_Settings.maxOrder;
- while(!m_SndFile.Order().IsValidPat(endOrder) && endOrder > startOrder)
- {
- endOrder--;
- }
- if(m_SndFile.Order().IsValidPat(endOrder))
- {
- target = GetLengthTarget(endOrder, m_SndFile.Patterns[m_SndFile.Order()[endOrder]].GetNumRows() - 1);
- }
- target.StartPos(m_SndFile.Order.GetCurrentSequenceIndex(), startOrder, 0);
- m_SndFile.m_nMaxOrderPosition = endOrder + 1;
- } else
- {
- m_SndFile.SetRepeatCount(std::max(0, m_Settings.repeatCount - 1));
- }
- uint64 l = mpt::saturate_round<uint64>(m_SndFile.GetLength(eNoAdjust, target).front().duration * samplerate * (1 + m_SndFile.GetRepeatCount()));
- m_SndFile.SetCurrentOrder(startOrder);
- m_SndFile.GetLength(eAdjust, GetLengthTarget(startOrder, 0)); // adjust playback variables / visited rows vector
- m_SndFile.m_PlayState.m_nCurrentOrder = startOrder;
- if (l < max) max = l;
- SetRange(0, max);
- EnableTaskbarProgress();
- // No pattern cue points yet
- std::vector<PatternCuePoint> patternCuePoints;
- patternCuePoints.reserve(m_SndFile.Order().size());
- m_SndFile.m_PatternCuePoints = &patternCuePoints;
- // Process the conversion
- // For calculating the remaining time
- auto dwStartTime = timeGetTime(), prevTime = dwStartTime;
- uint32 timeRemaining = 0;
- uint64 bytesWritten = 0;
- auto mainFrame = CMainFrame::GetMainFrame();
- mainFrame->PauseMod();
- m_SndFile.m_SongFlags.reset(SONG_STEP | SONG_PATTERNLOOP);
- mainFrame->InitRenderer(&m_SndFile);
- for (UINT n = 0; ; n++)
- {
- UINT lRead = 0;
- if(m_Settings.normalize)
- {
- lRead = ReadInterleaved(m_SndFile, normalizeBuffer, channels, MIXBUFFERSIZE, dithers);
- } else
- {
- switch(fileEnc->GetSampleFormat())
- {
- case SampleFormat::Float64:
- lRead = ReadInterleaved(m_SndFile, buffer.float64, channels, MIXBUFFERSIZE, dithers);
- break;
- case SampleFormat::Float32:
- lRead = ReadInterleaved(m_SndFile, buffer.float32, channels, MIXBUFFERSIZE, dithers);
- break;
- case SampleFormat::Int32:
- lRead = ReadInterleaved(m_SndFile, buffer.int32, channels, MIXBUFFERSIZE, dithers);
- break;
- case SampleFormat::Int24:
- lRead = ReadInterleaved(m_SndFile, buffer.int24, channels, MIXBUFFERSIZE, dithers);
- break;
- case SampleFormat::Int16:
- lRead = ReadInterleaved(m_SndFile, buffer.int16, channels, MIXBUFFERSIZE, dithers);
- break;
- case SampleFormat::Int8:
- lRead = ReadInterleaved(m_SndFile, buffer.int8, channels, MIXBUFFERSIZE, dithers);
- break;
- case SampleFormat::Unsigned8:
- lRead = ReadInterleaved(m_SndFile, buffer.uint8, channels, MIXBUFFERSIZE, dithers);
- break;
- }
- }
- // Process cue points (add base offset), if there are any to process.
- for(auto iter = patternCuePoints.rbegin(); iter != patternCuePoints.rend(); ++iter)
- {
- if(iter->processed)
- {
- // From this point, all cues have already been processed.
- break;
- }
- iter->offset += ullSamples;
- iter->processed = true;
- }
- if (m_bGivePlugsIdleTime)
- {
- Sleep(20);
- }
- if (!lRead)
- break;
- ullSamples += lRead;
- if(m_Settings.normalize)
- {
- std::size_t countSamples = lRead * m_SndFile.m_MixerSettings.gnChannels;
- const float *src = normalizeBuffer;
- while(countSamples--)
- {
- const float val = *src;
- if(val > normalizePeak) normalizePeak = val;
- else if(0.0f - val >= normalizePeak) normalizePeak = 0.0f - val;
- src++;
- }
- if(!mpt::IO::WriteRaw(*normalizeFile, mpt::as_span(reinterpret_cast<const std::byte*>(normalizeBuffer), lRead * m_SndFile.m_MixerSettings.gnChannels * sizeof(float))))
- {
- break;
- }
- } else
- {
- const std::streampos oldPos = fileStream.tellp();
- switch(fileEnc->GetSampleFormat())
- {
- case SampleFormat::Float64:
- fileEnc->WriteInterleaved(lRead, buffer.float64);
- break;
- case SampleFormat::Float32:
- fileEnc->WriteInterleaved(lRead, buffer.float32);
- break;
- case SampleFormat::Int32:
- fileEnc->WriteInterleaved(lRead, buffer.int32);
- break;
- case SampleFormat::Int24:
- fileEnc->WriteInterleaved(lRead, buffer.int24);
- break;
- case SampleFormat::Int16:
- fileEnc->WriteInterleaved(lRead, buffer.int16);
- break;
- case SampleFormat::Int8:
- fileEnc->WriteInterleaved(lRead, buffer.int8);
- break;
- case SampleFormat::Unsigned8:
- fileEnc->WriteInterleaved(lRead, buffer.uint8);
- break;
- }
- const std::streampos newPos = fileStream.tellp();
- bytesWritten += static_cast<uint64>(newPos - oldPos);
- if(!fileStream)
- {
- break;
- }
- }
- if(m_dwSongLimit && (ullSamples >= ullMaxSamples))
- {
- break;
- }
- auto currentTime = timeGetTime();
- if((currentTime - prevTime) >= 16)
- {
- prevTime = currentTime;
- DWORD seconds = (DWORD)(ullSamples / m_SndFile.m_MixerSettings.gdwMixingFreq);
- if((ullSamples > 0) && (ullSamples < max))
- {
- timeRemaining = static_cast<uint32>((timeRemaining + ((currentTime - dwStartTime) * (max - ullSamples) / ullSamples) / 1000) / 2);
- }
- if(m_Settings.normalize)
- {
- SetText(MPT_CFORMAT("Rendering {}... ({}mn{}s, {}mn{}s remaining)")(caption, seconds / 60, mpt::ufmt::dec0<2>(seconds % 60u), timeRemaining / 60, mpt::ufmt::dec0<2>(timeRemaining % 60u)));
- } else
- {
- SetText(MPT_CFORMAT("Writing {}... ({}kB, {}mn{}s, {}mn{}s remaining)")(caption, bytesWritten >> 10, seconds / 60, mpt::ufmt::dec0<2>(seconds % 60u), timeRemaining / 60, mpt::ufmt::dec0<2>(timeRemaining % 60u)));
- }
- SetProgress(ullSamples);
- }
- ProcessMessages();
- if (m_abort)
- {
- ok = IDCANCEL;
- break;
- }
- }
- m_SndFile.m_nMaxOrderPosition = 0;
- mainFrame->StopRenderer(&m_SndFile);
- if(m_Settings.normalize)
- {
- const float normalizeFactor = (normalizePeak != 0.0f) ? (1.0f / normalizePeak) : 1.0f;
- const uint64 framesTotal = ullSamples;
- int lastPercent = -1;
- mpt::IO::SeekAbsolute(*normalizeFile, 0);
- uint64 framesProcessed = 0;
- uint64 framesToProcess = framesTotal;
- SetRange(0, framesTotal);
- while(framesToProcess)
- {
- const std::size_t framesChunk = std::min(mpt::saturate_cast<std::size_t>(framesToProcess), std::size_t(MIXBUFFERSIZE));
- const uint32 samplesChunk = static_cast<uint32>(framesChunk * channels);
-
- const std::size_t bytes = samplesChunk * sizeof(float);
- if(mpt::IO::ReadRaw(*normalizeFile, mpt::as_span(reinterpret_cast<std::byte*>(normalizeBuffer), bytes)).size() != bytes)
- {
- break;
- }
- for(std::size_t i = 0; i < samplesChunk; ++i)
- {
- normalizeBuffer[i] *= normalizeFactor;
- }
- const std::streampos oldPos = fileStream.tellp();
- std::visit(
- [&](auto& ditherInstance)
- {
- switch(fileEnc->GetSampleFormat())
- {
- case SampleFormat::Unsigned8:
- ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<uint8>(buffer.uint8, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
- break;
- case SampleFormat::Int8:
- ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<int8>(buffer.int8, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
- break;
- case SampleFormat::Int16:
- ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<int16>(buffer.int16, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
- break;
- case SampleFormat::Int24:
- ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<int24>(buffer.int24, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
- break;
- case SampleFormat::Int32:
- ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<int32>(buffer.int32, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
- break;
- case SampleFormat::Float32:
- ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<float>(buffer.float32, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
- break;
- case SampleFormat::Float64:
- ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<double>(buffer.float64, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
- break;
- default: MPT_ASSERT_NOTREACHED(); break;
- }
- },
- dithers.Variant()
- );
- switch(fileEnc->GetSampleFormat())
- {
- case SampleFormat::Float64:
- fileEnc->WriteInterleaved(framesChunk, buffer.float64);
- break;
- case SampleFormat::Float32:
- fileEnc->WriteInterleaved(framesChunk, buffer.float32);
- break;
- case SampleFormat::Int32:
- fileEnc->WriteInterleaved(framesChunk, buffer.int32);
- break;
- case SampleFormat::Int24:
- fileEnc->WriteInterleaved(framesChunk, buffer.int24);
- break;
- case SampleFormat::Int16:
- fileEnc->WriteInterleaved(framesChunk, buffer.int16);
- break;
- case SampleFormat::Int8:
- fileEnc->WriteInterleaved(framesChunk, buffer.int8);
- break;
- case SampleFormat::Unsigned8:
- fileEnc->WriteInterleaved(framesChunk, buffer.uint8);
- break;
- }
- const std::streampos newPos = fileStream.tellp();
- bytesWritten += static_cast<std::size_t>(newPos - oldPos);
- auto currentTime = timeGetTime();
- if((currentTime - prevTime) >= 16)
- {
- prevTime = currentTime;
-
- int percent = static_cast<int>(100 * framesProcessed / framesTotal);
- if(percent != lastPercent)
- {
- SetText(MPT_CFORMAT("Normalizing... ({}%)")(percent));
- SetProgress(framesProcessed);
- lastPercent = percent;
- }
- ProcessMessages();
- }
- framesProcessed += framesChunk;
- framesToProcess -= framesChunk;
- }
- mpt::IO::Flush(*normalizeFile);
- normalizeFile.reset();
- for(int retry=0; retry<10; retry++)
- {
- // stupid virus scanners
- if(DeleteFile(normalizeFileName.AsNative().c_str()) != EACCES)
- {
- break;
- }
- Sleep(10);
- }
- }
- if(!patternCuePoints.empty())
- {
- if(encSettings.Cues)
- {
- std::vector<uint64> cues;
- cues.reserve(patternCuePoints.size());
- for(const auto &cue : patternCuePoints)
- {
- cues.push_back(static_cast<uint32>(cue.offset));
- }
- fileEnc->WriteCues(cues);
- }
- }
- m_SndFile.m_PatternCuePoints = nullptr;
- fileEnc->WriteFinalize();
- fileEnc = nullptr;
- CMainFrame::UpdateAudioParameters(m_SndFile, TRUE);
- EndDialog(ok);
- }
- OPENMPT_NAMESPACE_END
|