2016-06-02 3 views
0

Ich benutze NAudio, um das Sample aus dem gerade gespielten Song zu erhalten und die Wellenform zu zeichnen, während das Lied abgespielt wird. Ich verwende AudioFileReader und ToSampleProvider, um alle Samples als float zu bekommen und dann zeichne ich sie in eine InkCanvas, während das Lied spielt. Mein Problem ist, dass die Samples nicht mit dem Sound übereinstimmen. Ich habe dies auch überprüft, indem ich denselben Song im WPF-Beispiel verwendet habe, das sich im Quellcode von NAudio befindet. Im Beispiel stimmt die Wellenform mit dem Sound überein, in meiner Anwendung jedoch nicht. Also habe ich mich gefragt, ob jemand mir helfen könnte, herauszufinden, was ich falsch mache (oder lese) oder ob meine Zeichnungslogik falsch ist.NAudio Get Audio Samples

Hier ist mein aktueller 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 
} 

Ich weiß, dass diese Zeichnung Algorithmus ist nicht sehr gut, aber ich habe andere versucht und sie scheinen auch das Audio nicht zu folgen.

Danke.

HINWEIS: Ich weiß, dass es ähnliche Fragen gibt, aber die anderen Fragen schlagen vor, Dinge wie AudioFileReader oder ToSampleProvider zu verwenden, die ich bereits verwende. Mein Fehler ist wahrscheinlich mehr in, wie ich die Proben lese, vielleicht fehlen mir einige Bytes oder müssen einige Byte oder vielleicht eine fehlende Eigenschaft überspringen, die ich nicht einstelle.

+1

Sie sollten ernsthaft in Erwägung ziehen, den Code aus dem [WFP-Beispiel] (https://github.com/naudio/NAudio/tree/master/NAudioWpfDemo/AudioPlaybackDemo) zu verwenden, das Lese-/Wiedergabe ** und die Min/Max-Berechnung handhabt Arbeit**. Es wird ein wenig Aufwand erfordern, aber ich verspreche es wird es wert sein. Sie arbeiten dann mit einem Datensatz, von dem Sie wissen, dass er in der richtigen Form vorliegt, und Sie können sich auf den Zeichnungsteil konzentrieren. Sieh dir 'AudioPlayback.cs' und seine Beziehung zu' SampleAggregator.cs' genauer an. –

+0

Ich schätze den Tipp, und wenn das meine einzige Wahl ist, werde ich wahrscheinlich am Quellcode arbeiten, aber ich versuche es zu verstehen ein bisschen mehr über Audioprogrammierung, also versuche ich tatsächlich, den Quellcode nicht zu benutzen und tue es selbst (tatsächlich, indem ich auf den Quellcode schaue, habe ich mein Programmierer-Ego kaputt gemacht, aber damit kann ich diesmal leben). – Agustin0987

+0

Ich sollte auch erwähnen, dass Sie, wenn Sie dieses Beispiel richtig verwenden, nicht mit dem Versuch, einen 'DispatchTimer' zu benutzen, um Ihre Aktualisierungen der Wellenformzeichnung zu fahren, herumspielen müssen.Ich denke, Sie werden es viel einfacher finden, wenn Sie Callbacks von 'SampleAggregator' automatisch an Ihre Event-Delegate-Methode' OnMaximumCalculated' erhalten, sobald alle 100 ms (Sie können eine Eigenschaft einstellen, um dieses Intervall zu ändern). –

Antwort

0

Sie sollten ernsthaft vom WFP example unter Verwendung der Teile des Codes betrachten, die/Spiel lesen Griffe und die min/max Rechenarbeit.

Es wird ein wenig Aufwand, aber ich verspreche, es wird es wert sein.

Sie arbeiten dann mit einem Datensatz, von dem Sie wissen, dass er in der richtigen Form vorliegt, und Sie können sich auf den Zeichnungsteil konzentrieren. Schaue genau auf AudioPlayback.cs und seine Beziehung zu SampleAggregator.cs.

Sie werden auch feststellen, dass das Erhalten automatischer Rückrufe während des Lesens (und Abspielens) der Samples eine viel bessere Methode ist, um Ihre Waveform-Zeichnung zu aktualisieren als die Verwendung einer DispatchTimer. Es wird Ihnen auch helfen, den Wellenpuffer erneut zu lesen - das wollen Sie wirklich vermeiden, wenn Sie können.

EDIT:

ich Ihren Conversion-Code getestet und die daraus resultierenden float Werte korrekt zu sein scheinen (im Bereich von -1 bis 1). Ich denke, das Problem liegt in der Art, wie Sie die Wellenform in WPF darstellen.

Verwandte Themen