How can I get each Tracks Volume Level?

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