gremlin/gremlin.in: Try querying duration early to guess bitrate.
[autoys] / flaccrip / flaccrip-discid
CommitLineData
583b7e4a
MW
1#! /bin/bash
2
3set -e
4: ${JBDIR=/mnt/jb}
5
6###--------------------------------------------------------------------------
7### CD identification algorithms.
8###
9### 1. CDDB
10###
11### CCLLLLNN [NTRACK TRACK-START... LENGTH]
12###
13### CC is a checksum of the track start times; LLLL is the offset of the
14### leadout track, in seconds (rounded down), and NN is the total number of
15### tracks. All of these are in hexadecimal, and include the 150-frame (2
16### second) pre-gap. All of these are in hexadecimal. Since a CD can have
17### at most 99 tracks, and can contain no more than 90 minutes of audio (!),
18### the other two items fit without needing reduction.
19###
20### The checksum is the sum of the decimal digits of the track start times,
21### in seconds, reduced modulo 255.
22###
23### NTRACK is the number of tracks; LENGTH is the offset of the leadout in
24### seconds. These are the same as in the checksum, so repeating them is
25### pointless, but it's done anyway. The TRACK-STARTs are the track start
26### offsets, in frames.
27###
28###
29### 2. AccurateRip
30###
31### DA1-DA2-CDDBID
32###
33### CDDBID is the CDDB id as described above. DA1 is simply the sum of the
34### track starts, including the lead-out track; DA2 is the sum of the
35### products TRACKNO * OFFSET for the audio tracks only, but including the
36### final lead-out -- so a data track makes the last audio track look very
37### long. Another wrinkle: the OFFSET for the first track is forced to 1 if
38### it's zero (to avoid the entry being lost, I presume, though I'm not sure
39### why this is ever so useful).
40###
41###
42### 3. MusicBrainz
43###
44### The MusicBrainz identification is a base64-encoded SHA-1 hash of the
45### table of contents. The base64 encoding uses `.', `_' and `-' in place of
46### `+', `/' and `=', because the standard characters /all/ have special
47### meanings in URL query strings. (Duh. And I'm not quite sure why we
48### still need the trailing marker.)
49###
50### The message to be hashed is FIRST LAST LENGTH TRACK-START..., where FIRST
51### and LAST are the first and last track numbers, LENGTH is the offset of
52### the lead-out, in frames, and the TRACK-STARTs are the start offsets of
53### the tracks, in order, also in frames. The track numbers are two
54### uppercase hex digits; the frame offsets are eight. All of these are
55### simply concatenated together.
56###
57### MusicBrainz only concerns itself with the audio tracks. If there's a
58### data track, then we ignore it, and the lead-out is considered to be 11400
59### frames before the data track.
60
61###--------------------------------------------------------------------------
62### Command line.
63
64format=cddb
65while getopts "acCm" opt; do
66 case "$opt" in
67 a) format=accuraterip ;;
68 c) format=cddb ;;
69 C) format=cddb-tracks ;;
70 m) format=musicbrainz ;;
71 *) exit 1 ;;
72 esac
73done
74shift $((OPTIND - 1))
75
76case $# in
77 0)
78 ;;
79 1)
80 if [ -r "$1/.discid" ]; then
81 exec <"$1/.discid"
82 else
83 exec < <($JBDIR/bin/flaccrip-toc "$1")
84 fi
85 ;;
86 *)
87 echo >&2 "Usage: $0 [-acCm] [DIRECTORY]"
88 exit 1
89 ;;
90esac
91
92###--------------------------------------------------------------------------
93### Main work.
94
95## Initial setup.
96cddbck=0
97cddbtracks=""
98nt=0 nat=0
99da=0 db=0
100mbtracks=""
101
102## Wander through the table of contents picking up unconsidered trifles.
103while read type offset; do
104
105 ## Bump the track numbers here. Most things want 1-based numbering, so
106 ## this is right. Don't bump for the end marker. Those who care
107 ## (AccurateRip) will sort it out for themselves.
108 case "$type" in
109 T) nt=$((nt + 1)) nat=$((nat + 1));;
110 D) nt=$((nt + 1)) ;;
111 esac
112
113 ## Update the CDDB state. This is common to several formats.
114 case "$type" in
115 [TD])
116 o=$((offset + 150))
117 s=$((o/75))
118 cddbtracks="${cddbtracks:+$cddbtracks }$o"
119 while :; do
120 case "$s" in
121 ?*) cddbck=$((cddbck + ${s:0:1})); s=${s#?} ;;
122 *) break ;;
123 esac
124 done
125 ;;
126 E)
127 final=$offset
128 ;;
129 esac
130
131 ## Update other bits of information.
132 case "$type" in
133 T)
134 da=$((da + offset))
135 db=$((db + nat*(offset > 0 ? offset : 1)))
136 mbtracks="$mbtracks$(printf "%08X" $((offset + 150)))"
137 ;;
138 D)
139 mbfinal=$((offset - 11250))
140 ;;
141 E)
142 da=$((da + offset))
143 db=$((db + (nat + 1)*(offset > 0 ? offset : 1)))
144 case "${mbfinal+t}" in
145 t) ;;
146 *) mbfinal=$((offset + 150)) ;;
147 esac
148 ;;
149 esac
150done
151
152## Sort out the CDDB id.
153cddbid=$(printf "%02x%04x%02x" $((cddbck%255)) $((final/75)) $nt)
154
155###--------------------------------------------------------------------------
156### Produce the answer.
157
158case "$format" in
159 cddb)
160 echo "$cddbid"
161 ;;
162 cddb-tracks)
163 echo "$cddbid $nt $cddbtracks $((final/75 + 2))"
164 ;;
165 accuraterip)
166 printf "%03d-%08x-%08x-%s\n" $nat $da $db $cddbid
167 ;;
168 musicbrainz)
169 mb=$(printf "%02X%02X%08X%s" 1 $nat $mbfinal $mbtracks)
170 for ((i = nat; i < 99; i++)); do
171 mb="${mb}00000000"
172 done
173 printf "%s" $mb |
174 openssl dgst -sha1 -binary |
175 openssl base64 | tr '+/=' '._-'
176 ;;
177esac