Page 1 of 1

Using Inlinec PB6.30

Posted: Wed Sep 17, 2025 10:23 pm
by idle
Examples of using InlineC for PB6.30b2 windows x64

Examples

Raylib 4 ; juliaset and screen

SoundRecorder using inbuilt miniaudio library

*note you will need to edit the #include path to the absolute paths

To use InclineC in Purebasic you either need to have a full c install like mingw64 or you can extract the include.zip to
"C:\PureBasic\Compilers\gcc\"

Next Edit your environment variables by adding a CPATH variable and set it to the include folder and restart the IDE

Alternatively if you want to install mingw64
http://winlibs.com/
you probably want a UCRT version for windows 10 and above and compatibility with pb
edit your environment variables adding CPATH and add two folders from you minwgW64 install eg
C:\mingw64\include
C:\mingw64\lib\gcc\x86_64-w64-mingw32\13.2.0\include

The repo
https://github.com/idle-PB/InlineC


For Linux you probably don't need to do anything as you should already have gcc on system with paths set.
For macOS no idea?

Re: Using Inlinec PB6.30

Posted: Thu Sep 18, 2025 7:03 pm
by Quin
Wow, this is really neat. Thanks for sharing, Idle!

Re: Using Inlinec PB6.30

Posted: Fri Sep 19, 2025 8:15 am
by pjay
This is a really user-friendly & much needed educational resource Idle.

Thanks for making it easier to setup for those who needed it (me for sure :!: :lol: ).

Re: Using Inlinec PB6.30

Posted: Fri Sep 19, 2025 10:20 am
by idle
pjay wrote: Fri Sep 19, 2025 8:15 am This is a really user-friendly & much needed educational resource Idle.

Thanks for making it easier to setup for those who needed it (me for sure :!: :lol: ).
It can still be a bit of a headache to get something working but it certainly makes it easier to plug in a c lib

Re: Using Inlinec PB6.30

Posted: Fri Sep 19, 2025 12:58 pm
by acreis
Sorry for dummy question

Code: Select all

HeaderSection 

#define GLSL_VERSION 330
#define _WIN32 
#define USE_LIBTYPE_SHARED 

 
#include "C:\InlineC\raylib-4.0.0_win64_msvc16\include\raylib.h";

EndHeaderSection 

 
ImportC "C:\InlineC\raylib-4.0.0_win64_msvc16\raylibdll.lib"  


"HeaderSection" causes syntax error.

Re: Using Inlinec PB6.30

Posted: Fri Sep 19, 2025 8:47 pm
by idle
Make sure the compiler is 6.30b2 x64 c backend.

Re: Using Inlinec PB6.30

Posted: Sat Sep 20, 2025 10:46 am
by acreis
Thanks Idle! My bad!


But inside Headersection ...

return keeps changing to Return
static keeps changing to Static

Any idea?

Re: Using Inlinec PB6.30

Posted: Sat Sep 20, 2025 8:59 pm
by idle
acreis wrote: Sat Sep 20, 2025 10:46 am Thanks Idle! My bad!


But inside Headersection ...

return keeps changing to Return
static keeps changing to Static

Any idea?
That sounds like the syntax highlighter in the ide are you using an older ide?

Re: Using Inlinec PB6.30

Posted: Sat Sep 20, 2025 10:24 pm
by idle
updated soundrecorder adding a monitor function with callbacks and effects chain

eg

Code: Select all

Procedure fuzzCallback(*input,numframes,timeSeconds.d)
    
    Protected *pfIn.float = *input 
    Protected i
                 
    If *input <> 0
      While i < numframes
        *pfIn\f = FUZZ(*pfIn\f)   
        *pfin + 4
        i + 4
      Wend 
    EndIf 
    
  EndProcedure 
  
  Procedure RMSSignal(*input,numframes,timeseconds.d) 
    Protected rms.f,db.f, *pfIn.float = *input 
    If *input <> 0
      While i < numframes
        rms + (*pfIn\f * *pfIn\f)   
        *pfin + 4
        i + 4
      Wend 
    EndIf 
    
    DB = 20 * Log10(Sqr(rms/numframes))
    
    PrintN("Peek RMS DB " + StrF(DB,2))  
    
  EndProcedure  

