Quit callback's calling from inside the callback => probl

Just starting out? Need help? Post your questions and find answers here.
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

Quit callback's calling from inside the callback => probl

Post by Froggerprogger »

Hi!
I've got a strange problem at the moment, which needs some more words to explain:

I just coded a small program, that plays a sound via a stream-callback using fmod (but you don't need to know fmod to understand my problem).
So there's a callback-procedure, that is called every time from the stream, when the stream-buffer is empty. The callback-procedure fills the buffer again with data. (using CopyMemory or leave it as it is)
I made this several times, and it always worked fine.

But now I tried to stop the stream, which is calling the callback-procedure, from the INSIDE of the callback-procedure to stop it exactly after one buffer-length.
When I start this program from PB, all is OK, and it works as expected.
But when I compile it to an EXE, the program hangs up immediately at the moment, when I stop the stream in this way.
[One more observation: The loopcounter increases + 1 from PB, from the EXE the program hangs up before I see the increased loopcount]

My question now:
Is this a problem of stdcall / cdecl or anything like that ? Is it perhaps unlogical to quit the 'callbacks caller' from the inside of the callback, or what is wrong with it, and why does it work from PB, but not from a compiled EXE ??

You can get the whole small (and well-commented) PB-code, a small WAV, the fmod.dll and a compiled EXE, here (193kB) : http://www.public.2mal2mal.de (samplelooper.zip)

Just run the program from PB or directly the EXE, choose the 'car.wav' to play, start it and then stop it through 'Stop immediately after actual loop'.

And here I post the callback-routine:

Code: Select all

Procedure.l Loop_Callback (*hStream, *Buffer, length, dummy)
  If loopcount <= 1 ; (load the data only the first two times to spare CPU-time - two times because of double-buffering -> *Buffer flips)
    FSOUND_Sample_Lock(*hSample, 0, sample_length_bytes, @*SourceData, @*Dummy2, @SourceDataLen, @Dummy3)
    If SourceDataLen = sample_length_bytes ; (just a small logical check)
      CopyMemory(*SourceData, *Buffer, SourceDataLen)
    EndIf
    FSOUND_Sample_Unlock(*hSample, @*SourceData, @*Dummy2, @SourceDataLen, @Dummy3)
  EndIf
  ;>>>>>>>>>>>>>>> NOW !
  ;>>>>>>>>>>>>>>> The next line stops this callback-procedure's calling stream
  If stopplaying : FSOUND_Stream_Stop(*hLoopStream) : EndIf ; (stops immediately after the actual loop, exactly BEFORE this one, which would be the next.)
  loopcount + 1
  ProcedureReturn continueplaying ; (if continueplaying is 0, then sound stops after listening to the other buffer a last time)
EndProcedure
Does anyone have an idea ? 8O
%1>>1+1*1/1-1!1|1&1<<$1=1
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

Post by Froggerprogger »

I tested it now on Win2k (before it was Win98), and here the program always hangs up when clicking on "Stop immediately after actual loop", even if started from PB.

But do you know, why ?
%1>>1+1*1/1-1!1|1&1<<$1=1
FloHimself
Enthusiast
Enthusiast
Posts: 229
Joined: Wed May 14, 2003 3:38 pm
Location: Lüneburg - Germany

Post by FloHimself »

Hi you should upload the generated UserLibraray for the fmod.dll ... :roll:
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

Post by Froggerprogger »

Oops.
Here you'll find it:
It is the PB-Import for fmod-version 3.61 (stdcall-DLL included), although
the actual version is 3.62 - but there's no PB-Import for it, yet.
(I need moooooore time to prepare it for Danilo...)

http://www.fmod.de/files/PureFmod_1.1.zip

When the import for 3.62 is finished, I'll send it to fmod.org, so it would be downloadable from there, too - if Brett places it there.

The resident-file including all FMOD-constants for direct access in PB is finished already, but because of PB's float-bug you cannot call the float-constants from it - so it is not online, yet.

OK - ready to try ?
%1>>1+1*1/1-1!1|1&1<<$1=1
FloHimself
Enthusiast
Enthusiast
Posts: 229
Joined: Wed May 14, 2003 3:38 pm
Location: Lüneburg - Germany

Post by FloHimself »

Froggerprogger wrote:OK - ready to try ?
While waiting for your post, i got it allready from fmod site :P

I checked your little proggy.. but it isn't easy to read. I would like to give you the advice to rewrite it to a more well formed style. :wink:
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

Post by Froggerprogger »

Not well formed ??
I think it is hyperstructured . :)

