First post, by Akuma
BIN/CUE to ISO/CUE converter v1.09, supports : FLAC,MP3,OGG,OPUS
Copyright (c) 2020 by Akuma under GNU General Public License 2.0
Features:
*Converts BIN/CUE to ISO/WAV
*Encodes WAV to FLAC,MP3,OGG,OPUS
*Parallel Encoding (credit krcroft)
*Presets and presetfiles
*Writes a new CUE sheet
*Skips existing files
*Dependency checks
Step 1. The script will first use bchunk to convert BIN/CUE to
ISO/WAV. Files will be placed in a newly created folder with the
basename of the CUE file. So for /my/games/mygame.cue it will be
placed in: /my/games/mygame.
The bchunk process is logged to:
/my/games/mygame/bchunk.log
Step 2. Then it encodes all WAV files with the preset to:
/my/games/mygame/[preset]/trackXX.[codec] (XX=02-99)
The encoding process is logged to:
/my/games/mygame/[preset]/trackXX.log
Step 3. A CUE sheet is created for the new files and is
written to the [preset] folder: /my/games/mygame/[preset]/cdrom.cue
Step 4. A batch file is created to use with DOSBox
written to the [preset] folder: /my/games/mygame/[preset]/cdrom.bat
Usage:
usage: dbx-conv [-f] [-s] <cue/cdrom> <preset/file> [options]
eg:
dbx-conv myfile.cue ogg (default preset)
dbx-conv myfile.cue opus_music_simple_mono (advanced preset)
dbx-conv myfile.cue myfile.preset (preset file)
dbx-conv myfile.cue mp3 -b 192 (custom mp3)
(optional)
* -f : force overwrite
(optional) binchunker:
* -s : swap byte order in audio tracks
codecs/default presets:
* flac
* mp3
* ogg
* opus
advanced presets: (mp3/ogg/opus)
* <codec>_music_simple_mono
* <codec>_music_simple_stereo
* <codec>_music_moderate_mono
* <codec>_music_moderate_stereo
* <codec>_music_complex_mono
* <codec>_music_complex_stereo
* <codec>_speech_simple_mono
* <codec>_speech_simple_stereo
* <codec>_speech_moderate_mono
* <codec>_speech_moderate_stereo
* <codec>_speech_complex_mono
* <codec>_speech_complex_stereo
* <codec>_filler_simple_mono
* <codec>_filler_simple_stereo
* <codec>_filler_moderate_mono
* <codec>_filler_moderate_stereo
* <codec>_filler_complex_mono
* <codec>_filler_complex_stereo
available presets: (flac)
* flac_max
options:
* codec options passthrough
(see codec manpage/manual)
#!/bin/bash
# BIN/CUE to ISO/CUE converter v$version, supports : FLAC,MP3,OGG,OPUS"
# Copyright (c) 2020 by Akuma under GNU General Public License 2.0"
#[spdx-license-identifier]-----------------------------------------------------
# GPL-2.0-only @ https://spdx.org/licenses/GPL-2.0-only.html
#[dependencies]----------------------------------------------------------------
# bchunk cdrdao flac lame opus-tools vorbis-tools
#
# *BinChunker (CUE/WAV) http://he.fi/bchunk/
# *CdrDao (CUE/BIN) http://cdrdao.sourceforge.net/
# *Free Lossless Audio Codec (FLAC) https://xiph.org/flac/
# *The LAME Project Codec (MP3) https://lame.sourceforge.io/
# *The Ogg container Codec (OGG) https://xiph.org/ogg/
# *Opus Interactive Audio Codec (OPUS) https://opus-codec.org/
#
#[versioning]------------------------------------------------------------------
version=1.09
self="$(readlink -f "$0")"
#[global settings]-------------------------------------------------------------
set -euo pipefail
OLDIFS=$IFS;IFS=$'\t\r\n' #\r for WIN/DOS cue-files
trap "result" 0 1 3 15
#[functions]-------------------------------------------------------------------
result(){
case $? in
0) echo -e "\nSuccess";;
100) usage;;
101) echo "error: '$file' file not found";;
102) echo "error: unknown preset '${preset}'";;
103) echo "error: already in ISO format";;
104) echo "error: no audio tracks found";;
105) echo "error: merge files to a single bin/cue first";;
106) echo "error: '$file' dependency not found";;
107) echo "error: encoding failed, see logs";;
108) echo "error: cdrom.bin/cue already exist";;
109) echo "error: cdrdao failed to image cdrom";;
110) echo "error: toc2cue failed to convert toc-file to cue-file'";;
111) echo "presetfile: error, tracknumber out of range '$track'";;
112) echo -e "presetfile: '$presetfile'\nerror: unknown preset '$preset'";;
esac
echo
IFS=$OLDIFS
}
usage(){ cat <<EOF
usage: dbx-conv [-f] [-s] <cue/cdrom> <preset/file> [options]
eg:
dbx-conv myfile.cue ogg (default preset)
dbx-conv myfile.cue opus_music_simple_mono (advanced preset)
dbx-conv myfile.cue myfile.preset (preset file)
dbx-conv myfile.cue mp3 -b 192 (custom mp3)
(optional)
* -f : force overwrite
(optional) binchunker:
* -s : swap byte order in audio tracks
codecs/default presets:
* flac
* mp3
* ogg
* opus
advanced presets: (mp3/ogg/opus)
* <codec>_music_simple_mono
* <codec>_music_simple_stereo
* <codec>_music_moderate_mono
* <codec>_music_moderate_stereo
* <codec>_music_complex_mono
* <codec>_music_complex_stereo
* <codec>_speech_simple_mono
* <codec>_speech_simple_stereo
* <codec>_speech_moderate_mono
* <codec>_speech_moderate_stereo
* <codec>_speech_complex_mono
* <codec>_speech_complex_stereo
* <codec>_filler_simple_mono
* <codec>_filler_simple_stereo
* <codec>_filler_moderate_mono
* <codec>_filler_moderate_stereo
* <codec>_filler_complex_mono
* <codec>_filler_complex_stereo
available presets: (flac)
* flac_max
options:
* codec options passthrough
(see codec manpage/manual)
EOF
}
checkfile(){ file="$1";[[ -f "$file" ]] || exit 101;}
getbin(){ grep -Po '.*?"\K[^"]+(?=")' "$1";}
checkapp(){ file="$1";command -v "$file" >/dev/null 2>&1 || exit 104;}
dumpcdrom(){
workdir="$PWD"
bin="$workdir/cdrom.bin"
cue="${bin%.*}.cue"
local cdrom="$1" toc="${bin%.*}.toc" log="${bin%.*}.log"
[[ -f "$bin" ]] && exit 108
echo " *Dumping: \"$cdrom\" to BIN/TOC/CUE"
if [[ -f "$bin" && "$overwrite" = "false" ]]; then
checkapp "cdrdao"
[[ "$overwrite" = "true" ]] && echo " ! overwrite enabled"
echo " > ${bin##*/}"
cdrdao read-cd --datafile "$bin" --driver generic-mmc:0x20000\
--device "$cdrom" --read-raw "$toc" >"$log" 2>&1 || exit 109
else
echo " - ${bin##*/} exists"
fi
if [[ -f "$cue" && "$overwrite" = "false" ]]; then
checkapp "toc2cue"
[[ "$overwrite" = "true" ]] && echo " ! overwrite enabled"
echo " > ${cue##*/}"
toc2cue "$toc" "$cue" || exit 110
else
echo " - ${cue##*/} exists"
fi
}
range(){ seq ${1%-*} ${1#*-};}
queue(){
local workdir="$1" outdir="$2" codec="$3"
shift 3
local options=$@
local codec options infile outfile track
for infile in "$workdir"/*.wav;do
outfile="${infile##*/}";outfile="$outdir/${outfile%.*}.$codec"
track="${infile##*/}";track="${track:5:2}"
queue+=([10#$track]="compress \"$infile\" \"$outfile\" $codec $options")
done
}
queue_presetfile(){
local workdir="$1" outdir="$2" presetfile="$3" que="$4"
local codec options infile outfile track tracks saveifs
saveifs=$IFS
total=$(grep -c TRACK "$que")
while read line;do
IFS='=';set -- $line
preset=${1:-}
grep -q "^$preset=" "$presets" || exit 112
codec=${1:-};codec=${codec/_*}
options=${!1:-}
tracks=${2:-}
IFS=$' \t\r\n'
tracks=$(
for track in $tracks
do case $track in
default) range 2-$total;; #fill queue with the default preset
*-*) range $track;; #change preset for range eg: 12-20
[0-9]*) echo $track;; #change preset per track eg: 13
*) exit 130;; #presetfile script error
esac
done
)
for track in $tracks;do
[[ $track -ge 2 && $track -le $total ]] || exit 111
infile=$(printf "$workdir/track%02i.wav" $track)
outfile=$(printf "$outdir/track%02i.$codec" $track)
queue+=([$track]="compress \"$infile\" \"$outfile\" $codec $options")
done
done < <(grep '^[^#].*=' "$presetfile") #grep presets from presetfile
IFS=$saveifs
}
compress(){ #infile outfile codec options
local infile="$1" outfile="$2" codec=$3; shift 3; options=$@
local log="${outfile%.*}.log"
trap "rm \"$outfile\";exit 106" 2
IFS=$' \t\r\n'
case "$codec" in
flac) set flac $options "$infile" -o "$outfile";;
ogg) set oggenc $options "$infile" -o "$outfile";;
opus) set opusenc $options "$infile" "$outfile";;
mp3) set lame $options "$infile" "$outfile";;
esac
IFS=$'\t\r\n'
checkapp $1
echo $@ >"$log"; $@ >>"$log" 2>&1 || exit 112;
}
to_frames(){ set ${1//:/ }; echo $(((60*10#$1+10#$2)*75+10#$3));}
to_time(){\
printf "%02d:%02d:%02d" $(((10#$1/75)/60)) $(((10#$1/75)%60)) $((10#$1%75));}
makecue(){ #cue outdir
local cue="$1" outdir="$2" line file type c zero offset time
IFS=$' \t\r\n'
while read line;do
set $line
case "$1" in
FILE)
unset offset #reset offset on new file
;;
TRACK)
c=$(printf "%02d" $((10#$2)))
for file in "$outdir"/track$c.*;do
case "${file##*.}" in
iso) type=BINARY;;
mp3|ogg|flac|opus) type=${file##*.};type="${type^^}";;
*) unset type zero;; #reset index zero on new track
esac
[[ -n "${type:=}" ]] && {
printf "FILE \"${file##*/}\" $type\n"
printf "${line/MODE*/MODE1\/2048}\n"
}
done
;;
INDEX)
time="00:00:00"
[[ -n "${zero:=}" ]] && {
offset="$(to_frames $3)"
time="$(to_time $((offset-zero)))"
}
[[ $2 -eq 0 ]] && zero="$(to_frames $3)"
printf "${line/??:??:??/$time}\n";;
*)
printf "$line\n"
;;
esac
done < <(cat "$cue")
IFS=$'\t\r\n'
}
#[title]-----------------------------------------------------------------------
echo "BIN/CUE to ISO/CUE converter v$version, supports : FLAC,MP3,OGG,OPUS"
echo "Copyright (c) 2020 by Akuma under GNU General Public License 2.0"
echo
#[codec presets]---------------------------------------------------------------
presets="${self%.*}.presets"
checkfile "$presets"
source "$presets"
#[commandline arguments]-------------------------------------------------------
#usage: self [-f] [-s] cue-file preset/presetfile/codec [codec-options]
#options
byteswap=""
overwrite=false
while :; do
case ${1:-} in
-f) overwrite="true";; #set overwrite flag
-s) byteswap="-s";; #bchunk: swap byteorder in audiotracks
*) break;;
esac
shift
done
#2+ arguments remaining else show usage
[[ $# -ge 2 ]] || exit 100
#if cdrom then create bin/cue and set cue
if [[ -b "$1" ]]; then
dumpcdrom
else
cue="$(readlink -f "$1")"
fi
#check cue-file
checkfile "$cue"
grep -q 'MODE2/2048' "$cue" && exit 103
grep -q 'TRACK.*AUDIO' "$cue" || exit 104
[[ $(grep -c 'FILE' "$cue") -eq 1 ]] || exit 105
bin="${cue%/*}/$(getbin $cue)"
checkfile "$bin"
shift
#[workdir]---------------------------------------------------------------------
workdir="${cue%.*}"
[[ -d "$workdir" ]] || mkdir -p "$workdir"
#[queue]-----------------------------------------------------------------------
case $# in
0) exit 100
;;
1) if [[ -f "$1" ]]; then
presetfile="$(readlink -f $1)"
checkfile "$presetfile"
outdir="$workdir/presetfile"
queue_presetfile "$workdir" "$outdir" "$presetfile" "$cue"
encoding="${presetfile##*/}"
else
preset="${1,,}"
grep -q "^$preset=" "$presets" || exit 102
outdir="$workdir/$preset"
codec="${preset/_*}"
options="${!preset}"
queue "$workdir" "$outdir" "$codec" "$options"
encoding="$preset"
fi
;;
*) outdir="$workdir/custom"
queue "$workdir" "$outdir" $@
encoding="CUSTOM: $@"
;;
esac
[[ -d "$outdir" ]] || mkdir -p "$outdir"
set --
#[conversion]------------------------------------------------------------------
cd "$workdir"
echo " *Converting BIN/CUE to ISO/WAV"
outfile="$workdir/track01.iso"
if [[ -f "$outfile" ]]; then
echo " - ${outfile##*/} exists"
else
echo " > ${outfile##*/}"
checkapp bchunk
[[ -n "${byteswap:-}" ]] && echo " > swapping byte order in audio tracks"
bchunk $byteswap -w "$bin" "$cue" track >> "bchunk.log" 2>&1
fi
symlink="$outdir/${outfile##*/}"
[[ -h "$symlink" ]] || ln -s "../${outfile##*/}" "$symlink" >/dev/null 2>&1
[[ -f "${outfile%.*}.ugh" ]] && mv "${outfile%.*}.ugh" "${outfile}"
#[encoding]--------------------------------------------------------------------
echo " *Encoding WAV with: \"$encoding\""
[[ -d "$outdir" ]] || mkdir -p "$outdir"
[[ "$overwrite" = "true" ]] && echo " ! overwrite enabled"
nproc="$(nproc)"
for command in "${!queue[@]}"; do
IFS='"';set -- ${queue[$command]}
outfile="$4";IFS=$' \t\r\n'
if [[ -f "$outfile" && "$overwrite" = "false" ]]; then
echo " - ${outfile##*/} exists";continue
else
echo " > ${outfile##*/}"
( set -e; eval ${queue[$command]} ) &
[[ $? -eq 0 ]] || exit 107
while [[ "$(jobs -p | wc -l)" -gt "$nproc" ]]; do sleep 0.3; done
fi
done
#[cuesheet]--------------------------------------------------------------------
echo " *Writing CUE sheet"
outfile="$outdir/cdrom.cue"
[[ -f "$outfile" ]] && rm "$outfile"
if [[ -f "$outfile" ]]; then
echo " - ${outfile##*/} exists"
else
echo " > ${outfile##*/}"
makecue "$cue" "$outdir" >"$outfile"
fi
#[cdrom.bat]-------------------------------------------------------------------
echo " *Writing BAT file"
outfile="$outdir/cdrom.bat"
if [[ -f "$outfile" ]]; then
echo " - ${outfile##*/} exists"
else
echo " > ${outfile##*/}"
echo imgmount d -t cdrom cdrom.cue >"$outfile"
fi
exit 0
# BIN/CUE to ISO/CUE converter v$version, supports : FLAC,MP3,OGG,OPUS
# Copyright (c) 2020 by Akuma & krcroft under GNU General Public License 2.0
# This preset file can be customized to your needs
#FLAC: (flac 1.3.3-1 libflac8 1.3.3-1)
# flac <preset> "$infile" -o "$outfile"
flac="-8 -b 512 -P=4096 -S=1s"
flac_max="-8 -e -p -b 512 -P=4096 -S=1s"
#OGG: (vorbis-tools 1.4.0-11, libogg0 1.3.4-0)
# oggenc <preset> "$infile" -o "$outfile"
ogg=""
ogg_speech_simple_mono="--advanced-encode-option lowpass_frequency=13000 --bitrate 44 --downmix"
ogg_speech_moderate_mono="--advanced-encode-option lowpass_frequency=14000 --bitrate 50 --downmix"
ogg_speech_complex_mono="--advanced-encode-option lowpass_frequency=16000 --bitrate 52 --downmix"
ogg_speech_simple_stereo="--advanced-encode-option lowpass_frequency=13000 --bitrate 50"
ogg_speech_moderate_stereo="--advanced-encode-option lowpass_frequency=14000 --bitrate 56"
ogg_speech_complex_stereo="--advanced-encode-option lowpass_frequency=16000 --quality 1.4"
ogg_music_simple_mono="--quality 2.2 --downmix"
ogg_music_moderate_mono="--quality 2.3 --downmix"
ogg_music_complex_mono="--quality 2.5 --downmix"
ogg_music_simple_stereo="--quality 3.5"
ogg_music_moderate_stereo="--quality 4.3"
ogg_music_complex_stereo="--quality 5.0"
ogg_filler_simple_mono="--quality -1 --resample 512 --downmix"
ogg_filler_moderate_mono="--quality -1 --resample 512 --downmix"
ogg_filler_complex_mono="--quality -1 --resample 512 --downmix"
ogg_filler_simple_stereo="--quality -1 --resample 512 --downmix"
ogg_filler_moderate_stereo="--quality -1 --resample 512 --downmix"
ogg_filler_complex_stereo="--quality -1 --resample 512 --downmix"
#Opus: (opus-tools 0.1.10-1 libopus0 1.3.1-0)
# opusenc <preset> "$infile" "$outfile"
opus=""
opus_speech_simple_mono="--bitrate 24 --downmix-mono"
opus_speech_moderate_mono="--bitrate 28 --downmix-mono"
opus_speech_complex_mono="--bitrate 36 --downmix-mono"
opus_speech_simple_stereo="--bitrate 32"
opus_speech_moderate_stereo="--bitrate 36"
opus_speech_complex_stereo="--bitrate 40"
opus_music_simple_mono="--bitrate 50 --downmix-mono"
opus_music_moderate_mono="--bitrate 56 --downmix-mono"
opus_music_complex_mono="--bitrate 72 --downmix-mono"
opus_music_simple_stereo="--bitrate 72"
opus_music_moderate_stereo="--bitrate 84"
opus_music_complex_stereo="--bitrate 96"
opus_filler_simple_mono="--bitrate 1 --downmix-mono"
opus_filler_moderate_mono="--bitrate 1 --downmix-mono"
opus_filler_complex_mono="--bitrate 1 --downmix-mono"
opus_filler_simple_stereo="--bitrate 1 --downmix-mono"
opus_filler_moderate_stereo="--bitrate 1 --downmix-mono"
opus_filler_complex_stereo="--bitrate 1 --downmix-mono"
#Mp3: (lame 3.100-3 libmp3lame0 3.100-3)
# lame <preset> "$infile" "$outfile"
mp3="-q0 -b4 -T"
mp3_speech_simple_mono="-V7.0 --lowpass 13000 -mm -q0 -b4 -T"
mp3_speech_moderate_mono="-V6.5 --lowpass 14000 -mm -q0 -b4 -T"
mp3_speech_complex_mono="-V6.3 --lowpass 15000 -mm -q0 -b4 -T"
mp3_speech_simple_stereo="-V6.0 --lowpass 13000 -q0 -b4 -T"
mp3_speech_moderate_stereo="-V5.7 --lowpass 15000 -q0 -b4 -T"
mp3_speech_complex_stereo="-V5.5 --lowpass 16000 -q0 -b4 -T"
mp3_music_simple_mono="-V6.0 -mm -q0 -b4 -T"
mp3_music_moderate_mono="-V5.5 -mm -q0 -b4 -T"
mp3_music_complex_mono="-V5.0 -mm -q0 -b4 -T"
mp3_music_simple_stereo="-V4.5 -q0 -b4 -T"
mp3_music_moderate_stereo="-V3.5 -q0 -b4 -T"
mp3_music_complex_stereo="-V2.5 -q0 -b4 -T"
mp3_filler_simple_mono="-mm -q0 -b4 -T"
mp3_filler_moderate_mono="-mm -q0 -b4 -T"
mp3_filler_complex_mono="-mm -q0 -b4 -T"
mp3_filler_simple_stereo="-mm -q0 -b4 -T"
mp3_filler_moderate_stereo="-mm -q0 -b4 -T"
mp3_filler_complex_stereo="-mm -q0 -b4 -T"
# PRESET: example file
#
# comment with #
# empty lines allowed
# preset=tracknumbers
# set default for all files
opus_music_moderate_stereo=default
# set ranges
mp3_speech_simple_mono=19-61
# set individual files
ogg_music_moderate_mono=21
opus_music_complex_stereo=20 23 27
History:
2020-11-02 1.09 Completely rewritten
2020-10-27 1.08 Added presets file *not released*
2020-10-25 1.07 Implemented parallel encoding + bugfixes
2020-10-22 1.06 First public release version
Changelog:
1.09 Fixed: Completely rewritten (to use a queue)
Added: cdrdao support *buggy*
Added: cdrom.preset file with:
|- multi codec support
|- multi preset support
`- per track support
1.08 Added: presets file, customizable by user
Fixed: commandline options
Added: (suggestions by krcroft)
|- Bash strict-mode
|- Mono option for mono audio to save diskspace
|- Speech option (Jones in the Fast Lane only has voices).
|- variable-bit-rate encodings as a default
`- filler option (unused space at lowest bitrate and in mono)
1.07 Added: (suggestions by krcroft)
|- Bash strict-mode without 'u'
|- Removed unneeded usage text brackets.
|- Options in usage elaborated
|- Renamed 'track.cue' to 'cdrom.cue'
|- Parallel Encoding
|- Double-bracket tests
`- FLAC seekpoints to reduce CPU load
1.06 Fixed: Recalculation defaults to octal instead of decimal
1.05 Added: Index time recalculation
1.04 Added: Parsing CUE sheet instead of generating it
1.03 Added: Set basename to fixed "track" instead of dynamic
1.02 Added: CTRL-C trap and resume
1.01 Added: Codec support for OGG/FLAC/OPUS
1.00 Draft: Initial draft with MP3 and BCHUNK
Dependencies:
*bin/cue to iso/wav : bchunk
*flac encoder : flac
*mp3 encoder : lame
*opus encoder : opus-tools
*ogg encoder : vorbis-tools
*cdripper : cdrdao
** It is probably still a bit buggy, so beware **