From ed7f050a0f6d008b327098ad930c4873d3b3e461 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Wed, 13 Oct 2010 11:52:54 +0200 Subject: changed repository layout --- calc.erl | 164 ------------------- calc/calc.erl | 164 +++++++++++++++++++ calc/remote.erl | 22 +++ id3v2.erl | 468 ------------------------------------------------------- media.erl | 46 ------ remote.erl | 22 --- server/id3v2.erl | 468 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ server/media.erl | 46 ++++++ 8 files changed, 700 insertions(+), 700 deletions(-) delete mode 100644 calc.erl create mode 100644 calc/calc.erl create mode 100644 calc/remote.erl delete mode 100644 id3v2.erl delete mode 100644 media.erl delete mode 100644 remote.erl create mode 100644 server/id3v2.erl create mode 100644 server/media.erl diff --git a/calc.erl b/calc.erl deleted file mode 100644 index 4869104..0000000 --- a/calc.erl +++ /dev/null @@ -1,164 +0,0 @@ --module(calc). --export([eval/1]). - -%% num := [-] 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 -num(Input) -> - num(Input, []). - -num([Current|Input], Num) when ($0 =< Current) and ($9 >= Current) -> - num(Input, Num ++ [Current]); -num([Head|_], "-") -> - {error, {unexpected, list_to_atom([Head])}}; -num(Input, []) -> - {ok, {Input, []}}; -num(Input, Num) -> - {ok, {Input, list_to_integer(Num)}}. - -%% bracket := [-] ( '(' sum '}' | num ) -bracket([$-|Input]) -> - case bracket(Input) of - {ok, {Tail, []}} -> - {error, {expected_number, {before, Tail}}}; - {ok, {Tail, Result}} -> - {ok, {Tail, -1 * Result}}; - {error, Why} -> - {error, Why} - end; -bracket([$(|Input]) -> - case sum(Input) of - {ok, {Tail, Result}} -> - bracketCheck(Tail, Result); - {error, Why} -> - {error, Why} - end; -bracket(Input) -> - num(Input). - -bracketCheck([$)|Tail], Result) -> - {ok, {Tail, Result}}; -bracketCheck(_, _) -> - {error, missing_bracket}. - -%% factor := num [ ^ factor ] -factor(Input) -> - case bracket(Input) of - {ok, {Tail, Number2}} -> - factorExtended(Tail, Number2); - {error, Why} -> - {error, Why} - end. - -factor([Head|Input], Number1) -> - case bracket([Head|Input]) of - {ok, {Tail, Number2}} -> - Result = math:pow(Number1, Number2), - factorExtended(Tail, Result); - {error, Why} -> - {error, Why} - end; -factor([], _) -> - {error, unexpected_end_of_input}. - -factorExtended([$ |Tail], Result) -> - factorExtended(Tail, Result); -factorExtended([$^|Tail], Result) -> - factor(Tail, Result); -factorExtended(Tail, Result) -> - {ok, {Tail, Result}}. - -%% term := factor [ ( * | / ) term ] -term(Input) -> - case factor(Input) of - {ok, {Tail, Result}} -> - termExtended(Tail, Result); - {error, Why} -> - {error, Why} - end. - -term([Head|Input], Number1, Operator) -> - case factor([Head|Input]) of - {ok, {Tail, Number2}} -> - case calc(Number1, Operator, Number2) of - {ok, Result} -> - termExtended(Tail, Result); - {error, Why} -> - {error, Why} - end; - - {error, Why} -> - {error, Why} - end; -term([], _, _) -> - {error, unexpected_end_of_input}. - -termExtended([$ |Tail], Result) -> - termExtended(Tail, Result); -termExtended([$*|Tail], Result) -> - term(Tail, Result, $*); -termExtended([$/|Tail], Result) -> - term(Tail, Result, $/); -termExtended(Tail, Result) -> - {ok, {Tail, Result}}. - -%% sum := term [ ( + | - ) sum ] -sum([$+|Input]) -> - sum(Input); -sum(Input) -> - case term(Input) of - {ok, {Tail, Result}} -> - sumExtended(Tail, Result); - {error, Why} -> - {error, Why} - end. - -sum([Head|Input], Number1, Operator) -> - case term([Head|Input]) of - {ok, {Tail, Number2}} -> - case calc(Number1, Operator, Number2) of - {ok, Result} -> - sumExtended(Tail, Result); - {error, Why} -> - {error, Why} - end; - - {error, Why} -> - {error, Why} - end; -sum([], _, _) -> - {error, unexpected_end_of_input}. - -sumExtended([$ |Tail], Result) -> - sumExtended(Tail, Result); -sumExtended([$+|Tail], Result) -> - sum(Tail, Result, $+); -sumExtended([$-|Tail], Result) -> - sum(Tail, Result, $-); -sumExtended(Tail, Result) -> - {ok, {Tail, Result}}. - -%% helper function to do the calculations -calc(Number1, $+, Number2) -> - {ok, Number1 + Number2}; -calc(Number1, $-, Number2) -> - {ok, Number1 - Number2}; -calc(Number1, $*, Number2) -> - {ok, Number1 * Number2}; -calc(Number1, $/, Number2) when (Number2 /= 0) -> - {ok, Number1 / Number2}; -calc(_, $/, _) -> - {error, division_zero}; -calc(_,_,_) -> - {error, invalid_operator}. - -eval(Formular) -> - case sum([X || X <- Formular, X /= $ ]) of - {ok, {Tail, Result}} -> - evalCheck(Tail, Result); - {error, Why} -> - {error, Why} - end. - -evalCheck([], Result) -> - {ok, Result}; -evalCheck(Tail, _) -> - {error, {unexpected_garbage_at_end_of_input, Tail}}. diff --git a/calc/calc.erl b/calc/calc.erl new file mode 100644 index 0000000..4869104 --- /dev/null +++ b/calc/calc.erl @@ -0,0 +1,164 @@ +-module(calc). +-export([eval/1]). + +%% num := [-] 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 +num(Input) -> + num(Input, []). + +num([Current|Input], Num) when ($0 =< Current) and ($9 >= Current) -> + num(Input, Num ++ [Current]); +num([Head|_], "-") -> + {error, {unexpected, list_to_atom([Head])}}; +num(Input, []) -> + {ok, {Input, []}}; +num(Input, Num) -> + {ok, {Input, list_to_integer(Num)}}. + +%% bracket := [-] ( '(' sum '}' | num ) +bracket([$-|Input]) -> + case bracket(Input) of + {ok, {Tail, []}} -> + {error, {expected_number, {before, Tail}}}; + {ok, {Tail, Result}} -> + {ok, {Tail, -1 * Result}}; + {error, Why} -> + {error, Why} + end; +bracket([$(|Input]) -> + case sum(Input) of + {ok, {Tail, Result}} -> + bracketCheck(Tail, Result); + {error, Why} -> + {error, Why} + end; +bracket(Input) -> + num(Input). + +bracketCheck([$)|Tail], Result) -> + {ok, {Tail, Result}}; +bracketCheck(_, _) -> + {error, missing_bracket}. + +%% factor := num [ ^ factor ] +factor(Input) -> + case bracket(Input) of + {ok, {Tail, Number2}} -> + factorExtended(Tail, Number2); + {error, Why} -> + {error, Why} + end. + +factor([Head|Input], Number1) -> + case bracket([Head|Input]) of + {ok, {Tail, Number2}} -> + Result = math:pow(Number1, Number2), + factorExtended(Tail, Result); + {error, Why} -> + {error, Why} + end; +factor([], _) -> + {error, unexpected_end_of_input}. + +factorExtended([$ |Tail], Result) -> + factorExtended(Tail, Result); +factorExtended([$^|Tail], Result) -> + factor(Tail, Result); +factorExtended(Tail, Result) -> + {ok, {Tail, Result}}. + +%% term := factor [ ( * | / ) term ] +term(Input) -> + case factor(Input) of + {ok, {Tail, Result}} -> + termExtended(Tail, Result); + {error, Why} -> + {error, Why} + end. + +term([Head|Input], Number1, Operator) -> + case factor([Head|Input]) of + {ok, {Tail, Number2}} -> + case calc(Number1, Operator, Number2) of + {ok, Result} -> + termExtended(Tail, Result); + {error, Why} -> + {error, Why} + end; + + {error, Why} -> + {error, Why} + end; +term([], _, _) -> + {error, unexpected_end_of_input}. + +termExtended([$ |Tail], Result) -> + termExtended(Tail, Result); +termExtended([$*|Tail], Result) -> + term(Tail, Result, $*); +termExtended([$/|Tail], Result) -> + term(Tail, Result, $/); +termExtended(Tail, Result) -> + {ok, {Tail, Result}}. + +%% sum := term [ ( + | - ) sum ] +sum([$+|Input]) -> + sum(Input); +sum(Input) -> + case term(Input) of + {ok, {Tail, Result}} -> + sumExtended(Tail, Result); + {error, Why} -> + {error, Why} + end. + +sum([Head|Input], Number1, Operator) -> + case term([Head|Input]) of + {ok, {Tail, Number2}} -> + case calc(Number1, Operator, Number2) of + {ok, Result} -> + sumExtended(Tail, Result); + {error, Why} -> + {error, Why} + end; + + {error, Why} -> + {error, Why} + end; +sum([], _, _) -> + {error, unexpected_end_of_input}. + +sumExtended([$ |Tail], Result) -> + sumExtended(Tail, Result); +sumExtended([$+|Tail], Result) -> + sum(Tail, Result, $+); +sumExtended([$-|Tail], Result) -> + sum(Tail, Result, $-); +sumExtended(Tail, Result) -> + {ok, {Tail, Result}}. + +%% helper function to do the calculations +calc(Number1, $+, Number2) -> + {ok, Number1 + Number2}; +calc(Number1, $-, Number2) -> + {ok, Number1 - Number2}; +calc(Number1, $*, Number2) -> + {ok, Number1 * Number2}; +calc(Number1, $/, Number2) when (Number2 /= 0) -> + {ok, Number1 / Number2}; +calc(_, $/, _) -> + {error, division_zero}; +calc(_,_,_) -> + {error, invalid_operator}. + +eval(Formular) -> + case sum([X || X <- Formular, X /= $ ]) of + {ok, {Tail, Result}} -> + evalCheck(Tail, Result); + {error, Why} -> + {error, Why} + end. + +evalCheck([], Result) -> + {ok, Result}; +evalCheck(Tail, _) -> + {error, {unexpected_garbage_at_end_of_input, Tail}}. diff --git a/calc/remote.erl b/calc/remote.erl new file mode 100644 index 0000000..793c27c --- /dev/null +++ b/calc/remote.erl @@ -0,0 +1,22 @@ +-module(remote). +-export([start/0, calculate/1]). + +rpc(C) -> + rechner ! {self(), C}, + receive + {rechner, Reply} -> + Reply + end. + +loop() -> + receive + {From, {calculate, String}} -> + From ! {rechner, calc:eval(String)}, + loop() + end. + +start() -> + register(rechner, spawn(fun() -> loop() end)). + +calculate(String) -> + rpc({calculate, String}). diff --git a/id3v2.erl b/id3v2.erl deleted file mode 100644 index 196288a..0000000 --- a/id3v2.erl +++ /dev/null @@ -1,468 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : id3v2.erl -%%% Author : Brendon Hogger -%%% Description : ID3v2 reader -%%% -%%% Created : 13 Aug 2009 by Brendon Hogger -%%%------------------------------------------------------------------- --module(id3v2). - --export([read_file/1, read/1, test/0]). - --define(DBG(Term), io:format("~p: ~p~n", [self(), Term])). --define(GV(E, P), proplists:get_value(E, P)). - - -read_file(Filename) -> - case file:open(Filename, [read, raw, binary]) of - {ok, File} -> - RV = read(File), - file:close(File), - RV; - _ -> - not_found - end. - -read(File) -> - {ok, Start} = file:read(File, 10), - case read_v2_header(Start) of - {ok, Props} -> - read_v2(File, Props); - not_found -> - file:position(File, {eof, -128}), - {ok, Tail} = file:read(File, 128), - case read_v1_header(Tail) of - {ok, Props} -> - read_v1(File, Props); - not_found -> - not_found - end - end. - - -read_v2_header(<<"ID3", VMaj:8/integer, VMin:8/integer, - A:1/integer, B:1/integer, C:1/integer, D:1/integer, 0:4, - S1:8/integer, S2:8/integer, S3:8/integer, S4:8/integer>>) - when S1 < 128 andalso S2 < 128 andalso S3 < 128 andalso S4 < 128 -> - [Unsync, Extended, Experimental, Footer] = [bool(X) || X <- [A,B,C,D]], - Size = parse_synchsafe(<>), - {ok, [{version, {2, VMaj, VMin}}, - {unsync, Unsync}, {extended, Extended}, - {experimental, Experimental}, {footer, Footer}, - {size, Size}]}; -read_v2_header(_) -> - not_found. - - -read_v2(File, Props) -> - Version = ?GV(version, Props), - read_v2(Version, File, Props). - -read_v2({2, 2, _}, File, Props) -> - {ok, Header} = file:read(File, ?GV(size, Props)), - Frames = read_next_frame_22(Header, []), - {ok, Frames}; -read_v2({2, 3, _}, File, Props) -> - {ok, Header} = file:read(File, ?GV(size, Props)), - Header2 = skip_v2_extended_header(Header, Props), - Frames = read_next_frame_23(Header2, []), - {ok, Frames}; -read_v2({2, 4, _}, File, Props) -> - {ok, Header} = file:read(File, ?GV(size, Props)), - Header2 = skip_v2_extended_header(Header, Props), - Frames = read_next_frame_24(Header2, []), - {ok, Frames}. - - -% Untested! None of my mp3 files have extended headers. -skip_v2_extended_header(Header, Props) -> - case ?GV(extended, Props) of - true -> - <> = Header, - ExtSize = parse_synchsafe(SynchSafeSize), - {_, Header2} = split_binary(Header, ExtSize), - Header2; - _ -> - Header - end. - - -read_next_frame_24(<<0, _Rest/binary>>, Frames) -> - % Assume the header length was a lie, and we're done - finalize_v2_frames(Frames); -read_next_frame_24(<>, - Frames) - when S1 < 128 andalso S2 < 128 andalso S3 < 128 andalso S4 < 128 -> - Size = parse_synchsafe(<>), - case Size of - 0 -> finalize_v2_frames(Frames); - _ -> - {Content, Tail} = split_binary(Rest, Size), - <<0:1, A:1/integer, B:1/integer, C:1/integer, 0:5, - H:1/integer, 0:2, K:1/integer, M:1/integer, N:1/integer, P:1/integer>> = FlagBytes, - Flags = list_to_tuple([bool(X) || X <- [A, B, C, H, K, M, N, P]]), - Frame = parse_v2_frame(FrameID, Size, Content, Flags, {2,4,0}), - read_next_frame_24(Tail, [Frame|Frames]) - end; -read_next_frame_24(_, Frames) -> - finalize_v2_frames(Frames). - - -read_next_frame_23(<<0, _Rest/binary>>, Frames) -> - % Assume the header length was a lie, and we're done - finalize_v2_frames(Frames); -read_next_frame_23(<>, - Frames) when Size > 0 andalso Size =< byte_size(Rest) -> - {Content, Tail} = split_binary(Rest, Size), - <> = FlagBytes, - Flags = list_to_tuple([bool(X) || X <- [A, B, C, I, J, K]]), - Frame = parse_v2_frame(FrameID, Size, Content, Flags, {2,3,0}), - read_next_frame_23(Tail, [Frame|Frames]); -read_next_frame_23(_Other, Frames) -> - finalize_v2_frames(Frames). - - - -read_next_frame_22(<>, - Frames) -> - {Content, Tail} = split_binary(Rest, Size), - Frame = parse_v2_frame(FrameID, Size, Content, none, {2,3,0}), - read_next_frame_22(Tail, [Frame|Frames]); -read_next_frame_22(_, Frames) -> - finalize_v2_frames(Frames). - - - -parse_v2_frame(<<"TIT2">>, _Size, RawContent, _Flags, _Version) -> - {tit2, extract_v2_string(RawContent)}; -parse_v2_frame(<<"TT2">>, _Size, RawContent, _Flags, _Version) -> - {tit2, extract_v2_string(RawContent)}; -parse_v2_frame(<<"TALB">>, _Size, RawContent, _Flags, _Version) -> - {talb, extract_v2_string(RawContent)}; -parse_v2_frame(<<"TAL">>, _Size, RawContent, _Flags, _Version) -> - {talb, extract_v2_string(RawContent)}; -parse_v2_frame(<<"TPE1">>, _Size, RawContent, _Flags, _Version) -> - {tpe1, extract_v2_string(RawContent)}; -parse_v2_frame(<<"TE1">>, _Size, RawContent, _Flags, _Version) -> - {tpe1, extract_v2_string(RawContent)}; -parse_v2_frame(<<"TRCK">>, _Size, RawContent, _Flags, _Version) -> - {trck, extract_v2_string(RawContent)}; -parse_v2_frame(<<"TRK">>, _Size, RawContent, _Flags, _Version) -> - {trck, extract_v2_string(RawContent)}; -parse_v2_frame(<<"TCON">>, _Size, RawContent, _Flags, _Version) -> - {tcon, extract_v2_string(RawContent)}; -parse_v2_frame(<<"TCO">>, _Size, RawContent, _Flags, _Version) -> - {tcon, extract_v2_string(RawContent)}; -parse_v2_frame(<<"MCDI">>, _Size, RawContent, _Flags, _Version) -> - {mcdi, RawContent}; -parse_v2_frame(<<"MCI">>, _Size, RawContent, _Flags, _Version) -> - {mcdi, RawContent}; -parse_v2_frame(<<"TLEN">>, _Size, RawContent, _Flags, _Version) -> - {tlen, parse_length(RawContent)}; -parse_v2_frame(<<"TLE">>, _Size, RawContent, _Flags, _Version) -> - {tlen, parse_length(RawContent)}; -parse_v2_frame(_Other, _, _, _, _Version) -> - ignored. - -parse_length(RawContent) -> - Content = binary_to_list(extract_v2_string(RawContent)), - case catch (list_to_integer(Content)) of - Milliseconds when is_integer(Milliseconds) -> - Secs = Milliseconds div 1000, - {Secs div 60, Secs rem 60}; - _ -> undefined - end. - -finalize_v2_frames(Frames) -> - lists:reverse([F || F <- Frames, F /= ignored]). - - - - -read_v1_header(<<"TAG", Rest/binary>>) -> - {ok, [{version, {1, 0, 0}}, - {data, Rest}]}; -read_v1_header(_) -> - not_found. - - -read_v1(File, _Props) -> - file:position(File, {eof, -355}), - case file:read(File, 227) of - <<"TAG+", TitleExt:60/binary, ArtistExt:60/binary, AlbumExt:60/binary, - _Speed:8/integer, Genre:30/binary, _StartTime:6/binary, _EndTime:6/binary>> - -> ok; - _Other -> - {TitleExt, ArtistExt, AlbumExt, _Speed, Genre, _StartTime, _EndTime} = - {<<"">>, <<"">>, <<"">>, 0, <<"">>, <<"">>, <<"">>} - end, - - {ok, Header} = file:read(File, 128), - <<"TAG", Title:30/binary, Artist:30/binary, Album:30/binary, Year:4/binary, - CommentAndTrack:30/binary, GenreNum:8/integer>> = Header, - - case CommentAndTrack of - <> when Track > 0 -> ok; % v1 - _ -> Comment = CommentAndTrack, Track=0 % v1.1 - end, - - CB = fun(B1, B2) -> - BinStr = erlang:concat_binary([strip_nulls(B1, 0), strip_nulls(B2, 0)]), - unicode:characters_to_binary(string:strip(binary_to_list(BinStr)), latin1) - end, - - GenreStr = case Genre of - <<"">> -> v1genre(GenreNum); - _ -> Genre - end, - - Frames = [{tit2, CB(Title, TitleExt)}, {tpe1, CB(Artist, ArtistExt)}, - {talb, CB(Album, AlbumExt)}, - {tdrc, Year}, {comm, Comment}, {trck, Track}, - {tcon, GenreStr}], - %{speed, Speed}, {startTime, StartTime}, {endTime, EndTime}], - {ok, Frames}. - - -parse_synchsafe(FourBytes) -> - <<0:1, S1:7/integer, 0:1, S2:7/integer, 0:1, S3:7/integer, 0:1, S4:7/integer>> = FourBytes, - <> = <<0:4, S1:7/integer, S2:7/integer, S3:7/integer, S4:7/integer>>, - Size. - -bool(1) -> true; -bool(_) -> false. - -strip_nulls(Bin, N) -> - case split_binary(Bin, N) of - {Stuff, <<0, _Rest/binary>>} -> Stuff; - {All, <<>>} -> All; - _Other -> strip_nulls(Bin, N+1) - end. - - -extract_v2_string(Binary) -> - Bin = strip_nulls(decode_v2_string(Binary), 0), - Str = string:strip(binary_to_list(Bin)), - list_to_binary(Str). - -decode_v2_string(<<0:8, Rest/binary>>) -> - unicode:characters_to_binary(Rest, latin1); -decode_v2_string(<<1:8, BOM:2/binary, Rest/binary>>) -> - {Encoding, _} = unicode:bom_to_encoding(BOM), - unicode:characters_to_binary(Rest, Encoding); -decode_v2_string(<<2:8, Rest/binary>>) -> - unicode:characters_to_binary(Rest, {utf16, big}); -decode_v2_string(<<3:8, Rest/binary>>) -> - unicode:characters_to_binary(Rest, utf8); -decode_v2_string(Other) -> - unicode:characters_to_binary(Other, latin1). - - - -v1genre(0) -> "Blues"; -v1genre(1) -> "Classic Rock"; -v1genre(2) -> "Country"; -v1genre(3) -> "Dance"; -v1genre(4) -> "Disco"; -v1genre(5) -> "Funk"; -v1genre(6) -> "Grunge"; -v1genre(7) -> "Hip-Hop"; -v1genre(8) -> "Jazz"; -v1genre(9) -> "Metal"; -v1genre(10) -> "New Age"; -v1genre(11) -> "Oldies"; -v1genre(12) -> "Other"; -v1genre(13) -> "Pop"; -v1genre(14) -> "R&B"; -v1genre(15) -> "Rap"; -v1genre(16) -> "Reggae"; -v1genre(17) -> "Rock"; -v1genre(18) -> "Techno"; -v1genre(19) -> "Industrial"; -v1genre(20) -> "Alternative"; -v1genre(21) -> "Ska"; -v1genre(22) -> "Death Metal"; -v1genre(23) -> "Pranks"; -v1genre(24) -> "Soundtrack"; -v1genre(25) -> "Euro-Techno"; -v1genre(26) -> "Ambient"; -v1genre(27) -> "Trip-Hop"; -v1genre(28) -> "Vocal"; -v1genre(29) -> "Jazz+Funk"; -v1genre(30) -> "Fusion"; -v1genre(31) -> "Trance"; -v1genre(32) -> "Classical"; -v1genre(33) -> "Instrumental"; -v1genre(34) -> "Acid"; -v1genre(35) -> "House"; -v1genre(36) -> "Game"; -v1genre(37) -> "Sound Clip"; -v1genre(38) -> "Gospel"; -v1genre(39) -> "Noise"; -v1genre(40) -> "Alternative Rock"; -v1genre(41) -> "Bass"; -v1genre(42) -> "Soul"; -v1genre(43) -> "Punk"; -v1genre(44) -> "Space"; -v1genre(45) -> "Meditative"; -v1genre(46) -> "Instrumental Pop"; -v1genre(47) -> "Instrumental Rock"; -v1genre(48) -> "Ethnic"; -v1genre(49) -> "Gothic"; -v1genre(50) -> "Darkwave"; -v1genre(51) -> "Techno-Industrial"; -v1genre(52) -> "Electronic"; -v1genre(53) -> "Pop-Folk"; -v1genre(54) -> "Eurodance"; -v1genre(55) -> "Dream"; -v1genre(56) -> "Southern Rock"; -v1genre(57) -> "Comedy"; -v1genre(58) -> "Cult"; -v1genre(59) -> "Gangsta"; -v1genre(60) -> "Top 40"; -v1genre(61) -> "Christian Rap"; -v1genre(62) -> "Pop/Funk"; -v1genre(63) -> "Jungle"; -v1genre(64) -> "Native US"; -v1genre(65) -> "Cabaret"; -v1genre(66) -> "New Wave"; -v1genre(67) -> "Psychadelic"; -v1genre(68) -> "Rave"; -v1genre(69) -> "Showtunes"; -v1genre(70) -> "Trailer"; -v1genre(71) -> "Lo-Fi"; -v1genre(72) -> "Tribal"; -v1genre(73) -> "Acid Punk"; -v1genre(74) -> "Acid Jazz"; -v1genre(75) -> "Polka"; -v1genre(76) -> "Retro"; -v1genre(77) -> "Musical"; -v1genre(78) -> "Rock & Roll"; -v1genre(79) -> "Hard Rock"; -v1genre(80) -> "Folk"; -v1genre(81) -> "Folk-Rock"; -v1genre(82) -> "National Folk"; -v1genre(83) -> "Swing"; -v1genre(84) -> "Fast Fusion"; -v1genre(85) -> "Bebob"; -v1genre(86) -> "Latin"; -v1genre(87) -> "Revival"; -v1genre(88) -> "Celtic"; -v1genre(89) -> "Bluegrass"; -v1genre(90) -> "Avantgarde"; -v1genre(91) -> "Gothic Rock"; -v1genre(92) -> "Progressive Rock"; -v1genre(93) -> "Psychedelic Rock"; -v1genre(94) -> "Symphonic Rock"; -v1genre(95) -> "Slow Rock"; -v1genre(96) -> "Big Band"; -v1genre(97) -> "Chorus"; -v1genre(98) -> "Easy Listening"; -v1genre(99) -> "Acoustic"; -v1genre(100) -> "Humour"; -v1genre(101) -> "Speech"; -v1genre(102) -> "Chanson"; -v1genre(103) -> "Opera"; -v1genre(104) -> "Chamber Music"; -v1genre(105) -> "Sonata"; -v1genre(106) -> "Symphony"; -v1genre(107) -> "Booty Bass"; -v1genre(108) -> "Primus"; -v1genre(109) -> "Porn Groove"; -v1genre(110) -> "Satire"; -v1genre(111) -> "Slow Jam"; -v1genre(112) -> "Club"; -v1genre(113) -> "Tango"; -v1genre(114) -> "Samba"; -v1genre(115) -> "Folklore"; -v1genre(116) -> "Ballad"; -v1genre(117) -> "Power Ballad"; -v1genre(118) -> "Rhythmic Soul"; -v1genre(119) -> "Freestyle"; -v1genre(120) -> "Duet"; -v1genre(121) -> "Punk Rock"; -v1genre(122) -> "Drum Solo"; -v1genre(123) -> "Acapella"; -v1genre(124) -> "Euro-House"; -v1genre(125) -> "Dance Hall"; -v1genre(126) -> "Goa"; -v1genre(127) -> "Drum & Bass"; -v1genre(128) -> "Club - House"; -v1genre(129) -> "Hardcore"; -v1genre(130) -> "Terror"; -v1genre(131) -> "Indie"; -v1genre(132) -> "BritPop"; -v1genre(133) -> "Negerpunk"; -v1genre(134) -> "Polsk Punk"; -v1genre(135) -> "Beat"; -v1genre(136) -> "Christian Gangsta Rap"; -v1genre(137) -> "Heavy Metal"; -v1genre(138) -> "Black Metal"; -v1genre(139) -> "Crossover"; -v1genre(140) -> "Contemporary Christian"; -v1genre(141) -> "Christian Rock"; -v1genre(142) -> "Merengue"; -v1genre(143) -> "Salsa"; -v1genre(144) -> "Thrash Metal"; -v1genre(145) -> "Anime"; -v1genre(146) -> "JPop"; -v1genre(147) -> "Synthpop"; -v1genre(_) -> "Unknown". - - -%% ------------------------------------------------------------ -%% Tests -%% ------------------------------------------------------------ - --define(TESTPATTERN, "/media/everything/music/The Roots/Things Fall Apart/*.mp3"). - -test() -> - Start = now(), - read_files(filelib:wildcard(?TESTPATTERN), 0, 0), - ?DBG({time, timer:now_diff(now(), Start) / 1000000}). - -read_files([FN|Rest], Total, Fail) -> - case read_file(FN) of - {ok, Props} -> - ?DBG({?GV(trck, Props), ?GV(tit2, Props)}), - read_files(Rest, Total+1, Fail); - not_found -> - read_files(Rest, Total+1, Fail+1) - end; -read_files([], Total, Fail) -> - ?DBG({total, Total}), - ?DBG({fail, Fail}). - - - - -%% $ make test -%% erl +W w -pa ebin -noshell -s id3v2 test -s init stop -%% <0.1.0>: {<<"11">>,<<"100% Dundee">>} -%% <0.1.0>: {<<"14">>,<<"3rd Acts: Ques Vs. Scratch 2...Electric Boogaloo">>} -%% <0.1.0>: {<<"10">>,<<"Act Too (Love Of My Life)">>} -%% <0.1.0>: {<<"1">>,<<"Act Won (Things Fall Apart)">>} -%% <0.1.0>: {<<"13">>,<<"Adrenaline!">>} -%% <0.1.0>: {<<"8">>,<<"Ain't Sayin Nothin' New">>} -%% <0.1.0>: {<<"18">>,<<"Bonus">>} -%% <0.1.0>: {<<"12">>,<<"Diedre vs. Dice">>} -%% <0.1.0>: {<<"16">>,<<"Don't See Us">>} -%% <0.1.0>: {<<"9">>,<<"Double Trouble">>} -%% <0.1.0>: {<<"6">>,<<"Dynamite!">>} -%% <0.1.0>: {<<"4">>,<<"Step Into The Realm">>} -%% <0.1.0>: {<<"2">>,<<"Table Of Contents (Pts. 1 & 2)">>} -%% <0.1.0>: {<<"3">>,<<"The Next Movement">>} -%% <0.1.0>: {<<"17">>,<<"The Return To Innocence Lost">>} -%% <0.1.0>: {<<"5">>,<<"The Spark">>} -%% <0.1.0>: {<<"7">>,<<"Without A Doubt">>} -%% <0.1.0>: {<<"15">>,<<"You Got Me (wt Erykah Badu)">>} -%% <0.1.0>: {total,18} -%% <0.1.0>: {fail,0} -%% <0.1.0>: {time,0.01496} diff --git a/media.erl b/media.erl deleted file mode 100644 index a761f64..0000000 --- a/media.erl +++ /dev/null @@ -1,46 +0,0 @@ --module(media). --export([init/0,insert/3, ask/2]). - -% Since we are not willing to calculate and deliver all the id3 tags everytime they are requested, -% we try to get something persistent with mnesia. -% Concerning the parsing of id3tags we use the library id3v2 by Brendon Hogger. For detailed information take a -% look at the header of the library. - -% What is an entry in our database made of? By the way the filepath includes the filename. - --record(track, {artist, title, votes, locked, filepath }). - -% With which application do we play mp3s? - -player(path) -> - {ok, ["/usr/bin/env", "mplayer", "-quiet", path]}. - -% Before this module becomes usable, we must initialize it with the following steps: -% 1. Initialize the mnesiadatabase and create the table within it. -% 2. Parse the mp3s in the working directory and add them to the database -% 3. Get into a loop so the database can be actually queried and files can be played. - -init() -> - mnesia:create_schema([node()]), - mnesia:start(), - mnesia:create_table(track, [{attributes, record_info(fields, track)}]), - io:format("Initialisation of mnesia successful.\n"). - -% Basic insertion of entrys into the database. Some entries are left out because they are 0 or false. - -insert(Artist, Title, Filepath) -> - F = fun() -> - mnesia:write(#track{artist = Artist, title = Title, votes = 0, locked = false, filepath = Filepath}) - end, - mnesia:transaction(F). - -% We want to query in order to simplify the next calls. -ask(Artist, Title) -> - F = fun() -> - mnesia:match_object({track, Artist, Title, '_', '_', '_'}) - end, - {atomic, Results} = mnesia:transaction(F), - Results. - -% We want to play mp3s from our database. -play(Artist, Title) -> io:format("blub"). diff --git a/remote.erl b/remote.erl deleted file mode 100644 index 793c27c..0000000 --- a/remote.erl +++ /dev/null @@ -1,22 +0,0 @@ --module(remote). --export([start/0, calculate/1]). - -rpc(C) -> - rechner ! {self(), C}, - receive - {rechner, Reply} -> - Reply - end. - -loop() -> - receive - {From, {calculate, String}} -> - From ! {rechner, calc:eval(String)}, - loop() - end. - -start() -> - register(rechner, spawn(fun() -> loop() end)). - -calculate(String) -> - rpc({calculate, String}). diff --git a/server/id3v2.erl b/server/id3v2.erl new file mode 100644 index 0000000..196288a --- /dev/null +++ b/server/id3v2.erl @@ -0,0 +1,468 @@ +%%%------------------------------------------------------------------- +%%% File : id3v2.erl +%%% Author : Brendon Hogger +%%% Description : ID3v2 reader +%%% +%%% Created : 13 Aug 2009 by Brendon Hogger +%%%------------------------------------------------------------------- +-module(id3v2). + +-export([read_file/1, read/1, test/0]). + +-define(DBG(Term), io:format("~p: ~p~n", [self(), Term])). +-define(GV(E, P), proplists:get_value(E, P)). + + +read_file(Filename) -> + case file:open(Filename, [read, raw, binary]) of + {ok, File} -> + RV = read(File), + file:close(File), + RV; + _ -> + not_found + end. + +read(File) -> + {ok, Start} = file:read(File, 10), + case read_v2_header(Start) of + {ok, Props} -> + read_v2(File, Props); + not_found -> + file:position(File, {eof, -128}), + {ok, Tail} = file:read(File, 128), + case read_v1_header(Tail) of + {ok, Props} -> + read_v1(File, Props); + not_found -> + not_found + end + end. + + +read_v2_header(<<"ID3", VMaj:8/integer, VMin:8/integer, + A:1/integer, B:1/integer, C:1/integer, D:1/integer, 0:4, + S1:8/integer, S2:8/integer, S3:8/integer, S4:8/integer>>) + when S1 < 128 andalso S2 < 128 andalso S3 < 128 andalso S4 < 128 -> + [Unsync, Extended, Experimental, Footer] = [bool(X) || X <- [A,B,C,D]], + Size = parse_synchsafe(<>), + {ok, [{version, {2, VMaj, VMin}}, + {unsync, Unsync}, {extended, Extended}, + {experimental, Experimental}, {footer, Footer}, + {size, Size}]}; +read_v2_header(_) -> + not_found. + + +read_v2(File, Props) -> + Version = ?GV(version, Props), + read_v2(Version, File, Props). + +read_v2({2, 2, _}, File, Props) -> + {ok, Header} = file:read(File, ?GV(size, Props)), + Frames = read_next_frame_22(Header, []), + {ok, Frames}; +read_v2({2, 3, _}, File, Props) -> + {ok, Header} = file:read(File, ?GV(size, Props)), + Header2 = skip_v2_extended_header(Header, Props), + Frames = read_next_frame_23(Header2, []), + {ok, Frames}; +read_v2({2, 4, _}, File, Props) -> + {ok, Header} = file:read(File, ?GV(size, Props)), + Header2 = skip_v2_extended_header(Header, Props), + Frames = read_next_frame_24(Header2, []), + {ok, Frames}. + + +% Untested! None of my mp3 files have extended headers. +skip_v2_extended_header(Header, Props) -> + case ?GV(extended, Props) of + true -> + <> = Header, + ExtSize = parse_synchsafe(SynchSafeSize), + {_, Header2} = split_binary(Header, ExtSize), + Header2; + _ -> + Header + end. + + +read_next_frame_24(<<0, _Rest/binary>>, Frames) -> + % Assume the header length was a lie, and we're done + finalize_v2_frames(Frames); +read_next_frame_24(<>, + Frames) + when S1 < 128 andalso S2 < 128 andalso S3 < 128 andalso S4 < 128 -> + Size = parse_synchsafe(<>), + case Size of + 0 -> finalize_v2_frames(Frames); + _ -> + {Content, Tail} = split_binary(Rest, Size), + <<0:1, A:1/integer, B:1/integer, C:1/integer, 0:5, + H:1/integer, 0:2, K:1/integer, M:1/integer, N:1/integer, P:1/integer>> = FlagBytes, + Flags = list_to_tuple([bool(X) || X <- [A, B, C, H, K, M, N, P]]), + Frame = parse_v2_frame(FrameID, Size, Content, Flags, {2,4,0}), + read_next_frame_24(Tail, [Frame|Frames]) + end; +read_next_frame_24(_, Frames) -> + finalize_v2_frames(Frames). + + +read_next_frame_23(<<0, _Rest/binary>>, Frames) -> + % Assume the header length was a lie, and we're done + finalize_v2_frames(Frames); +read_next_frame_23(<>, + Frames) when Size > 0 andalso Size =< byte_size(Rest) -> + {Content, Tail} = split_binary(Rest, Size), + <> = FlagBytes, + Flags = list_to_tuple([bool(X) || X <- [A, B, C, I, J, K]]), + Frame = parse_v2_frame(FrameID, Size, Content, Flags, {2,3,0}), + read_next_frame_23(Tail, [Frame|Frames]); +read_next_frame_23(_Other, Frames) -> + finalize_v2_frames(Frames). + + + +read_next_frame_22(<>, + Frames) -> + {Content, Tail} = split_binary(Rest, Size), + Frame = parse_v2_frame(FrameID, Size, Content, none, {2,3,0}), + read_next_frame_22(Tail, [Frame|Frames]); +read_next_frame_22(_, Frames) -> + finalize_v2_frames(Frames). + + + +parse_v2_frame(<<"TIT2">>, _Size, RawContent, _Flags, _Version) -> + {tit2, extract_v2_string(RawContent)}; +parse_v2_frame(<<"TT2">>, _Size, RawContent, _Flags, _Version) -> + {tit2, extract_v2_string(RawContent)}; +parse_v2_frame(<<"TALB">>, _Size, RawContent, _Flags, _Version) -> + {talb, extract_v2_string(RawContent)}; +parse_v2_frame(<<"TAL">>, _Size, RawContent, _Flags, _Version) -> + {talb, extract_v2_string(RawContent)}; +parse_v2_frame(<<"TPE1">>, _Size, RawContent, _Flags, _Version) -> + {tpe1, extract_v2_string(RawContent)}; +parse_v2_frame(<<"TE1">>, _Size, RawContent, _Flags, _Version) -> + {tpe1, extract_v2_string(RawContent)}; +parse_v2_frame(<<"TRCK">>, _Size, RawContent, _Flags, _Version) -> + {trck, extract_v2_string(RawContent)}; +parse_v2_frame(<<"TRK">>, _Size, RawContent, _Flags, _Version) -> + {trck, extract_v2_string(RawContent)}; +parse_v2_frame(<<"TCON">>, _Size, RawContent, _Flags, _Version) -> + {tcon, extract_v2_string(RawContent)}; +parse_v2_frame(<<"TCO">>, _Size, RawContent, _Flags, _Version) -> + {tcon, extract_v2_string(RawContent)}; +parse_v2_frame(<<"MCDI">>, _Size, RawContent, _Flags, _Version) -> + {mcdi, RawContent}; +parse_v2_frame(<<"MCI">>, _Size, RawContent, _Flags, _Version) -> + {mcdi, RawContent}; +parse_v2_frame(<<"TLEN">>, _Size, RawContent, _Flags, _Version) -> + {tlen, parse_length(RawContent)}; +parse_v2_frame(<<"TLE">>, _Size, RawContent, _Flags, _Version) -> + {tlen, parse_length(RawContent)}; +parse_v2_frame(_Other, _, _, _, _Version) -> + ignored. + +parse_length(RawContent) -> + Content = binary_to_list(extract_v2_string(RawContent)), + case catch (list_to_integer(Content)) of + Milliseconds when is_integer(Milliseconds) -> + Secs = Milliseconds div 1000, + {Secs div 60, Secs rem 60}; + _ -> undefined + end. + +finalize_v2_frames(Frames) -> + lists:reverse([F || F <- Frames, F /= ignored]). + + + + +read_v1_header(<<"TAG", Rest/binary>>) -> + {ok, [{version, {1, 0, 0}}, + {data, Rest}]}; +read_v1_header(_) -> + not_found. + + +read_v1(File, _Props) -> + file:position(File, {eof, -355}), + case file:read(File, 227) of + <<"TAG+", TitleExt:60/binary, ArtistExt:60/binary, AlbumExt:60/binary, + _Speed:8/integer, Genre:30/binary, _StartTime:6/binary, _EndTime:6/binary>> + -> ok; + _Other -> + {TitleExt, ArtistExt, AlbumExt, _Speed, Genre, _StartTime, _EndTime} = + {<<"">>, <<"">>, <<"">>, 0, <<"">>, <<"">>, <<"">>} + end, + + {ok, Header} = file:read(File, 128), + <<"TAG", Title:30/binary, Artist:30/binary, Album:30/binary, Year:4/binary, + CommentAndTrack:30/binary, GenreNum:8/integer>> = Header, + + case CommentAndTrack of + <> when Track > 0 -> ok; % v1 + _ -> Comment = CommentAndTrack, Track=0 % v1.1 + end, + + CB = fun(B1, B2) -> + BinStr = erlang:concat_binary([strip_nulls(B1, 0), strip_nulls(B2, 0)]), + unicode:characters_to_binary(string:strip(binary_to_list(BinStr)), latin1) + end, + + GenreStr = case Genre of + <<"">> -> v1genre(GenreNum); + _ -> Genre + end, + + Frames = [{tit2, CB(Title, TitleExt)}, {tpe1, CB(Artist, ArtistExt)}, + {talb, CB(Album, AlbumExt)}, + {tdrc, Year}, {comm, Comment}, {trck, Track}, + {tcon, GenreStr}], + %{speed, Speed}, {startTime, StartTime}, {endTime, EndTime}], + {ok, Frames}. + + +parse_synchsafe(FourBytes) -> + <<0:1, S1:7/integer, 0:1, S2:7/integer, 0:1, S3:7/integer, 0:1, S4:7/integer>> = FourBytes, + <> = <<0:4, S1:7/integer, S2:7/integer, S3:7/integer, S4:7/integer>>, + Size. + +bool(1) -> true; +bool(_) -> false. + +strip_nulls(Bin, N) -> + case split_binary(Bin, N) of + {Stuff, <<0, _Rest/binary>>} -> Stuff; + {All, <<>>} -> All; + _Other -> strip_nulls(Bin, N+1) + end. + + +extract_v2_string(Binary) -> + Bin = strip_nulls(decode_v2_string(Binary), 0), + Str = string:strip(binary_to_list(Bin)), + list_to_binary(Str). + +decode_v2_string(<<0:8, Rest/binary>>) -> + unicode:characters_to_binary(Rest, latin1); +decode_v2_string(<<1:8, BOM:2/binary, Rest/binary>>) -> + {Encoding, _} = unicode:bom_to_encoding(BOM), + unicode:characters_to_binary(Rest, Encoding); +decode_v2_string(<<2:8, Rest/binary>>) -> + unicode:characters_to_binary(Rest, {utf16, big}); +decode_v2_string(<<3:8, Rest/binary>>) -> + unicode:characters_to_binary(Rest, utf8); +decode_v2_string(Other) -> + unicode:characters_to_binary(Other, latin1). + + + +v1genre(0) -> "Blues"; +v1genre(1) -> "Classic Rock"; +v1genre(2) -> "Country"; +v1genre(3) -> "Dance"; +v1genre(4) -> "Disco"; +v1genre(5) -> "Funk"; +v1genre(6) -> "Grunge"; +v1genre(7) -> "Hip-Hop"; +v1genre(8) -> "Jazz"; +v1genre(9) -> "Metal"; +v1genre(10) -> "New Age"; +v1genre(11) -> "Oldies"; +v1genre(12) -> "Other"; +v1genre(13) -> "Pop"; +v1genre(14) -> "R&B"; +v1genre(15) -> "Rap"; +v1genre(16) -> "Reggae"; +v1genre(17) -> "Rock"; +v1genre(18) -> "Techno"; +v1genre(19) -> "Industrial"; +v1genre(20) -> "Alternative"; +v1genre(21) -> "Ska"; +v1genre(22) -> "Death Metal"; +v1genre(23) -> "Pranks"; +v1genre(24) -> "Soundtrack"; +v1genre(25) -> "Euro-Techno"; +v1genre(26) -> "Ambient"; +v1genre(27) -> "Trip-Hop"; +v1genre(28) -> "Vocal"; +v1genre(29) -> "Jazz+Funk"; +v1genre(30) -> "Fusion"; +v1genre(31) -> "Trance"; +v1genre(32) -> "Classical"; +v1genre(33) -> "Instrumental"; +v1genre(34) -> "Acid"; +v1genre(35) -> "House"; +v1genre(36) -> "Game"; +v1genre(37) -> "Sound Clip"; +v1genre(38) -> "Gospel"; +v1genre(39) -> "Noise"; +v1genre(40) -> "Alternative Rock"; +v1genre(41) -> "Bass"; +v1genre(42) -> "Soul"; +v1genre(43) -> "Punk"; +v1genre(44) -> "Space"; +v1genre(45) -> "Meditative"; +v1genre(46) -> "Instrumental Pop"; +v1genre(47) -> "Instrumental Rock"; +v1genre(48) -> "Ethnic"; +v1genre(49) -> "Gothic"; +v1genre(50) -> "Darkwave"; +v1genre(51) -> "Techno-Industrial"; +v1genre(52) -> "Electronic"; +v1genre(53) -> "Pop-Folk"; +v1genre(54) -> "Eurodance"; +v1genre(55) -> "Dream"; +v1genre(56) -> "Southern Rock"; +v1genre(57) -> "Comedy"; +v1genre(58) -> "Cult"; +v1genre(59) -> "Gangsta"; +v1genre(60) -> "Top 40"; +v1genre(61) -> "Christian Rap"; +v1genre(62) -> "Pop/Funk"; +v1genre(63) -> "Jungle"; +v1genre(64) -> "Native US"; +v1genre(65) -> "Cabaret"; +v1genre(66) -> "New Wave"; +v1genre(67) -> "Psychadelic"; +v1genre(68) -> "Rave"; +v1genre(69) -> "Showtunes"; +v1genre(70) -> "Trailer"; +v1genre(71) -> "Lo-Fi"; +v1genre(72) -> "Tribal"; +v1genre(73) -> "Acid Punk"; +v1genre(74) -> "Acid Jazz"; +v1genre(75) -> "Polka"; +v1genre(76) -> "Retro"; +v1genre(77) -> "Musical"; +v1genre(78) -> "Rock & Roll"; +v1genre(79) -> "Hard Rock"; +v1genre(80) -> "Folk"; +v1genre(81) -> "Folk-Rock"; +v1genre(82) -> "National Folk"; +v1genre(83) -> "Swing"; +v1genre(84) -> "Fast Fusion"; +v1genre(85) -> "Bebob"; +v1genre(86) -> "Latin"; +v1genre(87) -> "Revival"; +v1genre(88) -> "Celtic"; +v1genre(89) -> "Bluegrass"; +v1genre(90) -> "Avantgarde"; +v1genre(91) -> "Gothic Rock"; +v1genre(92) -> "Progressive Rock"; +v1genre(93) -> "Psychedelic Rock"; +v1genre(94) -> "Symphonic Rock"; +v1genre(95) -> "Slow Rock"; +v1genre(96) -> "Big Band"; +v1genre(97) -> "Chorus"; +v1genre(98) -> "Easy Listening"; +v1genre(99) -> "Acoustic"; +v1genre(100) -> "Humour"; +v1genre(101) -> "Speech"; +v1genre(102) -> "Chanson"; +v1genre(103) -> "Opera"; +v1genre(104) -> "Chamber Music"; +v1genre(105) -> "Sonata"; +v1genre(106) -> "Symphony"; +v1genre(107) -> "Booty Bass"; +v1genre(108) -> "Primus"; +v1genre(109) -> "Porn Groove"; +v1genre(110) -> "Satire"; +v1genre(111) -> "Slow Jam"; +v1genre(112) -> "Club"; +v1genre(113) -> "Tango"; +v1genre(114) -> "Samba"; +v1genre(115) -> "Folklore"; +v1genre(116) -> "Ballad"; +v1genre(117) -> "Power Ballad"; +v1genre(118) -> "Rhythmic Soul"; +v1genre(119) -> "Freestyle"; +v1genre(120) -> "Duet"; +v1genre(121) -> "Punk Rock"; +v1genre(122) -> "Drum Solo"; +v1genre(123) -> "Acapella"; +v1genre(124) -> "Euro-House"; +v1genre(125) -> "Dance Hall"; +v1genre(126) -> "Goa"; +v1genre(127) -> "Drum & Bass"; +v1genre(128) -> "Club - House"; +v1genre(129) -> "Hardcore"; +v1genre(130) -> "Terror"; +v1genre(131) -> "Indie"; +v1genre(132) -> "BritPop"; +v1genre(133) -> "Negerpunk"; +v1genre(134) -> "Polsk Punk"; +v1genre(135) -> "Beat"; +v1genre(136) -> "Christian Gangsta Rap"; +v1genre(137) -> "Heavy Metal"; +v1genre(138) -> "Black Metal"; +v1genre(139) -> "Crossover"; +v1genre(140) -> "Contemporary Christian"; +v1genre(141) -> "Christian Rock"; +v1genre(142) -> "Merengue"; +v1genre(143) -> "Salsa"; +v1genre(144) -> "Thrash Metal"; +v1genre(145) -> "Anime"; +v1genre(146) -> "JPop"; +v1genre(147) -> "Synthpop"; +v1genre(_) -> "Unknown". + + +%% ------------------------------------------------------------ +%% Tests +%% ------------------------------------------------------------ + +-define(TESTPATTERN, "/media/everything/music/The Roots/Things Fall Apart/*.mp3"). + +test() -> + Start = now(), + read_files(filelib:wildcard(?TESTPATTERN), 0, 0), + ?DBG({time, timer:now_diff(now(), Start) / 1000000}). + +read_files([FN|Rest], Total, Fail) -> + case read_file(FN) of + {ok, Props} -> + ?DBG({?GV(trck, Props), ?GV(tit2, Props)}), + read_files(Rest, Total+1, Fail); + not_found -> + read_files(Rest, Total+1, Fail+1) + end; +read_files([], Total, Fail) -> + ?DBG({total, Total}), + ?DBG({fail, Fail}). + + + + +%% $ make test +%% erl +W w -pa ebin -noshell -s id3v2 test -s init stop +%% <0.1.0>: {<<"11">>,<<"100% Dundee">>} +%% <0.1.0>: {<<"14">>,<<"3rd Acts: Ques Vs. Scratch 2...Electric Boogaloo">>} +%% <0.1.0>: {<<"10">>,<<"Act Too (Love Of My Life)">>} +%% <0.1.0>: {<<"1">>,<<"Act Won (Things Fall Apart)">>} +%% <0.1.0>: {<<"13">>,<<"Adrenaline!">>} +%% <0.1.0>: {<<"8">>,<<"Ain't Sayin Nothin' New">>} +%% <0.1.0>: {<<"18">>,<<"Bonus">>} +%% <0.1.0>: {<<"12">>,<<"Diedre vs. Dice">>} +%% <0.1.0>: {<<"16">>,<<"Don't See Us">>} +%% <0.1.0>: {<<"9">>,<<"Double Trouble">>} +%% <0.1.0>: {<<"6">>,<<"Dynamite!">>} +%% <0.1.0>: {<<"4">>,<<"Step Into The Realm">>} +%% <0.1.0>: {<<"2">>,<<"Table Of Contents (Pts. 1 & 2)">>} +%% <0.1.0>: {<<"3">>,<<"The Next Movement">>} +%% <0.1.0>: {<<"17">>,<<"The Return To Innocence Lost">>} +%% <0.1.0>: {<<"5">>,<<"The Spark">>} +%% <0.1.0>: {<<"7">>,<<"Without A Doubt">>} +%% <0.1.0>: {<<"15">>,<<"You Got Me (wt Erykah Badu)">>} +%% <0.1.0>: {total,18} +%% <0.1.0>: {fail,0} +%% <0.1.0>: {time,0.01496} diff --git a/server/media.erl b/server/media.erl new file mode 100644 index 0000000..a761f64 --- /dev/null +++ b/server/media.erl @@ -0,0 +1,46 @@ +-module(media). +-export([init/0,insert/3, ask/2]). + +% Since we are not willing to calculate and deliver all the id3 tags everytime they are requested, +% we try to get something persistent with mnesia. +% Concerning the parsing of id3tags we use the library id3v2 by Brendon Hogger. For detailed information take a +% look at the header of the library. + +% What is an entry in our database made of? By the way the filepath includes the filename. + +-record(track, {artist, title, votes, locked, filepath }). + +% With which application do we play mp3s? + +player(path) -> + {ok, ["/usr/bin/env", "mplayer", "-quiet", path]}. + +% Before this module becomes usable, we must initialize it with the following steps: +% 1. Initialize the mnesiadatabase and create the table within it. +% 2. Parse the mp3s in the working directory and add them to the database +% 3. Get into a loop so the database can be actually queried and files can be played. + +init() -> + mnesia:create_schema([node()]), + mnesia:start(), + mnesia:create_table(track, [{attributes, record_info(fields, track)}]), + io:format("Initialisation of mnesia successful.\n"). + +% Basic insertion of entrys into the database. Some entries are left out because they are 0 or false. + +insert(Artist, Title, Filepath) -> + F = fun() -> + mnesia:write(#track{artist = Artist, title = Title, votes = 0, locked = false, filepath = Filepath}) + end, + mnesia:transaction(F). + +% We want to query in order to simplify the next calls. +ask(Artist, Title) -> + F = fun() -> + mnesia:match_object({track, Artist, Title, '_', '_', '_'}) + end, + {atomic, Results} = mnesia:transaction(F), + Results. + +% We want to play mp3s from our database. +play(Artist, Title) -> io:format("blub"). -- cgit v1.2.3 From c9243b1a59b5494f75485d02d4db13762b886136 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Wed, 13 Oct 2010 11:53:04 +0200 Subject: added makefiles --- client/Makefile | 16 ++++++++++++++++ server/Makefile | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 client/Makefile create mode 100644 server/Makefile diff --git a/client/Makefile b/client/Makefile new file mode 100644 index 0000000..f447307 --- /dev/null +++ b/client/Makefile @@ -0,0 +1,16 @@ +SRC=$(wildcard *.erl) +BIN=$(SRC:.erl=.beam) + +.PHONY: all clean + +####################### + +all: $(BIN) + +%.beam: %.erl + erlc $< + +####################### + +clean: + -$(RM) $(BIN) diff --git a/server/Makefile b/server/Makefile new file mode 100644 index 0000000..f447307 --- /dev/null +++ b/server/Makefile @@ -0,0 +1,16 @@ +SRC=$(wildcard *.erl) +BIN=$(SRC:.erl=.beam) + +.PHONY: all clean + +####################### + +all: $(BIN) + +%.beam: %.erl + erlc $< + +####################### + +clean: + -$(RM) $(BIN) -- cgit v1.2.3 From 3b4edbdddf8af55f63c22bb3ba80fffa46156dd2 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Wed, 13 Oct 2010 11:53:17 +0200 Subject: added some modules for server --- server/client.erl | 15 +++++++++++++++ server/dispatcher.erl | 27 +++++++++++++++++++++++++++ server/server.erl | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 server/client.erl create mode 100644 server/dispatcher.erl create mode 100644 server/server.erl diff --git a/server/client.erl b/server/client.erl new file mode 100644 index 0000000..72658b7 --- /dev/null +++ b/server/client.erl @@ -0,0 +1,15 @@ +-module(client). +-export([start/1, loop/1]). + +start(Client) -> + spawn(client, loop, Client). + +loop(Client) -> + receive + {list} -> + Client ! {ok, media ! {all}}; + + true -> + Client ! {error, unknown_command}, + loop(Client) + end. diff --git a/server/dispatcher.erl b/server/dispatcher.erl new file mode 100644 index 0000000..86b2627 --- /dev/null +++ b/server/dispatcher.erl @@ -0,0 +1,27 @@ +-module(dispatcher). +-export([start/0, init/0, handle/3]). + +start() -> + server:start(dis, dispatcher). + +init() -> + dict:new(). + +handle(_, {register, {User, Password}}, Dict) -> + case dict:find(User) of + {ok, _} -> + {{error, duplicated_user}, Dict}; + true -> + {{ok, user_created}, dict:store(User, Password, Dict)} + end; + +handle(From, {login, {User, Password}}, Dict) -> + case dict:find(User) of + {ok, Password} -> + {{ok, {logged_in, client:start(From)}}, Dict}; + true -> + {{error, user_or_password_invalid}, Dict} + end; + +handle(_, _, Dict) -> + {{error, unknown_command}, Dict}. diff --git a/server/server.erl b/server/server.erl new file mode 100644 index 0000000..42b1822 --- /dev/null +++ b/server/server.erl @@ -0,0 +1,45 @@ +%% --- +%% Excerpted from "Programming Erlang", +%% published by The Pragmatic Bookshelf. +%% Copyrights apply to this code. It may not be used to create training material, +%% courses, books, articles, and the like. Contact us if you are in doubt. +%% We make no guarantees that this code is fit for any purpose. +%% Visit http://www.pragmaticprogrammer.com/titles/jaerlang for more book information. +%%--- +-module(server). +-export([start/2, rpc/2, swap_code/2]). + +start(Name, Mod) -> + register(Name, spawn(fun() -> loop(Name,Mod,Mod:init()) end)). + +swap_code(Name, Mod) -> rpc(Name, {swap_code, Mod}). + +rpc(Name, Request) -> + Name ! {self(), Request}, + receive + {Name, crash} -> exit(rpc); + {Name, ok, Response} -> Response + end. + +loop(Name, Mod, OldState) -> + receive + {From, {swap_code, NewCallbackMod}} -> + From ! {Name, ok, ack}, + loop(Name, NewCallbackMod, OldState); + {From, Request} -> + try Mod:handle(From, Request, OldState) of + {Response, NewState} -> + From ! {Name, ok, Response}, + loop(Name, Mod, NewState) + catch + _: Why -> + log_the_error(Name, Request, Why), + From ! {Name, crash}, + loop(Name, Mod, OldState) + end + end. + +log_the_error(Name, Request, Why) -> + io:format("Server ~p request ~p ~n" + "caused exception ~p~n", + [Name, Request, Why]). -- cgit v1.2.3