0
0

I’ve seen this problem described in bits and pieces around these forums, but I’m still not exactly sure how to address it.

I want to make a double-buffered PCM stream play in FMOD, and use my custom decompressor to decompress directly into FMOD’s buffers so that I don’t have to duplicate space.

I create a Sound using
FMOD_OPENMEMORY_POINT
FMOD_CREATESTREAM
and point it at a buffer of X samples that’s already been filled with PCM data.

When creating the sound I specify that decodebuffersize is X/2 samples. From what I understand from the docs, this means that I’ll get a pcmreadcallback after X/2 samples are played, which means I’ll get one in the middle of the buffer playback, and one at the end.

While playing around with the usercreatesound example, I noticed that even if I modified the "decodebuffersize" value, the datalen passed in to the pcmreadcallback was still the same (and not anything like the decodebuffersize).

For example, in the provided code the decodebuffersize is 44100 but in the pcmreadcallback calls the datalen is 16384

[quote="FMOD Documentation: FMOD_CREATESOUNDEXINFO":1257jpfa]
[i:1257jpfa]decodebuffersize
[/i:1257jpfa]
[in] Optional. Specify 0 to ignore. For streams. This determines the size of the double buffer (in PCM samples) that a stream uses. Use this for user created streams if you want to determine the size of the callback buffer passed to you. Specify 0 to use FMOD’s default size which is currently equivalent to 400ms of the sound format created/loaded. [/quote:1257jpfa]

What I’m really looking for is a way to ask a Sound "how many buffers (out of your two) have you finished playing since the last time I asked?" but since it doesn’t look like that’s possible, I plan to use the pcmreadcallbacks to increment a semaphore for that purpose. I’m polling that semaphore at a very high rate, so my goal is that as soon as I discover that I’ve processed one side of a double-buffer, I decode a chunk of data into it while the other side plays.

The important things for me are:

1) Controlling whether or not (and how often) I do my decodes, knowing full well that I might inadvertently "starve" the FMOD stream and get artifacts/looping if I wait too long to update.
2) Having only the one double-buffer for each sound that is both decoded-into and played-from.

Am I missing some FMOD functionality that would help me achieve my goal here? I’m basically trying to get a better window into how FMOD stream double-buffering works I suppose, and if there’s any way I can figure out "where" in the provided OPENMEMORY_POINT buffer the sound is so I can take action at the halfway and endpoints.

  • You must to post comments
0
0

if you want fmod to use your decompressor why aren’t you creating a custom codec instead?

The way you are going about it is not intuitive at all and isnt going to work.

Check plugin_dev/codec_raw and you can make fmod use your codec by making one of these.
It doesnt have to be a dll either. You can hardcode it right into your app using System::createCodec and just pass it a FMOD_CODEC_DESCRIPTION with a bunch of callbacks.

  • You must to post comments
0
0

I don’t really want FMOD to use my decompressor per se, it would not be appropriate for the decompressor to be event-driven via callbacks.

I want to maintain control over that and just get better information about what part of a stream buffer FMOD is currently reading.

If there’s no way to get any more information than that, do you have any information about how FMOD_CREATESOUNDEXINFO.decodebuffersize should affect "datalen" in pcmreadcallbacks?

  • You must to post comments
0
0

Ok its just that you specifically said

[quote="Dogbert":1djv4083]I want to make a double-buffered PCM stream play in FMOD, and use my custom decompressor
[/quote:1djv4083]

if you just want to know how FMOD_CREATESOUNDEXINFO.decodebuffersize should affect "datalen" in pcmreadcallbacks, well it doesnt. The maximum read size you will get is 16kb. If you have a 32kb decodebuffersize, it will read twice at a time.

  • You must to post comments
0
0

Ah, I can see how I confused you.

Thanks for the info about decodebuffersize.

I decided to keep track of where in the doublebuffer FMOD is by monitoring the size of datalen in each of the callbacks and having a separate pointer that moves that far each time.

  • You must to post comments
0
0

[quote="brett":12d7pc14]The way you are going about it is not intuitive at all and isnt going to work.[/quote:12d7pc14]
Can you elaborate more on this statement please?

I’m curious is if part of what you’re mentioning is related to a problem I’m running into.

