简体   繁体   English

NAudio获取音频样本

[英]NAudio Get Audio Samples

I am using NAudio to get the sample from the song that is currently playing and draw the waveform as the song plays. 我正在使用NAudio从当前正在播放的歌曲中获取样本,并在歌曲播放时绘制波形。 I am using AudioFileReader and ToSampleProvider to get all samples as float and then I plot them into an InkCanvas while the song is playing. 我正在使用AudioFileReaderToSampleProvider来获取所有样本为float样本,然后在播放歌曲时将它们绘制到InkCanvas My problem is that the samples don't seem to match the sound. 我的问题是样本似乎与声音不匹配。 I also have verify this by using this same song in the WPF example that is located in the source code of NAudio. 我还通过在NAudio源代码中的WPF示例中使用这首歌曲来验证这一点。 In the example the waveform matches the sound, but in my application it doesn't. 在示例中,波形与声音匹配,但是在我的应用程序中,波形与声音不匹配。 So I was wondering if someone could help me find out what I am doing (or reading) wrong or if maybe my drawing logic is wrong. 所以我想知道是否有人可以帮助我找出我在做什么(或阅读)是错误的,还是我的绘图逻辑是错误的。

Here is my current code: 这是我当前的代码:

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
}

I know this drawing algorithm is not very good but I have tried others and they also seem to not follow the audio. 我知道这个绘图算法不是很好,但是我尝试了其他方法,他们似乎也不遵循音频。

Thanks. 谢谢。

NOTE: I know that there are similar question, but the other questions suggest to use things like AudioFileReader or ToSampleProvider which I am already using. 注意:我知道也有类似的问题,但是其他问题建议使用已经使用的AudioFileReaderToSampleProvider类的东西。 My error is probably more into how I am reading the samples, maybe I am missing some bytes or have to skip some byte, or maybe some missing property that I am not setting. 我的错误可能更多地在于我如何读取样本,也许我丢失了一些字节或不得不跳过某些字节,或者也许有一些我未设置的属性。

You should seriously consider using the portions of the code from the WFP example that handles read/play and the min / max calculation work . 您应该认真考虑使用WFP示例中处理读/播放和最小/最大计算工作的代码部分。

It will take you a little bit of effort but I promise it will be worth it. 这将需要您一点点的努力,但是我保证这是值得的。

You then will be working with a dataset that you know is in the correct form and you can focus on the drawing part. 然后,您将使用已知格式正确的数据集,并且可以专注于工程图部分。 Look closely at AudioPlayback.cs and its relationship to SampleAggregator.cs . 仔细查看AudioPlayback.cs及其与SampleAggregator.cs关系。

You'll also find that getting automatic callbacks while the samples are being read (and played) is a much better way to refresh your waveform drawing than trying to use a DispatchTimer . 您还会发现,在读取(播放)样本时获得自动回调是刷新波形图的一种好方法,而不是尝试使用DispatchTimer It will also get you out of re-reading the wave buffer as well -- you really want to avoid that if you can. 它还将使您不必重新读取wave缓冲区-如果可以的话,您确实想避免这种情况。

EDIT: 编辑:

I tested your conversion code and the resulting float values appear to be correct (in the range of -1 through 1). 我测试了您的转换代码,结果float值似乎正确(在-1到1的范围内)。 So I think the problem is with the way you are plotting the waveform in WPF. 因此,我认为问题在于您在WPF中绘制波形的方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM