Umrechnen von Byte Position/Pixel/TimeSpan

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von kafffee.

    Umrechnen von Byte Position/Pixel/TimeSpan

    Hallo liebe Community,

    ich möchte mit Hilfe der bass.dll Vinylplatten aufnehmen (Datenfromat PCM) und in ein Byte Array speichern. Soweit so gut, das klappt. Nun möchte ich dem User während und nach der Aufnahme eine WaveForm anzeigen (klappt auch), damit mein Programm, nachdem der User die Schnitte auf der Waveform markiert hat, die Aufnahme in die einzelnen Musiktitel "zerhacken" kann. Soweit bin ich noch nicht.

    Problem ist, ich möchte während der Aufnahme eine vertikale Linie auf der Waveform zeichnen, die den Aufnahmefortschritt anzeigt und diesen auch als TimeSpan in einem Label ausgeben.
    Desweiteren möchte ich nach der Aufnahme den User den Schnitt markieren lassen, indem er mit der Maus auf die entsprechende Stelle klickt. Da soll dann eine weitere Linie angezeigt werden (das hab ich auch schon, jetzt möchte ich bloss, dass mir in einem Label diese Position als TimeSpan ausgibt).

    Im Grunde genommen eher ein mathematisches Problem. aber ich krieg es einfach seit vier Stunden nicht hin, wie man die Werte umrechnet...

    Es ist ein WPF MVVM Projekt, ich poste das hier trotzdem in diesem Unterforum, weil das Problem an sich nichts MVVM-spezifisches ist, sondern wirklich eher eins mit der bass.dll Ich versuche das mal so darzustellen, dass auch die Kollegen die mit WinForms unterwegs sind, das auch lesen können.

    Ich habe folgende Properties:



    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Property WFBreite As Double 'Breite der WaveForm
    2. Private ReadOnly Property MaxTrackLaengeInBytes As Double 'Die begrenzte maximale Aufnahmedauer
    3. Get
    4. If LaengeInBytes Then 'Der User kann diese entweder in Megabyte eingeben oder in Minuten, gewaehlt durch LaengeInBytes As Boolean
    5. Return CDbl(MaxMB) * 1024 * 1024 'MaxMB: eingegebener Wert für maximale MegaBytes; Umrechnung von Megabytes in Bytes
    6. Else
    7. Return 44100 * 16 * 2 * CDbl(MaxMinutes) * 60 / 8 'MaxMinutes: eingegebener maximaler Wert für Minuten, Umrechnung in Bytes für PCM-Format (Samplerate * Bittiefe * 2 Kanäle (stereo) * MaxMinutes * 60 Sekunden / 8
    8. End If
    9. End Get
    10. End Property
    11. Public Property RecordingXPosWF As Double 'X-Position der vertikalen Linie für den Aufnahmefortschritt
    12. Private _MovingXPosWF As Double ''X-Position der vertikalen Linie, die mit einem Klick festgelegt wird
    13. Public Property MovingXPosWF As Double
    14. Get
    15. Return _MovingXPosWF
    16. End Get
    17. Set(value As Double)
    18. _MovingXPosWF = value
    19. AktuellePosition = TimeSpan.FromSeconds(GetPositionInSeconds(value))
    20. RaisePropertyChanged() 'MVVM-spezifisch
    21. End Set
    22. End Property
    23. Public Property AktuellePosition As TimeSpan 'diese wird auf einem Label ausgegeben; aktuelle Position der Maus auf der Waveform als TimeSpan





    Und diese Funktionen:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Function GetPositionInSeconds(CursorPos As Double) As Double 'Umrechnen der Mausposition auf der Waveform in Sekunden, da bin ich mir unsicher ob das so die richtigen Werte gibt.
    2. Dim AufnahmeLaenge As Double = Bass.BASS_ChannelBytes2Seconds(RecordingChannel, Bass.BASS_ChannelGetLength(RecordingChannel, BASSMode.BASS_POS_BYTE))
    3. Dim Visualisierungbreite = Bass.BASS_ChannelGetPosition(RecordingChannel, BASSMode.BASS_POS_BYTE) * MaxTrackLaengeInBytes / WFBreite ' da die Aufnahme nicht jedes Mal die maximal festgelegte Länge hat, berechne ich hier die visuelle Breite der tatsächlich aufgenommenen Daten nach Beenden der Aufnahme, hier könnte der Fehler liegen
    4. Dim percent As Double = CursorPos / Visualisierungbreite * 100
    5. Return AufnahmeLaenge / 100 * percent
    6. End Function
    7. Private Function MyRecording(handle As Integer, buffer As IntPtr, length As Integer, user As IntPtr) As Boolean ' Callback-Funktion, wird während der Aufnahme aufgerufen
    8. If length > 0 AndAlso buffer <> IntPtr.Zero Then
    9. If RecordedData Is Nothing OrElse RecordedData.Length < length Then
    10. RecordedData = New Byte(length) {}
    11. End If
    12. ' copy from managed to unmanaged memory
    13. Marshal.Copy(buffer, RecordedData, 0, length)
    14. BytesWritten += length
    15. Dim AufnahmeLaenge As Double = Bass.BASS_ChannelBytes2Seconds(RecordingChannel, Bass.BASS_ChannelGetLength(RecordingChannel, BASSMode.BASS_POS_BYTE)) 'diese vier Zeilen hier müssen die Aufnahmeposition von Bytes (Aufnahmeposition) in Pixel (bzw. Units bei WPF) (Position der Linie auf der Waveform) umgerechnet werden, das klappt nicht, die Linie bleibt auf Position 0.
    16. Dim Visualisierungsbreite = Bass.BASS_ChannelGetPosition(RecordingChannel, BASSMode.BASS_POS_BYTE) * MaxTrackLaengeInBytes / WFBreite
    17. Dim percent As Double = Bass.BASS_ChannelGetPosition(RecordingChannel, BASSMode.BASS_POS_BYTE) / Visualisierungbreite * 100
    18. RecordingXPosWF = 60 / 100 * percent
    19. ' get and draw our live recording waveform
    20. WF.RenderRecording(buffer, length)
    21. Services.ServiceContainer.GetService(Of IMainWindowService)?.HoleDispatcher().Invoke(Sub() WellenFormZeichnen())
    22. End If
    23. Return True
    24. End Function


    Bei der ersten Funktion bin ich mir unsicher ob das so richtig ist, denn wenn ich die maximale Aufnahmedauer auf 1 min setze, zeigt er mir, wenn ich mit der Maus ganz rechts auf die Waveform fahre, eben nicht genau eine Minute an... sondern "00:00:00.5320000".

    Bei der zweiten Funktion bleibt die Stellung der Linie auf 0.

    Es ist also wie gesagt eher ein mathematisches Problem. Kann mir da jemand weiterhelfen?
    @kafffee Warum neu erfinden, was schon andere erfunden haben: Audacity
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Beispielhafte Ausgangsituation:
    60000ms = 1000px = 100%

    Um nun auszurechen wie viele Pixel für 15 Sekunden gebraucht werden bzw. entsprechen.
    also:
    1000 / 60000 * 15000 = 250, also 250px entsprechen 15 Sekunden.

    Um nun zu wissen viele Bytes eine Sekunde entsprechen:(Bittiefe 16 Bit z.B.. Bei BASS_SAMPLE_FLOAT wären das 32Bit)
    Samplerate * Bittiefe / 8. Die Samplerate ist ja schon in Hertz(schwingungen pro Sekunde) Der Rest ist wirklich wie das hier auch einfache Mathematik.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

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

    Jo das sollte helfen, mal gucken ob ich's damit hinkrieg. Hab mir grad auch in der Zwischenzeit fertigen Code auf einem Blatt notiert, nachdem meine Denksperre wieder weg ist... Habs noch nicht verglichen mit deinem aber ich mach's glaub genauso. Bin bloß noch nicht dazu gekommen das mal zu testen.

    Bytes pro Sekunde ist klar, ist zwar bei PCM einfach, aber für andere Formate stellt die bass.dll ja sogar was zur Verfügung:

    Bass.BASS_ChannelBytes2Seconds()

    bzw.

    Bass.BASS_ChannelSeconds2Bytes()

    Das basiert dann auf Samplingrate usw. des ChannelHandles...

    @DTF
    Edit:

    Danke dir habs hinbekommen :)

    Für die aktuelle Position der laufenden Aufnahme (vertikale Linie):

    VB.NET-Quellcode

    1. Dim Prozent As Double = BytesWritten / MaxTrackLaengeInBytes * 100
    2. RecordingXPosWF = WFBreite / 100 * Prozent


    Für die Labelausgabe der Mausposition als TimeSpan:


    VB.NET-Quellcode

    1. Private Function GetPositionInSeconds(CursorPos As Double) As Double
    2. Dim Prozent As Double = CursorPos / WFBreite * 100
    3. Dim Position As Double = MaxTrackLaengeInBytes * Prozent / 100
    4. Dim BytesPerSecondPCM As Double = 44100 * 16 * 2 / 8
    5. Return Position / BytesPerSecondPCM
    6. End Function

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „kafffee“ ()