NAudio 获取音频样本
NAudio Get Audio Samples
我正在使用 NAudio 从当前正在播放的歌曲中获取样本,并在歌曲播放时绘制波形。我正在使用 AudioFileReader
和 ToSampleProvider
将所有样本获取为 float
,然后在播放歌曲时将它们绘制成 InkCanvas
。我的问题是样本似乎与声音不匹配。我还通过在位于 NAudio 源代码中的 WPF 示例中使用同一首歌曲来验证这一点。在示例中,波形与声音匹配,但在我的应用程序中却不匹配。所以我想知道是否有人可以帮助我找出我在做什么(或阅读)的错误或者我的绘图逻辑是否错误。
这是我当前的代码:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ISampleProvider provider;
private DispatcherTimer timer;
private AudioFileReader reader;
private WaveOut waveOut;
private StylusPointCollection topPoints, bottomPoints;
private DrawingAttributes attr;
private double canvasHeight, canvasWidth;
private int samplesGroupSize;
private double drawPos = 0;
private StrokeCollection _WaveformLines;
public StrokeCollection WaveformLines
{
get { return _WaveformLines; }
set
{
_WaveformLines = value;
OnPropertyChanged("WaveformLines");
}
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
reader = new AudioFileReader("C:\Users\Agustin\Desktop\DragonRider.mp3");
waveOut = new WaveOut();
waveOut.Init(reader);
provider = reader.ToSampleProvider(); //Here I get the samples
reader.Position = 0; //Go to the position 0 after reading the samples
canvasHeight = Waveform.ActualHeight;
canvasWidth = Waveform.ActualWidth;
WaveformLines = new StrokeCollection();
topPoints = new StylusPointCollection();
topPoints.Add(new StylusPoint(0, (canvasHeight / 2)));
topPoints.Changed += topPoints_Changed;
bottomPoints = new StylusPointCollection();
bottomPoints.Add(new StylusPoint(0, (canvasHeight / 2)));
bottomPoints.Changed += topPoints_Changed;
WaveformLines.Add(new Stroke(topPoints));
WaveformLines.Add(new Stroke(bottomPoints));
attr = new DrawingAttributes();
attr.Color = Colors.Green;
attr.Width = 1.5;
attr.Height = 1;
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(1);
timer.Tick += timer_Tick;
timer.Start();
samplesGroupSize = (int)(timer.Interval.TotalSeconds * reader.WaveFormat.SampleRate); //The value for this is 44.
}
private void PlayButton_Click(object sender, RoutedEventArgs e)
{
waveOut.Play();
}
private void PauseButton_Click(object sender, RoutedEventArgs e)
{
waveOut.Pause();
}
private void timer_Tick(object sender, EventArgs e)
{
if (waveOut.PlaybackState == PlaybackState.Playing)
{
TimeLabel.Content = string.Format("Time: {0}", reader.CurrentTime.ToString(@"mm\:ss\:ff")); //NEED TO KEEP WORKING
float[] samps = new float[samplesGroupSize];
provider.Read(samps, 0, samps.Length);
float max = Max(samps);
float min = Min(samps);
topPoints.Add(new StylusPoint(drawPos, (canvasHeight / 2) - ((canvasHeight / 2) * max)));
bottomPoints.Add(new StylusPoint(drawPos, (canvasHeight / 2) - ((canvasHeight / 2) * min)));
drawPos += 2;
if (drawPos > canvasWidth)
{
WaveformLines.Clear();
topPoints = new StylusPointCollection();
topPoints.Add(new StylusPoint(0, (canvasHeight / 2)));
bottomPoints = new StylusPointCollection();
bottomPoints.Add(new StylusPoint(0, (canvasHeight / 2)));
WaveformLines.Add(new Stroke(topPoints));
WaveformLines.Add(new Stroke(bottomPoints));
drawPos = 0;
}
}
}
private float Min(float[] samps)
{
float max = samps[0];
foreach (float s in samps)
{
if (s > max)
max = s;
}
return max;
}
private float Max(float[] samps)
{
float min = samps[0];
foreach (float s in samps)
{
if (s < min)
min = s;
}
return min;
}
//I excluded the INotifyPropertyChanged implementation, but in the
//actual code is located here
}
我知道这个绘图算法不是很好,但我试过其他的,他们也似乎不跟随音频。
谢谢。
注意:我知道有类似的问题,但其他问题建议使用我已经在使用的 AudioFileReader
或 ToSampleProvider
之类的东西。我的错误可能更多地与我阅读示例的方式有关,也许我丢失了一些字节或必须跳过一些字节,或者可能缺少一些我没有设置的 属性。
您应该认真考虑使用 WFP example 中处理 read/play 和 最小/最大计算工作 的代码部分。
这会花费您一些努力,但我保证这是值得的。
然后您将使用一个您知道其格式正确的数据集,您可以专注于绘图部分。仔细查看 AudioPlayback.cs
及其与 SampleAggregator.cs
的关系。
您还会发现,与尝试使用 DispatchTimer
相比,在读取(和播放)样本时获取自动回调是刷新波形图的更好方法。它还可以让您避免重新读取波形缓冲区——如果可以的话,您真的想避免这种情况。
编辑:
我测试了您的转换代码,结果 float
值似乎是正确的(在 -1 到 1 的范围内)。所以我认为问题在于您在 WPF 中绘制波形的方式。
我正在使用 NAudio 从当前正在播放的歌曲中获取样本,并在歌曲播放时绘制波形。我正在使用 AudioFileReader
和 ToSampleProvider
将所有样本获取为 float
,然后在播放歌曲时将它们绘制成 InkCanvas
。我的问题是样本似乎与声音不匹配。我还通过在位于 NAudio 源代码中的 WPF 示例中使用同一首歌曲来验证这一点。在示例中,波形与声音匹配,但在我的应用程序中却不匹配。所以我想知道是否有人可以帮助我找出我在做什么(或阅读)的错误或者我的绘图逻辑是否错误。
这是我当前的代码:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ISampleProvider provider;
private DispatcherTimer timer;
private AudioFileReader reader;
private WaveOut waveOut;
private StylusPointCollection topPoints, bottomPoints;
private DrawingAttributes attr;
private double canvasHeight, canvasWidth;
private int samplesGroupSize;
private double drawPos = 0;
private StrokeCollection _WaveformLines;
public StrokeCollection WaveformLines
{
get { return _WaveformLines; }
set
{
_WaveformLines = value;
OnPropertyChanged("WaveformLines");
}
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
reader = new AudioFileReader("C:\Users\Agustin\Desktop\DragonRider.mp3");
waveOut = new WaveOut();
waveOut.Init(reader);
provider = reader.ToSampleProvider(); //Here I get the samples
reader.Position = 0; //Go to the position 0 after reading the samples
canvasHeight = Waveform.ActualHeight;
canvasWidth = Waveform.ActualWidth;
WaveformLines = new StrokeCollection();
topPoints = new StylusPointCollection();
topPoints.Add(new StylusPoint(0, (canvasHeight / 2)));
topPoints.Changed += topPoints_Changed;
bottomPoints = new StylusPointCollection();
bottomPoints.Add(new StylusPoint(0, (canvasHeight / 2)));
bottomPoints.Changed += topPoints_Changed;
WaveformLines.Add(new Stroke(topPoints));
WaveformLines.Add(new Stroke(bottomPoints));
attr = new DrawingAttributes();
attr.Color = Colors.Green;
attr.Width = 1.5;
attr.Height = 1;
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(1);
timer.Tick += timer_Tick;
timer.Start();
samplesGroupSize = (int)(timer.Interval.TotalSeconds * reader.WaveFormat.SampleRate); //The value for this is 44.
}
private void PlayButton_Click(object sender, RoutedEventArgs e)
{
waveOut.Play();
}
private void PauseButton_Click(object sender, RoutedEventArgs e)
{
waveOut.Pause();
}
private void timer_Tick(object sender, EventArgs e)
{
if (waveOut.PlaybackState == PlaybackState.Playing)
{
TimeLabel.Content = string.Format("Time: {0}", reader.CurrentTime.ToString(@"mm\:ss\:ff")); //NEED TO KEEP WORKING
float[] samps = new float[samplesGroupSize];
provider.Read(samps, 0, samps.Length);
float max = Max(samps);
float min = Min(samps);
topPoints.Add(new StylusPoint(drawPos, (canvasHeight / 2) - ((canvasHeight / 2) * max)));
bottomPoints.Add(new StylusPoint(drawPos, (canvasHeight / 2) - ((canvasHeight / 2) * min)));
drawPos += 2;
if (drawPos > canvasWidth)
{
WaveformLines.Clear();
topPoints = new StylusPointCollection();
topPoints.Add(new StylusPoint(0, (canvasHeight / 2)));
bottomPoints = new StylusPointCollection();
bottomPoints.Add(new StylusPoint(0, (canvasHeight / 2)));
WaveformLines.Add(new Stroke(topPoints));
WaveformLines.Add(new Stroke(bottomPoints));
drawPos = 0;
}
}
}
private float Min(float[] samps)
{
float max = samps[0];
foreach (float s in samps)
{
if (s > max)
max = s;
}
return max;
}
private float Max(float[] samps)
{
float min = samps[0];
foreach (float s in samps)
{
if (s < min)
min = s;
}
return min;
}
//I excluded the INotifyPropertyChanged implementation, but in the
//actual code is located here
}
我知道这个绘图算法不是很好,但我试过其他的,他们也似乎不跟随音频。
谢谢。
注意:我知道有类似的问题,但其他问题建议使用我已经在使用的 AudioFileReader
或 ToSampleProvider
之类的东西。我的错误可能更多地与我阅读示例的方式有关,也许我丢失了一些字节或必须跳过一些字节,或者可能缺少一些我没有设置的 属性。
您应该认真考虑使用 WFP example 中处理 read/play 和 最小/最大计算工作 的代码部分。
这会花费您一些努力,但我保证这是值得的。
然后您将使用一个您知道其格式正确的数据集,您可以专注于绘图部分。仔细查看 AudioPlayback.cs
及其与 SampleAggregator.cs
的关系。
您还会发现,与尝试使用 DispatchTimer
相比,在读取(和播放)样本时获取自动回调是刷新波形图的更好方法。它还可以让您避免重新读取波形缓冲区——如果可以的话,您真的想避免这种情况。
编辑:
我测试了您的转换代码,结果 float
值似乎是正确的(在 -1 到 1 的范围内)。所以我认为问题在于您在 WPF 中绘制波形的方式。