AAX_Result IPlugAAX::SetChunk(AAX_CTypeID chunkID, const AAX_SPlugInChunk * iChunk ) { TRACE; if (chunkID == GetUniqueID()) { ByteChunk IPlugChunk; IPlugChunk.PutBytes(iChunk->fData, iChunk->fSize); int pos = 0; //GetIPlugVerFromChunk(&IPlugChunk, &pos); pos = UnserializeState(&IPlugChunk, pos); for (int i = 0; i< NParams(); i++) { SetParameterNormalizedValue(mParamIDs.Get(i)->Get(), GetParam(i)->GetNormalized() ); } RedrawParamControls(); //TODO: what about icontrols not linked to params how do they get redrawn - setdirty via UnserializeState()? mNumPlugInChanges++; // necessary in order to cause CompareActiveChunk() to get called again and turn off the compare light return AAX_SUCCESS; } return AAX_ERROR_INVALID_CHUNK_ID; }
AAX_Result IPlugAAX::UpdateParameterNormalizedValue(AAX_CParamID iParameterID, double iValue, AAX_EUpdateSource iSource ) { TRACE; AAX_Result result = AAX_SUCCESS; AAX_IParameter* parameter = mParameterManager.GetParameterByID(iParameterID); if (parameter == 0) return AAX_ERROR_INVALID_PARAMETER_ID; // Store the value into the parameter. parameter->UpdateNormalizedValue(iValue); int paramIdx = atoi(iParameterID) - kAAXParamIdxOffset; if ((paramIdx >= 0) && (paramIdx < NParams())) { IMutexLock lock(this); GetParam(paramIdx)->SetNormalized(iValue); if (GetGUI()) { GetGUI()->SetParameterFromPlug(paramIdx, iValue, true); } OnParamChange(paramIdx, kAutomation); } // Now the control has changed result = mPacketDispatcher.SetDirty(iParameterID); mNumPlugInChanges++; return result; }
tresult PLUGIN_API IPlugVST3Plugin::initialize (FUnknown* context) { TRACE; tresult result = SingleComponentEffect::initialize(context); String128 tmpStringBuf; char hostNameCString[128]; FUnknownPtr<IHostApplication>app(context); if (app) { app->getName(tmpStringBuf); Steinberg::UString(tmpStringBuf, 128).toAscii(hostNameCString, 128); SetHost(hostNameCString, 0); // Can't get version in VST3 } if (result == kResultOk) { int maxInputs = getSpeakerArrForChans(NInChannels()-mScChans); if(maxInputs < 0) maxInputs = 0; // add io buses with the maximum i/o to start with if (maxInputs) { Steinberg::UString(tmpStringBuf, 128).fromAscii(GetInputBusLabel(0)->Get(), 128); addAudioInput(tmpStringBuf, maxInputs); } if(!mIsInst) // if effect, just add one output bus with max chan count { Steinberg::UString(tmpStringBuf, 128).fromAscii(GetOutputBusLabel(0)->Get(), 128); addAudioOutput(tmpStringBuf, getSpeakerArrForChans(NOutChannels()) ); } else { for (int i = 0, busIdx = 0; i < NOutChannels(); i+=2, busIdx++) { Steinberg::UString(tmpStringBuf, 128).fromAscii(GetOutputBusLabel(busIdx)->Get(), 128); addAudioOutput(tmpStringBuf, SpeakerArr::kStereo ); } } if (mScChans) { if (mScChans > 2) mScChans = 2; Steinberg::UString(tmpStringBuf, 128).fromAscii(GetInputBusLabel(1)->Get(), 128); addAudioInput(tmpStringBuf, getSpeakerArrForChans(mScChans), kAux, 0); } if(DoesMIDI()) { addEventInput (STR16("MIDI Input"), 1); //addEventOutput(STR16("MIDI Output"), 1); } if (NPresets()) { parameters.addParameter(new Parameter(STR16("Preset"), kPresetParam, STR16(""), 0, NPresets(), ParameterInfo::kIsProgramChange)); } if(!mIsInst) { StringListParameter * bypass = new StringListParameter(STR16("Bypass"), kBypassParam, 0, ParameterInfo::kCanAutomate | ParameterInfo::kIsBypass | ParameterInfo::kIsList); bypass->appendString(STR16("off")); bypass->appendString(STR16("on")); parameters.addParameter(bypass); } for (int i=0; i<NParams(); i++) { IParam *p = GetParam(i); int32 flags = 0; UnitID unitID = kRootUnitId; const char* paramGroupName = p->GetParamGroupForHost(); if (CSTR_NOT_EMPTY(paramGroupName)) { for(int j = 0; j < mParamGroups.GetSize(); j++) { if(strcmp(paramGroupName, mParamGroups.Get(j)) == 0) { unitID = j+1; } } if (unitID == kRootUnitId) // new unit, nothing found, so add it { mParamGroups.Add(paramGroupName); unitID = mParamGroups.GetSize(); } } if (p->GetCanAutomate()) { flags |= ParameterInfo::kCanAutomate; } switch (p->Type()) { case IParam::kTypeDouble: case IParam::kTypeInt: { Parameter* param = new RangeParameter( STR16(p->GetNameForHost()), i, STR16(p->GetLabelForHost()), p->GetMin(), p->GetMax(), p->GetDefault(), 0, // continuous flags, unitID); param->setPrecision (p->GetPrecision()); parameters.addParameter(param); break; } case IParam::kTypeEnum: case IParam::kTypeBool: { StringListParameter* param = new StringListParameter (STR16(p->GetNameForHost()), i, STR16(p->GetLabelForHost()), flags | ParameterInfo::kIsList, unitID); int nDisplayTexts = p->GetNDisplayTexts(); assert(nDisplayTexts); for (int j=0; j<nDisplayTexts; j++) { param->appendString(STR16(p->GetDisplayText(j))); } parameters.addParameter(param); break; } default: break; } } } OnHostIdentified(); RestorePreset(0); return result; }
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; }
tresult PLUGIN_API IPlugVST3::initialize (FUnknown* context) { TRACE; tresult result = SingleComponentEffect::initialize (context); if (result == kResultOk) { addAudioInput (STR16("Audio Input"), getSpeakerArrForChans(NInChannels()) ); addAudioOutput (STR16("Audio Output"), getSpeakerArrForChans(NOutChannels()) ); if (mScChans == 1) addAudioInput(STR16("Sidechain Input"), SpeakerArr::kMono, kAux, 0); else if (mScChans >= 2) { mScChans = 2; addAudioInput(STR16("Sidechain Input"), SpeakerArr::kStereo, kAux, 0); } if(mDoesMidi) { addEventInput (STR16("MIDI In"), 1); addEventOutput(STR16("MIDI Out"), 1); } for (int i=0;i<NParams();i++) { IParam *p = GetParam(i); int32 flags = 0; if (p->GetCanAutomate()) { flags |= ParameterInfo::kCanAutomate; } switch (p->Type()) { case IParam::kTypeDouble: case IParam::kTypeInt: { Parameter* param = new RangeParameter ( STR16(p->GetNameForHost()), i, STR16(p->GetLabelForHost()), p->GetMin(), p->GetMax(), p->GetDefault(), p->GetStep(), flags); param->setPrecision (p->GetPrecision()); parameters.addParameter (param); break; } case IParam::kTypeEnum: case IParam::kTypeBool: { StringListParameter* param = new StringListParameter (STR16(p->GetNameForHost()), i, STR16(p->GetLabelForHost()), flags | ParameterInfo::kIsList); int nDisplayTexts = p->GetNDisplayTexts(); assert(nDisplayTexts); for (int j=0; j<nDisplayTexts; j++) { param->appendString(STR16(p->GetDisplayText(j))); } parameters.addParameter (param); break; } default: break; } } } return result; }
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; }
AAX_Result IPlugAAX::EffectInit() { TRACE; AAX_CString bypassID = NULL; this->GetMasterBypassParameter( &bypassID ); mBypassParameter = new AAX_CParameter<bool>(bypassID.CString(), AAX_CString("Master Bypass"), false, AAX_CBinaryTaperDelegate<bool>(), AAX_CBinaryDisplayDelegate<bool>("bypass", "on"), true); mBypassParameter->SetNumberOfSteps( 2 ); mBypassParameter->SetType( AAX_eParameterType_Discrete ); mParameterManager.AddParameter(mBypassParameter); for (int i=0;i<NParams();i++) { IParam *p = GetParam(i); AAX_IParameter* param = 0; WDL_String* paramID = new WDL_String("_", 1); paramID->SetFormatted(32, "%i", i+kAAXParamIdxOffset); mParamIDs.Add(paramID); switch (p->Type()) { case IParam::kTypeDouble: { param = new AAX_CParameter<double>(paramID->Get(), AAX_CString(p->GetNameForHost()), p->GetDefault(), AAX_CIPlugTaperDelegate<double>(p->GetMin(), p->GetMax(), p->GetShape()), AAX_CUnitDisplayDelegateDecorator<double>( AAX_CNumberDisplayDelegate<double>(), AAX_CString(p->GetLabelForHost())), p->GetCanAutomate()); param->SetNumberOfSteps(128); // TODO: check this https://developer.digidesign.com/index.php?L1=5&L2=13&L3=56 param->SetType(AAX_eParameterType_Continuous); break; } case IParam::kTypeInt: { param = new AAX_CParameter<int>(paramID->Get(), AAX_CString(p->GetNameForHost()), (int)p->GetDefault(), AAX_CLinearTaperDelegate<int>((int)p->GetMin(), (int)p->GetMax()), AAX_CUnitDisplayDelegateDecorator<int>( AAX_CNumberDisplayDelegate<int>(), AAX_CString(p->GetLabelForHost())), p->GetCanAutomate()); param->SetNumberOfSteps(128); param->SetType(AAX_eParameterType_Continuous); break; } case IParam::kTypeEnum: case IParam::kTypeBool: { int nTexts = p->GetNDisplayTexts(); std::map<int, AAX_CString> displayTexts; for (int j=0; j<p->GetNDisplayTexts(); j++) { int value; const char* text = p->GetDisplayTextAtIdx(j, &value); displayTexts.insert(std::pair<int, AAX_CString>(value, AAX_CString(text)) ); } param = new AAX_CParameter<int>(paramID->Get(), AAX_CString(p->GetNameForHost()), (int)p->GetDefault(), AAX_CLinearTaperDelegate<int>((int)p->GetMin(), (int)p->GetMax()), AAX_CStringDisplayDelegate<int>(displayTexts), p->GetCanAutomate()); param->SetNumberOfSteps(nTexts); param->SetType(AAX_eParameterType_Discrete); break; } default: break; } mParameterManager.AddParameter(param); } AAX_CSampleRate sr; Controller()->GetSampleRate(&sr); SetSampleRate(sr); Reset(); return AAX_SUCCESS; }