0
0

Hi,

I’m trying to generate a sinusoidal wave with a callback with C# but it seems as if the callback was only called once, so when the delay at the end of the callback ends the sound ends also. Any ideas?

Here is the code:

[code:2o0tgnvz]using System;
using System.Runtime.InteropServices;

public delegate void DelegateStreamCallback(IntPtr FSOUND_STREAM, IntPtr buff, int len, ref IntPtr userdata);

public class EnumReportApp
{
[DllImport("fmod.dll")]
public static extern bool FSOUND_Init(int mixrate, int maxsoftwarechannels, FSOUND .Enums .FSOUND_INIT_FLAGS flags);

[DllImport("fmod.dll")]
public static extern bool FSOUND_SetBufferSize(int len_ms);

[DllImport("fmod.dll")]
public static extern void FSOUND_Close();

[DllImport("fmod.dll")]
public static extern IntPtr FSOUND_Stream_Create(DelegateStreamCallback myCallBack, int lenbytes, FSOUND.Enums.FSOUND_MODES mode, int samplerate, IntPtr userdata);

[DllImport("fmod.dll")]
public static extern IntPtr FSOUND_Stream_Open(string name_or_data, FSOUND.Enums.FSOUND_MODES mode, int offset, int length);

[DllImport("fmod.dll")]
public static extern int FSOUND_Stream_Play(int channel, IntPtr FSOUND_STREAM_stream);

[DllImport("fmod.dll")]
public static extern bool FSOUND_Stream_Stop(IntPtr FSOUND_STREAM_stream);

[DllImport("fmod.dll")]
public static extern bool FSOUND_Stream_Close(IntPtr FSOUND_STREAM_stream);

[DllImport("fmod.dll")]
public static extern bool FSOUND_Stream_SetMode(IntPtr FSOUND_STREAM_stream, FSOUND.Enums.FSOUND_MODES mode);


public static double  angulo;
public static int freq = 200;
const double pi = 3.1415926;

public static void Main()
{

    IntPtr Stream = new IntPtr(0);
    int Channel;

    DelegateStreamCallback myCallBack = new DelegateStreamCallback(Genera_Onda);        

    FSOUND_Init(44100, 16, 0);
    FSOUND_SetBufferSize(100);
    Stream  = FSOUND_Stream_Create(myCallBack, 88200, FSOUND.Enums.FSOUND_MODES.FSOUND_16BITS | FSOUND.Enums.FSOUND_MODES .FSOUND_STEREO   , 44100, IntPtr.Zero);

    FSOUND_Stream_SetMode(Stream, FSOUND.Enums.FSOUND_MODES.FSOUND_LOOP_NORMAL);
    Channel = FSOUND_Stream_Play(0, Stream);


    for (Channel  = 0; Channel  < 50; Channel ++)
    {
        freq++;
        System.Threading.Thread.Sleep(100);
    }

    FSOUND_Stream_Stop(Stream);
    FSOUND_Stream_Close(Stream);

}

   public static void  Genera_Onda(IntPtr stream, IntPtr buffer, int length, ref IntPtr userdata)
    {
        int sample_act = 0;
        int sample_last = (length - 1) / 2;
        const double pi = 3.141526;

        Int16 resultado;
        int offset;

        Int16[] bufferTmp = new Int16[length / 2];

        while (sample_act < sample_last)
        {
            angulo += (2 * pi * freq  / 44100);
            if (angulo > 2 * pi) { angulo = angulo - 2 * pi; }; 
            resultado = Convert.ToInt16(32767 * Math.Sin(angulo)); 

            for (offset = 0; offset < 2; offset++) 
            {
                bufferTmp[sample_act + offset] = resultado;
            }
            sample_act+=2;
        }
        System.Runtime.InteropServices.Marshal.Copy(bufferTmp, 0, buffer, length / 2);
        System.Threading.Thread.Sleep(100); 

    }

}

[/code:2o0tgnvz]

  • You must to post comments
0
0

The compiler complains if I add volatile to angulo, and there isn’t any diference if I add it to the definition of freq.

Solved the problem with the sound adding a call to the FMOD_Update function every time the frequency is changed. A perfect sound now, no more BEEPS.

I think is better to reproduce all the code with the new changes:

[code:y5vtrnby]using System;
using System.Runtime.InteropServices;