But you brought me to an idea !
I changed the code in this way, that it doesn't use the PB-Import of the fmod 3.61 any longer, but it opens the fmod.dll as a library and call all commands direct using CallFunction() now.
(So there was the problem of the PB-Bug "Calling a function out of a procedure crashes", so I have to take the functions out of the procedure, otherwise the program crashes.)

The whole code is now:

Code: Select all

;FSOUND-SAMPLE-Loop-Counter by Froggerprogger, 22nd May 2003
;
;Here's a way to get a callback exactly at each loopend of a looped SAMPLE:
;
;- Decompress sample-data from file into memory
;- Create a custom stream-callback with exact the same length of the sample-data
;- Copy the decompressed sample-data into the custom stream's buffer (mind the doublebuffering!)
;- Do anything inside the callback each time it is called

;____________ Declaration
Global samplerate.l           ; it's the global samplerate
Global loopcount.l            ; the loopcounter
Global *hSample.l             ; handle of the sample
Global *hLoopStream.l         ; handle of the created stream
Global sample_length_bytes.l  ; length of sampledata in bytes
Global continueplaying.l      ; this is directly the ProcedureReturn (0 = stop)
Global stopplaying.l          ; this is set to 0 to stop exactly inside the actual callback
Global *SourceData.l          ; Pointer to the Sampledata - returned from Sample_Lock
Global slewschange.l          ; just for the toggleable SillyLoopEndWindowSize-Change

;____________ Initialization
samplerate = 44100
loopcount = 0
continueplaying = 1
#FSOUND_16BITS = $10 : #FSOUND_STEREO = $40 

OpenLibrary(1, "fmod362std.dll")

If CallFunction(1, "_FSOUND_Init@12", samplerate,32,0) = #False
  MessageRequester("","Could not initialize FMOD. Stdcall-FMOD.DLL in the EXE's directory ?",0) : End
EndIf

;____________ The Loop-Callback-Procedure
Procedure.l Loop_Callback (*hStream, *Buffer, length, dummy)
  If stopplaying
    CallFunction(1, "_FSOUND_Stream_Stop@4",*hLoopStream)
  EndIf ;(stops immediately after the actual heard loop, exactly BEFORE playing this one, which would be the next hearable.)
  If loopcount < 2 ; (load the data only the first two times to spare CPU-time - two times because of double-buffering -> *Buffer flips)
    CopyMemory(*SourceData, *Buffer, sample_length_bytes)
  EndIf
  loopcount + 1
  If slewschange
    ResizeWindow(300 + Random(100), 205 + Random(100)) 
  EndIf
  ProcedureReturn continueplaying ; (WHY THIS VALUE IS IGNORED BY FMOD 3.62 ?? SETTING TO 0 HAS NO EFFECT!?)
EndProcedure

