Mod2wave.cpp 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401
  1. /*
  2. * mod2wave.cpp
  3. * ------------
  4. * Purpose: Module to WAV conversion (dialog + conversion code).
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #include "Mptrack.h"
  11. #include "Sndfile.h"
  12. #include "Dlsbank.h"
  13. #include "Mainfrm.h"
  14. #include "Mpdlgs.h"
  15. #include "mod2wave.h"
  16. #include "WAVTools.h"
  17. #include "../common/mptString.h"
  18. #include "../common/version.h"
  19. #include "../soundlib/MixerLoops.h"
  20. #include "openmpt/soundbase/Dither.hpp"
  21. #include "../common/Dither.h"
  22. #include "../soundlib/AudioReadTarget.h"
  23. #include "../soundlib/plugins/PlugInterface.h"
  24. #include "../common/mptFileIO.h"
  25. #include "mpt/audio/span.hpp"
  26. #include <variant>
  27. #include "mpt/io/io.hpp"
  28. #include "mpt/io/io_stdstream.hpp"
  29. OPENMPT_NAMESPACE_BEGIN
  30. extern const TCHAR *gszChnCfgNames[3];
  31. template <typename Tsample>
  32. static CSoundFile::samplecount_t ReadInterleaved(CSoundFile &sndFile, Tsample *outputBuffer, std::size_t channels, CSoundFile::samplecount_t count, DithersOpenMPT &dithers)
  33. {
  34. sndFile.ResetMixStat();
  35. MPT_ASSERT(sndFile.m_MixerSettings.gnChannels == channels);
  36. AudioTargetBuffer<mpt::audio_span_interleaved<Tsample>, DithersOpenMPT> target(mpt::audio_span_interleaved<Tsample>(outputBuffer, channels, count), dithers);
  37. return sndFile.Read(count, target);
  38. }
  39. ///////////////////////////////////////////////////
  40. // CWaveConvert - setup for converting a wave file
  41. BEGIN_MESSAGE_MAP(CWaveConvert, CDialog)
  42. ON_COMMAND(IDC_CHECK2, &CWaveConvert::OnCheckTimeLimit)
  43. ON_COMMAND(IDC_CHECK4, &CWaveConvert::OnCheckChannelMode)
  44. ON_COMMAND(IDC_CHECK6, &CWaveConvert::OnCheckInstrMode)
  45. ON_COMMAND(IDC_RADIO1, &CWaveConvert::UpdateDialog)
  46. ON_COMMAND(IDC_RADIO2, &CWaveConvert::UpdateDialog)
  47. ON_COMMAND(IDC_RADIO3, &CWaveConvert::UpdateDialog)
  48. ON_COMMAND(IDC_RADIO4, &CWaveConvert::OnExportModeChanged)
  49. ON_COMMAND(IDC_RADIO5, &CWaveConvert::OnExportModeChanged)
  50. ON_COMMAND(IDC_PLAYEROPTIONS, &CWaveConvert::OnPlayerOptions)
  51. ON_CBN_SELCHANGE(IDC_COMBO5, &CWaveConvert::OnFileTypeChanged)
  52. ON_CBN_SELCHANGE(IDC_COMBO1, &CWaveConvert::OnSamplerateChanged)
  53. ON_CBN_SELCHANGE(IDC_COMBO4, &CWaveConvert::OnChannelsChanged)
  54. ON_CBN_SELCHANGE(IDC_COMBO6, &CWaveConvert::OnDitherChanged)
  55. ON_CBN_SELCHANGE(IDC_COMBO2, &CWaveConvert::OnFormatChanged)
  56. ON_CBN_SELCHANGE(IDC_COMBO9, &CWaveConvert::OnSampleSlotChanged)
  57. END_MESSAGE_MAP()
  58. CWaveConvert::CWaveConvert(CWnd *parent, ORDERINDEX minOrder, ORDERINDEX maxOrder, ORDERINDEX numOrders, CSoundFile &sndFile, const std::vector<EncoderFactoryBase*> &encFactories)
  59. : CDialog(IDD_WAVECONVERT, parent)
  60. , m_Settings(theApp.GetSettings(), encFactories)
  61. , m_SndFile(sndFile)
  62. {
  63. ASSERT(!encFactories.empty());
  64. encTraits = m_Settings.GetTraits();
  65. m_bGivePlugsIdleTime = false;
  66. if(minOrder != ORDERINDEX_INVALID && maxOrder != ORDERINDEX_INVALID)
  67. {
  68. // render selection
  69. m_Settings.minOrder = minOrder;
  70. m_Settings.maxOrder = maxOrder;
  71. }
  72. m_Settings.repeatCount = 1;
  73. m_Settings.minSequence = m_Settings.maxSequence = m_SndFile.Order.GetCurrentSequenceIndex();
  74. m_nNumOrders = numOrders;
  75. m_dwSongLimit = 0;
  76. }
  77. void CWaveConvert::DoDataExchange(CDataExchange *pDX)
  78. {
  79. CDialog::DoDataExchange(pDX);
  80. DDX_Control(pDX, IDC_COMBO5, m_CbnFileType);
  81. DDX_Control(pDX, IDC_COMBO1, m_CbnSampleRate);
  82. DDX_Control(pDX, IDC_COMBO4, m_CbnChannels);
  83. DDX_Control(pDX, IDC_COMBO6, m_CbnDither);
  84. DDX_Control(pDX, IDC_COMBO2, m_CbnSampleFormat);
  85. DDX_Control(pDX, IDC_SPIN3, m_SpinMinOrder);
  86. DDX_Control(pDX, IDC_SPIN4, m_SpinMaxOrder);
  87. DDX_Control(pDX, IDC_SPIN5, m_SpinLoopCount);
  88. DDX_Control(pDX, IDC_SPIN6, m_SpinMinSequence);
  89. DDX_Control(pDX, IDC_SPIN7, m_SpinMaxSequence);
  90. DDX_Control(pDX, IDC_COMBO9, m_CbnSampleSlot);
  91. DDX_Control(pDX, IDC_COMBO3, m_CbnGenre);
  92. DDX_Control(pDX, IDC_EDIT10, m_EditGenre);
  93. DDX_Control(pDX, IDC_EDIT11, m_EditTitle);
  94. DDX_Control(pDX, IDC_EDIT6, m_EditAuthor);
  95. DDX_Control(pDX, IDC_EDIT7, m_EditAlbum);
  96. DDX_Control(pDX, IDC_EDIT8, m_EditURL);
  97. DDX_Control(pDX, IDC_EDIT9, m_EditYear);
  98. }
  99. BOOL CWaveConvert::OnInitDialog()
  100. {
  101. CDialog::OnInitDialog();
  102. CheckDlgButton(IDC_CHECK5, BST_UNCHECKED); // Normalize
  103. CheckDlgButton(IDC_CHECK3, BST_CHECKED); // Cue points
  104. CheckDlgButton(IDC_CHECK4, BST_UNCHECKED);
  105. CheckDlgButton(IDC_CHECK6, BST_UNCHECKED);
  106. const bool selection = (m_Settings.minOrder != ORDERINDEX_INVALID && m_Settings.maxOrder != ORDERINDEX_INVALID);
  107. CheckRadioButton(IDC_RADIO1, IDC_RADIO3, selection ? IDC_RADIO2 : IDC_RADIO1);
  108. if(selection)
  109. {
  110. SetDlgItemInt(IDC_EDIT3, m_Settings.minOrder);
  111. SetDlgItemInt(IDC_EDIT4, m_Settings.maxOrder);
  112. }
  113. m_SpinMinOrder.SetRange32(0, m_nNumOrders);
  114. m_SpinMaxOrder.SetRange32(0, m_nNumOrders);
  115. const SEQUENCEINDEX numSequences = m_SndFile.Order.GetNumSequences();
  116. const BOOL enableSeq = numSequences > 1 ? TRUE : FALSE;
  117. GetDlgItem(IDC_RADIO3)->EnableWindow(enableSeq);
  118. m_SpinMinSequence.SetRange32(1, numSequences);
  119. m_SpinMaxSequence.SetRange32(1, numSequences);
  120. SetDlgItemInt(IDC_EDIT12, m_Settings.minSequence + 1);
  121. SetDlgItemInt(IDC_EDIT13, m_Settings.maxSequence + 1);
  122. SetDlgItemInt(IDC_EDIT5, m_Settings.repeatCount, FALSE);
  123. m_SpinLoopCount.SetRange32(1, int16_max);
  124. FillFileTypes();
  125. FillSamplerates();
  126. FillChannels();
  127. FillFormats();
  128. FillDither();
  129. LoadTags();
  130. m_EditYear.SetLimitText(4);
  131. m_EditTitle.SetWindowText(mpt::ToCString(m_Settings.Tags.title));
  132. m_EditAuthor.SetWindowText(mpt::ToCString(m_Settings.Tags.artist));
  133. m_EditURL.SetWindowText(mpt::ToCString(m_Settings.Tags.url));
  134. m_EditAlbum.SetWindowText(mpt::ToCString(m_Settings.Tags.album));
  135. m_EditYear.SetWindowText(mpt::ToCString(m_Settings.Tags.year));
  136. m_EditGenre.SetWindowText(mpt::ToCString(m_Settings.Tags.genre));
  137. FillTags();
  138. // Plugin quirk options are only available if there are any plugins loaded.
  139. GetDlgItem(IDC_GIVEPLUGSIDLETIME)->EnableWindow(FALSE);
  140. GetDlgItem(IDC_RENDERSILENCE)->EnableWindow(FALSE);
  141. #ifndef NO_PLUGINS
  142. for(const auto &plug : m_SndFile.m_MixPlugins)
  143. {
  144. if(plug.pMixPlugin != nullptr)
  145. {
  146. GetDlgItem(IDC_GIVEPLUGSIDLETIME)->EnableWindow(TRUE);
  147. GetDlgItem(IDC_RENDERSILENCE)->EnableWindow(TRUE);
  148. break;
  149. }
  150. }
  151. #endif // NO_PLUGINS
  152. // Fill list of sample slots to render into
  153. if(m_SndFile.GetNextFreeSample() != SAMPLEINDEX_INVALID)
  154. {
  155. m_CbnSampleSlot.SetItemData(m_CbnSampleSlot.AddString(_T("<empty slot>")), 0);
  156. }
  157. CString s;
  158. for(SAMPLEINDEX smp = 1; smp <= m_SndFile.GetNumSamples(); smp++)
  159. {
  160. s.Format(_T("%02u: %s%s"), smp, m_SndFile.GetSample(smp).HasSampleData() ? _T("*") : _T(""), mpt::ToCString(m_SndFile.GetCharsetInternal(), m_SndFile.GetSampleName(smp)).GetString());
  161. m_CbnSampleSlot.SetItemData(m_CbnSampleSlot.AddString(s), smp);
  162. }
  163. if(m_Settings.sampleSlot > m_SndFile.GetNumSamples()) m_Settings.sampleSlot = 0;
  164. m_CbnSampleSlot.SetCurSel(m_Settings.sampleSlot);
  165. CheckRadioButton(IDC_RADIO4, IDC_RADIO5, m_Settings.outputToSample ? IDC_RADIO5 : IDC_RADIO4);
  166. UpdateDialog();
  167. return TRUE;
  168. }
  169. void CWaveConvert::LoadTags()
  170. {
  171. m_Settings.Tags.title = mpt::ToUnicode(mpt::Charset::Locale, m_SndFile.GetTitle());
  172. m_Settings.Tags.comments = mpt::ToUnicode(mpt::Charset::Locale, m_SndFile.m_songMessage.GetFormatted(SongMessage::leLF));
  173. m_Settings.Tags.artist = m_SndFile.m_songArtist;
  174. m_Settings.Tags.album = m_Settings.storedTags.album;
  175. m_Settings.Tags.trackno = m_Settings.storedTags.trackno;
  176. m_Settings.Tags.year = m_Settings.storedTags.year;
  177. m_Settings.Tags.url = m_Settings.storedTags.url;
  178. m_Settings.Tags.genre = m_Settings.storedTags.genre;
  179. }
  180. void CWaveConvert::SaveTags()
  181. {
  182. m_Settings.storedTags.artist = m_Settings.Tags.artist;
  183. m_Settings.storedTags.album = m_Settings.Tags.album;
  184. m_Settings.storedTags.trackno = m_Settings.Tags.trackno;
  185. m_Settings.storedTags.year = m_Settings.Tags.year;
  186. m_Settings.storedTags.url = m_Settings.Tags.url;
  187. m_Settings.storedTags.genre = m_Settings.Tags.genre;
  188. }
  189. void CWaveConvert::FillTags()
  190. {
  191. EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
  192. DWORD_PTR dwFormat = m_CbnSampleFormat.GetItemData(m_CbnSampleFormat.GetCurSel());
  193. Encoder::Mode mode = (Encoder::Mode)((dwFormat >> 24) & 0xff);
  194. CheckDlgButton(IDC_CHECK3, encTraits->canCues?encSettings.Cues?TRUE:FALSE:FALSE);
  195. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_CHECK3), encTraits->canCues?TRUE:FALSE);
  196. const BOOL canTags = encTraits->canTags ? TRUE : FALSE;
  197. CheckDlgButton(IDC_CHECK7, encSettings.Tags ? canTags : FALSE);
  198. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_CHECK7), canTags);
  199. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_COMBO3), canTags);
  200. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT11), canTags);
  201. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT6), canTags);
  202. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT7), canTags);
  203. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT8), canTags);
  204. ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT9), canTags);
  205. m_CbnGenre.EnableWindow(canTags?TRUE:FALSE);
  206. m_EditGenre.EnableWindow(canTags?TRUE:FALSE);
  207. if((encTraits->modesWithFixedGenres & mode) && !encTraits->genres.empty())
  208. {
  209. m_EditGenre.ShowWindow(SW_HIDE);
  210. m_CbnGenre.ShowWindow(SW_SHOW);
  211. m_EditGenre.Clear();
  212. m_CbnGenre.ResetContent();
  213. m_CbnGenre.AddString(_T(""));
  214. for(const auto &genre : encTraits->genres)
  215. {
  216. m_CbnGenre.AddString(mpt::ToCString(genre));
  217. }
  218. } else
  219. {
  220. m_CbnGenre.ShowWindow(SW_HIDE);
  221. m_EditGenre.ShowWindow(SW_SHOW);
  222. m_CbnGenre.ResetContent();
  223. m_EditGenre.Clear();
  224. }
  225. }
  226. void CWaveConvert::FillFileTypes()
  227. {
  228. m_CbnFileType.ResetContent();
  229. int sel = 0;
  230. for(std::size_t i = 0; i < m_Settings.EncoderFactories.size(); ++i)
  231. {
  232. int ndx = m_CbnFileType.AddString(MPT_CFORMAT("{} ({})")(mpt::ToCString(m_Settings.EncoderFactories[i]->GetTraits().fileShortDescription), mpt::ToCString(m_Settings.EncoderFactories[i]->GetTraits().fileDescription)));
  233. m_CbnFileType.SetItemData(ndx, i);
  234. if(m_Settings.EncoderIndex == i)
  235. {
  236. sel = ndx;
  237. }
  238. }
  239. m_CbnFileType.SetCurSel(sel);
  240. }
  241. void CWaveConvert::FillSamplerates()
  242. {
  243. EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
  244. m_CbnSampleRate.CComboBox::ResetContent();
  245. int sel = -1;
  246. if(TrackerSettings::Instance().ExportDefaultToSoundcardSamplerate)
  247. {
  248. for(auto samplerate : encTraits->samplerates)
  249. {
  250. if(samplerate == TrackerSettings::Instance().MixerSamplerate)
  251. {
  252. encSettings.Samplerate = samplerate;
  253. }
  254. }
  255. }
  256. for(auto samplerate : encTraits->samplerates)
  257. {
  258. int ndx = m_CbnSampleRate.AddString(MPT_CFORMAT("{} Hz")(samplerate));
  259. m_CbnSampleRate.SetItemData(ndx, samplerate);
  260. if(samplerate == encSettings.Samplerate)
  261. {
  262. sel = ndx;
  263. }
  264. }
  265. if(sel == -1)
  266. {
  267. sel = 0;
  268. }
  269. m_CbnSampleRate.SetCurSel(sel);
  270. }
  271. void CWaveConvert::FillChannels()
  272. {
  273. EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
  274. m_CbnChannels.CComboBox::ResetContent();
  275. int sel = 0;
  276. for(int channels = 4; channels >= 1; channels /= 2)
  277. {
  278. if(channels > encTraits->maxChannels)
  279. {
  280. continue;
  281. }
  282. if(IsDlgButtonChecked(IDC_RADIO5) != BST_UNCHECKED)
  283. {
  284. if(channels > 2)
  285. {
  286. // sample export only supports 2 channels max
  287. continue;
  288. }
  289. }
  290. int ndx = m_CbnChannels.AddString(gszChnCfgNames[(channels+2)/2-1]);
  291. m_CbnChannels.SetItemData(ndx, channels);
  292. if(channels == encSettings.Channels)
  293. {
  294. sel = ndx;
  295. }
  296. }
  297. m_CbnChannels.SetCurSel(sel);
  298. }
  299. void CWaveConvert::FillFormats()
  300. {
  301. EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
  302. m_CbnSampleFormat.CComboBox::ResetContent();
  303. int sel = -1;
  304. int samplerate = static_cast<int>(m_CbnSampleRate.GetItemData(m_CbnSampleRate.GetCurSel()));
  305. int channels = static_cast<int>(m_CbnChannels.GetItemData(m_CbnChannels.GetCurSel()));
  306. if(encTraits->modes & Encoder::ModeQuality)
  307. {
  308. for(int quality = 100; quality >= 0; quality -= 10)
  309. {
  310. int ndx = m_CbnSampleFormat.AddString(mpt::ToCString(m_Settings.GetEncoderFactory()->DescribeQuality(quality * 0.01f)));
  311. m_CbnSampleFormat.SetItemData(ndx, (Encoder::ModeQuality<<24) | (quality<<0));
  312. if(encSettings.Mode == Encoder::ModeQuality && mpt::saturate_round<int>(encSettings.Quality*100.0f) == quality)
  313. {
  314. sel = ndx;
  315. }
  316. }
  317. }
  318. if(encTraits->modes & Encoder::ModeVBR)
  319. {
  320. for(int bitrate = static_cast<int>(encTraits->bitrates.size()-1); bitrate >= 0; --bitrate)
  321. {
  322. if(!m_Settings.GetEncoderFactory()->IsBitrateSupported(samplerate, channels, encTraits->bitrates[bitrate]))
  323. {
  324. continue;
  325. }
  326. int ndx = m_CbnSampleFormat.AddString(mpt::ToCString(m_Settings.GetEncoderFactory()->DescribeBitrateVBR(encTraits->bitrates[bitrate])));
  327. m_CbnSampleFormat.SetItemData(ndx, (Encoder::ModeVBR<<24) | (encTraits->bitrates[bitrate]<<0));
  328. if(encSettings.Mode == Encoder::ModeVBR && static_cast<int>(encSettings.Bitrate) == encTraits->bitrates[bitrate])
  329. {
  330. sel = ndx;
  331. }
  332. }
  333. }
  334. if(encTraits->modes & Encoder::ModeABR)
  335. {
  336. for(int bitrate = static_cast<int>(encTraits->bitrates.size()-1); bitrate >= 0; --bitrate)
  337. {
  338. if(!m_Settings.GetEncoderFactory()->IsBitrateSupported(samplerate, channels, encTraits->bitrates[bitrate]))
  339. {
  340. continue;
  341. }
  342. int ndx = m_CbnSampleFormat.AddString(mpt::ToCString(m_Settings.GetEncoderFactory()->DescribeBitrateABR(encTraits->bitrates[bitrate])));
  343. m_CbnSampleFormat.SetItemData(ndx, (Encoder::ModeABR<<24) | (encTraits->bitrates[bitrate]<<0));
  344. if(encSettings.Mode == Encoder::ModeABR && static_cast<int>(encSettings.Bitrate) == encTraits->bitrates[bitrate])
  345. {
  346. sel = ndx;
  347. }
  348. }
  349. }
  350. if(encTraits->modes & Encoder::ModeCBR)
  351. {
  352. for(int bitrate = static_cast<int>(encTraits->bitrates.size()-1); bitrate >= 0; --bitrate)
  353. {
  354. if(!m_Settings.GetEncoderFactory()->IsBitrateSupported(samplerate, channels, encTraits->bitrates[bitrate]))
  355. {
  356. continue;
  357. }
  358. int ndx = m_CbnSampleFormat.AddString(mpt::ToCString(m_Settings.GetEncoderFactory()->DescribeBitrateCBR(encTraits->bitrates[bitrate])));
  359. m_CbnSampleFormat.SetItemData(ndx, (Encoder::ModeCBR<<24) | (encTraits->bitrates[bitrate]<<0));
  360. if(encSettings.Mode == Encoder::ModeCBR && static_cast<int>(encSettings.Bitrate) == encTraits->bitrates[bitrate])
  361. {
  362. sel = ndx;
  363. }
  364. }
  365. }
  366. if(encTraits->modes & Encoder::ModeLossless)
  367. {
  368. bool allBig = true;
  369. bool allLittle = true;
  370. for(const auto &format : encTraits->formats)
  371. {
  372. if(format.endian != mpt::endian::little)
  373. {
  374. allLittle = false;
  375. }
  376. if(format.endian != mpt::endian::big)
  377. {
  378. allBig = false;
  379. }
  380. }
  381. bool showEndian = !(allBig || allLittle);
  382. for(std::size_t i = 0; i < encTraits->formats.size(); ++i)
  383. {
  384. const Encoder::Format &format = encTraits->formats[i];
  385. mpt::ustring description;
  386. switch(format.encoding)
  387. {
  388. case Encoder::Format::Encoding::Float:
  389. description = MPT_UFORMAT("{} Bit Floating Point")(format.bits);
  390. break;
  391. case Encoder::Format::Encoding::Integer:
  392. description = MPT_UFORMAT("{} Bit")(format.bits);
  393. break;
  394. case Encoder::Format::Encoding::Alaw:
  395. description = U_("A-law");
  396. break;
  397. case Encoder::Format::Encoding::ulaw:
  398. description = MPT_UTF8("\xce\xbc-law");
  399. break;
  400. case Encoder::Format::Encoding::Unsigned:
  401. description = MPT_UFORMAT("{} Bit (unsigned)")(format.bits);
  402. break;
  403. }
  404. if(showEndian && format.bits != 8 && format.encoding != Encoder::Format::Encoding::Alaw && format.encoding != Encoder::Format::Encoding::ulaw)
  405. {
  406. switch(format.endian)
  407. {
  408. case mpt::endian::big:
  409. description += U_(" Big-Endian");
  410. break;
  411. case mpt::endian::little:
  412. description += U_(" Little-Endian");
  413. break;
  414. }
  415. }
  416. int ndx = m_CbnSampleFormat.AddString(mpt::ToCString(description));
  417. m_CbnSampleFormat.SetItemData(ndx, format.AsInt());
  418. if(encSettings.Mode & Encoder::ModeLossless && format == encSettings.Format2)
  419. {
  420. sel = ndx;
  421. }
  422. }
  423. }
  424. if(sel == -1)
  425. {
  426. sel = 0;
  427. }
  428. m_CbnSampleFormat.SetCurSel(sel);
  429. }
  430. void CWaveConvert::FillDither()
  431. {
  432. EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
  433. m_CbnDither.CComboBox::ResetContent();
  434. int format = m_CbnSampleFormat.GetItemData(m_CbnSampleFormat.GetCurSel()) & 0xffffff;
  435. if((encTraits->modes & Encoder::ModeLossless) && Encoder::Format::FromInt(format).GetSampleFormat() != SampleFormat::Invalid && !Encoder::Format::FromInt(format).GetSampleFormat().IsFloat())
  436. {
  437. m_CbnDither.EnableWindow(TRUE);
  438. for(std::size_t dither = 0; dither < DithersOpenMPT::GetNumDithers(); ++dither)
  439. {
  440. int ndx = m_CbnDither.AddString(mpt::ToCString(DithersOpenMPT::GetModeName(dither) + U_(" dither")));
  441. m_CbnDither.SetItemData(ndx, dither);
  442. }
  443. } else
  444. {
  445. m_CbnDither.EnableWindow(FALSE);
  446. for(std::size_t dither = 0; dither < DithersOpenMPT::GetNumDithers(); ++dither)
  447. {
  448. int ndx = m_CbnDither.AddString(mpt::ToCString(DithersOpenMPT::GetModeName(DithersOpenMPT::GetNoDither()) + U_(" dither")));
  449. m_CbnDither.SetItemData(ndx, dither);
  450. }
  451. }
  452. m_CbnDither.SetCurSel(encSettings.Dither);
  453. }
  454. void CWaveConvert::OnFileTypeChanged()
  455. {
  456. SaveEncoderSettings();
  457. DWORD_PTR dwFileType = m_CbnFileType.GetItemData(m_CbnFileType.GetCurSel());
  458. m_Settings.SelectEncoder(dwFileType);
  459. encTraits = m_Settings.GetTraits();
  460. FillSamplerates();
  461. FillChannels();
  462. FillFormats();
  463. FillDither();
  464. FillTags();
  465. }
  466. void CWaveConvert::OnSamplerateChanged()
  467. {
  468. SaveEncoderSettings();
  469. FillFormats();
  470. FillDither();
  471. }
  472. void CWaveConvert::OnChannelsChanged()
  473. {
  474. SaveEncoderSettings();
  475. FillFormats();
  476. FillDither();
  477. }
  478. void CWaveConvert::OnDitherChanged()
  479. {
  480. SaveEncoderSettings();
  481. }
  482. void CWaveConvert::OnFormatChanged()
  483. {
  484. SaveEncoderSettings();
  485. FillDither();
  486. FillTags();
  487. }
  488. void CWaveConvert::UpdateDialog()
  489. {
  490. CheckDlgButton(IDC_CHECK2, (m_dwSongLimit) ? BST_CHECKED : 0);
  491. GetDlgItem(IDC_EDIT2)->EnableWindow(m_dwSongLimit ? TRUE : FALSE);
  492. // Repeat / selection play
  493. int sel = GetCheckedRadioButton(IDC_RADIO1, IDC_RADIO3);
  494. GetDlgItem(IDC_EDIT3)->EnableWindow(sel == IDC_RADIO2);
  495. GetDlgItem(IDC_EDIT4)->EnableWindow(sel == IDC_RADIO2);
  496. m_SpinMinOrder.EnableWindow(sel == IDC_RADIO2);
  497. m_SpinMaxOrder.EnableWindow(sel == IDC_RADIO2);
  498. GetDlgItem(IDC_EDIT5)->EnableWindow(sel == IDC_RADIO1);
  499. m_SpinLoopCount.EnableWindow(sel == IDC_RADIO1);
  500. const SEQUENCEINDEX numSequences = m_SndFile.Order.GetNumSequences();
  501. const BOOL enableSeq = (numSequences > 1 && sel == IDC_RADIO3) ? TRUE : FALSE;
  502. GetDlgItem(IDC_EDIT12)->EnableWindow(enableSeq);
  503. GetDlgItem(IDC_EDIT13)->EnableWindow(enableSeq);
  504. m_SpinMinSequence.EnableWindow(enableSeq);
  505. m_SpinMaxSequence.EnableWindow(enableSeq);
  506. // No free slots => Cannot do instrument- or channel-based export to sample
  507. BOOL canDoMultiExport = (IsDlgButtonChecked(IDC_RADIO4) != BST_UNCHECKED /* normal export */ || m_CbnSampleSlot.GetItemData(0) == 0 /* "free slot" is in list */) ? TRUE : FALSE;
  508. GetDlgItem(IDC_CHECK4)->EnableWindow(canDoMultiExport);
  509. GetDlgItem(IDC_CHECK6)->EnableWindow(canDoMultiExport);
  510. }
  511. void CWaveConvert::OnExportModeChanged()
  512. {
  513. SaveEncoderSettings();
  514. bool sampleExport = (IsDlgButtonChecked(IDC_RADIO5) != BST_UNCHECKED);
  515. m_CbnFileType.EnableWindow(sampleExport ? FALSE : TRUE);
  516. m_CbnSampleSlot.EnableWindow(sampleExport && !IsDlgButtonChecked(IDC_CHECK4) && !IsDlgButtonChecked(IDC_CHECK6));
  517. if(sampleExport)
  518. {
  519. // Render to sample: Always use WAV
  520. if(m_CbnFileType.GetCurSel() != 0)
  521. {
  522. m_CbnFileType.SetCurSel(0);
  523. OnFileTypeChanged();
  524. }
  525. }
  526. FillChannels();
  527. FillFormats();
  528. FillDither();
  529. FillTags();
  530. }
  531. void CWaveConvert::OnSampleSlotChanged()
  532. {
  533. CheckRadioButton(IDC_RADIO4, IDC_RADIO5, IDC_RADIO5);
  534. // When choosing a specific sample slot, we cannot use per-channel or per-instrument export
  535. int sel = m_CbnSampleSlot.GetCurSel();
  536. if(sel >= 0 && m_CbnSampleSlot.GetItemData(sel) > 0)
  537. {
  538. CheckDlgButton(IDC_CHECK4, BST_UNCHECKED);
  539. CheckDlgButton(IDC_CHECK6, BST_UNCHECKED);
  540. }
  541. UpdateDialog();
  542. }
  543. void CWaveConvert::OnPlayerOptions()
  544. {
  545. CPropertySheet dlg(_T("Mixer Settings"), this);
  546. COptionsMixer mixerpage;
  547. dlg.AddPage(&mixerpage);
  548. #if !defined(NO_REVERB) || !defined(NO_DSP) || !defined(NO_EQ) || !defined(NO_AGC)
  549. COptionsPlayer dsppage;
  550. dlg.AddPage(&dsppage);
  551. #endif
  552. dlg.DoModal();
  553. }
  554. void CWaveConvert::OnCheckTimeLimit()
  555. {
  556. if (IsDlgButtonChecked(IDC_CHECK2))
  557. {
  558. m_dwSongLimit = GetDlgItemInt(IDC_EDIT2, NULL, FALSE);
  559. if (!m_dwSongLimit)
  560. {
  561. m_dwSongLimit = 600;
  562. SetDlgItemText(IDC_EDIT2, _T("600"));
  563. }
  564. } else m_dwSongLimit = 0;
  565. UpdateDialog();
  566. }
  567. // Channel render is mutually exclusive with instrument render
  568. void CWaveConvert::OnCheckChannelMode()
  569. {
  570. if(IsDlgButtonChecked(IDC_CHECK4) != BST_UNCHECKED)
  571. {
  572. CheckDlgButton(IDC_CHECK6, BST_UNCHECKED);
  573. m_CbnSampleSlot.SetCurSel(0);
  574. }
  575. UpdateDialog();
  576. }
  577. // Channel render is mutually exclusive with instrument render
  578. void CWaveConvert::OnCheckInstrMode()
  579. {
  580. if(IsDlgButtonChecked(IDC_CHECK6) != BST_UNCHECKED)
  581. {
  582. CheckDlgButton(IDC_CHECK4, BST_UNCHECKED);
  583. m_CbnSampleSlot.SetCurSel(0);
  584. }
  585. UpdateDialog();
  586. }
  587. void CWaveConvert::OnOK()
  588. {
  589. if (m_dwSongLimit) m_dwSongLimit = GetDlgItemInt(IDC_EDIT2, NULL, FALSE);
  590. const bool selection = IsDlgButtonChecked(IDC_RADIO2) != BST_UNCHECKED;
  591. if(selection)
  592. {
  593. // Play selection
  594. m_Settings.minOrder = static_cast<ORDERINDEX>(GetDlgItemInt(IDC_EDIT3, NULL, FALSE));
  595. m_Settings.maxOrder = static_cast<ORDERINDEX>(GetDlgItemInt(IDC_EDIT4, NULL, FALSE));
  596. if(m_Settings.minOrder > m_Settings.maxOrder)
  597. std::swap(m_Settings.minOrder, m_Settings.maxOrder);
  598. } else
  599. {
  600. m_Settings.minOrder = m_Settings.maxOrder = ORDERINDEX_INVALID;
  601. }
  602. if(IsDlgButtonChecked(IDC_RADIO3))
  603. {
  604. const UINT maxSequence = m_SndFile.Order.GetNumSequences();
  605. m_Settings.minSequence = static_cast<SEQUENCEINDEX>(std::clamp(GetDlgItemInt(IDC_EDIT12, NULL, FALSE), 1u, maxSequence) - 1u);
  606. m_Settings.maxSequence = static_cast<SEQUENCEINDEX>(std::clamp(GetDlgItemInt(IDC_EDIT13, NULL, FALSE), 1u, maxSequence) - 1u);
  607. if(m_Settings.minSequence > m_Settings.maxSequence)
  608. std::swap(m_Settings.minSequence, m_Settings.maxSequence);
  609. } else
  610. {
  611. m_Settings.minSequence = m_Settings.maxSequence = m_SndFile.Order.GetCurrentSequenceIndex();
  612. }
  613. m_Settings.repeatCount = static_cast<uint16>(GetDlgItemInt(IDC_EDIT5, NULL, FALSE));
  614. m_Settings.normalize = IsDlgButtonChecked(IDC_CHECK5) != BST_UNCHECKED;
  615. m_Settings.silencePlugBuffers = IsDlgButtonChecked(IDC_RENDERSILENCE) != BST_UNCHECKED;
  616. m_Settings.outputToSample = IsDlgButtonChecked(IDC_RADIO5) != BST_UNCHECKED;
  617. m_bGivePlugsIdleTime = IsDlgButtonChecked(IDC_GIVEPLUGSIDLETIME) != BST_UNCHECKED;
  618. if (m_bGivePlugsIdleTime)
  619. {
  620. static bool showWarning = true;
  621. 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?",
  622. "Really enable slow render?") == cnfNo)
  623. {
  624. m_bGivePlugsIdleTime = false;
  625. } else
  626. {
  627. showWarning = false;
  628. }
  629. }
  630. m_bChannelMode = IsDlgButtonChecked(IDC_CHECK4) != BST_UNCHECKED;
  631. m_bInstrumentMode= IsDlgButtonChecked(IDC_CHECK6) != BST_UNCHECKED;
  632. m_Settings.sampleSlot = static_cast<SAMPLEINDEX>(m_CbnSampleSlot.GetItemData(m_CbnSampleSlot.GetCurSel()));
  633. SaveEncoderSettings();
  634. EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
  635. m_Settings.Tags = FileTags();
  636. m_Settings.Tags.SetEncoder();
  637. if(encSettings.Tags)
  638. {
  639. CString tmp;
  640. m_EditTitle.GetWindowText(tmp);
  641. m_Settings.Tags.title = mpt::ToUnicode(tmp);
  642. m_EditAuthor.GetWindowText(tmp);
  643. m_Settings.Tags.artist = mpt::ToUnicode(tmp);
  644. m_EditAlbum.GetWindowText(tmp);
  645. m_Settings.Tags.album = mpt::ToUnicode(tmp);
  646. m_EditURL.GetWindowText(tmp);
  647. m_Settings.Tags.url = mpt::ToUnicode(tmp);
  648. if((encTraits->modesWithFixedGenres & encSettings.Mode) && !encTraits->genres.empty())
  649. {
  650. m_CbnGenre.GetWindowText(tmp);
  651. m_Settings.Tags.genre = mpt::ToUnicode(tmp);
  652. } else
  653. {
  654. m_EditGenre.GetWindowText(tmp);
  655. m_Settings.Tags.genre = mpt::ToUnicode(tmp);
  656. }
  657. m_EditYear.GetWindowText(tmp);
  658. m_Settings.Tags.year = mpt::ToUnicode(tmp);
  659. if(m_Settings.Tags.year == U_("0"))
  660. {
  661. m_Settings.Tags.year = mpt::ustring();
  662. }
  663. if(!m_SndFile.m_songMessage.empty())
  664. {
  665. m_Settings.Tags.comments = mpt::ToUnicode(mpt::Charset::Locale, m_SndFile.m_songMessage.GetFormatted(SongMessage::leLF));
  666. }
  667. m_Settings.Tags.bpm = mpt::ufmt::val(m_SndFile.GetCurrentBPM());
  668. SaveTags();
  669. }
  670. CDialog::OnOK();
  671. }
  672. void CWaveConvert::SaveEncoderSettings()
  673. {
  674. EncoderSettingsConf &encSettings = m_Settings.GetEncoderSettings();
  675. encSettings.Samplerate = static_cast<uint32>(m_CbnSampleRate.GetItemData(m_CbnSampleRate.GetCurSel()));
  676. encSettings.Channels = static_cast<uint16>(m_CbnChannels.GetItemData(m_CbnChannels.GetCurSel()));
  677. DWORD_PTR dwFormat = m_CbnSampleFormat.GetItemData(m_CbnSampleFormat.GetCurSel());
  678. if(encTraits->modes & Encoder::ModeLossless)
  679. {
  680. int format = (int)((dwFormat >> 0) & 0xffffff);
  681. encSettings.Dither = static_cast<int>(m_CbnDither.GetItemData(m_CbnDither.GetCurSel()));
  682. encSettings.Format2 = Encoder::Format::FromInt(format);
  683. encSettings.Mode = Encoder::ModeLossless;
  684. encSettings.Bitrate = 0;
  685. encSettings.Quality = encTraits->defaultQuality;
  686. } else
  687. {
  688. encSettings.Dither = static_cast<int>(m_CbnDither.GetItemData(m_CbnDither.GetCurSel()));
  689. Encoder::Mode mode = (Encoder::Mode)((dwFormat >> 24) & 0xff);
  690. int quality = (int)((dwFormat >> 0) & 0xff);
  691. int bitrate = (int)((dwFormat >> 0) & 0xffff);
  692. encSettings.Mode = mode;
  693. encSettings.Bitrate = bitrate;
  694. encSettings.Quality = static_cast<float>(quality) * 0.01f;
  695. encSettings.Format2 = { Encoder::Format::Encoding::Float, 32, mpt::get_endian() };
  696. }
  697. encSettings.Cues = IsDlgButtonChecked(IDC_CHECK3) ? true : false;
  698. encSettings.Tags = IsDlgButtonChecked(IDC_CHECK7) ? true : false;
  699. }
  700. std::size_t CWaveConvertSettings::FindEncoder(const mpt::ustring &name) const
  701. {
  702. for(std::size_t i = 0; i < EncoderFactories.size(); ++i)
  703. {
  704. if(EncoderFactories[i]->GetTraits().encoderSettingsName == name)
  705. {
  706. return i;
  707. }
  708. }
  709. return 0;
  710. }
  711. void CWaveConvertSettings::SelectEncoder(std::size_t index)
  712. {
  713. MPT_ASSERT(!EncoderFactories.empty());
  714. MPT_ASSERT(index < EncoderFactories.size());
  715. EncoderIndex = index;
  716. EncoderName = EncoderFactories[EncoderIndex]->GetTraits().encoderSettingsName;
  717. }
  718. EncoderFactoryBase *CWaveConvertSettings::GetEncoderFactory() const
  719. {
  720. MPT_ASSERT(!EncoderFactories.empty());
  721. return EncoderFactories[EncoderIndex];
  722. }
  723. const Encoder::Traits *CWaveConvertSettings::GetTraits() const
  724. {
  725. MPT_ASSERT(!EncoderFactories.empty());
  726. return &EncoderFactories[EncoderIndex]->GetTraits();
  727. }
  728. EncoderSettingsConf &CWaveConvertSettings::GetEncoderSettings() const
  729. {
  730. MPT_ASSERT(!EncoderSettings.empty());
  731. return *(EncoderSettings[EncoderIndex]);
  732. }
  733. Encoder::Settings CWaveConvertSettings::GetEncoderSettingsWithDetails() const
  734. {
  735. MPT_ASSERT(!EncoderSettings.empty());
  736. Encoder::Settings settings = static_cast<Encoder::Settings>(*(EncoderSettings[EncoderIndex]));
  737. settings.Details = static_cast<Encoder::StreamSettings>(TrackerSettings::Instance().ExportStreamEncoderSettings);
  738. return settings;
  739. }
  740. CWaveConvertSettings::CWaveConvertSettings(SettingsContainer &conf, const std::vector<EncoderFactoryBase*> &encFactories)
  741. : EncoderFactories(encFactories)
  742. , EncoderName(conf, U_("Export"), U_("Encoder"), U_(""))
  743. , EncoderIndex(FindEncoder(EncoderName))
  744. , storedTags(conf)
  745. , repeatCount(0)
  746. , minOrder(ORDERINDEX_INVALID), maxOrder(ORDERINDEX_INVALID)
  747. , sampleSlot(0)
  748. , normalize(false)
  749. , silencePlugBuffers(false)
  750. , outputToSample(false)
  751. {
  752. Tags.SetEncoder();
  753. for(const auto & factory : EncoderFactories)
  754. {
  755. const Encoder::Traits &encTraits = factory->GetTraits();
  756. EncoderSettings.push_back(
  757. std::make_unique<EncoderSettingsConf>(
  758. conf,
  759. encTraits.encoderSettingsName,
  760. encTraits.canCues,
  761. encTraits.canTags,
  762. encTraits.defaultSamplerate,
  763. encTraits.defaultChannels,
  764. encTraits.defaultMode,
  765. encTraits.defaultBitrate,
  766. encTraits.defaultQuality,
  767. encTraits.defaultFormat,
  768. encTraits.defaultDitherType
  769. )
  770. );
  771. }
  772. SelectEncoder(EncoderIndex);
  773. }
  774. /////////////////////////////////////////////////////////////////////////////////////////
  775. // CDoWaveConvert: save a mod as a wave file
  776. void CDoWaveConvert::Run()
  777. {
  778. UINT ok = IDOK;
  779. uint64 ullSamples = 0;
  780. std::vector<float> normalizeBufferData;
  781. float *normalizeBuffer = nullptr;
  782. float normalizePeak = 0.0f;
  783. const mpt::PathString normalizeFileName = mpt::CreateTempFileName(P_("OpenMPT"));
  784. std::optional<mpt::fstream> normalizeFile;
  785. if(m_Settings.normalize)
  786. {
  787. normalizeBufferData.resize(MIXBUFFERSIZE * 4);
  788. normalizeBuffer = normalizeBufferData.data();
  789. // Ensure this temporary file is marked as temporary in the file system, to increase the chance it will never be written to disk
  790. 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)
  791. {
  792. ::CloseHandle(hFile);
  793. }
  794. normalizeFile.emplace(normalizeFileName, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc);
  795. }
  796. const Encoder::Settings encSettings = m_Settings.GetEncoderSettingsWithDetails();
  797. const uint32 samplerate = encSettings.Samplerate;
  798. const uint16 channels = encSettings.Channels;
  799. ASSERT(m_Settings.GetEncoderFactory() && m_Settings.GetEncoderFactory()->IsAvailable());
  800. // Silence mix buffer of plugins, for plugins that don't clear their reverb buffers and similar stuff when they are reset
  801. #ifndef NO_PLUGINS
  802. if(m_Settings.silencePlugBuffers)
  803. {
  804. SetText(_T("Clearing plugin buffers"));
  805. for(auto &plug : m_SndFile.m_MixPlugins)
  806. {
  807. if(plug.pMixPlugin != nullptr)
  808. {
  809. // Render up to 20 seconds per plugin
  810. for(int j = 0; j < 20; j++)
  811. {
  812. const float maxVal = plug.pMixPlugin->RenderSilence(samplerate);
  813. if(maxVal <= FLT_EPSILON)
  814. {
  815. break;
  816. }
  817. }
  818. ProcessMessages();
  819. if(m_abort)
  820. {
  821. m_abort = false;
  822. break;
  823. }
  824. }
  825. }
  826. }
  827. #endif // NO_PLUGINS
  828. MixerSettings oldmixersettings = m_SndFile.m_MixerSettings;
  829. MixerSettings mixersettings = TrackerSettings::Instance().GetMixerSettings();
  830. mixersettings.m_nMaxMixChannels = MAX_CHANNELS; // always use max mixing channels when rendering
  831. mixersettings.gdwMixingFreq = samplerate;
  832. mixersettings.gnChannels = channels;
  833. m_SndFile.m_SongFlags.reset(SONG_PAUSED | SONG_STEP);
  834. if(m_Settings.normalize)
  835. {
  836. #ifndef NO_AGC
  837. mixersettings.DSPMask &= ~SNDDSP_AGC;
  838. #endif
  839. }
  840. DithersOpenMPT dithers(theApp.PRNG(), encSettings.Dither, encSettings.Channels);
  841. m_SndFile.ResetChannels();
  842. m_SndFile.SetMixerSettings(mixersettings);
  843. m_SndFile.SetResamplerSettings(TrackerSettings::Instance().GetResamplerSettings());
  844. m_SndFile.InitPlayer(true);
  845. // Tags must be known at the stream start,
  846. // so that the encoder class could write them before audio data if mandated by the format,
  847. // otherwise they should just be cached by the encoder.
  848. std::unique_ptr<IAudioStreamEncoder> fileEnc = m_Settings.GetEncoderFactory()->ConstructStreamEncoder(fileStream, encSettings, m_Settings.Tags);
  849. std::variant<
  850. std::unique_ptr<std::array<double, MIXBUFFERSIZE * 4>>,
  851. std::unique_ptr<std::array<float, MIXBUFFERSIZE * 4>>,
  852. std::unique_ptr<std::array<int32, MIXBUFFERSIZE * 4>>,
  853. std::unique_ptr<std::array<int24, MIXBUFFERSIZE * 4>>,
  854. std::unique_ptr<std::array<int16, MIXBUFFERSIZE * 4>>,
  855. std::unique_ptr<std::array<int8, MIXBUFFERSIZE * 4>>,
  856. std::unique_ptr<std::array<uint8, MIXBUFFERSIZE * 4>>> bufferData;
  857. union AnyBufferSamplePointer
  858. {
  859. double *float64;
  860. float *float32;
  861. int32 *int32;
  862. int24 *int24;
  863. int16 *int16;
  864. int8 *int8;
  865. uint8 *uint8;
  866. void *any;
  867. };
  868. AnyBufferSamplePointer buffer;
  869. buffer.any = nullptr;
  870. switch(fileEnc->GetSampleFormat())
  871. {
  872. case SampleFormat::Float64:
  873. bufferData = std::make_unique<std::array<double, MIXBUFFERSIZE * 4>>();
  874. buffer.float64 = std::get<0>(bufferData)->data();
  875. break;
  876. case SampleFormat::Float32:
  877. bufferData = std::make_unique<std::array<float, MIXBUFFERSIZE * 4>>();
  878. buffer.float32 = std::get<1>(bufferData)->data();
  879. break;
  880. case SampleFormat::Int32:
  881. bufferData = std::make_unique<std::array<int32, MIXBUFFERSIZE * 4>>();
  882. buffer.int32 = std::get<2>(bufferData)->data();
  883. break;
  884. case SampleFormat::Int24:
  885. bufferData = std::make_unique<std::array<int24, MIXBUFFERSIZE * 4>>();
  886. buffer.int24 = std::get<3>(bufferData)->data();
  887. break;
  888. case SampleFormat::Int16:
  889. bufferData = std::make_unique<std::array<int16, MIXBUFFERSIZE * 4>>();
  890. buffer.int16 = std::get<4>(bufferData)->data();
  891. break;
  892. case SampleFormat::Int8:
  893. bufferData = std::make_unique<std::array<int8, MIXBUFFERSIZE * 4>>();
  894. buffer.int8 = std::get<5>(bufferData)->data();
  895. break;
  896. case SampleFormat::Unsigned8:
  897. bufferData = std::make_unique<std::array<uint8, MIXBUFFERSIZE * 4>>();
  898. buffer.uint8 = std::get<6>(bufferData)->data();
  899. break;
  900. }
  901. uint64 ullMaxSamples = uint64_max / (channels * ((fileEnc->GetSampleFormat().GetBitsPerSample()+7) / 8));
  902. if (m_dwSongLimit)
  903. {
  904. LimitMax(ullMaxSamples, m_dwSongLimit * samplerate);
  905. }
  906. // Calculate maximum samples
  907. uint64 max = m_dwSongLimit ? ullMaxSamples : uint64_max;
  908. // Reset song position tracking
  909. m_SndFile.ResetPlayPos();
  910. m_SndFile.m_SongFlags.reset(SONG_PATTERNLOOP);
  911. ORDERINDEX startOrder = 0;
  912. GetLengthTarget target;
  913. if(m_Settings.minOrder != ORDERINDEX_INVALID && m_Settings.maxOrder != ORDERINDEX_INVALID)
  914. {
  915. m_SndFile.SetRepeatCount(0);
  916. startOrder = m_Settings.minOrder;
  917. ORDERINDEX endOrder = m_Settings.maxOrder;
  918. while(!m_SndFile.Order().IsValidPat(endOrder) && endOrder > startOrder)
  919. {
  920. endOrder--;
  921. }
  922. if(m_SndFile.Order().IsValidPat(endOrder))
  923. {
  924. target = GetLengthTarget(endOrder, m_SndFile.Patterns[m_SndFile.Order()[endOrder]].GetNumRows() - 1);
  925. }
  926. target.StartPos(m_SndFile.Order.GetCurrentSequenceIndex(), startOrder, 0);
  927. m_SndFile.m_nMaxOrderPosition = endOrder + 1;
  928. } else
  929. {
  930. m_SndFile.SetRepeatCount(std::max(0, m_Settings.repeatCount - 1));
  931. }
  932. uint64 l = mpt::saturate_round<uint64>(m_SndFile.GetLength(eNoAdjust, target).front().duration * samplerate * (1 + m_SndFile.GetRepeatCount()));
  933. m_SndFile.SetCurrentOrder(startOrder);
  934. m_SndFile.GetLength(eAdjust, GetLengthTarget(startOrder, 0)); // adjust playback variables / visited rows vector
  935. m_SndFile.m_PlayState.m_nCurrentOrder = startOrder;
  936. if (l < max) max = l;
  937. SetRange(0, max);
  938. EnableTaskbarProgress();
  939. // No pattern cue points yet
  940. std::vector<PatternCuePoint> patternCuePoints;
  941. patternCuePoints.reserve(m_SndFile.Order().size());
  942. m_SndFile.m_PatternCuePoints = &patternCuePoints;
  943. // Process the conversion
  944. // For calculating the remaining time
  945. auto dwStartTime = timeGetTime(), prevTime = dwStartTime;
  946. uint32 timeRemaining = 0;
  947. uint64 bytesWritten = 0;
  948. auto mainFrame = CMainFrame::GetMainFrame();
  949. mainFrame->PauseMod();
  950. m_SndFile.m_SongFlags.reset(SONG_STEP | SONG_PATTERNLOOP);
  951. mainFrame->InitRenderer(&m_SndFile);
  952. for (UINT n = 0; ; n++)
  953. {
  954. UINT lRead = 0;
  955. if(m_Settings.normalize)
  956. {
  957. lRead = ReadInterleaved(m_SndFile, normalizeBuffer, channels, MIXBUFFERSIZE, dithers);
  958. } else
  959. {
  960. switch(fileEnc->GetSampleFormat())
  961. {
  962. case SampleFormat::Float64:
  963. lRead = ReadInterleaved(m_SndFile, buffer.float64, channels, MIXBUFFERSIZE, dithers);
  964. break;
  965. case SampleFormat::Float32:
  966. lRead = ReadInterleaved(m_SndFile, buffer.float32, channels, MIXBUFFERSIZE, dithers);
  967. break;
  968. case SampleFormat::Int32:
  969. lRead = ReadInterleaved(m_SndFile, buffer.int32, channels, MIXBUFFERSIZE, dithers);
  970. break;
  971. case SampleFormat::Int24:
  972. lRead = ReadInterleaved(m_SndFile, buffer.int24, channels, MIXBUFFERSIZE, dithers);
  973. break;
  974. case SampleFormat::Int16:
  975. lRead = ReadInterleaved(m_SndFile, buffer.int16, channels, MIXBUFFERSIZE, dithers);
  976. break;
  977. case SampleFormat::Int8:
  978. lRead = ReadInterleaved(m_SndFile, buffer.int8, channels, MIXBUFFERSIZE, dithers);
  979. break;
  980. case SampleFormat::Unsigned8:
  981. lRead = ReadInterleaved(m_SndFile, buffer.uint8, channels, MIXBUFFERSIZE, dithers);
  982. break;
  983. }
  984. }
  985. // Process cue points (add base offset), if there are any to process.
  986. for(auto iter = patternCuePoints.rbegin(); iter != patternCuePoints.rend(); ++iter)
  987. {
  988. if(iter->processed)
  989. {
  990. // From this point, all cues have already been processed.
  991. break;
  992. }
  993. iter->offset += ullSamples;
  994. iter->processed = true;
  995. }
  996. if (m_bGivePlugsIdleTime)
  997. {
  998. Sleep(20);
  999. }
  1000. if (!lRead)
  1001. break;
  1002. ullSamples += lRead;
  1003. if(m_Settings.normalize)
  1004. {
  1005. std::size_t countSamples = lRead * m_SndFile.m_MixerSettings.gnChannels;
  1006. const float *src = normalizeBuffer;
  1007. while(countSamples--)
  1008. {
  1009. const float val = *src;
  1010. if(val > normalizePeak) normalizePeak = val;
  1011. else if(0.0f - val >= normalizePeak) normalizePeak = 0.0f - val;
  1012. src++;
  1013. }
  1014. if(!mpt::IO::WriteRaw(*normalizeFile, mpt::as_span(reinterpret_cast<const std::byte*>(normalizeBuffer), lRead * m_SndFile.m_MixerSettings.gnChannels * sizeof(float))))
  1015. {
  1016. break;
  1017. }
  1018. } else
  1019. {
  1020. const std::streampos oldPos = fileStream.tellp();
  1021. switch(fileEnc->GetSampleFormat())
  1022. {
  1023. case SampleFormat::Float64:
  1024. fileEnc->WriteInterleaved(lRead, buffer.float64);
  1025. break;
  1026. case SampleFormat::Float32:
  1027. fileEnc->WriteInterleaved(lRead, buffer.float32);
  1028. break;
  1029. case SampleFormat::Int32:
  1030. fileEnc->WriteInterleaved(lRead, buffer.int32);
  1031. break;
  1032. case SampleFormat::Int24:
  1033. fileEnc->WriteInterleaved(lRead, buffer.int24);
  1034. break;
  1035. case SampleFormat::Int16:
  1036. fileEnc->WriteInterleaved(lRead, buffer.int16);
  1037. break;
  1038. case SampleFormat::Int8:
  1039. fileEnc->WriteInterleaved(lRead, buffer.int8);
  1040. break;
  1041. case SampleFormat::Unsigned8:
  1042. fileEnc->WriteInterleaved(lRead, buffer.uint8);
  1043. break;
  1044. }
  1045. const std::streampos newPos = fileStream.tellp();
  1046. bytesWritten += static_cast<uint64>(newPos - oldPos);
  1047. if(!fileStream)
  1048. {
  1049. break;
  1050. }
  1051. }
  1052. if(m_dwSongLimit && (ullSamples >= ullMaxSamples))
  1053. {
  1054. break;
  1055. }
  1056. auto currentTime = timeGetTime();
  1057. if((currentTime - prevTime) >= 16)
  1058. {
  1059. prevTime = currentTime;
  1060. DWORD seconds = (DWORD)(ullSamples / m_SndFile.m_MixerSettings.gdwMixingFreq);
  1061. if((ullSamples > 0) && (ullSamples < max))
  1062. {
  1063. timeRemaining = static_cast<uint32>((timeRemaining + ((currentTime - dwStartTime) * (max - ullSamples) / ullSamples) / 1000) / 2);
  1064. }
  1065. if(m_Settings.normalize)
  1066. {
  1067. 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)));
  1068. } else
  1069. {
  1070. 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)));
  1071. }
  1072. SetProgress(ullSamples);
  1073. }
  1074. ProcessMessages();
  1075. if (m_abort)
  1076. {
  1077. ok = IDCANCEL;
  1078. break;
  1079. }
  1080. }
  1081. m_SndFile.m_nMaxOrderPosition = 0;
  1082. mainFrame->StopRenderer(&m_SndFile);
  1083. if(m_Settings.normalize)
  1084. {
  1085. const float normalizeFactor = (normalizePeak != 0.0f) ? (1.0f / normalizePeak) : 1.0f;
  1086. const uint64 framesTotal = ullSamples;
  1087. int lastPercent = -1;
  1088. mpt::IO::SeekAbsolute(*normalizeFile, 0);
  1089. uint64 framesProcessed = 0;
  1090. uint64 framesToProcess = framesTotal;
  1091. SetRange(0, framesTotal);
  1092. while(framesToProcess)
  1093. {
  1094. const std::size_t framesChunk = std::min(mpt::saturate_cast<std::size_t>(framesToProcess), std::size_t(MIXBUFFERSIZE));
  1095. const uint32 samplesChunk = static_cast<uint32>(framesChunk * channels);
  1096. const std::size_t bytes = samplesChunk * sizeof(float);
  1097. if(mpt::IO::ReadRaw(*normalizeFile, mpt::as_span(reinterpret_cast<std::byte*>(normalizeBuffer), bytes)).size() != bytes)
  1098. {
  1099. break;
  1100. }
  1101. for(std::size_t i = 0; i < samplesChunk; ++i)
  1102. {
  1103. normalizeBuffer[i] *= normalizeFactor;
  1104. }
  1105. const std::streampos oldPos = fileStream.tellp();
  1106. std::visit(
  1107. [&](auto& ditherInstance)
  1108. {
  1109. switch(fileEnc->GetSampleFormat())
  1110. {
  1111. case SampleFormat::Unsigned8:
  1112. ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<uint8>(buffer.uint8, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
  1113. break;
  1114. case SampleFormat::Int8:
  1115. ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<int8>(buffer.int8, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
  1116. break;
  1117. case SampleFormat::Int16:
  1118. ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<int16>(buffer.int16, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
  1119. break;
  1120. case SampleFormat::Int24:
  1121. ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<int24>(buffer.int24, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
  1122. break;
  1123. case SampleFormat::Int32:
  1124. ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<int32>(buffer.int32, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
  1125. break;
  1126. case SampleFormat::Float32:
  1127. ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<float>(buffer.float32, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
  1128. break;
  1129. case SampleFormat::Float64:
  1130. ConvertBufferMixInternalToBuffer<false>(mpt::audio_span_interleaved<double>(buffer.float64, channels, framesChunk), mpt::audio_span_interleaved<const MixSampleFloat>(normalizeBuffer, channels, framesChunk), ditherInstance, channels, framesChunk);
  1131. break;
  1132. default: MPT_ASSERT_NOTREACHED(); break;
  1133. }
  1134. },
  1135. dithers.Variant()
  1136. );
  1137. switch(fileEnc->GetSampleFormat())
  1138. {
  1139. case SampleFormat::Float64:
  1140. fileEnc->WriteInterleaved(framesChunk, buffer.float64);
  1141. break;
  1142. case SampleFormat::Float32:
  1143. fileEnc->WriteInterleaved(framesChunk, buffer.float32);
  1144. break;
  1145. case SampleFormat::Int32:
  1146. fileEnc->WriteInterleaved(framesChunk, buffer.int32);
  1147. break;
  1148. case SampleFormat::Int24:
  1149. fileEnc->WriteInterleaved(framesChunk, buffer.int24);
  1150. break;
  1151. case SampleFormat::Int16:
  1152. fileEnc->WriteInterleaved(framesChunk, buffer.int16);
  1153. break;
  1154. case SampleFormat::Int8:
  1155. fileEnc->WriteInterleaved(framesChunk, buffer.int8);
  1156. break;
  1157. case SampleFormat::Unsigned8:
  1158. fileEnc->WriteInterleaved(framesChunk, buffer.uint8);
  1159. break;
  1160. }
  1161. const std::streampos newPos = fileStream.tellp();
  1162. bytesWritten += static_cast<std::size_t>(newPos - oldPos);
  1163. auto currentTime = timeGetTime();
  1164. if((currentTime - prevTime) >= 16)
  1165. {
  1166. prevTime = currentTime;
  1167. int percent = static_cast<int>(100 * framesProcessed / framesTotal);
  1168. if(percent != lastPercent)
  1169. {
  1170. SetText(MPT_CFORMAT("Normalizing... ({}%)")(percent));
  1171. SetProgress(framesProcessed);
  1172. lastPercent = percent;
  1173. }
  1174. ProcessMessages();
  1175. }
  1176. framesProcessed += framesChunk;
  1177. framesToProcess -= framesChunk;
  1178. }
  1179. mpt::IO::Flush(*normalizeFile);
  1180. normalizeFile.reset();
  1181. for(int retry=0; retry<10; retry++)
  1182. {
  1183. // stupid virus scanners
  1184. if(DeleteFile(normalizeFileName.AsNative().c_str()) != EACCES)
  1185. {
  1186. break;
  1187. }
  1188. Sleep(10);
  1189. }
  1190. }
  1191. if(!patternCuePoints.empty())
  1192. {
  1193. if(encSettings.Cues)
  1194. {
  1195. std::vector<uint64> cues;
  1196. cues.reserve(patternCuePoints.size());
  1197. for(const auto &cue : patternCuePoints)
  1198. {
  1199. cues.push_back(static_cast<uint32>(cue.offset));
  1200. }
  1201. fileEnc->WriteCues(cues);
  1202. }
  1203. }
  1204. m_SndFile.m_PatternCuePoints = nullptr;
  1205. fileEnc->WriteFinalize();
  1206. fileEnc = nullptr;
  1207. CMainFrame::UpdateAudioParameters(m_SndFile, TRUE);
  1208. EndDialog(ok);
  1209. }
  1210. OPENMPT_NAMESPACE_END