September 17, 2021, 05:07:16

Author Topic: How to write a wrapper?  (Read 7006 times)

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: How to write a wrapper?
« Reply #60 on: April 12, 2021, 16:53:00 »
thank you for the first answer. I'm still struggling with this pointer stuff. I already check the adresses casted to INT in BlitzMax and in C. This helps a lot.

Your second answer would not help in my project, because it is the wrapper, which needs the pointer. The pointer adress will be a part of the STRUCT and later the wrapper will read this value and the call then BlitzMax-function every 10msec.

The TenCallbacks was only a simulation of the wrappers job.

The next job for me is now to pass various types of variables between C and the MyCallBack(). At the moment I only tested INT.

This is the final target:
Code: [Select]
DATA_CALLBACK ()

//at line 36
// example how a MyCallback() should look like:
    void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
    {
        // my code
    }

// at line 3125
// the definition of the CallBack in MidiAudio.h:
typedef void (* ma_device_callback_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);


and the manual says:
Quote
//at line 3086
The callback for processing audio data from the device.

The data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data available. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the callback will be fired with a consistent frame count.


Parameters
----------
pDevice (in)
    A pointer to the relevant device.

pOutput (out)
    A pointer to the output buffer that will receive audio data that will later be played back through the  speakers. This will be non-null for a playback or  full-duplex device and null for a capture and loopback device.

pInput (in)
    A pointer to the buffer containing input data from a recording device. This will be non-null for a capture, full-duplex or loopback device and null for a playback device.

frameCount (in)
    The number of PCM frames to process. Note that this will not necessarily be equal to what you requested when you initialized the device. The `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must  not assume this will always be the same value each time the callback is fired.

« Last Edit: April 12, 2021, 16:56:23 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: C-Callback to a BlitzMax Methode?
« Reply #61 on: April 13, 2021, 13:08:58 »
Today I have a Callback related question.

The MiniAudio.h needs a Callback-function to receive data from the main app. As my Wrapper will be a TYPE with instances I ask myself, if it is possible to also give the C-STRUCT the (pointer-) adress to a METHODE instead to a FUNCTION?

Additional the user does not need all parameters the Callback returns. So I forward the CallBack with less parameters from a method to a user function. This enables the user to use a free name for his function. But this part (I think) is already correct.



Code: [Select]
typedef void (* ma_device_callback_proc)(int a, int b, int c, int d);

// store the  Callback :
extern void SetCallBack(struct ma_device_config *config, ma_device_callback_proc Callback) {
printf("FILL_CALLBACK \n");
config->dataCallback= Callback;
printf("set callback pointer= %i \n", config->dataCallback);
}

void SingleCallback(ma_device_config *config){
printf("SIMULATE SINGLE CALLBACK \n");
printf("get callback pointer= %i \n", config->dataCallback);
(* config->dataCallback) (1,2,3,4);
}

Code: BlitzMax
  1. Type TMiniAudio
  2.         Field  DeviceConfig Byte Ptr, Field UserCallBack:Byte Ptr
  3.  
  4.         Method SetDevice(Format:Int, Channels:Int, SampleRate:Int, UserCallBackPointer: Byte Ptr)
  5.                 ....
  6.                 UserCallBack = UserCallBackPointer
  7.                 _SetDeviceConfig DeviceConfig, Format, Channels, SampleRate, InternCallBack
  8.                 Device=GetDevice(DeviceConfig)
  9.         End Method
  10.  
  11.         Method InternCallBack(a% , b% , c% , d%)
  12.                 Local ExternCallBack(x:Int) = UserCallBack
  13.                 ExternCall a
  14.         End Method
  15. End Type
  16.  
  17. Function MyCallBack(a%)
  18.                 TestValue=TestValue+ a
  19. End Function
  20.  
  21. MiniAudio.SetDevice( Miniaudio.FORMAT_S16, 2, 48000, MyCallBack)
  22.  

« Last Edit: April 21, 2021, 16:36:13 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline col

  • Hero Member
  • *****
  • Posts: 590
Re: How to write a wrapper?
« Reply #62 on: April 13, 2021, 14:40:59 »
When calling methods you would expect to find a 'this' (Self in BMax) kind of parameter that will be a pointer to the type instance. The instance will contain the Field members (with correct expected values).

What this all means is while this seems to be working while passing in simple parameters to a method you may run into trouble when trying to access the Field members of an instance.

Methods in OO style languages are usually regular functions but usually with the first parameter being a pointer to the object instance and the parameters you pass to method are pushed one along. I can't remember how Brucey does this in C so you may well want to check the output of the compiler for this example to check.

I'd be careful with how you are doing this as all may not be as it seems... double check and triple check that you can access the Fields properly before continuing on.

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: First Try to Start the real MiniAudio.h
« Reply #63 on: April 14, 2021, 09:33:37 »
Ok... I wrote all functionality into a TYPE, for instancing. But the Callback remains a Function() at the moment.

for testing purposes I wrote a simulation for the real MiniAudio.h, which contains only the functions that are already called. These functions are no copies of the original MiniAudio.h-functions, but have only the minimum requirement they need to communicate with my Miniaudio.c.

So the ZIP-attachment contains now always 4 files:
  • MiniAudio.bmx
  • MiniAudio.c
  • simulate.h
  • MiniAudio.txt

The Miniaudio.txt is a table to help you finding the relevant code lines in the very big Miniaudio.h, which is linked here:


Cannot find the functions in Miniaudio.h

With the simulator it works perfect. All functions can be called. But when I try to use the original MiniAudio.h-file the compiler says:
Quote
C:/BasicNG/FreeAudio/.bmx/MiniAudio.bmx.gui.release.win32.x64.o:MiniAudio.bmx.gui.release.win32.x64.c:(.text+0x95): undefined reference to `ma_device_start'
C:/BasicNG/FreeAudio/.bmx/MiniAudio.bmx.gui.release.win32.x64.o:MiniAudio.bmx.gui.release.win32.x64.c:(.text+0xa5): undefined reference to `ma_device_stop'
....(continued for all miniaudio.h-functions)

What do I forget?

Miniaudio.bmx
Code: BlitzMax
  1. Extern "C"
  2.         Function _StartDevice:Int (DeviceRAM:Byte Ptr) = "ma_device_start"
  3.         .....
  4.  

Miniaudio.c
Code: [Select]
#define MA_API extern
#define MA_DEBUG_OUTPUT //printf() logging=on
#define MA_LOG_LEVEL_INFO 3
#define MA_LOG_LEVEL MA_LOG_LEVEL_INFO
#include "miniaudio.h"
//#include "simulate.h"
.....

simulate.h
Code: [Select]
MA_API int ma_device_start(ma_device *pdevice){
printf("INTERN START_DEVICE \n");
return 0;
}
....



miniaudio.h
Code: [Select]
MA_API ma_result ma_device_start(ma_device* pDevice);
....

***EDIT***************
ZIP file moved to post #69
**********************
« Last Edit: April 15, 2021, 10:11:19 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline col

  • Hero Member
  • *****
  • Posts: 590
Re: How to write a wrapper?
« Reply #64 on: April 15, 2021, 06:58:48 »
Quote
But when I try to use the original MiniAudio.h-file the compiler says:
[...]
What do I forget?

I think I mentioned before that single header implementations general have a #define to allow the implementation to be exposed. Immediately upon looking at the docs on Page 1

1. Introduction
===============
miniaudio is a single file library for audio playback and capture. To use it, do the following in one .c file:

    ```c
    #define MINIAUDIO_IMPLEMENTATION
    #include "miniaudio.h"


I would think this is the problem.

Offline col

  • Hero Member
  • *****
  • Posts: 590
Re: How to write a wrapper?
« Reply #65 on: April 15, 2021, 07:11:40 »
Quote
Ok... I wrote all functionality into a TYPE, for instancing. But the Callback remains a Function() at the moment.

For when you get to using the callback properly:

Callback functions also usually have a parameter called UserData or Context or something similar which is also usually a pointer. That parameter is passed into the called function - ie the callback function. It may be possible (and is usually used for) to pass in an instance of something that 'you as the user of miniaudio' have created. As the creator of the wrapper you could use the parameter to pass a BMax instance back into the BMax Type Function.

That way you could have access to a Type instance via a parameter in the BMax callback Function. This is a more robust method than trying to access a BMax Method from C.
« Last Edit: April 15, 2021, 07:15:12 by col »

Offline col

  • Hero Member
  • *****
  • Posts: 590
Re: How to write a wrapper?
« Reply #66 on: April 15, 2021, 07:52:49 »
Quote
undefined reference to `ma_device_start'

For your learning this is always where the linker cannot find the defintion of a function.

Hmm how to make this simple without going into the whole compilation process of which there a many steps...

In c/cpp there is a clear distinction between a declaration and a definition.

Code: [Select]
// Here MyFunc is a declaration - formally called a forward declaration.
// We have declared what the function will look like - name, return type and number of parameters and parameter types .
// Code can 'use' this declaration before the compilation process sees the actual function body (the function definition),
//     and it is trusted that the function body (definition) will be present somewhere in all of the binaries used to create the final executable.
int MyFunc();

// This is a definition - a function definition - ie the body of the function.
int MyFunc() {
   return 0;
}


When you declare a declaration you are promising to the linker that the [/b]definition[/b] will be in an object file that the linker can also 'see' when it comes to create the final executable. Here an 'object file - *.o' is a binary file made from a .c file and NOT the final exe.

So the compiler will allow to compile a .c file if it has seen the declaration even if it has not seen the definition - The definition could be in the same object file, another object file, a static library file etc. It is the job of the linker to scan all of the object files looking for the defintion. If it can't find the defintion in any of the object files then you get your error above.

Clear as mud :D

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Re: How to write a wrapper?
« Reply #67 on: April 15, 2021, 09:03:53 »
Immediately upon looking at the docs on Page 1
....
    ```c
    #define MINIAUDIO_IMPLEMENTATION
    #include "miniaudio.h"
I would think this is the problem.

Oh shit! I did not see this information in the midiaudio.h. Of course now is clear why it did not work. Thank you a lot. It is good to know to have some helping friends here in the forum. I could not continue without this help.

I also thought about using the device.pUserData to transport informations into the callback. The MiniAudio offers such an option already:
Code: [Select]
    int main()
    {
        ma_device_config config = ma_device_config_init(ma_device_type_playback);
        ....
        config.pUserData         = pMyCustomData;   // Can be accessed from the device object (device.pUserData).
   }

    void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)


At the moment I decided to stay during the testing phase at a simple function.

I think now I understand the concept of declarations and definitions. Can I say: "The declaration is a promise, that there will a function with the same name more down in the code or any where else."

But for the wrapper you have to find the relevant definitions. I will try to find out, why this MINIAUDIO_IMPLEMENTATION was necessary and where else it is metioned in the code.

First Start

with the added line #define MINIAUDIO_IMPLEMENTATION the app now answers:

Code: [Select]
Building MiniAudio
[ 97%] Processing:MiniAudio.bmx
[ 98%] Compiling:MiniAudio.c
[ 99%] Compiling:MiniAudio.bmx.gui.release.win32.x64.c
[100%] Linking:MiniAudio.exe
Executing:MiniAudio.exe
START_DEVICE_GLUE
set sample rate= 0
FILL_VALUE_B
set sample rate= 48000
set callback pointer= 4199760
CREATE A DEVICE
: Attempting to initialize WASAPI backend...
[miniaudio] Endian:  LE
[miniaudio] SSE2:    YES
[miniaudio] AVX2:    NO
[miniaudio] AVX512F: NO
[miniaudio] NEON:    NO
[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=480)
    defaultPeriodInFrames=480
    fundamentalPeriodInFrames=480
    minPeriodInFrames=480
    maxPeriodInFrames=480
[WASAPI] Using IAudioClient3
    periodSizeInFramesOut=480
INFO: [WASAPI]
INFO:   1 - 32W_LCD_TV (AMD High Definition Audio Device) (Playback)
INFO:     Format:      16-bit Signed Integer -> 32-bit IEEE Floating Point
INFO:     Channels:    2 -> 2
INFO:     Sample Rate: 48000 -> 48000
INFO:     Buffer Size: 480*3 (1440)
INFO:     Conversion:
INFO:       Pre Format Conversion:    NO
INFO:       Post Format Conversion:   YES
INFO:       Channel Routing:          NO
INFO:       Resampling:               NO
INFO:       Passthrough:              NO
set sample rate= 48000

