joeylib2/scripts/check-vgm.sh

107 lines
4.6 KiB
Bash
Executable file

#!/usr/bin/env bash
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
# See LICENSE.md for license information.
# SPDX-License-Identifier: MIT
#
# check-vgm.sh - Host gate for tools/vgm2song.py, the VGM-to-.song converter
# that brings tracker-made chip music (Furnace's, docs/Assets.adoc) to
# JoeyLib's chip tracker.
#
# 1. tests/host/vgmCheck.py synthetic: VGM files written to known register
# writes, converted and baked; every baked row must hold the voices worked
# out by hand.
# 2. With Furnace installed (./install.sh install furnace): every SN76489 and
# AY-3-8910 demo song Furnace comes with that uses that chip alone is
# exported by Furnace to VGM and to audio (WAV), converted and baked; each
# must bake, and Furnace's own audio must sound the pitches the baked song
# plays (tests/host/vgmCheck.py pitch): at least PITCH_MIN_SONG percent of
# each song's pitched windows, PITCH_MIN_MEAN on average (a wrong clock or
# divider gives under 10). The demos are their authors' and stay where the
# installer put them; nothing made from them is kept.
#
# Usage: scripts/check-vgm.sh
# Exit: 0 PASS, 1 FAIL
set -uo pipefail
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
source "$repo/scripts/lib/joeyEnv.sh"
label=check-vgm
PITCH_MIN_SONG=40
PITCH_MIN_MEAN=75
FURNACE_SECONDS=300
DEMO_FOLDERS="sn7:sn ay8910:ay"
work=$(mktemp -d -t joeylib-vgm.XXXXXX)
trap 'rm -rf "$work"' EXIT
mkdir -p "$work/synthetic" "$work/demos" "$work/home"
synthetic=$(python3 "$repo/tests/host/vgmCheck.py" synthetic "$repo" "$work/synthetic")
status=$?
echo "$synthetic" | grep -v '^SYNTHETIC'
counts=$(echo "$synthetic" | sed -n 's/^SYNTHETIC //p')
if [ "$status" -ne 0 ]; then
echo "$label: FAIL - synthetic checks $counts"
exit 1
fi
if [ -z "${FURNACE:-}" ] || [ ! -x "${FURNACE:-}" ]; then
echo "$label: PASS (synthetic $counts; Furnace not installed, so no demo songs: ./install.sh install furnace)"
exit 0
fi
fails=""
songs=0
total=0
for spec in $DEMO_FOLDERS; do
folder=${spec%%:*}
chip=${spec##*:}
for fur in "$FURNACE_DEMOS/$folder"/*.fur; do
[ -f "$fur" ] || continue
name=$(basename "$fur" .fur | tr -c 'A-Za-z0-9_\n-' '_')
base="$work/demos/$name"
# Furnace keeps its settings under HOME: the work folder's, not ours.
if ! HOME="$work/home" timeout "$FURNACE_SECONDS" "$FURNACE" -loglevel error -vgmout "$base.vgm" "$fur" < /dev/null > /dev/null 2>&1 \
|| ! HOME="$work/home" timeout "$FURNACE_SECONDS" "$FURNACE" -loglevel error -output "$base.wav" "$fur" < /dev/null > /dev/null 2>&1; then
fails="${fails:+$fails; }$folder/$name: Furnace did not export it"
continue
fi
if ! python3 "$repo/tools/vgm2song.py" --chip "$chip" "$base.vgm" "$base.song" 2> "$base.warn"; then
fails="${fails:+$fails; }$folder/$name: $(tr '\n' ' ' < "$base.warn")"
continue
fi
# A song for more chips than this one sounds parts the conversion
# leaves out: no pitch to compare.
if grep -q 'other chips' "$base.warn"; then
echo "$label: $folder/$name: skipped (the song uses another chip too)"
continue
fi
if ! python3 "$repo/tools/songbake/songbake.py" "$base.song" "$base.jym" > /dev/null 2> "$base.bake"; then
fails="${fails:+$fails; }$folder/$name: songbake: $(grep -v warning "$base.bake" | tr '\n' ' ')"
continue
fi
result=$(python3 "$repo/tests/host/vgmCheck.py" pitch "$base.wav" "$base.jym" | sed -n 's/^PITCH //p')
matched=${result%%/*}
tested=${result##*/}
if [ -z "$tested" ] || [ "$tested" -eq 0 ]; then
fails="${fails:+$fails; }$folder/$name: no pitched window to compare"
continue
fi
percent=$((100 * matched / tested))
echo "$label: $folder/$name: $(sed -n 2p "$base.song" | cut -c3-); pitch $matched/$tested ($percent%)"
songs=$((songs + 1))
total=$((total + percent))
if [ "$percent" -lt "$PITCH_MIN_SONG" ]; then
fails="${fails:+$fails; }$folder/$name: pitch $percent% < $PITCH_MIN_SONG%"
fi
done
done
if [ "$songs" -eq 0 ]; then
fails="${fails:+$fails; }no demo song converted"
elif [ $((total / songs)) -lt "$PITCH_MIN_MEAN" ]; then
fails="${fails:+$fails; }mean pitch $((total / songs))% < $PITCH_MIN_MEAN%"
fi
if [ -n "$fails" ]; then
echo "$label: FAIL - $fails"
exit 1
fi
echo "$label: PASS (synthetic $counts; $songs Furnace demo songs convert, bake, and sound their pitches: mean $((total / songs))%)"