When I run my application, I’m finding that the "data" pointer passed into PCMReadCallback is not inside the buffer I supplied to createSound, even though I used OPENMEMORY_POINT.

-DEBUG- PCMReadCallback call: StreamingBuffer = 0x13e48a00, data = 0x147c76b0, datalen = 16384

Here’s my createSound and related code:
[code:12d7pc14] // 108ms of 44.1KHz data
// 216ms of 22KHz data
#define MONO_PCM_BUFFER_SAMPLES 10240
#define MONO_PCM_BUFFER_SIZE ( MONO_PCM_BUFFER_SAMPLES * sizeof( SWORD ) ) // SWORD is signed short

Buffer->DoubleBufferSize = MONO_PCM_BUFFER_SIZE * Wave->NumChannels * 2;  // size of streambuffer in bytes (2 buffers of roughly 100ms of sound each)
Buffer->StreamingBuffer = (BYTE*)appMalloc( Buffer->DoubleBufferSize );  // create the streaming buffer that FMOD will use
appMemzero( Buffer->StreamingBuffer, Buffer->DoubleBufferSize );  // zero it out (silence)

// Set up extended info
FMOD_CREATESOUNDEXINFO ExtendedSoundInfo;
appMemzero(&ExtendedSoundInfo, sizeof(FMOD_CREATESOUNDEXINFO));

ExtendedSoundInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
// these next three required by FMOD_OPENRAW
ExtendedSoundInfo.decodebuffersize = MONO_PCM_BUFFER_SIZE;  // roughly 100ms of mono data
ExtendedSoundInfo.defaultfrequency = Buffer->SampleRate;  // sampling rate of sound
ExtendedSoundInfo.numchannels = Buffer->NumChannels;  // number of channels
ExtendedSoundInfo.format = FMOD_SOUND_FORMAT_PCM16;  // must supply sound format
ExtendedSoundInfo.pcmreadcallback = &PCMReadCallback;  // callback for streaming reads
ExtendedSoundInfo.length = Wave->SampleDataSize;  // total size of the waveform  (Duration * SampleRate * Channels)
ExtendedSoundInfo.userdata = Buffer;  // UserData is a pointer to this object (used in PCMReadCallback to figure out which SoundBuffer's sound is calling the callback)

FMOD_MODE SoundFlags = FMOD_OPENMEMORY_POINT;  // supplying FMOD a pointer to a buffer, instead of a filename
SoundFlags |= FMOD_CREATESTREAM;  // will be a streaming sound
SoundFlags |= FMOD_OPENRAW;  // raw PCM data, does not have any WAV headers
SoundFlags |= FMOD_SOFTWARE;  // use FMOD software mixing
SoundFlags |= FMOD_3D;  // allow 3d spatialization
SoundFlags |= FMOD_3D_CUSTOMROLLOFF;  // allow for a custom rolloff model (in our case, no rolloff)

// Load the sound into FMod's internal buffers
FMOD_RESULT Result = AudioDevice->FModSystem->createSound(
    (char*) Buffer->StreamingBuffer,     // Data pointer
    SoundFlags,
    &ExtendedSoundInfo, // Extended info
    &Buffer->FModSound
    );[/code:12d7pc14]
  • You must to post comments
0
0

It doesn’t really make sense to use FMOD_CREATESTREAM with FMOD_OPENMEMORY_POINT, you should probably try just using FMOD_OPENMEMORY_POINT only. The ‘data’ pointer is probably just pointing to the stream buffer.

  • You must to post comments
0
0

My reasoning behind using CREATESTREAM was that I want FMOD to "play this 30-second sound and stop when it’s done, but only use 1 second’s worth of space." In this scenario, I would provide it the following CREATESOUNDEXINFO parameters:

createsoundexinfo.length: 30 seconds * samplingrate * number of channels
createsoundexinfo.decodebuffersize: 1 second * samplingrate * numberofchannels

I wanted to use OPENMEMORY_POINT because I want to be able to write the data into the streambuffer in presized chunks without using any extra storage.

This is why my original question was "how do I change datalen in pcmreadcallback to be some other number" so that I could make my decoder calls in the callback. If datalen cannot be changed to the preset size I spoke of (larger than 16k) that means I’ll need a separate "DecodedBuffer" that pcmreadcallback pulls from, which means memory usage at runtime.