Process complete

and this is really a good sign!


The MiniAudio already uses WASAPI. This is a novice for BlitzMax and the latency is expected to be 30msec:
Code: [Select]
...
INFO:     Sample Rate: 48000 -> 48000
INFO:     Buffer Size: 480*3 (1440)
...
This is faster then ever it was possible with BlitzMax.

There are a lot of conditional compiling definitions starting at line 06356-64161:
Code: [Select]
/***********************************************************************************************
IMPLEMENTATION
*********************************************************************************/
//line 06356
#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
#ifndef miniaudio_c
#define miniaudio_c
....
//line 64161
#endif  /* MINIAUDIO_IMPLEMENTATION */
The first is MINIAUDIO_IMPLEMENTATION and the corresponing #endif comes 58000 lines later.

This would mean without MINIAUDIO_IMPLEMENTATION these 58.000 lines would not be compiled?
« Last Edit: April 15, 2021, 09:30:53 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline col

  • Hero Member
  • *****
  • Posts: 590
Re: How to write a wrapper?
« Reply #68 on: April 15, 2021, 10:02:57 »
That's awesome that you got it working! Well done :)

For sure the main guts of the file will be the implementation. Normally you would have a .h and a .c file with the declarations in the header and the definitions in the .c file.

Remembering that #include pastes the contents of the included file into the .c file you will need a method to prevent multiple definitions for if more than once .c file #include's it - hence the implementation is guarded by the #ifdef MINIAUDIO_IMPLEMENTATION and only one .c file will use #define MINIAUDIO_IMPLEMENTATION before #include'ing the header in order to expose the implementation.


Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
It Works! And It Works Incredible Fast!
« Reply #69 on: April 15, 2021, 10:10:21 »
Ok here is are some first running examples of the new Audio-Device MiniAudio()

With MiniAudio as SoundDevice you can use WASAPI for the first time in BlitzMax. This is completety new for BlitzMax.

In this example you can get a first impression of the speed of the Interface. You hear a short rhythmic noise every 1000msec. Listen to the rhythm and the press down the mouse every 4th beat in the same rhythm. Now you will hear a second NOISE immediately. The difference between the rhythmic NOISE and yours is the latency. I would guess the latency is <30mses.

The current approach is not to use PlaySound() any more but sending Samples directly to the Device. This offers you new possiblities of direct acccess into sounds in realtime while they are already playing. not only pan or voulme, but also single sample values can be changed.

You need the miniaudio.h from here:
https://raw.githubusercontent.com/mackron/miniaudio/master/miniaudio.h

...and the files in the attachment:
  • OpenClose.bmx
    PlayNoise.bmx
  • MiniAudioTestLatency.bmx
  • MiniAudioWrapper.bmx
  • MiniAudio.c

BlitzMax-Examples:

1st Example: Open and Close
In this example you can see the minimum requirements for the interface. This app will only open and close the Interface immediately and show some informations. Further apps will always need this "startup"-code.

OpenClose.bmx

Code: BlitzMax
  1. SuperStrict
  2.  
  3. Import "MiniAudioWrapper.bmx"
  4.  
  5. ' Setup of the device:
  6. Global MiniAudio:TMiniAudio=New TMiniAudio
  7. If MiniAudio.GetDeviceConfig(MiniAudio.PLAYBACK)=False
  8.         Notify "Drive not startet"
  9. EndIf
  10. MiniAudio.SetDevice( Miniaudio.FORMAT_S16, 2, 48000, MyCallBack)
  11. ' now start it:
  12. MiniAudio.StartDevice()
  13. ' delte it:
  14. Miniaudio.KillDevice()
  15. End
  16.  
  17. Function MyCallBack(Buffer:Byte Ptr, Frames%)
  18.         ' do something with the samples
  19. End Function
  20.  

After creating the new device...
Code: [Select]
Global MiniAudio:TMiniAudio=New TMiniAudio
... you have to decide whether you will use it for
MiniAudio.PLAYBACK
or
MiniAudio.CAPTURE
Code: [Select]
If MiniAudio.GetDeviceConfig(MiniAudio.PLAYBACK)=False
Notify "Drive not startet"
EndIf

