Inline C Arrays help

Just starting out? Need help? Post your questions and find answers here.
User avatar
idle
Always Here
Always Here
Posts: 5042
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Inline C Arrays help

Post by idle »

I'll have a look in the morning. I was just trying to find a solution for both asm and c backend. You can use it to update the read and write pointers.
User avatar
idle
Always Here
Always Here
Posts: 5042
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Inline C Arrays help

Post by idle »

here's a port of the PortAudio Ring buffer, it's lock free but only safe for single producer consumer threads.

Code: Select all

;  * Portable Audio I/O Library
;  * Ring Buffer utility.
;  *
;  * Author: Phil Burk, http://www.softsynth.com
;  * modified For SMP safety on Mac OS X by Bjorn Roche
;  * modified For SMP safety on Linux by Leland Lucius
;  * also, allowed For const where possible
;  * modified For multiple-byte-sized Data elements by Sven Fischer
;  *
;  * Note that this is safe only For a single-thread reader And a
;  * single-thread writer.
;  *
;  * This program uses the PortAudio Portable Audio Library.
;  * For more information see: http://www.portaudio.com
;  * Copyright (c) 1999-2000 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.
;  */
;  
;  Ported to PB 6.0 idle x86/x64 asm and cbackend 

Structure RingBuffer_Ar 
  e.a[0]   
EndStructure

Structure RingBuffer 
  stop.i
  bufferSize.i
  writeIndex.i
  readIndex.i
  bigMask.i
  smallMask.i
  elementSizeBytes.i 
  *buffer.Ringbuffer_Ar 
EndStructure   

Macro FullMemoryBarrier() 
  CompilerIf #PB_Compiler_Backend = #PB_Backend_Asm     
    !mfence 
  CompilerElse 
    !__sync_synchronize(); 
  CompilerEndIf   
EndMacro 

Macro WriteMemoryBarrier()
  CompilerIf #PB_Compiler_Backend = #PB_Backend_Asm  
    !sfence 
  CompilerElse  
    !__sync_synchronize(); 
  CompilerEndIf   
EndMacro 

Macro ReadMemoryBarrier() 
  CompilerIf #PB_Compiler_Backend = #PB_Backend_Asm   
    !lfence
  CompilerElse  
    !__sync_synchronize();
  CompilerEndIf    
EndMacro 

Procedure InitializeRingBuffer(*rbuf.RingBuffer,elementSizeBytes,elementCount)

  If(((elementCount-1) & elementCount) <> 0) 
    ProcedureReturn -1
  EndIf  
  
  *rbuf\bufferSize = elementCount
  *rbuf\buffer = AllocateMemory(elementCount*elementSizeBytes)  
  *rbuf\bigMask = (elementCount*2)-1
  *rbuf\smallMask = (elementCount)-1
  *rbuf\elementSizeBytes = elementSizeBytes
  
  ProcedureReturn *rbuf\buffer 
  
EndProcedure 

Procedure FreeRingBuffer(*rb.RingBuffer) 
  
  If *rb 
    If *rb\buffer 
      FreeMemory(*rb\buffer) 
      *rb\buffer = #Null 
    EndIf 
  EndIf
  
EndProcedure   

Procedure GetRingBufferReadAvailable(*rb.RingBuffer)
  
  ProcedureReturn((*rb\writeIndex - *rb\readIndex) & *rb\bigMask)
  
EndProcedure 

Procedure GetRingBufferWriteAvailable(*rb.RingBuffer)
  
  ProcedureReturn(*rb\bufferSize - GetRingBufferReadAvailable(*rb))
  
EndProcedure 

Procedure FlushRingBuffer(*rb.RingBuffer)
  
  *rb\writeIndex = 0 
  *rb\readIndex = 0
  
EndProcedure 

Procedure GetRingBufferWriteRegions(*rb.RingBuffer,elementCount,*dataPtr1.integer,*sizePtr1.integer,*dataPtr2.integer,*sizePtr2.integer )
    Protected index.i, available.i
    
    available.i = GetRingBufferWriteAvailable(*rb)
    
    If elementCount > available 
      elementCount = available
    EndIf   
   
    index = (*rb\writeIndex & *rb\smallMask)
    
    If(index + elementCount) > *rb\bufferSize 
        firstHalf = *rb\bufferSize - index
        *dataPtr1\i = @*rb\buffer\e[index * *rb\elementSizeBytes]
        *sizePtr1\i = firstHalf
        *dataPtr2\i = @*rb\buffer\e[0]
        *sizePtr2\i = elementCount - firstHalf
    Else
        *dataPtr1\i = @*rb\buffer\e[index * *rb\elementSizeBytes]
        *sizePtr1\i = elementCount
        *dataPtr2\i = #Null
        *sizePtr2\i = 0
    EndIf 

    If available 
      FullMemoryBarrier() 
    EndIf 
    
    ProcedureReturn elementCount
    