;____________ Create the main-window and put some gadgets onto in
hWnd = OpenWindow (1, 0,0,300,205, #PB_Window_SystemMenu | #PB_Window_ScreenCentered, "Froggerprogger's SAMPLE-Loop-Counter")
CreateGadgetList(hWnd)
ButtonGadget(1,5,25,290,20,"Start (again)")
ButtonGadget(2,5,67,290,20,"FSOUND_Stream_Stop immediately")
ButtonGadget(3,5,89,290,20,"FSOUND_Stream_Stop inside the next callback")
ButtonGadget(4,5,111,290,20,"Stop via callbackreturn=0 (doesn't work in fmod 3.62 ?)")
ButtonGadget(5,5,153,290,20,"Toggle SillyLoopEndWindowSizeChange On/Off")
TextGadget(6,0,183,290,20,"", #PB_Text_Center )

;____________ Get a soundfile's name
GetCurrentDirectory_(dirname.s, 255)
filename.s = OpenFileRequester("Choose a soundfile (e.g. try the car)",dirname+"car.wav","*.wav,*.mp3,*.mp2,*.ogg|*.wav;*.mp3;*.mp2;*.ogg|*.*|*.*",0)

;____________ Load the soundfile as a sample and calculate it's length in bytes
*hSample = CallFunction(1, "_FSOUND_Sample_Load@16",1,filename,0,0)
sample_modes = CallFunction(1, "_FSOUND_Sample_GetMode@4",*hSample)
sample_length_bytes = CallFunction(1, "_FSOUND_Sample_GetLength@4",*hSample)
If sample_modes & #FSOUND_16BITS : sample_length_bytes * 2 : EndIf
If sample_modes & #FSOUND_STEREO : sample_length_bytes * 2 : EndIf

;____________ Create a callback-stream with the sample's length
*hLoopStream = CallFunction(1, "_FSOUND_Stream_Create@20", @Loop_Callback(), sample_length_bytes, sample_modes, samplerate, 0)

;____________ Main-Loop
resume = 1
While resume
  Select WindowEvent()
    Case #PB_Event_Gadget
      Select EventGadgetID()
        Case 1 : stopplaying = 0 : continueplaying = 1 : 
                 CallFunction(1, "_FSOUND_Sample_Lock@28",*hSample, 0, sample_length_bytes, @*SourceData, @*Dummy2, @SourceDataLen, @Dummy3) ; get the pointer to the sample-data
                 CallFunction(1, "_FSOUND_Stream_Play@8", 1, *hLoopStream) : loopcount = 0 ; play it (again)
                 CallFunction(1, "_FSOUND_Sample_Unlock@20",*hSample, @*SourceData, @*Dummy2, @SourceDataLen, @Dummy3) ; just unlock again

        Case 2 : CallFunction(1, "_FSOUND_Stream_Stop@4",*hLoopStream) : continueplaying = 0 ; stop immediately
        Case 3 : stopplaying = 1 : actloop = loopcount : continueplaying = 0 ; stop immediately after the current loop
        Case 4 : continueplaying = 0 ; stop using Callback-ProcedureReturn 0
        Case 5 : slewschange ! 1 ; toggles the colorchange at loopend on/off
      EndSelect
   
    Case #PB_Event_CloseWindow  : resume = 0

    Default
      SetGadgetText(6, "Loop:"+Str(loopcount))
  EndSelect
  
  Delay(1)
Wend

;____________ Prepare program's end
CallFunction(1, "_FSOUND_Stream_Stop@4",*hLoopStream)
CallFunction(1, "_FSOUND_Close@0")
End


What I did then was trying this code with the stdcall - fmod.dll 3.61 and 3.62. (just change the name in OpenLibrary())

--> Tataaa. The behaviour is different:
With 3.61 'Stop after actual double-buffered loop' works fine and 'Stop immediately after actual loop' has no effect.
With 3.62 'Stop after actual double-buffered loop' has no effect anymore and 'Stop immediately after actual loop' WORKS FINE !

--> So there was no problem with PB, it was a bug in the old fmod.dll 3.61
But now I have 2 new problems and 1 more strange question:
poblem 1.) Why does the return-value 0 has no effect any longer ? FMOD-docu 3.62 says it should works - hmm.
problem 2.) I have to prepare the new FMOD.DLL 3.62 - Import AS FAST AS POSSIBLE . Damn. :wink:

question 1.) I call the FSOUND_Stream_Stop-function from the inside of the callback using CallFunction() AND THERE'S NO PROBLEM. Why ? I had to put the Lock an Unlock-functions out of the loop, because it crashed, so the PB-CallFunction-from-Procedure-Bug seems not to exist always?

