Wiedergabe einer Streamaufnahme (mit der BASS_LIB)
Verfasst: 20.11.2020 20:45
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?
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