. "$BST_LIB" # flac 1.4.3 (poky recipes-multimedia/flac) -- the Free Lossless Audio Codec's # reference encoder/decoder, plus metaflac, its metadata editor. # # WHAT THIS FILE ASSERTS, AND WHY IT CAN ASSERT IT AT ALL. A media package on a # headless browser guest is normally only testable to the "it ran and printed a # banner" standard -- there is no sound card, no ALSA, no /dev/dsp, and nothing # to listen to a decoded stream with. flac is the exception, because LOSSLESSNESS # IS AN EXACT OFFLINE PROPERTY: what comes out of the decoder must equal what # went into the encoder, byte for byte. So the gate here is the ticket's own # criterion -- encode a generated PCM file, decode it again, and `cmp` the two -- # and it is a hard equality, not a heuristic. Every case below is deterministic # and needs no device, no network and no peer. # # THE FORMAT CARRIES ITS OWN ORACLE, which is the second reason this port is # checkable to a standard the rest of the media tier is not. FLAC's STREAMINFO # block stores an MD5 of the UNENCODED audio, computed by the encoder over the # raw samples. `flac -t` recomputes it from the decoded output and compares, so # it is a self-check the suite does not have to construct; and because the # fixture here is signed 16-bit little-endian mono -- exactly the byte order # STREAMINFO's MD5 is defined over -- the stored digest must also equal `md5sum` # of the raw fixture. That cross-check ties the encoder's internal accounting to # a digest computed by a completely different program, which is a stronger # statement than either alone: an encoder that was CONSISTENTLY wrong would still # pass `flac -t`. # # WHAT A GREEN HERE SAYS ABOUT wasm32. The recipe passes ac_cv_prog_NASM="" and # leaves PACKAGECONFIG avx off, so no assembly and no x86 intrinsic is compiled # in: what runs on target is the portable fixed-predictor and LPC code. A # lossless round-trip through it is therefore a statement about this arm's # integer arithmetic and shifts, not about a SIMD kernel that was skipped. # # THE FIXTURE IS BUILT WITHOUT FORKING. Every byte of the 32 KiB sawtooth is # emitted by shell arithmetic and the printf builtin -- not a `printf '%03o'` # subshell per byte, which would be 256 forks before the first case. On an arm # where each exec is a fresh wasm module instantiation and guest memory is not # returned on process exit, that is the difference between a fixture and a cost. require flac flac/present tmp=${TMPDIR:-/tmp}/bst-flac.$$ mkdir -p "$tmp" 2>/dev/null pcm=$tmp/tone.pcm fl=$tmp/tone.flac back=$tmp/back.pcm wav=$tmp/tone.wav wfl=$tmp/tone-wav.flac wback=$tmp/back.wav # ------------------------------------------------------------------ fixture --- # A 256-sample sawtooth, 16-bit signed little-endian mono, repeated 64 times: # 16384 samples / 32768 bytes / 2.048 s at 8 kHz. Sawtooth rather than noise # because the codec must be seen to COMPRESS, and rather than silence because a # constant signal is carried by the verbatim path alone and would exercise no # predictor. The low byte is held at zero so the waveform is a clean ramp in the # high byte. \NNN escapes are assembled by arithmetic expansion, a shell builtin. i=0; esc="" while [ $i -lt 256 ]; do esc="$esc\\000\\$((i/64))$((i/8%8))$((i%8))" i=$((i+1)) done j=0 while [ $j -lt 64 ]; do printf "$esc"; j=$((j+1)); done > "$pcm" nbytes=$(wc -c < "$pcm" 2>/dev/null | tr -d ' ') check_eq flac/fixture-size 32768 "$nbytes" # A fixture the shell failed to write would make every case below fail for a # reason that has nothing to do with flac, so stop here rather than emit twenty # misleading reds against the wrong subject. if [ "$nbytes" != "32768" ]; then bst_notok flac/fixture "the shell could not emit the 32768-byte PCM fixture -- remaining cases not run" rm -rf "$tmp" exit 0 fi RAWFMT="--force-raw-format --endian=little --sign=signed" RAWIN="$RAWFMT --channels=1 --bps=16 --sample-rate=8000" check_out flac/version "flac 1.4.3" flac --version # ------------------------------------------------ the ticket's own criterion --- # -V makes the encoder decode its own output as it writes and compare against the # input, so an encoder fault is caught at encode time with a diagnostic rather # than surfacing as an opaque cmp difference three cases later. check flac/encode flac --totally-silent -V $RAWIN -o "$fl" "$pcm" check_eq flac/magic "fLaC" "$(dd if="$fl" bs=1 count=4 2>/dev/null)" # `flac -t` decodes the stream and compares against the MD5 the encoder stored in # STREAMINFO; it reports ": ok". The needle is ": ok" rather than the # filename because whether flac echoes the path or its basename is a detail of # its progress reporting, not a property this case is about. check_out flac/verify-md5 ": ok" flac -t "$fl" check flac/decode flac -d --totally-silent $RAWFMT -o "$back" "$fl" # THE GATE: lossless means byte for byte, and this is the case the ticket names. check flac/lossless cmp "$pcm" "$back" # It must also actually be a codec, not a container rewrite. encbytes=$(wc -c < "$fl" 2>/dev/null | tr -d ' ') if [ -n "$encbytes" ] && [ "$encbytes" -lt 32768 ]; then bst_ok flac/compresses else bst_notok flac/compresses "encoded $encbytes bytes from a 32768-byte fixture -- no compression" fi # ------------------------------------------------------------ the WAV path --- # The raw-format round-trip above is the exact criterion, but nobody feeds flac # raw PCM in practice -- they feed it a WAV. This wraps the same samples in a # canonical 44-byte RIFF/WAVE header and round-trips through it, covering the # container parser and writer. The decoded WAV must equal the input WAV byte for # byte, header included: flac reproduces the canonical header it consumed. ob() { printf "\\$(($1%256/64))$(($1%256/8%8))$(($1%8))"; } le16() { ob $1; ob $(($1/256)); } le32() { ob $1; ob $(($1/256)); ob $(($1/65536)); ob $(($1/16777216)); } { printf 'RIFF'; le32 32804; printf 'WAVEfmt '; le32 16; le16 1; le16 1; le32 8000; le32 16000; le16 2; le16 16; printf 'data'; le32 32768; cat "$pcm"; } > "$wav" check_eq flac/wav-fixture-size 32812 "$(wc -c < "$wav" 2>/dev/null | tr -d ' ')" check flac/wav-encode flac --totally-silent -V -o "$wfl" "$wav" check flac/wav-decode flac -d --totally-silent -o "$wback" "$wfl" check flac/wav-lossless cmp "$wav" "$wback" # ------------------------------------------------------ compression levels --- # -0 and -8 are two different predictors, not two settings of one: -0 is the # fixed-predictor path and -8 is the exhaustive LPC model search. Both must be # lossless, and both are arithmetic this arm has never run before. Which of the # two is SMALLER is deliberately not asserted -- on a synthetic sawtooth the # fixed predictor beats LPC, so pinning the inequality would assert a property of # the fixture rather than of the codec. for L in 0 8; do lfl=$tmp/l$L.flac; lpcm=$tmp/l$L.pcm if flac --totally-silent -$L $RAWIN -o "$lfl" "$pcm" 2>/dev/null && flac -d --totally-silent $RAWFMT -o "$lpcm" "$lfl" 2>/dev/null; then check "flac/level$L-lossless" cmp "$pcm" "$lpcm" else bst_notok "flac/level$L-lossless" "encode or decode failed at -$L" fi done # --------------------------------------------------------------- metaflac --- # metaflac ships in the same package as flac (FILES:${PN} = "${bindir}/*"), so an # absent one is a packaging fault on this image rather than a legitimately absent # tool -- which is why it is `not ok` here and not the `require` skip that guards # flac itself. if [ ! -x /usr/bin/metaflac ]; then bst_notok metaflac/present "metaflac is not in /usr/bin, though it is packaged with flac" else bst_ok metaflac/present # The digest flac stored, against one computed over the same bytes by a # completely different program. if command -v md5sum >/dev/null 2>&1; then check_eq flac/streaminfo-md5 "$(md5sum < "$pcm" 2>/dev/null | cut -d' ' -f1)" \ "$(metaflac --show-md5sum "$fl" 2>/dev/null)" else bst_skip flac/streaminfo-md5 "md5sum not installed on this image" fi # STREAMINFO's other fields. Four separate execs would cost four module # instantiations, so they are read in one and split by the shell itself; # metaflac emits one line per --show-* option, in the order given. set -- $(metaflac --show-total-samples --show-sample-rate --show-channels --show-bps "$fl" 2>/dev/null) check_eq flac/total-samples 16384 "${1:-}" check_eq flac/sample-rate 8000 "${2:-}" check_eq flac/channels 1 "${3:-}" check_eq flac/bps 16 "${4:-}" # A metadata WRITE rewrites the file in place -- a different path from anything # the encoder exercises, and the one that touches the filesystem hardest. check metaflac/set-tag metaflac --set-tag=TITLE=brintOS "$fl" check_out metaflac/show-tag "TITLE=brintOS" metaflac --show-tag=TITLE "$fl" # The rewritten file must still decode losslessly: a tag write that corrupted a # frame would otherwise pass both cases above. check flac/decode-after-tag flac -d --totally-silent -f $RAWFMT -o "$back" "$fl" check flac/lossless-after-tag cmp "$pcm" "$back" fi # ------------------------------------------------------------- static-only --- # No libFLAC.so on a distro that configures --disable-shared image-wide. One # appearing here would mean the static-only rule silently stopped holding, and # with it the reason EMCRAFT_TOOLS names `flac` alone rather than also naming the # libflac and libflac++ packages the recipe declares. if [ -e /usr/lib/libFLAC.so ] || [ -e /usr/lib/libFLAC.so.12 ] || [ -e /usr/lib/libFLAC++.so ]; then bst_notok flac/static-only "a shared libFLAC shipped on a --disable-shared distro" else bst_ok flac/static-only fi rm -rf "$tmp"