Is the FMOD::Sound’s internal streambuffer location fixed and stable after the first pcmreadcallback? If so, I’ll might also try hijacking the address in "data" the first time it’s called and writing directly to each half of the double buffer.

I’ll also see what I can do to modify my decoder so that it can take arbitrary data sizes (so that it can be driven via pcmreadcallback) but even if that works the flow control seems as if it’s going to be the tricky part. If I just use OPENMEMORY_POINT without CREATESTREAM then it seems I’ll have to make my "sound" (stream) loop forever, set loopcount to CEIL( SoundSizeInBytes / DoubleBufferSizeInBytes ), and have my decoder zero-fill the extra space in the event there’s not enough sound data to fill the last buffer.

I appreciate your help, I fully realize I’m probably trying to shoehorn in my solution into the FMOD framework. I’m porting code that used to be using OpenAL which gives direct access to sound buffers. Ironically the existing code also "fakes" streaming sounds by using alCreateBuffer to make two halves of a doublebuffer and when the front buffer gets processed (monitored by BuffersProcessed) the data gets replaced and the front buffer is queued to play after the back is done, and vice-versa.

Being able to write directly to the streambuffer would go a long way toward maintaining functionality and compatibility with the existing code (even though FMOD doesn’t have any way to poll what half of the doublebuffer a stream is using).

  • You must to post comments
0
0

[quote="Dogbert":312yyduh]My reasoning behind using CREATESTREAM was that I want FMOD to "play this 30-second sound and stop when it’s done, but only use 1 second’s worth of space." In this scenario, I would provide it the following CREATESOUNDEXINFO parameters:
[/quote:312yyduh]

yes that is what a custom stream is for. You feed it from callbacks from your 1 second buffer.

[quote:312yyduh]
I wanted to use OPENMEMORY_POINT because I want to be able to write the data into the streambuffer in presized chunks without using any extra storage.
[/quote:312yyduh]

You’re assuming a flag does something it doesnt. If you want to write to a buffer, create your own buffer, but don’t expect fmod to ‘point’ to it.

[quote:312yyduh]
Is the FMOD::Sound’s internal streambuffer location fixed and stable after the first pcmreadcallback? If so, I’ll might also try hijacking the address in "data" the first time it’s called and writing directly to each half of the double buffer.
[/quote:312yyduh]

Don’t do this, it is pointless and i don’t see what you’re trying to save or achieve. The memory is trivial and you could be copying from an outside buffer instead of thinking you can use fmod’s buffer.

[quote:312yyduh]
I’ll also see what I can do to modify my decoder so that it can take arbitrary data sizes (so that it can be driven via pcmreadcallback) but even if that works the flow control seems as if it’s going to be the tricky part.
[/quote:312yyduh]

This is exactly what it should be doing. Absolutely every single codec inside fmod (and any well written codec you find on the net) – has the ability to ask for 1 sample at a time, or any arbitrary number of samples.

[quote:312yyduh]
I appreciate your help, I fully realize I’m probably trying to shoehorn in my solution into the FMOD framework. I’m porting code that used to be using OpenAL which gives direct access to sound buffers. Ironically the existing code also "fakes" streaming sounds by using alCreateBuffer to make two halves of a doublebuffer and when the front buffer gets processed (monitored by BuffersProcessed) the data gets replaced and the front buffer is queued to play after the back is done, and vice-versa.

Being able to write directly to the streambuffer would go a long way toward maintaining functionality and compatibility with the existing code (even though FMOD doesn’t have any way to poll what half of the doublebuffer a stream is using).[/quote:312yyduh]

We cannot let you write to a stream buffer. FMOD has to maintain the buffer itself to handle things like overflow bytes for interpolation prediction and it has to modify the ends of the buffer etc. This is not a feature we will be adding. I would suggest rethinking your approach or not trying to shoehorn old code into fmod which is based on a not very well written api – and maybe work out how to do it without huge hacks like it sounds like you are contemplating.

  • You must to post comments
0
0

Looks like that’s all the information I need, I’ll work on using CREATESTREAM driven by pcmreadcallbacks (just as FMOD intended :))

  • You must to post comments
Showing 9 results
Your Answer

Please first to submit.