patch #7498
[mldonkey.git] / src / utils / mp3tagui / mp3_info.ml
blob6d64df9a3052a73102885c1260ddf81b4b352274
1 (**************************************************************************)
2 (* Copyright 2003, 2002 b8_bavard, b8_zoggy, , b52_simon INRIA *)
3 (* *)
4 (* This file is part of mldonkey. *)
5 (* *)
6 (* mldonkey is free software; you can redistribute it and/or modify *)
7 (* it under the terms of the GNU General Public License as published *)
8 (* by the Free Software Foundation; either version 2 of the License, *)
9 (* or (at your option) any later version. *)
10 (* *)
11 (* mldonkey is distributed in the hope that it will be useful, *)
12 (* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
13 (* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)
14 (* GNU General Public License for more details. *)
15 (* *)
16 (* You should have received a copy of the GNU General Public License *)
17 (* along with mldonkey; if not, write to the Free Software *)
18 (* Foundation, Inc., 59 Temple Place, Suite 330, Boston, *)
19 (* MA 02111-1307 USA *)
20 (* *)
21 (**************************************************************************)
23 type channel_mode =
24 Stereo
25 | Joint_stereo
26 | Dual_channel_stereo
27 | Mono
29 type mp3_encoding = CBR | VBR
31 type t =
32 { duration: int; (** in seconds *)
33 samplerate: int; (** in kilobits per second *)
34 mode: channel_mode; (** stereo, mono, etc *)
35 bitrate: int; (** in kilobits per second *)
36 encoding: mp3_encoding; (** variable or constant bit rate *)
37 filesize: int (** in bytes *)
40 let check_head h =
41 let h0 = Char.code h.[0]
42 and h1 = Char.code h.[1]
43 and h2 = Char.code h.[2] in
44 h0 = 0xFF &&
45 h1 land 0xE0 = 0xE0 &&
46 h1 land 0x18 <> 0x08 &&
47 h1 land 0x06 <> 0x00 &&
48 h2 land 0xF0 <> 0xF0 &&
49 h2 land 0x0C <> 0x0C
51 let tabsel_123 =
52 [| [| [|0; 32; 64; 96; 128; 160; 192; 224; 256; 288; 320; 352; 384; 416; 448|];
53 [|0; 32; 48; 56; 64; 80; 96; 112; 128; 160; 192; 224; 256; 320; 384|];
54 [|0; 32; 40; 48; 56; 64; 80; 96; 112; 128; 160; 192; 224; 256; 320|]
55 |];
56 [| [|0; 32; 48; 56; 64; 80; 96; 112; 128; 144; 160; 176; 192; 224; 256|];
57 [|0; 8; 16; 24; 32; 40; 48; 56; 64; 80; 96; 112; 128; 144; 160|];
58 [|0; 8; 16; 24; 32; 40; 48; 56; 64; 80; 96; 112; 128; 144; 160|]
62 let mpg123_freqs =
63 [|44100; 48000; 32000; 22050; 24000; 16000; 11025; 12000; 8000|]
65 let mpg123_bs =
66 [|0; 384; 1152; 1152|]
68 let read_i4 ic =
69 let b1 = input_byte ic in let b2 = input_byte ic in
70 let b3 = input_byte ic in let b4 = input_byte ic in
71 (b1 lsl 24) lor (b2 lsl 16) lor (b3 lsl 8) lor b4
73 let get_xing_header ic header =
74 let id = (header lsr 19) land 1
75 and mode = (header lsr 6) land 3 in
76 let offset =
77 if id > 0
78 then if mode <> 3 then 32 else 17
79 else if mode <> 3 then 17 else 9 in
80 seek_in ic (pos_in ic + offset);
81 let buf = String.create 4 in
82 really_input ic buf 0 4;
83 if buf <> "Xing" then raise Not_found;
84 let flags = read_i4 ic in
85 (* 3 = FRAMES_FLAG | BYTES_FLAG *)
86 if flags land 3 <> 3 then raise Not_found;
87 let frames = read_i4 ic in
88 let bytes = read_i4 ic in
89 (frames, bytes)
91 let for_channel ic =
92 seek_in ic 0;
93 let buf = String.create 4 in
94 really_input ic buf 0 4;
95 while not (check_head buf) do
96 String.blit buf 1 buf 0 3;
97 buf.[3] <- input_char ic
98 done;
99 let header =
100 (Char.code buf.[1] lsl 16) lor
101 (Char.code buf.[2] lsl 8) lor
102 (Char.code buf.[3]) in
103 let (lsf, mpeg25) =
104 if header land 0x100000 <> 0
105 then ((if header land 0x80000 = 0 then 1 else 0), false)
106 else (1, true) in
107 let lay = 4 - ((header lsr 17) land 3) in
108 let sampling_frequency =
109 if mpeg25
110 then 6 + ((header lsr 10) land 3)
111 else ((header lsr 10) land 3) + 3 * lsf in
112 let sample_rate =
113 mpg123_freqs.(sampling_frequency) in
114 let bitrate_index = (header lsr 12) land 0xF in
115 (* let padding = (header lsr 9) land 1 in *)
116 let mode =
117 match (header lsr 6) land 3 with
118 0 -> Stereo | 1 -> Joint_stereo | 2 -> Dual_channel_stereo | _ -> Mono in
119 let tpf =
120 float mpg123_bs.(lay) /. float (max 1 (sample_rate lsl lsf)) in
121 let bpf =
122 float tabsel_123.(lsf).(lay - 1).(bitrate_index) *.
123 (if lay = 1 then 12000.0 *. 4.0 else 144000.0) /.
124 float (max 1 (sample_rate lsl lsf)) in
125 let filesize = in_channel_length ic in
126 let (enc, duration, bitrate) =
128 let (frames, bytes) = get_xing_header ic header in
129 (VBR,
130 tpf *. float frames,
131 truncate (float bytes *. 8.0 /. (
132 max 0.00001 (tpf *. float frames *. 1000.0))))
133 with Not_found ->
134 let len = filesize - pos_in ic in
135 (CBR,
136 float len /. (max 0.00001 bpf) *. tpf,
137 tabsel_123.(lsf).(lay - 1).(bitrate_index)) in
138 { duration = truncate duration;
139 samplerate = sample_rate / 1000;
140 mode = mode;
141 bitrate = bitrate;
142 encoding = enc;
143 filesize = filesize }
146 let framesize =
147 if bitrate_index = 0 then 0 else
148 match lay with
149 1 ->
150 let a = tabsel_123.(lsf).(0).(bitrate_index) * 12000 in
151 let b = a / mpg123_freqs.(sampling_frequency) in
152 ((b + padding) lsl 2) - 4
153 | 2 ->
154 let a = tabsel_123.(lsf).(1).(bitrate_index) * 144000 in
155 let b = a / mpg123_freqs.(sampling_frequency) in
156 b + padding - 4
157 | 3 ->
158 let a = tabsel_123.(lsf).(1).(bitrate_index) * 144000 in
159 let b = a / (mpg123_freqs.(sampling_frequency) lsl lsf) in
160 b + padding - 4
161 | _ ->
162 assert false in
165 let info filename =
166 let ic = open_in_bin filename in
168 let res = for_channel ic in close_in ic; res
169 with x ->
170 close_in ic; raise x