Wiedergabe einer Streamaufnahme (mit der BASS_LIB)

Für allgemeine Fragen zur Programmierung mit PureBasic.
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Wiedergabe einer Streamaufnahme (mit der BASS_LIB)

Beitrag von ccode_new »

Hallo liebe Leute,

ich habe mal einen kleinen Recorder mit der Bass-Lib gebastelt.
(An einer SDL2-Version arbeite ich noch, da macht mir die WAV-Erstellung-Probleme)

Jetzt geht es aber um die BASS-Lib. (Die funktioniert auch unter Linux und MacOS)

Was mache ich hier beim Abspielen der Streamaufnahme falsch, bzw. wie mache ich es richtig?

Code: Alles auswählen

;-Begin

IncludeFile "bass.pbi" ;Benötigt die Bass_LIB (DLL und Include)

Structure WAVEHEADER_RIFF     ; == 12 bytes ==
    RIFF.l                    ; "RIFF" = &H46464952
    riffBlockSize.l           ; pos + 44 - 8
    riffBlockType.l           ; "WAVE" = &H45564157
EndStructure
;Debug SizeOf(WAVEHEADER_RIFF)  

Structure WAVEHEADER_data     ; == 8 bytes ==
   dataBlockType.l            ; "data" = &H61746164
   dataBlockSize.l            ; pos
EndStructure

Structure NEW_WAVEFORMAT             ; == 24 bytes ==
    wfBlockType.l             ; "fmt " = &H20746D66
    wfBlockSize.l
    ; == Die Blockgröße beginnt hier = 16 bytes
    wFormatTag.w
    nChannels.w
    nSamplesPerSec.l
    nAvgBytesPerSec.l
    nBlockAlign.w
    wBitsPerSample.w
EndStructure
;Debug SizeOf(WAVEFORMAT)  

Global wr.WAVEHEADER_RIFF
Global wf.NEW_WAVEFORMAT
Global wd.WAVEHEADER_data

#BUFSTEP = 200000 ;Speicherzuordnungseinheit

Global input_.l ;Aktuelle Eingabequelle
Global *recPtr ;Ein Aufnahmezeiger auf einen Speicherort
Global reclen.l ;Pufferlänge

Global rchan.i ;Aufnahmekanal
Global chan.i  ;Wiedergabekanal

Global Event

Global btnRecord, btnPlay, btnSave, txtRecDevice, lstDeviceViewer

Declare lstInput_Click()