:?

thank you for your impression FloHimself !
%1>>1+1*1/1-1!1|1&1<<$1=1
FloHimself
Enthusiast
Enthusiast
Posts: 229
Joined: Wed May 14, 2003 3:38 pm
Location: Lüneburg - Germany

Post by FloHimself »

Froggerprogger wrote:Not well formed ??
I think it is hyperstructured . :)
So let me explain just a few things, i was thinking of. Maybe most doesn't matter on only a 'few lines' of code like this ..or maybe.. it simply doesn't matter :P :

I would not define constants in a row like this:

Code: Select all

#FSOUND_16BITS = $10 : #FSOUND_STEREO = $40 
because its hard to read and find when you have to change or check a constant later.
btw i would not use the ":" operator that much, because this:

Code: Select all

      Select EventGadgetID()
        Case 1 : stopplaying = 0 : continueplaying = 1 : FSOUND_Stream_Play(1, *hLoopStream) : loopcount = 0 ; play it again
        Case 2 : FSOUND_Stream_Stop(*hLoopStream) : continueplaying = 0 ; stop immediately
        Case 3 : continueplaying = 0 ; stop using Callback-ProcedureReturn 0
        Case 4 : stopplaying = 1 : actloop = loopcount : continueplaying = 0 ; stop immediately after the current loop
isn't very clearly.

In addition, you are using three global vars to control the procedure, so these states / values can only be tracked badly. They are not needed.

Let's have a look at the FMOD API
DLL_API FSOUND_STREAM * F_API FSOUND_Stream_Create(
FSOUND_STREAMCALLBACK callback,
int lenbytes,
unsigned int mode,
int samplerate,
int userdata
);

callback A pointer to a user defined stream callback function.
An example of the callback function would look like this.
void streamcallback(FSOUND_STREAM *stream, void *buff, int len, int param)
{
return TRUE;
}
len in this callback is specified in BYTES.
The return value is whether to end the stream or not. Returning FALSE will
terminate the stream.

lenbytes Size of the data in BYTES the callback will require to be written to the
buffer.

mode Description of the raw sample data being opened. see FSOUND_MODES for
a description of these modes.

samplerate Rate of playback. Be careful you dont set the sample rate too high
so that the stream servicer (ie harddisk) cant keep up. A sound that has
a default rate of 44.1khz that is played at 132khz will have to read the
hard disk 3 times faster than normal.

userdata data value that is passed into the stream callback when playing.
So you can give the callback a parameter with data that you like to have accessable in the procedure.. maybe to control the states. Why not define a Struct and Procedure like:

Code: Select all

Structure callback_state
  state.l     ;  (IN to Procedure)   1 = stop immeadately | 2 = stop after double buffered loop | 3 = stop after actual loop
  loopNr.l    ;  (OUT from Procedure)  Number of current loop
EndStructure

Procedure.l Loop_Callback(*hStream, *Buffer, length, *control.callback_state)
; ...
EndProcedure
and pass it to the callback with:

Code: Select all

control.callback_state
*hLoopStream = FSOUND_Stream_Create(@Loop_Callback(), sample_length_bytes, sample_modes, samplerate, @control)
now you can set the 'state' and get the 'loops' from outside the procedure with one handy struct.

