Ham Cockpit   

    Show / Hide Table of Contents

    S-Meter Plugin

    The source code of this plugin is available here GitHub.

    The S-Meter plugin demonstrates how to receive a copy of the data being processed in the DSP Pipeline for analysis and visualization.

    As in the previous example, a reference to the DSP Pipeline is obtained using an importing constructor:

    [ImportingConstructor]
    SMeter([Import(typeof(IPluginHost))] IPluginHost host)
    {
      pipeline = host.DspPipeline;
    }
    

    When the visual panel is created, the plugin subscribes to two events, the DspPipeline.StatusChanged and DspPipeline.ProcessedAudio.SamplesAvailable:

    public UserControl CreatePanel()
    {
      panel = new SMeterControl { Name = "S-Meter" };
      pipeline.StatusChanged += StatusEventhandler;
      pipeline.ProcessedAudio.SamplesAvailable += SamplesAvailableEventHandler;
      return panel;
    }
    

    StatusChanged is used to disable the S-Meter when the radio is stopped, and SamplesAvailable is where processed audio data actually becomes available.

    The SamplesAvailable event is fired on the real-time signal processing thread. S-Meter cannot do its own data processing on this thread, it just stores the data in a buffer and passes the buffer to the main thread for processing and display.

    The event handler for this event is shown below. Note that the sampling rate and data block size, and even the number of channels, are not known in advance. The plugin reads the Format property to find out the current data parameters.

    private void SamplesAvailableEventHandler(object sender, SamplesAvailableEventArgs e)
    {
      if (!pipeline.ProcessedAudio.IsAvailable) return;
    
      //make a copy of the data
      //the data may come in different formats, get only what is needed
      var format = pipeline.ProcessedAudio.Format;
      int stride = format.Channels * (format.IsComplex ? 2 : 1);
      int count = e.Count / stride;
      float[] data = new float[count];
      for (int i = 0; i < count; i++) data[i] = e.Data[e.Offset + i * stride];
      
      //tell the main thread to process and display the data
      context.Post(s => ProcessData(data), null);
    }
    

    Here is the method that performs data processing. It computes signal power, smooths the values over the time, and updates the meter display about 10 times a second:

    private void ProcessData(float[] data)
    {
      if (!pipeline.ProcessedAudio.IsAvailable) return;
    
      //calculate the value to display
      foreach (var v in data) value += 0.0007f * (v * v - value);
      value = Math.Max(0, value);
    
      //the block size is not known in advance
      //count the samples and update the control 10 times per second
      sampleCount += data.Length;
      if (sampleCount > pipeline.ProcessedAudio.Format.SamplingRate / 10)
      {
        //if previous stages apply any gain, subtract it from the reading
        float dB = Dsp.ToDb(value) - pipeline.ProcessedAudio.Format.TotalGain;
        panel?.ShowValue(Math.Max(-100, Math.Min(0, dB)));
        sampleCount = 0;
      }
    }
    
    Back to top Generated by DocFX