bool IPlugStandalone::SendMidiMsg(IMidiMsg* pMsg) { #ifdef OS_IOS mIOSLink->SendMidiMsg(pMsg); #else if (DoesMIDI()) { IMidiMsg newMsg = *pMsg; // if the midi channel out filter is set, reassign the status byte appropriately if (!*mMidiOutChan == 0) { newMsg.mStatus = (*mMidiOutChan)-1 | ((unsigned int) newMsg.StatusMsg() << 4) ; } std::vector<unsigned char> message; message.push_back( newMsg.mStatus ); message.push_back( newMsg.mData1 ); message.push_back( newMsg.mData2 ); mMidiOut->sendMessage( &message ); return true; } #endif return false; }
void MIDIReceiver::advance() { while (!mMidiQueue.Empty()) { IMidiMsg* midiMessage = mMidiQueue.Peek(); if (midiMessage->mOffset > mOffset) break; IMidiMsg::EStatusMsg status = midiMessage->StatusMsg(); int noteNumber = midiMessage->NoteNumber(); int velocity = midiMessage->Velocity(); // There are only note on/off messages in the queue, see ::OnMessageReceived if (status == IMidiMsg::kNoteOn && velocity) { if (mKeyStatus[noteNumber] == false) { mKeyStatus[noteNumber] = true; mNumKeys += 1; noteOn(noteNumber, velocity); } } else { if (mKeyStatus[noteNumber] == true) { mKeyStatus[noteNumber] = false; mNumKeys -= 1; noteOff(noteNumber, velocity); } } mMidiQueue.Remove(); } mOffset++; }
// We're only ever playing one note (monophonic) void Arponaut::NoteOff() { if (playing_.StatusMsg() != IMidiMsg::kNoteOff) { IMidiMsg offMsg; offMsg.MakeNoteOffMsg(playing_.NoteNumber(), playing_.mOffset); SendMidiMsg(&offMsg); playing_ = offMsg; } }
void Arponaut::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames) { // Mutex is already locked for us. ENoteLength noteLength = (ENoteLength) ((int(GetParam(kNoteLength)->Value())) + 1); EOctaves octaves = (EOctaves) ((int(GetParam(kOctaves)->Value())) + 1); EArpMode arpMode = (EArpMode) (int(GetParam(kArpMode)->Value())); EInsertMode insertMode = (EInsertMode) (int(GetParam(kInsertMode)->Value())); if (GetGUI()) { //GetGUI()->SetControlFromPlug(mMeterIdx_L, peakL); //GetGUI()->SetControlFromPlug(mMeterIdx_R, peakR); } int pos = GetSamplePos(); running_ = (pos != lastPos_); int tnum, tden; GetTimeSig(&tnum, &tden); if (keymap_.held() == 0) { NoteOff(); // only sent if a note is already playing } if (running_ && keymap_.held()) { sequence_->setOctaves(octaves); sequence_->setArpMode(arpMode); sequence_->setInsertMode(insertMode); double perBeat = GetSamplesPerBeat() / noteLength; // trigger? int ibar = static_cast<int>(double(pos) / perBeat); int ilastBar = static_cast<int>(double(lastPos_) / perBeat); if ((pos == 0 && ibar == 0) || (ibar != ilastBar)) { // Log("pos %d pb %f Num %d Den %d ibar %d lastbar %d\n", pos, perBeat, tnum, tden, ibar, ilastBar); NoteOff(); IMidiMsg* next = sequence_->next(); if (next && next->StatusMsg() == IMidiMsg::kNoteOn) { SendMidiMsg(next); playing_ = *next; } matrix->SetDirty(false); } } lastPos_ = pos; }
void Synthesis::processVirtualKeyboard() { IKeyboardControl* virtualKeyboard = (IKeyboardControl*) mVirtualKeyboard; int virtualKeyboardNoteNumber = virtualKeyboard->GetKey() + virtualKeyboardMinimumNoteNumber; if(lastVirtualKeyboardNoteNumber >= virtualKeyboardMinimumNoteNumber && virtualKeyboardNoteNumber != lastVirtualKeyboardNoteNumber) { // The note number has changed from a valid key to something else (valid key or nothing). Release the valid key: IMidiMsg midiMessage; midiMessage.MakeNoteOffMsg(lastVirtualKeyboardNoteNumber, 0); mMIDIReceiver.onMessageReceived(&midiMessage); } if (virtualKeyboardNoteNumber >= virtualKeyboardMinimumNoteNumber && virtualKeyboardNoteNumber != lastVirtualKeyboardNoteNumber) { // A valid key is pressed that wasn't pressed the previous call. Send a "note on" message to the MIDI receiver: IMidiMsg midiMessage; midiMessage.MakeNoteOnMsg(virtualKeyboardNoteNumber, virtualKeyboard->GetVelocity(), 0); mMIDIReceiver.onMessageReceived(&midiMessage); } lastVirtualKeyboardNoteNumber = virtualKeyboardNoteNumber; }
void Vega::processVirtualKeyboard() { IKeyboardControl* virtualKeyboard = (IKeyboardControl*) mVirtualKeyboard; int virtualKeyboardNoteNumber = virtualKeyboard->GetKey() + virtualKeyboardMinimumNoteNumber; if (lastVirtualKeyboardNoteNumber >= virtualKeyboardMinimumNoteNumber && virtualKeyboardNoteNumber != lastVirtualKeyboardNoteNumber) { IMidiMsg midiMessage; midiMessage.MakeNoteOffMsg(lastVirtualKeyboardNoteNumber, 0); mMIDIReceiver.onMessageReceived(&midiMessage); } if (virtualKeyboardNoteNumber >= virtualKeyboardMinimumNoteNumber && virtualKeyboardNoteNumber != lastVirtualKeyboardNoteNumber) { IMidiMsg midiMessage; midiMessage.MakeNoteOnMsg(virtualKeyboardNoteNumber, virtualKeyboard->GetVelocity(), 0); mMIDIReceiver.onMessageReceived(&midiMessage); } lastVirtualKeyboardNoteNumber = virtualKeyboardNoteNumber; }
void MIDIReceiver::advance() { while (!mMidiQueue.Empty()) { IMidiMsg* midiMessage = mMidiQueue.Peek(); if (midiMessage->mOffset > mOffset) break; IMidiMsg::EStatusMsg status = midiMessage->StatusMsg(); int noteNumber = midiMessage->NoteNumber(); int velocity = midiMessage->Velocity(); // There are only note on/off messages in the queue, see ::OnMessageReceived if (status == IMidiMsg::kNoteOn && velocity) { if (mKeyStatus[noteNumber] == false) { mKeyStatus[noteNumber] = true; mNumKeys += 1; } // A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; } } else { if (mKeyStatus[noteNumber] == true) { mKeyStatus[noteNumber] = false; mNumKeys -= 1; } // If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; mLastFrequency = -1; mLastVelocity = 0; } } mMidiQueue.Remove(); } mOffset++; }
tresult PLUGIN_API IPlugVST3Plugin::process(ProcessData& data) { TRACE_PROCESS; IMutexLock lock(this); if(data.processContext) memcpy(&mProcessContext, data.processContext, sizeof(ProcessContext)); //process parameters IParameterChanges* paramChanges = data.inputParameterChanges; if (paramChanges) { int32 numParamsChanged = paramChanges->getParameterCount(); //it is possible to get a finer resolution of control here by retrieving more values (points) from the queue //for now we just grab the last one for (int32 i = 0; i < numParamsChanged; i++) { IParamValueQueue* paramQueue = paramChanges->getParameterData(i); if (paramQueue) { int32 numPoints = paramQueue->getPointCount(); int32 offsetSamples; double value; if (paramQueue->getPoint(numPoints - 1, offsetSamples, value) == kResultTrue) { int idx = paramQueue->getParameterId(); switch (idx) { case kBypassParam: { bool bypassed = (value > 0.5); if (bypassed != mIsBypassed) { mIsBypassed = bypassed; } break; } case kPresetParam: RestorePreset(FromNormalizedParam(value, 0, NPresets(), 1.)); break; //TODO pitch bend, modwheel etc default: if (idx >= 0 && idx < NParams()) { GetParam(idx)->SetNormalized((double)value); if (GetGUI()) GetGUI()->SetParameterFromPlug(idx, (double)value, true); OnParamChange(idx); } break; } } } } } if(DoesMIDI()) { //process events.. only midi note on and note off? IEventList* eventList = data.inputEvents; if (eventList) { int32 numEvent = eventList->getEventCount(); for (int32 i=0; i<numEvent; i++) { Event event; if (eventList->getEvent(i, event) == kResultOk) { IMidiMsg msg; switch (event.type) { case Event::kNoteOnEvent: { msg.MakeNoteOnMsg(event.noteOn.pitch, event.noteOn.velocity * 127, event.sampleOffset, event.noteOn.channel); ProcessMidiMsg(&msg); break; } case Event::kNoteOffEvent: { msg.MakeNoteOffMsg(event.noteOff.pitch, event.sampleOffset, event.noteOff.channel); ProcessMidiMsg(&msg); break; } } } } } } #pragma mark process single precision if (processSetup.symbolicSampleSize == kSample32) { if (data.numInputs) { if (mScChans) { if (getAudioInput(1)->isActive()) // Sidechain is active { mSidechainActive = true; SetInputChannelConnections(0, NInChannels(), true); } else { if (mSidechainActive) { ZeroScratchBuffers(); mSidechainActive = false; } SetInputChannelConnections(0, NInChannels(), true); SetInputChannelConnections(data.inputs[0].numChannels, NInChannels() - mScChans, false); } AttachInputBuffers(0, NInChannels() - mScChans, data.inputs[0].channelBuffers32, data.numSamples); AttachInputBuffers(mScChans, NInChannels() - mScChans, data.inputs[1].channelBuffers32, data.numSamples); } else { SetInputChannelConnections(0, data.inputs[0].numChannels, true); SetInputChannelConnections(data.inputs[0].numChannels, NInChannels() - data.inputs[0].numChannels, false); AttachInputBuffers(0, NInChannels(), data.inputs[0].channelBuffers32, data.numSamples); } } for (int outBus = 0, chanOffset = 0; outBus < data.numOutputs; outBus++) { int busChannels = data.outputs[outBus].numChannels; SetOutputChannelConnections(chanOffset, busChannels, (bool) getAudioOutput(outBus)->isActive()); SetOutputChannelConnections(chanOffset + busChannels, NOutChannels() - (chanOffset + busChannels), false); AttachOutputBuffers(chanOffset, busChannels, data.outputs[outBus].channelBuffers32); chanOffset += busChannels; } if (mIsBypassed) PassThroughBuffers(0.0f, data.numSamples); else ProcessBuffers(0.0f, data.numSamples); // process buffers single precision } #pragma mark process double precision else if (processSetup.symbolicSampleSize == kSample64) { if (data.numInputs) { if (mScChans) { if (getAudioInput(1)->isActive()) // Sidechain is active { mSidechainActive = true; SetInputChannelConnections(0, NInChannels(), true); } else { if (mSidechainActive) { ZeroScratchBuffers(); mSidechainActive = false; } SetInputChannelConnections(0, NInChannels(), true); SetInputChannelConnections(data.inputs[0].numChannels, NInChannels() - mScChans, false); } AttachInputBuffers(0, NInChannels() - mScChans, data.inputs[0].channelBuffers64, data.numSamples); AttachInputBuffers(mScChans, NInChannels() - mScChans, data.inputs[1].channelBuffers64, data.numSamples); } else { SetInputChannelConnections(0, data.inputs[0].numChannels, true); SetInputChannelConnections(data.inputs[0].numChannels, NInChannels() - data.inputs[0].numChannels, false); AttachInputBuffers(0, NInChannels(), data.inputs[0].channelBuffers64, data.numSamples); } } for (int outBus = 0, chanOffset = 0; outBus < data.numOutputs; outBus++) { int busChannels = data.outputs[outBus].numChannels; SetOutputChannelConnections(chanOffset, busChannels, (bool) getAudioOutput(outBus)->isActive()); SetOutputChannelConnections(chanOffset + busChannels, NOutChannels() - (chanOffset + busChannels), false); AttachOutputBuffers(chanOffset, busChannels, data.outputs[outBus].channelBuffers64); chanOffset += busChannels; } if (mIsBypassed) PassThroughBuffers(0.0, data.numSamples); else ProcessBuffers(0.0, data.numSamples); // process buffers double precision } // Midi Out // if (mDoesMidi) { // IEventList eventList = data.outputEvents; // // if (eventList) // { // Event event; // // while (!mMidiOutputQueue.Empty()) { // //TODO: parse events and add // eventList.addEvent(event); // } // } // } return kResultOk; }
void IPlugInstrument::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames) { double* output = outputs[0]; for (int offset = 0; offset < nFrames; ++offset) { // Handle any MIDI messages in the queue. while (!mMidiQueue.Empty()) { IMidiMsg* pMsg = mMidiQueue.Peek(); // Stop when we've reached the current sample frame (offset). if (pMsg->mOffset > offset) break; // Handle the MIDI message. int status = pMsg->StatusMsg(); switch (status) { case IMidiMsg::kNoteOn: case IMidiMsg::kNoteOff: { int velocity = pMsg->Velocity(); // Note On if (status == IMidiMsg::kNoteOn && velocity) { mNote = pMsg->NoteNumber(); mFreq = 440. * pow(2., (mNote - 69.) / 12.); mGain = velocity / 127.; } // Note Off else // if (status == IMidiMsg::kNoteOff || !velocity) { if (pMsg->NoteNumber() == mNote) mNote = NONE; } break; } case IMidiMsg::kControlChange: switch (pMsg->ControlChangeIdx()) { case IMidiMsg::kChannelVolume: mVolume = pMsg->ControlChange(IMidiMsg::kChannelVolume); break; case IMidiMsg::kAllNotesOff: if (mNote != NONE || mPhase != 0.) { mNote = NONE; mPhase = 0.; } break; } break; } // Delete the MIDI message we've just handled from the queue. mMidiQueue.Remove(); } // Now that the MIDI messages have been handled we are ready to // generate a sample of audio. if (mNote == NONE) { *output++ = 0.; } else // if (mNote != NONE) { double gain = mVolume; if (mVelocity) gain *= mGain; // Output a (non-band-limited) square wave. double phase = mPhase * mFreq; if (phase - floor(phase) < 0.50) // 50% duty cycle *output++ = +gain; else *output++ = -gain; } mPhase += mSamplePeriod; } // Try to keep the phase below 1. if (mNote == NONE && mPhase >= 1.) mPhase -= floor(mPhase); // Update the offsets of any MIDI messages still in the queue. mMidiQueue.Flush(nFrames); }
bool operator() (IMidiMsg& a, IMidiMsg& b) { return (a.NoteNumber() < b.NoteNumber()); }
void Sequence::rebuild() { int held = keymap_.held(); playLength_ = held * octaves_; if (arpMode_ == kUpDown && (held*octaves_ > 2)) { playLength_ += playLength_ - 2; } if (insertMode_ == kInsertLow || insertMode_ == kInsertHi) { playLength_ *= 2; } if (playLength_ > length()) { sequence.resize(playLength_); } // clear all notes, reset position if none held if (held == 0) { for (int i=0; i < length(); ++i) { get(i)->MakeNoteOffMsg(0, 0); } pos = 0; } else { std::vector<IMidiMsg> input = keymap_.sortedEvents(); IMidiMsg loNote = input.front(); IMidiMsg hiNote = input.back(); hiNote.MakeNoteOnMsg(hiNote.NoteNumber() + 12*(octaves_-1), hiNote.Velocity(), hiNote.mOffset); if (arpMode_ == kUp || arpMode_ == kDown || arpMode_ == kUpDown) { if (arpMode_ == kDown) { std::reverse(input.begin(), input.end()); } } else { // default others to manual for now input = keymap_.events; } int iLast; for (int oct=0; oct < octaves_; ++oct) { int noteOffset = (arpMode_ == kDown) ? ((octaves_-1)*12 - oct*12) : oct*12; for (int i=0; i < held; ++i) { IMidiMsg* inp = &(input[i % held]); iLast = i + oct*held; get(iLast)->MakeNoteOnMsg(inp->NoteNumber() + noteOffset, inp->Velocity(), inp->mOffset); } } // Mirror the ascending sequence to produce an up/down sequence int iNext = iLast+1; if (arpMode_ == kUpDown) { while (--iLast > 0) { IMidiMsg* lastMsg = get(iLast); get(iNext++)->MakeNoteOnMsg(lastMsg->NoteNumber(), lastMsg->Velocity(), lastMsg->mOffset); } } if (insertMode_ == kInsertLow || insertMode_ == kInsertHi) { IMidiMsg& insertNote = (insertMode_ == kInsertLow) ? loNote : hiNote; // XXX change to using a member buffer instead of instantiating every time std::vector<IMidiMsg> seqcopy = sequence; int idx = 0; std::vector<IMidiMsg>::iterator prev = seqcopy.begin() + (playLength_/2 - 1); for (std::vector<IMidiMsg>::iterator it = seqcopy.begin(); it != seqcopy.begin() + (playLength_/2); ++it) { if (it->NoteNumber() != insertNote.NoteNumber() && prev->NoteNumber() != insertNote.NoteNumber()) { sequence[idx++] = insertNote; } sequence[idx++] = *it; prev = it; } playLength_ = idx; } } }
Sequence::Sequence(KeyMap& keymap, int length) : keymap_(keymap), pos(0), playLength_(0), octaves_(1), arpMode_(kUp), insertMode_(kInsertOff) { IMidiMsg off; off.MakeNoteOffMsg(0, 0); sequence.insert(sequence.begin(), length, off); }
tresult PLUGIN_API IPlugVST3::process(ProcessData& data) { TRACE_PROCESS; IMutexLock lock(this); // TODO: is this the best place to lock the mutex? memcpy(&mProcessContext, data.processContext, sizeof(ProcessContext)); //process parameters IParameterChanges* paramChanges = data.inputParameterChanges; if (paramChanges) { int32 numParamsChanged = paramChanges->getParameterCount(); //it is possible to get a finer resolution of control here by retrieving more values (points) from the queue //for now we just grab the last one for (int32 i = 0; i < numParamsChanged; i++) { IParamValueQueue* paramQueue = paramChanges->getParameterData(i); if (paramQueue) { int32 numPoints = paramQueue->getPointCount(); int32 offsetSamples; double value; if (paramQueue->getPoint(numPoints - 1, offsetSamples, value) == kResultTrue) { int idx = paramQueue->getParameterId(); if (idx >= 0 && idx < NParams()) { GetParam(idx)->SetNormalized((double)value); if (GetGUI()) GetGUI()->SetParameterFromPlug(idx, (double)value, true); OnParamChange(idx); } } } } } if(mDoesMidi) { //process events.. only midi note on and note off? IEventList* eventList = data.inputEvents; if (eventList) { int32 numEvent = eventList->getEventCount(); for (int32 i=0; i<numEvent; i++) { Event event; if (eventList->getEvent(i, event) == kResultOk) { IMidiMsg msg; switch (event.type) { case Event::kNoteOnEvent: { msg.MakeNoteOnMsg(event.noteOn.pitch, event.noteOn.velocity * 127, event.sampleOffset, event.noteOn.channel); ProcessMidiMsg(&msg); break; } case Event::kNoteOffEvent: { msg.MakeNoteOffMsg(event.noteOff.pitch, event.sampleOffset, event.noteOff.channel); ProcessMidiMsg(&msg); break; } } } } } } //process audio if (data.numInputs == 0 || data.numOutputs == 0) { // nothing to do return kResultOk; } if (processSetup.symbolicSampleSize == kSample32) { float** in = data.inputs[0].channelBuffers32; float** out = data.outputs[0].channelBuffers32; if (mScChans) { float** side = data.inputs[1].channelBuffers32; if (getAudioInput(1)->isActive()) { int totalNInputs = data.inputs[0].numChannels + data.inputs[1].numChannels; float** allInputs = new float*[totalNInputs]; for (int i = 0; i < data.inputs[0].numChannels; i ++) { allInputs[i] = in[i]; } for (int i = 0; i < data.inputs[1].numChannels; i ++) { allInputs[i + data.inputs[0].numChannels] = side[i]; } AttachInputBuffers(0, totalNInputs, allInputs, data.numSamples); mSideChainIsConnected = true; delete [] allInputs; } else { AttachInputBuffers(0, data.inputs[0].numChannels, in, data.numSamples); mSideChainIsConnected = false; } } else { AttachInputBuffers(0, data.inputs[0].numChannels, in, data.numSamples); } AttachOutputBuffers(0, data.outputs[0].numChannels, out); ProcessBuffers(0.0f, data.numSamples); } else if (processSetup.symbolicSampleSize == kSample64) // TODO: parity for double precision { double** in = data.inputs[0].channelBuffers64; double** out = data.outputs[0].channelBuffers64; AttachInputBuffers(0, data.inputs[0].numChannels, in, data.numSamples); AttachOutputBuffers(0, data.outputs[0].numChannels, out); ProcessBuffers(0.0, data.numSamples); } // Midi Out // if (mDoesMidi) { // IEventList eventList = data.outputEvents; // // if (eventList) // { // Event event; // // while (!mMidiOutputQueue.Empty()) { // //TODO: parse events and add // eventList.addEvent(event); // } // } // } return kResultOk; }
void IPlugMultiTargets::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames) { // Mutex is already locked for us. double* in1 = inputs[0]; double* in2 = inputs[1]; double* out1 = outputs[0]; double* out2 = outputs[1]; double peakL = 0.0, peakR = 0.0; GetTime(&mTimeInfo); IKeyboardControl* pKeyboard = (IKeyboardControl*) mKeyboard; if (pKeyboard->GetKey() != mKey) { IMidiMsg msg; if (mKey >= 0) { msg.MakeNoteOffMsg(mKey + 48, 0, 0); mMidiQueue.Add(&msg); } mKey = pKeyboard->GetKey(); if (mKey >= 0) { msg.MakeNoteOnMsg(mKey + 48, pKeyboard->GetVelocity(), 0, 0); mMidiQueue.Add(&msg); } } for (int offset = 0; offset < nFrames; ++offset, ++in1, ++in2, ++out1, ++out2) { while (!mMidiQueue.Empty()) { IMidiMsg* pMsg = mMidiQueue.Peek(); if (pMsg->mOffset > offset) break; // TODO: make this work on win sa #if !defined(OS_WIN) && !defined(SA_API) SendMidiMsg(pMsg); #endif int status = pMsg->StatusMsg(); switch (status) { case IMidiMsg::kNoteOn: case IMidiMsg::kNoteOff: { int velocity = pMsg->Velocity(); // Note On if (status == IMidiMsg::kNoteOn && velocity) { mNote = pMsg->NoteNumber(); mFreq = 440. * pow(2., (mNote - 69.) / 12.); mNoteGain = velocity / 127.; } // Note Off else // if (status == IMidiMsg::kNoteOff || !velocity) { if (pMsg->NoteNumber() == mNote) mNote = -1; mNoteGain = 0.; } break; } } mMidiQueue.Remove(); } *out1 = sin( 2. * M_PI * mFreq * mPhase / mSampleRate ) * mGainLSmoother.Process(mGainL * mNoteGain); *out2 = sin( 2. * M_PI * mFreq * 1.01 * (mPhase++) / mSampleRate ) * mGainRSmoother.Process(mGainR * mNoteGain); peakL = IPMAX(peakL, fabs(*out1)); peakR = IPMAX(peakR, fabs(*out2)); } const double METER_ATTACK = 0.6, METER_DECAY = 0.05; double xL = (peakL < mPrevL ? METER_DECAY : METER_ATTACK); double xR = (peakR < mPrevR ? METER_DECAY : METER_ATTACK); peakL = peakL * xL + mPrevL * (1.0 - xL); peakR = peakR * xR + mPrevR * (1.0 - xR); mPrevL = peakL; mPrevR = peakR; if (GetGUI()) { GetGUI()->SetControlFromPlug(mMeterIdx_L, peakL); GetGUI()->SetControlFromPlug(mMeterIdx_R, peakR); } mMidiQueue.Flush(nFrames); }