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. |