S-Meter Plugin
The source code of this plugin is available
here
.
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;
}
}