With a simple Start/Stop UI, it’s perfect for quick voice notes, testing, or grabbing raw mic takes for further processing.
Very nice: created with ChatGPT 5.2, it worked now out of the box—code in, compile, record.
Code: Select all
EnableExplicit
CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
CompilerError "Dieses Beispiel nutzt WinMM (waveIn*) und ist Windows-only."
CompilerEndIf
; ===== WinMM Imports =====
Import "winmm.lib"
waveInOpen(*phwi, uDeviceID.l, *pwfx, dwCallback.i, dwInstance.i, fdwOpen.l)
waveInPrepareHeader(hwi.i, *pwh, cbwh.l)
waveInUnprepareHeader(hwi.i, *pwh, cbwh.l)
waveInAddBuffer(hwi.i, *pwh, cbwh.l)
waveInStart(hwi.i)
waveInStop(hwi.i)
waveInReset(hwi.i)
waveInClose(hwi.i)
EndImport
; ===== Constants =====
#WAVE_MAPPER = -1
#CALLBACK_FUNCTION = $00030000
#MM_WIM_OPEN = $3BE
#MM_WIM_CLOSE = $3BF
#MM_WIM_DATA = $3C0
; ===== Globals =====
Global g_hWaveIn.i
Global g_isRecording.i
Global g_file.i
Global g_outPath$
Global g_bytesWritten.q
Global g_mutex.i
Global g_sampleRate.l = 44100
Global g_bits.w = 16
Global g_channels.w = 1
Global g_bufferCount.i = 4
Global g_bufferSize.i = 16384 ; bytes pro Buffer (kleiner => weniger Latenz, höher => weniger Overhead)
Global Dim g_hdr.WAVEHDR(g_bufferCount - 1)
Global Dim g_bufPtr.i(g_bufferCount - 1)
; ===== WAV Header =====
Procedure WriteWavHeader(file.i, sampleRate.l, bits.w, channels.w, dataSize.q)
Protected byteRate.l = sampleRate * channels * (bits / 8)
Protected blockAlign.w = channels * (bits / 8)
FileSeek(file, 0)
WriteString(file, "RIFF", #PB_Ascii)
WriteLong(file, 36 + dataSize)
WriteString(file, "WAVE", #PB_Ascii)
WriteString(file, "fmt ", #PB_Ascii)
WriteLong(file, 16) ; PCM
WriteWord(file, 1) ; AudioFormat = 1 (PCM)
WriteWord(file, channels)
WriteLong(file, sampleRate)
WriteLong(file, byteRate)
WriteWord(file, blockAlign)
WriteWord(file, bits)
WriteString(file, "data", #PB_Ascii)
WriteLong(file, dataSize)
EndProcedure
; ===== waveIn Callback =====
Procedure waveInProc(hWaveIn.i, uMsg.l, dwInstance.i, dwParam1.i, dwParam2.i)
If uMsg = #MM_WIM_DATA And g_isRecording
Protected *wh.WAVEHDR = dwParam1
If *wh And *wh\dwBytesRecorded > 0 And g_file
LockMutex(g_mutex)
WriteData(g_file, *wh\lpData, *wh\dwBytesRecorded)
g_bytesWritten + *wh\dwBytesRecorded
UnlockMutex(g_mutex)
EndIf
; Buffer wieder in die Queue
*wh\dwBytesRecorded = 0
waveInAddBuffer(g_hWaveIn, *wh, SizeOf(WAVEHDR))
EndIf
EndProcedure
; ===== Recording Control =====
Procedure.i SetupAndStartRecording(outPath$)
Protected fmt.WAVEFORMATEX
Protected i, mmr.l
g_outPath$ = outPath$
g_bytesWritten = 0
g_file = CreateFile(#PB_Any, g_outPath$)
If g_file = 0
MessageRequester("Fehler", "Datei kann nicht erstellt werden:" + #CRLF$ + g_outPath$)
ProcedureReturn #False
EndIf
; Placeholder Header (wird beim Stop aktualisiert)
WriteWavHeader(g_file, g_sampleRate, g_bits, g_channels, 0)
If g_mutex = 0 : g_mutex = CreateMutex() : EndIf
fmt\wFormatTag = 1
fmt\nChannels = g_channels
fmt\nSamplesPerSec = g_sampleRate
fmt\wBitsPerSample = g_bits
fmt\nBlockAlign = g_channels * (g_bits / 8)
fmt\nAvgBytesPerSec = fmt\nSamplesPerSec * fmt\nBlockAlign
fmt\cbSize = 0
mmr = waveInOpen(@g_hWaveIn, #WAVE_MAPPER, @fmt, @waveInProc(), 0, #CALLBACK_FUNCTION)
If mmr <> 0 Or g_hWaveIn = 0
CloseFile(g_file) : g_file = 0
MessageRequester("Fehler", "waveInOpen fehlgeschlagen (MMRESULT=" + Str(mmr) + ")")
ProcedureReturn #False
EndIf
; Buffers vorbereiten und hinzufügen
For i = 0 To g_bufferCount - 1
g_bufPtr(i) = AllocateMemory(g_bufferSize)
If g_bufPtr(i) = 0
MessageRequester("Fehler", "AllocateMemory fehlgeschlagen.")
ProcedureReturn #False
EndIf
g_hdr(i)\lpData = g_bufPtr(i)
g_hdr(i)\dwBufferLength = g_bufferSize
g_hdr(i)\dwBytesRecorded = 0
g_hdr(i)\dwFlags = 0
g_hdr(i)\dwLoops = 0
waveInPrepareHeader(g_hWaveIn, @g_hdr(i), SizeOf(WAVEHDR))
waveInAddBuffer(g_hWaveIn, @g_hdr(i), SizeOf(WAVEHDR))
Next
g_isRecording = #True
waveInStart(g_hWaveIn)
ProcedureReturn #True
EndProcedure
Procedure StopRecording()
Protected i
If g_isRecording = 0
ProcedureReturn
EndIf
g_isRecording = #False
If g_hWaveIn
waveInStop(g_hWaveIn)
waveInReset(g_hWaveIn)
For i = 0 To g_bufferCount - 1
waveInUnprepareHeader(g_hWaveIn, @g_hdr(i), SizeOf(WAVEHDR))
If g_bufPtr(i)
FreeMemory(g_bufPtr(i))
g_bufPtr(i) = 0
EndIf
Next
waveInClose(g_hWaveIn)
g_hWaveIn = 0
EndIf
If g_file
; Header mit korrekter Datenlänge überschreiben
WriteWavHeader(g_file, g_sampleRate, g_bits, g_channels, g_bytesWritten)
CloseFile(g_file)
g_file = 0
EndIf
EndProcedure
; ===== Simple GUI =====
Enumeration
#Win
#BtnStart
#BtnStop
#TxtStatus
EndEnumeration
Procedure SetStatus(text$)
SetGadgetText(#TxtStatus, text$)
EndProcedure
OpenWindow(#Win, 0, 0, 520, 180, "Mikrofon aufnehmen -> WAV", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ButtonGadget(#BtnStart, 20, 20, 150, 35, "Start")
ButtonGadget(#BtnStop, 190, 20, 150, 35, "Stop")
TextGadget(#TxtStatus, 20, 75, 480, 80, "Bereit. Klick auf Start.", #PB_Text_Border)
DisableGadget(#BtnStop, #True)
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Break
Case #PB_Event_Gadget
Select EventGadget()
Case #BtnStart
Define save$ = SaveFileRequester("Speichern als ...", "aufnahme.wav", "WAV (*.wav)|*.wav", 0)
If save$
If SetupAndStartRecording(save$)
SetStatus("Aufnahme läuft..." + #CRLF$ + save$)
DisableGadget(#BtnStart, #True)
DisableGadget(#BtnStop, #False)
EndIf
EndIf
Case #BtnStop
StopRecording()
SetStatus("Gespeichert:" + #CRLF$ + g_outPath$ + #CRLF$ + "Bytes Audio: " + Str(g_bytesWritten))
DisableGadget(#BtnStart, #False)
DisableGadget(#BtnStop, #True)
EndSelect
EndSelect
ForEver
; Sauber beenden, falls Fenster während Aufnahme geschlossen wird
StopRecording()
End

