Skip to content

Commit a26cbdb

Browse files
committed
add shout test
1 parent 3cb9c77 commit a26cbdb

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

erlang/shout/mp3_manager.erl

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
-module(mp3_manager).
2+
3+
-export([start/0]).
4+
5+
-import(lists, [map/2, reverse/1]).
6+
7+
start() ->
8+
Files = lib_files_find:files(".", "*.mp3", true),
9+
V = map(fun handle/1, Files),
10+
lib_misc:dump("mp3data", V).
11+
12+
handle(File) ->
13+
(catch read_id3_tag(File)).
14+
15+
read_id3_tag(File) ->
16+
case file:open(File, [read, binary, raw]) of
17+
{ok, S} ->
18+
Size = filelib:file_size(File),
19+
Result = (catch analyse(S, Size)),
20+
file:close(S),
21+
{File, Result};
22+
Error ->
23+
{File, Error}
24+
end.
25+
26+
analyse(S, Size) ->
27+
{ok, B1} = file:pread(S, 0, 10),
28+
case parse_start_tag(B1) of
29+
{"ID3v2.3.0", {_Unsync, Extended, _Experimental, Len}} ->
30+
{ok, Data} = file:pread(S, 10, Len),
31+
case Extended of
32+
1 ->
33+
{Extended, Data1} = split_binary(Data, 10),
34+
parse_frames(Data1);
35+
0 ->
36+
parse_frames(Data)
37+
end;
38+
no ->
39+
{ok, B2} = file:pread(S, Size - 128, 128),
40+
parse_v1_tag(B2)
41+
end.
42+
43+
parse_start_tag(<<$I, $D, $3, 3, 0, Unsync:1, Extended:1, Experimental:1, _:5, K:32>>) ->
44+
Tag = "ID3v2.3.0",
45+
Size = syncsafe2int(K),
46+
{Tag, {Unsync, Extended, Experimental, Size}};
47+
parse_start_tag(_) ->
48+
no.
49+
50+
parse_frames(B) ->
51+
F = gather_frames(B),
52+
G = map(fun parse_frame/1, F),
53+
H = [{I, J} || {I, J} <- G],
54+
{ok, H}.
55+
parse_frame({"T1T2", _, Txt}) -> {title, parse_txt(Txt)};
56+
parse_frame({"TPE1", _, Txt}) -> {performer, parse_txt(Txt)};
57+
parse_frame({"TALB", _, Txt}) -> {album, parse_txt(Txt)};
58+
parse_frame({"TRCK", _, Txt}) -> {track, parse_txt(Txt)};
59+
parse_frame(_) -> skipped.
60+
61+
parse_txt(<<0:8, Txt/binary>>) -> Txt;
62+
parse_txt(<<1:8, 16#ff, 16#fe, Txt/binary>>) -> unicode_to_ascii(Txt).
63+
64+
unicode_to_ascii(Bin) -> list_to_binary(uni_to_ascii(binary_to_list(Bin))).
65+
66+
uni_to_ascii([X, _ | Y]) -> [X | uni_to_ascii(Y)];
67+
uni_to_ascii([]) -> [].
68+
69+
gather_frames(B) when size(B) < 10 -> [];
70+
gather_frames(<<0, 0, 0, 0, _/binary>>) -> [];
71+
gather_frames(<<$P, $R, $I, $V, _/binary>>) -> [];
72+
gather_frames(<<Id1, Id2, Id3, Id4, SafeN:32, Flags:16, Rest/binary>>) ->
73+
<<_A:1, _B:1, _C:1, _:5, I:1, J:1, _K:1, _:5>> = <<Flags:16>>,
74+
case {I, J} of
75+
{0, 0} ->
76+
Tag = {Id1, Id2, Id3, Id4},
77+
case is_tag(Tag) of
78+
true ->
79+
Size = syncsafe2int(SafeN),
80+
{Data, Next} = split_binary(Rest, Size),
81+
[{Tag, Flags, Data} | gather_frames(Next)];
82+
false ->
83+
[]
84+
end;
85+
_ ->
86+
[]
87+
end;
88+
gather_frames(CC) -> [{error, CC}].
89+
90+
is_tag([H | T]) when $A =< H, H =< $Z -> is_tag(T);
91+
is_tag([H | T]) when $0 =< H, H =< $9 -> is_tag(T);
92+
is_tag([]) -> true;
93+
is_tag(_) -> false.
94+
95+
syncsafe2int(N) ->
96+
<<_:1, N1:7, _:1, N2:7, _:1, N3:7, _:1, N4:7>> = <<N:32>>,
97+
<<I:32>> = <<0:4, N1:7, N2:7, N3:7, N4:7>>,
98+
I.
99+
100+
parse_v1_tag(<<$T, $A, $G, B/binary>>) ->
101+
{Title, B1} = split_binary(B, 30),
102+
{Artist, B2} = split_binary(B1, 30),
103+
{Album, B3} = split_binary(B2, 30),
104+
{_Year, B4} = split_binary(B3, 30),
105+
{_Comment, <<K, Track, _Gendre>>} = split_binary(B4, 28),
106+
L = [{title, trim(Title)}, {artist, trim(Artist)}, {album, trim(Album)}],
107+
case K of
108+
0 ->
109+
{"ID3v1.1", [{track, Track} | L]};
110+
_ ->
111+
{"ID3v1", L}
112+
end;
113+
parse_v1_tag(_) ->
114+
no.
115+
116+
trim(Bin) ->
117+
list_to_binary(trim_blanks(binary_to_list(Bin))).
118+
119+
trim_blanks(X) -> reverse(skip_blanks_and_zero(reverse(X))).
120+
121+
skip_blanks_and_zero([$\s | T]) -> skip_blanks_and_zero(T);
122+
skip_blanks_and_zero([0 | T]) -> skip_blanks_and_zero(T);
123+
skip_blanks_and_zero(X) -> X.

erlang/shout/song.mp3

5.09 MB
Binary file not shown.

0 commit comments

Comments
 (0)