views:

182

answers:

2

Hi, i am trying to play a stream in real time ( I keep appedning data to it as it comes in from a nexternal source) but no matter what FMOD doesn't want to carry on playing after the first chunk that got loaded, it seems as it is copying the memory stream/decoding it before playing, then as it is playing it doesn't use my stream anymore.

I am using the following to play my stream:

        var exinfo = new FMOD.CREATESOUNDEXINFO();
        exinfo.cbsize = Marshal.SizeOf(exinfo);
        exinfo.length = (uint)_buffer.Length;

        _result = System.createStream(_buffer, MODE.CREATESTREAM | MODE.OPENMEMORY_POINT , ref exinfo, ref _sound);
        FMODErrorCheck(_result);

        _result = System.playSound(FMOD.CHANNELINDEX.FREE, _sound, false, ref _channel);
        FMODErrorCheck(_result);

But no matter what, it only plays the amount of data that is in the stream at the point of calling playSound.

Can anyone know how to modify the buffer in real time? After the stream has started playing...?

+1  A: 

I would recommend you check out the "usercreatedsound" example that ships with FMOD, it should do what you require.

The basic idea is you define the properties of the sound you wish to play in the CreateSoundExInfo structure and provide it with callbacks which you can use to load / stream data from wherever you like.

Function pointer:

private FMOD.SOUND_PCMREADCALLBACK pcmreadcallback = new FMOD.SOUND_PCMREADCALLBACK(PCMREADCALLBACK);

Callback used to populate the FMOD sound:

private static FMOD.RESULT PCMREADCALLBACK(IntPtr soundraw, IntPtr data, uint datalen)
{
    unsafe
    {  
        short *stereo16bitbuffer = (short *)data.ToPointer();

        // Populate the 'stereo16bitbuffer' with sound data 
    }

    return FMOD_OK;
}

Code to create the sound that will use the callback:

// ...Usual FMOD initialization code here...

FMOD.CREATESOUNDEXINFO exinfo = new FMOD.CREATESOUNDEXINFO();

// You define your required frequency and channels
exinfo.cbsize            = Marshal.SizeOf(exinfo);
exinfo.length            = frequency * channels * 2 * 5; // *2 for sizeof(short) *5 for 5 seconds
exinfo.numchannels       = (int)channels;
exinfo.defaultfrequency  = (int)frequency;
exinfo.format            = FMOD.SOUND_FORMAT.PCM16;
exinfo.pcmreadcallback   = pcmreadcallback;

result = system.createStream((string)null, (FMOD.MODE.DEFAULT | FMOD.MODE.OPENUSER | FMOD.MODE.LOOP_NORMAL), ref exinfo, ref sound);

That should be sufficient to get you going, hope this helps.

Mathew Block
Thanks for the answer, will this work with ogg? beacuse i don't have the raw format of the files...
LnDCobra
No this solution is just for PCM, I will provide another answer for the case of compressed data.
Mathew Block
+1  A: 

If you wish to stream raw data, not PCM data you could achieve this by overriding the FMOD file system. There are two ways to achieve this, the first is by setting the file callbacks in the CreateSoundExInfo structure if this is for one specific file. The second is you can set the file system globally for all FMOD file operations (incase you want to do this with multiple files).

I will explain the latter, it would be trivial to switch to the former though. Refer to the "filecallbacks" FMOD example for a complete example.

Function pointers:

private FMOD.FILE_OPENCALLBACK  myopen  = new FMOD.FILE_OPENCALLBACK(OPENCALLBACK);
private FMOD.FILE_CLOSECALLBACK myclose = new FMOD.FILE_CLOSECALLBACK(CLOSECALLBACK);
private FMOD.FILE_READCALLBACK  myread  = new FMOD.FILE_READCALLBACK(READCALLBACK);
private FMOD.FILE_SEEKCALLBACK  myseek  = new FMOD.FILE_SEEKCALLBACK(SEEKCALLBACK);

Callbacks:

private static FMOD.RESULT OPENCALLBACK([MarshalAs(UnmanagedType.LPWStr)]string name, int unicode, ref uint filesize, ref IntPtr handle, ref IntPtr userdata)
{
    // You can ID the file from the name, then do any loading required here
    return FMOD.RESULT.OK;
}

private static FMOD.RESULT CLOSECALLBACK(IntPtr handle, IntPtr userdata)
{
    // Do any closing required here
    return FMOD.RESULT.OK;
}

private static FMOD.RESULT READCALLBACK(IntPtr handle, IntPtr buffer, uint sizebytes, ref uint bytesread, IntPtr userdata)
{
    byte[] readbuffer = new byte[sizebytes];

    // Populate readbuffer here with raw data

    Marshal.Copy(readbuffer, 0, buffer, (int)sizebytes);
    return FMOD.RESULT.OK;
}

private static FMOD.RESULT SEEKCALLBACK(IntPtr handle, int pos, IntPtr userdata)
{
    // Seek your stream to desired position
    return FMOD.RESULT.OK;
}

Implementation:

// Usual init code here...

result = system.setFileSystem(myopen, myclose, myread, myseek, 2048);
ERRCHECK(result);

// Usual create sound code here...
Mathew Block