Split audio file with cue file
Solution 1
I wrote an implementation in Go:
https://github.com/89z/cove/tree/4a721f1a/cue-split
It takes a Cue file as argument. Then it opens the Cue file using a CSV reader, with spaces instead of Commas and variable fields. Then it reads the Cue file line by line. First it finds the input FLAC file. Then for each Track, it gets the Title, Artist, Start time and Stop time.
Finally it runs FFmpeg to create each track, naming the separate files with the track number and track Title, as well as adding Metadata for Artist, Title and track number.
Solution 2
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.
Solution 3
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)
Related videos on Youtube
Zombo
Updated on September 18, 2022Comments
-
Zombo almost 2 years
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.
-
bertieb almost 6 yearsPlease read how to recommend software in answers, particularly the bits in bold; then edit your answer to follow the guidelines there. Thanks!
-
varun about 5 yearsThanks for the detailed answer, 6 years old answer is still helpful! :)