Next step is to define the SampleSize, the number of channels, and the frequency in Hertz.
as last parameter the MiniAudio expects a function, where you fill the samples into the buffer.
Code: [Select]
MiniAudio.SetDevice( Miniaudio.FORMAT_S16, 2, 48000, MyCallBack)
When you need the Decvice you can start it with MiniAudio.StartDevice() and stop it with MiniAudio.StopDevice(). For restarting it you can simply use MiniAudio.StartDevice() again.


The MiniAudio expects a function, where you fill the samples into the buffer.
Code: [Select]
Function MyCallBack(Buffer:Byte Ptr, Frames%)
' do something with the samples
End Function
The number of Bytes you need to send is calculated by...

Number of Bytes= Frames*SampleSize*Channels
 
Frames is given by the Callback
SampleSize is 1 if you use 8bit Samples, 2 if you use 16bit Samples and 4 if you use 32bit samples
Channels is 1 if you work MONO, 2 if you work STEREO, or 3-32 if you work multichannel




2nd Example: Play Noise
In this example you can get a first impression of the speed of the Interface. You hear SILENCE although the Device is already open. When you press down the mouse you will hear a  NOISE immediately. This means you can feed the Device with samples or not, it does not crash, but answers with silence
PlayNoise.bmx

Code: BlitzMax
  1. SuperStrict
  2.  
  3. Import "MiniAudioWrapper.bmx"
  4.  
  5. Graphics 800,600
  6.  
  7. ' Setup of the device:
  8. Global MiniAudio:TMiniAudio=New TMiniAudio
  9. If MiniAudio.GetDeviceConfig(MiniAudio.PLAYBACK)=False
  10.         Notify "Drive not startet"
  11. EndIf
  12. MiniAudio.SetDevice( Miniaudio.FORMAT_S16, 2, 48000, MyCallBack)
  13.  
  14.  
  15. ' now start it:
  16. MiniAudio.StartDevice()
  17.  
  18. Global NOISE%=0, Zeit%=MilliSecs()
  19. Repeat
  20.         Cls
  21.         DrawText "Press LEFT MOUSE for NOISE",100,100
  22.         If MouseDown(1)
  23.                         NOISE=1
  24.         Else
  25.                 NOISE=0
  26.         EndIf
  27.         Delay 1
  28.         Flip
  29. Until AppTerminate()
  30. Miniaudio.KillDevice()
  31. End
  32.  
  33.  
  34. Function MyCallBack(Buffer:Byte Ptr, Frames%)
  35.         ' here you manipulate the sound:
  36.         If NOISE=1
  37.                 For Local i%=0 To 4*(frames-1)
  38.                         Buffer[i]=Rand(255)
  39.                 Next
  40.  
  41.         EndIf
  42. End Function
  43.  

3rd Example:Test Latency
In this example you can check the latency of the Interface. You hear a short rhythmic noise every 1000msec. Listen to the rhythm and the press down the mouse every 4th beat in the same rhythm. Now you will hear a second NOISE immediately. The difference between the rhythmic NOISE and yours is the latency. I would guess the latency is <30mses.

MiniAudiotestLatency.bmx
Code: BlitzMax
  1. ....
  2. Global NOISE%=0, Zeit%=MilliSecs()
  3. Repeat
  4.         Cls
  5.         DrawText "Kick LEFT MOUSE for a short beat",100,100
  6.         If MouseDown(1)
  7.                 If NOISE=0
  8.                         NOISE=1
  9.                 EndIf
  10.         Else
  11.                 NOISE=0
  12.         EndIf
  13.         Delay 1
  14.         Flip
  15. Until AppTerminate()
  16. Miniaudio.KillDevice()
  17. End
  18.  
  19.  
  20. Function MyCallBack(Buffer:Byte Ptr, Frames%)
  21.         ' here you manipulate the sound:
  22.         If NOISE=1
  23.                 NOISE=2
  24.                 For Local i%=0 To 4*(frames-1)
  25.                         Buffer[i]=Rand(255)
  26.                 Next
  27.         ElseIf Zeit<MilliSecs()
  28.                 zeit=Zeit+1000
  29.                 For Local i%=0 To 4*(frames-1)
  30.                         Buffer[i]=Rand(255)
  31.                 Next
  32.         EndIf
  33. End Function
  34.  


