# Getting Programmer Instrument clip info locally

**URL:** <https://qa.fmod.com/t/getting-programmer-instrument-clip-info-locally/21072>\
**Category:** Unity\
**Tags:** unity, csharp\
**Created:** [January 8, 2024, 3:10pm UTC](https://qa.fmod.com/t/getting-programmer-instrument-clip-info-locally/21072 "2024-01-08T15:10:55Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![virtuosoofficial](https://avatars.discourse-cdn.com/v4/letter/v/f0a364/32.png) [@virtuosoofficial](https://qa.fmod.com/u/virtuosoofficial)\
**Post date:** [January 8, 2024, 3:10pm UTC](https://qa.fmod.com/t/getting-programmer-instrument-clip-info-locally/21072/1 "2024-01-08T15:10:55Z")

</div>

Hello,

I’m using the programmer instrument approach to voiceover illustrated in the docs, within the context of a finite state machine. This has worked well for basic playback, but I need access to more information about the sounds played to complete our approach and the docs seem to not elaborate further on programmer instrument usage.

Specifically: for our dialogue display/playback behavior, I need instance-level access to:

- Whether or not the string provided references a sound file to be played. If not, I use a default wait time or wait for input before proceeding.
- If audio exists, the duration of the audio. If there is a voice line, I wait for the duration of the line then automatically proceed to the next.

Everything I can find on working with programmer instruments uses static functions for callbacks - is there a way to do this on an instance level, or am I misunderstanding the intended workflow?

Thanks!

---

<div class="post-metadata">

**Author:** ![Leah\_FMOD](https://yyz2.discourse-cdn.com/flex036/user_avatar/qa.fmod.com/leah_fmod/32/3685_2.png) [@Leah\_FMOD](https://qa.fmod.com/u/Leah_FMOD)\
**Post date:** [January 9, 2024, 4:59am UTC](https://qa.fmod.com/t/getting-programmer-instrument-clip-info-locally/21072/2 "2024-01-09T04:59:01Z")

</div>

> [@virtuosoofficial](#):
>
> I’m using the programmer instrument approach to voiceover illustrated in the docs, within the context of a finite state machine.

In this case, I will assume you’re using audio tables - if this isn’t the case, please let me know.

> [@virtuosoofficial](#):
>
> Whether or not the string provided references a sound file to be played. If not, I use a default wait time or wait for input before proceeding.

If, in your callback, you’re using [Studio.System.getSoundInfo()](https://www.fmod.com/docs/2.02/api/studio-api-system.html#studio_system_getsoundinfo) to retrieve an entry from an audio table, you can simply check the returned result - if the sound doesn’t exist, `getSoundInfo()` should return `FMOD.RESULT.ERR_EVENT_NOTFOUND`:

```auto
// Attempt to get audio asset and its sound info with key
FMOD.Studio.SOUND_INFO soundInfo;
var keyResult = FMODUnity.RuntimeManager.StudioSystem.getSoundInfo(key, out soundInfo);
// Check result to see whether asset exists
// Checking against != FMOD.RESULT.OK also works as a catch-all
if (keyResult == FMOD.RESULT.ERR_EVENT_NOTFOUND)
{
    Debug.Log("Key '" + key + "' doesn't exist");
    break;
}

```

> [@virtuosoofficial](#):
>
> If audio exists, the duration of the audio. If there is a voice line, I wait for the duration of the line then automatically proceed to the next.

If the audio file _does_ exist, you can create your Sound, and then do the following to check its length:

```auto
// Using asset audio table/FSB, so we need to get the subsound to get length
programmerSound.getSubSound(soundInfo.subsoundindex, out FMOD.Sound subsound);
// Get length of sound in milliseconds
subsound.getLength(out uint length, FMOD.TIMEUNIT.MS);
Debug.Log("Sound length = " + length);

```

> [@virtuosoofficial](#):
>
> Everything I can find on working with programmer instruments uses static functions for callbacks - is there a way to do this on an instance level, or am I misunderstanding the intended workflow?

Callbacks and user data _are_ handled per-instance, even when the functions are static - each event instance that has a callback set with [`Studio.EventInstance.setCallback()`](https://www.fmod.com/docs/2.02/api/studio-api-eventinstance.html#studio_eventinstance_setcallback) will use the specific callback set, even if there are multiple instances of the event. Using [`Studio.EventDescription.setCallback()`](https://www.fmod.com/docs/2.02/api/studio-api-eventdescription.html#studio_eventdescription_setcallback) will set a functions as a callback for all newly created instances of a given event.

We recommend that the functions used are static to ensure that your code functions properly on all AOT platforms. Please see the [section on Callbacks in our Unity docs](https://www.fmod.com/docs/2.02/unity/user-guide.html#callbacks) for more information. Are you running into any specific issues due to the static nature of the functions?

---

<div class="post-metadata">

**Author:** ![virtuosoofficial](https://avatars.discourse-cdn.com/v4/letter/v/f0a364/32.png) [@virtuosoofficial](https://qa.fmod.com/u/virtuosoofficial)\
**Post date:** [January 10, 2024, 1:30pm UTC](https://qa.fmod.com/t/getting-programmer-instrument-clip-info-locally/21072/3 "2024-01-10T13:30:52Z")

</div>

Thanks for the reply - right, the static nature of the callback is what I’m having trouble wrapping my head around. I have an instance of my script reader class controlling a specific flow of dialogue, and its state is reliant upon the information I’m trying to get (clip [non]existence, clip length if extant) for the waiting/playback logic I described. Effectively my problem is relaying the retrieved data in the callback to my script reader instance.

What I’m hoping to achieve:

- Attempt create programmer sound  
 → If key doesn’t exist, set flag on ScriptReader instance to false.  
 → Else, retrieve sound length from programmer instrument, and store in local variable on ScriptReader instance.
- Additionally, subscribe ScriptReader instance to receive event when when programmer instrument has finished or stopped playback if possible.

So far, it still seems like all of the above info is only available in a static context. Am I misunderstanding something?

---

<div class="post-metadata">

**Author:** ![Leah\_FMOD](https://yyz2.discourse-cdn.com/flex036/user_avatar/qa.fmod.com/leah_fmod/32/3685_2.png) [@Leah\_FMOD](https://qa.fmod.com/u/Leah_FMOD)\
**Post date:** [January 11, 2024, 2:12am UTC](https://qa.fmod.com/t/getting-programmer-instrument-clip-info-locally/21072/4 "2024-01-11T02:12:57Z")

</div>

> [@virtuosoofficial](#):
>
> Effectively my problem is relaying the retrieved data in the callback to my script reader instance.

Apologies for it being unclear - this the intended purpose of “user data” set on your event with [`Studio::EventInstance::setUserData`](https://www.fmod.com/docs/2.02/api/studio-api-eventinstance.html#studio_eventinstance_setuserdata). For the [programmer sound scripting example](https://www.fmod.com/docs/2.02/unity/examples-programmer-sounds.html), it’s used to solely to pass a pointer to a key _in_, but since you’re passing a pointer, you can set the data being pointed to to effectively pass data between the callback and class instance, which is what it’s used for in the [timeline callback scripting example](https://www.fmod.com/docs/2.02/unity/examples-timeline-callbacks.html). The scope of the data you set as user data can basically be anything you want - while it’s best to keep it simple, you could pass a handle to your entire ScriptReader class instance if you wanted to.

In your case, you’ll likely want to do the following:

- Create a class to hold the info you need to pass between the static callback and your non-static class instance:

```auto
class ScriptReaderInfo
{
    public string key;
    public bool keyExists;
    public float length;
}

private ScriptReaderInfo scriptReaderInfo;

```

- Initialize an instance of the ScriptReaderInfo class and set a pointer to it as your event instance’s user data:

```auto
// After creating your event instance...
// Initialize class
scriptReaderInfo = new ScriptReaderInfo();
scriptReaderInfo.key = key;
scriptReaderInfo.keyExists = false;
scriptReaderInfo.length = -1;

// Pin the class in memory and pass a pointer through the user data
GCHandle structHandle = GCHandle.Alloc(scriptReaderInfo);
dialogueInstance.setUserData(GCHandle.ToIntPtr(structHandle));

```

- Instead of retrieving _just_ the key in the callback, get the whole class:

```auto
// Start of callback...

FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);

// Retrieve the user data
IntPtr structPtr;
instance.getUserData(out structPtr);

// Get the string object
GCHandle structHandle = GCHandle.FromIntPtr(structPtr);
ScriptReaderInfo scriptReaderInfo = (ScriptReaderInfo)structHandle.Target;

```

You can then directly set `scriptReaderInfo` members inside the callback and the data will be accessible outside of the callback, and vice-versa.

* * *

> [@virtuosoofficial](#):
>
> Additionally, subscribe ScriptReader instance to receive event when when programmer instrument has finished or stopped playback if possible.

This is possible in a few ways. One possibility is, on recieving a specific callback type, setting a bool in the aforementioned ScriptReaderInfo class and checking it from Update(). A more direct way would be to pass a delegate as a part of your class:

```auto
public delegate void EventStoppedDelegate();

class ScriptReaderInfo
{
    public string key;
    public bool keyExists;
    public float length;
    // delegate to be called when event is stopped
    public EventStoppedDelegate onEventStopped;
}

void EventStopped()
{
    Debug.Log("Event stopped!");
}

```

```auto
scriptReaderInfo = new ScriptReaderInfo();
scriptReaderInfo.key = key;
scriptReaderInfo.keyExists = false;
scriptReaderInfo.length = -1;
scriptReaderInfo.onEventStopped = new EventStoppedDelegate(EventStopped);

```

Then, in your callback, you can check for a specific callback type, and call the delegate:

```auto
case FMOD.Studio.EVENT_CALLBACK_TYPE.STOPPED:
    {
        scriptReaderInfo.onEventStopped();
        break;
    }

```

It’s worth noting that, if you are calling a delegate from the callback, you’ll want to keep it simple since it’ll be called from the FMOD Studio thread, as has the potential to slow/block.

This is a bit of a wall of text, so please let me know if you have any questions.

---

<div class="post-metadata">

**Author:** ![virtuosoofficial](https://avatars.discourse-cdn.com/v4/letter/v/f0a364/32.png) [@virtuosoofficial](https://qa.fmod.com/u/virtuosoofficial)\
**Post date:** [January 11, 2024, 2:46am UTC](https://qa.fmod.com/t/getting-programmer-instrument-clip-info-locally/21072/5 "2024-01-11T02:46:58Z")

</div>

I see - thanks for the in-depth explanation! Only issue now: I’m getting a 0 result returned from getLength(), is there something on the Studio side I need to do to ensure the value returned matches the sound length?

---

<div class="post-metadata">

**Author:** ![Leah\_FMOD](https://yyz2.discourse-cdn.com/flex036/user_avatar/qa.fmod.com/leah_fmod/32/3685_2.png) [@Leah\_FMOD](https://qa.fmod.com/u/Leah_FMOD)\
**Post date:** [January 11, 2024, 11:43pm UTC](https://qa.fmod.com/t/getting-programmer-instrument-clip-info-locally/21072/6 "2024-01-11T23:43:26Z")

</div>

> [@virtuosoofficial](#):
>
> Only issue now: I’m getting a 0 result returned from getLength(), is there something on the Studio side I need to do to ensure the value returned matches the sound length?

It’s likely that you’re getting the length of the parent sound, instead of the subsound. As previously mentioned, since you’re using audio tables, you’ll need to retrieve the subsound matching the `subsoundindex` member of the sound info struct retrieved with `FMODUnity.RuntimeManager.StudioSystem.getSoundInfo()`.

If you are retrieving the correct subsound, could I get you to post the snippet of code where you create the sound and attempt to get its length?

---

<div class="post-metadata">

**Author:** ![virtuosoofficial](https://avatars.discourse-cdn.com/v4/letter/v/f0a364/32.png) [@virtuosoofficial](https://qa.fmod.com/u/virtuosoofficial)\
**Post date:** [January 12, 2024, 1:13am UTC](https://qa.fmod.com/t/getting-programmer-instrument-clip-info-locally/21072/7 "2024-01-12T01:13:09Z")

</div>

Here’s the snippet of the code within the callback responsible for creating the sound/gathering the information.

```auto
FMOD.Sound dialogueSound;
var soundResult = FMODUnity.RuntimeManager.CoreSystem.createSound(dialogueSoundInfo.name_or_data, soundMode | dialogueSoundInfo.mode, ref dialogueSoundInfo.exinfo, out dialogueSound);
  
if (soundResult == FMOD.RESULT.OK)
{
    info.keyExists = true;
    
    parameter.sound = dialogueSound.handle;
    parameter.subsoundIndex = dialogueSoundInfo.subsoundindex;
    
    
    Marshal.StructureToPtr(parameter, parameterPtr, false);
    FMOD.Sound subsound;
    dialogueSound.getSubSound(dialogueSoundInfo.subsoundindex, out subsound);
    uint msLength;
    subsound.getLength(out msLength, FMOD.TIMEUNIT.MS);
    float floatLength = Convert.ToSingle(msLength) * .001f;
    info.length = floatLength;
    Debug.Log("Floatlength: " + floatLength);//outputs 0
    Debug.Log("Intlength: " + msLength);//outputs 0
}

```

---

<div class="post-metadata">

**Author:** ![virtuosoofficial](https://avatars.discourse-cdn.com/v4/letter/v/f0a364/32.png) [@virtuosoofficial](https://qa.fmod.com/u/virtuosoofficial)\
**Post date:** [January 15, 2024, 4:26pm UTC](https://qa.fmod.com/t/getting-programmer-instrument-clip-info-locally/21072/8 "2024-01-15T16:26:44Z")

</div>

Bumping this - any insight on why subsound lengths would always return 0?

---

<div class="post-metadata">

**Author:** ![Leah\_FMOD](https://yyz2.discourse-cdn.com/flex036/user_avatar/qa.fmod.com/leah_fmod/32/3685_2.png) [@Leah\_FMOD](https://qa.fmod.com/u/Leah_FMOD)\
**Post date:** [January 16, 2024, 12:14am UTC](https://qa.fmod.com/t/getting-programmer-instrument-clip-info-locally/21072/9 "2024-01-16T00:14:21Z")

</div>

Thanks for posting the code snippet.

Unfortunately, your code works for me without any issues when using audio tables. I tested on FMOD for Unity 2.02.20, trying various advanced loading modes for the audio table in Studio, and was able to retrieve the length of all of them. Just in case it works, I would recommend trying to get the length of `dialogueSound` instead, but getting the subsound length should be what works.

What version of FMOD for Unity are you using? Can I get you to upload the full code for your ScriptReader class, as well as the relevant banks, to your [FMOD user profile](https://www.fmod.com/profile#uploads) so I can give them a test on my end?