soundRec\Monitor(@fuzzCallback()) ;distort with cubic amp
soundRec\Monitor(@RMSSignal())    ;adds to call back chain note this isn't ideal as it's blocking   

source

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\miniaudio_11_23.h"; 

EndHeaderSection 

ImportC "miniaudio.lib" : EndImport 

#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

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   

Structure cSoundRec 
  *EnumDevices
  *NextDevice
  *IsDefault
  *SetFile
  *Config
  *Monitor
  *Start
  *Stop
  *Free
EndStructure 

Prototype EffectsCB(*input,numframes,time)

Structure SoundRec 
  *vt.cSoundRec
  device.i
  *WAVBuffer
  bRecord.i 
  *pbc 
  file.s 
  List Effects.i()
  Map CaptureDevices.devices(0)  
  Map PlayBackDevices.devices(0) 
EndStructure   

Procedure.i ResetSoundRecorder(*sound.SoundRec) 
  
  Protected.i Result, HeaderSize, DataSize
  Protected *RiffPtr.RIFFStructure, *fmtPtr.fmtStructure, *dataPtr.dataStructure, *audioPtr.word
    
  If *sound\WAVBuffer <> 0 
    FreeMemory(*sound\WAVBuffer) 
  EndIf   
  
  HeaderSize = SizeOf(RIFFStructure)
  HeaderSize + SizeOf(fmtStructure)
  HeaderSize + SizeOf(dataStructure)
  
  *sound\WAVBuffer = AllocateMemory(HeaderSize)
  
  If *sound\WAVBuffer 
    ProcedureReturn *sound\WAVBuffer 
  EndIf 
EndProcedure    

Procedure AppendSoundRecorder(*sound.SoundRec,*Data,len) 
  If *sound\WAVBuffer 
    pos = MemorySize(*sound\WAVBuffer) 
    *sound\WAVBuffer = ReAllocateMemory(*sound\WAVBuffer,pos+len) 
    CopyMemory(*data,*sound\WAVBuffer+pos,len) 
    ProcedureReturn *sound\WAVBuffer 
  EndIf 
EndProcedure 

Procedure StartSoundRecorder(SoundRec) 
  Protected *sound.SoundRec = SoundRec
  Protected *device = *sound\device 
  *sound\bRecord = 1  
  !ma_device_start(p_device);
  
EndProcedure  

Procedure MonitorSoundRecorder(SoundRec,*EffectsCB=0) 
  Protected *sound.SoundRec = SoundRec
  Protected *device = *sound\device 
  If *EffectsCB <> 0 
    AddElement(*sound\Effects()) 
    *sound\Effects() = *EffectsCB 
  EndIf 
  !ma_device_start(p_device);
  
EndProcedure 