to be continued....
« Last Edit: April 15, 2021, 13:10:32 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
Problems with Garbage Collector
« Reply #70 on: April 18, 2021, 14:06:56 »
I'm now testing the wrapper in various situations. Now I recognized that the app chrash after 12-40sec with one of this two message-boxes:

"Fatal error in GC: collecting from unknwon thread"

or

"Windows exception: EXCEPTION_BREAKPOINT"

When my callback contains nothing, the problem does not happen:

Code: BlitzMax
  1. Function MyCallBack(a%, Buffer:Byte Ptr, RecordingBuffer:Byte Ptr, Frames%)
  2.     ' do nothing
  3. End Function
  4.  


The problem only happens, if I write anything in the callback

Code: BlitzMax
  1. Function MyCallBack_II(a%, Buffer:Byte Ptr, RecordingBuffer:Byte Ptr, Frames%)
  2.         Print "START"
  3. End Function
  4.  
or...
Code: BlitzMax
  1. Function MyCallBack_II(a%, Buffer:Byte Ptr, RecordingBuffer:Byte Ptr, Frames%)
  2.         Local Sample:TBank=CreateStaticBank(Buffer,Frames*4)
  3.         local F%=Frames
  4. End Function
  5.  

It happens later, when I add a DELAY in my main loop:

Code: BlitzMax
  1. MiniAudio.SetDevice( Miniaudio.FORMAT_S16, 2, 48000, MyCallBack)
  2. ...
  3. MiniAudio.StartDevice()
  4. ...
  5. Repeat
  6.         Cls
  7.         DrawText "Hello World", 100,100
  8.         Delay 1
  9.         Flip
  10. Until AppTerminate()
  11. Miniaudio.KillDevice()
  12. End
...but it will happen!

When I add more code lines in the Callback  it happens earlier.

What do I forget?


The MiniAudio receives direct the adress of MyCallback:
Code: BlitzMax
  1.  
  2. Global MiniAudio:TMiniAudio=New TMiniAudio
  3. If MiniAudio.GetDeviceConfig(MiniAudio.PLAYBACK)=False
  4.         Notify "Drive not startet"
  5. EndIf
  6. MiniAudio.SetDevice( Miniaudio.FORMAT_S16, 2, 48000, MyCallBack_II)
  7.  
  8. Type TMiniAudio
  9.         Field  DeviceConfig: Byte Ptr, Device:Byte Ptr
  10.         ...
  11.         Method SetDevice(Format:Int, Channels:Int, SampleRate:Int, UserCallBackPointer: Byte Ptr)
  12.                 _SetDeviceConfig DeviceConfig, Format, Channels, SampleRate, UserCallBackPointer
  13.                 Device=GetDevice(DeviceConfig)
  14.         End Method
  15.  
  16.  
Code: [Select]
// Device Filler:
void MM_SetDeviceConfig (struct ma_device_config *config, int format, int channels, int hertz, ma_device_callback_proc *Callback) {
printf("FILL_VALUE_B \n");
config->sampleRate=hertz;
config->playback.format=format;
config->playback.channels=channels;

printf("set sample rate= %i \n", config->sampleRate);
config->dataCallback= Callback;
printf("set callback pointer= %i \n", config->dataCallback);
}

**** EDIT ****

Do I need a TBank "around" the pointer "Device" to prevent the RAM from beeing collected?

Code: BlitzMax
  1. Type TMiniAudio
  2.                 Field  DeviceConfig: Byte Ptr, Device:Byte Ptr, DeviceBank:TBank
  3.                                
  4.                 Method SetDevice(Format:Int, Channels:Int, SampleRate:Int, UserCallBackPointer: Byte Ptr)
  5.                         _SetDeviceConfig DeviceConfig, Format, Channels, SampleRate, UserCallBackPointer  ' InternCallBack
  6.                         Device=GetDevice(DeviceConfig)
  7.                         DeviceBank=CreateStaticBank(Device,24000)
  8.                 End Method
« Last Edit: April 19, 2021, 09:52:26 by Midimaster »
See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline col

  • Hero Member
  • *****
  • Posts: 590