;Eventuelle Fehlermeldung anzeigen
Procedure Error_(Message.s)
  Debug Message
  MessageRequester("Error", Message + Chr(13) + Chr(13) + "Error Code : " + Str(BASS_ErrorGetCode), #PB_MessageRequester_Error)
EndProcedure

BASS_SetConfig(#BASS_CONFIG_UPDATEPERIOD, 0)

Debug "Bass-Version: " + Hex(BASS_GetVersion(), #PB_Long)
;Für die Gerätetypinformationen wird 2040B00 (BASS 2.4.11.0) oder höher benötigt.

BASS_SetConfig(#BASS_CONFIG_UNICODE, #True)

;Puffern der aufgezeichneten Daten (Rückrufverfahren)
Procedure RecordingCallback(handle.l, *buffer, length.l, *user)
  ;Puffergröße bei Bedarf erhöhen
  If ((reclen % #BUFSTEP) + length >= #BUFSTEP)
    *recPtr = ReAllocateMemory(*recPtr, ((reclen + length) / #BUFSTEP + 1) * #BUFSTEP)
    If *recPtr = #Null
      rchan = #Null
      Error_("Out of memory!")
      SetGadgetText(btnRecord, "Record")
      ProcedureReturn #False ;Abbrechen
    EndIf
  EndIf
  ;Puffern der Daten
  CopyMemory(*buffer, *recPtr + reclen, length)
  ;CopyMemory(*Destination, *Source, SIZE_T Length) - PureBasic ist hier verwirrend. (Source <> Destination)
  
  reclen + length
  ProcedureReturn #True ;Weitermachen
EndProcedure

Procedure StartRecording()
  ;Bereinigung der alten Aufnahme
  If (*recPtr <> #Null)
    BASS_StreamFree(chan)
    FreeMemory(*recPtr)
    *recPtr = #Null
    chan = #Null
    DisableGadget(btnPlay, #True)
    DisableGadget(btnSave, #True)
  EndIf
  
  ;Platz da, jetzt kommt der "WAVE-HEADER"
  *recPtr = AllocateMemory(#BUFSTEP)
  reclen = 44
  
  ;Mit diesen Informationen ist es ein offizieller "WAVE-HEADER"
  wf\wFormatTag = 1
  wf\nChannels = 2
  wf\wBitsPerSample = 16
  wf\nSamplesPerSec = 44100
  wf\nBlockAlign = wf\nChannels * wf\wBitsPerSample / 8
  wf\nAvgBytesPerSec = wf\nSamplesPerSec * wf\nBlockAlign
  
  ;Setzen des "WAV-FMT-Header"
  wf\wfBlockType = $20746D66 ;"fmt "
  wf\wfBlockSize = 16
  
  ;Setzen des "WAV-RIFF-Header"
  wr\RIFF = $46464952 ;"RIFF"
  wr\riffBlockSize = 0 ;nach der Aufnahme
  wr\riffBlockType = $45564157 ;"WAVE"
  
  ;Setzen des "WAV-DATA-Header"
  wd\dataBlockType = $61746164 ;"data"
  wd\dataBlockSize = 0 ;nach der Aufnahme
  
  ;Kopieren des WAV-Header in den Speicher
  CopyMemory(wr, *recPtr, SizeOf(wr)) ;"RIFF"
  CopyMemory(wf, *recPtr + 12, SizeOf(wf)) ;"fmt "
  CopyMemory(wd, *recPtr + 36, SizeOf(wd)) ;"data"
  
  ;Aufnahmegerät setzen / bzw. neu initialisieren (z.B. unter MacOS notwendig)
  If input_ >= 0
    BASS_RecordInit(input_)
    BASS_RecordSetInput(input_, #BASS_INPUT_ON, 1)
  EndIf
  
  ;Die Aufnahme beginnen @ 44100hz 16-bit stereo
  rchan = BASS_RecordStart(44100, 2, 0, @RecordingCallback(), 0)
  
  If (rchan = #Null)
    Error_("Die Aufnahme kann nicht gestartet werden.")
    FreeMemory(*recPtr)
    *recPtr = #Null
    ProcedureReturn 0
  EndIf
  SetGadgetText(btnRecord, "Stopp")
EndProcedure

Procedure StopRecording()
  BASS_ChannelStop(rchan)
  rchan = #Null
  
  SetGadgetText(btnRecord, "Aufnahme")
  
  ;So, ab jetzt wirst du ein vollständiger "WAV-Header" (Vervollständigen des Headers)
  wr\riffBlockSize = reclen - 8
  wd\dataBlockSize = reclen - 44
  
  CopyMemory(@wr\riffBlockSize, *recPtr + 4, SizeOf(wr\riffBlockSize))
  CopyMemory(@wd\dataBlockSize, *recPtr + 40, SizeOf(wd\dataBlockSize))
  
  ;Einen Stream aus der Aufnahme erstellen
  chan = BASS_StreamCreateFile(#True, *recPtr, 0, reclen, 0)
  
  ;HSTREAM BASS_StreamCreateFile(BOOL mem, void *file, QWORD offset, QWORD length, DWORD flags);

  If (chan <> #Null)
    ;Aktivieren des Abspiel- und Speicherknopfs
    DisableGadget(btnPlay, #False)
    DisableGadget(btnSave, #False)
  Else
    Error_("Der Aufnahmestream kann nicht erstellt werden.")
  EndIf
EndProcedure

;Die aufgezeichneten Daten auf die Festplatte schreiben
Procedure WriteToDisk()
  Protected FileName.s
  Protected FileHandle.i
  
  FileName = SaveFileRequester("Speichern als...","","WAV-Dateien|*.wav|Alles|*.*",0)
  
  If OpenFile(0, FileName)
    WriteData(0, *recPtr, reclen)
    CloseFile(0)
  Else
    Error_("Die Datei kann nicht erstellt werden.")
    ProcedureReturn 0
  EndIf
  
EndProcedure

Procedure UpdateInputInfo()
  Protected it.l
  Protected level.f
  Protected type_.s
  
  it = BASS_RecordGetInput(input_, @level) ;Informationen zur Eingabe erhalten
  If (it = -1 Or level < 0)                ;Fehlgeschlagen?
    BASS_RecordGetInput(-1, @level)        ;Haupteingabequelle versuchen
    If (level < 0)
      level = 1
    EndIf
  EndIf
  
  Select (it & #BASS_INPUT_TYPE_MASK)
    Case #BASS_INPUT_TYPE_DIGITAL
      type_ = "digital"
    Case #BASS_INPUT_TYPE_LINE
      type_ = "line-in"
    Case #BASS_INPUT_TYPE_MIC
      type_ = "microphone"
    Case #BASS_INPUT_TYPE_SYNTH
      type_ = "midi synth"
    Case #BASS_INPUT_TYPE_CD
      type_ = "analog cd"
    Case #BASS_INPUT_TYPE_PHONE
      type_ = "telephone"
    Case #BASS_INPUT_TYPE_SPEAKER
      type_ = "pc speaker"
    Case #BASS_INPUT_TYPE_WAVE
      type_ = "wave/pcm"
    Case #BASS_INPUT_TYPE_AUX
      type_ = "aux"
    Case #BASS_INPUT_TYPE_ANALOG
      type_ = "analog"
    Default
      type_ = "undefined"
  EndSelect
  StatusBarText(0, 1, "Eingabegerät: "+ type_)
EndProcedure

Procedure tmrRecTest_Timer()
  ;Aktualisieren des Aufnahme- / Wiedergabezähler
  If (rchan) ;Aufnahme
    StatusBarText(0, 1, Str(BASS_ChannelGetPosition(rchan, #BASS_POS_BYTE)))
  ElseIf (chan)
    If (BASS_ChannelIsActive(chan)) ;Wiedergabe
      StatusBarText(0, 1, Str(BASS_ChannelGetPosition(rchan, #BASS_POS_BYTE)) + " / " + Str(BASS_ChannelGetLength(chan, #BASS_POS_BYTE)))
    Else
      StatusBarText(0, 1, Str(BASS_ChannelGetLength(chan, #BASS_POS_BYTE)))
    EndIf
  EndIf
EndProcedure

Procedure Form_Load()
  Protected c.l = 0
  Protected info.BASS_DEVICEINFO
  ;Setup-Aufnahme- und Ausgabegeräte (mit Standardgeräten)
  If (BASS_RecordInit(-1) = 0) Or (BASS_Init(-1, 44100, 0, WindowID(0), 0) = 0)
    Error_("Gerät kann nicht initialisiert werden.")
    End
  Else
    ;Liste der Eingabegeräte abrufen
    input_ = -1
    
    While BASS_RecordGetDeviceInfo(c, info)
      AddGadgetItem(lstDeviceViewer, -1, PeekS(info\name, 31, #PB_UTF8))
      If (BASS_RecordGetInput(c, 0) And #BASS_INPUT_OFF) = 0
        SetGadgetState(lstDeviceViewer, c)
        input_ = c
        UpdateInputInfo()
      EndIf
      c + 1
    Wend
  EndIf
  
  AddWindowTimer(0, 123, 200) ;Timer zum Aktualisieren der Positionsanzeige (200ms)
  
  *recPtr = #Null
  reclen = 0
EndProcedure

;Aufnahmegerät ändern
Procedure lstInput_Click()
  Protected.w i
  input_ = GetGadgetState(lstDeviceViewer)
  If input_ <> -1
    For i = 0 To CountGadgetItems(lstDeviceViewer)
      BASS_RecordSetInput(i, #BASS_INPUT_OFF, -1)
    Next i
    BASS_RecordSetInput(input_, #BASS_INPUT_ON, -1)
    UpdateInputInfo()
  EndIf
EndProcedure


;-GUI

If OpenWindow(0, 0, 0, 430, 200, "Mein Bass_Aufnahme_Test", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)

  CreateStatusBar(0, WindowID(0))
  AddStatusBarField(50)
  StatusBarText(0, 0, "Status:", #PB_StatusBar_Raised)
  AddStatusBarField(200)
  btnRecord = ButtonGadget(#PB_Any, 10, 10, 130, 30, "Aufnahme")
  btnPlay = ButtonGadget(#PB_Any, 150, 10, 130, 30, "Abspielen")
  btnSave = ButtonGadget(#PB_Any, 290, 10, 130, 30, "Speichern als WAV")
  txtRecDevice = TextGadget(#PB_Any, 10, 60, 110, 20, "Aufnahmegerät:")
  lstDeviceViewer = ListViewGadget(#PB_Any, 120, 60, 300, 100)
  
  DisableGadget(btnPlay, #True)
  DisableGadget(btnSave, #True)
  Form_Load()
  
  Repeat
    Event=WaitWindowEvent(10)
    Select Event
      Case #PB_Event_Gadget
        Select EventGadget()
          Case btnRecord ;Record
            If (rchan = 0)
              StartRecording()
            Else
              StopRecording()
            EndIf
          Case btnSave ;Save
            WriteToDisk()
          Case btnPlay
            BASS_ChannelPlay(chan, #True)  ;Warum funktioniert das Abspielen nicht?
          Case lstDeviceViewer
            lstInput_Click()
        EndSelect
      Case #PB_Event_Timer
        Select EventTimer()
          Case 123
            tmrRecTest_Timer()
        EndSelect
    EndSelect
  Until Event=#PB_Event_CloseWindow
EndIf

If *recPtr <> #Null
  FreeMemory(*recPtr)
EndIf
BASS_RecordFree()
BASS_Free()
End
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Benutzeravatar
Mijikai
Beiträge: 754
Registriert: 25.09.2016 01:42

Re: Wiedergabe einer Streamaufnahme (mit der BASS_LIB)

Beitrag von Mijikai »

StreamCreateFile.l() akzeptiert nur ein schon existierendes File.
StreamCreate.l() wird wohl das Richtige sein benötigt jedoch einen Callback...
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Wiedergabe einer Streamaufnahme (mit der BASS_LIB)

Beitrag von ccode_new »

Hi!

Aber es existiert ein File im RAM.

Der Aufruf der Funktion "StreamCreateFile" ist laut BASS-Dokumentation so eigentlich richtig.

http://www.un4seen.com/doc/#bass/BASS_S ... eFile.html
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Benutzeravatar
Mijikai
Beiträge: 754
Registriert: 25.09.2016 01:42

Re: Wiedergabe einer Streamaufnahme (mit der BASS_LIB)

Beitrag von Mijikai »

Stimmt habs getestet es funktioiert.
Vermutlich ist dann das Format/Header nicht korrekt.
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Wiedergabe einer Streamaufnahme (mit der BASS_LIB)

Beitrag von ccode_new »

Das hier funktioniert aber:

Code: Alles auswählen

;....

;-GUI
InitSound()

If OpenWindow(0, 0, 0, 430, 200, "Mein Bass_Aufnahme_Test", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)

  CreateStatusBar(0, WindowID(0))
  AddStatusBarField(50)
  StatusBarText(0, 0, "Status:", #PB_StatusBar_Raised)
  AddStatusBarField(200)
  btnRecord = ButtonGadget(#PB_Any, 10, 10, 130, 30, "Aufnahme")
  btnPlay = ButtonGadget(#PB_Any, 150, 10, 130, 30, "Abspielen")
  btnSave = ButtonGadget(#PB_Any, 290, 10, 130, 30, "Speichern als WAV")
  txtRecDevice = TextGadget(#PB_Any, 10, 60, 110, 20, "Aufnahmegerät:")
  lstDeviceViewer = ListViewGadget(#PB_Any, 120, 60, 300, 100)
  
  DisableGadget(btnPlay, #True)
  DisableGadget(btnSave, #True)
  Form_Load()
  
  Repeat
    Event=WaitWindowEvent(10)
    Select Event
      Case #PB_Event_Gadget
        Select EventGadget()
          Case btnRecord ;Record
            If (rchan = 0)
              StartRecording()
            Else
              StopRecording()
            EndIf
          Case btnSave ;Save
            WriteToDisk()
          Case btnPlay
            ;BASS_ChannelPlay(chan, #True)  ;Warum funktioniert das Abspielen nicht?
            CatchSound(0, *recPtr, reclen)
            PlaySound(0) ;Das funktioniert sehr gut!
          Case lstDeviceViewer
            lstInput_Click()
        EndSelect
      Case #PB_Event_Timer
        Select EventTimer()
          Case 123
            tmrRecTest_Timer()
        EndSelect
    EndSelect
  Until Event=#PB_Event_CloseWindow
EndIf
;...
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Wiedergabe einer Streamaufnahme (mit der BASS_LIB)

Beitrag von ccode_new »

Anbei:

Außerdem gefällt mir mein ganzes bisheriges Konzept nicht.
Es ist einfach total unsinnig soooo viel Daten in den Arbeitsspeicher zu schreiben.
Nach einer kurzen Weile wird dann auch das Programm mit Debugger beenden, oder das Betriebssystem sagt:
"Nein du kleines Programmchen, du schreibst mir konstant zu viel in den Arbeitsspeicher."
->Und Tschüss!

Besser wäre:
Schreiben->Lesen->vorheriges verwerfen->...

Aber bei Aufnahmen?
Komprimieren?
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Benutzeravatar
Mijikai
Beiträge: 754
Registriert: 25.09.2016 01:42

Re: Wiedergabe einer Streamaufnahme (mit der BASS_LIB)

Beitrag von Mijikai »

Warum es mit CatchSound()/PlaySound() funktioniert kann ich nicht sagen.
Aber das bedeutet leider nicht das die Daten korrekt sind!

z.B. bastel ich gerade an einem kleinen Renderer der PixelBuffer rotiert.
Bei der Rotation enstehen Artefakte - aber keine der PB Grafikausgaben zeigt das Problem!
PB interpoliert/Filter alles egal ob in ein Screen/Image/Canvas/... gerendert wird.
So bleiben Probleme verborgen oder es enstehen Neue.
Die einzigste Lösung war der Umweg über WinApi um Pixel im Original darzustellen.

Wenn das WAV im Speicher in Ornung ist gibt es einen Bug in BASS.
Kannst du es mit einem externen Soundfile mit gleichen Paramerern testen?
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Wiedergabe einer Streamaufnahme (mit der BASS_LIB)

Beitrag von ccode_new »

Hallo!

Ich habe hier eine korrigierte Version: (Diese funktioniert!)

Code: Alles auswählen

;-Begin

IncludeFile "bass.pbi" ;Benötigt die Bass_LIB (DLL und Include)

Structure WAVEHEADER_RIFF     ; == 12 bytes ==
  RIFF.l                      ; "RIFF" = &H46464952
  riffBlockSize.l             ; pos + 44 - 8
  riffBlockType.l             ; "WAVE" = &H45564157
EndStructure
;Debug SizeOf(WAVEHEADER_RIFF)  

Structure WAVEHEADER_data     ; == 8 bytes ==
  dataBlockType.l             ; "data" = &H61746164
  dataBlockSize.l             ; pos
EndStructure

Structure NEW_WAVEFORMAT             ; == 24 bytes ==
  wfBlockType.l                      ; "fmt " = &H20746D66
  wfBlockSize.l
  ; == Die Blockgröße beginnt hier = 16 bytes
  wFormatTag.u
  nChannels.u
  nSamplesPerSec.l
  nAvgBytesPerSec.l
  nBlockAlign.u
  wBitsPerSample.u
EndStructure
;Debug SizeOf(WAVEFORMAT)

Global wr.WAVEHEADER_RIFF
Global wf.NEW_WAVEFORMAT
Global wd.WAVEHEADER_data

#BUFSTEP = 200000 ;Speicherzuordnungseinheit

Global input_.l ;Aktuelle Eingabequelle
Global *recPtr  ;Ein Aufnahmezeiger auf einen Speicherort
Global reclen.l ;Pufferlänge

Global rchan.i ;Aufnahmekanal
Global chan.i  ;Wiedergabekanal

Global Event

Global btnRecord, btnPlay, btnSave, txtRecDevice, lstDeviceViewer

Declare UseInput_()

;Eventuelle Fehlermeldung anzeigen
Procedure Error_(Message.s)
  Debug Message
  MessageRequester("Error", Message + Chr(13) + Chr(13) + "Error Code : " + Str(BASS_ErrorGetCode), #PB_MessageRequester_Error)
EndProcedure

Debug "Bass-Version: " + Hex(BASS_GetVersion(), #PB_Long)
;Für die Gerätetypinformationen wird 2040B00 (BASS 2.4.11.0) oder höher benötigt.

BASS_SetConfig(#BASS_CONFIG_UNICODE, #True)

;Puffern der aufgezeichneten Daten (Rückrufverfahren)
Procedure RecordingCallback(handle.l, *buffer, length.l, *user)
  ;Puffergröße bei Bedarf erhöhen
  If ((reclen % #BUFSTEP) + length >= #BUFSTEP)
    *recPtr = ReAllocateMemory(*recPtr, ((reclen + length) / #BUFSTEP + 1) * #BUFSTEP)
    If *recPtr = #Null
      rchan = #Null
      Error_("Out of memory!")
      SetGadgetText(btnRecord, "Record")
      ProcedureReturn #False ;Abbrechen
    EndIf
  EndIf
  ;Puffern der Daten
  CopyMemory(*buffer, *recPtr + reclen, length)
  ;CopyMemory(*Destination, *Source, SIZE_T Length) - PureBasic ist hier verwirrend. (Source <> Destination)
  
  reclen + length
  ProcedureReturn #True ;Weitermachen
EndProcedure

Procedure StartRecording()
  ;Bereinigung der alten Aufnahme
  If (*recPtr <> #Null)
    BASS_StreamFree(chan)
    FreeMemory(*recPtr)
    *recPtr = #Null
    chan = #Null
    DisableGadget(btnPlay, #True)
    DisableGadget(btnSave, #True)
  EndIf
  
  ;Platz da, jetzt kommt der "WAVE-HEADER"
  *recPtr = AllocateMemory(#BUFSTEP)
  reclen = 44
  
  ;Mit diesen Informationen ist es ein offizieller "WAVE-HEADER"
  wf\wFormatTag = 1
  wf\nChannels = 2
  wf\wBitsPerSample = 16
  wf\nSamplesPerSec = 44100
  wf\nBlockAlign = wf\nChannels * wf\wBitsPerSample / 8
  wf\nAvgBytesPerSec = wf\nSamplesPerSec * wf\nBlockAlign
  
  ;Setzen des "WAV-FMT-Header"
  wf\wfBlockType = $20746D66 ;"fmt "
  wf\wfBlockSize = 16
  
  ;Setzen des "WAV-RIFF-Header"
  wr\RIFF = $46464952 ;"RIFF"
  wr\riffBlockSize = 0;nach der Aufnahme
  wr\riffBlockType = $45564157 ;"WAVE"
  
  ;Setzen des "WAV-DATA-Header"
  wd\dataBlockType = $61746164 ;"data"
  wd\dataBlockSize = 0         ;nach der Aufnahme
  
  ;Kopieren des WAV-Header in den Speicher
  CopyMemory(wr, *recPtr, SizeOf(wr)) ;"RIFF"
  CopyMemory(wf, *recPtr + 12, SizeOf(wf)) ;"fmt "
  CopyMemory(wd, *recPtr + 36, SizeOf(wd)) ;"data"
  
  ;Aufnahmegerät setzen / bzw. neu initialisieren (z.B. unter MacOS notwendig)
  UseInput_()
  Debug "Eingabe: " + input_
  
  If input_ >= 0
    BASS_RecordInit(input_)
    ;BASS_RecordSetDevice(input_)
    BASS_RecordSetInput(input_, #BASS_INPUT_ON, 1)
  EndIf
  
  ;Die Aufnahme beginnen @ 44100hz 16-bit stereo
  rchan = BASS_RecordStart(44100, 2, 0, @RecordingCallback(), 0)
  
  If (rchan = #Null)
    Error_("Die Aufnahme kann nicht gestartet werden.")
    FreeMemory(*recPtr)
    *recPtr = #Null
    ProcedureReturn 0
  EndIf
  SetGadgetText(btnRecord, "Stopp")
EndProcedure

Procedure StopRecording()
  BASS_ChannelStop(rchan)
  rchan = #Null
  
  SetGadgetText(btnRecord, "Aufnahme")
  
  ;So, ab jetzt wirst du ein vollständiger "WAV-Header" (Vervollständigen des Headers)
  wr\riffBlockSize = reclen - 8
  wd\dataBlockSize = reclen - 44
  
  CopyMemory(@wr\riffBlockSize, *recPtr + 4, SizeOf(wr\riffBlockSize))
  CopyMemory(@wd\dataBlockSize, *recPtr + 40, SizeOf(wd\dataBlockSize))
  
  ;Einen Stream aus der Aufnahme erstellen
  chan = BASS_StreamCreateFile(#True, *recPtr, 0, reclen, 0)
  
  ;HSTREAM BASS_StreamCreateFile(BOOL mem, void *file, QWORD offset, QWORD length, DWORD flags);
  
  If (chan <> #Null)
    ;Aktivieren des Abspiel- und Speicherknopfs
    DisableGadget(btnPlay, #False)
    DisableGadget(btnSave, #False)
  Else
    Error_("Der Aufnahmestream kann nicht erstellt werden.")
  EndIf
EndProcedure

;Die aufgezeichneten Daten auf die Festplatte schreiben
Procedure WriteToDisk()
  Protected FileName.s
  Protected FileHandle.i
  
  FileName = SaveFileRequester("Speichern als...","","WAV-Dateien|*.wav|Alles|*.*",0)
  
  If OpenFile(0, FileName)
    WriteData(0, *recPtr, reclen)
    CloseFile(0)
  Else
    Error_("Die Datei kann nicht erstellt werden.")
    ProcedureReturn 0
  EndIf
  
EndProcedure

Procedure UpdateInputInfo()
  Protected it.l
  Protected level.f
  Protected type_.s
  
  it = BASS_RecordGetInput(input_, @level) ;Informationen zur Eingabe erhalten
  If (it = -1 Or level < 0)                ;Fehlgeschlagen?
    BASS_RecordGetInput(-1, @level)        ;Haupteingabequelle versuchen
    If (level < 0)
      level = 1
    EndIf
  EndIf
  
  Select (it & #BASS_INPUT_TYPE_MASK)
    Case #BASS_INPUT_TYPE_DIGITAL
      type_ = "digital"
    Case #BASS_INPUT_TYPE_LINE
      type_ = "line-in"
    Case #BASS_INPUT_TYPE_MIC
      type_ = "microphone"
    Case #BASS_INPUT_TYPE_SYNTH
      type_ = "midi synth"
    Case #BASS_INPUT_TYPE_CD
      type_ = "analog cd"
    Case #BASS_INPUT_TYPE_PHONE
      type_ = "telephone"
    Case #BASS_INPUT_TYPE_SPEAKER
      type_ = "pc speaker"
    Case #BASS_INPUT_TYPE_WAVE
      type_ = "wave/pcm"
    Case #BASS_INPUT_TYPE_AUX
      type_ = "aux"
    Case #BASS_INPUT_TYPE_ANALOG
      type_ = "analog"
    Default
      type_ = "undefined"
  EndSelect
  StatusBarText(0, 2, "Eingabegerät: "+ type_)
EndProcedure

Procedure tmrRecTest_Timer()
  Protected.i Handle
  Protected.i fPos, ePos, eTime, aktSec, aktMin
  Protected.s aktTime, endTime
  ;Aktualisieren des Aufnahme- / Wiedergabezähler
  If rchan <> #Null
    Handle = rchan
  ElseIf chan <> #Null
    Handle = chan
  EndIf
  fPos = BASS_ChannelGetPosition(Handle, #BASS_POS_BYTE)
  ePos = BASS_ChannelGetLength(Handle, #BASS_POS_BYTE)
  aktSec = BASS_ChannelBytes2Seconds(Handle, fPos)
  eTime = BASS_ChannelBytes2Seconds(Handle, ePos)
  aktMin = Int(aktSec / 60)
  aktSec = Int(Mod(aktSec, 60))
  If aktSec < 10
    aktTime = StrD(aktMin) + ":" + "0" + StrD(aktSec)
  Else
    aktTime = StrD(aktMin) + ":" + StrD(aktSec)
  EndIf
  If Int(Mod(eTime, 60)) < 10
    endTime = StrD(Int(eTime / 60)) + ":" + "0" + StrD(Int(Mod(eTime, 60)))
  Else
    endTime = StrD(Int(eTime / 60)) + ":" + StrD(Int(Mod(eTime, 60)))
  EndIf
  If (rchan) ;Aufnahme
    StatusBarText(0, 1, aktTime + " Min")
  ElseIf (chan)
    If (BASS_ChannelIsActive(chan)) ;Wiedergabe
      StatusBarText(0, 1, aktTime + " Min / " + endTime + " Min")
    Else
      StatusBarText(0, 1, aktTime + " Min")
    EndIf
  EndIf
EndProcedure

Procedure Form_Load()
  Protected c.l = 0
  Protected info.BASS_DEVICEINFO
  ;Setup-Aufnahme- und Ausgabegeräte (mit Standardgeräten)
  If (BASS_RecordInit(-1) = 0) Or (BASS_Init(-1, 44100, 0, WindowID(0), 0) = 0)
    Error_("Gerät kann nicht initialisiert werden.")
    End
  Else
    ;Liste der Eingabegeräte abrufen
    input_ = -1
    
    While BASS_RecordGetDeviceInfo(c, info)
      AddGadgetItem(lstDeviceViewer, -1, PeekS(info\name, 31, #PB_UTF8))
      If (BASS_RecordGetInput(c, 0) And #BASS_INPUT_OFF) = 0
        SetGadgetState(lstDeviceViewer, c)
        input_ = c
      EndIf
      c + 1
    Wend
  EndIf
  
  AddWindowTimer(0, 123, 200) ;Timer zum Aktualisieren der Positionsanzeige (200ms)
  
  *recPtr = #Null
  reclen = 0
EndProcedure

;Aufnahmegerät ändern
Procedure UseInput_()
  Protected.w i
  input_ = GetGadgetState(lstDeviceViewer)
  If input_ <> -1
    For i = 0 To CountGadgetItems(lstDeviceViewer)
      BASS_RecordSetInput(i, #BASS_INPUT_OFF, -1)
    Next i
    BASS_RecordSetInput(input_, #BASS_INPUT_ON, -1)
    UpdateInputInfo()
  EndIf
EndProcedure


;-GUI

If OpenWindow(0, 0, 0, 430, 200, "Bass_Aufnahme_Test", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
  
  CreateStatusBar(0, WindowID(0))
  AddStatusBarField(50)
  StatusBarText(0, 0, "Status:", #PB_StatusBar_Raised)
  AddStatusBarField(200)
  AddStatusBarField(200)
  btnRecord = ButtonGadget(#PB_Any, 10, 10, 130, 30, "Aufnahme")
  btnPlay = ButtonGadget(#PB_Any, 150, 10, 130, 30, "Abspielen")
  btnSave = ButtonGadget(#PB_Any, 290, 10, 130, 30, "Speichern als WAV")
  txtRecDevice = TextGadget(#PB_Any, 10, 60, 110, 20, "Aufnahmegerät:")
  lstDeviceViewer = ListViewGadget(#PB_Any, 120, 60, 300, 100)
  
  DisableGadget(btnPlay, #True)
  DisableGadget(btnSave, #True)
  Form_Load()
  
  Repeat
    Event=WindowEvent()
    Select Event
      Case #PB_Event_Gadget
        Select EventGadget()
          Case btnRecord ;Record
            If (rchan = 0)
              StartRecording()
            Else
              StopRecording()
            EndIf
          Case btnSave ;Save
            WriteToDisk()
          Case btnPlay
            If BASS_ChannelPlay(chan, #True) = #Null
              Error_("Die Datei kann nicht abgespielt werden.")
            EndIf
          Case lstDeviceViewer
            UpdateInputInfo()
        EndSelect
      Case #PB_Event_Timer
        Select EventTimer()
          Case 123
            tmrRecTest_Timer()
        EndSelect
    EndSelect
  Until Event=#PB_Event_CloseWindow
EndIf

If *recPtr <> #Null
  FreeMemory(*recPtr)
EndIf
BASS_RecordFree()
BASS_Free()
End
Ich hatte in dem vorherigen Code eine Befehlzeile am Anfang geschrieben, die das ganze Abspiel-Disaster auf logischer Weise erklärt.
BASS_SetConfig(#BASS_CONFIG_UPDATEPERIOD, 0) ;Dieser Aufruf war der ganze Fehler.
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Antworten