# Playing sound specific range

**URL:** <https://qa.fmod.com/t/playing-sound-specific-range/18497>\
**Category:** FMOD Engine\
**Created:** [March 9, 2022, 7:13pm UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497 "2022-03-09T19:13:16Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![intofin](https://avatars.discourse-cdn.com/v4/letter/i/ea666f/32.png) [@intofin](https://qa.fmod.com/u/intofin)\
**Post date:** [March 9, 2022, 7:13pm UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/1 "2022-03-09T19:13:16Z")

</div>

Hi,

I have few questions:

1. I would like to play a specific range[startTime, endTime] of a sound file, is there an efficient way to do it ? I tried to setLoopPoints and setDelay but I couldn’t make them work. Of course, I could monitor the playback through callback and stop the playing, but I think there might be a better way to do it.

2. Per the documentation, after playing a sound its channel should be invalid, I don’t see it happens when I call hasHandle on a channel the handle seems valid. Does it make any sense?

Thanks

---

<div class="post-metadata">

**Author:** ![jeff\_fmod](https://yyz2.discourse-cdn.com/flex036/user_avatar/qa.fmod.com/jeff_fmod/32/1766_2.png) [@jeff\_fmod](https://qa.fmod.com/u/jeff_fmod)\
**Post date:** [March 16, 2022, 12:57am UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/2 "2022-03-16T00:57:29Z")

</div>

1. You can scrub to a position of a playing Sound by calling [`Channel::setPosition`](https://fmod.com/resources/documentation-api?version=2.02&page=core-api-channel.html#channel_setposition) on it’s Channel handle. For `setLoopPoints` to work the sound’s mode will need to be set to FMOD\_LOOP\_NORMAL or FMOD\_LOOP\_BIDI.
2. Where in the docs does it say after playing a sound the channel should be invalid? After playing a sound the channel should be valid if `playSound` was succesful.

---

<div class="post-metadata">

**Author:** ![intofin](https://avatars.discourse-cdn.com/v4/letter/i/ea666f/32.png) [@intofin](https://qa.fmod.com/u/intofin)\
**Post date:** [March 21, 2022, 3:59pm UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/3 "2022-03-21T15:59:26Z")

</div>

Hi Jeff,

Thanks.

1. Channel::setPosition will work on the “start”, but the problem is how to accurately end the playback on endTime; I thought about starting some timer, but would like to avoid managing multiple timers. Is there some more elegant way to deal with this? Maybe I could register to some callbacks? I did check the channel callback, didn’t look like it could help.

2. I saw it in the below book… I think I saw it also in the documentation, but not sure.

---

<div class="post-metadata">

**Author:** ![jeff\_fmod](https://yyz2.discourse-cdn.com/flex036/user_avatar/qa.fmod.com/jeff_fmod/32/1766_2.png) [@jeff\_fmod](https://qa.fmod.com/u/jeff_fmod)\
**Post date:** [March 23, 2022, 2:00am UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/4 "2022-03-23T02:00:59Z")

</div>

I believe `setLoopPoints` should be sample accurate, and then you would just need to set the loop count to 0 with [`Channel::setLoopCount`](https://fmod.com/resources/documentation-api?version=2.02&page=core-api-channel.html#channel_setloopcount) to make sure it only plays once.

That book is correct, the channel will be invalid only after the sound has stopped or completed it’s playback, but will be valid until either of those two things occur.

---

<div class="post-metadata">

**Author:** ![intofin](https://avatars.discourse-cdn.com/v4/letter/i/ea666f/32.png) [@intofin](https://qa.fmod.com/u/intofin)\
**Post date:** [March 24, 2022, 10:17am UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/6 "2022-03-24T10:17:42Z")

</div>

1. Well, it doesn’t seem like setLoopPoints is doing the trick. It does loop, but after the count is reached, the sound is playing without stopping. It looks like I will have to monitor the playback ☹ Any idea?

2. Yeah, that’s what I meant - that post-playback(complete/stopped) the channel’s handle cannot be re-used and should be invalid? I don’t see this happening… how do I check if the handle is valid?

---

<div class="post-metadata">

**Author:** ![jeff\_fmod](https://yyz2.discourse-cdn.com/flex036/user_avatar/qa.fmod.com/jeff_fmod/32/1766_2.png) [@jeff\_fmod](https://qa.fmod.com/u/jeff_fmod)\
**Post date:** [March 24, 2022, 11:28pm UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/7 "2022-03-24T23:28:41Z")

</div>

1. You are right, sorry for the oversight there. I think your original solution of having callbacks is the best way to go, though I am finding it is off by 5-30ms which isn’t ideal. I have added a task to our backlog to implement a call for sample-accurate playback within a given time range.  
Here is a rough example of you can implement such a callback:

```cpp
FMOD_RESULT F_CALLBACK SyncCallback(FMOD_CHANNELCONTROL* channelcontrol, FMOD_CHANNELCONTROL_TYPE controltype, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbacktype, void* commanddata1, void* commanddata2)
{
    if (callbacktype == FMOD_CHANNELCONTROL_CALLBACK_SYNCPOINT)
    { 
        FMOD::ChannelGroup *cc = (FMOD::ChannelGroup*)channelcontrol;
        return cc->stop();
    }
    return FMOD_OK;
}

FMOD_RESULT FMOD_Play_Sound_Range(FMOD::System *system, FMOD::Sound *sound, int start, FMOD_TIMEUNIT starttimeunit, int end, FMOD_TIMEUNIT endtimeunit)
{
    FMOD_RESULT result;
    FMOD_SYNCPOINT *endpoint;
    FMOD::Channel *channel;
    int numsyncpoints;

    // Remove existing syncpoints
    result = sound->getNumSyncPoints(&numsyncpoints);
    ERRCHECK(result);
    for (int i = 0; i < numsyncpoints; ++i)
	{
		FMOD_SYNCPOINT* syncpoint;
		result = sound->getSyncPoint(i, &syncpoint);
		ERRCHECK(result);
		result = sound->deleteSyncPoint(syncpoint);
		ERRCHECK(result);
	}
    // Add a single syncpoint for the end time
    result = sound->addSyncPoint(end, FMOD_TIMEUNIT_MS, "END", &endpoint);
    ERRCHECK(result);
 
	result = system->playSound(sound, 0, false, &channel);
	ERRCHECK(result);

    // Set start time
	result = channel->setPosition(start, FMOD_TIMEUNIT_MS);
	ERRCHECK(result);
	result = channel->setCallback(SyncCallback);
	ERRCHECK(result);

    return FMOD_OK;
}

```

1. The easiest way is to try and call something on the handle I suppose, if it returns [`FMOD_ERR_INVALID_HANDLE`](https://fmod.com/resources/documentation-api?version=2.02&page=core-api-common.html#fmod_result).

---

<div class="post-metadata">

**Author:** ![intofin](https://avatars.discourse-cdn.com/v4/letter/i/ea666f/32.png) [@intofin](https://qa.fmod.com/u/intofin)\
**Post date:** [August 9, 2022, 10:07am UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/8 "2022-08-09T10:07:32Z")

</div>

Hi,  
Any updates re this task?  
And for some reason, I don’t get any callback from the channel; my code is as follows:

> this.hSystem.playSound(this.hSound, this.channelGroup, true, out this.currentChannel);  
> this.channelControlCallback = new FMOD.CHANNELCONTROL\_CALLBACK(this.ChannelControlCallbackFunc);  
> if (this.currentChannel.setCallback(this.channelControlCallback) != RESULT.OK){  
> return -2;  
> }
> 
> this.currentChannel.setPaused(false);

Thanks!

---

<div class="post-metadata">

**Author:** ![jeff\_fmod](https://yyz2.discourse-cdn.com/flex036/user_avatar/qa.fmod.com/jeff_fmod/32/1766_2.png) [@jeff\_fmod](https://qa.fmod.com/u/jeff_fmod)\
**Post date:** [August 9, 2022, 11:46pm UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/9 "2022-08-09T23:46:55Z")

</div>

Sample accurate playback is achieved with [`ChannelControl::setPosition`](https://fmod.com/docs/2.02/api/core-api-channel.html#channel_setposition) to set the start time and [`ChannelControl::setDelay`](https://fmod.com/docs/2.02/api/core-api-channelcontrol.html#channelcontrol_setdelay) to set a duration. The duration of `ChannelControl::setDelay` needs to be offset by the current dsp clock time, which you can get through [`ChannelControl::getDSPClock`](https://fmod.com/docs/2.02/api/core-api-channelcontrol.html#channelcontrol_getdspclock).

```cpp
void SetChannelRange(FMOD::ChannelGroup *channel, int start, int end)
{
    int duration = stop - start;
    int sampleRate;
    unsigned long long time;
    
    result = system->getSoftwareFormat(&sampleRate, 0, 0);
    ERRCHECK(result);
    result = channel->getDSPClock(0, &time);
    ERRCHECK(result);

    result = channel->setPosition(start * sampleRate, FMOD_TIMEUNIT_PCM);
    ERRCHECK(result);
    result = channel->setDelay(0, time + duration * sampleRate);
    ERRCHECK(result);
}

```

As for the callback, I can see you are using C#, does your callback signature look like this?

```cs
[AOT.MonoPInvokeCallback(typeof(FMOD.CHANNELCONTROL_CALLBACK))]
static FMOD.RESULT ChannelControlCallbackFunc(
  IntPtr channelcontrol,
  FMOD.CHANNELCONTROL_TYPE controltype,
  FMOD.CHANNELCONTROL_CALLBACK_TYPE callbacktype,
  IntPtr commanddata1,
  IntPtr commanddata2
){}

```

---

<div class="post-metadata">

**Author:** ![intofin](https://avatars.discourse-cdn.com/v4/letter/i/ea666f/32.png) [@intofin](https://qa.fmod.com/u/intofin)\
**Post date:** [August 10, 2022, 12:18pm UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/10 "2022-08-10T12:18:49Z")

</div>

Hi,

Same signature,  
I don’t have the MonoPInvokeCallback though.

> this.hSystem.createChannelGroup(groupName, out this.channelGroup);  
> this.hSystem.playSound(this.hSound, this.channelGroup, true, out this.currentChannel);

The above is how group channel being created. Is this how it should be done? Basically in c#, fmod’s API forces me to provide channel group. The callback is set on “this.currentChannel”.  
Am I missing something?

---

<div class="post-metadata">

**Author:** ![jeff\_fmod](https://yyz2.discourse-cdn.com/flex036/user_avatar/qa.fmod.com/jeff_fmod/32/1766_2.png) [@jeff\_fmod](https://qa.fmod.com/u/jeff_fmod)\
**Post date:** [August 10, 2022, 11:48pm UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/11 "2022-08-10T23:48:39Z")

</div>

Any callbacks into FMOD require the `MonoPInvokeCallback` attribute since the callback will be called by native code. `MonoPInvokeCallback` also needs to be static.

> [@intofin](#):
>
> The above is how group channel being created. Is this how it should be done?

Yes you can create a new `ChannelGroup` for your sound, or:

```auto
playSound(sound, new ChannelGroup(), false, out Channel channel);

```

To give it an invalid `ChannelGroup` which will make it playback on the default master channel group.

---

<div class="post-metadata">

**Author:** ![intofin](https://avatars.discourse-cdn.com/v4/letter/i/ea666f/32.png) [@intofin](https://qa.fmod.com/u/intofin)\
**Post date:** [August 11, 2022, 9:13pm UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/12 "2022-08-11T21:13:45Z")

</div>

> [@jeff\_fmod](#):
>
> `MonoPInvokeCallback`

Thanks! Now it is clear why it doesn’t work… I have .NET 4.6 c# project without Unity; which reference should I add to use it?

---

<div class="post-metadata">

**Author:** ![jeff\_fmod](https://yyz2.discourse-cdn.com/flex036/user_avatar/qa.fmod.com/jeff_fmod/32/1766_2.png) [@jeff\_fmod](https://qa.fmod.com/u/jeff_fmod)\
**Post date:** [August 12, 2022, 12:11am UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/13 "2022-08-12T00:11:19Z")

</div>

If you are just using .NET without Mono/Unity I don’t think you need any of the AOT stuff. Here is a basic .NET program showing how to set a `CHANNELCONTROL_CALLBACK`.

```cs
private static CHANNELCONTROL_CALLBACK channelControlCallback;

private static RESULT ChannelControlCallbackFunc(IntPtr channelcontrol, FMOD.CHANNELCONTROL_TYPE controltype, FMOD.CHANNELCONTROL_CALLBACK_TYPE callbacktype, IntPtr commanddata1, IntPtr commanddata2)
{
    if (callbacktype == CHANNELCONTROL_CALLBACK_TYPE.SYNCPOINT)
    {
        Channel cc = new Channel(channelcontrol);
        return cc.stop();
    }
    return RESULT.OK;
}

private static RESULT PlaySoundRange(FMOD.System system, Sound sound, uint start, TIMEUNIT starttimeunit, uint end, TIMEUNIT endtimeunit)
{
    RESULT result;
    IntPtr syncpoint, endpoint;
    Channel channel;
    int numsyncpoints;

    // Remove existing syncpoints
    result = sound.getNumSyncPoints(out numsyncpoints);
    ERRCHECK(result);
    for (int i = 0; i < numsyncpoints; ++i)
    {
        result = sound.getSyncPoint(i, out syncpoint);
        ERRCHECK(result);
        result = sound.deleteSyncPoint(syncpoint);
        ERRCHECK(result);
    }
    // Add a single syncpoint for the end time
    result = sound.addSyncPoint(end, TIMEUNIT.MS, "END", out endpoint);
    ERRCHECK(result);

    result = system.playSound(sound, new ChannelGroup(), false, out channel);
    ERRCHECK(result);

    // Set start time
    result = channel.setPosition(start, TIMEUNIT.MS);
    ERRCHECK(result);
    result = channel.setCallback(channelControlCallback);
    ERRCHECK(result);

    return RESULT.OK;
}

[STAThread]
static void Main(string[] args)
{
    channelControlCallback = new CHANNELCONTROL_CALLBACK(ChannelControlCallbackFunc);

    ERRCHECK(Factory.System_Create(out FMOD.System system));
    ERRCHECK(system.init(32, INITFLAGS.NORMAL, IntPtr.Zero));

    ERRCHECK(system.createSound("music_48kbps.wav", MODE.DEFAULT, out Sound sound));
    PlaySoundRange(system, sound, 2000, TIMEUNIT.MS, 4000, TIMEUNIT.MS); // Play 2-4 seconds
    
    do
    {
        ERRCHECK(system.update());
        System.Threading.Thread.Sleep(50);
    } while (!exit);

    ERRCHECK(sound.release());
    ERRCHECK(system.release());
}

#region Details

static bool exit = false;
static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
    exit = true;
}

public static void ERRCHECK(FMOD.RESULT result)
{
    if (result != FMOD.RESULT.OK)
    {
        Console.WriteLine(result);
    }
}

#endregion

```

---

<div class="post-metadata">

**Author:** ![intofin](https://avatars.discourse-cdn.com/v4/letter/i/ea666f/32.png) [@intofin](https://qa.fmod.com/u/intofin)\
**Post date:** [August 14, 2022, 1:04pm UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/14 "2022-08-14T13:04:42Z")

</div>

> [@jeff\_fmod](#):
>
> `channelControlCallback`

yes, exactly my code… I don’t loop and call “system.update” repeatedly though. I guess that’s the problem? I have a UI thread, so I guess I need a dedicated thread for fmod in this case?

---

<div class="post-metadata">

**Author:** ![jeff\_fmod](https://yyz2.discourse-cdn.com/flex036/user_avatar/qa.fmod.com/jeff_fmod/32/1766_2.png) [@jeff\_fmod](https://qa.fmod.com/u/jeff_fmod)\
**Post date:** [August 14, 2022, 11:23pm UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/15 "2022-08-14T23:23:02Z")

</div>

Yes that would be a problem, you need to call [`System::update`](https://fmod.com/docs/2.02/api/core-api-system.html#system_update) for the following to occur:

> - Panning and reverb from 3D attributes changes.
> - Virtualization of [Channel](https://fmod.com/docs/2.02/api/core-api-channel.html)s based on their audibility.
> - Mixing for non-realtime output types. See comment below.
> - Streaming if using [FMOD\_INIT\_STREAM\_FROM\_UPDATE](https://fmod.com/docs/2.02/api/core-api-system.html#fmod_init_stream_from_update).
> - Mixing if using [FMOD\_INIT\_MIX\_FROM\_UPDATE](https://fmod.com/docs/2.02/api/core-api-system.html#fmod_init_mix_from_update)
> - Firing callbacks that are deferred until Update.
> - DSP cleanup.

It doesn’t neccessarily need to be on a different thread, you can just run it on your main thread. Keeping it separate from the UI is definitely a good idea though.

---

<div class="post-metadata">

**Author:** ![intofin](https://avatars.discourse-cdn.com/v4/letter/i/ea666f/32.png) [@intofin](https://qa.fmod.com/u/intofin)\
**Post date:** [August 15, 2022, 2:42pm UTC](https://qa.fmod.com/t/playing-sound-specific-range/18497/16 "2022-08-15T14:42:50Z")

</div>

Many thanks, Jeff
