Compute and display setlist duration

Hi there. In my journey through running backing tracks on stage for a live band using GP5, I faced the fact that there is no “duration” concept for songs and setlists. This is because GP is probably not primarily designed for that. I searched here without any success, so I made my first GP Script and share it with you. Hope it helps, perhaps someone will make it better.

Basically : I decided to use the built-in “Artist” field of songs to store each song duration. So first, I entered every duration using the format mm:ss (eg. 05:43 = five minutes and forty three seconds).

Then : I added two Text Labels to my Global Rackspace, named lbl_Song_Duration and lbl_Setlist_Duration. I want the selected song duration to be displayed in the first one while the selected setlist complete duration is displayed in the second one.

And then I created this GLOBAL RACKSPACE script :

// Déclaration du widget textuel pour l'affichage
Var
   lbl_Setlist_Duration, lbl_Song_Duration : Widget
   setlistDurationAlreadyLoaded : Boolean

// Fonction utilitaire pour extraire la durée [MM:SS] du nom d'un morceau
Function ExtractDurationFromSongName(songName : String) Returns Integer
Var
   posOpen, posColon, posClose : Integer
   minStr, secStr : String
   minutes, seconds : Integer
   duration : Integer

   duration = 0
   posOpen = IndexOfSubstring(songName, "[", false)
   posColon = IndexOfSubstring(songName, ":", false)
   posClose = IndexOfSubstring(songName, "]", false)

   // Si le format [MM:SS] est bien détecté dans le titre
   If posOpen >= 0 And posColon > posOpen And posClose > posColon Then

      minStr = CopySubstring(songName, posOpen + 1, posColon - posOpen - 1)
      secStr = CopySubstring(songName, posColon + 1, posClose - posColon - 1)

      minutes = StringToInt(minStr)
      seconds = StringToInt(secStr)

      // On retourne le total en secondes pour faciliter le calcul

      duration = (minutes * 60) + seconds
   End
   result = duration // Retourne 0 si aucun format n'est trouvé
End

Function ExtractDurationFromString(durationString : String) Returns Integer
Var
    duration, posColon, length : Integer
    minStr, secStr : String
    minutes, seconds : Integer

    duration = 0
    posColon = IndexOfSubstring(durationString, ":", false)
    length = Length(durationString)

If posColon > -1 Then
    minStr = CopySubstring(durationString, 0, posColon)
    secStr = CopySubstring(durationString, posColon + 1, length)

    minutes = StringToInt(minStr)
    seconds = StringToInt(secStr)

    // On retourne le total en secondes pour faciliter le calcul
    duration = (minutes * 60) + seconds
End

result = duration
End

// Procédure principale qui calcule le temps total de la setlist active
function FormatSecondsToString(totalSeconds : Integer) Returns String
Var
   hStr, mStr, sStr : String
   displayText : String
   finalHours, finalMinutes, finalSeconds : Integer
   
   // Conversion du total des secondes en Minutes:Secondes pour l'affichage
   finalHours = totalSeconds / 3600
   finalMinutes = (totalSeconds % 3600) / 60
   finalSeconds = totalSeconds % 60
   
   // Formatage avec des zéros initiaux si nécessaire pour les minutes/secondes
   If finalMinutes < 10 Then mStr = "0" + finalMinutes Else mStr = "" + finalMinutes End
   If finalSeconds < 10 Then sStr = "0" + finalSeconds Else sStr = "" + finalSeconds End
   hStr = "" + finalHours
   
   // Construction de la chaîne d'affichage
   If finalHours > 0 Then
      displayText = hStr + ":" + mStr + ":" + sStr
   Else
      // Si moins d'une heure, on peut masquer le "0:" pour garder de la clarté
      displayText = mStr + ":" + sStr
   End
   
   result = displayText
End

// Procédure principale qui calcule le temps total de la setlist active
function CalculateSetlistDuration()
Var
   songCount : Integer
   i : Integer
   currentSongName : String
   totalSeconds : Integer

   totalSeconds = 0
   songCount = GetSongCount() // Récupère le nombre total de morceaux de la setlist
   
   // Boucle à travers tous les morceaux de la setlist active
   For i = 0; i < songCount; i = i + 1 Do
      currentSongName = GetSongName(i)
      Print(currentSongName)
      //totalSeconds = totalSeconds + ExtractDurationFromSongName(currentSongName)
      totalSeconds = totalSeconds + ExtractDurationFromString(GetSongArtistName(i))
      Print(totalSeconds)
   End
   

   // Mise à jour du widget sur votre écran
   SetWidgetLabel(lbl_Setlist_Duration, FormatSecondsToString(totalSeconds))
End


// Called when the setlist changes - (GigScript/Global rackspace) only)
On SystemEvent Matching SetlistChanged
    If GetCurrentSetlistIndex() > -1 Then
        CalculateSetlistDuration() // Called when the setlist changes - (GigScript/Global rackspace) only)
    End
End

// Called when you switch to another song
On Song(oldSongIndex : integer, newSongIndex : integer)
    // A l'ouverture de GP5 même si opn est pas en mode setlist ce callback est appelé
    // et dans ce cas cette fonction renvoie une ereur et bloque le script, on teste donc si on est en mode setlist
    // en testant l'index de la setlist active
    If GetCurrentSetlistIndex() > -1 Then
        if !setlistDurationAlreadyLoaded Then
            CalculateSetlistDuration()
            setlistDurationAlreadyLoaded = true
        End
        SetWidgetLabel(lbl_Song_Duration, FormatSecondsToString(ExtractDurationFromString(GetSongArtistName(newSongIndex))))
    End
End

I faced an issue, because GP5 is probabely loading songs even the setlist mode is not active, and unfortunately, calling Song functions will provoke a crash and further scripts will not run. This why there are tests like “GetCurrentSetlistIndex() > -1”

So the ideea is : each time the song selection changes, the duration of the song is displayed. Each time the setlist is changes, the total duration is displayed.

Please post here the crash log.

GPScript System Function Exception: You must be in setlist mode for song operations No source information available - check GPScript options

Then the script wont run until recompiled. So I found a workaround in order to « check » if the setlist is the actuve mode. Would you have a better approach in mind ?

This is very nice idea.
Having duration information is something I need as well, I hope it will be integrated into the application in the future.

If every song and song part will have a duration data then it could indeed calculate and show the total time for the gig, the total time of a song (sum of its parts) and also show total time from the start until a specific song.

Furthermore, allow entering the number of bars of a song or a part then it could calculate the total time based on the tempo :slight_smile:

I believe he meant a script error which needs to be recompiled.

That is not a crash. That is a runtime error telling you that you can’t perform setlist functions when you’re not in setlist mode

That is not a workaround - that is precisely the test you need to perform to determine whether it is ok to use setlist functions. It is no different than testing a variable to make sure it is not zero before you use it to mistakenly divide by zero :wink:

Yes I was talking about an exception not a crash. But once raised, the script won’t run anymore. Initially I was looking for a way to handle the exception but this is not possible in GP Script.

So the approach of looking for the current index is the right one, thank you for having confirmed that. Hope it will help others !