ONLamp.com
oreilly.comSafari Books Online.Conferences.

advertisement


Linux Audio Plug-Ins: A Look Into LADSPA
Pages: 1, 2, 3, 4

And now let's apply the plug-in to a sound file. First, take a look at the syntax for applyplugin:



[dlphilp@localhost dlphilp]$ applyplugin            
Usage:  applyplugin [flags] <input Wave file> <output Wave file>
        <LADSPA plugin file name> <plugin label> <Control1> 
        <Control2>...
        [<LADSPA plugin file name> <plugin label> <Control1>
        <Control2>...]...
Flags:  -s <seconds>  Add seconds of silence after end of input file.

The plug-in file name is the actual name of the object file, in this case delay.so. The plug-in label is delay_5s, and there are two control ports. The ports are numbered from the top down, and they may be audio or control ports. A control port receives data values specified by the user or by a user-defined connection such as a MIDI controller.

Using the analysis information, the following command-line sequence will apply 3 seconds of delay to an input file, with a 50/50 balance of dry/wet signal and with 3 seconds added to the end of the input (to hear the delayed signal to its end), and then pipe the output file to a sound file player for immediate playback:

applyplugin -s 3 your_input.wav your_output.wav delay.so delay_5s 3 .5; play your_output.wav

By now you should be getting the hang of how a LADSPA plug-in is used, so let's look more closely into the code behind delay.so and see how it works.

Inside a simple LADSPA plug-in

The C code for delay.so resides in $HOME/LADSPA_SDK/src/plugins and was written by Richard Furse. I've condensed its design into an outline here, and the interested reader should read delay.c in its entirety for the details of this plug-in's structure.

Delay.c follows this relatively simple plan:

  • Invoke standard includes and the obligatory LADSPA header.
  • Declare and define local variables.
  • Declare delay-line structure (for sample rate, buffer size, port declarations, etc.).
  • Instantiate delay-line plug-in.
  • Initialize and activate plug-in.
  • Connect ports to data locations.
  • Run signal processing algorithm on a block of samples.
  • General initialization and clean-up routines.

Of course the actual code is more complex, but it really does follow the straightforward outline given above. Let's take a quick peek at Richard's sources to see how the input data gets into the plug-in and how it gets processed by the delay line.

The following code excerpt illustrates how each of the four ports within the delay line's structure (the delay time, the wet/dry balance, and the audio I/O) connects to a data location:

/* Connect a port to a data location. */
void
connectPortToSimpleDelayLine(LADSPA_Handle Instance,
                             unsigned long Port,
                             LADSPA_Data * DataLocation) {

  SimpleDelayLine * psSimpleDelayLine;

  psSimpleDelayLine = (SimpleDelayLine *)Instance;
  switch (Port) {
  case SDL_DELAY_LENGTH:
    psSimpleDelayLine->m_pfDelay = DataLocation;
    break;
  case SDL_DRY_WET:
    psSimpleDelayLine->m_pfDryWet = DataLocation;
    break;
  case SDL_INPUT:
    psSimpleDelayLine->m_pfInput = DataLocation;
    break;
  case SDL_OUTPUT:
    psSimpleDelayLine->m_pfOutput = DataLocation;
    break;
  }
}

Once the connections have been established, the data is ready for processing via this code block:

/* Run a delay-line instance for a block of SampleCount samples. */
void
runSimpleDelayLine(LADSPA_Handle Instance,
                   unsigned long SampleCount) {

  LADSPA_Data * pfBuffer;
  LADSPA_Data * pfInput;
  LADSPA_Data * pfOutput;
  LADSPA_Data fDry;
  LADSPA_Data fInputSample;
  LADSPA_Data fWet;
  SimpleDelayLine * psSimpleDelayLine;
  unsigned long lBufferReadOffset;
  unsigned long lBufferSizeMinusOne;
  unsigned long lBufferWriteOffset;
  unsigned long lDelay;
  unsigned long lSampleIndex;

  psSimpleDelayLine = (SimpleDelayLine *)Instance;

  lBufferSizeMinusOne = psSimpleDelayLine->m_lBufferSize - 1;
  lDelay = (unsigned long)
    (LIMIT_BETWEEN_0_AND_MAX_DELAY(*(psSimpleDelayLine->m_pfDelay))
     * psSimpleDelayLine->m_fSampleRate);

  pfInput = psSimpleDelayLine->m_pfInput;
  pfOutput = psSimpleDelayLine->m_pfOutput;
  pfBuffer = psSimpleDelayLine->m_pfBuffer;
  lBufferWriteOffset = psSimpleDelayLine->m_lWritePointer;
  lBufferReadOffset
    = lBufferWriteOffset + psSimpleDelayLine->m_lBufferSize - lDelay;
  fWet = LIMIT_BETWEEN_0_AND_1(*(psSimpleDelayLine->m_pfDryWet));
  fDry = 1 - fWet;

  for (lSampleIndex = 0;
       lSampleIndex < SampleCount;
       lSampleIndex++) {
    fInputSample = *(pfInput++);
    *(pfOutput++) = (fDry * fInputSample
                     + fWet * pfBuffer[((lSampleIndex + lBufferReadOffset)
                                        & lBufferSizeMinusOne)]);
    pfBuffer[((lSampleIndex + lBufferWriteOffset)
              & lBufferSizeMinusOne)] = fInputSample;
  }

  psSimpleDelayLine->m_lWritePointer
    = ((psSimpleDelayLine->m_lWritePointer + SampleCount)
       & lBufferSizeMinusOne);
}

It is beyond the scope of this article to perform an in-depth analysis of this block (it's the DSP heart of the plug-in), but I encourage novice plug-in developers to study Richard's code for a well-documented introduction.

The LADSPA architects deliberately designed a truly simple API to make it easy to write plug-ins.. However, the simplicity of the programming interface certainly does not limit the developer to writing only simple plug-ins. The currently available plug-in collections include some very complex plug-ins. Let's look at a few of those collections now.

Pages: 1, 2, 3, 4

Next Pagearrow





Sponsored by: