C#中声音信号的实时可视化

在进行机器人项目信号处理研究时,发现C#编写的示例代码较为罕见。这促使创建了一些自己的类,这里展示的代码就是用于演示的。

本文主要介绍了如何利用C#Windows GDI实现声音信号的实时可视化。通过Fast Fourier Transform(快速傅里叶变换)和Windows GDI,可以将声音信号在时间和频率域的表示形式进行可视化。

在进行机器人项目信号处理研究时,发现C#编写的示例代码较为罕见。这促使创建了一些自己的类,这里展示的代码就是用于演示的。

代码使用

本文的演示使用Ianier Munoz开发的Wave类来提供音频输入。Wave样本进一步使用AudioFrame类进行处理。

AudioFrame类是C#中一个用于处理音频样本的类,它包含画布、波形数据、快速傅里叶变换数据等成员。

AudioFrame类的主要功能是处理16位音频样本,并将其在时间和频率域的可视化渲染到PictureBox控件中。

AudioFrame类中的Process方法用于处理16位音频样本。如果_isTest为false,则从样本中分离出左右声道;如果为true,则生成人工样本用于测试。

public void Process(ref byte[] wave) { _waveLeft = new double[wave.Length / 4]; _waveRight = new double[wave.Length / 4]; if (!_isTest) { // 分离出左右声道 int h = 0; for (int i = 0; i < wave.Length; i += 4) { _waveLeft[h] = (double)BitConverter.ToInt16(wave, i); _waveRight[h] = (double)BitConverter.ToInt16(wave, i + 2); h++; } } else { // 生成人工样本用于测试 _signalGenerator = new SignalGenerator(); _signalGenerator.SetWaveform("Sine"); _signalGenerator.SetSamplingRate(44100); _signalGenerator.SetSamples(16384); _signalGenerator.SetFrequency(5000); _signalGenerator.SetAmplitude(32768); _waveLeft = _signalGenerator.GenerateSignal(); _waveRight = _signalGenerator.GenerateSignal(); } // 生成频率域数据 _fftLeft = FourierTransform.FFTDb(ref _waveLeft); _fftRight = FourierTransform.FFTDb(ref _waveRight); }

RenderTimeDomain方法用于将时间域数据渲染到PictureBox控件中。首先设置绘图环境,然后确定左右声道的边界,接着绘制左右声道的波形。

public void RenderTimeDomain(ref PictureBox pictureBox) { // 设置绘图环境 _canvasTimeDomain = new Bitmap(pictureBox.Width, pictureBox.Height); Graphics offScreenDC = Graphics.FromImage(_canvasTimeDomain); SolidBrush brush = new System.Drawing.SolidBrush(Color.FromArgb(0, 0, 0)); Pen pen = new System.Drawing.Pen(Color.WhiteSmoke); // 确定左右声道的边界 int width = _canvasTimeDomain.Width; int center = _canvasTimeDomain.Height / 2; int height = _canvasTimeDomain.Height; offScreenDC.DrawLine(pen, 0, center, width, center); // 绘制左声道 double yCenterLeft = (leftBottom - leftTop) / 2; double yScaleLeft = 0.5 * (leftBottom - leftTop) / 32768; int xPrevLeft = 0, yPrevLeft = 0; for (int xAxis = leftLeft; xAxis < leftRight; xAxis++) { int yAxis = (int)(yCenterLeft + (_waveLeft[_waveLeft.Length / (leftRight - leftLeft) * xAxis] * yScaleLeft)); if (xAxis == 0) { xPrevLeft = 0; yPrevLeft = yAxis; } else { pen.Color = Color.LimeGreen; offScreenDC.DrawLine(pen, xPrevLeft, yPrevLeft, xAxis, yAxis); xPrevLeft = xAxis; yPrevLeft = yAxis; } } // 绘制右声道 double yScaleRight = 0.5 * (rightBottom - rightTop) / 32768; int xCenterRight = rightTop + ((rightBottom - rightTop) / 2); int xPrevRight = 0, yPrevRight = 0; for (int xAxis = rightLeft; xAxis < rightRight; xAxis++) { int yAxis = (int)(xCenterRight + (_waveRight[_waveRight.Length / (rightRight - rightLeft) * xAxis] * yScaleRight)); if (xAxis == 0) { xPrevRight = 0; yPrevRight = yAxis; } else { pen.Color = Color.LimeGreen; offScreenDC.DrawLine(pen, xPrevRight, yPrevRight, xAxis, yAxis); xPrevRight = xAxis; yPrevRight = yAxis; } } // 清理 pictureBox.Image = _canvasTimeDomain; offScreenDC.Dispose(); } public void RenderFrequencyDomain(ref PictureBox pictureBox) { // 设置绘图环境 _canvasFrequencyDomain = new Bitmap(pictureBox.Width, pictureBox.Height); Graphics offScreenDC = Graphics.FromImage(_canvasFrequencyDomain); SolidBrush brush = new System.Drawing.SolidBrush(Color.FromArgb(0, 0, 0)); Pen pen = new System.Drawing.Pen(Color.WhiteSmoke); // 确定左右声道的边界 int width = _canvasFrequencyDomain.Width; int center = _canvasFrequencyDomain.Height / 2; int height = _canvasFrequencyDomain.Height; offScreenDC.DrawLine(pen, 0, center, width, center); // 绘制左声道 for (int xAxis = leftLeft; xAxis < leftRight; xAxis++) { double amplitude = (int)_fftLeft[(int)(((double)(_fftLeft.Length) / (double)(width)) * xAxis)]; if (amplitude < 0) amplitude = 0; int yAxis = (int)(leftTop + ((leftBottom - leftTop) * amplitude) / 100); pen.Color = Color.FromArgb(0, 0, (int)amplitude % 255); offScreenDC.DrawLine(pen, xAxis, leftTop, xAxis, yAxis); } // 绘制右声道 for (int xAxis = rightLeft; xAxis < rightRight; xAxis++) { double amplitude = (int)_fftRight[(int)(((double)(_fftRight.Length) / (double)(width)) * xAxis)]; if (amplitude < 0) amplitude = 0; int yAxis = (int)(rightBottom - ((rightBottom - rightTop) * amplitude) / 100); pen.Color = Color.FromArgb(0, 0, (int)amplitude % 255); offScreenDC.DrawLine(pen, xAxis, rightBottom, xAxis, yAxis); } // 清理 pictureBox.Image = _canvasFrequencyDomain; offScreenDC.Dispose(); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485