Procedure StopSoundRecorder(SoundRec,samplerate=44100) 
  
  Protected.i Result, HeaderSize, DataSize,channels,format,depth  
  Protected *RiffPtr.RIFFStructure, *fmtPtr.fmtStructure, *dataPtr.dataStructure, *audioPtr.word
  Protected *sound.SoundRec = soundRec 
  Protected *device = *sound\device 
  
  *sound\bRecord = 0 
  !ma_device_stop(p_device);
  
  !ma_device *tpdevice = p_device; 
      
  If *sound\WAVBuffer 
    
    If *sound\file <> ""
      
      !v_format = tpdevice->capture.format;
      !v_channels = tpdevice->capture.channels; 
      
      HeaderSize = SizeOf(RIFFStructure)
      HeaderSize + SizeOf(fmtStructure)
      HeaderSize + SizeOf(dataStructure)
      
      DataSize = MemorySize(*sound\WAVBuffer) - (HeaderSize)
      
      *RiffPtr = *sound\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 = *sound\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 = *sound\WAVBuffer + SizeOf(RIFFStructure) + SizeOf(fmtStructure)
      PokeS(@*dataPtr\Signature, "data", 4, #PB_Ascii|#PB_String_NoZero)
      *dataPtr\Length = DataSize
      
      fn = CreateFile(#PB_Any,*sound\file) 
      If fn 
        x = WriteData(fn,*sound\WAVBuffer,MemorySize(*sound\WAVBuffer)) 
        CloseFile(fn) 
      EndIf
      
    EndIf 
    
    FreeMemory(*sound\WAVBuffer)
    
  EndIf  
  
  ProcedureReturn x
  
EndProcedure

ProcedureC data_callback(*Device,*Output,*Input,frameCount)
  Protected amount,format,channels,*sound.SoundRec 
  Protected *fx.EffectsCB 
  If frameCount  
    !ma_device *pdevice = p_device; 
    !v_amount = v_framecount * ma_get_bytes_per_frame(pdevice->capture.format, pdevice->capture.channels);
    !p_sound = pdevice->pUserData; 
    !v_format = pdevice->capture.format; 
    !v_channels = pdevice->capture.channels;
    If amount <> 0  
      If (*input And *output)  
        If amount > 4 
        ForEach *sound\Effects() 
          *fx = *sound\Effects() 
          *fx(*input,frameCount,ElapsedMilliseconds()/1000.0)
        Next  
        EndIf 
        CopyMemory(*input,*Output,amount)
      EndIf 
      If (*input And *sound\bRecord)
        If *sound\WAVBuffer = 0 
          *sound\WAVBuffer = ResetSoundRecorder(*sound)
        EndIf   
        If *sound\WAVBuffer 
          *sound\WAVBuffer = AppendSoundRecorder(*sound,*input,amount)
        EndIf 
      EndIf   
    EndIf
  EndIf 
EndProcedure 

Procedure EnumSoundDevices(*sound.SoundRec) 
  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) {
        
     !for (ma_uint32 iDevice = 0; iDevice < captureCount; iDevice += 1) {
       !v_ind = iDevice;  
       !v_temp = pCaptureInfos[iDevice].name;
       AddMapElement(*sound\CaptureDevices(),PeekS(temp,-1,#PB_Ascii))       
       !v_temp = &pCaptureInfos[iDevice];
       *sound\CaptureDevices()\device = temp 
       !v_temp = &pCaptureInfos[iDevice].id; 
       *sound\CaptureDevices()\id = temp 
       !v_temp = pCaptureInfos[iDevice].name;
       *sound\CaptureDevices()\name = PeekS(temp,-1,#PB_Ascii)
       !v_temp = pCaptureInfos[iDevice].isDefault;
       *sound\CaptureDevices()\isDefault = temp 
       !v_temp = pCaptureInfos[iDevice].nativeDataFormatCount; 
       *sound\CaptureDevices()\nativeDataFormatCount=temp 
       
       !for (ma_uint32 a = 0; a < pCaptureInfos[iDevice].nativeDataFormatCount; a += 1) { 
          !v_ta = a;
          !v_temp = pCaptureInfos[iDevice].nativeDataFormats[a].format;  
          *sound\CaptureDevices()\format[ta]\format = temp 
          !v_temp = pCaptureInfos[iDevice].nativeDataFormats[a].channels;  
          *sound\CaptureDevices()\format[ta]\channels = temp 
          !v_temp = pCaptureInfos[iDevice].nativeDataFormats[a].sampleRate;  
          *sound\CaptureDevices()\format[ta]\sampleRate = temp 
          !}
          
              
     !}
     !for (ma_uint32 iDevice = 0; iDevice < playCount; iDevice += 1) {
       !v_ind = iDevice;  
        !v_temp = pPlaybackInfos[iDevice].name; 
       AddMapElement(*sound\PlayBackDevices(),PeekS(temp,-1,#PB_Ascii))
       
       !v_temp = &pPlaybackInfos[iDevice];
       *sound\PlayBackDevices()\device = temp 
       !v_temp = &pPlaybackInfos[iDevice].id;
       *sound\PlayBackDevices()\id = temp 
       !v_temp = pPlaybackInfos[iDevice].name; 
       *sound\PlayBackDevices()\name = PeekS(temp,-1,#PB_Ascii)
        !v_temp = pPlaybackInfos[iDevice].isDefault;
       *sound\PlayBackDevices()\isDefault = temp 
       
       !v_temp = pPlaybackInfos[iDevice].nativeDataFormatCount; 
       *sound\PlayBackDevices()\nativeDataFormatCount=temp 
       
       !for (ma_uint32 a = 0; a < pPlaybackInfos[iDevice].nativeDataFormatCount; a += 1) { 
          !v_ta = a;
          !v_temp = pPlaybackInfos[iDevice].nativeDataFormats[a].format;  
          *sound\PlayBackDevices()\format[ta]\format = temp 
          !v_temp = pPlaybackInfos[iDevice].nativeDataFormats[a].channels;  
          *sound\PlayBackDevices()\format[ta]\channels = temp 
          !v_temp = pPlaybackInfos[iDevice].nativeDataFormats[a].sampleRate;  
          *sound\PlayBackDevices()\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  

Procedure ConfigSoundRecorder(soundRec,input.s,format=#ma_format_s16,sampleRate=44100,mode=#ma_device_type_capture) 
  Protected dev,result,iinput 
  Protected *sound.SoundRec = soundRec  
  
  If FindMapElement(*sound\CaptureDevices(),input) 
    iinput = *sound\CaptureDevices()\id 
  EndIf 
  
  !ma_device_config deviceConfig;
  !ma_device *device = (ma_device*) malloc(sizeof(ma_device));
  !device->pUserData = p_sound;

Select mode 
   Case #ma_device_type_capture 
     !deviceConfig = ma_device_config_init(ma_device_type_capture);
     !deviceConfig.sampleRate = v_samplerate;
     !deviceConfig.capture.pDeviceID  = v_iinput;  //NULL;
     !deviceConfig.capture.format     = v_format;
     !deviceConfig.capture.channels   = 2;
     !deviceConfig.capture.shareMode  = ma_share_mode_shared;
     !deviceConfig.dataCallback       = &f_data_callback; 
     !deviceConfig.pUserData = p_sound;
   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;
     !deviceConfig.pUserData = p_sound;
   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_iinput;  //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; 
     !deviceConfig.pUserData = p_sound;
     
EndSelect       

!v_result = ma_device_init(NULL, &deviceConfig, &*device);

If result <> 0
  MessageRequester("error","failed to config")
  ProcedureReturn 0
EndIf  

!v_dev = device; 

*sound\device = dev 

ProcedureReturn #True  

EndProcedure 

Procedure FreeSoundRecorder(sound) 
  Protected *sound.SoundRec = sound 
  recorder = *sound\device
  !ma_device_uninit(v_recorder);  
  !free(v_recorder); 
  FreeStructure(*sound) 
EndProcedure  

Procedure EnumSoundCaptureDevices(sound)
  Protected *sound.SoundRec = sound 
  ResetMap(*sound\CaptureDevices()) 
EndProcedure  

Procedure NextSoundCaptureDevice(sound) 
  Protected *sound.SoundRec = sound 
  If NextMapElement(*sound\CaptureDevices())
    ProcedureReturn @*sound\CaptureDevices()\name  
  EndIf   
EndProcedure   

Procedure IsDefault(sound,device.s) 
  Protected *sound.SoundRec = sound  
  If FindMapElement(*sound\CaptureDevices(),device) 
    ProcedureReturn *sound\CaptureDevices()\isDefault 
  EndIf  
EndProcedure   

Procedure SetRecordingFile(sound,filename.s) 
  Protected *sound.SoundRec = sound   
  *sound\file = filename 
EndProcedure  

ProcedureDLL InitSoundRecorder() 
  *sound.SoundRec = AllocateStructure(SoundRec)  
  *sound\vt = AllocateMemory(SizeOf(cSoundRec))
  *sound\vt\Config = @ConfigSoundRecorder()
  *sound\vt\Free = @FreeSoundRecorder() 
  *sound\vt\EnumDevices = @EnumSoundCaptureDevices() 
  *sound\vt\NextDevice = @NextSoundCaptureDevice() 
  *sound\vt\IsDefault = @IsDefault() 
  *sound\vt\SetFile = @SetRecordingFile()
  *sound\vt\Monitor = @MonitorSoundRecorder() 
  *sound\vt\Start = @StartSoundRecorder()
  *sound\vt\Stop = @StopSoundRecorder() 
  *sound\pbc = @data_callback() 
  EnumSoundDevices(*sound)  
  ProcedureReturn *sound 
EndProcedure   

#TestSound = 1

CompilerIf #TestSound 
  
  Procedure.f CubicAmplifier(input.f)
    Protected  output.f, temp.f
    If input < 0.0
       temp = input + 1.0
       output = (temp * temp * temp) - 1.0
    Else
       temp = input - 1.0
       output = (temp * temp * temp) + 1.0
    EndIf    
          
    ProcedureReturn output;
  EndProcedure 
  
  Macro FUZZ(x)
    CubicAmplifier(CubicAmplifier(CubicAmplifier(x)))
  EndMacro
  
  Procedure fuzzCallback(*input,numframes,timeSeconds.d)
    
    Protected *pfIn.float = *input 
    Protected i
                 
    If *input <> 0
      While i < numframes
        *pfIn\f = FUZZ(*pfIn\f)   
        *pfin + 4
        i + 4
      Wend 
    EndIf 
    
  EndProcedure 
  
  Procedure RMSSignal(*input,numframes,timeseconds.d) 
    Protected rms.f,db.f, *pfIn.float = *input 
    If *input <> 0
      While i < numframes
        rms + (*pfIn\f * *pfIn\f)   
        *pfin + 4
        i + 4
      Wend 
    EndIf 
    
    DB = 20 * Log10(Sqr(rms/numframes))
    
    PrintN("Peek RMS DB " + StrF(DB,2))  
    
  EndProcedure  
  
    
InitSound()

Interface iSoundRecorder 
  EnumDevices()
  NextDevice() 
  IsDefault(device.s)
  SetFile(filename.s)
  Config(device.s,format=#ma_format_s16,sampleRate=44100,mode=#ma_device_type_capture) 
  Monitor(*EffectsCB=0)
  Start()
  Stop(samplerate=44100)
  Free()
EndInterface   

OpenConsole()   

filename.s = GetTemporaryDirectory()+"tempwav.wav"

soundRec.iSoundRecorder = InitSoundRecorder() 

soundRec\SetFile(filename)

soundRec\EnumDevices()
Repeat
  device.s = PeekS(soundRec\NextDevice())   
  If soundRec\IsDefault(device)
    Debug "Is default Capture device index = " + device
    Break  
  EndIf 
Until device = ""  

PrintN("Init Recorder") 

soundRec\Config("",#ma_format_f32,44100,#ma_device_type_duplex)       ;record on default input and playback 
;soundRec\Config(device,#ma_format_s16,44100)                          ;record on selected input 
;soundRec\Config(device,#ma_format_s16,44100,#ma_device_type_loopback)  ;record on whats being currently played on speaker

PrintN("Monitoring Press Enter  to record") 

soundRec\Monitor(@fuzzCallback()) ;distort with cubic amp
soundRec\Monitor(@RMSSignal())    ;adds to call back chain note this isn't ideal as it's blocking  

Input()  

PrintN("Press enter tostart recording") 


soundRec\Start()

PrintN("Recording press enter to stop") 
Input();

soundRec\Stop()

snd = LoadSound(-1,filename) 
If snd 
  PlaySound(snd) 
EndIf   

PrintN("Stopped press enter to end") 
Input();

soundRec\Free()

PrintN("free") 

Input();

CloseConsole()
  
CompilerEndIf