Blitz not suitable for callbacks from APIs?
BlitzMax Forums/BlitzMax Programming/Blitz not suitable for callbacks from APIs?| 
 | ||
| I'm writing a callback in Blitz to create realtime sound effects with FMOD. All is fine until I try to iterate through a list with EACHIN, and it just will not iterate through the list. I have checked the list is non-null inside the callback, and the same code iterates fine outside the callback. So can I use blitz for these callbacks, and if so, what are the limitations? | 
| 
 | ||
| John, I am heavily using BlitzMAX callback functions from within Lua - and never had any problems which were related to the fact, that my functions weren't invoked from within BlitzMAX directly. On the other hand: I never used BlitzMAX's built-in lists... | 
| 
 | ||
| Call GCSuspend before calling the function that calls your callbacks, then call GCResume when it is done. | 
| 
 | ||
| Josh, is this a general recommendation? What could happen if you don't suspend the GC? Local variables should not be collected, right? How would suspend/resume affect the overall performance? | 
| 
 | ||
| Nope- it plain refuses to iterate, even with Josh's suggestion. If I replace it with an array, the array reports as zero length within the callback, even though it reports 1 outside. It makes no odds whether the list or array is declared as a type global or plain old global. Function pcmreadcallback:Int(sound:TFMODSound, data:Byte Ptr, dataLen:Int)
	'Local f1:Float=1600.0
	'Local f2:Float=2400.0
	Global floatStream:Float[]=New Float[1]
	Local count:Int
	'Global f1phase:Float=0.0
	'Global f2phase:Float=0.0
	Local tonal:Ttonal=Null
	Local toneIndex:Int=0    	
    	Local stereo16bitbuffer:Short Ptr = Short Ptr (data);
	Local samplesPerChannel:Int=dataLen Shr 2 ' shr2 = 16bit stereo (4 bytes per sample)
	
	If floatStream.length<samplesPerChannel
		floatStream=New Float[samplesPerChannel]
	EndIf
	globcount=0
    	For count=0 Until samplesPerChannel 
		floatStream[count]=0.0
		
		'listvar=Ttonal._list
		
		Rem
		globCount=Ttonal._listArray.length		
		For toneIndex=0 Until Ttonal._listArray.length
			tonal=Ttonal(Ttonal._listArray[toneIndex])
			floatStream[count]:+tonal._amp*Sin(tonal._phi)
		
			tonal._phi:+tonal._dphi
			globcount:+1
			While tonal._phi>=360.0
        			tonal._phi:-360.0
			Wend
		Next
		EndRem
		
		For tonal=EachIn _list
			floatStream[count]:+tonal._amp*Sin(tonal._phi)
		
			tonal._phi:+tonal._dphi
			globcount:+1
			While tonal._phi>=360.0
        			tonal._phi:-360.0
			Wend
		Next
		
		stereo16bitbuffer[count*2] = makeSignedShort(floatStream[count])    'Left channel
        	'stereo16bitbuffer[count*2+1] = makeSignedShort( (Sin(f2phase) * 32767.0 ) )  'Right channel
		
        	Rem
		f1phase :+ 360.0*f1*(1.0/44100.0);
		While f1phase>=360.0
        		f1phase:-360.0
		Wend
		
		f2phase :+ 360.0*f2*(1.0/44100.0);
		While f2phase>=360.0
        		f2phase:-360.0
		Wend
		EndRem
	Next
	
    Return FMOD_OK;
End Function | 
| 
 | ||
| Also make sure your callback is the right protocol.  You have "win32" and "c".  I forget what the C equivalent is, something to do with STD's. | 
| 
 | ||
| Hey, thanks again, Josh. How do I declare my function as, say, win32? | 
| 
 | ||
| "win32" is the equivalent to C's stdcall. | 
| 
 | ||
| How do a declare a blitz function to be "win32" though? | 
| 
 | ||
| function whatever() "win32" endfunction | 
| 
 | ||
| You can use it fine from callbacks... unless the callback occurs out of the main thread. Specifically, pcmreadcallback() is being called from a different thread. You may want to try enabling multi-threading if you intend to use that. It's basically an issue with BlitzMax's default garbage collector, in that it is not thread-safe. | 
| 
 | ||
| Thanks, Brucey. Unfortunately it started complaining about Bah.fmod when I tried to build in threaded mode. I tried rebuilding the mods in threaded mode to no avail. After a couple of hours I just decided it was easier to just write the callback in C. | 
| 
 | ||
| How did you do this?  Did you import a function from an imported .c file and send that to fmod?  I could use something like this for my collision callbacks. | 
| 
 | ||
| Hi Josh... that is exactly what I'm doing. It's all working fine, and I assume I will be able to make c calls to set variables in my callback so I can change the sound properties, but I haven't implemented that part yet. I actually got the idea from a thread of yours way back. 
/*cbit.c*/
#include "/Developer/FMOD Programmers API Mac/api/inc/fmod.h"
#include "/Developer/FMOD Programmers API Mac/api/inc/fmod_errors.h"
#include "/Developer/FMOD Programmers API Mac/examples/common/wincompat.h"
#include <math.h>
FMOD_RESULT F_CALLBACK pcmreadcallback(FMOD_SOUND *sound, void *data, unsigned int datalen)
{
    unsigned int  count;
    static float  t1 = 0, t2 = 0;        // time
    static float  v1 = 0, v2 = 0;        // velocity
    signed short *stereo16bitbuffer = (signed short *)data;
    for (count=0; count<datalen>>2; count++)        // >>2 = 16bit stereo (4 bytes per sample)
    {
        *stereo16bitbuffer++ = (signed short)(sin(t1) * 32767.0f);    // left channel
        *stereo16bitbuffer++ = (signed short)(sin(t2) * 32767.0f);    // right channel
        t1 += 0.01f   + v1;
        t2 += 0.0142f + v2;
        v1 += (float)(sin(t1) * 0.002f);
        v2 += (float)(sin(t2) * 0.002f);
    }
    return FMOD_OK;
}Part of the main blitz file... Import "cbit.c" Extern Function pcmreadcallback:Int(sound:TFMODSound, data:Byte Ptr, dataLen:Int) 'FMOD_RESULT F_CALLBACK pcmreadcallback(FMOD_SOUND *sound, void *data, unsigned Int datalen) End Extern createsoundexinfo.SetPCMReadCallback (pcmreadcallback) 'User callback For reading. | 
| 
 | ||
| Hmm... word of warning... my callback refuses to see any variables defined outside the callback, whether written in blitz or c. |