bool LadspaEffect::ProcessStereo(int count, WaveTrack *left, WaveTrack *right, sampleCount lstart, sampleCount rstart, sampleCount len) { /* Allocate buffers */ if (mBlockSize == 0) { mBlockSize = left->GetMaxBlockSize() * 2; fInBuffer = new float *[inputs]; unsigned long i; for (i = 0; i < inputs; i++) fInBuffer[i] = new float[mBlockSize]; fOutBuffer = new float *[outputs]; for (i = 0; i < outputs; i++) fOutBuffer[i] = new float[mBlockSize]; } /* Instantiate the plugin */ unsigned long rate = (unsigned long)(left->GetRate() + 0.5); LADSPA_Handle handle = mData->instantiate(mData, rate); unsigned long p; for(p=0; p<inputs; p++) { mData->connect_port(handle, inputPorts[p], fInBuffer[p]); } for(p=0; p<outputs; p++) { mData->connect_port(handle, outputPorts[p], fOutBuffer[p]); } for(p=0; p<mData->PortCount; p++) { LADSPA_PortDescriptor d = mData->PortDescriptors[p]; if (LADSPA_IS_PORT_CONTROL(d)) { if (LADSPA_IS_PORT_INPUT(d)) { mData->connect_port(handle, p, &inputControls[p]); } else mData->connect_port(handle, p, &outputControls[p]); } } if (mData->activate) mData->activate(handle); // Actually perform the effect here sampleCount originalLen = len; sampleCount ls = lstart; sampleCount rs = rstart; while (len) { int block = mBlockSize; if (block > len) block = len; if (left && inputs > 0) { left->Get((samplePtr)fInBuffer[0], floatSample, ls, block); } if (right && inputs > 1) { right->Get((samplePtr)fInBuffer[1], floatSample, rs, block); } mData->run(handle, block); if (left && outputs > 0) { left->Set((samplePtr)fOutBuffer[0], floatSample, ls, block); } if (right && outputs > 1) { right->Set((samplePtr)fOutBuffer[1], floatSample, rs, block); } len -= block; ls += block; rs += block; if (inputs > 1) { if (TrackGroupProgress(count, (ls-lstart)/(double)originalLen)) return false; } else { if (TrackProgress(count, (ls-lstart)/(double)originalLen)) return false; } } if (mData->deactivate) mData->deactivate(handle); if (mData->cleanup) mData->cleanup(handle); return true; }
bool VampEffect::Process() { if (!mPlugin) return false; TrackListIterator iter(mWaveTracks); int count = 0; WaveTrack *left = (WaveTrack *)iter.First(); bool multiple = false; int prevTrackChannels = 0; TrackListIterator scooter(iter); if (left->GetLinked()) scooter.Next(); if (scooter.Next()) { // if there is another track beyond this one and any linked one, // then we're processing more than one track. That means we // should use the originating track name in each new label // track's name, to make clear which is which multiple = true; } while (left) { sampleCount lstart, rstart; sampleCount len; GetSamples(left, &lstart, &len); WaveTrack *right = NULL; int channels = 1; if (left->GetLinked()) { right = (WaveTrack *)iter.Next(); channels = 2; GetSamples(right, &rstart, &len); } size_t step = mPlugin->getPreferredStepSize(); size_t block = mPlugin->getPreferredBlockSize(); bool initialiseRequired = true; if (block == 0) { if (step != 0) block = step; else block = 1024; } if (step == 0) { step = block; } if (prevTrackChannels > 0) { // Plugin has already been initialised, so if the number of // channels remains the same, we only need to do a reset. // Otherwise we need to re-construct the whole plugin, // because a Vamp plugin can't be re-initialised. if (prevTrackChannels == channels) { mPlugin->reset(); initialiseRequired = false; } else { //!!! todo: retain parameters previously set Init(); } } if (initialiseRequired) { if (!mPlugin->initialise(channels, step, block)) { wxMessageBox(_("Sorry, Vamp Plug-in failed to initialize.")); return false; } } LabelTrack *ltrack = mFactory->NewLabelTrack(); if (!multiple) { ltrack->SetName(GetEffectName()); } else { ltrack->SetName(wxString::Format(wxT("%s: %s"), left->GetName().c_str(), GetEffectName().c_str())); } mTracks->Add(ltrack); float **data = new float*[channels]; for (int c = 0; c < channels; ++c) data[c] = new float[block]; sampleCount originalLen = len; sampleCount ls = lstart; sampleCount rs = rstart; while (len) { int request = block; if (request > len) request = len; if (left) left->Get((samplePtr)data[0], floatSample, ls, request); if (right) right->Get((samplePtr)data[1], floatSample, rs, request); if (request < (int)block) { for (int c = 0; c < channels; ++c) { for (int i = request; i < (int)block; ++i) { data[c][i] = 0.f; } } } Vamp::RealTime timestamp = Vamp::RealTime::frame2RealTime (ls, (int)(mRate + 0.5)); Vamp::Plugin::FeatureSet features = mPlugin->process(data, timestamp); AddFeatures(ltrack, features); if (len > (int)step) len -= step; else len = 0; ls += step; rs += step; if (channels > 1) { if (TrackGroupProgress(count, (ls - lstart) / double(originalLen))) return false; } else { if (TrackProgress(count, (ls - lstart) / double(originalLen))) return false; } } Vamp::Plugin::FeatureSet features = mPlugin->getRemainingFeatures(); AddFeatures(ltrack, features); prevTrackChannels = channels; left = (WaveTrack *)iter.Next(); } return true; }
bool LV2Effect::ProcessStereo(int count, WaveTrack *left, WaveTrack *right, sampleCount lstart, sampleCount rstart, sampleCount len) { /* Allocate buffers */ if (mBlockSize == 0) { mBlockSize = left->GetMaxBlockSize() * 2; fInBuffer = new float *[mAudioInputs.GetCount()]; for (size_t i = 0; i < mAudioInputs.GetCount(); i++) { fInBuffer[i] = new float[mBlockSize]; } fOutBuffer = new float *[mAudioOutputs.GetCount()]; for (size_t i = 0; i < mAudioOutputs.GetCount(); i++) { fOutBuffer[i] = new float[mBlockSize]; } } /* Instantiate the plugin */ LilvInstance *handle = lilv_plugin_instantiate(mData, left->GetRate(), gLV2Features); if (!handle) { wxMessageBox(wxString::Format(_("Unable to load plug-in %s"), pluginName.c_str())); return false; } /* Write the Note On to the MIDI event buffer and connect it */ LV2_Event_Buffer *midiBuffer = NULL; int noteOffTime; if (mMidiInput) { midiBuffer = lv2_event_buffer_new(40, 2); LV2_Event_Iterator iter; lv2_event_begin(&iter, midiBuffer); uint8_t noteOn[] = { 0x90, mNoteKey, mNoteVelocity }; lv2_event_write(&iter, 0, 0, 1, 3, noteOn); noteOffTime = mNoteLength * left->GetRate(); if (noteOffTime < len && noteOffTime < mBlockSize) { uint8_t noteOff[] = { 0x80, mNoteKey, 64 }; lv2_event_write(&iter, noteOffTime, 0, 1, 3, noteOff); } lilv_instance_connect_port(handle, mMidiInput->mIndex, midiBuffer); } for (size_t p = 0; p < mAudioInputs.GetCount(); p++) { lilv_instance_connect_port(handle, mAudioInputs[p].mIndex, fInBuffer[p]); } for (size_t p = 0; p < mAudioOutputs.GetCount(); p++) { lilv_instance_connect_port(handle, mAudioOutputs[p].mIndex, fOutBuffer[p]); } for (size_t p = 0; p < mControlInputs.GetCount(); p++) { lilv_instance_connect_port(handle, mControlInputs[p].mIndex, &mControlInputs[p].mControlBuffer); } for (size_t p = 0; p < mControlOutputs.GetCount(); p++) { lilv_instance_connect_port(handle, mControlOutputs[p].mIndex, &mControlOutputs[p].mControlBuffer); } float latency = 0.0; if (mLatencyPortIndex >= 0) { lilv_instance_connect_port(handle, mLatencyPortIndex, &latency); } lilv_instance_activate(handle); // Actually perform the effect here sampleCount originalLen = len; sampleCount ls = lstart; sampleCount rs = rstart; sampleCount ols = ls; sampleCount ors = rs; bool noteOver = false; sampleCount delayed = 0; sampleCount delay = 0; bool cleared = false; while (len || delayed) { int block = mBlockSize; if (len) { if (block > len) { block = len; } if (left && mAudioInputs.GetCount() > 0) { left->Get((samplePtr)fInBuffer[0], floatSample, ls, block); } if (right && mAudioInputs.GetCount() > 1) { right->Get((samplePtr)fInBuffer[1], floatSample, rs, block); } } else if (delayed) { // At the end if we don't have enough left for a whole block if (block > delayed) { block = delayed; } // Clear the input buffer so that we only pass zeros to the effect. if (!cleared) { for (int i = 0; i < mBlockSize; i++) { fInBuffer[0][i] = 0.0; } if (right) { memcpy(fInBuffer[1], fOutBuffer[0], mBlockSize); } cleared = true; } } lilv_instance_run(handle, block); if (delayed == 0 && latency != 0) { delayed = delay = latency; } if (delay >= block) { delay -= block; } else if (delay > 0) { sampleCount oblock = block - delay; if (left && mAudioOutputs.GetCount() > 0) { left->Set((samplePtr)(fOutBuffer[0] + delay), floatSample, ols, oblock); } if (right && mAudioOutputs.GetCount() > 1) { right->Set((samplePtr)(fOutBuffer[1] + delay), floatSample, ors, oblock); } ols += oblock; ors += oblock; delay = 0; } else { if (left && mAudioOutputs.GetCount() > 0) { left->Set((samplePtr)fOutBuffer[0], floatSample, ols, block); } if (right && mAudioOutputs.GetCount() > 1) { right->Set((samplePtr)fOutBuffer[1], floatSample, ors, block); } ols += block; ors += block; } if (len) { len -= block; noteOffTime -= block; // Clear the event buffer and add the note off event if needed if (mMidiInput) { lv2_event_buffer_reset(midiBuffer, 1, (uint8_t *)midiBuffer + sizeof(LV2_Event_Buffer)); if (!noteOver && noteOffTime < len && noteOffTime < block) { LV2_Event_Iterator iter; lv2_event_begin(&iter, midiBuffer); uint8_t noteOff[] = { 0x80, mNoteKey, 64 }; lv2_event_write(&iter, noteOffTime, 0, 1, 3, noteOff); noteOver = true; } } } else if (delayed) { delayed -= block; } ls += block; rs += block; if (mAudioInputs.GetCount() > 1) { if (TrackGroupProgress(count, (ls-lstart)/(double)originalLen)) { return false; } } else { if (TrackProgress(count, (ls-lstart)/(double)originalLen)) { return false; } } } lilv_instance_deactivate(handle); lilv_instance_free(handle); return true; }
bool VSTEffect::ProcessStereo(int count, WaveTrack *left, WaveTrack *right, sampleCount lstart, sampleCount rstart, sampleCount len) { bool rc = true; // Initialize time info mTimeInfo.samplePos = 0.0; mTimeInfo.sampleRate = left->GetRate(); mTimeInfo.flags |= kVstTransportPlaying; // Turn the power on callDispatcher(effMainsChanged, 0, 1, NULL, 0.0); // Tell effect we're starting to process callDispatcher(effStartProcess, 0, 0, NULL, 0.0); // Actually perform the effect here sampleCount originalLen = len; sampleCount ls = lstart; sampleCount rs = rstart; while (len) { int block = mBlockSize; if (block > len) { block = len; } left->Get((samplePtr)mInBuffer[0], floatSample, ls, block); if (right) { right->Get((samplePtr)mInBuffer[1], floatSample, rs, block); } callProcessReplacing(mInBuffer, mOutBuffer, block); left->Set((samplePtr)mOutBuffer[0], floatSample, ls, block); if (right) { right->Set((samplePtr)mOutBuffer[1], floatSample, rs, block); } len -= block; ls += block; rs += block; mTimeInfo.samplePos += ((double) block / mTimeInfo.sampleRate); if (mInputs > 1) { if (TrackGroupProgress(count, (ls - lstart) / (double)originalLen)) { rc = false; break; } } else { if (TrackProgress(count, (ls - lstart) / (double)originalLen)) { rc = false; break; } } } // Tell effect we're done callDispatcher(effStopProcess, 0, 0, NULL, 0.0); // Turn the power off callDispatcher(effMainsChanged, 0, 0, NULL, 0.0); // No longer playing mTimeInfo.samplePos = 0.0; mTimeInfo.sampleRate = 44100.0; mTimeInfo.tempo = 120.0; mTimeInfo.timeSigNumerator = 4; mTimeInfo.timeSigDenominator = 4; mTimeInfo.flags = kVstTempoValid | kVstNanosValid; return rc; }
bool LV2Effect::ProcessStereo(int count, WaveTrack *left, WaveTrack *right, sampleCount lstart, sampleCount rstart, sampleCount len) { /* Allocate buffers */ if (mBlockSize == 0) { mBlockSize = left->GetMaxBlockSize() * 2; fInBuffer = new float *[mAudioInputs.size()]; unsigned long i; for (i = 0; i < mAudioInputs.size(); i++) fInBuffer[i] = new float[mBlockSize]; fOutBuffer = new float *[mAudioOutputs.size()]; for (i = 0; i < mAudioOutputs.size(); i++) fOutBuffer[i] = new float[mBlockSize]; } /* Instantiate the plugin */ SLV2Instance handle = slv2_plugin_instantiate(mData, left->GetRate(), gLV2Features); /* Write the Note On to the MIDI event buffer and connect it */ LV2_Event_Buffer* midiBuffer; int noteOffTime; if (mMidiInput) { midiBuffer = lv2_event_buffer_new(40, 2); LV2_Event_Iterator iter; lv2_event_begin(&iter, midiBuffer); uint8_t noteOn[] = { 0x90, mNoteKey, mNoteVelocity }; lv2_event_write(&iter, 0, 0, 1, 3, noteOn); noteOffTime = mNoteLength * left->GetRate(); if (noteOffTime < len && noteOffTime < mBlockSize) { uint8_t noteOff[] = { 0x80, mNoteKey, 64 }; lv2_event_write(&iter, noteOffTime, 0, 1, 3, noteOff); } slv2_instance_connect_port(handle, mMidiInput->mIndex, midiBuffer); } unsigned long p; for(p = 0; p < mAudioInputs.size(); p++) { slv2_instance_connect_port(handle, mAudioInputs[p].mIndex, fInBuffer[p]); } for(p = 0; p < mAudioOutputs.size(); p++) { slv2_instance_connect_port(handle, mAudioOutputs[p].mIndex, fOutBuffer[p]); } for (p = 0; p < mControlInputs.size(); p++) { slv2_instance_connect_port(handle, mControlInputs[p].mIndex, &mControlInputs[p].mControlBuffer); } for (p = 0; p < mControlOutputs.size(); p++) { slv2_instance_connect_port(handle, mControlOutputs[p].mIndex, &mControlOutputs[p].mControlBuffer); } slv2_instance_activate(handle); // Actually perform the effect here sampleCount originalLen = len; sampleCount ls = lstart; sampleCount rs = rstart; bool noteOver = false; while (len) { int block = mBlockSize; if (block > len) block = len; if (left && mAudioInputs.size() > 0) { left->Get((samplePtr)fInBuffer[0], floatSample, ls, block); } if (right && mAudioInputs.size() > 1) { right->Get((samplePtr)fInBuffer[1], floatSample, rs, block); } slv2_instance_run(handle, block); if (left && mAudioOutputs.size() > 0) { left->Set((samplePtr)fOutBuffer[0], floatSample, ls, block); } if (right && mAudioOutputs.size() > 1) { right->Set((samplePtr)fOutBuffer[1], floatSample, rs, block); } len -= block; noteOffTime -= block; ls += block; rs += block; // Clear the event buffer and add the note off event if needed if (mMidiInput) { lv2_event_buffer_reset(midiBuffer, 1, (uint8_t*)midiBuffer + sizeof(LV2_Event_Buffer)); if (!noteOver && noteOffTime < len && noteOffTime < block) { LV2_Event_Iterator iter; lv2_event_begin(&iter, midiBuffer); uint8_t noteOff[] = { 0x80, mNoteKey, 64 }; lv2_event_write(&iter, noteOffTime, 0, 1, 3, noteOff); noteOver = true; } } if (mAudioInputs.size() > 1) { if (TrackGroupProgress(count, (ls-lstart)/(double)originalLen)) return false; } else { if (TrackProgress(count, (ls-lstart)/(double)originalLen)) return false; } } slv2_instance_deactivate(handle); slv2_instance_free(handle); return true; }
bool VSTEffect::ProcessStereo(int count, WaveTrack *left, WaveTrack *right, sampleCount lstart, sampleCount rstart, sampleCount len) { bool rc = true; sampleCount amountLeft = 0; // Initialize time info mTimeInfo.samplePos = 0.0; mTimeInfo.sampleRate = left->GetRate(); mTimeInfo.flags |= kVstTransportPlaying; // Turn the power on callDispatcher(effMainsChanged, 0, 1, NULL, 0.0); // Tell effect we're starting to process callDispatcher(effStartProcess, 0, 0, NULL, 0.0); // Actually perform the effect here sampleCount originalLen = len; sampleCount ls = lstart; sampleCount rs = rstart; sampleCount outls = lstart; sampleCount outrs = rstart; sampleCount outBufferCursor = 0; float **outBufSegment = new float *[mOutputs]; while (len) { int block = mBlockSize; if (block > len) { block = len; } left->Get((samplePtr)mInBuffer[0], floatSample, ls, block); if (right) { right->Get((samplePtr)mInBuffer[1], floatSample, rs, block); } for (int i = 0; i < mOutputs; i++) outBufSegment[i] = mOutBuffer[i ] + outBufferCursor; callProcessReplacing(mInBuffer, outBufSegment, block); outBufferCursor += block; //Process 2 audacity blockfiles per WaveTrack::Set independently of mBlockSize //because it is extremely slow to do multiple Set()s per blockfile due to Undo History //If we do more optimization we should probably align the Sets to blockfile boundries. if (outBufferCursor >= mWTBlockSize) { left->Set((samplePtr)mOutBuffer[0], floatSample, outls, mWTBlockSize); if (right) { right->Set((samplePtr)mOutBuffer[1], floatSample, outrs, mWTBlockSize); } if (outBufferCursor >= mWTBlockSize) { //snake the buffer down memmove(mOutBuffer[0], mOutBuffer[0] + mWTBlockSize, SAMPLE_SIZE(floatSample) * (outBufferCursor - mWTBlockSize)); memmove(mOutBuffer[1], mOutBuffer[1] + mWTBlockSize, SAMPLE_SIZE(floatSample) * (outBufferCursor - mWTBlockSize)); } outBufferCursor -= mWTBlockSize; outls += mWTBlockSize; outrs += mWTBlockSize; } len -= block; ls += block; rs += block; mTimeInfo.samplePos += ((double) block / mTimeInfo.sampleRate); if (mInputs > 1) { if (TrackGroupProgress(count, (ls - lstart) / (double)originalLen)) { rc = false; break; } } else { if (TrackProgress(count, (ls - lstart) / (double)originalLen)) { rc = false; break; } } } //finish taking the remainder. if (outBufferCursor) { left->Set((samplePtr)mOutBuffer[0], floatSample, outls, outBufferCursor); if (right) { right->Set((samplePtr)mOutBuffer[1], floatSample, outrs, outBufferCursor); } } // Tell effect we're done callDispatcher(effStopProcess, 0, 0, NULL, 0.0); // Turn the power off callDispatcher(effMainsChanged, 0, 0, NULL, 0.0); // No longer playing mTimeInfo.samplePos = 0.0; mTimeInfo.sampleRate = 44100.0; mTimeInfo.tempo = 120.0; mTimeInfo.timeSigNumerator = 4; mTimeInfo.timeSigDenominator = 4; mTimeInfo.flags = kVstTempoValid | kVstNanosValid; return rc; }
bool VampEffect::Process() { if (!mPlugin) { return false; } int count = 0; bool multiple = false; unsigned prevTrackChannels = 0; if (GetNumWaveGroups() > 1) { // if there is another track beyond this one and any linked one, // then we're processing more than one track. That means we // should use the originating track name in each NEW label // track's name, to make clear which is which multiple = true; } std::vector<std::shared_ptr<Effect::AddedAnalysisTrack>> addedTracks; for (auto leader : inputTracks()->Leaders<const WaveTrack>()) { auto channelGroup = TrackList::Channels(leader); auto left = *channelGroup.first++; sampleCount lstart, rstart = 0; sampleCount len; GetSamples(left, &lstart, &len); unsigned channels = 1; // channelGroup now contains all but the first channel const WaveTrack *right = channelGroup.size() ? *channelGroup.first++ : nullptr; if (right) { channels = 2; GetSamples(right, &rstart, &len); } // TODO: more-than-two-channels size_t step = mPlugin->getPreferredStepSize(); size_t block = mPlugin->getPreferredBlockSize(); bool initialiseRequired = true; if (block == 0) { if (step != 0) { block = step; } else { block = 1024; } } if (step == 0) { step = block; } if (prevTrackChannels > 0) { // Plugin has already been initialised, so if the number of // channels remains the same, we only need to do a reset. // Otherwise we need to re-construct the whole plugin, // because a Vamp plugin can't be re-initialised. if (prevTrackChannels == channels) { mPlugin->reset(); initialiseRequired = false; } else { //!!! todo: retain parameters previously set Init(); } } if (initialiseRequired) { if (!mPlugin->initialise(channels, step, block)) { Effect::MessageBox(_("Sorry, Vamp Plug-in failed to initialize.")); return false; } } const auto effectName = GetSymbol().Translation(); addedTracks.push_back(AddAnalysisTrack( multiple ? wxString::Format( _("%s: %s"), left->GetName(), effectName ) : effectName )); LabelTrack *ltrack = addedTracks.back()->get(); FloatBuffers data{ channels, block }; auto originalLen = len; auto ls = lstart; auto rs = rstart; while (len != 0) { const auto request = limitSampleBufferSize( block, len ); if (left) { left->Get((samplePtr)data[0].get(), floatSample, ls, request); } if (right) { right->Get((samplePtr)data[1].get(), floatSample, rs, request); } if (request < block) { for (unsigned int c = 0; c < channels; ++c) { for (decltype(block) i = request; i < block; ++i) { data[c][i] = 0.f; } } } // UNSAFE_SAMPLE_COUNT_TRUNCATION // Truncation in case of very long tracks! Vamp::RealTime timestamp = Vamp::RealTime::frame2RealTime( long( ls.as_long_long() ), (int)(mRate + 0.5) ); Vamp::Plugin::FeatureSet features = mPlugin->process( reinterpret_cast< float** >( data.get() ), timestamp); AddFeatures(ltrack, features); if (len > (int)step) { len -= step; } else { len = 0; } ls += step; rs += step; if (channels > 1) { if (TrackGroupProgress(count, (ls - lstart).as_double() / originalLen.as_double() )) { return false; } } else { if (TrackProgress(count, (ls - lstart).as_double() / originalLen.as_double() )) { return false; } } } Vamp::Plugin::FeatureSet features = mPlugin->getRemainingFeatures(); AddFeatures(ltrack, features); prevTrackChannels = channels; } // All completed without cancellation, so commit the addition of tracks now for (auto &addedTrack : addedTracks) addedTrack->Commit(); return true; }