CSCore - Highperformance Audiobibliothek

    • Release
    • Open Source

    Es gibt 589 Antworten in diesem Thema. Der letzte Beitrag () ist von simpelSoft.

      Jetzt auf die Schnelle kann ich dir keine guten Formeln nenne um die Werte zusammenzufassen. Jedoch habe ich kürzlich eine gute Bibliothek ausprobiert(und auch erfolgreich mit CSCore zum laufen gebracht -> war das auf dem Screenshot). Hier ist ein Code welcher aus den Rohen FFT-Werten eine recht gute Visualisierung errechnet: wpfsvl.codeplex.com/SourceCont…alizationLibrary/Spectrum Analyzer/SpectrumAnalyzer.cs

      Was die Kanäle(links, rechts,...) angeht. Ist natürlich möglich. Jedoch würde ich mich an deiner Stelle erst darauf beschränken einfach nur den linken Kanal zu verwenden(was du im Moment auch tust). Wenn du wirklich Kanäle willst, dann musst du im Moment selbst anfangen an den Komponenten rumzudrehen was eigentlich nicht wirklich kompliziert wäre, jedoch im Moment sicher den Aufwand nicht wirklich Wert ist.
      Ist aber prinzipiell recht einfach möglich(wie gesagt... die Lib ist zu 100% erweiterbar).


      Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
      Soo, ich habe das jetzt einfach mal so gemacht (soll ne Übergangslösung sein, bis ich was besseres gefunden habe, was auch einigermaßen übersichtlich ist ;). Mein Code sieht so aus:

      VB.NET-Quellcode

      1. Imports CSCore.DSP
      2. Imports CSCore.SoundIn
      3. Imports CSCore.Streams
      4. Imports System.Runtime.CompilerServices
      5. Public Class Form1
      6. Dim Values As IEnumerable(Of Byte) = Nothing
      7. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
      8. If Not Values Is Nothing Then
      9. ProgressBar1.Value = Values(0)
      10. ProgressBar2.Value = Values(1)
      11. ProgressBar3.Value = Values(2)
      12. ProgressBar4.Value = Values(3)
      13. End If
      14. End Sub
      15. Private Sub Aggregator_FFTCalculated(sender As Object, e As FFTCalculatedEventArgs)
      16. 'Die ersten 512 Werte abspeichern und den Real in einen Single umwandeln
      17. 'Dim Values As IEnumerable(Of Single) = e.Data.Take(512).Select(Function(c) c.Value)
      18. 'Testausgabe, Form: 'Output: {0.23, 0.00, 0.01, [...] }'
      19. Values = e.Data.Take(512).Select(Function(c) c.Value).Reduce(4).Select(Function(s) CByte(Math.Max(Math.Min((s * 100 * 200), 255), 0)))
      20. 'Debug.WriteLine(String.Format("Output: {{{0}}}", String.Join(", ", ReducedValues.Select(Function(s) s.ToString).ToArray)))
      21. End Sub
      22. Private Buffer() As Byte
      23. Private Sub SoundInSource_OnNewData(sender As Object, e As DataAvailableEventArgs)
      24. While Aggregator.Read(Buffer, 0, Buffer.Length) > 0
      25. 'Mit den Daten einfach nichts machen
      26. End While
      27. End Sub
      28. Dim SoundIn As ISoundIn
      29. Dim SoundInSource As SoundInSource
      30. Private WithEvents Aggregator As FFTAggregator
      31. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
      32. SoundIn = New WasapiLoopbackCapture
      33. SoundIn.Initialize()
      34. SoundInSource = New SoundInSource(SoundIn)
      35. Aggregator = New FFTAggregator(SoundInSource)
      36. Buffer = New Byte(Aggregator.WaveFormat.BytesPerSecond) {}
      37. AddHandler SoundInSource.DataAvailable, AddressOf SoundInSource_OnNewData
      38. AddHandler Aggregator.FFTCalculated, AddressOf Aggregator_FFTCalculated
      39. SoundIn.Start()
      40. Timer1.Start()
      41. End Sub
      42. Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
      43. Timer1.Stop()
      44. SoundIn.Stop()
      45. SoundIn.Dispose()
      46. End Sub
      47. End Class
      48. Public Module EnumerableExtensions
      49. <Extension()>
      50. Public Function Reduce(source As IEnumerable(Of Single), count As Integer) As IEnumerable(Of Single)
      51. Dim [Step] As Double = source.Count / count
      52. Dim Output As New List(Of Single)
      53. For Counter As Integer = 0 To count - 1
      54. Output.Add(source.Skip(CInt([Step] * Counter)).Take(CInt([Step])).Average())
      55. Next
      56. Return Output
      57. End Function
      58. End Module

      Die 4 Progressbars sollten eigentlich die Werte anzeigen, jedoch kriege ich da nur eine wirre Art "Flackern" hin. Außerdem zeigt eigentlich nur die ProgressBar1 einen wirklichen Ausschlag.
      Ich habe mich mit solchen Dingen nicht viel befasst. Jedoch wird dir a) eine Progressbar mit der "Animation" wohl zu langsam sein. b) sind 4 Progressbars meiner Meinung nach zu wenig um da ernsthaft was darzustellen. c) Die ersten Frequenzbänder sind in über 99% der Lieder etc. die stärksten -> deshalb der höchste Ausschlag. Damit sich das aber besser verteilt, gibt es da verschiedene Sakalierungen etc. mit welchen ich mich jedoch nicht wirklich auskenne. Aber du kannst mal hier schauen: wpfsvl.codeplex.com/SourceCont…lyzer/SpectrumAnalyzer.cs. Recht weit unten siehst du wie dort die Frequenzbänder zusammengefasst werden und berechnet werden. Diese Lib bietet auch 3 verschiedene Skalierungsverfahren an(Decibel, Linear, Wurzel). Ist sicherlich eine gute Implementation welche du mir recht wenig Aufwand abkucken kannst.


      Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
      @thefiloe

      Auch wenn wir uns nicht gut verstehen,muss ich dir sagen das diese Lib einfach beeindruckend ist,man kann einfach so viel machen wo von ich nie gedacht hätte das das allgemein überhaupt in irgend einer Weise möglich ist(Musikkurve auslesen usw.).Hut ab und Respekt :)
      Hey, erstmal super Lib! Jedoch habe ich ein kleines Problem:
      ich möchte ein Wasapi Loopback Stream aufzeichnen und anschließend durch eine FFT jagen. Ich habe mir hier den Thread durchgelesen und wollte das Beispiel hier in C# übernehmen, jedoch ist der Typ SoundInSource nicht vorhanden. Außerdem fordert der FFTAggregator ein WaveIn-Objekt als Parameter, ich kann mein WasapiCaptureLoopback jedoch nicht nach WaveIn casten. Hat da jemand n Beispiel wie ich den FFTAggregator anweden kann? Ich nutze die letzte Version der Bibliothek.
      Die Probleme sind bzw. waren alle bekannt. Müssten jedoch schon alle beim NuGet-Build behoben sein: docs.nuget.org/docs/start-here/installing-nuget


      Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
      Hey!

      Ich war mal so frei, und hab CSCore fest in GameUtils integriert, ich hoffe du hast nichts dagegen (bekommst auch ne Erwähnung).

      Ich hätte dann auch zwei klitzekleine Fragen, wegen denen ich mich jetzt nicht auf Codeplex anmelden wollte. :P
      1. Was muss man bei CodecFactory.Instance.GetCodec(obejct key, Stream stream) als Key angeben?
      2. Was bewirken der eventSync- und der latency-Parameter beim WasapiOut-Konstruktor?
      Ich weiß, ist bestimmte ne heiden Arbeit, aber bei sowas wäre ne XML-Doku schon recht hilfreich. ;)
      Also erstmal: Freut mich natürlich, dass du dir das antust :P. Bin über jeden der das Teil verwendet und vll. zur Behebung eines Fehlers beitragen kann wirklich froh! Also :thumbup:

      Jetzt zu
      1. Der key gibt den Codec an, da die Engine nicht wissen kann welches Format sich in dem Stream verbirgt(bei manchen schon, jedoch nicht bei allen). Deshalb gibt es nen Key welcher eigentlich immer nen String wie z.B. "mp3" ist und dadurch wird der Stream als MP3-Stream interpretiert. Werde im nächsten Update mitbringen, dass das durch die Mediafoundation etwas erleichtert wird(sprich wenn null angegeben wird, wird versucht das Format zu ermitteln). Ist jedoch wie immer bei Mediafoundation erst ab Vista verfügbar(es sei denn mit zusätzlichen Installationen auf XP). Wenn du keine keys angeben willst, dann kannst du einfach die Überladung mit einem filename angeben. Dort wird der Typ des Streams aufgrund des Dateinamen ermittelt(z.B. .mp3 => mp3, .flac => flac,...).
      2. EventSync ist etwas was intern von WasapiOut abläuft. Es gibt zwei Möglichkeiten mit dem Treiber zu kommunizieren. a) über EventWaitHandles was in dem Fall eventSync ist und b) durch warten und regelmäßiges anstupsen des Treibers und schauen ob dieser wieder Platz für neue Daten hat. Latency ist die Latenz mit welcher das Playback abläuft. Dies ist von SoundOut zu SoundOut verschieden. Wasapi ist hier am leistungsstärksten und schafft bei mir bis zu 20ms. DirectSoundOut schafft maximal 50 (eher 80-90) und WaveOut kannste mal bei so 100-150 ansetzen. Diese Einstellung beeinflusst die Puffergröße und somit auch das Interval in welchem der Treiber mit neuen Rohdaten gefüttert wird. Bei reinem Playback hat eine etwas höhere Latenz den Vorteil, dass das Playback weniger anfällig für kurze Lags oder Performanceinbrüche ist. Lässt man jedoch z.B. ne Visualisierung in Realtime laufen, dann wirst du nicht froh sein, wenn sich die Anzeige alle 2 Sekunden aktualisiert sondern eher bei 60-70ms. Gleiches Prinzip wenn du z.B. nen Equalizer verwendest und du veränderst die einzelnen Filter. Verwendest du hier eine Latenz von 2000 => 2 sek., dann kann es sein, dass die neuen Einstellungen erst zwei Sekunden später zum Tragen kommen, da bereits der Treiber die Daten für die nächsten 2 Sekunden im Puffer hat, welche jedoch noch nicht die neuen Eq Einstellungen bekommen haben.

        Alles in allem ist das immer so ne Gratwanderung zwischen Benutzerfreundlichkeit, Aktualisierungszeit,... und Performance. Wie gesagt oben sind so paar Richtwerte wobei ich bei Wasapi auch so bei 50ms ansetzen würde damit es auf den meisten Rechnern schön flüssig läuft.


      Ansonsten bei Fragen/Fehlen/Anliegen immer her damit :!: :!:


      Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
      Dass es auch mit dem Dateinamen geht, ist mir klar. Der Vollständigkeit halber wollte ich aber beides in die Engine einbauen.
      Dann löse ichs am besten so, dass ich das einfach nach oben weiterreiche und der Aufrufer sich drum kümmern muss, oder?

      Also in Sachen Bugs: entweder ich mach was falsch, oder mit dem WasapiOut läuft was schief. Ich kann den nur genau einmal abspielen, auch wenn ich die Position in der zugrundeliegende IWaveSource auf 0 setze oder ihn neu initialisiere. Benutze ich nen DirectSoundOut funktioniert mein Code bestens, aber Wasapi spielt wohl besser ab (bei Directsound bekomme ich imme son Knacken am Anfang und Ende).
      Wasapi ist definitiv neuer und hat weniger knacken etc.
      Abspielen solltest du theoretische mehrmals können. Wie sieht denn dein Code genau aus?


      Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
      Der Code sieht so aus (den ersten Teil hab ich aus dem Startpost):
      Spoiler anzeigen

      C-Quellcode

      1. public class CSCoreEngine : AudioEngine
      2. {
      3. private ISoundOut CreateSoundOut(ref IWaveSource source)
      4. {
      5. ISoundOut soundOut;
      6. if (WasapiOut.IsSupportedOnCurrentPlatform)
      7. soundOut = new WasapiOut(false, AudioClientShareMode.Shared, 100);
      8. else
      9. {
      10. soundOut = new DirectSoundOut() { Latency = 100 };
      11. if (source.WaveFormat.BitsPerSample > 16)
      12. source = source.ToSampleSource().ToWaveSource(16);
      13. }
      14. return soundOut;
      15. }
      16. public override AudioHandle CreateHandle(Stream stream)
      17. {
      18. IWaveSource source = CodecFactory.Instance.GetCodec(null, stream);
      19. ISoundOut soundOut = this.CreateSoundOut(ref source);
      20. return new CSCoreHandle(soundOut, source);
      21. }
      22. public override AudioHandle CreateHandle(string file)
      23. {
      24. IWaveSource source = CodecFactory.Instance.GetCodec(file);
      25. ISoundOut soundOut = this.CreateSoundOut(ref source);
      26. return new CSCoreHandle(soundOut, source);
      27. }
      28. }
      29. internal class CSCoreHandle : AudioHandle
      30. {
      31. readonly ISoundOut soundOut;
      32. readonly IWaveSource source;
      33. public override TimeSpan Length
      34. {
      35. get { return source.GetLength(); }
      36. }
      37. public override TimeSpan Position
      38. {
      39. get { return source.GetPosition(); }
      40. set { source.SetPosition(value); }
      41. }
      42. public override float Volume
      43. {
      44. get { return soundOut.Volume; }
      45. set { soundOut.Volume = value; }
      46. }
      47. public override PlaybackState State
      48. {
      49. get { return (PlaybackState)soundOut.PlaybackState; }
      50. }
      51. internal CSCoreHandle(ISoundOut soundOut, IWaveSource source)
      52. {
      53. this.soundOut = soundOut;
      54. this.source = source;
      55. soundOut.Initialize(source);
      56. soundOut.Stopped += (sender, e) => this.OnPlaybackStopped(EventArgs.Empty);
      57. }
      58. public override void Play()
      59. {
      60. if (soundOut.PlaybackState == sOut.PlaybackState.Stopped)
      61. {
      62. source.Position = 0;
      63. soundOut.Play();
      64. }
      65. else if (soundOut.PlaybackState == sOut.PlaybackState.Paused)
      66. soundOut.Resume();
      67. }
      68. public override void Pause()
      69. {
      70. soundOut.Pause();
      71. }
      72. public override void Stop()
      73. {
      74. soundOut.Stop();
      75. }
      76. protected override void Dispose(bool disposing)
      77. {
      78. soundOut.Dispose();
      79. source.Dispose();
      80. }
      81. }

      Aber wie gesagt, wenn ich in CSCoreEngine.CreateSoundOut einfach immer nen DirectSoundOut erstelle, anstatt nen WasapiOut, dann funktionierts so, deswegen kannst doch eigentlich nicht an meinem Code liegen.
      Kann auf die Schnelle jetzt keinen Fehler entdecken. Habe deshalb das auch noch extra bei mir ausprobiert mit folgendem ganz simplen Code:

      Quellcode

      1. WasapiOut soundOut = new WasapiOut(); IWaveSource source = CodecFactory.Instance.GetCodec(@"C:\Temp\test.mp3");
      2. while (true)
      3. {
      4. soundOut.Initialize(source);
      5. soundOut.Play();
      6. Thread.Sleep(300);
      7. soundOut.Stop();
      8. source.Position = 0;
      9. }


      Und das jetzt knapp 15 Minuten laufen lassen. Ohne Probleme.
      Es KANN und wird wahrscheinlich so sein, dass ich grad ne neuere Version habe. Ich kann dir die mal geben, da ich eigentlich laufend am Fehler suchen bin und schon über einen Monat nix mehr auf NuGet geladen habe.
      Dateien
      • CSCore.dll

        (365,57 kB, 114 mal heruntergeladen, zuletzt: )


      Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
      Gut, dass es jetzt noch schlimmer geworden ist. :thumbsup:
      Jetzt wird nichtmal beim ersten mal richtig abgespielt.

      Edit: ich nehm gleich einfach WaveOut, das funktioniert wunderbar.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Artentus“ ()

      So, hab nochmal ein bisschen in nem Testprojekt rumprobiert.
      Ist deine Testdatei länger als 300ms? Wenn ja, dann erklärt das, warum es bei dir funktioniert. Bei mir funktionierts nämlich auch, solange ich manuell Stop aufrufe, bevor die Datei zu Ende ist. Lasse ich aber bis zum Ende durchlaufen, dann gehts nicht mehr.
      Ja ist sie. Wenn die Datei fertig ist, dann wird automatisch gestoppt. Ich schau mal kurz.
      Dateien
      • CSCore.dll

        (365,57 kB, 119 mal heruntergeladen, zuletzt: )


      Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „thefiloe“ ()

      Ich versuche gerade, etwas aufzunehmen und verwende im Grunde genommen den Code aus deinem Startpost. Das funktioniert auch super, alles so, wie ich will. Nun möchte ich aber den abgespeicherten Dateien ID3-Tags hinzufügen. Auslesen kann ich die Dinger schon, das Schreiben ist aber noch nicht möglich. Kurz zusammengefasst, was ich momentan mache:
      ISoundIn -> WaveWriter -> Datei und bei Beenden der Aufnahme wird die Datei in eine weitere kopiert. Hier möchte ich halt ein paar ID3-Infos hinzufügen, vorzugsweise ID3v2. Hoffe, du kannst mir helfen :).

      //EDIT:
      Was mir gerade noch aufgefallen ist: Ein einziges Lied ist bei dieser Aufnahmevariante ca. 100MB groß, gibt es dafür auch Workarounds?

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „nafets3646“ ()