Embedding C code in PB source need to be described

Found an issue in the documentation ? Please report it here !

Moderator: Documentation Editors

User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2056
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Embedding C code in PB source need to be described

Post by Andre »

Something for Fred (or another 'Pro' ;-)):

Similar to InlineASM there is needed a new chapter about InlineC.... see for example this thread with questions/discussions: viewtopic.php?t=81776
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
juergenkulow
Enthusiast
Enthusiast
Posts: 544
Joined: Wed Sep 25, 2019 10:18 am

Re: Embedding C code in PB source need to be described

Post by juergenkulow »

Example of a codeline of the C programming language in PureBasic:

Code: Select all

; Sample for a Codeline C in PureBasic. 
ProcedureCDLL HelloStar(q.q)
  *p=AllocateMemory(100)
  ! sprintf(p_p,"Hello visible universe on star %.0lf.",v_q*108420.0);
  ProcedureReturn *p
EndProcedure

*p=HelloStar(Random(9223372036854775807))
Debug PeekS(*p,-1,#PB_Ascii)
FreeMemory(*p)
; Hello visible universe on star 889966170588799872008192.
Is the example suitable for documentation?
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Embedding C code in PB source need to be described

Post by mk-soft »

No,
its memory leak

fixed

Code: Select all

; Sample for a Codeline C in PureBasic. 

Procedure InitDLL()
  Global *retval = AllocateMemory(1024)
EndProcedure : InitDLL()

ProcedureCDLL HelloStar(q.q) ; Retval static string 
  ! sprintf(gp_retval,"Hello visible universe on star %.0lf.", v_q * 108420.0);
  ProcedureReturn *retval
EndProcedure

*p=HelloStar(Random(9223372036854775807))
Debug PeekS(*p,-1,#PB_Ascii)
FreeMemory(*p)
; Hello visible universe on star 889966170588799872008192.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
idle
Always Here
Always Here
Posts: 5042
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Embedding C code in PB source need to be described

Post by idle »

At the moment it's a little limited in it's scope but it's not much different than using inline asm.
All your c symbols are lower case and prefixed as

Variables: v_ local or g_ global
Pointers: p_ local or gp_ global
Functions: f_
Structures: s_
Structure Fields: f_

Arrays: a_
Lists: t_
Maps: m_

Modules: modulenameX

Labels: il_

As an example of the same function in c and asm

Code: Select all

CompilerIf #PB_Compiler_Backend = #PB_Backend_C  
    
  Procedure.q FastHash64(*buf,len,Seed.q=0)
    Protected result.q  
    ;FastHash64 algorithm by Zilong Tan
    !typedef unsigned long long uint64_t; 
    
    !#define mix(h) ({				      	  \
    !			(h) ^= (h) >> 23;		          \
    !			(h) *= 0x2127599bf4325c37ULL;	\
    !			(h) ^= (h) >> 47; })
    !
    
    !	const uint64_t m = 0x880355f21e6d1965ULL;
    !	const uint64_t *pos = (const uint64_t *)p_buf;
    !	const uint64_t *end = pos + (v_len / 8);
    !	const unsigned char *pos2;
    !	uint64_t h = v_seed ^ (v_len * m);
    !	uint64_t v;
    ! uint64_t result; 
    
    !	while (pos != end) {
    !		v  = *pos++;
    !		h ^= mix(v);
    !		h *= m;
    !	}
    
    !	pos2 = (const unsigned char*)pos;
    !	v = 0;
    
    !	switch (v_len & 7) {
    !	case 7: v ^= (uint64_t)pos2[6] << 48;
    !	case 6: v ^= (uint64_t)pos2[5] << 40;
    !	case 5: v ^= (uint64_t)pos2[4] << 32;
    !	case 4: v ^= (uint64_t)pos2[3] << 24;
    !	case 3: v ^= (uint64_t)pos2[2] << 16;
    !	case 2: v ^= (uint64_t)pos2[1] << 8;
    !	case 1: v ^= (uint64_t)pos2[0];
    !		h ^= mix(v);
    !		h *= m;
    !	}
    !
    !	v_result = mix(h);
    
    ProcedureReturn result 
  EndProcedure  
  
