Processings with dynamic ports

From Clam

This page explain a technique to create processings that can be configured to have a different number of ports, multichannel processings so that the number of channels can be changed at configuration time. If you just need a fixed number of ports see Creating a minimal processing object.

Contents

Defining dynamic ports

First of all, we have to decide the class of Ports that we want to use. In this example we will use the AudioInPort and AudioOutPort classes, so it's necessary the following includes. However you can decide another port type if you want.

       ...
       #include <CLAM/AudioInPort.hxx>
       #include <CLAM/AudioOutPort.hxx>
       ...

We'll use a vector of AudioInPort (or AudioOutPort) instead of an unique AudioInPort or AudioOutPort. That's because we want to work with dynamic port and we don't know the number of ports that we will need. So we can add this lines in the class declaration.

       ...
       CLAM::AudioInPort _in;                      <--- OLD
       CLAM::AudioOutPort _out;                  <--- OLD
       typedef std::vector<CLAM::AudioOutPort*> OutPorts; <--- NEW
       typedef std::vector<CLAM::AudioInPort*> InPorts;   <--- NEW
       OutPorts _outports;                                <--- NEW
       InPorts _inports;                                  <--- NEW
       ...

Initialization

In case we decide to have at least one port, we need to initialize the vector in the ConcreteConfigure() method. ( This code is for _inports vector, but it's the same for the _outports vector).

       if(_inports.empty())
       {	AudioInPort * port = new AudioInPort( "in1", this);
               port->SetSize(BackendBufferSize());
               port->SetHop( BackendBufferSize());
               _inports.push_back( port );
       }
This processing has one provisional port

Removing all ports

For removing all the ports we have to delete all the items and tell the ports that the vector of AudioPorts is clear.

       if(!_outports.empty())
       {
               for(OutPorts::iterator itOutPorts=_outports.begin();itOutPorts!=_outports.end();itOutPorts++)
                       delete *itOutPorts;        					            
       }
        _outports.clear();
       // Delete ports from Processing (base class) register
       GetOutPorts().Clear();

Keeping the port connections

If we want to increase or decrease the number of ports we can not remove all the ports because we will lose the port connections. The solution is delete or add the items that are completely necessary. So we have three cases.

First case: Maintaining the same ports

We don't have to put and remove anything.

       if ( numberChannelsOut == _outports.size() )
               return true;

Second case: To increase the number of ports

We have to create an AudioPort and insert it in the vector. The following code shows how to create as number of AudioInPort as number of channels. The condition is the number of channels has to be greater than the vector size.

       std::string nameOutPort = "out";
       if ( numberChannelsOut > _outports.size() )
       {
               for (int i=_outports.size()+1; i<= numberChannelsOut; i++)
               {
                       std::ostringstream nameStream;
                       nameStream << nameOutPort << i;
                       AudioOutPort * port = new AudioOutPort( nameStream.str(), this);
                       port->SetSize( BackendBufferSize());
                       port->SetHop(BackendBufferSize());
                       _outports.push_back( port );
               }
       }

Third case: Deleting some items from the dynamic ports

The example below shows how to decrease in number of ports. We want to get a vector that will have as number of AudioPorts as number and channels, so we will delete the remaining items. The condition is that the vector size has to be greater than the number of channels.

       if ( numberChannelsOut < _outports.size() )
       {
               for (unsigned i= numberChannelsOut; i<_outports.size(); i++)
                       delete _outports[i];
               _outports.resize( numberChannelsOut );
       }


This example shows the connection of two multi-port processing

Using the dynamic ports

Within the Do method we can read or write the AudioPorts. We create a CLAM::TData* array for save the buffer of each channel.

       CLAM::TData* channels[_numberChannelsOut];
       for (unsigned channel=0; channel<_numberChannelsOut; channel++)
               channels[channel] = &_outports[channel]->GetAudio().GetBuffer()[0];

Then we can set the values that we want in the Audio Buffers.

       const int portSize = _outports[0]->GetAudio().GetBuffer().Size();
       for(int i=0; i< portSize; i++)
               for(unsigned channel = 0; channel<_numberChannelsOut; channel++)
                       channels[channel][i] = value; 

Finally, we have to call the Produce method for the AudioOutPorts and the Consume method for the AudioInPorts.

       for (unsigned channel=0; channel<_numberChannelsOut; channel++)
               _outports[channel]->Produce();
       for (unsigned channel=0; channel<_numChannelsIn; channel++)
               _inports[channel]->Consume();