C# 异步音频波形生成器
一个用于异步生成音频波形的 C# 类。
引言
此项目包含一个 C# 类,用于使用任务并行库 (TPL) 从音频文件异步生成音频波形。
音频波形是音频轨道的视觉表示,以其功率电平随时间变化的图表形式呈现。 它通过连续解码并查找从音频轨道开始到结束的小样本音频的功率电平来生成。 这些功率电平像图表上的点一样绘制,然后连接在一起形成波形。 但是,此解码过程开销很大,并且解码整个音频轨道可能需要一些时间。
此项目旨在通过以下方式改进此问题:
1) 在后台异步运行解码过程,使 UI 在整个过程中保持响应。
2) 允许在解码完成之前部分渲染和显示波形。 如果在解码时连续渲染和显示波形,则会产生动画效果,因为波形看起来是从开始到结束“增长”。
特点
- 可调整的波形设置
-
细节(细节越高=图像越清晰)
-
方向(从左到右 / 从上到下 / 等)
-
方向(左侧在顶部或左侧 / 左侧在底部或右侧)
-
左侧、右侧和中心线的颜色
-
-
支持取消
-
易于修改的类
-
支持多种格式(MP3、MP2、MP1、OGG、WAV、AIFF)
使用代码
- 添加对 Bass.Net.dll 的引用。
- 将 bass.dll 放置在输出文件夹中。
- 将 WaveformGenerator 类添加到项目中。
- 导入 WaveformGenerator 命名空间。
属性
细节(细节越高=图像越清晰)
每个波形图像的像素有 [细节] 个绘图点。(即,如果细节 = 2,则对于波形图像的每个像素,有 2 个绘图点。)
使用抗锯齿功能,以便可以很好地渲染细节 > 1 的波形。
wg.Detail = 1.5f;
方向(从左到右 / 从上到下 / 等)
wg.Direction = WaveformGenerator.WaveformDirection.LeftToRight;
方向(左侧在顶部或左侧 / 左侧在底部或右侧)
wg.Orientation = WaveformGenerator.WaveformSideOrientation.LeftSideOnTopOrLeft;
左侧、右侧和中心线的颜色。
左侧和右侧是立体声音频的左右声道。 中心线是在左侧和右侧之间绘制的线。
wg.LeftSideBrush = new SolidBrush(Color.Orange);
wg.RightSideBrush = new SolidBrush(Color.Gray);
wg.CenterLineBrush = new SolidBrush(Color.Black);
方法
开始检测/解码
wg.DetectWaveformLevelsAsync();
取消检测/解码
wg.CancelDetection();
在检测/解码期间或之后生成波形
wg.CreateWaveform(width, height);
完整代码(来自演示项目)
private WaveformGenerator wg;
public Form1() {
InitializeComponent();
Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);
}
private async void openBtn_Click(object sender, EventArgs e) {
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
openBtn.Enabled = false;
cancelBtn.Enabled = true;
wg = new WaveformGenerator(ofd.FileName);
// Change settings.
wg.Direction = WaveformGenerator.WaveformDirection.LeftToRight;
wg.Orientation = WaveformGenerator.WaveformSideOrientation.LeftSideOnTopOrLeft;
wg.Detail = 1.5f;
wg.LeftSideBrush = new SolidBrush(Color.Orange);
wg.RightSideBrush = new SolidBrush(Color.Gray);
wg.ProgressChanged += wg_ProgressChanged;
wg.Completed += wg_Completed;
wg.CreateStream();
await wg.DetectWaveformLevelsAsync();
// Add code to execute after completion. (Alternatively, add it in the wg_Completed method)
// ...
}
}
private void cancelBtn_Click(object sender, EventArgs e) {
wg.CancelDetection();
}
private void wg_Completed(object sender, EventArgs e) {
Console.WriteLine("Completed");
// Add code to execute after completion. (Alternatively, add it after "await wg.DetectWaveformLevelsAsync();")
// ...
wg.CloseStream();
openBtn.Enabled = true;
cancelBtn.Enabled = false;
ReloadWaveform();
//pictureBox1.Image.Save("Waveform.jpg");
}
private void wg_ProgressChanged(object sender, WaveformGenerator.ProgressChangedEventArgs e) {
ReloadWaveform();
}
private void Form1_SizeChanged(object sender, EventArgs e) {
if (pictureBox1.Image != null)
ReloadWaveform();
}
private void ReloadWaveform() {
if (pictureBox1.Width > 0 && pictureBox1.Height > 0) {
if (pictureBox1.Image != null)
pictureBox1.Image.Dispose();
pictureBox1.Image = wg.CreateWaveform(pictureBox1.Width, pictureBox1.Height);
}
}
如何生成波形图像?
检索音频轨道 20 毫秒“帧”(样本)的功率电平。
Bass.BASS_ChannelGetLevel(stream, levels);
创建波形图像时,这些帧被分成相等的组并压缩成“渲染帧”,其中每个渲染帧代表一个绘图点。 这些渲染帧的功率电平 = 其中所有帧的平均功率电平。 这些绘图点首尾相连形成一个多边形,然后使用 Graphics.FillPolygon 方法绘制波形的左侧和右侧。
g.FillPolygon(leftSideBrush, leftPlotPointsArray);
生成细节= 2 且宽度= 3(像素)的波形图像
为简单起见,宽度设置为 3 像素,但已“放大”。
Notice
- 每个像素有 2 个绘图点,因为细节 = 2。
- 对于部分渲染,最后一个绘图点直接向下连接到基线,而不是连接到它的末尾。
- 实际上,生成的波形图像可能会远大于 3 像素。
依赖项(第三方库)
BASS:用于解码音频文件的音频库。
BASS.NET:BASS .NET 包装器。
BASS / BASS.NET 库的主要用途是解码和检索音频文件的功率电平。 此库可以替换为具有此类功能的另一个库,例如 NAudio。
注释
由于此类使用 TPL,因此与低于 5.0 的 C# 版本(低于 4.5 的 .NET 版本)不兼容。 可以通过使用其他线程类(例如 BackgroundWorker)而不是 Tasks 轻松修改为以前的版本。