Delving into Delusion

Splitting Audio Files into Tracks Based on .cue File

 January 26, 2021      Stardate: 74536.8     Tagged as: Audacity Python

Here’s a problem that I come across once in a while. Let’s say you’ve downloaded an album from torrent and the entire album is in a single file. Sure, you can load the .cue file into your favorite music player and play the different tracks but you really would like this album split into individual files for each track.

Warning
It’s assumed that you are downloading legally obtained torrents. Don’t sue me RIAA!!

You can, of course, load the flac/mp3 into Audacity, manually insert labels (for example at the arrow locations), and export tracks. But I’m going to show you a semi-automated method.

Audacity Showing Track Breaks in the Audio File

Audacity has the ability to import label files. These are basically text files that tells the program at what starting/ending point to add the label and what the label’s… um… label should be. That’s great, but I don’t know where you get this label file from. This is not included with your (legally obtained!) torrent. But you do typically get a .cue file. If you got this file we are in the game.

There is a site online that someone wrote that will convert for you here http://grimblefritz.com/audacity/cue2lbl.php.

Of course, being a geek I wrote my own Python script to do it for me! Download the github gist here.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Convert a music .cue file into a label file.

This module will accept an optional string attribute that specifies the input
.cue file. If this file is not provided in the call then file-select box will
be presented to the user. Output is a .txt file of labels that can be input
into Audacity.

Examples:
        $ python cue2labels.py
        $ python cue2labels.py "InputFile.cue"

"""

def stringtime_to_millisec(stringtime):
    """

    Parameters
    ----------
    stringtime : STRING
        A string in the form of "HH:MM:SS:MS", where MS are millisecs.
        Hours(HH) and Minutes(MM) are optional.
        Seconds(SS) and Millisecs(MS) are mandatory.
        Example: 10:05:12 = 10hrs, 5mins, 12ms

    Returns
    -------
    FLOAT
        Returns a the input stringtime as decimal seconds

    """
    hours, minutes, seconds, milliseconds = (["00", "00"] + stringtime.split(":"))[-4:]
    hours = int(hours[-2:])*360
    minutes = int(minutes[-2:])*60 
    seconds = int(seconds[-2:])
    milliseconds = float(milliseconds[-2:])/60
    return hours + minutes + seconds + milliseconds


def parse_cue(cue_filename):
    """
    
    Parameters
    ----------
    cue_filename : STRING
        The name of the .cue file to be read and parsed.

    Returns
    -------
    track_times : LIST
        The time that the audio track starts, as given in the .cue file, 
        in decimal seconds
    titles : LIST
        The title of the track, as given in the .cue file.

    """
    from re import findall
    
    file = open(cue_filename, 'r') 
    track_times = [float(0)]
    titles = []
    
    while True: 
        # Get next line from file 
        line = file.readline() 
      
        # if line is empty end of file is reached 
        if not line: 
            break
        if line.strip()[:5] == 'INDEX 01':
            stringtime = stringtime_to_millisec(line.strip())
            if stringtime != float(0):
                track_times.append(stringtime)
        elif line.strip()[:5] == 'TITLE':
            track = findall(r'"([^"]*)"', line)
            titles.append(track)                

    # I've had trouble in the past         
    if not titles or track_times[-1]==float(0):
         warning_string = '''There is someting wrong with the .cue file, it\'s not formatted properly.
         Unable to continue processing!'''
         sys.exit(warning_string)     
    file.close() 
    return track_times, titles

    
def write_labels(label_filename, track_times, titles):
    """

    Parameters
    ----------
    label_filename : STRING
        The desired path/name of the output label file.
    track_times : LIST        
        The time that the audio track starts, as given in the .cue file, 
        in decimal seconds
    titles : LIST
        The title of the track, as given in the .cue file.

    Returns
    -------
    bool

    """
    # Due to the format of the .cue file, the first track title may be the album title
    if len(titles) > len(track_times):
        titles.pop(0)
        
    file = open(label_filename, "w")
    # Write out in tab delimited format
    for i in range(len(titles)):
        line = f"{track_times[i]:.5f}\t{track_times[i]:.5f}\t{titles[i][0]}"
        file.write(line)
        file.write("\n")
    
    file.close()
    return True


if __name__ == "__main__":
    import sys
    import os
    
    # Check if .cue file was given in the python call
    try:
        cue_filename = str(sys.argv[1])
    # If not, ask user to select        
    except:
        from tkinter.filedialog import askopenfilename
        cue_filename = askopenfilename(title="Select a cue file", filetypes=(("cue files",'*.cue'),))
        ## EXIT if user selects cancel
    
    # Read cue file and parse out the times and titles
    track_times, titles = parse_cue(cue_filename)
    # Make a label file name
    label_filename = os.path.splitext(cue_filename)[0]+'_labels.txt'
    # Write times and tracks to label file in proper format
    write_labels(label_filename, track_times, titles)
    print("File created")

You can run this script with or without specifying the optional cue file string attribute. If you don’t specify it the script will present a popup for you to select it:

$ python cue2labels.py

or

$ python cue2labels.py "inputfile.cue"

Your cue file contains the albums information, like album title, metadata, track titles, starting position, etc. It will look something like this:

Example .cue File

At this point you can convert the cue to a label file, either online or with the Python script. If you ran the Python script you will have a new file in the same directory as the cue file. Like this:

Example of Label File Created in Directory

The label file will be pretty simple with the track starting times and title names and will look like this:

Example Lable File

Now we are on the home stretch! Open Audacity and import your music flac/mp3 file. Now go to File > Import > Labels..., select the label file.

The labels are now magically inserted at the track start times with the title filled in!

Example Showing Labels Imported from File Within Audacity

From here go to File > Export > Export Multiple... yadda, yadda. The result are individual files for each track as shown here:

Example Output Showing Individual Track Files Were Created

On a final and unrelated note, if you’ve never heard of Tower of Power you are missing out. It’s an R&B band started in the 70’s that has a killer horn section.

Software Versions

This is an automated list of software versions used during the writing of this article.

SoftwareVersion
Audacity 2.3.3
Python 3.7.9