Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何有效地绘制波形_C#_Wpf - Fatal编程技术网

C# 如何有效地绘制波形

C# 如何有效地绘制波形,c#,wpf,C#,Wpf,我试图显示音频文件的波形。我希望在ffmpeg处理文件时逐步绘制波形,而不是在完成后一次绘制所有波形。虽然我已经达到了这个效果,但它真的很慢;就像痛苦的缓慢。它的启动速度非常快,但速度会下降到需要几分钟来提取样本的程度 我觉得必须有一种更有效的方法来做到这一点,因为我使用了一个程序来做到这一点,我只是不知道如何做到。另一个程序可以接收>10小时的音频,并逐步显示波形,而不会降低速度。我已将ffmpeg设置为以500个采样/秒的速度处理该文件,但其他程序以1000个采样/秒的速度处理该文件,并且它

我试图显示音频文件的波形。我希望在ffmpeg处理文件时逐步绘制波形,而不是在完成后一次绘制所有波形。虽然我已经达到了这个效果,但它真的很慢;就像痛苦的缓慢。它的启动速度非常快,但速度会下降到需要几分钟来提取样本的程度

我觉得必须有一种更有效的方法来做到这一点,因为我使用了一个程序来做到这一点,我只是不知道如何做到。另一个程序可以接收>10小时的音频,并逐步显示波形,而不会降低速度。我已将ffmpeg设置为以500个采样/秒的速度处理该文件,但其他程序以1000个采样/秒的速度处理该文件,并且它仍然比我编写的程序运行得更快。另一个程序的波形显示在10小时文件中只需要120MB内存,而我的10分钟文件需要1.5GB内存

我相当确定缓慢是由所有UI更新造成的,RAM的使用是由所有正在创建的矩形对象造成的。当我禁用绘制波形时,异步流完成得非常快;对于10小时的文件,少于1分钟

这是我唯一能想到的实现我想要的东西的方法。我将欢迎任何帮助来改进我所写的内容,或者任何建议,以一种完全不同的方式来完成它

作为旁注,这并不是我想要展示的全部。我最终希望添加一个背景网格来帮助判断时间,并添加可拖动的线条注释来标记波形中的特定位置

main window.xaml


MainWindow.xaml.cs

private string\u audioFilePath;
公共字符串音频文件路径
{
get=>\u音频文件路径;
设置
{
if(_audioFilePath!=值)
{
_audioFilePath=值;
NotifyPropertyChanged();
}
}
}
私人可观测采集样本;
公众可观测采集样本
{
获取=>\u个样本;
设置
{
如果(_samples!=值)
{
_样本=价值;
NotifyPropertyChanged();
}
}
}
//启动整个过程的Eventhandler
私有异步void getWaveform\u单击(对象发送方,路由目标)
{
((按钮)发送器).IsEnabled=false;
等待GetWaveformClickAsync();
((按钮)发送器).IsEnabled=true;
}
专用异步任务GetWaveformClickAsync()
{
示例。清除();
左双=0;
双宽度=.01;
双层顶=0;
双倍高度=0;
Wait foreach(FFmpeg.GetAudioWaveform(AudioFilePath.ConfigureWait(false))中的var样本)
{
//将{-32768,32767}(PCM16le)映射到{-50,50}(样本显示高度)
//我不认为这个映射是正确的,但现在这并不重要
高度=((样本+32768)*100/65535)-50;
//“0”pcm值不是为了保存UI更新而绘制的,
//但平局仍然领先
如果(高度==0)
{
左+=宽度;
继续;
}
//正pcm值在画布中心线上方拉伸“高度”
如果(高度>0)
顶部=50-高度;
//负pcm值在中心线下方延伸“高度”
其他的
{
top=50;
高度=-高度;
}
样本。添加(新样本)
{
高度=高度,
宽度=宽度,
顶部=顶部,
左=左,
ZIndex=1
});
左+=宽度;
}
}
用于定义样本的类

公共接口IShape
{
双头{get;set;}
左双{get;set;}
}
公共抽象类形状:IShape
{
公共双顶部{get;set;}
公共双左{get;set;}
公共int-ZIndex{get;set;}
}
公共类示例:Shape
{
公共双宽度{get;set;}
公共双倍高度{get;set;}
}
FFMpeg.cs

公共静态类FFmpeg
{
公共静态异步IAsyncEnumerable GetAudioWaveform(字符串文件名)
{
var args=GetFFmpegArgs(FFmpegTasks.GetWaveform,文件名);
等待foreach(RunFFmpegAsyncStream(args)中的var样本)
{
回收样品;
}
}
/// 
///使用给定参数字符串流化运行ffmpeg.exe的原始结果
/// 
///用于ffmpeg.exe的CLI参数字符串
私有静态异步IAsyncEnumerable RunFFmpegAsyncStream(字符串参数)
{
使用(var进程=新进程())
{
process.StartInfo.FileName=@“外部\ffmpeg\bin\x64\ffmpeg.exe”;
process.StartInfo.Arguments=args;
process.StartInfo.UseShellExecute=false;
process.StartInfo.RedirectStandardError=true;
process.StartInfo.RedirectStandardOutput=true;
process.StartInfo.CreateNoWindow=true;
process.Start();
process.BeginErrorReadLine();
var buffer=新字节[2];
while(true)
{
//从ffmpeg.exe输出异步读取pcm16_le值
var r=等待进程.StandardOutput.BaseStream.ReadAsync(缓冲区,0,2);
如果(r==0)
打破
产生返回位转换器.ToInt16(缓冲区);
}
}
}
FFmpegTasks
只是一个枚举。
GetFFmpegArgs
使用
FFmpegTasks
上的开关参数为ffmpeg.exe返回适当的CLI参数

我尝试使用下面的类而不是标准的
observateCollection
,因为我希望较少的UI更新会加快速度,但实际上这会使绘制波形的速度变慢

可观察范围收集
/// <summary>
/// Converts a histogram to a <see cref="PathGeometry"/>.
/// This is used by converters to draw a path.
/// It is easiest to use the default Canvas width and height and then use a ViewBox.
/// </summary>
/// <param name="histogram">The counts of each value.</param>
/// <param name="CanvasWidth">Width of the canvas</param>
/// <param name="CanvasHeight">Height of the canvas.</param>=
/// <returns>A path geometry. This value can be bound to a <see cref="Path"/>'s Data.</returns>
public static PathGeometry HistogramToPathGeometry(int[] histogram, double CanvasWidth = 100.0, double CanvasHeight = 100.0)
{
    double xscale = CanvasWidth / histogram.Length;
    double histMax = histogram.Max();

    double yscale = CanvasHeight / histMax;

    List<LineSegment> segments = new List<LineSegment>();
    for (int i = 0; i < histogram.Length; i++)
    {
        double X = i * xscale;
        double Y1 = histogram[i] * yscale;

        double Y = CanvasHeight - Y1;
        if (Y == double.PositiveInfinity) Y = CanvasHeight;
        segments.Add(new LineSegment(new Point(X, Y), true));
    }
    segments.Add(new LineSegment(new Point(CanvasWidth, CanvasHeight), true));
    PathGeometry geometry = new PathGeometry();
    PathFigure figure = new PathFigure(new Point(0, CanvasHeight), segments, true);

    geometry.Figures = new PathFigureCollection
    {
        figure
    };
    return geometry;
}

<Canvas Width="100" Height="100">
    <Path Data="{Binding ConvertedPathGeometry}" />
</Canvas>