. "$BST_LIB" # lame 3.100 (poky recipes-multimedia/lame) -- the LAME MP3 encoder. # # THE GATE IS A BYTE-FOR-BYTE COMPARISON AGAINST A REFERENCE ENCODE, which is # the ticket's own criterion and a much stronger statement than "it produced an # MP3". data/lame-ref-128cbr.mp3 was produced by lame 3.100 -- the identical # release, from the identical tarball this recipe builds -- encoding the # identical fixture at the identical settings, on an x86-64 host. If the wasm32 # build agrees with it byte for byte, then this arm's floating-point # psychoacoustic model, its MDCT, its quantisation loop and its bitstream # formatter all reproduce the reference implementation exactly. Nothing about # that is implied by the encoder merely running. # # WHY A LOSSY CODEC CAN BE TESTED THIS HARD AT ALL. flac (57-flac.test) is # checkable because losslessness is an exact property. lame is not lossless, so # the analogous round-trip assertion does not exist -- decode(encode(x)) != x by # construction, and how far it differs is a matter of psychoacoustics rather than # arithmetic. What replaces it is DETERMINISM: for a fixed encoder version, fixed # settings and fixed input, the output is one specific byte string, so a # reference encode is a complete oracle. That is why the comparison is against a # stored artifact rather than against a property. # # WHAT A RED HERE WOULD AND WOULD NOT MEAN. A mismatch is NOT automatically a # brintOS defect: lame's psymodel runs on doubles and calls libm (log, exp, pow, # sin), and libm is not correctly rounded, so a last-ulp difference between this # arm's libm and the host's could in principle propagate through the # quantisation decisions into different bits. That is why the mismatch case # reports the size and digest it actually got instead of only failing: a # same-size, different-digest result is the libm story, and a wildly different # size is a real encoder fault. The fixture itself is digest-checked first # (lame/fixture-digest), so a byte difference can never be blamed on the encoder # when the shell in fact wrote a different WAV. # # WHAT RUNS ON TARGET IS THE PORTABLE C. configure answered `working SSE # intrinsics... no` and `if I have to build the internal vector lib... no`, and # the recipe passes no --enable-nasm, so no assembly and no x86 intrinsic is # compiled in. A green below is a statement about this arm's own arithmetic. # # THE FIXTURE IS BUILT WITHOUT FORKING -- every byte of the 32 KiB sawtooth comes # from shell arithmetic and the printf builtin, for the reason 57-flac.test # states: on this arm each exec is a fresh wasm module instantiation. require lame lame/present tmp=${TMPDIR:-/tmp}/bst-lame.$$ mkdir -p "$tmp" 2>/dev/null pcm=$tmp/tone.pcm wav=$tmp/tone.wav mp3=$tmp/tone.mp3 ref=$BST_DATA/lame-ref-128cbr.mp3 # ------------------------------------------------------------------ fixture --- # The same 256-sample sawtooth 57-flac.test uses -- 16-bit signed little-endian # mono, repeated 64 times: 16384 samples, 32768 bytes. Wrapped here in a # canonical 44-byte RIFF/WAVE header at 44100 Hz, because 44100 is an MPEG-1 # Layer III rate and the ticket asks for a WAV at a fixed bitrate; at 8 kHz lame # would drop to MPEG-2.5, where 128 kbps does not exist. 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" 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 44100; le32 88200; le16 2; le16 16; printf 'data'; le32 32768; cat "$pcm"; } > "$wav" wavbytes=$(wc -c < "$wav" 2>/dev/null | tr -d ' ') check_eq lame/fixture-size 32812 "$wavbytes" # A fixture the shell failed to write would make every case below fail for a # reason that has nothing to do with lame, so stop here rather than emit a dozen # misleading reds against the wrong subject. if [ "$wavbytes" != "32812" ]; then bst_notok lame/fixture "the shell could not emit the 32812-byte WAV fixture -- remaining cases not run" rm -rf "$tmp" exit 0 fi # The reference was made from THIS byte string. Pinning its digest here is what # lets a later cmp failure be read as an encoder difference rather than as # fixture drift -- without it the two causes are indistinguishable, and the # wrong one gets filed. if command -v md5sum >/dev/null 2>&1; then check_eq lame/fixture-digest da93fc854f85a82305553d1080c0b66a \ "$(md5sum < "$wav" 2>/dev/null | cut -d' ' -f1)" else bst_skip lame/fixture-digest "md5sum not installed on this image" fi check_out lame/version "3.100" lame --version # ------------------------------------------------ the ticket's own criterion --- check lame/encode lame --quiet -b 128 --cbr "$wav" "$mp3" if [ ! -f "$ref" ]; then bst_notok lame/reference-present "$ref is not installed -- the reference encode did not ship" else bst_ok lame/reference-present gotbytes=$(wc -c < "$mp3" 2>/dev/null | tr -d ' ') check_eq lame/encode-size 7104 "$gotbytes" # THE GATE. if cmp -s "$ref" "$mp3" 2>/dev/null; then bst_ok lame/matches-reference else # Report what was actually produced. A same-size/different-digest result and # a wildly-different-size result are different findings, and the detail line # is the only place a reader of the report can tell them apart. if command -v md5sum >/dev/null 2>&1; then gotmd5=$(md5sum < "$mp3" 2>/dev/null | cut -d' ' -f1) else gotmd5="(md5sum absent)" fi bst_notok lame/matches-reference \ "encoded $gotbytes bytes md5=$gotmd5; reference is 7104 bytes md5=c15eb97bcb066cab224876c497f1e676" fi fi # ------------------------------------------------------- bitstream structure --- # Independent of the reference: these say the output is a real MPEG-1 Layer III # stream and not merely 7104 bytes that happen to compare equal. ff fb is the # 11-bit frame sync plus MPEG-1, Layer III, no CRC. check_eq lame/frame-sync "377 373" "$(dd if="$mp3" bs=1 count=2 2>/dev/null | od -An -to1 | tr -s ' ' | sed 's/^ //;s/ $//')" # CBR output carries an "Info" header frame (the VBR frame's CBR spelling), and # the encoder stamps its own version into the stream. Both are lame writing its # identity into the bitstream, which is what makes a reference encode # reproducible in the first place. check_out lame/info-tag "Info" dd if="$mp3" bs=1 skip=21 count=4 check lame/lame-version-in-stream grep -q "LAME3.100" "$mp3" # ------------------------------------------------------------- the CBR knob --- # 64 / 128 / 320 kbps over the same input must give strictly increasing sizes. # That is a property of constant-bitrate coding -- bytes per second is the knob # -- and not of this fixture, which is why it is safe to assert where an # equivalent claim about compression LEVELS would not be (see 57-flac.test). lo=$tmp/lo.mp3; hi=$tmp/hi.mp3 if lame --quiet -b 64 --cbr "$wav" "$lo" 2>/dev/null && lame --quiet -b 320 --cbr "$wav" "$hi" 2>/dev/null; then lob=$(wc -c < "$lo" | tr -d ' '); hib=$(wc -c < "$hi" | tr -d ' ') if [ "$lob" -lt "$gotbytes" ] && [ "$gotbytes" -lt "$hib" ]; then bst_ok lame/cbr-scales else bst_notok lame/cbr-scales "64k=$lob 128k=$gotbytes 320k=$hib -- not strictly increasing" fi # It must also actually be a codec: 128 kbps over 0.37 s of 705.6 kbps PCM. if [ "$gotbytes" -lt 32812 ]; then bst_ok lame/compresses else bst_notok lame/compresses "encoded $gotbytes bytes from a 32812-byte fixture -- no compression"; fi else bst_notok lame/cbr-scales "encode failed at -b 64 or -b 320" bst_notok lame/compresses "encode failed at -b 64 or -b 320" fi # ----------------------------------------------------------------- decoding --- # lame carries mpglib and decodes with --decode, so the port includes a decoder # and it is worth holding to the same standard. The digest is of the WAV the # host reference decoder produced from the very file in data/, so this is again # an exact comparison against the identical release rather than a shape check. dec=$tmp/back.wav if lame --quiet --decode "$ref" "$dec" 2>/dev/null; then bst_ok lame/decode check_eq lame/decode-size 32812 "$(wc -c < "$dec" | tr -d ' ')" if command -v md5sum >/dev/null 2>&1; then check_eq lame/decode-matches-reference faa1ade6ec897c5590a25fea25304e17 \ "$(md5sum < "$dec" 2>/dev/null | cut -d' ' -f1)" else bst_skip lame/decode-matches-reference "md5sum not installed on this image" fi else bst_notok lame/decode "lame --decode failed on the shipped reference stream" fi # --------------------------------------------------------------------- id3 --- # Tagging is a different code path from the encoder: one writer prepends before # the first frame, the other appends after the last. --tt/--ta drives BOTH by # default -- an ID3v2 header at offset 0 and the fixed 128-byte ID3v1 trailer -- # which is why the size below is not simply the encode plus 128, and why the two # writers are asserted separately rather than through one number. tagged=$tmp/tagged.mp3 if lame --quiet -b 128 --cbr --tt brintOS --ta Emcraft "$wav" "$tagged" 2>/dev/null; then bst_ok lame/id3-write check_eq lame/id3v2-header "ID3" "$(dd if="$tagged" bs=1 count=3 2>/dev/null)" # ID3v1 is a fixed-layout 128-byte trailer: "TAG" then a 30-byte title field. # The MAGIC is asserted unconditionally -- it is written by lame regardless of # what the title ends up containing. head33=$(tail -c 128 "$tagged" 2>/dev/null | dd bs=1 count=33 2>/dev/null) check_eq lame/id3v1-magic "TAG" "$(printf '%s' "$head33" | dd bs=1 count=3 2>/dev/null)" # THE TITLE TEXT IS PROBED, NOT ASSERTED, and this is the one place in the # suite where that is the honest shape. lame does not copy a tag string # through: parse.c's toLatin1() calloc's a zeroed buffer and fills it via # iconv_open("ISO_8859-1//TRANSLIT", nl_langinfo(CODESET)) -- and when that # open is REFUSED it returns the zeroed buffer, so the tag is written with an # empty title rather than failing. This image refuses every iconv_open: it # links statically, glibc serves charsets from dlopen'd gconv modules, and no # /usr/lib/gconv is staged (BRINTOS-184 established the mechanism through # mtools/CP850; BRINTOS-343 established that even the BUILTIN ISO-8859-1 # transformation is refused). That is our own image content, not a brintOS # platform defect, so this is a skip carrying its witness -- and because the # witness is probed at run time, the case becomes a real green the day the # image ships gconv data, instead of sitting as a stale exemption. A title # that came back as neither "brintOS" nor empty is a third thing entirely and # stays a hard red. case "$head33" in TAGbrintOS*) bst_ok lame/id3v1-title ;; TAG) # The skip is gated on its own witness, so it cannot outlive its cause: an # empty title on an image that DOES carry gconv data is a genuine red, not # this exemption. if [ -d /usr/lib/gconv ]; then bst_notok lame/id3v1-title \ "title empty although /usr/lib/gconv exists -- the iconv exemption no longer applies" else bst_skip lame/id3v1-title \ "title empty: iconv_open refused, no /usr/lib/gconv on this static image (BRINTOS-184/343)" fi ;; *) bst_notok lame/id3v1-title "ID3v1 title field is neither 'brintOS' nor empty: '$head33'" ;; esac else bst_notok lame/id3-write "lame --tt/--ta failed" bst_notok lame/id3v2-header "no tagged file to read" bst_notok lame/id3v1-magic "no tagged file to read" bst_notok lame/id3v1-title "no tagged file to read" fi # With ID3v2 declined the arithmetic is exact and the trailer is the ONLY thing # added, so the 128 bytes can be asserted as a number rather than inferred. v1=$tmp/v1.mp3 if lame --quiet -b 128 --cbr --id3v1-only --tt brintOS "$wav" "$v1" 2>/dev/null; then check_eq lame/id3v1-only-size $((gotbytes + 128)) "$(wc -c < "$v1" | tr -d ' ')" else bst_notok lame/id3v1-only-size "lame --id3v1-only failed" fi # ------------------------------------------------------------- static-only --- # No libmp3lame.so on a distro that configures --disable-shared image-wide. The # recipe declares PACKAGES += "libmp3lame" with FILES = "${libdir}/libmp3lame.so.*", # which matches nothing here -- which is why EMCRAFT_TOOLS names the bare `lame` # and not that package. One appearing would mean the static-only rule silently # stopped holding. if [ -e /usr/lib/libmp3lame.so ] || [ -e /usr/lib/libmp3lame.so.0 ]; then bst_notok lame/static-only "a shared libmp3lame shipped on a --disable-shared distro" else bst_ok lame/static-only fi # The GTK frame analyser. poky's no-gtk1.patch forces HAVE_GTK=no, so mp3x must # not exist; one appearing would mean the patch stopped applying and the image # grew a GTK dependency nobody asked for. if [ -e /usr/bin/mp3x ]; then bst_notok lame/no-mp3x "mp3x shipped -- the GTK frame analyser was built despite no-gtk1.patch" else bst_ok lame/no-mp3x fi rm -rf "$tmp"