EndProcedure 

Procedure AdvanceRingBufferWriteIndex(*rb.RingBuffer,elementCount.i)
    Protected index,*ptr  
    
    WriteMemoryBarrier()
    *rb\writeIndex = (*rb\writeIndex + elementCount) & *rb\bigMask
    
    ProcedureReturn *rb\writeIndex
    
EndProcedure 

Procedure GetRingBufferReadRegions(*Rb.RingBuffer,elementCount,*dataPtr1.integer,*sizePtr1.integer,*dataPtr2.integer,*sizePtr2.integer)
    Protected index.i,firsthalf.i,available.i
    
    available.i = GetRingBufferReadAvailable(*rb)
    
    If( elementCount > available ) 
      elementCount = available
    EndIf   
   
    index = (*rb\readIndex & *rb\smallMask)
    
    If((index + elementCount) > *rb\bufferSize )
        firstHalf = *rb\bufferSize - index
        *dataPtr1\i = @*rb\buffer\e[index * *rb\elementSizeBytes]
        *sizePtr1\i = firstHalf
        *dataPtr2\i = @*rb\buffer\e[0]
        *sizePtr2\i = elementCount - firstHalf
    Else
       *dataPtr1\i = @*rb\buffer\e[index * *rb\elementSizeBytes]
       *sizePtr1\i = elementCount
       *dataPtr2\i = 0
       *sizePtr2\i = 0
    EndIf 
    
    If( available )
       ReadMemoryBarrier() 
    EndIf 
    
    ProcedureReturn elementCount
    
EndProcedure 

Procedure AdvanceRingBufferReadIndex(*rb.RingBuffer,elementCount)
  
    FullMemoryBarrier()
    
    *rb\readIndex = (*rb\readIndex + elementCount) & *rb\bigMask
    
    ProcedureReturn *rb\readIndex
    
EndProcedure 

Procedure WriteRingBuffer(*rb.RingBuffer,*Data,elementCount)
    Protected size1, size2, numWritten
    Protected data1, data2
    
    numWritten = GetRingBufferWriteRegions(*rb,elementCount,@data1, @size1, @data2, @size2)
    
    If( size2 > 0 )
       CopyMemory(*Data,data1,size1 * *rb\elementSizeBytes)
       *data + (size1 * *rb\elementSizeBytes)
       CopyMemory(*Data,data2,size2 * *rb\elementSizeBytes)
    Else
       CopyMemory(*Data,data1, size1 * *rb\elementSizeBytes )
    EndIf 
    
    AdvanceRingBufferWriteIndex(*rb,numWritten)
    
    ProcedureReturn numWritten                 
    
EndProcedure 

Procedure ReadRingBuffer(*rb.RingBuffer,*Data,elementCount)
    Protected size1, size2, numRead
    Protected data1, data2
    
    numRead = GetRingBufferReadRegions(*rb,elementCount,@data1,@size1,@data2,@size2 )
    
    If( size2 > 0 )
       CopyMemory(data1,*data,size1 * *rb\elementSizeBytes )
        *Data + size1 * *rb\elementSizeBytes
        CopyMemory(data2,*Data,size2 * *rb\elementSizeBytes )
    Else
      CopyMemory(data1,*Data,size1 * *rb\elementSizeBytes )
    EndIf 
    
    AdvanceRingBufferReadIndex(*rb,numRead)
    
    ProcedureReturn numRead                
    
EndProcedure 

