123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- #include "stdafx.h"
- #include "Autotune.h"
- #include <math.h>
- #include "../common/misc_util.h"
- #include "../soundlib/Sndfile.h"
- #include <algorithm>
- #include <execution>
- #include <numeric>
- #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2)
- #include <emmintrin.h>
- #endif
- OPENMPT_NAMESPACE_BEGIN
- #define BINS_PER_NOTE 32
- #define MIN_SAMPLE_LENGTH 2
- #define START_NOTE (24 * BINS_PER_NOTE)
- #define END_NOTE (96 * BINS_PER_NOTE)
- #define HISTORY_BINS (12 * BINS_PER_NOTE)
- static double FrequencyToNote(double freq, double pitchReference)
- {
- return ((12.0 * (log(freq / (pitchReference / 2.0)) / log(2.0))) + 57.0);
- }
- static double NoteToFrequency(double note, double pitchReference)
- {
- return pitchReference * pow(2.0, (note - 69.0) / 12.0);
- }
- static SmpLength NoteToShift(uint32 sampleFreq, int note, double pitchReference)
- {
- const double fundamentalFrequency = NoteToFrequency((double)note / BINS_PER_NOTE, pitchReference);
- return std::max(mpt::saturate_round<SmpLength>((double)sampleFreq / fundamentalFrequency), SmpLength(1));
- }
- template <class T>
- void Autotune::CopySamples(const T* origSample, SmpLength sampleLoopStart, SmpLength sampleLoopEnd)
- {
- const uint8 channels = m_sample.GetNumChannels();
- sampleLoopStart *= channels;
- sampleLoopEnd *= channels;
- for(SmpLength i = 0, pos = 0; i < m_sampleLength; i++, pos += channels)
- {
- if(pos >= sampleLoopEnd)
- {
- pos = sampleLoopStart;
- }
- const T* smp = origSample + pos;
- int32 data = 0;
- for(uint8 chn = 0; chn < channels; chn++)
- {
-
- data += static_cast<int32>(smp[chn] >> ((sizeof(T) - 1) * 8));
- }
- data /= channels;
- m_sampleData[i] = static_cast<int16>(data);
- }
- }
- bool Autotune::PrepareSample(SmpLength maxShift)
- {
-
- SmpLength sampleOffset = 0, sampleLoopStart = 0, sampleLoopEnd = m_sample.nLength;
- if(m_selectionEnd >= sampleLoopStart + MIN_SAMPLE_LENGTH)
- {
-
- sampleOffset = m_selectionStart;
- sampleLoopStart = 0;
- sampleLoopEnd = m_selectionEnd - m_selectionStart;
- } else if(m_sample.uFlags[CHN_SUSTAINLOOP] && m_sample.nSustainEnd >= m_sample.nSustainStart + MIN_SAMPLE_LENGTH)
- {
-
- sampleOffset = 0;
- sampleLoopStart = m_sample.nSustainStart;
- sampleLoopEnd = m_sample.nSustainEnd;
- } else if(m_sample.uFlags[CHN_LOOP] && m_sample.nLoopEnd >= m_sample.nLoopStart + MIN_SAMPLE_LENGTH)
- {
-
- sampleOffset = 0;
- sampleLoopStart = m_sample.nLoopStart;
- sampleLoopEnd = m_sample.nLoopEnd;
- }
-
- m_sampleLength = std::max(sampleLoopEnd, static_cast<SmpLength>(m_sample.GetSampleRate(m_modType))) + maxShift;
- m_sampleLength = (m_sampleLength + 7) & ~7;
- if(m_sampleData != nullptr)
- {
- delete[] m_sampleData;
- }
- m_sampleData = new int16[m_sampleLength];
- if(m_sampleData == nullptr)
- {
- return false;
- }
-
- switch(m_sample.GetElementarySampleSize())
- {
- case 1:
- CopySamples(m_sample.sample8() + sampleOffset * m_sample.GetNumChannels(), sampleLoopStart, sampleLoopEnd);
- return true;
- case 2:
- CopySamples(m_sample.sample16() + sampleOffset * m_sample.GetNumChannels(), sampleLoopStart, sampleLoopEnd);
- return true;
- }
- return false;
- }
- bool Autotune::CanApply() const
- {
- return (m_sample.HasSampleData() && m_sample.nLength >= MIN_SAMPLE_LENGTH) || m_sample.uFlags[CHN_ADLIB];
- }
- namespace
- {
- struct AutotuneHistogramEntry
- {
- int index;
- uint64 sum;
- };
- struct AutotuneHistogram
- {
- std::array<uint64, HISTORY_BINS> histogram{};
- };
- struct AutotuneContext
- {
- const int16 *m_sampleData;
- double pitchReference;
- SmpLength processLength;
- uint32 sampleFreq;
- };
- #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2)
- static inline AutotuneHistogramEntry CalculateNoteHistogramSSE2(int note, AutotuneContext ctx)
- {
- const SmpLength autocorrShift = NoteToShift(ctx.sampleFreq, note, ctx.pitchReference);
- uint64 autocorrSum = 0;
- {
- const __m128i *normalData = reinterpret_cast<const __m128i *>(ctx.m_sampleData);
- const __m128i *shiftedData = reinterpret_cast<const __m128i *>(ctx.m_sampleData + autocorrShift);
- for(SmpLength i = ctx.processLength / 8; i != 0; i--)
- {
- __m128i normal = _mm_loadu_si128(normalData++);
- __m128i shifted = _mm_loadu_si128(shiftedData++);
- __m128i diff = _mm_sub_epi16(normal, shifted);
- __m128i squares = _mm_madd_epi16(diff, diff);
- __m128i sum1 = _mm_shuffle_epi32(squares, _MM_SHUFFLE(0, 1, 2, 3));
- __m128i sum2 = _mm_add_epi32(squares, sum1);
- __m128i sum3 = _mm_shuffle_epi32(sum2, _MM_SHUFFLE(1, 1, 1, 1));
- __m128i sum4 = _mm_add_epi32(sum2, sum3);
- autocorrSum += _mm_cvtsi128_si32(sum4);
- }
- }
- return {note % HISTORY_BINS, autocorrSum};
- }
- #endif
- static inline AutotuneHistogramEntry CalculateNoteHistogram(int note, AutotuneContext ctx)
- {
- const SmpLength autocorrShift = NoteToShift(ctx.sampleFreq, note, ctx.pitchReference);
- uint64 autocorrSum = 0;
- {
- const int16 *normalData = ctx.m_sampleData;
- const int16 *shiftedData = ctx.m_sampleData + autocorrShift;
-
- for(SmpLength i = ctx.processLength; i != 0; i--, normalData++, shiftedData++)
- {
- autocorrSum += (*normalData - *shiftedData) * (*normalData - *shiftedData);
- }
- }
- return {note % HISTORY_BINS, autocorrSum};
- }
- static inline AutotuneHistogram operator+(AutotuneHistogram a, AutotuneHistogram b) noexcept
- {
- AutotuneHistogram result;
- for(std::size_t i = 0; i < HISTORY_BINS; ++i)
- {
- result.histogram[i] = a.histogram[i] + b.histogram[i];
- }
- return result;
- }
- static inline AutotuneHistogram & operator+=(AutotuneHistogram &a, AutotuneHistogram b) noexcept
- {
- for(std::size_t i = 0; i < HISTORY_BINS; ++i)
- {
- a.histogram[i] += b.histogram[i];
- }
- return a;
- }
- static inline AutotuneHistogram &operator+=(AutotuneHistogram &a, AutotuneHistogramEntry b) noexcept
- {
- a.histogram[b.index] += b.sum;
- return a;
- }
- struct AutotuneHistogramReduce
- {
- inline AutotuneHistogram operator()(AutotuneHistogram a, AutotuneHistogram b) noexcept
- {
- return a + b;
- }
- inline AutotuneHistogram operator()(AutotuneHistogramEntry a, AutotuneHistogramEntry b) noexcept
- {
- AutotuneHistogram result;
- result += a;
- result += b;
- return result;
- }
- inline AutotuneHistogram operator()(AutotuneHistogramEntry a, AutotuneHistogram b) noexcept
- {
- b += a;
- return b;
- }
- inline AutotuneHistogram operator()(AutotuneHistogram a, AutotuneHistogramEntry b) noexcept
- {
- a += b;
- return a;
- }
- };
- }
- bool Autotune::Apply(double pitchReference, int targetNote)
- {
- if(!CanApply())
- {
- return false;
- }
- const uint32 sampleFreq = m_sample.GetSampleRate(m_modType);
-
- const SmpLength maxShift = NoteToShift(sampleFreq, START_NOTE, pitchReference);
- if(!PrepareSample(maxShift))
- {
- return false;
- }
-
- const SmpLength processLength = m_sampleLength - maxShift;
- AutotuneContext ctx;
- ctx.m_sampleData = m_sampleData;
- ctx.pitchReference = pitchReference;
- ctx.processLength = processLength;
- ctx.sampleFreq = sampleFreq;
-
-
- std::array<int, END_NOTE - START_NOTE> notes;
- std::iota(notes.begin(), notes.end(), START_NOTE);
- AutotuneHistogram autocorr =
- #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE2)
- (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); } ) :
- #endif
- std::transform_reduce(std::execution::par_unseq, std::begin(notes), std::end(notes), AutotuneHistogram{}, AutotuneHistogramReduce{}, [ctx](int note) { return CalculateNoteHistogram(note, ctx); } );
-
-
- AutotuneHistogram interpolated;
- for(int i = 0; i < HISTORY_BINS; i++)
- {
- interpolated.histogram[i] = autocorr.histogram[i];
- const int kernelWidth = 4;
- for(int ki = kernelWidth; ki >= 0; ki--)
- {
-
- int left = i - ki;
- if(left < 0) left += HISTORY_BINS;
- int right = i + ki;
- if(right >= HISTORY_BINS) right -= HISTORY_BINS;
- interpolated.histogram[i] = interpolated.histogram[i] / 2 + (autocorr.histogram[left] + autocorr.histogram[right]) / 2;
- }
- }
-
- int minimumBin = static_cast<int>(std::min_element(std::begin(interpolated.histogram), std::end(interpolated.histogram)) - std::begin(interpolated.histogram));
-
- if(targetNote >= 6)
- {
- targetNote -= 12;
- }
-
- minimumBin -= targetNote * BINS_PER_NOTE;
- if(minimumBin >= 6 * BINS_PER_NOTE)
- {
- minimumBin -= 12 * BINS_PER_NOTE;
- }
- minimumBin += targetNote * BINS_PER_NOTE;
- const double newFundamentalFreq = NoteToFrequency(static_cast<double>(69 - targetNote) + static_cast<double>(minimumBin) / BINS_PER_NOTE, pitchReference);
- if(const auto newFreq = mpt::saturate_round<uint32>(sampleFreq * pitchReference / newFundamentalFreq); newFreq != sampleFreq)
- m_sample.nC5Speed = newFreq;
- else
- return false;
- if((m_modType & (MOD_TYPE_XM | MOD_TYPE_MOD)))
- {
- m_sample.FrequencyToTranspose();
- if((m_modType & MOD_TYPE_MOD))
- {
- m_sample.RelativeTone = 0;
- }
- }
- return true;
- }
- int CAutotuneDlg::m_pitchReference = 440;
- int CAutotuneDlg::m_targetNote = 0;
- void CAutotuneDlg::DoDataExchange(CDataExchange* pDX)
- {
- CDialog::DoDataExchange(pDX);
-
- DDX_Control(pDX, IDC_COMBO1, m_CbnNoteBox);
-
- }
- BOOL CAutotuneDlg::OnInitDialog()
- {
- CDialog::OnInitDialog();
- m_CbnNoteBox.ResetContent();
- for(int note = 0; note < 12; note++)
- {
- const int item = m_CbnNoteBox.AddString(mpt::ToCString(CSoundFile::GetDefaultNoteName(note)));
- m_CbnNoteBox.SetItemData(item, note);
- if(note == m_targetNote)
- {
- m_CbnNoteBox.SetCurSel(item);
- }
- }
- SetDlgItemInt(IDC_EDIT1, m_pitchReference, FALSE);
- return TRUE;
- }
- void CAutotuneDlg::OnOK()
- {
- int pitch = GetDlgItemInt(IDC_EDIT1);
- if(pitch <= 0)
- {
- MessageBeep(MB_ICONWARNING);
- return;
- }
- CDialog::OnOK();
- m_targetNote = (int)m_CbnNoteBox.GetItemData(m_CbnNoteBox.GetCurSel());
- m_pitchReference = pitch;
- }
- OPENMPT_NAMESPACE_END
|