Re: How to write a wrapper?
« Reply #71 on: April 19, 2021, 12:24:08 »
I think that if MiniAudio spins up any threads and they callback into BMax then you can register the thread with the BMax garbage collector. I'm not sure at the mo if that would be the solution but I do remember seeing somewhere about using other (external) threads with BMax need to be registered if you want garbage collection on them.

I can take a look later...

Offline Derron

  • Hero Member
  • *****
  • Posts: 3651
Re: How to write a wrapper?
« Reply #72 on: April 19, 2021, 19:53:33 »
when writing a (win32) DLL you call "InitBrl()" in this DLL to register / enable the GC .

InitBRL() calls "bbLibInit()" which is defined in appstub.mod/appstub.win32.c this way:
Code: [Select]
void bbLibInit() {
bbLibStartup(bbLibFile);
__bb_brl_appstub_appstub();
}

whith bbLibStartup being in blitz.mod/blitz_app.c:
Code: [Select]
void bbLibStartup(wchar_t * buf){

bbGCStartup();
bbThreadStartup();

bbGetAppFileDir(buf);

startup();
}


Maybe the C thread could call this stuff too?


bye
Ron

Offline Midimaster

  • Sr. Member
  • ****
  • Posts: 363
    • Midimaster Music Education Software
GC collector problems with C
« Reply #73 on: April 20, 2021, 10:16:52 »
1.
If I switch off the garbage collector via GCSupspend, the problem is gone. However, it is not sufficient to set the GCSuspend within the callback and switch on the garbage collector again with GCResume at the end of the callback. If I do it like this, the program will run longer, but still crash after 90-120sec.

It looks like The app only runs permanently when the garbage collector is completely switched off immediately before the main REPEAT loop. So far, tests are only 200 seconds long.

It is clear to me, however, that with this trick I am not fixing the code error, but rather preventing one of its consequences.

2.
The second problem seems to be the load on the computer due to the Main REPEAT loop. A waiting point is absolutely necessary there, which enables the program counter to jump back into the SYSTEM. So a DELAY 1 or the "FLIP without 0", or both. If the performance of the app in the task manager increases to values ‚Äč‚Äčover 20%, the program crashes. It can also be observed that the memory value GCMemAlloced() increases dramatically. If the DELAY is set, it also rises much more slowly.

A friend made the suggestion, that the parameter Callback-pointer maybe given in a wrong way to the function:
Code: [Select]
void MM_SetDeviceConfig (struct ma_device_config *config, int format, int channels, int hertz, ma_device_callback_proc *Callback)he says:
Quote
. ma_device_callback_proc is already a pointer to a function, i.e. a pointer to a pointer is expected at this point, which I don't think is correct. It should be sufficient to expect a ma_device_callback_proc at this point.
My skills are to low to answer this. What do you mean?


@Col
This sounds good, but there are zero informtions about this topic in the BltzMax resources, or?

@Derron
Thank you... but... I understand not one word of your answer.  ??? Is this related to Cols idea?








See my current project on PlayStore: 20Tracks-Audio-Player https://play.google.com/store/apps/details?id=midimaster.twentytrackd

Offline col

  • Hero Member
  • *****
  • Posts: 590
Re: How to write a wrapper?
« Reply #74 on: April 20, 2021, 10:53:54 »
You're in a deep area eh :)

It's unlikely that you'll find help in the BMax docs as learning C isn't for BMax to teach you, and it certainly won't teach you about how BMax works internally. Also, If you want to use C with BMax then BMax assumes you know C very well already - at least well enough to know how to do language interop.

Quote
A friend made the suggestion, that the parameter Callback-pointer maybe given in a wrong way to the function
Your friend is correct - I seem to remember mentioning something related to this already... check out reply #58

Your crashing issue is definitely related to the BMax GC not knowing about the thread that actually calls back into your BMax code - when in BMax and the thread tries to allocate from the GC then that unknown thread is trampling all over the GC's head. I've still not had time to look into this just yet to give you an accurate direction to pursue - Brucey would know off the top of his head as he wrote the interface for BMax to work with the GC in the first place. Derron's reply is related but specific to dll's so may or may not apply in this case. As an intelligent guess I would think there is are single functions to register and deregister a thread with the GC I just don't know without looking.
« Last Edit: April 20, 2021, 11:17:04 by col »

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal