Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
0
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;
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
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;
}