CompilerElse
  
  Procedure.q FastHash64(*Buffer, Len, Seed.q=0)
    ; FastHash64 algorithm by Zilong Tan ported by wilbert
    !mov r10, 0x2127599bf4325c37
    !mov r11, 0x880355f21e6d1965
    !mov rdx, [p.p_Buffer]
    !mov rcx, [p.v_Len]
    !mov rax, rcx         ; h = seed ^ (len * m);
    !imul rax, r11
    !xor rax, [p.v_Seed]
    !sub rcx, 8
    !jc .l1
    ; 8 byte loop  
    !.l0:
    !mov r8, [rdx]        ; v = *pos++;
    !add rdx, 8
    ; -- mix(v) start --
    !mov r9, r8
    !shr r9, 23
    !xor r8, r9
    !imul r8, r10
    !mov r9, r8
    !shr r9, 47
    !xor r8, r9
    ; -- mix end --
    !xor rax, r8          ; h ^= mix(v);
    !imul rax, r11        ; h *= m;
    !sub rcx, 8
    !jnc .l0
    ; remaining bytes
    !.l1:
    !add rcx, 8
    !jz .l5
    !xor r8, r8
    !test rcx, 4
    !jz .l2
    ; get 4 bytes
    !mov r8d, [rdx]
    !add rdx, 4
    !ror r8, 32
    !.l2:
    !test rcx, 2
    !jz .l3
    ; get 2 bytes
    !movzx r9d, word [rdx]
    !add rdx, 2
    !xor r8, r9
    !ror r8, 16
    !.l3:
    !test rcx, 1
    !jz .l4
    ; get 1 byte
    !movzx r9d, byte [rdx]
    !xor r8, r9
    !ror r8, 8
    !.l4:
    !and rcx, 7
    !shl rcx, 3
    !rol r8, cl
    ; -- mix(v) start --
    !mov r9, r8
    !shr r9, 23
    !xor r8, r9
    !imul r8, r10
    !mov r9, r8
    !shr r9, 47
    !xor r8, r9
    ; -- mix end --
    !xor rax, r8          ; h ^= mix(v);
    !imul rax, r11        ; h *= m;
    ; -- mix(h) start --
    !.l5:
    !mov r9, rax
    !shr r9, 23
    !xor rax, r9
    !imul rax, r10
    !mov r9, rax
    !shr r9, 47
    !xor rax, r9
    ; -- mix end --
    ProcedureReturn       ; return mix(h);
  EndProcedure
    
CompilerEndIf   


Global s.s = "hello world c" 
Debug FastHash64(@s,StringByteLength(s))
;-5558325713940964056 fasm
;-5558325713940964056 c backend 



In some cases you can include a c header, for example portaudio.h it's doesn't include any other c headers so it is ok, this behaves just like your pasting it into the programs global scope but there's a distinction as it actually added to the main function scope in c which isn't really the intent.

portaudio.h

Code: Select all

#ifndef PORTAUDIO_H
#define PORTAUDIO_H
/*
 * $Id$
 * PortAudio Portable Real-Time Audio Library
 * PortAudio API Header File
 * Latest version available at: http://www.portaudio.com/
 *
 * Copyright (c) 1999-2002 Ross Bencina and Phil Burk
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * The text above constitutes the entire PortAudio license; however, 
 * the PortAudio community also makes the following non-binding requests:
 *
 * Any person wishing to distribute modifications to the Software is
 * requested to send the modifications to the original developer so that
 * they can be incorporated into the canonical version. It is also 
 * requested that these non-binding requests be included along with the 
 * license above.
 */

/** @file
 @ingroup public_header
 @brief The portable PortAudio API.
*/


#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */

/** Retrieve the release number of the currently running PortAudio build.
 For example, for version "19.5.1" this will return 0x00130501.

 @see paMakeVersionNumber
*/
int Pa_GetVersion( void );

/** Retrieve a textual description of the current PortAudio build,
 e.g. "PortAudio V19.5.0-devel, revision 1952M".
 The format of the text may change in the future. Do not try to parse the
 returned string.

 @deprecated As of 19.5.0, use Pa_GetVersionInfo()->versionText instead.
*/
const char* Pa_GetVersionText( void );

/**
 Generate a packed integer version number in the same format used
 by Pa_GetVersion(). Use this to compare a specified version number with
 the currently running version. For example:

 @code
     if( Pa_GetVersion() < paMakeVersionNumber(19,5,1) ) {}
 @endcode

 @see Pa_GetVersion, Pa_GetVersionInfo
 @version Available as of 19.5.0.
*/
#define paMakeVersionNumber(major, minor, subminor) \
    (((major)&0xFF)<<16 | ((minor)&0xFF)<<8 | ((subminor)&0xFF))


/**
 A structure containing PortAudio API version information.
 @see Pa_GetVersionInfo, paMakeVersionNumber
 @version Available as of 19.5.0.
*/
typedef struct PaVersionInfo {
    int versionMajor;
    int versionMinor;
    int versionSubMinor;
    /**
     This is currently the Git revision hash but may change in the future.
     The versionControlRevision is updated by running a script before compiling the library.
     If the update does not occur, this value may refer to an earlier revision.
    */
    const char *versionControlRevision;
    /** Version as a string, for example "PortAudio V19.5.0-devel, revision 1952M" */
    const char *versionText;
} PaVersionInfo;
    
/** Retrieve version information for the currently running PortAudio build.
 @return A pointer to an immutable PaVersionInfo structure.

 @note This function can be called at any time. It does not require PortAudio
 to be initialized. The structure pointed to is statically allocated. Do not
 attempt to free it or modify it.

 @see PaVersionInfo, paMakeVersionNumber
 @version Available as of 19.5.0.
*/
const PaVersionInfo* Pa_GetVersionInfo();


/** Error codes returned by PortAudio functions.
 Note that with the exception of paNoError, all PaErrorCodes are negative.
*/

typedef int PaError;
typedef enum PaErrorCode
{
    paNoError = 0,

    paNotInitialized = -10000,
    paUnanticipatedHostError,
    paInvalidChannelCount,
    paInvalidSampleRate,
    paInvalidDevice,
    paInvalidFlag,
    paSampleFormatNotSupported,
    paBadIODeviceCombination,
    paInsufficientMemory,
    paBufferTooBig,
    paBufferTooSmall,
    paNullCallback,
    paBadStreamPtr,
    paTimedOut,
    paInternalError,
    paDeviceUnavailable,
    paIncompatibleHostApiSpecificStreamInfo,
    paStreamIsStopped,
    paStreamIsNotStopped,
    paInputOverflowed,
    paOutputUnderflowed,
    paHostApiNotFound,
    paInvalidHostApi,
    paCanNotReadFromACallbackStream,
    paCanNotWriteToACallbackStream,
    paCanNotReadFromAnOutputOnlyStream,
    paCanNotWriteToAnInputOnlyStream,
    paIncompatibleStreamHostApi,
    paBadBufferPtr
} PaErrorCode;


/** Translate the supplied PortAudio error code into a human readable
 message.
*/
const char *Pa_GetErrorText( PaError errorCode );


/** Library initialization function - call this before using PortAudio.
 This function initializes internal data structures and prepares underlying
 host APIs for use.  With the exception of Pa_GetVersion(), Pa_GetVersionText(),
 and Pa_GetErrorText(), this function MUST be called before using any other
 PortAudio API functions.

 If Pa_Initialize() is called multiple times, each successful 
 call must be matched with a corresponding call to Pa_Terminate(). 
 Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not 
 required to be fully nested.

 Note that if Pa_Initialize() returns an error code, Pa_Terminate() should
 NOT be called.

 @return paNoError if successful, otherwise an error code indicating the cause
 of failure.

 @see Pa_Terminate
*/
PaError Pa_Initialize( void );


