We are currently using FMOD for a music oriented game. We currently have nice functionalities that let the user play around with a music sample. Setting the loop point, play it backward, forward etc. FMOD handles it quite nicely. Now we are trying to implement a function that would simply plays a loop once.
For example the user would set his loop points, press play, then our application would simply stop at the end of the loop.
Although, it is not that much simple. Here is the basic set-up and the solutions that we tried:
1. We load a sound file into memory
2. We let the user play around with loops points
3. The user play the loop
(That works just fine)
Problem: How can we know that a Loop finished playing.
1. Use a timer to stop the loop: Lead to inaccuracies, the loop is stop too late, because I don’t have sample accuracy but ms accuracy…
- Use channel callback function
– FMOD_CHANNEL_CALLBACKTYPE_SYNCPOINT : Not good only updated when we call system::update, not accurate enough
- Get the data from the loop of the sound, create an OPEN_MEMORY, OPEN_RAW sound with the loop
SNDFILEERRCHECK(m_sound->lock(m_curLoop.start * bytesPerSample * channels, byteLen, &ptr1,&ptr2,&len1,&len2),this);
SNDFILEERRCHECK( m_system->createSound((const char *)data, FMOD_SOFTWARE | FMOD_OPENRAW | FMOD_OPENMEMORY, &exinfo, &subSound), this); SNDFILEERRCHECK(m_sound->unlock(ptr1,ptr2,len1,len2),this);
This could potentially work but it is not efficient because we are loading the "loop" in memory again. Why not using FMOD_CREATESTREAM, well because we want the ability of playing the sound backward… which is not possible on a stream.
Finally, by reading other related, the only solution I see is to write my own mixer. Which means losing all the cool feature of the FMOD::Sound, like setFrequency, setLoopPoints, etc.
How hard would it be to make a callback function such as LOOP_END?
For what reasons, does the function setLoopCount(0) doesn’t work at all with loopPoints but just playtrough the file?
Here is quote of Brett form the following topic:
http://18.104.22.168/forum/viewtopic.php … t=accurate
[quote="brett":23wx30l4]a mixer is a very difficult job to get to work correctly, it is not really worth me doing pseudocode because there would be quite a lot of it.
for interests sake anyway, just the sample accurate streaming bit, would look something like this
update(char *buffer, int length)
int size = length;
if (size > 256)
size = 256;
updatesequencer(); mix(buffer, size); length -= size; buffer += size;
that 256 means, no matter what length is, the timing is always 256 samples at a time. 256 could of course be anything.
That pseudocode doesnt handle what happens if size < 256 and not enough data is mixed, but that just means the next time update is called that portion has to be mixed before continuing.[/quote:23wx30l4]
- Steph asked 10 years ago
Thanks for the tips, I’ll try it ,but I doubt that It will me more accurate than my timer method…
Here is more details about what was the problem with the timer method:
– If the loop of the sound has a length that is a multiple(PCM samples) of the DSP buffersize then, my timer stop the loop perfectly at the right time.
– The problem happens when the length is not a multiple of the DSPbuffersize… 😥 … which happens all time basically.
For example, if we load a drum beat inside our program, and set the loop points to isolate the sound of a snare drum. Let say, that the loop is perfectly cut (the loop points are at zero points).
When the snare drum sound loops it doesn’t clip at all, but if even if we would have to most accurate timer in the world, I don’t think there is a way of stopping an FMOD::sound in the middle of a buffer…correct me if I’m wrong.
So, for the example of our snare drum, if the loop is not a multiple of the buffersize we are still stuck with the sound playing once. Then when the timer notify us at the right moment, the channel buffer was already filled with some data of the beginning of the sound… So the result is the sound of the snare drum + couple of samples from the beginning of the loop. You know that it creates a enormous click at the end.
More precisely the extra samples after the loop is equal to:
extraSample = lengthOfLoop % DSPBufferSize
I can provide you with some picture, might be clearer
Here are some picture to help describe what I wrote 😀
Here I have two examples of the same sound, the highlighted region is the loopregion set by the setLoopPoints function. Both region start at the exact same sample, but their end point is different.
The playhead is in red on both pictures. It show the place at where the sound is stopped. To get that, we set the position to the beginning of the loop, and then we play the loop. We pause the channel in a DSP callback function, where at each sample we check if the loopCount was decremented (via getLoopCount).
Here are the two examples:
Note that every time, when we trigger the loop the playhead stop at the exact same points for those two cases.
Almost-right has a lenght of 10205 samples
Not-Right at all has a lenght of 10256
We would like to stop the playhead right at the beginning of the loop. Although, It seems impossible to do with the current function of FMOD. The only way I can think of for now is to get data myself from the sounds by feeding it to a FMOD_OPENUSER sound throught callback function and that is a lot of work… 😥
I don’t like to say that but it make us think about switching to another sound engine.
I have a similar problem… I need to do loop 4 times and when it finishes would wish to continue from another position (with setPosition), but I must call to setPosition at the exact moment that the cursor arrives at the end of loop. I need callback (real) of end loop. 😕
It is possible to be added?
- LeoCombes answered 10 years ago
[quote="Colores":1lwnnr4s]I need callback (real) of end loop. 😕
It is possible to be added?[/quote:1lwnnr4s]
When you say real, I assume that you mean a Callback that doesn’t need system::update to be called in order to be trigger.
Anyway it would be really nice, if all the callback functions of the channel would not need the system::update… It would help me a lot
Please login first to submit.