#!/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))%)"