/** Library termination function - call this when finished using PortAudio.
 This function deallocates all resources allocated by PortAudio since it was
 initialized by a call to Pa_Initialize(). In cases where Pa_Initialise() has
 been called multiple times, each call must be matched with a corresponding call
 to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically
 close any PortAudio streams that are still open.

 Pa_Terminate() MUST be called before exiting a program which uses PortAudio.
 Failure to do so may result in serious resource leaks, such as audio devices
 not being available until the next reboot.

 @return paNoError if successful, otherwise an error code indicating the cause
 of failure.
 
 @see Pa_Initialize
*/
PaError Pa_Terminate( void );

.... 
And then you can use it directly importing what you need

Code: Select all

ImportC "portaudio_x64.lib" : EndImport 
!#include "D:\portaudio\portaudio.h";

!#define SAMPLE_RATE         (44100)
!#define PA_SAMPLE_TYPE      paFloat32
!typedef float SAMPLE;

#DOUBLEPREC = 1

CompilerIf #DOUBLEPREC = 0
  Structure complex 
    Re.d
    Im.d 
  EndStructure   
CompilerElse 
  Structure complex 
    Re.f
    Im.f 
  EndStructure   
CompilerEndIf 

Structure arcomplex 
  ar.complex[0]
EndStructure 

Procedure _stockham(*x.arcomplex,n.i,flag.i,n2.i,*y.arcomplex)
  
  Protected *y_orig.arcomplex 
  Protected *tmp.complex 
  
  Protected i.i, j.i, k.i, k2.i, Ls.i, r.i, jrs.i
  Protected half, m, m2                          
  Protected wr.d, wi.d, tr.d, ti.d               
  
  *y_orig = *y
  half = n >> 1
  r = half 
  Ls = 1                                     
  
  While(r >= n2) 
    *tmp = *x                  
    *x = *y                             
    *y = *tmp
    m = 0                      
    m2 = half                    
    j=0
    While j < ls
      wr = Cos(#PI*j/Ls)
      wi = -flag * Sin(#PI*j/Ls)            
      jrs = j*(r+r)
      k = jrs
      While k < jrs+r
        k2 = k + r
        tr =  wr * *y\ar[k2]\Re - wi * *y\ar[k2]\Im   
        ti =  wr * *y\ar[k2]\Im + wi * *y\ar[k2]\Re
        *x\ar[m]\Re = *y\ar[k]\Re + tr
        *x\ar[m]\Im = *y\ar[k]\Im + ti
        *x\ar[m2]\Re = *y\ar[k]\Re - tr
        *x\ar[m2]\Im = *y\ar[k]\Im - ti
        m+1
        m2+1
        k+1
      Wend 
      j+1
    Wend  
    r  >> 1
    Ls << 1
  Wend 
  
  CopyMemory(*x,*y,n*SizeOf(complex))  
   
EndProcedure   

Procedure fft(*x.arcomplex,n.i,flag.i=1)
  Protected *y.arcomplex
  *y = AllocateMemory((n)*SizeOf(complex))
  _stockham(*x, n, flag, 1, *y)
  FreeMemory(*y) 
EndProcedure 

#PI2 = 2 * #PI 
#hamming =1
#Hanning = 2 
#Blackman = 3  

Global gNumNoInputs = 0;
Global fftpoints = 4096
Global Dim inp.complex(fftpoints)
Global Dim windowing.f(fftpoints) 
Global windowtype = 1 
Global samplesize = fftpoints / 4 
Global *buf = AllocateMemory(fftpoints*SizeOf(complex)) 

For a = 0 To fftpoints 
  If windowtype = #Hamming 
    windowing(a) =  0.54 - (0.46 * Cos(#PI2 * a / ((fftpoints/2)-1)))  
  ElseIf windowtype = #Hanning 
    windowing(a) = 0.5 * (1.0-Cos((#pi2*a /(fftpoints/2)-1)))   
  ElseIf windowtype = #BLackman  
    windowing(a) = 0.42- ((0.5 * Cos(#PI2*a / (fftpoints-1))) + (0.08 * Cos(4*#PI*a/(fftpoints-1))))  
  EndIf 
Next   

Procedure Freqency() 
  Protected res.d, time.d 
  res = (44100 / 2.0 / fftpoints)
  time = 1/res 
EndProcedure  

Procedure.f Mag(*val.complex) 
  ProcedureReturn Sqr(*val\Re * *val\re + *val\Im * *val\Im)
EndProcedure   

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(CubicAmplifier(x))))
  EndMacro

ProcedureCDLL fuzzCallback(*in.float,*out.float,framesPerBuffer,*timeInfo,statusFlags,*userData)
  Protected a,half,*pbuf.float, in.s, out.s ,shift  
   
  half = fftpoints >> 1 
    
  For a = 0 To half-1
     inp(a)\Re = *in\f * windowing(a)   
     inp(a)\Im = 0 
     *in+4
  Next   
    
  fft(@inp(0),fftpoints,1)  ;do forward fft 
  
  For a = 0 To half-256
   inp(a)\Re = inp(a+256)\Re  ;mag(@inp(Random(64,half))) ;mess with the signal
   inp(a)\Im = inp(a+256)\Im   
  Next 
       
  fft(@inp(0),fftpoints,-1) ;do inverse fft 
  
  For a = 1 To half-1 
    If a < 20 
      *out\f = 0
     Else  
      *out\f = inp(a)\re ;* windowing(a)  ;write it out should really divide by ftpoints  
    EndIf
    *out+4 
  Next
  
  FillMemory(@inp(0),fftpoints*SizeOf(complex),0,#PB_Long) 
  
  ProcedureReturn 0 
EndProcedure 

OpenConsole() 

!PaStreamParameters inputParameters;
!PaStreamParameters outputParameters;
!PaStream *stream;

Global err,pcb ;

pcb= @fuzzCallback()
!g_err = Pa_Initialize();
If err <> 0 
  Goto error;
EndIf  

!inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */

!if (inputParameters.device == paNoDevice) {
Debug "Error: No default input device."
Goto error;
!}

!inputParameters.channelCount = 2;       /* stereo input */
!inputParameters.sampleFormat = PA_SAMPLE_TYPE;
!inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device )->defaultLowInputLatency;
!inputParameters.hostApiSpecificStreamInfo = 0;

!outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */

!if (outputParameters.device == paNoDevice) {
Debug "Error: No Default output device."
Goto error;
!}

!outputParameters.channelCount = 2;       /* stereo output */
!outputParameters.sampleFormat = PA_SAMPLE_TYPE;
!outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
!outputParameters.hostApiSpecificStreamInfo = 0;

!g_err = Pa_OpenStream(&stream,&inputParameters,&outputParameters,SAMPLE_RATE,g_samplesize,0,g_pcb,0);
If err <> 0 
  Goto error;
EndIf 

!g_err = Pa_StartStream( stream );
If err <> 0 
  Goto error;
EndIf 

PrintN("Hit ENTER to stop program.");
Input()

!g_err = Pa_CloseStream( stream );
If err <> 0 
  Goto error;
EndIf 

PrintN("Finished.");
Input() 
!Pa_Terminate();

error:
!Pa_Terminate();

User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2056
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: Embedding C code in PB source need to be described

Post by Andre »

Still an issue for @Fred.... he must decide if there should be a combined chapter 'Using Inlince ASM / C' or two different chapters, where the usage of InlineASM and InlineC is described and examples are provided.
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
Post Reply