tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_PitchShift.cpp
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
15{
16 static constexpr int samplesPerBlock = 512;
17
18 Pimpl (PitchShiftPlugin& p) : owner (p)
19 {
20 }
21
22 void initialise (double sr, float semitonesUp, TimeStretcher::Mode newMode, TimeStretcher::ElastiqueProOptions newOptions)
23 {
24 if (timestretcher == nullptr || mode != newMode || elastiqueOptions != newOptions)
25 {
26 mode = newMode;
27 elastiqueOptions = newOptions;
28 timestretcher = std::make_unique<TimeStretcher>();
29 }
30
31 if (! timestretcher->isInitialised())
32 {
33 timestretcher->initialise (sr, samplesPerBlock, 2, mode, elastiqueOptions, true);
34 jassert (timestretcher->isInitialised());
35 }
36
37 if (timestretcher->isInitialised())
38 {
39 timestretcher->reset();
40 timestretcher->setSpeedAndPitch (1.0f, semitonesUp);
41
42 latencySamples = timestretcher->getMaxFramesNeeded();
43
44 inputFifo.setSize (2, latencySamples + 2000);
45 outputFifo.setSize (2, latencySamples + 2000);
46 }
47
48 inputFifo.reset();
49 outputFifo.reset();
50
51 outputFifo.writeSilence (latencySamples);
52
53 latencySeconds = latencySamples / sr;
54 }
55
56 void applyToBuffer (const PluginRenderContext& fc, float semis)
57 {
58 SCOPED_REALTIME_CHECK
59
60 if (timestretcher->isInitialised())
61 {
62 auto newMode = (TimeStretcher::Mode) owner.mode.get();
63 auto newOptions = owner.elastiqueOptions.get();
64
65 if (newMode != mode || newOptions != elastiqueOptions)
66 initialise (owner.sampleRate, semis, newMode, newOptions);
67
68 inputFifo.write (*fc.destBuffer, fc.bufferStartSample, fc.bufferNumSamples);
69
70 if (! timestretcher->setSpeedAndPitch (1.0f, semis))
72
73 // Loop until output FIFO has enough samples to proceed
74 {
75 int needed = timestretcher->getFramesNeeded();
76
77 for (;;)
78 {
79 if (inputFifo.getNumReady() < needed)
80 break;
81
82 if (outputFifo.getFreeSpace() < samplesPerBlock)
83 break;
84
85 timestretcher->processData (inputFifo, needed, outputFifo);
86 needed = timestretcher->getFramesNeeded();
87
88 if (outputFifo.getNumReady() >= fc.bufferNumSamples)
89 break;
90 }
91 }
92
93 if (outputFifo.getNumReady() < fc.bufferNumSamples)
94 {
97 }
98 else
99 {
100 outputFifo.read (*fc.destBuffer, fc.bufferStartSample, fc.bufferNumSamples);
101 }
102
103 if (fc.bufferForMidiMessages != nullptr)
104 fc.bufferForMidiMessages->addToNoteNumbers (juce::roundToInt (semis));
105 }
106 }
107
108 PitchShiftPlugin& owner;
109
110 std::unique_ptr<TimeStretcher> timestretcher;
112 TimeStretcher::ElastiqueProOptions elastiqueOptions;
113
114 AudioFifo inputFifo { 2, 2000 }, outputFifo { 2, 2000 };
115
116 int latencySamples = 8192;
117 double latencySeconds = latencySamples / 44100.0;
118
120};
121
122//==============================================================================
123PitchShiftPlugin::PitchShiftPlugin (PluginCreationInfo info) : Plugin (info)
124{
125 pimpl = std::make_unique<Pimpl> (*this);
126
127 auto um = getUndoManager();
128
129 semitones = addParam ("semitones up", TRANS("Semitones"),
130 { -PitchShiftPlugin::getMaximumSemitones(), PitchShiftPlugin::getMaximumSemitones() },
131 [] (float value) { return std::abs (value) < 0.01f ? "(" + TRANS("Original pitch") + ")"
132 : getSemitonesAsString (value); },
133 [] (const juce::String& s) { return juce::jlimit (-PitchShiftPlugin::getMaximumSemitones(),
134 PitchShiftPlugin::getMaximumSemitones(),
135 s.getFloatValue()); });
136
137 semitonesValue.referTo (state, IDs::semitonesUp, um);
138 mode.referTo (state, IDs::mode, um, (int) TimeStretcher::defaultMode);
139 elastiqueOptions.referTo (state, IDs::elastiqueOptions, um);
140
141 semitones->attachToCurrentValue (semitonesValue);
142}
143
144PitchShiftPlugin::PitchShiftPlugin (Edit& ed, const juce::ValueTree& v)
145 : PitchShiftPlugin (PluginCreationInfo (ed, v, false))
146{
147}
148
149PitchShiftPlugin::~PitchShiftPlugin()
150{
151 notifyListenersOfDeletion();
152
153 semitones->detachFromCurrentValue();
154}
155
156juce::ValueTree PitchShiftPlugin::create()
157{
158 return createValueTree (IDs::PLUGIN,
159 IDs::type, xmlTypeName);
160}
161
162const char* PitchShiftPlugin::xmlTypeName = "pitchShifter";
163
164//==============================================================================
165void PitchShiftPlugin::initialise (const PluginInitialisationInfo& info)
166{
167 pimpl->initialise (info.sampleRate, semitones->getCurrentValue(),
168 (TimeStretcher::Mode) mode.get(), elastiqueOptions.get());
169}
170
174
176{
177 pimpl->applyToBuffer (fc, semitones->getCurrentValue());
178
179 clearChannels (*fc.destBuffer, 2, -1, fc.bufferStartSample, fc.bufferNumSamples);
180}
181
182double PitchShiftPlugin::getLatencySeconds()
183{
184 return pimpl->latencySeconds;
185}
186
188{
189 return TRANS("Pitch Shifter Plugin");
190}
191
192void PitchShiftPlugin::restorePluginStateFromValueTree (const juce::ValueTree& v)
193{
194 copyPropertiesToCachedValues (v, semitonesValue, mode);
195
196 for (auto p : getAutomatableParameters())
197 p->updateFromAttachedValue();
198}
199
200}} // namespace tracktion { inline namespace engine
void clear() noexcept
Type get() const noexcept
void applyToBuffer(const PluginRenderContext &) override
Process the next block of data.
juce::String getSelectableDescription() override
Subclasses must return a description of what they are.
void deinitialise() override
Called after play stops to release resources.
Mode
Holds the various algorithms to which can be used (if enabled).
T is_pointer_v
#define TRANS(stringLiteral)
#define jassert(expression)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
#define jassertfalse
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
int roundToInt(const FloatType value) noexcept
Passed into Plugins when they are being initialised, to give them useful contextual information that ...
The context passed to plugin render methods to provide it with buffers to fill.
int bufferNumSamples
The number of samples to write into the audio buffer.
MidiMessageArray * bufferForMidiMessages
A buffer of MIDI events to process.
juce::AudioBuffer< float > * destBuffer
The target audio buffer which needs to be filled.
int bufferStartSample
The index of the start point in the audio buffer from which data must be written.
A set of options that can be used in conjunction with the elastiquePro Mode to fine tune the algorith...