Skip to content
Permalink
main
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
Latest commit 183b318 Apr 15, 2023 History
1 contributor

Users who have contributed to this file

executable file 907 lines (690 sloc) 23.3 KB
#!/bin/bash
# This script is meant to take an input BIN/CUE file, extract the raw
# track(s) (data / audio) in whatever format the user has specified
# through script arguments. The script simply separates all the tracks
# of BIN/CUE files.
# Available audio formats are:
# * cdr (native CD audio)
# * ogg (Ogg Vorbis)
# * flac (Free Lossless Audio Codec)
# If no format is specified as an argument, the script will extract all
# 3 formats, and create CUE sheets for all 3 formats as well.
# The original purpose of the script is to take DOS games that have CD
# audio, and getting rid of the need to store the uncompressed audio.
# Ogg Vorbis is a lossy codec, so the files are much smaller and near
# the same quality. In the case of FLAC, it's a lossless format so the
# quality is identical to native CD audio. The only difference is FLAC
# is losslessly compressed so the files are slightly smaller. The
# generated CUE sheets can be used with DOSBox, using the 'IMGMOUNT'
# command.
# https://www.dosbox.com/wiki/IMGMOUNT
# Another use case for this script is to simply extract the OST from
# games, to listen to.
# The script will work with all kinds of games, including PS1 and Sega
# Saturn games. All that's required is that the disc image is in the
# BIN/CUE format. Though, I'm not sure if there's emulators that can
# handle FLAC or Ogg Vorbis tracks. The point would mainly be to listen
# to the music.
# Yet another use case is to just split a BIN/CUE into its separate
# tracks, with the '-cdr' argument, without encoding the audio.
# It's possible to do a byteswap on the audio tracks (to switch the
# endianness / byte order), through the optional '-byteswap' argument.
# This is needed in some cases, or audio tracks will be white noise if
# the endianness is wrong. So, it's easy to tell whether or not the byte
# order is correct.
# The script is able to process CUE sheets that contain multiple FILE
# commands (list multiple BIN files). As an example, Redump will use 1
# BIN file / track, so that can be processed by the script directly in
# this case, without having to merge the BIN/CUE first.
# Earlier versions of the script used to depend on 'bchunk', which is a
# good program, but not needed anymore as other functions have replaced
# it.
if=$(readlink -f "$1")
if_bn=$(basename "$if")
if_bn_lc="${if_bn,,}"
# Creates a function called 'usage', which will print usage and quit.
usage () {
cat <<USAGE
Usage: $(basename "$0") [cue] [...]
Optional arguments:
-cdr
Audio tracks will be output exclusively in CD audio format.
-ogg
Audio tracks will be output exclusively in Ogg Vorbis.
-flac
Audio tracks will be output exclusively in FLAC.
-sox
Uses 'sox' instead of 'ffmpeg' to convert CD audio to WAV.
-byteswap
Reverses the endianness / byte order of the audio tracks.
USAGE
exit
}
# If input is not a real file, or it has the wrong extension, print
# usage and quit.
if [[ ! -f $if || ${if_bn_lc##*.} != 'cue' ]]; then
usage
fi
declare -A audio_types audio_types_run
audio_types=([cdr]='cdr' [ogg]='wav' [flac]='wav')
mode='ffmpeg'
byteswap=0
# The loop below handles the arguments to the script.
shift
while [[ $# -gt 0 ]]; do
case "$1" in
'-cdr')
audio_types_run[cdr]=1
shift
;;
'-ogg')
audio_types_run[ogg]=1
shift
;;
'-flac')
audio_types_run[flac]=1
shift
;;
'-sox')
mode='sox'
shift
;;
'-byteswap')
byteswap=1
shift
;;
*)
usage
;;
esac
done
if [[ ${#audio_types_run[@]} -eq 0 ]]; then
for type in "${!audio_types[@]}"; do
audio_types_run["${type}"]=1
done
fi
session="${RANDOM}-${RANDOM}"
of_name="${if_bn_lc%.*}"
of_name=$(sed -E 's/[[:blank:]]+/_/g' <<<"$of_name")
if_dn=$(dirname "$if")
of_dn="${PWD}/${of_name}-${session}"
declare -a format
declare -A regex
format[0]='^[0-9]+$'
format[1]='^([0-9]{2}):([0-9]{2}):([0-9]{2})$'
format[2]='[0-9]{2}:[0-9]{2}:[0-9]{2}'
format[3]='^(FILE) (.*) (.*)$'
format[4]='^(TRACK) ([0-9]{2,}) (.*)$'
format[5]="^(PREGAP) (${format[2]})$"
format[6]="^(INDEX) ([0-9]{2,}) (${format[2]})$"
format[7]="^(POSTGAP) (${format[2]})$"
regex[blank]='^[[:blank:]]*(.*)[[:blank:]]*$'
regex[path]='^(.*[\\\/])'
regex[fn]='^(.*)\.([^.]*)$'
regex[data]='^MODE([0-9])\/([0-9]{4})$'
regex[audio]='^AUDIO$'
declare -a tracks_file tracks_type tracks_sector tracks_start tracks_length tracks_total
declare -a files_cdr files_wav of_cue_cdr of_cue_ogg of_cue_flac
declare -A if_cue gaps
# Creates a function called 'check_cmd', which will check if the
# necessary commands are installed. If any of the commands are missing
# print them and quit.
check_cmd () {
declare -a missing_pkg
for cmd in "$@"; do
command -v "$cmd" 1>&-
if [[ $? -ne 0 ]]; then
missing_pkg+=("$cmd")
fi
done
if [[ ${#missing_pkg[@]} -gt 0 ]]; then
printf '\n%s\n\n' 'You need to install the following through your package manager:'
printf '%s\n' "${missing_pkg[@]}"
printf '\n'
exit
fi
}
# Creates a function called 'run_cmd', which will be used to run
# external commands, capture their output, and print the output (and
# quit) if the command fails.
run_cmd () {
declare exit_status
declare -a cmd_stdout
mapfile -t cmd_stdout < <(eval "$@" 2>&1; printf '%s\n' "$?")
exit_status="${cmd_stdout[-1]}"
unset -v cmd_stdout[-1]
# Prints the output from the command if it has a non-zero exit status,
# and then quits.
if [[ $exit_status != '0' ]]; then
printf '%s\n' "${cmd_stdout[@]}"
exit
fi
}
# Creates a function called 'get_files', which will be used to generate
# file lists to be used by other functions.
get_files () {
declare -a files_tmp
shopt -s nullglob
files_tmp=($@)
shopt -u nullglob
if [[ ${#files_tmp[@]} -gt 0 ]]; then
printf '%s\n' "${files_tmp[@]}" | sort -n
fi
}
# Creates a function called 'time_convert', which converts track
# timestamps back and forth between the time (mm:ss:ff) format and
# frames / sectors.
time_convert () {
time="$1"
m=0
s=0
f=0
# If argument is in the mm:ss:ff format...
if [[ $time =~ ${format[1]} ]]; then
m="${BASH_REMATCH[1]#0}"
s="${BASH_REMATCH[2]#0}"
f="${BASH_REMATCH[3]#0}"
# Converting minutes and seconds to frames, and adding all the numbers
# together.
m=$(( m * 60 * 75 ))
s=$(( s * 75 ))
time=$(( m + s + f ))
# If argument is in the frame format...
elif [[ $time =~ ${format[0]} ]]; then
f="$time"
s=$(( f / 75 ))
m=$(( s / 60 ))
f=$(( f % 75 ))
s=$(( s % 60 ))
time=$(printf '%02d:%02d:%02d' "$m" "$s" "$f")
fi
printf '%s' "$time"
}
# Creates a function called 'read_cue', which will read the source CUE
# sheet, get all the relevant information from it and store that in
# variables. It will also add full path to file names listed in the CUE
# sheet.
read_cue () {
declare line file_n track_n
declare -a files not_found wrong_format wrong_mode lines
declare -a error_types
declare -A error_msgs
file_n=0
track_n=0
error_types=('not_found' 'wrong_format' 'wrong_mode')
error_msgs[not_found]='The files below were not found:'
error_msgs[wrong_format]='The files below have the wrong format:'
error_msgs[wrong_mode]='The tracks below have an unrecognized mode:'
# Creates a function called 'handle_command', which will process each
# line in the CUE sheet and store all the relevant information in the
# 'if_cue' hash.
handle_command () {
# If line is a FILE command...
if [[ $line =~ ${format[3]} ]]; then
match=("${BASH_REMATCH[@]:1}")
# Strips quotes, and path that may be present in the CUE sheet, and adds
# full path to the basename.
fn=$(tr -d '"' <<<"${match[1]}" | sed -E "s/${regex[path]}//")
fn="${if_dn}/${fn}"
# If file can't be found, or format isn't binary, then it's useless even
# trying to process this CUE sheet.
if [[ ! -f $fn ]]; then
not_found+=("$fn")
fi
if [[ ${match[2]} != 'BINARY' ]]; then
wrong_format+=("$fn")
fi
files+=("$fn")
(( file_n += 1 ))
if_cue["${file_n},filename"]="$fn"
if_cue["${file_n},file_format"]="${match[2]}"
return
fi
# If line is a TRACK command...
if [[ $line =~ ${format[4]} ]]; then
match=("${BASH_REMATCH[@]:1}")
track_n="${match[1]#0}"
# Saves the file number associated with this track.
tracks_file["${track_n}"]="$file_n"
# Saves the current track number (and in effect, every track number) in
# an array so the exact track numbers can be referenced later.
tracks_total+=("$track_n")
# Figures out if this track is data or audio, and saves the sector size.
# Typical sector size is 2048 bytes for data CDs, and 2352 for audio.
if [[ ${match[2]} =~ ${regex[data]} ]]; then
tracks_type["${track_n}"]='data'
tracks_sector["${track_n}"]="${BASH_REMATCH[2]}"
fi
if [[ ${match[2]} =~ ${regex[audio]} ]]; then
tracks_type["${track_n}"]='audio'
tracks_sector["${track_n}"]=2352
fi
# If the track mode was not recognized, then it's useless even trying to
# process this CUE sheet.
if [[ -z ${tracks_type[${track_n}]} ]]; then
wrong_mode+=("$track_n")
fi
if_cue["${track_n},track_number"]="${match[1]}"
if_cue["${track_n},track_mode"]="${match[2]}"
return
fi
# If line is a PREGAP command...
if [[ $line =~ ${format[5]} ]]; then
match=("${BASH_REMATCH[@]:1}")
frames=$(time_convert "${match[1]}")
if_cue["${track_n},pregap"]="$frames"
return
fi
# If line is an INDEX command...
if [[ $line =~ ${format[6]} ]]; then
match=("${BASH_REMATCH[@]:1}")
index_n="${match[1]#0}"
frames=$(time_convert "${match[2]}")
if_cue["${track_n},index,${index_n}"]="$frames"
return
fi
# If line is a POSTGAP command...
if [[ $line =~ ${format[7]} ]]; then
match=("${BASH_REMATCH[@]:1}")
frames=$(time_convert "${match[1]}")
if_cue["${track_n},postgap"]="$frames"
return
fi
}
# Reads the source CUE sheet and processes the lines.
mapfile -t lines < <(tr -d '\r' <"$if" | sed -E "s/${regex[blank]}/\1/")
for (( i = 0; i < ${#lines[@]}; i++ )); do
line="${lines[${i}]}"
handle_command
done
# If errors were found, print them and quit.
for error in "${error_types[@]}"; do
declare elements msg_ref list_ref
elements=0
case "$error" in
'not_found')
elements="${#not_found[@]}"
;;
'wrong_format')
elements="${#wrong_format[@]}"
;;
'wrong_mode')
elements="${#wrong_mode[@]}"
;;
esac
if [[ $elements -eq 0 ]]; then
continue
fi
msg_ref="error_msgs[${error}]"
list_ref="${error}[@]"
printf '\n%s\n\n' "${!msg_ref}"
printf '%s\n' "${!list_ref}"
printf '\n'
exit
done
}
# Creates a function called 'get_gaps', which will get pregap and
# postgap from the CUE sheet for the track number given as argument.
# If there's both a pregap specified using the PREGAP command and INDEX
# command, those values will be added together. However, a CUE sheet is
# highly unlikely to specify a pregap twice like that.
get_gaps () {
track_n="$1"
# If the CUE sheet contains PREGAP or POSTGAP commands, save that in the
# 'gaps' hash. Add it to the value that might already be there, cause of
# pregaps specified by INDEX commands.
pregap_ref="if_cue[${track_n},pregap]"
postgap_ref="if_cue[${track_n},postgap]"
if [[ -n ${!pregap_ref} ]]; then
(( ${gaps[${track_n},pre]} += ${!pregap_ref} ))
fi
if [[ -n ${!postgap_ref} ]]; then
(( ${gaps[${track_n},post]} += ${!postgap_ref} ))
fi
}
# Creates a function called 'get_length', which will get the start
# position, and length, (in bytes) of all tracks in the respective BIN
# files.
get_length () {
declare bytes_pregap bytes_track bytes_total frames
declare pregap_this_ref pregap_next_ref
declare index0_this_ref index1_this_ref index0_next_ref index1_next_ref
declare file_n_this_ref file_n_next_ref file_ref
declare sector_ref start_ref
bytes_total=0
# Creates a function called 'get_size', which will get the track length
# by reading the size of the BIN file associated with this track. This
# function will also reset the 'bytes_total' variable to '0' (as the
# current track is last in the current BIN file).
get_size () {
declare size
size=$(stat -c '%s' "${!file_ref}")
bytes_track=$(( size - ${!start_ref} ))
bytes_total=0
tracks_length["${this}"]="$bytes_track"
}
for (( i = 0; i < ${#tracks_total[@]}; i++ )); do
j=$(( i + 1 ))
this="${tracks_total[${i}]}"
next="${tracks_total[${j}]}"
pregap_this_ref="gaps[${this},pre]"
pregap_next_ref="gaps[${next},pre]"
index0_this_ref="if_cue[${this},index,0]"
index1_this_ref="if_cue[${this},index,1]"
index0_next_ref="if_cue[${next},index,0]"
index1_next_ref="if_cue[${next},index,1]"
file_n_this_ref="tracks_file[${this}]"
file_n_next_ref="tracks_file[${next}]"
file_ref="if_cue[${!file_n_this_ref},filename]"
sector_ref="tracks_sector[${this}]"
start_ref="tracks_start[${this}]"
# If the CUE sheet specifies a pregap using the INDEX command, save that
# in the 'gaps' hash so it can later be converted to a PREGAP command.
if [[ -n ${!index0_this_ref} && ${!pregap_this_ref} -eq 0 ]]; then
gaps["${this},pre"]=$(( ${!index1_this_ref} - ${!index0_this_ref} ))
fi
if [[ -n ${!index0_next_ref} ]]; then
gaps["${next},pre"]=$(( ${!index1_next_ref} - ${!index0_next_ref} ))
fi
# Converts potential pregap frames to bytes, and adds it to the total
# bytes of the track position. This makes it possible for the
# 'copy_track' function to skip over the useless junk data in the
# pregap, when reading the track.
bytes_pregap=$(( ${!pregap_this_ref} * ${!sector_ref} ))
tracks_start["${this}"]=$(( bytes_total + bytes_pregap ))
# If this is the last track, get the track length by reading the size of
# the BIN file associated with this track.
if [[ -z $next ]]; then
get_size
continue
fi
# If the BIN file associated with this track is the same as the next
# track, get the track length by subtracting the start position of the
# current track from the position of the next track.
if [[ ${!file_n_this_ref} -eq ${!file_n_next_ref} ]]; then
frames=$(( ${!index1_next_ref} - ${!index1_this_ref} ))
frames=$(( frames - ${!pregap_next_ref} ))
bytes_track=$(( frames * ${!sector_ref} ))
(( bytes_total += (bytes_track + bytes_pregap) ))
tracks_length["${this}"]="$bytes_track"
fi
# If the BIN file associated with this track is different from the next
# track, get the track length by reading the size of the BIN file
# associated with this track.
if [[ ${!file_n_this_ref} -ne ${!file_n_next_ref} ]]; then
get_size
fi
done
}
# Creates a function called 'loop_set', which will get the start
# positions, lengths, pregaps and postgaps for all tracks.
loop_set () {
declare track_n
for (( i = 0; i < ${#tracks_total[@]}; i++ )); do
track_n="${tracks_total[${i}]}"
gaps["${track_n},pre"]=0
gaps["${track_n},post"]=0
done
get_length
for (( i = 0; i < ${#tracks_total[@]}; i++ )); do
track_n="${tracks_total[${i}]}"
get_gaps "$track_n"
done
}
# Creates a function called 'block_calc', which will be used to get the
# optimal block size to use in the 'copy_track' function when reading
# and writing tracks using 'dd'. Bigger block sizes makes the process
# faster, and the reason for being able to handle variable block sizes
# is that it's technically possible for a CUE sheet to contain tracks
# that have different sector sizes. And that will affect the start
# positions of tracks. This function counts down from 16KB, subtracting
# 4 bytes at each iteration of the loop, until a matching block size is
# found. We're using 4 byte increments cause that guarantees the block
# size will be divisible by the common CD sector sizes:
# * 2048
# * 2324
# * 2336
# * 2352
# * 2448
block_calc () {
bytes1="$1"
bytes2="$2"
declare block_size block_diff1 block_diff2
block_size=16384
block_diff1=$(( bytes1 % block_size ))
block_diff2=$(( bytes2 % block_size ))
until [[ $block_diff1 -eq 0 && $block_diff2 -eq 0 ]]; do
(( block_size -= 4 ))
block_diff1=$(( bytes1 % block_size ))
block_diff2=$(( bytes2 % block_size ))
done
printf '%s' "$block_size"
}
# Creates a function called 'copy_track', which will extract the raw
# binary data for the track number given as argument, from the BIN file.
copy_track () {
track_n="$1"
track_type="$2"
declare file_n_ref file_ref start_ref length_ref
declare ext block_size skip count
declare -a args
file_n_ref="tracks_file[${track_n}]"
file_ref="if_cue[${!file_n_ref},filename]"
# Depending on whether the track type is data or audio, use the
# appropriate file name extension for the output file.
case "$track_type" in
'data')
ext='bin'
;;
'audio')
ext='cdr'
;;
esac
of_bin=$(printf '%s/%s%02d.%s' "$of_dn" "$of_name" "$track_n" "$ext")
# Creates the first part of the 'dd' command.
args=(dd if=\""${!file_ref}"\" of=\""${of_bin}"\")
# Does a byteswap if the script was run with the '-byteswap' option, and
# the track is audio.
if [[ $byteswap -eq 1 && $track_type == 'audio' ]]; then
args+=(conv=swab)
fi
# Gets the start position, and length, of the track.
start_ref="tracks_start[${track_n}]"
length_ref="tracks_length[${track_n}]"
# Gets the optimal block size to use with 'dd'.
block_size=$(block_calc "${!start_ref}" "${!length_ref}")
args+=(bs=\""${block_size}"\")
skip=$(( ${!start_ref} / block_size ))
count=$(( ${!length_ref} / block_size ))
# If the start position of the track is greater than '0', skip blocks
# until the start of the track.
if [[ $skip -gt 0 ]]; then
args+=(skip=\""${skip}"\")
fi
# If the track length is greater than '0', copy only a limited number of
# blocks.
if [[ $count -gt 0 ]]; then
args+=(count=\""${count}"\")
fi
# Runs 'dd'.
run_cmd "${args[@]}"
}
# Creates a function called 'copy_track_type', which will extract the
# raw binary data for all tracks of either the data or audio type.
copy_track_type () {
track_type="$1"
declare track_n track_type_ref
declare -a tracks
# Depending on whether the track type is set to 'all', 'data' or
# 'audio', copy only that type from the source BIN file.
if [[ $track_type == 'all' ]]; then
tracks=("${!tracks_type[@]}")
else
for (( i = 0; i < ${#tracks_total[@]}; i++ )); do
track_n="${tracks_total[${i}]}"
track_type_ref="tracks_type[${track_n}]"
if [[ ${!track_type_ref} == "$track_type" ]]; then
tracks+=("$track_n")
fi
done
fi
if [[ ${#tracks[@]} -eq 0 ]]; then
return
fi
for (( i = 0; i < ${#tracks[@]}; i++ )); do
track_n="${tracks[${i}]}"
track_type_ref="tracks_type[${track_n}]"
copy_track "$track_n" "${!track_type_ref}"
done
# Creates a file list to be used later in the 'create_cue' function.
mapfile -t files_cdr < <(get_files "*.bin" "*.cdr")
}
# Creates a function called 'cdr2wav', which will convert the extracted
# CDR files to WAV (using 'ffmpeg' or 'sox').
cdr2wav () {
type="$1"
declare type_tmp
declare -a files
type_tmp="${audio_types[${type}]}"
# If type is not 'wav' or WAV files have already been produced, return
# from this function.
if [[ $type_tmp != 'wav' || ${#files_wav[@]} -gt 0 ]]; then
return
fi
mapfile -t files < <(get_files "*.cdr")
for (( i = 0; i < ${#files[@]}; i++ )); do
cdr_if="${files[${i}]}"
wav_of="${cdr_if%.*}.wav"
declare args_ref
declare -a args_ffmpeg args_sox
# Creates the command arguments for 'ffmpeg' and 'sox'.
args_ffmpeg=(-ar 44.1k -ac 2)
args_ffmpeg=(ffmpeg -f s16le "${args_ffmpeg[@]}" -i \""${cdr_if}"\" -c:a pcm_s16le "${args_ffmpeg[@]}" \""${wav_of}"\")
args_sox=(sox -L \""${cdr_if}"\" \""${wav_of}"\")
# Depending on what the mode is, run 'ffmpeg' or 'sox' on the CDR file,
# specifying 'little-endian' for the input.
args_ref="args_${mode}[@]"
run_cmd "${!args_ref}"
# If 'cdr' is not among the chosen audio types, delete the CDR file.
if [[ -z ${audio_types_run[cdr]} ]]; then
rm -f "$cdr_if" || exit
fi
unset -v args_ref args_ffmpeg args_sox
done
# Creates a file list to be used later in the 'create_cue' function.
mapfile -t files_wav < <(get_files "*.bin" "*.wav")
}
# Creates a function called 'encode_audio', which will encode the WAVs
# created by previously run functions.
encode_audio () {
type="$1"
declare type_tmp
declare -a files
type_tmp="${audio_types[${type}]}"
mapfile -t files < <(get_files "*.wav")
# If type is not 'wav' or there's no WAV files, return from this
# function. This makes it possible for the script to finish normally,
# even if there's no audio tracks.
if [[ $type_tmp != 'wav' || ${#files[@]} -eq 0 ]]; then
return
fi
case "$type" in
'ogg')
oggenc --quality=10 "${files[@]}" || exit
;;
'flac')
flac -8 "${files[@]}" || exit
;;
esac
}
# Creates a function called 'create_cue', which will create a new CUE
# sheet, based on the file lists created by the 'copy_track_type' and
# 'cdr2wav' functions.
create_cue () {
type="$1"
declare index_string elements type_tmp
declare -a offset
declare -A ext_format
index_string='INDEX 01 00:00:00'
offset=(' ' ' ')
ext_format=([bin]='BINARY' [cdr]='BINARY' [ogg]='OGG' [flac]='FLAC')
type_tmp="${audio_types[${type}]}"
# Creates a function called 'set_track_info', which will add FILE,
# TRACK, PREGAP, INDEX and POSTGAP commands. Pregap and postgap is only
# added if they exist in the source CUE sheet.
set_track_info () {
declare mode_ref format_ref track_string
declare pregap_ref postgap_ref time_tmp
mode_ref="if_cue[${track_n},track_mode]"
format_ref="ext_format[${ext}]"
track_string=$(printf 'TRACK %02d %s' "$track_n" "${!mode_ref}")
eval of_cue_"${type}"+=\(\""FILE \\\"${fn}.${ext}\\\" ${!format_ref}"\"\)
eval of_cue_"${type}"+=\(\""${offset[0]}${track_string}"\"\)
pregap_ref="gaps[${track_n},pre]"
postgap_ref="gaps[${track_n},post]"
if [[ ${!pregap_ref} -gt 0 ]]; then
time_tmp=$(time_convert "${!pregap_ref}")
eval of_cue_"${type}"+=\(\""${offset[1]}PREGAP ${time_tmp}"\"\)
fi
eval of_cue_"${type}"+=\(\""${offset[1]}${index_string}"\"\)
if [[ ${!postgap_ref} -gt 0 ]]; then
time_tmp=$(time_convert "${!postgap_ref}")
eval of_cue_"${type}"+=\(\""${offset[1]}POSTGAP ${time_tmp}"\"\)
fi
}
# Goes through the list of files produced by previously run functions,
# and creates a new CUE sheet based on that.
for (( i = 0; i < ${#tracks_total[@]}; i++ )); do
line_ref="files_${type_tmp}[${i}]"
track_n="${tracks_total[${i}]}"
declare fn ext
# Separates file name and extension.
if [[ ${!line_ref} =~ ${regex[fn]} ]]; then
fn="${BASH_REMATCH[1]}"
ext="${BASH_REMATCH[2]}"
fi
# If the extension is 'wav', then the correct extension is the same as
# the current audio type.
if [[ $ext == 'wav' ]]; then
ext="$type"
fi
# Sets all the relevant file / track information.
set_track_info
unset -v fn ext
done
}
# Creates a function called 'clean_up', which deletes temporary files:
# * Potential WAV files
clean_up () {
declare -a files
mapfile -t files < <(get_files "*.wav")
for (( i = 0; i < ${#files[@]}; i++ )); do
fn="${files[${i}]}"
rm -f "$fn" || exit
done
}
# Checks if 'oggenc', 'flac' are installed. Depending on which mode is
# set, check if 'ffmpeg' or 'sox' is installed.
check_cmd 'oggenc' 'flac' "$mode"
# Creates the output directory and changes into it.
mkdir "$of_dn" || exit
cd "$of_dn" || exit
# Runs the functions.
read_cue
loop_set
copy_track_type 'all'
for type in "${!audio_types_run[@]}"; do
cdr2wav "$type"
encode_audio "$type"
create_cue "$type"
done
# Prints the created CUE sheet to the terminal, and to the output file.
for type in "${!audio_types_run[@]}"; do
of_cue="${of_dn}/${of_name}01_${type}.cue"
lines_ref="of_cue_${type}[@]"
printf '\n'
printf '%s\r\n' "${!lines_ref}" | tee "$of_cue"
done
printf '\n'
# Deletes temporary files.
clean_up