PB 6.03b2 windows x64 c backend
Prerequisites :
a gcc install eg mingw64 with Environment variables set so gcc can find the includes folder for typedef resolutions
source and dll windows x64
https://atomicwebserver.com/SoundRecorder.zip
chipmunk speed test
https://atomicwebserver.com/tempwav.wav
supported format
#ma_format_u8
#ma_format_s16
#ma_format_s24
#ma_format_s32
#ma_format_f32
modes
#ma_device_type_capture ;records from an input microphone or stereo mix
#ma_device_type_duplex ;records from input sources mic or stereo mix and plays back
#ma_device_type_loopback ;windows only captures what's currently playing
Code: Select all
;Test of miniaudio records to wav file and playbacks live
;note it buffers to memory, then saves to wave at StopSoundRecorder
;Records from default input to default output
;*This requires that you have a full gcc toolchain eg mingw64 windows with env paths set to find #include <stdio.h> ...
;add your gcc include paths to your PATH environment variable
;eg
;C:\mingw64\lib\gcc\x86_64-w64-mingw32\13.2.0\include\
;C:\mingw64\x86_64-w64-mingw32\include
;Download the header and adjust the #include path to suit
;https://atomicwebserver.com/miniaudio_11_23.h
HeaderSection
#include "E:\andrews\pbstuff\miniaudio\tminiaudio_11_23.h";
EndHeaderSection
InitSound()
Global gRecording
#ma_format_u8 = 1
#ma_format_s16 = 2
#ma_format_s24 = 3
#ma_format_s32 = 4
#ma_format_f32 = 5
Structure RIFFStructure
Riff.a[4]
Length.l
Wave.a[4]
EndStructure
Structure fmtStructure
fmt.a[4]
Length.l
Format.u
Channels.u
SampleRate.l
BytesPerSecond.l
BlockAlign.u
BitsPerSample.u
EndStructure
Structure dataStructure
Signature.a[4]
Length.l
EndStructure
Global *WAVBuffer
Procedure.i ResetSoundRecorder(device)
Protected.i Result, HeaderSize, DataSize
Protected *WAVBuffer, *RiffPtr.RIFFStructure, *fmtPtr.fmtStructure, *dataPtr.dataStructure, *audioPtr.word
If *WAVBuffer
FreeMemory(*WAVBuffer)
EndIf
HeaderSize = SizeOf(RIFFStructure)
HeaderSize + SizeOf(fmtStructure)
HeaderSize + SizeOf(dataStructure)
*WAVBuffer = AllocateMemory(HeaderSize)
If *WAVBuffer
ProcedureReturn *WAVBuffer
EndIf
EndProcedure
Procedure AppendSoundRecorder(*data,len)
If *WAVBuffer
pos = MemorySize(*WAVBuffer)
*WAVBuffer = ReAllocateMemory(*WAVBuffer,pos+len)
CopyMemory(*data,*WAVBuffer+pos,len)
ProcedureReturn *WAVBuffer
EndIf
EndProcedure
ProcedureDLL StartSoundRecorder(*device)
gRecording = 1
!ma_device_start(p_device);
EndProcedure
ProcedureDLL StopSoundRecorder(*device,file.s="",samplerate=44100)
Protected.i Result, HeaderSize, DataSize,channels,format,depth
Protected *RiffPtr.RIFFStructure, *fmtPtr.fmtStructure, *dataPtr.dataStructure, *audioPtr.word
gRecording = 0
!ma_device_stop(p_device);
!ma_device *tpdevice = p_device;
If *WAVBuffer
If file <> ""
!v_format = tpdevice->capture.format;
!v_channels = tpdevice->capture.channels;
HeaderSize = SizeOf(RIFFStructure)
HeaderSize + SizeOf(fmtStructure)
HeaderSize + SizeOf(dataStructure)
DataSize = MemorySize(*WAVBuffer) - (HeaderSize)
*RiffPtr = *WAVBuffer
PokeS(@*RiffPtr\Riff, "RIFF", 4, #PB_Ascii|#PB_String_NoZero)
*RiffPtr\Length = HeaderSize + DataSize - 8
PokeS(@*RiffPtr\Wave, "WAVE", 4, #PB_Ascii|#PB_String_NoZero)
*fmtPtr = *WAVBuffer + SizeOf(RIFFStructure)
PokeS(@*fmtPtr\fmt, "fmt ", 4, #PB_Ascii|#PB_String_NoZero)
*fmtPtr\Length = SizeOf(fmtStructure) - 8
If format = #ma_format_f32
*fmtPtr\Format = 3
depth = 32
Else
*fmtPtr\Format = 1
Select format
Case #ma_format_u8
depth = 8
Case #ma_format_s16
depth = 16
Case #ma_format_s24
depth = 24
Case #ma_format_s32
depth = 32
EndSelect
EndIf
*fmtPtr\Channels = channels
*fmtPtr\SampleRate = samplerate
*fmtPtr\BitsPerSample = depth
*fmtPtr\BlockAlign = (depth>>3) * 2
*fmtPtr\BytesPerSecond = samplerate * (depth>>3)
*dataPtr = *WAVBuffer + SizeOf(RIFFStructure) + SizeOf(fmtStructure)
PokeS(@*dataPtr\Signature, "data", 4, #PB_Ascii|#PB_String_NoZero)
*dataPtr\Length = DataSize
fn = CreateFile(#PB_Any,file)
If fn
x = WriteData(fn,*WAVBuffer,MemorySize(*WAVBuffer))
PrintN(Str(x) + " bytes written")
CloseFile(fn)
EndIf
EndIf
FreeMemory(*WAVBuffer)
EndIf
ProcedureReturn #True
EndProcedure
ProcedureC data_callback(*Device,*Output,*Input,frameCount)
Protected amount
If frameCount
!ma_device *pdevice = p_device;
!v_amount = v_framecount * ma_get_bytes_per_frame(pdevice->capture.format, pdevice->capture.channels);
If amount <> 0
If (*input And *output)
CopyMemory(*Input,*Output,amount)
EndIf
If (*input And gRecording)
If *WAVBuffer = 0
*WAVBuffer = ResetSoundRecorder(*device)
EndIf
If *WAVBuffer
*WAVBuffer = AppendSoundRecorder(*input,amount)
EndIf
EndIf
EndIf
EndIf
EndProcedure
Structure nativeDataFormats
format.i; /* Sample format. If set to 0, all sample formats are supported. */
channels.l; /* If set to 0, all channels are supported. */
sampleRate.l; /* If set to 0, all sample rates are supported. */
flags.l; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */
EndStructure
Structure devices
*device
id.i
isDefault.i
name.s
nativeDataFormatCount.l
format.nativeDataFormats[64]
EndStructure
Global temp.i,*ptemp,pcb = @data_callback() ;need to reference the function or PB won't include it
Global Dim CaptureDevices.devices(0)
Global Dim PlayBackDevices.devices(0)
ProcedureDLL EnumSoundDevices()
Protected ind,temp,ta
!ma_context context;
!ma_device_info *pCaptureInfos;// = (ma_device_info *) malloc(sizeof(ma_device_info));
!ma_device_info *pPlaybackInfos;// = (ma_device_info *) malloc(sizeof(ma_device_info));
!if (ma_context_init(NULL, 0, NULL, &context) == MA_SUCCESS) {
!ma_uint32 captureCount;
!ma_uint32 playCount;
!if (ma_context_get_devices(&context,&pPlaybackInfos,&playCount,&pCaptureInfos,&captureCount) == MA_SUCCESS) {
!v_temp = captureCount-1;
ReDim CaptureDevices.devices(temp)
!v_temp = playCount-1;
ReDim PlayBackDevices.devices(temp)
!for (ma_uint32 iDevice = 0; iDevice < captureCount; iDevice += 1) {
!v_ind = iDevice;
!v_temp = &pCaptureInfos[iDevice];
CaptureDevices(ind)\device = temp
!v_temp = &pCaptureInfos[iDevice].id;
CaptureDevices(ind)\id = temp
!v_temp = pCaptureInfos[iDevice].name;
CaptureDevices(ind)\name = PeekS(temp,-1,#PB_Ascii)
!v_temp = pCaptureInfos[iDevice].isDefault;
CaptureDevices(ind)\isDefault = temp
!v_temp = pCaptureInfos[iDevice].nativeDataFormatCount;
CaptureDevices(ind)\nativeDataFormatCount=temp
!for (ma_uint32 a = 0; a < pCaptureInfos[iDevice].nativeDataFormatCount; a += 1) {
!v_ta = a;
!v_temp = pCaptureInfos[iDevice].nativeDataFormats[a].format;
CaptureDevices(ind)\format[ta]\format = temp
!v_temp = pCaptureInfos[iDevice].nativeDataFormats[a].channels;
CaptureDevices(ind)\format[ta]\channels = temp
!v_temp = pCaptureInfos[iDevice].nativeDataFormats[a].sampleRate;
CaptureDevices(ind)\format[ta]\sampleRate = temp
!}
!}
!for (ma_uint32 iDevice = 0; iDevice < playCount; iDevice += 1) {
!v_ind = iDevice;
!v_temp = &pPlaybackInfos[iDevice];
PlayBackDevices(ind)\device = temp
!v_temp = &pPlaybackInfos[iDevice].id;
PlayBackDevices(ind)\id = temp
!v_temp = pPlaybackInfos[iDevice].name;
PlayBackDevices(ind)\name = PeekS(temp,-1,#PB_Ascii)
!v_temp = pPlaybackInfos[iDevice].isDefault;
PlayBackDevices(ind)\isDefault = temp
!v_temp = pPlaybackInfos[iDevice].nativeDataFormatCount;
PlayBackDevices(ind)\nativeDataFormatCount=temp
!for (ma_uint32 a = 0; a < pPlaybackInfos[iDevice].nativeDataFormatCount; a += 1) {
!v_ta = a;
!v_temp = pPlaybackInfos[iDevice].nativeDataFormats[a].format;
PlayBackDevices(ind)\format[ta]\format = temp
!v_temp = pPlaybackInfos[iDevice].nativeDataFormats[a].channels;
PlayBackDevices(ind)\format[ta]\channels = temp
!v_temp = pPlaybackInfos[iDevice].nativeDataFormats[a].sampleRate;
PlayBackDevices(ind)\format[ta]\sampleRate = temp
!}
!}
!}
!}
EndProcedure
#ma_device_type_capture =2 ;records from an input microphone or stereo mix
#ma_device_type_duplex = 3 ;records from input sources mic or stereo mix and plays back
#ma_device_type_loopback = 4 ;windows only captures what's currently playing
;if input is 0 it will record from the default device
ProcedureDLL InitSoundRecorder(input,format=#ma_format_s16,sampleRate=44100,mode=#ma_device_type_capture)
Protected dev,result
!ma_device_config deviceConfig;
!ma_device *device = (ma_device*) malloc(sizeof(ma_device));
Select mode
Case #ma_device_type_capture
!deviceConfig = ma_device_config_init(ma_device_type_capture);
!deviceConfig.sampleRate = v_samplerate;
!deviceConfig.capture.pDeviceID = v_input; //NULL;
!deviceConfig.capture.format = v_format;
!deviceConfig.capture.channels = 2;
!deviceConfig.capture.shareMode = ma_share_mode_shared;
!deviceConfig.dataCallback = &f_data_callback;
Case #ma_device_type_loopback
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
!deviceConfig = ma_device_config_init(ma_device_type_loopback);
!deviceConfig.sampleRate = v_samplerate;
!deviceConfig.capture.pDeviceID = NULL;
!deviceConfig.capture.format = v_format;
!deviceConfig.capture.channels = 2;
!deviceConfig.capture.shareMode = ma_share_mode_shared;
!deviceConfig.dataCallback = &f_data_callback;
CompilerElse
CompilerError "loopback only supported on windows"
CompilerEndIf
Case #ma_device_type_duplex
!deviceConfig = ma_device_config_init(ma_device_type_duplex);
!deviceConfig.sampleRate = v_samplerate;
!deviceConfig.capture.pDeviceID = v_input; //NULL;
!deviceConfig.capture.format = v_format;
!deviceConfig.capture.channels = 2;
!deviceConfig.capture.shareMode = ma_share_mode_shared;
!deviceConfig.playback.pDeviceID = NULL;
!deviceConfig.playback.format = v_format; //ma_format_s16;
!deviceConfig.playback.channels = 2;
!deviceConfig.dataCallback = &f_data_callback;
EndSelect
!v_result = ma_device_init(NULL, &deviceConfig, &*device);
If result <> 0
MessageRequester("error","failed to config")
ProcedureReturn 0
EndIf
!v_dev = device;
ProcedureReturn dev
EndProcedure
ProcedureDLL FreeSoundRecorder(recorder)
!ma_device_uninit(v_recorder);
!free(v_recorder);
EndProcedure
CompilerIf #PB_Compiler_IsMainFile ;-test
OpenConsole()
EnumSoundDevices()
For a = 0 To ArraySize(CaptureDevices())
Debug CaptureDevices(a)\name
If CaptureDevices(a)\isDefault
Debug "Is default Capture device index = " + Str(a)
EndIf
Next
PrintN("Init Recorder")
;Global recorder = InitSoundRecorder(CaptureDevices(1)\id,#ma_format_s16,44100) ;record on selected input
Global recorder = InitSoundRecorder(0,#ma_format_s16,44100,#ma_device_type_duplex) ;record on default input and playback
;Global recorder = InitSoundRecorder(0,#ma_format_s16,44100,#ma_device_type_loopback) ;record on whats being currently played on speaker
PrintN("Press enter tostart recording")
StartSoundRecorder(recorder)
PrintN("Recording press enter to stop")
Input();
StopSoundRecorder(recorder,GetTemporaryDirectory()+"tempwav.wav",44100) ;save at chipmunk speed normal speed 44100
snd = LoadSound(-1,GetTemporaryDirectory()+"tempwav.wav")
If snd
PlaySound(snd)
EndIf
PrintN("Stopped press enter to end")
Input();
FreeSoundRecorder(recorder)
PrintN("free")
CloseConsole()
CompilerEndIf