public class EnumReportApp
{
[DllImport("fmod.dll")]
public static extern bool FSOUND_Init(int mixrate, int maxsoftwarechannels, FSOUND .Enums .FSOUND_INIT_FLAGS flags);

[DllImport("fmod.dll")]
public static extern bool FSOUND_SetBufferSize(int len_ms);

[DllImport("fmod.dll")]
public static extern void FSOUND_Close();

[DllImport("fmod.dll")]
unsafe public static extern IntPtr FSOUND_Stream_Create( DelegateStreamCallback CallBack, int lenbytes, FSOUND.Enums.FSOUND_MODES mode, int samplerate, IntPtr userdata);

[DllImport("fmod.dll")]
public static extern IntPtr FSOUND_Stream_Open(string name_or_data, FSOUND.Enums.FSOUND_MODES mode, int offset, int length);

[DllImport("fmod.dll")]
public static extern int FSOUND_Stream_Play(int channel, IntPtr FSOUND_STREAM_stream);

[DllImport("fmod.dll")]
public static extern bool FSOUND_Stream_Stop(IntPtr FSOUND_STREAM_stream);

[DllImport("fmod.dll")]
public static extern bool FSOUND_Stream_Close(IntPtr FSOUND_STREAM_stream);

[DllImport("fmod.dll")]
public static extern void FSOUND_Update();

unsafe public delegate void DelegateStreamCallback(ref IntPtr FSOUND_STREAM, IntPtr buff, int len, ref IntPtr userdata);
unsafe public static  DelegateStreamCallback myCallBack = new DelegateStreamCallback(EnumReportApp.Genera_Onda);

public static  double angulo;
public static  int freq = 100;
const double pi = Math.PI;

unsafe public static void Main()
{
        IntPtr i = new IntPtr(0);
        IntPtr Stream = new IntPtr(0);
        int Channel;

    FSOUND_Init(44100, 16, FSOUND.Enums .FSOUND_INIT_FLAGS .FSOUND_INIT_STREAM_FROM_MAIN_THREAD );
    FSOUND_SetBufferSize(100);
    Stream  = FSOUND_Stream_Create( EnumReportApp.myCallBack, 128000, FSOUND .Enums .FSOUND_MODES .FSOUND_NORMAL | FSOUND.Enums.FSOUND_MODES.FSOUND_16BITS | FSOUND.Enums.FSOUND_MODES .FSOUND_STEREO   , 44100, IntPtr.Zero);

    Channel = FSOUND_Stream_Play(0, Stream);

    for (Channel  = 0; Channel  < 100000; Channel ++)
    {
      FSOUND_Update();
      freq++;
      Console.WriteLine(freq);   
    }

    FSOUND_Stream_Stop(Stream);
    FSOUND_Stream_Close(Stream);
}
public static void  Genera_Onda(ref IntPtr stream, IntPtr buff, int length, ref IntPtr userdata)
    {
        int sample_act = 0;
        int sample_last = (length - 1) / 2;
        const double pi = 3.141526;

        Int16 resultado;
        int offset;

        Int16[] bufferTmp = new Int16[length / 2];

        while (sample_act < sample_last)
        {
            angulo += (2 * pi * freq  / 44100);
            if (angulo > 2 * pi) { angulo = angulo - 2 * pi; }; 
            resultado = Convert.ToInt16(32767 * Math.Sin(angulo)); 

            for (offset = 0; offset < 2; offset++) 
            {
                bufferTmp[sample_act + offset] = resultado;
            }
            sample_act+=2;
        }
        System.Runtime.InteropServices.Marshal.Copy(bufferTmp, 0, buff, length / 2);
        Console.WriteLine("callback");
    }

}[/code:y5vtrnby]

The only problem now is that if I take out the call to the Console class inside the callback this is only reproduced once.

Thank you for your help bluemonkmn.

  • You must to post comments
0
0

I can’t find the way to solve the "only one call to the callback" problem. I’ve found this document from MS:

[url:zzb59oge]http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemGCClassKeepAliveTopic.asp[/url:zzb59oge]

in the direction that bluemonkmn pointed out some posts ago. So I’ve added the lines:

[code:zzb59oge]GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();[/code:zzb59oge]

and after the call to the Stream_Create:

[code:zzb59oge]GC.KeepAlive(EnumReportApp.myCallBack);[/code:zzb59oge]

But nothing changes. Curiously, MS also uses:

[code:zzb59oge]Console.WriteLine(message);[/code:zzb59oge]

So I’m begining to think that this is the only way to keep it alive.

Anyone?, bluemonkmn?.

  • You must to post comments
0
0

I have seen problems passing delegates stored in local variables to unmanaged code. Once it has been passed and gone out of scope, I think .NET believes that it’s no longer in use and cleans up the delegate so it no longer works. I mentioned this problem in the fmodex forums, but nobody had any reply. So I’m not sure if it’s an fmod problem or a fundamental code/design problem. In any case, try storing the delegate in a global variable (or any variable that doesn’t go out of scope) to see if that fixes the problem.

  • You must to post comments
0
0

I’m stumped. The only thing I can think of now is debugging FMOD source code to see what’s happening on that end.

  • You must to post comments
0
0

Thank you for reply bluemonkmn.

Yes, It also sounds to me that is a problem of scope. The case is I’ve declared everything inside the same class with the "public" statement, so everything should be available from everywhere.

In my last test I paused the program execution at the next line of code before the call to the play function and it can be listened a "BEEP pause BEEP pause BEEP" pattern of sound (the BEEPS corresponds to the sinusoidal wave at the frequency fixed). But it ends when I execute the next line, so still it doesn’t work.

Any suggestion?

  • You must to post comments
0
0

Solved adding this line inside the callback:

[code:hzb4tnev]System.GC.SuppressFinalize(myCallBack);[/code:hzb4tnev]

What I want to do now is reproduce exactly this program in a PDA with W2003 and Compact Framework 2.0, but what work for WinXP doesn’t work for Windows Mobile 2003.

Any expert in Windows Mobile?

  • You must to post comments
0
0

You have a variable "myCallback" declared as:

[code:19lhm47h]DelegateStreamCallback myCallBack = new DelegateStreamCallback(Genera_Onda);[/code:19lhm47h]

And I thought that maybe this variable was going out of scope because it was a local variable. But now I see that it is in the main function, so it should last for the whole program run. Then I remembered another problem. The .NET optimizer will allow the garbage collector to destroy objects that are never referenced again. After you set the callback in FMOD with FSOUN_Stream_Create, myCallBack is never referenced by the managed code again, so it might be optimized after that into a state that would allow it to be destroyed. The problem should not be visible in debug mode, though, because I don’t think debug mode performs these kinds of optimizations. Are you running in debug mode?

To test this theory, try adding the following line to the end of your main function:
[code:19lhm47h]GC.KeepAlive(myCallBack);[/code:19lhm47h]

  • You must to post comments
0
0

What a surpise, P/Invoke on the .NET Compact Framework does not support callbacks.

[url:pijjulbn]http://msdn2.microsoft.com/en-us/library/aa446536.aspx[/url:pijjulbn]

I’ll have to wait to .NET 3

  • You must to post comments
0
0

I’ve tested adding this line of code at the end or at the begining of main, and it does the same, a BEEP that last as long as it reads the buffer of the first call to the callback.

Yes, I was executing in debug mode, and you are right bluemonkmn, it works diferent. In debug mode I can see two calls to the callback before the sound ends, instead one call from the EXE.

I’ve tried also with the unsafe param, but still does’n work.

Do you think it would be a good idea to work with old style pointers?

  • You must to post comments
0
0

If it works correctly in debug mode, I’m confused that it didn’t work correctly in release mode with the KeepAlive at the end of the main function. Do you know if the callback was called twice, and maybe you just couldn’t tell because something else wasn’t working? Can you verify with a debug log/file or something how many times the callback function is called in release mode?

  • You must to post comments
0
0

I’m sorry, I explained bad myself. It doesn’t work correctly in debug mode, I just could see two calls to the callback, so I could listen two BEEPs instead one from the exe. But I think that, as you said, in debug mode can’t be emulated correctly some calls.

Following your advice of making a log inside the callback I’ve introduced this line at the end of this function:

[code:16x4p2bb]Console.WriteLine("callback f:" + freq + "angulo:" + angulo);[/code:16x4p2bb]

Surprisingly, it works now. Not as I expected, becouse it still does a BEEP-BEEP instead a continous sound, but I can see the lines printed in the console every time the callback is called. Probably, the fact of introducing this line of code has made it more persistent. Do you think it is posible to do the same with a more eficient way?

  • You must to post comments
0
0

Well, if simply displaying some values made it work better, then maybe it still has something to do with .NET reference tracking. You could perform a more efficient test by referencing those two values with KeepAlive instead of writing them to the console:
[code:1w2bq12a]GC.KeepAlive(freq);
GC.KeepAlive(angulo);
[/code:1w2bq12a]

  • You must to post comments
0
0

It seems that the keepalive statements don’t make any diference. I tried to reduce what is written down to just a text message, without any variable, and the result is the same as if I were including "angulo" and "freq". May be what works is the fact of having a call to a function inside the callback.

  • You must to post comments
0
0

Well, I’m running out of ideas. But I have one good idea left: perhaps you should try applying the "volatile" keyword to your freq and angulo declarations:
[code:dtcrtjxe] public static volatile double angulo;
public static volatile int freq = 200;[/code:dtcrtjxe]
Another thing to investigate further is this: you said that even in debug mode, the code didn’t change the pitch of the BEEP? Can you see in debug mode (when this is happening) if the values of freq and angulo are changing? Also, what happens if you remove the sleep(100) from the callback?

BTW, your value for pi is wrong: it should be 3.1415926, or use Math.PI instead. Of course this isn’t responsible for the problem :wink:.

  • You must to post comments
Showing 14 results
Your Answer

Please first to submit.