Autotune.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. /*
  2. * Autotune.cpp
  3. * ------------
  4. * Purpose: Class for tuning a sample to a given base note automatically.
  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 "Autotune.h"
  11. #include <math.h>
  12. #include "../common/misc_util.h"
  13. #include "../soundlib/Sndfile.h"
  14. #include <algorithm>
  15. #include <execution>
  16. #include <numeric>
  17. #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2)
  18. #include <emmintrin.h>
  19. #endif
  20. OPENMPT_NAMESPACE_BEGIN
  21. // The more bins, the more autocorrelations are done and the more precise the result is.
  22. #define BINS_PER_NOTE 32
  23. #define MIN_SAMPLE_LENGTH 2
  24. #define START_NOTE (24 * BINS_PER_NOTE) // C-2
  25. #define END_NOTE (96 * BINS_PER_NOTE) // C-8
  26. #define HISTORY_BINS (12 * BINS_PER_NOTE) // One octave
  27. static double FrequencyToNote(double freq, double pitchReference)
  28. {
  29. return ((12.0 * (log(freq / (pitchReference / 2.0)) / log(2.0))) + 57.0);
  30. }
  31. static double NoteToFrequency(double note, double pitchReference)
  32. {
  33. return pitchReference * pow(2.0, (note - 69.0) / 12.0);
  34. }
  35. // Calculate the amount of samples for autocorrelation shifting for a given note
  36. static SmpLength NoteToShift(uint32 sampleFreq, int note, double pitchReference)
  37. {
  38. const double fundamentalFrequency = NoteToFrequency((double)note / BINS_PER_NOTE, pitchReference);
  39. return std::max(mpt::saturate_round<SmpLength>((double)sampleFreq / fundamentalFrequency), SmpLength(1));
  40. }
  41. // Create an 8-Bit sample buffer with loop unrolling and mono conversion for autocorrelation.
  42. template <class T>
  43. void Autotune::CopySamples(const T* origSample, SmpLength sampleLoopStart, SmpLength sampleLoopEnd)
  44. {
  45. const uint8 channels = m_sample.GetNumChannels();
  46. sampleLoopStart *= channels;
  47. sampleLoopEnd *= channels;
  48. for(SmpLength i = 0, pos = 0; i < m_sampleLength; i++, pos += channels)
  49. {
  50. if(pos >= sampleLoopEnd)
  51. {
  52. pos = sampleLoopStart;
  53. }
  54. const T* smp = origSample + pos;
  55. int32 data = 0; // More than enough for 256 channels... :)
  56. for(uint8 chn = 0; chn < channels; chn++)
  57. {
  58. // We only want the MSB.
  59. data += static_cast<int32>(smp[chn] >> ((sizeof(T) - 1) * 8));
  60. }
  61. data /= channels;
  62. m_sampleData[i] = static_cast<int16>(data);
  63. }
  64. }
  65. // Prepare a sample buffer for autocorrelation
  66. bool Autotune::PrepareSample(SmpLength maxShift)
  67. {
  68. // Determine which parts of the sample should be examined.
  69. SmpLength sampleOffset = 0, sampleLoopStart = 0, sampleLoopEnd = m_sample.nLength;
  70. if(m_selectionEnd >= sampleLoopStart + MIN_SAMPLE_LENGTH)
  71. {
  72. // A selection has been specified: Examine selection
  73. sampleOffset = m_selectionStart;
  74. sampleLoopStart = 0;
  75. sampleLoopEnd = m_selectionEnd - m_selectionStart;
  76. } else if(m_sample.uFlags[CHN_SUSTAINLOOP] && m_sample.nSustainEnd >= m_sample.nSustainStart + MIN_SAMPLE_LENGTH)
  77. {
  78. // A sustain loop is set: Examine sample up to sustain loop and, if necessary, execute the loop several times
  79. sampleOffset = 0;
  80. sampleLoopStart = m_sample.nSustainStart;
  81. sampleLoopEnd = m_sample.nSustainEnd;
  82. } else if(m_sample.uFlags[CHN_LOOP] && m_sample.nLoopEnd >= m_sample.nLoopStart + MIN_SAMPLE_LENGTH)
  83. {
  84. // A normal loop is set: Examine sample up to loop and, if necessary, execute the loop several times
  85. sampleOffset = 0;
  86. sampleLoopStart = m_sample.nLoopStart;
  87. sampleLoopEnd = m_sample.nLoopEnd;
  88. }
  89. // We should analyse at least a one second (= GetSampleRate() samples) long sample.
  90. m_sampleLength = std::max(sampleLoopEnd, static_cast<SmpLength>(m_sample.GetSampleRate(m_modType))) + maxShift;
  91. m_sampleLength = (m_sampleLength + 7) & ~7;
  92. if(m_sampleData != nullptr)
  93. {
  94. delete[] m_sampleData;
  95. }
  96. m_sampleData = new int16[m_sampleLength];
  97. if(m_sampleData == nullptr)
  98. {
  99. return false;
  100. }
  101. // Copy sample over.
  102. switch(m_sample.GetElementarySampleSize())
  103. {
  104. case 1:
  105. CopySamples(m_sample.sample8() + sampleOffset * m_sample.GetNumChannels(), sampleLoopStart, sampleLoopEnd);
  106. return true;
  107. case 2:
  108. CopySamples(m_sample.sample16() + sampleOffset * m_sample.GetNumChannels(), sampleLoopStart, sampleLoopEnd);
  109. return true;
  110. }
  111. return false;
  112. }
  113. bool Autotune::CanApply() const
  114. {
  115. return (m_sample.HasSampleData() && m_sample.nLength >= MIN_SAMPLE_LENGTH) || m_sample.uFlags[CHN_ADLIB];
  116. }
  117. namespace
  118. {
  119. struct AutotuneHistogramEntry
  120. {
  121. int index;
  122. uint64 sum;
  123. };
  124. struct AutotuneHistogram
  125. {
  126. std::array<uint64, HISTORY_BINS> histogram{};
  127. };
  128. struct AutotuneContext
  129. {
  130. const int16 *m_sampleData;
  131. double pitchReference;
  132. SmpLength processLength;
  133. uint32 sampleFreq;
  134. };
  135. #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2)
  136. static inline AutotuneHistogramEntry CalculateNoteHistogramSSE2(int note, AutotuneContext ctx)
  137. {
  138. const SmpLength autocorrShift = NoteToShift(ctx.sampleFreq, note, ctx.pitchReference);
  139. uint64 autocorrSum = 0;
  140. {
  141. const __m128i *normalData = reinterpret_cast<const __m128i *>(ctx.m_sampleData);
  142. const __m128i *shiftedData = reinterpret_cast<const __m128i *>(ctx.m_sampleData + autocorrShift);
  143. for(SmpLength i = ctx.processLength / 8; i != 0; i--)
  144. {
  145. __m128i normal = _mm_loadu_si128(normalData++);
  146. __m128i shifted = _mm_loadu_si128(shiftedData++);
  147. __m128i diff = _mm_sub_epi16(normal, shifted); // 8 16-bit differences
  148. __m128i squares = _mm_madd_epi16(diff, diff); // Multiply and add: 4 32-bit squares
  149. __m128i sum1 = _mm_shuffle_epi32(squares, _MM_SHUFFLE(0, 1, 2, 3)); // Move upper two integers to lower
  150. __m128i sum2 = _mm_add_epi32(squares, sum1); // Now we can add the (originally) upper two and lower two integers
  151. __m128i sum3 = _mm_shuffle_epi32(sum2, _MM_SHUFFLE(1, 1, 1, 1)); // Move the second-lowest integer to lowest position
  152. __m128i sum4 = _mm_add_epi32(sum2, sum3); // Add the two lowest positions
  153. autocorrSum += _mm_cvtsi128_si32(sum4);
  154. }
  155. }
  156. return {note % HISTORY_BINS, autocorrSum};
  157. }
  158. #endif
  159. static inline AutotuneHistogramEntry CalculateNoteHistogram(int note, AutotuneContext ctx)
  160. {
  161. const SmpLength autocorrShift = NoteToShift(ctx.sampleFreq, note, ctx.pitchReference);
  162. uint64 autocorrSum = 0;
  163. {
  164. const int16 *normalData = ctx.m_sampleData;
  165. const int16 *shiftedData = ctx.m_sampleData + autocorrShift;
  166. // Add up squared differences of all values
  167. for(SmpLength i = ctx.processLength; i != 0; i--, normalData++, shiftedData++)
  168. {
  169. autocorrSum += (*normalData - *shiftedData) * (*normalData - *shiftedData);
  170. }
  171. }
  172. return {note % HISTORY_BINS, autocorrSum};
  173. }
  174. static inline AutotuneHistogram operator+(AutotuneHistogram a, AutotuneHistogram b) noexcept
  175. {
  176. AutotuneHistogram result;
  177. for(std::size_t i = 0; i < HISTORY_BINS; ++i)
  178. {
  179. result.histogram[i] = a.histogram[i] + b.histogram[i];
  180. }
  181. return result;
  182. }
  183. static inline AutotuneHistogram & operator+=(AutotuneHistogram &a, AutotuneHistogram b) noexcept
  184. {
  185. for(std::size_t i = 0; i < HISTORY_BINS; ++i)
  186. {
  187. a.histogram[i] += b.histogram[i];
  188. }
  189. return a;
  190. }
  191. static inline AutotuneHistogram &operator+=(AutotuneHistogram &a, AutotuneHistogramEntry b) noexcept
  192. {
  193. a.histogram[b.index] += b.sum;
  194. return a;
  195. }
  196. struct AutotuneHistogramReduce
  197. {
  198. inline AutotuneHistogram operator()(AutotuneHistogram a, AutotuneHistogram b) noexcept
  199. {
  200. return a + b;
  201. }
  202. inline AutotuneHistogram operator()(AutotuneHistogramEntry a, AutotuneHistogramEntry b) noexcept
  203. {
  204. AutotuneHistogram result;
  205. result += a;
  206. result += b;
  207. return result;
  208. }
  209. inline AutotuneHistogram operator()(AutotuneHistogramEntry a, AutotuneHistogram b) noexcept
  210. {
  211. b += a;
  212. return b;
  213. }
  214. inline AutotuneHistogram operator()(AutotuneHistogram a, AutotuneHistogramEntry b) noexcept
  215. {
  216. a += b;
  217. return a;
  218. }
  219. };
  220. } // local
  221. bool Autotune::Apply(double pitchReference, int targetNote)
  222. {
  223. if(!CanApply())
  224. {
  225. return false;
  226. }
  227. const uint32 sampleFreq = m_sample.GetSampleRate(m_modType);
  228. // At the lowest frequency, we get the highest autocorrelation shift amount.
  229. const SmpLength maxShift = NoteToShift(sampleFreq, START_NOTE, pitchReference);
  230. if(!PrepareSample(maxShift))
  231. {
  232. return false;
  233. }
  234. // We don't process the autocorrelation overhead.
  235. const SmpLength processLength = m_sampleLength - maxShift;
  236. AutotuneContext ctx;
  237. ctx.m_sampleData = m_sampleData;
  238. ctx.pitchReference = pitchReference;
  239. ctx.processLength = processLength;
  240. ctx.sampleFreq = sampleFreq;
  241. // Note that we cannot use a fake integer iterator here because of the requirement on ForwardIterator to return a reference to the elements.
  242. std::array<int, END_NOTE - START_NOTE> notes;
  243. std::iota(notes.begin(), notes.end(), START_NOTE);
  244. AutotuneHistogram autocorr =
  245. #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2)
  246. (CPU::HasFeatureSet(CPU::feature::sse2)) ? std::transform_reduce(std::execution::par_unseq, std::begin(notes), std::end(notes), AutotuneHistogram{}, AutotuneHistogramReduce{}, [ctx](int note) { return CalculateNoteHistogramSSE2(note, ctx); } ) :
  247. #endif
  248. std::transform_reduce(std::execution::par_unseq, std::begin(notes), std::end(notes), AutotuneHistogram{}, AutotuneHistogramReduce{}, [ctx](int note) { return CalculateNoteHistogram(note, ctx); } );
  249. // Interpolate the histogram...
  250. AutotuneHistogram interpolated;
  251. for(int i = 0; i < HISTORY_BINS; i++)
  252. {
  253. interpolated.histogram[i] = autocorr.histogram[i];
  254. const int kernelWidth = 4;
  255. for(int ki = kernelWidth; ki >= 0; ki--)
  256. {
  257. // Choose bins to interpolate with
  258. int left = i - ki;
  259. if(left < 0) left += HISTORY_BINS;
  260. int right = i + ki;
  261. if(right >= HISTORY_BINS) right -= HISTORY_BINS;
  262. interpolated.histogram[i] = interpolated.histogram[i] / 2 + (autocorr.histogram[left] + autocorr.histogram[right]) / 2;
  263. }
  264. }
  265. // ...and find global minimum
  266. int minimumBin = static_cast<int>(std::min_element(std::begin(interpolated.histogram), std::end(interpolated.histogram)) - std::begin(interpolated.histogram));
  267. // Center target notes around C
  268. if(targetNote >= 6)
  269. {
  270. targetNote -= 12;
  271. }
  272. // Center bins around target note
  273. minimumBin -= targetNote * BINS_PER_NOTE;
  274. if(minimumBin >= 6 * BINS_PER_NOTE)
  275. {
  276. minimumBin -= 12 * BINS_PER_NOTE;
  277. }
  278. minimumBin += targetNote * BINS_PER_NOTE;
  279. const double newFundamentalFreq = NoteToFrequency(static_cast<double>(69 - targetNote) + static_cast<double>(minimumBin) / BINS_PER_NOTE, pitchReference);
  280. if(const auto newFreq = mpt::saturate_round<uint32>(sampleFreq * pitchReference / newFundamentalFreq); newFreq != sampleFreq)
  281. m_sample.nC5Speed = newFreq;
  282. else
  283. return false;
  284. if((m_modType & (MOD_TYPE_XM | MOD_TYPE_MOD)))
  285. {
  286. m_sample.FrequencyToTranspose();
  287. if((m_modType & MOD_TYPE_MOD))
  288. {
  289. m_sample.RelativeTone = 0;
  290. }
  291. }
  292. return true;
  293. }
  294. /////////////////////////////////////////////////////////////
  295. // CAutotuneDlg
  296. int CAutotuneDlg::m_pitchReference = 440; // Pitch reference in Hz
  297. int CAutotuneDlg::m_targetNote = 0; // Target note (C- = 0, C# = 1, etc...)
  298. void CAutotuneDlg::DoDataExchange(CDataExchange* pDX)
  299. {
  300. CDialog::DoDataExchange(pDX);
  301. //{{AFX_DATA_MAP(CAutotuneDlg)
  302. DDX_Control(pDX, IDC_COMBO1, m_CbnNoteBox);
  303. //}}AFX_DATA_MAP
  304. }
  305. BOOL CAutotuneDlg::OnInitDialog()
  306. {
  307. CDialog::OnInitDialog();
  308. m_CbnNoteBox.ResetContent();
  309. for(int note = 0; note < 12; note++)
  310. {
  311. const int item = m_CbnNoteBox.AddString(mpt::ToCString(CSoundFile::GetDefaultNoteName(note)));
  312. m_CbnNoteBox.SetItemData(item, note);
  313. if(note == m_targetNote)
  314. {
  315. m_CbnNoteBox.SetCurSel(item);
  316. }
  317. }
  318. SetDlgItemInt(IDC_EDIT1, m_pitchReference, FALSE);
  319. return TRUE;
  320. }
  321. void CAutotuneDlg::OnOK()
  322. {
  323. int pitch = GetDlgItemInt(IDC_EDIT1);
  324. if(pitch <= 0)
  325. {
  326. MessageBeep(MB_ICONWARNING);
  327. return;
  328. }
  329. CDialog::OnOK();
  330. m_targetNote = (int)m_CbnNoteBox.GetItemData(m_CbnNoteBox.GetCurSel());
  331. m_pitchReference = pitch;
  332. }
  333. OPENMPT_NAMESPACE_END