11#include "pluginterfaces/vst/ivstparameterchanges.h"
12#include "pluginterfaces/vst/vstspeaker.h"
13#include "pluginterfaces/vst/ivstmidicontrollers.h"
14#include "public.sdk/source/vst/vsteventshelper.h"
15#include "IPlugVST3_ProcessorBase.h"
18using namespace Steinberg;
21#ifndef CUSTOM_BUSTYPE_FUNC
22uint64_t iplug::GetAPIBusTypeForChannelIOConfig(
int configIdx,
ERoute dir,
int busIdx,
const IOConfig* pConfig, WDL_TypedBuf<uint64_t>* APIBusTypes)
24 assert(pConfig !=
nullptr);
25 assert(busIdx >= 0 && busIdx < pConfig->NBuses(dir));
27 int numChans = pConfig->
GetBusInfo(dir, busIdx)->NChans();
31 case 0:
return SpeakerArr::kEmpty;
32 case 1:
return SpeakerArr::kMono;
33 case 2:
return SpeakerArr::kStereo;
34 case 3:
return SpeakerArr::k30Cine;
35 case 4:
return SpeakerArr::kAmbi1stOrderACN;
36 case 5:
return SpeakerArr::k50;
37 case 6:
return SpeakerArr::k51;
38 case 7:
return SpeakerArr::k70Cine;
39 case 8:
return SpeakerArr::k71CineSideFill;
40 case 9:
return SpeakerArr::kAmbi2cdOrderACN;
41 case 10:
return SpeakerArr::k71_2;
42 case 16:
return SpeakerArr::kAmbi3rdOrderACN;
44 DBGMSG(
"do not yet know what to do here\n");
46 return SpeakerArr::kEmpty;
51IPlugVST3ProcessorBase::IPlugVST3ProcessorBase(Config c,
IPlugAPIBase& plug)
55 SetChannelConnections(ERoute::kInput, 0, MaxNChannels(ERoute::kInput),
true);
56 SetChannelConnections(ERoute::kOutput, 0, MaxNChannels(ERoute::kOutput),
true);
58 mMaxNChansForMainInputBus = MaxNChannelsForBus(ERoute::kInput, 0);
62 IPlugProcessor::SetBlockSize(DEFAULT_BLOCK_SIZE);
65 memset(&mProcessContext, 0,
sizeof(ProcessContext));
74 int32 numEvent = pEventList->getEventCount();
75 for (int32 i=0; i<numEvent; i++)
78 if (pEventList->getEvent(i, event) == kResultOk)
82 case Event::kNoteOnEvent:
84 msg.
MakeNoteOnMsg(event.noteOn.pitch, event.noteOn.velocity * 127, event.sampleOffset, event.noteOn.channel);
86 processorQueue.
Push(msg);
90 case Event::kNoteOffEvent:
92 msg.
MakeNoteOffMsg(event.noteOff.pitch, event.sampleOffset, event.noteOff.channel);
94 processorQueue.
Push(msg);
97 case Event::kPolyPressureEvent:
99 msg.
MakePolyATMsg(event.polyPressure.pitch, event.polyPressure.pressure * 127., event.sampleOffset, event.polyPressure.channel);
101 processorQueue.
Push(msg);
104 case Event::kDataEvent:
106 ISysEx syx =
ISysEx(event.sampleOffset, event.data.bytes, event.data.size);
115 while (editorQueue.
Pop(msg))
123 if (!mMidiOutputQueue.Empty() && pOutputEvents)
128 while (!mMidiOutputQueue.Empty())
130 IMidiMsg& msg = mMidiOutputQueue.Peek();
132 if (msg.
StatusMsg() == IMidiMsg::kNoteOn)
134 Helpers::init(toAdd, Event::kNoteOnEvent, 0 , msg.mOffset);
136 toAdd.noteOn.channel = msg.
Channel();
138 toAdd.noteOn.tuning = 0.;
139 toAdd.noteOn.velocity = (float) msg.
Velocity() * (1.f / 127.f);
140 pOutputEvents->addEvent(toAdd);
142 else if (msg.
StatusMsg() == IMidiMsg::kNoteOff)
144 Helpers::init(toAdd, Event::kNoteOffEvent, 0 , msg.mOffset);
146 toAdd.noteOff.channel = msg.
Channel();
148 toAdd.noteOff.velocity = (float) msg.
Velocity() * (1.f / 127.f);
149 pOutputEvents->addEvent(toAdd);
151 else if (msg.
StatusMsg() == IMidiMsg::kPolyAftertouch)
153 Helpers::initLegacyMIDICCOutEvent(toAdd, ControllerNumbers::kCtrlPolyPressure, msg.
Channel(), msg.mData1, msg.mData2);
154 toAdd.sampleOffset = msg.mOffset;
155 pOutputEvents->addEvent(toAdd);
157 else if (msg.
StatusMsg() == IMidiMsg::kChannelAftertouch)
159 Helpers::initLegacyMIDICCOutEvent(toAdd, ControllerNumbers::kAfterTouch, msg.
Channel(), msg.mData1, msg.mData2);
160 toAdd.sampleOffset = msg.mOffset;
161 pOutputEvents->addEvent(toAdd);
163 else if (msg.
StatusMsg() == IMidiMsg::kProgramChange)
165 Helpers::initLegacyMIDICCOutEvent(toAdd, ControllerNumbers::kCtrlProgramChange, msg.
Channel(), msg.
Program(), 0);
166 toAdd.sampleOffset = msg.mOffset;
167 pOutputEvents->addEvent(toAdd);
169 else if (msg.
StatusMsg() == IMidiMsg::kControlChange)
171 Helpers::initLegacyMIDICCOutEvent(toAdd, msg.mData1, msg.
Channel(), msg.mData2, 0 );
172 toAdd.sampleOffset = msg.mOffset;
173 pOutputEvents->addEvent(toAdd);
175 else if (msg.
StatusMsg() == IMidiMsg::kPitchWheel)
177 toAdd.type = Event::kLegacyMIDICCOutEvent;
178 toAdd.midiCCOut.channel = msg.
Channel();
179 toAdd.sampleOffset = msg.mOffset;
180 toAdd.midiCCOut.controlNumber = ControllerNumbers::kPitchBend;
181 int16 tmp =
static_cast<int16
> (msg.
PitchWheel() * 0x3FFF);
182 toAdd.midiCCOut.value = (tmp & 0x7F);
183 toAdd.midiCCOut.value2 = ((tmp >> 7) & 0x7F);
184 pOutputEvents->addEvent(toAdd);
187 mMidiOutputQueue.Remove();
191 mMidiOutputQueue.Flush(numSamples);
198 while (sysExQueue.
Pop(sysExBuf))
200 toAdd.type = Event::kDataEvent;
201 toAdd.sampleOffset = sysExBuf.mOffset;
202 toAdd.data.type = DataEvent::kMidiSysEx;
203 toAdd.data.size = sysExBuf.mSize;
204 toAdd.data.bytes = (uint8*) sysExBuf.mData;
205 pOutputEvents->addEvent(toAdd);
210void IPlugVST3ProcessorBase::AttachBuffers(
ERoute direction,
int idx,
int n, AudioBusBuffers& pBus,
int nFrames, int32 sampleSize)
212 if (sampleSize == kSample32)
213 IPlugProcessor::AttachBuffers(direction, idx, n, pBus.channelBuffers32, nFrames);
214 else if (sampleSize == kSample64)
215 IPlugProcessor::AttachBuffers(direction, idx, n, pBus.channelBuffers64, nFrames);
218bool IPlugVST3ProcessorBase::SetupProcessing(
const ProcessSetup& setup, ProcessSetup& storedSetup)
220 if ((setup.symbolicSampleSize != kSample32) && (setup.symbolicSampleSize != kSample64))
225 SetSampleRate(setup.sampleRate);
226 IPlugProcessor::SetBlockSize(setup.maxSamplesPerBlock);
227 mMidiOutputQueue.Resize(setup.maxSamplesPerBlock);
233bool IPlugVST3ProcessorBase::SetProcessing(
bool state)
241bool IPlugVST3ProcessorBase::CanProcessSampleSize(int32 symbolicSampleSize)
243 switch (symbolicSampleSize)
246 case kSample64:
return true;
247 default:
return false;
251void IPlugVST3ProcessorBase::PrepareProcessContext(ProcessData& data, ProcessSetup& setup)
255 if (data.processContext)
256 memcpy(&mProcessContext, data.processContext,
sizeof(ProcessContext));
258 if (mProcessContext.state & ProcessContext::kProjectTimeMusicValid)
259 timeInfo.mSamplePos = (double) mProcessContext.projectTimeSamples;
260 timeInfo.mPPQPos = mProcessContext.projectTimeMusic;
261 timeInfo.mTempo = mProcessContext.tempo;
262 timeInfo.mLastBar = mProcessContext.barPositionMusic;
263 timeInfo.mCycleStart = mProcessContext.cycleStartMusic;
264 timeInfo.mCycleEnd = mProcessContext.cycleEndMusic;
265 timeInfo.mNumerator = mProcessContext.timeSigNumerator;
266 timeInfo.mDenominator = mProcessContext.timeSigDenominator;
267 timeInfo.mTransportIsRunning = mProcessContext.state & ProcessContext::kPlaying;
268 timeInfo.mTransportLoopEnabled = mProcessContext.state & ProcessContext::kCycleActive;
269 const bool offline = setup.processMode == Steinberg::Vst::kOffline;
270 SetTimeInfo(timeInfo);
271 SetRenderingOffline(offline);
274void IPlugVST3ProcessorBase::ProcessParameterChanges(ProcessData& data,
IPlugQueue<IMidiMsg>& fromProcessor)
276 IParameterChanges* paramChanges = data.inputParameterChanges;
280 int32 numParamsChanged = paramChanges->getParameterCount();
282 for (int32 i = 0; i < numParamsChanged; i++)
284 IParamValueQueue* paramQueue = paramChanges->getParameterData(i);
287 int32 numPoints = paramQueue->getPointCount();
291 if (paramQueue->getPoint(numPoints - 1, offsetSamples, value) == kResultTrue)
293 int idx = paramQueue->getParameterId();
299 const bool bypassed = (value > 0.5);
302 SetBypassed(bypassed);
308 if (idx >= 0 && idx < mPlug.NParams())
311 mPlug.mParams_mutex.Enter();
313 mPlug.GetParam(idx)->SetNormalized(value);
316 mPlug.OnParamChange(idx, kHost, offsetSamples);
318 mPlug.mParams_mutex.Leave();
321 else if (idx >= kMIDICCParamStartIdx)
323 int index = idx - kMIDICCParamStartIdx;
324 int channel = index / kCountCtrlNumber;
325 int ctrlr = index % kCountCtrlNumber;
329 if (ctrlr == kAfterTouch)
331 else if (ctrlr == kPitchBend)
336 fromProcessor.
Push(msg);
348void IPlugVST3ProcessorBase::ProcessAudio(ProcessData& data, ProcessSetup& setup,
const BusList& ins,
const BusList& outs)
350 int32 sampleSize = setup.symbolicSampleSize;
352 if (sampleSize == kSample32 || sampleSize == kSample64)
356 SetChannelConnections(ERoute::kInput, 0,
MaxNChannels(ERoute::kInput),
false);
360 if (ins[1].get()->isActive())
362 mSidechainActive =
true;
363 SetChannelConnections(ERoute::kInput, 0, data.inputs[0].numChannels,
true);
364 SetChannelConnections(ERoute::kInput, mMaxNChansForMainInputBus, data.inputs[1].numChannels,
true);
368 if (mSidechainActive)
370 ZeroScratchBuffers();
371 mSidechainActive =
false;
374 SetChannelConnections(ERoute::kInput, 0, data.inputs[0].numChannels,
true);
377 AttachBuffers(ERoute::kInput, 0, data.inputs[0].numChannels, data.inputs[0], data.numSamples, sampleSize);
380 AttachBuffers(ERoute::kInput, mMaxNChansForMainInputBus, data.inputs[1].numChannels, data.inputs[1], data.numSamples, sampleSize);
384 SetChannelConnections(ERoute::kInput, 0,
MaxNChannels(ERoute::kInput),
false);
385 SetChannelConnections(ERoute::kInput, 0, data.inputs[0].numChannels,
true);
386 AttachBuffers(ERoute::kInput, 0, data.inputs[0].numChannels, data.inputs[0], data.numSamples, sampleSize);
390 for (
int outBus = 0, chanOffset = 0; outBus < data.numOutputs; outBus++)
392 int busChannels = data.outputs[outBus].numChannels;
393 SetChannelConnections(ERoute::kOutput, chanOffset, busChannels, outs[outBus].get()->isActive());
394 SetChannelConnections(ERoute::kOutput, chanOffset + busChannels,
MaxNChannels(ERoute::kOutput) - (chanOffset + busChannels),
false);
395 AttachBuffers(ERoute::kOutput, chanOffset, busChannels, data.outputs[outBus], data.numSamples, sampleSize);
396 chanOffset += busChannels;
401 if (sampleSize == kSample32)
402 PassThroughBuffers(0.f, data.numSamples);
404 PassThroughBuffers(0.0, data.numSamples);
409 mPlug.mParams_mutex.Enter();
411 if (sampleSize == kSample32)
412 ProcessBuffers(0.f, data.numSamples);
414 ProcessBuffers(0.0, data.numSamples);
416 mPlug.mParams_mutex.Leave();
424 PrepareProcessContext(data, setup);
425 ProcessParameterChanges(data, fromProcessor);
429 ProcessMidiIn(data.inputEvents, fromEditor, fromProcessor);
432 ProcessAudio(data, setup, ins, outs);
436 ProcessMidiOut(sysExFromEditor, sysExBuf, data.outputEvents, data.numSamples);
442 mMidiOutputQueue.Add(msg);
The base class of an IPlug plug-in, which interacts with the different plug-in APIs.
The base class for IPlug Audio Processing.
virtual void ProcessMidiMsg(const IMidiMsg &msg)
Override this method to handle incoming MIDI messages.
virtual void ProcessSysEx(const ISysEx &msg)
Override this method to handle incoming MIDI System Exclusive (SysEx) messages.
int MaxNChannels(ERoute direction) const
virtual void OnReset()
Override this method in your plug-in class to do something prior to playback etc.
A lock-free SPSC queue used to transfer data between threads based on MLQueue.h by Randy Jones based ...
size_t ElementsAvailable() const
bool SendMidiMsg(const IMidiMsg &msg) override
Send a single MIDI message // TODO: info about what thread should this be called on or not called on!
ERoute
Used to identify whether a bus/channel connection is an input or an output.
Encapsulates information about the host transport state.
Encapsulates a MIDI message and provides helper functions.
void MakeNoteOnMsg(int noteNumber, int velocity, int offset, int channel=0)
Make a Note On message.
int Channel() const
Gets the channel of a MIDI message.
void MakeNoteOffMsg(int noteNumber, int offset, int channel=0)
Make a Note Off message.
void MakePitchWheelMsg(double value, int channel=0, int offset=0)
Create a pitch wheel/bend message.
int Velocity() const
Get the velocity value of a NoteOn/NoteOff message.
void MakeControlChangeMsg(EControlChangeMsg idx, double value, int channel=0, int offset=0)
Create a CC message.
int Program() const
Get the program index from a Program Change message.
EControlChangeMsg
Constants for MIDI CC messages.
void MakeChannelATMsg(int pressure, int offset, int channel)
Create a Channel AfterTouch message.
void MakePolyATMsg(int noteNumber, int pressure, int offset, int channel)
Create a Poly AfterTouch message.
double PitchWheel() const
Get the value from a Pitchwheel message.
int NoteNumber() const
Gets the MIDI note number.
EStatusMsg StatusMsg() const
Gets the MIDI Status message.
An IOConfig is used to store bus info for each input/output configuration defined in the channel io s...
const IBusInfo * GetBusInfo(ERoute direction, int index) const
A struct for dealing with SysEx messages.
This structure is used when queueing Sysex messages.