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

Originally I did this using shntool, however I found it to be limiting. shntool only has built in support for WAV, if you want any other output you must install the appropriate encoder. So for a time I was using shntool, then encoding the resultant files with FFmpeg. Turns out it is pretty easy to mimic shntool behavior with an Awk script, and since I already had Cygwin installed I was able to ditch the shntool requirement for my solutuion:

#!/bin/sh
if [ "$#" != 1 ]
then
  echo 'ff-split.sh [cue file]'
  exit
fi
awk '
$1 == "FILE" {
  split($0, i, /"/)
  file = i[2]
}
$1 == "TRACK" {
  tracks[++j] = $2
}
$1 == "TITLE" && j {
  split($0, i, /"/)
  titles[j] = i[2]
}
$1 == "INDEX" && $2 {
  split($3, i, ":")
  indexes[j] = sprintf("%d:%02d:%06.3f", i[1]/60, i[1]%60, i[2]+i[3]/75)
}
END {
  for (each in tracks) {
    print file
    print tracks[each]
    print titles[each]
    print indexes[each]
    print indexes[each+1]
  }
}
' "$1" |
while
  read file
  read track
  read title
  read start
  read stop
do
  ffmpeg -nostdin -i "$file" -ss $start ${stop:+-to $stop} "$track $title".m4a
done

Source

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.