Audacity 3.2.0
StaffPadTimeAndPitch.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cassert>
5#include <cmath>
6#include <memory>
7
8namespace
9{
10// Let's use StaffPad's default value. (We have to reproduce it here as it has
11// to be specified in the `setup` call.)
12constexpr auto maxBlockSize = 1024;
13
15 float** offsetBuffer, float* const* buffer, size_t numChannels,
16 size_t offset)
17{
18 for (auto i = 0u; i < numChannels; ++i)
19 offsetBuffer[i] = buffer[i] + offset;
20}
21
22std::unique_ptr<staffpad::TimeAndPitch> MaybeCreateTimeAndPitch(
23 int sampleRate, size_t numChannels,
25{
26 const auto timeRatio = params.timeRatio.value_or(1.);
27 const auto pitchRatio = params.pitchRatio.value_or(1.);
28 if (
31 {
32 return nullptr;
33 }
34 auto timeAndPitch = std::make_unique<staffpad::TimeAndPitch>(sampleRate);
35 timeAndPitch->setup(static_cast<int>(numChannels), maxBlockSize);
36 timeAndPitch->setTimeStretchAndPitchFactor(timeRatio, pitchRatio);
37 return timeAndPitch;
38}
39} // namespace
40
42 int sampleRate, size_t numChannels, TimeAndPitchSource& audioSource,
43 const Parameters& parameters)
44 : mAudioSource(audioSource)
45 , mReadBuffer(maxBlockSize, numChannels)
46 , mSampleRate(sampleRate)
47 , mNumChannels(numChannels)
48 , mTimeRatio(parameters.timeRatio.value_or(1.))
49 , mPitchRatio(parameters.pitchRatio.value_or(1.))
50 , mTimeAndPitch(
51 MaybeCreateTimeAndPitch(sampleRate, numChannels, parameters))
52{
54}
55
56void StaffPadTimeAndPitch::GetSamples(float* const* output, size_t outputLen)
57{
58 if (!mTimeAndPitch)
59 // Pass-through
60 return mAudioSource.Pull(output, outputLen);
61
62 std::lock_guard<std::mutex> lock(mTimeAndPitchMutex);
63 // No need to re-check for `mTimeAndPitch`, because once set it's never
64 // unset.
65
66 auto numOutputSamples = 0u;
67 while (numOutputSamples < outputLen)
68 {
69 if (IllState())
70 {
71 for (auto i = 0u; i < mNumChannels; ++i)
72 std::fill_n(
73 output[i] + numOutputSamples, outputLen - numOutputSamples, 0.f);
74 return;
75 }
76 auto numOutputSamplesAvailable =
77 mTimeAndPitch->getNumAvailableOutputSamples();
78 while (numOutputSamplesAvailable <= 0)
79 {
80 auto numRequired = mTimeAndPitch->getSamplesToNextHop();
81 while (numRequired > 0)
82 {
83 const auto numSamplesToFeed = std::min(numRequired, maxBlockSize);
84 mAudioSource.Pull(mReadBuffer.Get(), numSamplesToFeed);
85 mTimeAndPitch->feedAudio(mReadBuffer.Get(), numSamplesToFeed);
86 numRequired -= numSamplesToFeed;
87 }
88 numOutputSamplesAvailable =
89 mTimeAndPitch->getNumAvailableOutputSamples();
90 }
91 while (numOutputSamples < outputLen && numOutputSamplesAvailable > 0)
92 {
93 const auto numSamplesToGet =
94 std::min({ maxBlockSize, numOutputSamplesAvailable,
95 static_cast<int>(outputLen - numOutputSamples) });
96 // More-than-stereo isn't supported
97 assert(mNumChannels <= 2);
98 float* buffer[2] {};
99 GetOffsetBuffer(buffer, output, mNumChannels, numOutputSamples);
100 mTimeAndPitch->retrieveAudio(buffer, numSamplesToGet);
101 numOutputSamplesAvailable -= numSamplesToGet;
102 numOutputSamples += numSamplesToGet;
103 }
104 }
105}
106
108{
109 std::lock_guard<std::mutex> lock(mTimeAndPitchMutex);
110 // We don't unset `mTimeAndPitch` here, only reset it. If for some reason
111 // this needs to change, then `BootStretcher` and `GetSamples` need
112 // double-checked locking.
113
114 mPitchRatio = std::pow(2., cents / 1200.);
115 if (!mTimeAndPitch)
119 else
120 mTimeAndPitch->setTimeStretchAndPitchFactor(mTimeRatio, mPitchRatio);
121}
122
124{
125 if (!mTimeAndPitch)
126 // Bypass
127 return;
128
129 std::lock_guard<std::mutex> lock(mTimeAndPitchMutex);
130 // No need to re-check for `mTimeAndPitch`, because once set it's never
131 // unset.
132
133 auto numOutputSamplesToDiscard =
134 mTimeAndPitch->getLatencySamplesForStretchRatio(mTimeRatio * mPitchRatio);
136 while (numOutputSamplesToDiscard > 0)
137 {
138 if (IllState())
139 return;
140 auto numRequired = mTimeAndPitch->getSamplesToNextHop();
141 while (numRequired > 0)
142 {
143 const auto numSamplesToFeed = std::min(maxBlockSize, numRequired);
144 mAudioSource.Pull(container.Get(), numSamplesToFeed);
145 mTimeAndPitch->feedAudio(container.Get(), numSamplesToFeed);
146 numRequired -= numSamplesToFeed;
147 }
148 const auto totalNumSamplesToRetrieve = std::min(
149 mTimeAndPitch->getNumAvailableOutputSamples(),
150 numOutputSamplesToDiscard);
151 auto totalNumRetrievedSamples = 0;
152 while (totalNumRetrievedSamples < totalNumSamplesToRetrieve)
153 {
154 const auto numSamplesToRetrieve = std::min(
155 maxBlockSize, totalNumSamplesToRetrieve - totalNumRetrievedSamples);
156 mTimeAndPitch->retrieveAudio(container.Get(), numSamplesToRetrieve);
157 totalNumRetrievedSamples += numSamplesToRetrieve;
158 }
159 numOutputSamplesToDiscard -= totalNumSamplesToRetrieve;
160 }
161}
162
164{
165 // It doesn't require samples, yet it doesn't have output samples available.
166 // Note that this must not be a permanent state, and may recover if the user
167 // changes the pitch shift.
168 // TODO: try to fix this in the stretcher implementation.
169 return mTimeAndPitch->getSamplesToNextHop() <= 0 &&
170 mTimeAndPitch->getNumAvailableOutputSamples() <= 0;
171}
int min(int a, int b)
EffectDistortionSettings params
Definition: Distortion.cpp:77
StaffPadTimeAndPitch(int sampleRate, size_t numChannels, TimeAndPitchSource &, const Parameters &)
TimeAndPitchSource & mAudioSource
void OnCentShiftChange(int cents) override
void GetSamples(float *const *, size_t) override
std::unique_ptr< staffpad::TimeAndPitch > mTimeAndPitch
static bool IsPassThroughMode(double stretchRatio)
virtual void Pull(float *const *, size_t samplesPerChannel)=0
std::unique_ptr< staffpad::TimeAndPitch > MaybeCreateTimeAndPitch(int sampleRate, size_t numChannels, const TimeAndPitchInterface::Parameters &params)
void GetOffsetBuffer(float **offsetBuffer, float *const *buffer, size_t numChannels, size_t offset)
float *const * Get() const