Hier eine Bash-Shell-Funktion lameCRC()
, die LAME musicCRC und CRC-16 des Xing / Info-LAME-Header-Frames (wie in den Spezifikationen für Mp3-Info-Tags Version 1 - Entwurf 0 angegeben ) mithilfe des Apple- afinfo
Befehls und des crc
Befehlszeilentools von Hampa berechnet Hug, http://www.hampa.ch/misc-utils/index.html .
Wenn der afinfo
Befehl von Apple nicht verfügbar ist, dd
wird verwendet (was jedoch zu einem Geschwindigkeitsüberfall führt).
(Hinweis: Ich habe bewusst auf die internen String-Funktionen von Bash verzichtet, um die Portabilität zu erleichtern).
lameCRC() { # Bash shell function # lameCRC() uses the crc command line tool from http://www.hampa.ch/misc-utils/index.html. # lameCRC() is partly inspired by the output of Apple's afinfo command and # the C source code of Audio-Scan-0.93 and MP3-Cut-Gapless-0.03 by Andy Grundman: # https://metacpan.org/author/AGRUNDMA # Audio-Scan-0.93/src/mp3.c (GNU General Public License Version 2, June 1991 or later) # Audio-Scan-0.93/include/mp3.h ( ditto ) # MP3-Cut-Gapless-0.03/src/mp3cut.c ( ditto ) # usage: lameCRC lame.mp3 # Basic information: # Mp3 Info Tag rev 1 specifications, http://gabriel.mp3-tech.org/mp3infotag.html # LAME info header zone A has a length of 120 bytes (or 240 chars in xxd hex output). # The "LAMEx." string is followed by 30 bytes (or 60 chars in xxd hex output) according to the # "Suggested Info Tag extension fields + layout" in the Mp3 Info Tag rev 1 specifications. local n n1 n2 lines plus crcs hexchar lame_start_idx xinginfo_start_idx PATH PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin [[ ! -x '/usr/local/bin/crc' ]] && { printf '%s\n' 'No crc command line tool in /usr/local/bin!' 'See: http://www.hampa.ch/misc-utils/index.html' 1>&2; } # get Xing|Info|LAME strings and their offsets in binary file lines="$(strings -a -n 4 -t d "$1" | grep -E --line-buffered 'Xing|Info|LAME.\.' | head -n 2)" [[ $(echo "$lines" | grep -E -c 'Xing|Info') -ne 1 ]] || [[ $(echo "$lines" | grep -E -c 'LAME[^ ]') -ne 1 ]] && { echo 'No Xing|Info string or correct LAME encoder version string (e.g. LAME3.98r) found!' 1>&2; echo "$lines" 1>&2; return 1; } # get offset index numbers of Xing|Info|LAME strings lame_start_idx="$(printf '%s' "$lines" | awk '/LAME/' )" xinginfo_start_idx="$(printf '%s' "$lines" | awk '/Xing|Info/' )" # get possible offset of LAME string in output of strings command # LAME version string should consist of 9 chars, but may have a prefix in output of strings command # example: 9LAME3.98r instead of LAME3.98r # example: 7LAME3.88 (beta) instead of LAME3.88 (beta) #plus="$(printf '%s' "$lines" | sed -n 's/^[^ ]*[ ][ ]*\([^ ]*\)LAME[^ ]\.*/\1/p' | tr -d '\n' | wc -c)" # use [^ ]\ ? plus="$(printf '%s' "$lines" | sed -n 's/^[^ ]*[ ][ ]*\([^ ]*\)LAME.*/\1/p' | tr -d '\n' | wc -c)" lame_start_idx=$(( $lame_start_idx + $plus )) [[ $(( $lame_start_idx - $xinginfo_start_idx )) -ne 120 ]] && { echo 'No 120 bytes between Xing / Info and LAME string. Exiting ...' 1>&2; return 1; } # get entire LAME info tag #dd if="$1" bs=1 skip="$lame_start_idx" count=36 2>/dev/null | LC_ALL=C od -A n -cv; return 0 # get bytes $BC-$BD (MusicCRC) and bytes $BE-$BF (CRC-16 of Info Tag) (as described in http://gabriel.mp3-tech.org/mp3infotag.html) crcs="$(dd if="$1" bs=1 skip="$(( $lame_start_idx + 32 ))" count=4 2>/dev/null | xxd -p | tr -d '\n')" [[ -z "$crcs" ]] && { echo 'No LAME musicCRC and CRC-16 of Info Tag found!' 1>&2; return 1; } lameMusicLengthPlusCRCs="$(dd if="$1" bs=1 skip=$(( $lame_start_idx + 28 )) count=8 2>/dev/null | xxd -p | tr -d '\n')" lameMusicLength="$(echo "$lameMusicLengthPlusCRCs" | cut -b 1-8 )" lameMusicCRC1="$(echo "$lameMusicLengthPlusCRCs" | cut -b 9-10 )" # cf. http://gabriel.mp3-tech.org/mp3infotag#musiccrc lameMusicCRC2="$(echo "$lameMusicLengthPlusCRCs" | cut -b 11-12 )" lameInfoTagCRC16="$(echo "$lameMusicLengthPlusCRCs" | cut -b 13-16 )" # LAME MusicLength consists of: # [LAME Tag frame][complete mp3 music data] lameMusicLengthByteSize=$(printf '%d' "0x$") [[ $lameMusicLengthByteSize -le 0 ]] && { echo 'lameMusicLengthByteSize <= 0. Exiting ...' 1>&2; return 1; } if [[ -x '/usr/bin/sw_vers' ]] && [[ "$(/usr/bin/sw_vers -productName)" == "Mac OS X" ]] && [[ -x '/usr/bin/afinfo' ]]; then # get audio_bytes, i. e. [complete mp3 music data] - [LAME Tag frame] #id3v2 --delete-all "$1" 1>/dev/null # for testing purposes; edits file in-place! # afinfo seems to be only available on Mac OS X # afinfo alternative: Perl module Audio-Scan-0.93 by Andy Grundman # perl -e 'use Audio::Scan; my $offset = Audio::Scan->find_frame($ARGV[1],0); print "$offset\n";' _ file.mp3 audioinfo="$(afinfo "$1")" audio_bytes="$(echo "$audioinfo" | awk -F" " '/audio bytes:/' )" audio_data_file_offset="$(echo "$audioinfo" | awk -F" " '/audio data file offset:/')" xingInfoLameTagFrameSize=$(( lameMusicLengthByteSize - audio_bytes )) [[ $audio_bytes -le 0 ]] && { echo 'audio_bytes <= 0. Exiting ...' 1>&2; return 1; } # 0..xingInfoLameTagFrameOffset (match first 0xff byte in mp3 file) n=0 hexchar="" until [[ "$hexchar" == 'ff' ]]; do hexchar="$(dd if="$1" bs=1 skip=$n count=1 2>/dev/null | xxd -p)" n=$(( n + 1)) done xingInfoLameTagFrameOffset=$(( n - 1 )) else # dd speed bump # get xingInfoLameTagFrameSize # for mp3 magic numbers (\xFF\xFB) see: # http://www.digitalpreservation.gov/formats/fdd/fdd000105.shtml # n1 # count bytes from: 0xff...<--...$xinginfo_start_idx hexchar="" n=$xinginfo_start_idx until [[ "$hexchar" == 'ff' ]]; do n=$(( n - 1)) hexchar="$(dd if="$1" bs=1 skip=$n count=1 2>/dev/null | xxd -p)" done xingInfoLameTagFrameOffset=$n n1=$(( xinginfo_start_idx - n )) # n2 # count bytes from: $xinginfo_start_idx+120+36...-->...0xff hexchar="" n=$((xinginfo_start_idx + 120 + 36)) until [[ "$hexchar" == 'ff' ]]; do hexchar="$(dd if="$1" bs=1 skip=$n count=1 2>/dev/null | xxd -p)" n=$(( n + 1)) done n2=$(( n - xinginfo_start_idx - 120 - 36 - 1 )) # - 1 because the trailing 0xff got counted by $n xingInfoLameTagFrameSize=$(( $n1 + $n2 + 120 + 36 )) audio_data_file_offset=$((xingInfoLameTagFrameOffset + xingInfoLameTagFrameSize)) # get audio_bytes, i. e. [complete mp3 music data] - [LAME Tag frame] audio_bytes=$( printf "%s\n" "scale = 0; $ - $" | bc ) fi old_lameInfoTagCRC16="$lameInfoTagCRC16" new_lameInfoTagCRC16="$(head -c $(( xingInfoLameTagFrameOffset + xingInfoLameTagFrameSize )) "$1" | tail -c $ | head -c 190 | crc -R -r -g crc16)" old_lameMusicCRC16="$$" new_lameMusicCRC16="$(head -c $(( $ + $ )) "$1" | tail -c $ | crc -R -r -g crc16)" echo printf '%s\n' "old_lameInfoTagCRC16: $" "new_lameInfoTagCRC16: $" echo printf '%s\n' "old_lameMusicCRC16: $" "new_lameMusicCRC16: $" echo return 0 }