Wanna more criticism? (please don't get me wrong :D ..this is getting to long.. hard to explain all that stuff in english)
FloHimself
Enthusiast
Enthusiast
Posts: 229
Joined: Wed May 14, 2003 3:38 pm
Location: Lüneburg - Germany

Post by FloHimself »

Froggerprogger wrote: It is the PB-Import for fmod-version 3.61 (stdcall-DLL included), although
the actual version is 3.62 - but there's no PB-Import for it, yet.
(I need moooooore time to prepare it for Danilo...)
Btw why don't you use the DLL-IMPORTER to produce a new PB-Import file? :wink:
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

Post by Froggerprogger »

Hey, yes, hit me!
The tip with the structure is damn good !
I'll use it next time.
Some more ?

btw:
Any idea, why
CallFunction(1, "_FSOUND_Stream_Stop@4",*hLoopStream)
from inside the Procedure is OK, but
CallFunction(1, "_FSOUND_Sample_Lock@28",*hSample, 0, sample_length_bytes, @*SourceData, @*Dummy2, @SourceDataLen, @Dummy3)
crashes the program ?
%1>>1+1*1/1-1!1|1&1<<$1=1
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

Post by Froggerprogger »

DLL-IMPORTER ?

From another forum here:

a) Copy the *.lib in the PureLibraries\Windows\Libraries
b) Write a .pbl file for this DLL
c) Use the file made with "DLL Import" and copy it to PureLibraries\Windows\

2 questions (even if slowly going off main-topic) :
a) which .lib-file ?
b) for fmod 3.61 I made a .pdi and no .pbl file
%1>>1+1*1/1-1!1|1&1<<$1=1
FloHimself
Enthusiast
Enthusiast
Posts: 229
Joined: Wed May 14, 2003 3:38 pm
Location: Lüneburg - Germany

Post by FloHimself »

Froggerprogger wrote:Hey, yes, hit me!
The tip with the structure is damn good !
I'll use it next time.
Some more ?
thx ;)
your variable naming.. sometimes you use vars like "your_variable", sometimes it is "yourvariable".
maybe you google for 'variable naming conventions' and look at different coding styles and pick one that pleases most.
no more suggestions at the moment.. :D
Froggerprogger wrote: btw:
Any idea, why
CallFunction(1, "_FSOUND_Stream_Stop@4",*hLoopStream)
from inside the Procedure is OK, but
CallFunction(1, "_FSOUND_Sample_Lock@28",*hSample, 0, sample_length_bytes, @*SourceData, @*Dummy2, @SourceDataLen, @Dummy3)
crashes the program ?
Can't remember that in detail.. but there was a problem with callfunction inside / outside a procedure.. but can't say.. maybe it is even another problem.. null-pointer? don't know..
FloHimself
Enthusiast
Enthusiast
Posts: 229
Joined: Wed May 14, 2003 3:38 pm
Location: Lüneburg - Germany

Post by FloHimself »

Froggerprogger wrote:DLL-IMPORTER ?

From another forum here:

a) Copy the *.lib in the PureLibraries\Windows\Libraries
b) Write a .pbl file for this DLL
c) Use the file made with "DLL Import" and copy it to PureLibraries\Windows\

2 questions (even if slowly going off main-topic) :
a) which .lib-file ?
b) for fmod 3.61 I made a .pdi and no .pbl file
off topic? naaaah.. ;)

Read the Readme.txt in the DLL IMPOTER directory.

All you need is:
1) the fmod.dll
2) write a pbl file like explained in readme
3) and the DLL IMPORTER
to generate the LIB file.

to anwser your question:
a) the generated with the DLL IMPORTER
b) you will need a file like discribed in readme and i will bed it will work ;]
FloHimself
Enthusiast
Enthusiast
Posts: 229
Joined: Wed May 14, 2003 3:38 pm
Location: Lüneburg - Germany

Post by FloHimself »

just tested the DLL-IMPORTER with fmod.dll and it won't work, bacause fmod exports have the '@' sign in function name. PB do not allow to use '@' in function names, so you can generate the lib, but can't call the functions like ''FSOUND_Init@12_(param1,param1,param1)" :?
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

Post by Froggerprogger »

Arrgh. That's the reason why it doesn't work !
I tried until now, too.

Hmmm. For the last FMOD-Import Danilo needed a .pdi - file with a content of something like:

DECLARE FSOUND_Init@12_ () AS FSOUND_Init() : LONG , LONG , LONG

And then he did the rest :roll:

AND: With that you can call all FMOD-functions without the '_' after the functionname.

Hmmmm. Perhaps another tool ?
%1>>1+1*1/1-1!1|1&1<<$1=1
Post Reply