CompilerIf #PB_Compiler_IsMainFile
    
  Procedure Producer(*RB.RingBuffer) 
    
    Protected num 
    Dim inputs.f(64) 
    
    Repeat 
      
      For a = 0 To 63
        inputs(a) = ct 
        ct+1 
      Next 
      
      num = WriteRingBuffer(*RB,@inputs(0),64) ;write 64 elements to the ring if full it will return 0  
      
      Debug "num write " + Str(num) 
      
      Delay(Random(100,20))  
      
    Until *RB\stop 
    
  EndProcedure 
  
  
  Procedure Consumer(*RB.RingBuffer) 
    
    et= ElapsedMilliseconds() + 10000
    Dim outputs.f(64) 
    
    Repeat 
      
      num = ReadRingBuffer(*RB,@outputs(0),64)  ;read 64 elements off ring if empty it will return 0 
      
      Debug "num read " + Str(num) + " " + StrF(outputs(0),3)      
      
      Delay(Random(100,20))  
      
    Until ElapsedMilliseconds() > et       
    
    *rb\stop = 1 ;stop buffering  
    
  EndProcedure   
    
  
  Global RB.RingBuffer                          ;decalre a RB              
  InitializeRingBuffer(@RB,SizeOf(float),1024)  ;set the size in bytes of elements and the number or elements 
  
  t1 = CreateThread(@Producer(),@RB)    ;create writer thread 
  t2 = CreateThread(@consumer(),@RB)    ;create reader thread 
  
  WaitThread(t2) 
    
CompilerEndIf 
 
   
User avatar
idle
Always Here
Always Here
Posts: 5042
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Inline C Arrays help

Post by idle »

this might work for you it's simulating what you need to do through the ringbuffer in previous post using two call backs one for thge input on your external sound card and the other on the on board sound card

Code: Select all


XIncludeFile "ringbuffer.pbi" 

ImportC "portaudio_x64.lib" : EndImport 
!//#include E:\andrews\pbstuff\paintsound\portaudio\portaudio.h;  path to portaudio.h 

ProcedureC InputCallback(*in.float,*out.float,framesPerBuffer,*timeInfo,statusFlags,*rb)
  
  WriteRingBuffer(*rb,*in,framesPerBuffer) 
     
EndProcedure 

ProcedureC OutputCallback(*in.float,*out.float,framesPerBuffer,*timeInfo,statusFlags,*rb)
  
  ReadRingBuffer(*rb,*out,framesPerBuffer) 
      
EndProcedure 

OpenConsole() 

!#define SAMPLE_RATE         (44100)
!#define PA_SAMPLE_TYPE      paFloat32
!PaStreamParameters inputParameters;
!PaStreamParameters outputParameters;
!PaStream *streamin;
!PaStream *streamout;

Global err,pinputcb,poutputcb,samplesize ;

samplesize = 256*2*SizeOf(float)      
pinputcb= @InputCallback()  
poutputcb = @OutputCallback()

Global rb.ringbuffer 
InitializeRingBuffer(@rb,8,8192)

!v_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;

!v_err = Pa_OpenStream(&streamin,&inputParameters,0,SAMPLE_RATE,v_samplesize,0,v_pinputcb,&v_rb);
If err <> 0 
  Goto error;
EndIf 

!v_err = Pa_OpenStream(&streamout,0,&outputParameters,SAMPLE_RATE,v_samplesize,0,v_poutputcb,&v_rb);
If err <> 0 
  Goto error;
EndIf 


!v_err = Pa_StartStream( streamin );
If err <> 0 
  Goto error;
EndIf 
!v_err = Pa_StartStream( streamout );
If err <> 0 
  Goto error;
EndIf 


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

!v_err = Pa_CloseStream( streamin );
If err <> 0 
  Goto error;
EndIf 

!v_err = Pa_CloseStream( streamout );
If err <> 0 
  Goto error;
EndIf 


!Pa_Terminate();
End 

error:
!Pa_Terminate();


AndyMK
Enthusiast
Enthusiast
Posts: 540
Joined: Wed Jul 12, 2006 4:38 pm
Location: UK

Re: Inline C Arrays help

Post by AndyMK »

Thanks idle, i'll check it out today. You should have a donation link. I want to buy you a beer :D
AndyMK
Enthusiast
Enthusiast
Posts: 540
Joined: Wed Jul 12, 2006 4:38 pm
Location: UK

Re: Inline C Arrays help

Post by AndyMK »

It works flawlessly. Threadsafe needs to be switched on and the ringbuffer size must be a power of 2. This has solved all of my problems and simplified my code considerably. Thanks again idle.
User avatar
idle
Always Here
Always Here
Posts: 5042
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Inline C Arrays help

Post by idle »

Glad it solved your problem I also needed a better ringbuffer and that one is pretty good. Thanks for the 🍺 beer.
Post Reply