getLength/setPosition vs. getDSPClock values - sample rate dependent

I’m performing sample-accurate scheduling using both getDSPClock, getLength and setPosition.

I have two sounds – sound1 has already started playing, and I want to synchronise sound2 with it by starting it at the right point in the sound.

When the sound sample is different from the system sample rate (i.e. say system running at 48000 vs. sound loaded at 44100) I cannot use getLength directly to offset the position. I have to convert to the system sample rate first:

result = sound1->sound->getLength(&sound1->len_pcm, FMOD_TIMEUNIT_PCM);
ERRCHECK(result);
result = sound1->sound->getLength(&sound1->len_ms, FMOD_TIMEUNIT_MS);
ERRCHECK(result);
// work out the actual sample rate of the sound
sound1->sr = sound1->len_pcm / (sound1->len_ms / 1000);
// work out the length of the sound in terms of the system sample rate
// so we can call accurately using a combination of getDSPClock and setPosition
sound1->len_pcm_systemsr = sound1->len_ms * (system_samplerate / 1000);

…and then when I want to set the offset position correctly I have to convert back to the sound’s sample rate to use setPosition correctly. Assume I’ve already worked out what the correct position

unsigned long long channelgroup_dsp = 0;
channelgroup->getDSPClock(&channelgroup_dsp, 0);
unsigned long long channel_start_pos = 0;
unsigned long long start_point_offset = (channelgroup_dsp - startpoint);
if (start_point_offset < sound2->len_pcm_systemsr)
{
    // the current clock position of the channel group is less than the length of the sound
    // this means we're in the first loop
    // set the start position of the sound to the position
    channel_start_pos = start_point_offset;
}
else
{
    // we must be in a subsequent loop
    // work out where in the loop we are
    channel_start_pos = start_point_offset % sound2->len_pcm_systemsr;
}

// now we have to convert the dsp clock (which is at system sample rate)
// to the sound's sample rate
float soundoffset_ms = channel_start_pos / (system_samplerate / 1000);
unsigned int soundoffset_samples = soundoffset_ms * (sound1->sr / 1000);

sound2->channel->setPosition(soundoffset_samples, FMOD_TIMEUNIT_PCM);

What I’d like to know is:

  1. Is there a simpler way to get the sound’s sample rate?
  2. Is there a more efficient way to do this?

Hi,

What you’re doing is exactly what we recommend doing for gapless playback of sounds at different sample rates, which you can observe in the Core API “gapless_playback” example. As for what can be done more efficiently, instead of using Channel::setPosition, you can use ChannelControl::setDelay, which will allow you to schedule a start/stop time using DSP clock units instead of needing to convert back to the system sample rate.

Brilliant, massive thanks for your help Louis. However, can I use ChannelControl::setDelay for a time that’s passed?

For example, I start sound1 at a DSP clock of 10000.
Say we’re then at DSP clock 20000 (with getDSPClock).
Can I set sound2 to start at a DSP clock of 10000 (with setDelay), and it’ll start at ~10000 samples into the sound, according to the parent clock? Or do I have to use setPosition like I’m doing above?

No problem!

Channel::setDelay cannot schedule a channel to start in the past - if you attempt to do so, the channel should just start playback from the start of the sound. If you need to accommodate for a time that has passed, you should use ChannelControl::setPosition to set the position accordingly.