Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have a FLAC file and a CUE file. Using the command line, I would like to split this file, with one track per file.

share|improve this question
up vote 1 down vote accepted

I found this question, however this is for Linux. I went looking for shnsplit and found shntool. It only supports WAV out of the box, however you can download FLAC for FLAC support. I found a newer version than what is advertised on the shntool page.

Using examples from

I was able to construct this command

shntool split -f infile.cue -t %n-%t -m /- -o flac infile.flac

Example

share|improve this answer
1  
Steven, please consider leaving comment threads intact. I've often noticed you deleting comments that might be obsolete, and then flagging the others. This is okay if all the information was edited into the post, but there's no harm done from leaving the comments here stay, and there's nothing offensive about @Karan's comment either. Thank you. – slhck Apr 22 '13 at 8:10

The most popular and robust software for doing this on Windows is CUETools, which is free. It's primarily a GUI app, and it can't easily be configured from the command line, but it does have a command-line interface you can use to invoke the conversion with settings that were established in the GUI.

In the GUI, choose the "convert" profile (chooser is in the upper left corner). Take note of the Template in the CUE Paths section; this defines where the new files will go. If the Action section isn't greyed out, make sure it's on Encode, with the "default" script selected. In the Mode section, choose Tracks, and uncheck the AccurateRip box, unless you want it to verify before converting. Set the Audio Output to what type of audio files you want. In the Advanced Settings (gear icon in upper right corner), CUETools tab, Gaps handling, choose either Gaps Appended or Gaps Appended + HTOA, depending on whether you want any audio that comes before track 01 to be saved to a separate file or discarded (normally it'll just be a split-second of silence).

Now close the GUI; your settings are saved automatically. On the command line, you can now run it with those settings:

CUETools /convert infile.cue

The command will exit immediately, and a small GUI window will open to show you the progress and any error messages. This window will remain open until you click its close button. If all goes well, CUETools will write the converted audio file(s) and a converted cue sheet to a new folder. This cue sheet conversion is the main advantage over using shntool; the new .cue file will reference the split audio files. CUETools will also copy the .log file (if any) to the new folder.

share|improve this answer

I found mac (which is the command that shntool used for decoding APE files) is way less tolerant than ffmpeg if the source file contains minor errors.

Normally ffmpeg would still convert the file completely while mac very likely throws an error during the processing.

So I ended up writing a script for spliting APE file by parsing the CUE file and converting the APE file to FLAC files separated by titles using ffmpeg:

#!/usr/bin/env python2.7

import subprocess as subp
import sys
import os
from os.path import splitext, basename
import random
import glob

records = []
filename = ""
album=''
alb_artist=''
codec = 'flac'
ffmpeg_exec = 'ffmpeg'
encodingList = ('utf-8','euc-kr', 'shift-jis', 'cp936', 'big5')

filecontent = open(sys.argv[1]).read()
for enc in encodingList:
    try:
        lines = filecontent.decode(enc).split('\n')
        encoding = enc
        break
    except UnicodeDecodeError as e:
        if enc == encodingList[-1]:
            raise e
        else:
            pass

for l in lines:
    a = l.split()
    if not a:
        continue
    if a[0] == "FILE":
        filename = ' '.join(a[1:-1]).strip('\'"')
    elif a[0]=='TRACK':
        records.append({})
        records[-1]['index'] = a[1]
    elif a[0]=='TITLE':
        if len(records)>0:
            records[-1]['title'] = ' '.join(a[1:]).strip('\'"')
        else:
            album =  ' '.join(a[1:]).strip('\'"')
    elif a[0]=='INDEX' and a[1]=='01':
        timea = a[2].split(':')
        if len(timea) == 3 and int(timea[0]) >= 60:
            timea.insert(0, str(int(timea[0])/60))
            timea[1] = str(int(timea[1])%60)
        times = '{0}.{1}'.format(':'.join(timea[:-1]), timea[-1])
        records[-1]['start'] = times
    elif a[0]=='PERFORMER':
        if len(records)>1:
            records[-1]['artist'] = ' '.join(a[1:]).strip('\'"')
        else:
            alb_artist = ' '.join(a[1:]).strip('\'"')

for i, j in enumerate(records):
    try:
        j['stop'] = records[i+1]['start']
    except IndexError:
        pass

if not os.path.isfile(filename):
    tmpname = splitext(basename(sys.argv[1]))[0]+splitext(filename)[1]
    if os.path.exists(tmpname):
        filename = tmpname
        del tmpname
    else:
        for ext in ('.ape', '.flac', '.wav', '.mp3'):
            tmpname = splitext(filename)[0] + ext
            if os.path.exists(tmpname):
                filename = tmpname
                break

if not os.path.isfile(filename):
    raise IOError("Can't not find file: {0}".format(filename))

fstat = os.stat(filename)
atime = fstat.st_atime
mtime = fstat.st_mtime

records[-1]['stop'] = '99:59:59'

if filename.lower().endswith('.flac'):
    tmpfile = filename
else:
    tmpfile = splitext(filename)[0] + str(random.randint(10000,90000)) + '.flac'

try:
    if filename != tmpfile:
        ret = subp.call([ffmpeg_exec, '-hide_banner', '-y', '-i', filename, 
            '-c:a', codec,'-compression_level','12','-f','flac',tmpfile])

        if ret != 0:
            raise SystemExit('Converting failed.')

    for i in records:
        output = i['index'] +' - '+ i['title']+'.flac'
        commandline = [ffmpeg_exec, '-hide_banner', 
        '-y', '-i', tmpfile,
        '-c', 'copy', 
        '-ss', i['start'], '-to', i['stop'],
        '-metadata', u'title={0}'.format(i['title']), 
        '-metadata', u'artist={0}'.format(i.get('artist', '')),
        '-metadata', u'performer={0}'.format(i.get('artist', '')),
        '-metadata', u'album={0}'.format(album), 
        '-metadata', 'track={0}/{1}'.format(i['index'], len(records)), 
        '-metadata', u'album_artist={0}'.format(alb_artist), 
        '-metadata', u'composer={0}'.format(alb_artist), 
        '-metadata', 'encoder=Meow', 
        '-write_id3v1', '1', 
        output]
        ret = subp.call(commandline)
        if ret == 0:
            os.utime(output, (atime, mtime))
finally:
    if os.path.isfile(tmpfile):
        os.remove(tmpfile)
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.