简体   繁体   中英

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. 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. 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. 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. 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 .

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 .

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 . It will also get you out of re-reading the wave buffer as well -- you really want to avoid that if you can.

EDIT:

I tested your conversion code and the resulting float values appear to be correct (in the range of -1 through 1). So I think the problem is with the way you are plotting the waveform in WPF.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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