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