How can I get each Tracks Volume Level?

I could get Master RMS level metering data.
How can I get each Tracks Volume Level?
Main_Track(Event) have multiple instruments. Hope to get each tracks volume.

Thanks in advance.

You can place a Loudness Meter plugin on each track. In the deck of each track right click and select Add Effect > FMOD Loudness Meter.

I placed LoudnessMeter(EBU R-128) to each tracks in FMOD Studio.
I hope to get each track’s metering info in c++.
How can I get each datas in code.

Thanks for your time.

If you want to get the RMS metering data from the API then you can use DSP::setMeteringEnabled() and DSP::getMeteringInfo().

https://www.fmod.com/resources/documentation-api?version=2.02&page=core-api-dsp.html#dsp_setmeteringenabled

https://www.fmod.com/resources/documentation-api?version=2.02&page=core-api-dsp.html#dsp_getmeteringinfo

Hi Richard.

Sorry, I can’t figure out how to get eack RMS track using these functions. FMOD_DSP_METERING_INFO is only delivering Master RMS (using “rmslevel”). Could you explain with more detail, please?

In case FMOD Loudness Meter plugins-effect on each track is the only way. How I can get the RMS info of each track by code?

Thanks in advance.

Hi,

Please take a look at this thread, which contains an overview of how to drill down into FMOD’s DSP chain to retrieve metering info with the functions that Richard mentions: Unity: getVolume only returning a value of 1? - #6 by Louis_FMOD

The code snippets are in C#, but should be fairly easily to convert to C++.

As for getting metering info for each audio track in an event, it can be a bit more complex - would you mind explaining what you’re trying to accomplish, so that I have a better picture of how to address what you need?

1 Like

Thanks for your quick reply!

Actually I’m working on a Unity project so C# code examples are perfect for me.

I hadn’t thought about the possibility to set channelgroup to anything different than channelgroup-master. Gonna attempt the first solution you have given me and let you know if there is any issue! I think with the first solution I can get the results I expect.

Thanks again, Regards.

1 Like

Me again, sorry, I wasn’t able to get the metering with each audio track with the first solution.

Here is an example of one of multiple events that I have in my project (as you can see is a song with its multiple tracks) I am programming a mixer in Unity and I need to display metering of each track and the master track. I was able to get master metering info using the next functions:

private IEnumerator Training_GetChannelGroup() // Called once after load bank and create an instance
    {
        if (eventInstance_Training_User.isValid())
        {
            while (eventInstance_Training_User.getChannelGroup(out channelGroupTraining) != FMOD.RESULT.OK)
            {
                yield return new WaitForEndOfFrame();
            }

            channelGroupTraining.getDSP(0, out dspTraining);
            dspTraining.setMeteringEnabled(false, true);
        }
        else
        {
            yield return null;
        }
    }

    public float Training_GetRMS() //Called everytime I need master info for metering.
    {
        float rms = 0f;

        dspTraining.getMeteringInfo(IntPtr.Zero, out meterInfoTraining);
        for (int i = 0; i < meterInfoTraining.numchannels; i++)
        {
            rms += meterInfoTraining.rmslevel[i] * meterInfoTraining.rmslevel[i];
        }

        rms = Mathf.Sqrt(rms / (float)meterInfoTraining.numchannels);

        float dB = rms > 0 ? 20.0f * Mathf.Log10(rms * Mathf.Sqrt(2.0f)) : -80.0f;
        if (dB > 10.0f) dB = 10.0f;
        return dB;
    }

What should be the easist way to get metering of each individual track of an event like this?
Thanks in advance!

You can get the ChannelGroups that correspond to each Audio Track in Studio for the event by enumerating them with ChannelGroup::getNumGroups and ChannelGroup:getGroup. The difficult part is being able to identify which Audio Track in Studio matches with each ChannelGroup, as this cannot be done solely via the API.

Thankfully, I have a Studio script on hand that will, for events that you have selected in the event browser in Studio, calculate the order that the event’s Audio Tracks will be returned when using ChannelGroup::getGroup, and write that order to the event’s User Properties delimited with commas.

You can retrieve this info in-game from the event’s description with Studio::EventDescription::getUserProperty and parse it to help sort each Channel Group.

The script is as follows:

studio.menu.addMenuItem({
    name: "Write Group Order to User Properties",
    execute: function() {
        writeGroupOrder();
    }
});

function writeGroupOrder(){

    // Get all events selected
    var allEvents = studio.window.browserSelection();

    // Check that only events are selected
    var isOnlyEvents = true;
    allEvents.forEach(function(event) {
        if (!(event.isOfExactType("Event"))){
            console.log("object is not event - returning false")
            isOnlyEvents = false;
            return;
        }
    });

    // Show dialog box for error and return early if no selection or objects other than events selected
    if (!isOnlyEvents || allEvents.length == 0) {
        studio.ui.showModalDialog({
            windowTitle: "Warning - Error running script",
            widgetType: studio.ui.widgetType.Layout,
            layout: studio.ui.layoutType.VBoxLayout,
            items: [
                {
                    widgetType: studio.ui.widgetType.Label, text: "No selection, or objects other than events have been selected - please ensure only events are selected when running this script."
                }
            ]
        });
        return false;
    }

    // iterate through events
    allEvents.forEach(function(event) {

        // Create an array of groups that we will sort
        var groups = [];
        // Fill "groups" array with each track from the Event
        event.groupTracks.forEach(function(track) {
            groups.push(track.mixerGroup)
        });

        // Sort the "groups" array based on each track's internal ID
        groups.sort(function(a, b) {
            var id_a = guidToBytes(a.id);
            var id_b = guidToBytes(b.id);
            return intCompare(id_a, id_b);
        });

        // Create string of all IDs in order
        var string = "";
        var delimiter = ",";
        groups.forEach(function(group) {
            string += group.name + delimiter;
        });

        // iterate over event's existing user properties
        // and update Track Order property if it exists already
        var exists = false;
        event.userProperties.every(function(p){
            if (p.key === "Group Order"){
                p.value = string;
                exists = true;
                return false;
            }
            return true;
        });

        // if Track order does not exist already, create and add it to event
        if (!exists){
            var property = studio.project.create("UserProperty");
            property.key = "Group Order";
            property.value = string;
            property.event = event;
        }
        
    });

}

// Function to compare arrays by iterating through each element and comparing them
function intCompare(array1, array2){
    for (var i = 0; i < array1.length; i++){
        var result = array1[i] < array2[i] ? -1 : (array1[i] > array2[i]) ? 1 : 0;
        if (result != 0){
            return result;
        }
    }
    return 0;
}

// Function to convert a GUID string to a byte array
function guidToBytes(guid) {
    var bytes = [];
    // Strip braces and split into segments based on hyphen
    var segmentedGuid = guid.replace(/[{}]/g, "").split('-');
    segmentedGuid.forEach(function(segment, index) {
        // Extract the bytes, the first three segments must be reversed due to endian
        var bytesInSegment = index < 3 ? segment.match(/.{1,2}/g).reverse() : segment.match(/.{1,2}/g);
        bytesInSegment.forEach(function(element) {
            // Convert hex to decimal and add to output
            bytes.push(parseInt(element, 16));
        });
    });
    return bytes;
}

Place this script in a .js file in the ./scripts directory of your FMOD Studio install directory or project directory, selected the events you want it to write the order for, and run the script from the “Scripts” menu. Let me know if you run into any issues with it.

1 Like

Awesome! Thanks for share me this script and a detailed solution to reach my goal.

I followed your steps and I got the results that I expected,

thanks a lot for all your help with this issue!

Samuel.

1 Like