Download the file
  1. #!/usr/bin/perl -w
  2.  
  3. #####################################################################
  4. ## lcdplaylist v1.0
  5. ##
  6. ## displays filelist, playlist and play info on a 20x4 LCD display
  7. ## reacts on keyborad & IR remote control, pilots a player
  8. ##
  9. ## author: Matthieu Weber (mweber@mit.jyu.fi)
  10. ##
  11. ## Requirements : dmms, lcdproc, lirc
  12. ##
  13. #####################################################################
  14.  
  15. $DEBUG = 1; # other values: 0
  16.  
  17. #### Config #########################################################
  18.  
  19. my @mixer_channels = ('v', 'w'); # v = master w = PCM
  20. my %mixer_init_level = $DEBUG ? ('v' => 0, 'w' => 0) : ('v' => 80, 'w' => 80);
  21.  
  22. my $cd_device="/dev/cdrom";
  23. my $cd_root="/cdrom";
  24.  
  25. my $rejectlist = "\.txt\$|\.log\$|\.bmp\$|\.jpg\$|\.gif\$|\.png\$|\.cddb\$|md5sums";
  26. my $info_scroll_line="title"; # other values: "none", "title", "album", "author"
  27. my $playlist_displayed_info="title"; # other values: "full", "title", "album", "author"
  28.  
  29. my $scroll_speed=4;
  30.  
  31. my $startup_sound = "/hal_ok.ogg";
  32. #my $startup_sound = "/home/mweber/linux/freevo/testfiles/Music/ThomasRusiak-Hiphopper.mp3";
  33.  
  34. my %ir_action=(
  35. "OPEN/CLOSE" => {
  36. "info" => {"action" => "TOGGLE_MOUNT"},
  37. "playlist" => {"action" => "TOGGLE_MOUNT"},
  38. "filelist" => {"action" => "TOGGLE_MOUNT"},
  39. },
  40. "MODE" => {
  41. "info" => {"action" => "DISPLAY_MODE_SWITCH"},
  42. "playlist" => {"action" => "DISPLAY_MODE_SWITCH"},
  43. "filelist" => {"action" => "DISPLAY_MODE_SWITCH"},
  44. },
  45. "TEXT" => {
  46. "playlist" => {"action" =>"PLAYLIST_TEXT_SWITCH"},
  47. "info" => {"action" => "DISPLAY_TEXT_SWITCH"},
  48. },
  49. "2" => {
  50. "playlist" => {"action" => "PLAYLIST_UP", "repeat" => 2},
  51. "filelist" => {"action" => "FILELIST_UP", "repeat" => 2},
  52. },
  53. "4" => {
  54. "playlist" => {"action" => "PLAYLIST_LEFT"},
  55. "filelist" => {"action" => "FILELIST_LEFT"},
  56. },
  57. "6" => {
  58. "playlist" => {"action" => "PLAYLIST_RIGHT"},
  59. "filelist" => {"action" => "FILELIST_RIGHT"},
  60. },
  61. "8" => {
  62. "playlist" => {"action" => "PLAYLIST_DOWN", "repeat" => 2},
  63. "filelist" => {"action" => "FILELIST_DOWN", "repeat" => 2},
  64. },
  65. # "+" => {
  66. # "playlist" => {"action" => "FILELIST_ADD"},
  67. # "filelist" => {"action" => "PLAYLIST_PLAY"},
  68. # },
  69. # "0" => {
  70. # "playlist" => {"action" => "PLAYLIST_CLEAR"},
  71. # },
  72. # "-" => {
  73. # "playlist" => {"action" => "PLAYLIST_DEL"},
  74. # },
  75. # "A.F/P" => {
  76. # "info" => {"action" => "VOLUME_UP 2", "repeat" => 1},
  77. # "playlist" => {"action" => "VOLUME_UP 2", "repeat" => 1},
  78. # "filelist" => {"action" => "VOLUME_UP 2", "repeat" => 1},
  79. # },
  80. # "B.-/--" => {
  81. # "info" => {"action" => "VOLUME_DOWN 2", "repeat" => 1},
  82. # "playlist" => {"action" => "VOLUME_DOWN 2", "repeat" => 1},
  83. # "filelist" => {"action" => "VOLUME_DOWN 2", "repeat" => 1},
  84. # },
  85. # "MEMO" => {
  86. # "info" => {"action" => "MUTE"},
  87. # "playlist" => {"action" => "MUTE"},
  88. # "filelist" => {"action" => "MUTE"},
  89. # },
  90. "TIME" => {
  91. "info" => {"action" => "TIME"},
  92. },
  93. ">" => {
  94. "info" => {"action" => "PLAY"},
  95. "playlist" => {"action" => "PLAYLIST_PLAY"},
  96. "filelist" => {"action" => "FILELIST_ADD"},
  97. },
  98. "||" => {
  99. "info" => {"action" => "PAUSE"},
  100. "playlist" => {"action" => "PLAYLIST_DEL"},
  101. },
  102. "[]" => {
  103. "info" => {"action" => "STOP"},
  104. "playlist" => {"action" => "PLAYLIST_CLEAR"},
  105. },
  106. ">>|" => {
  107. "info" => {"action" => "NEXT", "repeat" => 2},
  108. "playlist" => {"action" => "NEXT", "repeat" => 2},
  109. },
  110. "|<<" => {
  111. "info" => {"action" => "PREV", "repeat" => 2},
  112. "playlist" => {"action" => "PREV", "repeat" => 2},
  113. },
  114. ">>" => {
  115. "info" => {"action" => "FWD 3", "repeat" => 2},
  116. },
  117. "<<" => {
  118. "info" => {"action" => "BWD 3", "repeat" => 2},
  119. },
  120. );
  121.  
  122. my %kbd_action = ();
  123. if ($DEBUG) {
  124. %kbd_action = (
  125. "." => {
  126. "info" => {"action" => "TOGGLE_MOUNT"},
  127. "playlist" => {"action" => "TOGGLE_MOUNT"},
  128. "filelist" => {"action" => "TOGGLE_MOUNT"},
  129. },
  130. "i" => {
  131. "playlist" => {"action" =>"PLAYLIST_UP"},
  132. "filelist" => {"action" =>"FILELIST_UP"},
  133. "info" => {"action" => "PREV"},
  134. },
  135. "j" => {
  136. "playlist" => {"action" =>"PLAYLIST_LEFT"},
  137. "filelist" => {"action" =>"FILELIST_LEFT"},
  138. "info" => {"action" => "BWD 5"},
  139. },
  140. "k" => {
  141. "playlist" => {"action" =>"PLAYLIST_RIGHT"},
  142. "filelist" => {"action" =>"FILELIST_RIGHT"},
  143. "info" => {"action" => "FWD 5"},
  144. },
  145. "m" => {
  146. "playlist" => {"action" =>"PLAYLIST_DOWN"},
  147. "filelist" => {"action" =>"FILELIST_DOWN"},
  148. "info" => {"action" => "NEXT"},
  149. },
  150. "+" => {
  151. "playlist" => {"action" =>"PLAYLIST_PLAY"},
  152. "filelist" => {"action" =>"FILELIST_ADD"},
  153. "info" => {"action" => "PLAY"},
  154. },
  155. "0" => {
  156. "playlist" => {"action" =>"PLAYLIST_CLEAR"},
  157. "info" => {"action" => "STOP"},
  158. },
  159. "9" => {
  160. "playlist" => {"action" =>"PLAYLIST_DEL"},
  161. "info" => {"action" => "PAUSE"},
  162. },
  163. " " => {
  164. "playlist" => {"action" =>"DISPLAY_MODE_SWITCH"},
  165. "filelist" => {"action" =>"DISPLAY_MODE_SWITCH"},
  166. "info" => {"action" =>"DISPLAY_MODE_SWITCH"},
  167. },
  168. "t" => {
  169. "playlist" => {"action" =>"PLAYLIST_TEXT_SWITCH"},
  170. "info" => {"action" =>"DISPLAY_TEXT_SWITCH"},
  171. },
  172. "e" => {
  173. "info" => {"action" =>"TIME"},
  174. },
  175. );
  176. }
  177. else {
  178. %kbd_action = (
  179. "e" => {
  180. "info" => {"action" => "TOGGLE_MOUNT"},
  181. "playlist" => {"action" => "TOGGLE_MOUNT"},
  182. "filelist" => {"action" => "TOGGLE_MOUNT"},
  183. },
  184. "t" => {
  185. "playlist" => {"action" =>"PLAYLIST_UP"},
  186. "filelist" => {"action" =>"FILELIST_UP"},
  187. "info" => {"action" => "PREV"},
  188. },
  189. "h" => {
  190. "playlist" => {"action" =>"PLAYLIST_LEFT"},
  191. "filelist" => {"action" =>"FILELIST_LEFT"},
  192. "info" => {"action" => "BWD 5"},
  193. },
  194. "k" => {
  195. "playlist" => {"action" =>"PLAYLIST_RIGHT"},
  196. "filelist" => {"action" =>"FILELIST_RIGHT"},
  197. "info" => {"action" => "FWD 5"},
  198. },
  199. "f" => {
  200. "playlist" => {"action" =>"PLAYLIST_DOWN"},
  201. "filelist" => {"action" =>"FILELIST_DOWN"},
  202. "info" => {"action" => "NEXT"},
  203. },
  204. "i" => {
  205. "playlist" => {"action" =>"PLAYLIST_PLAY"},
  206. "filelist" => {"action" =>"FILELIST_ADD"},
  207. "info" => {"action" => "PLAY"},
  208. },
  209. "r" => {
  210. "playlist" => {"action" =>"PLAYLIST_CLEAR"},
  211. "info" => {"action" => "STOP"},
  212. },
  213. "y" => {
  214. "playlist" => {"action" =>"PLAYLIST_DEL"},
  215. "info" => {"action" => "PAUSE"},
  216. },
  217. "u" => {
  218. "playlist" => {"action" =>"DISPLAY_MODE_SWITCH"},
  219. "filelist" => {"action" =>"DISPLAY_MODE_SWITCH"},
  220. "info" => {"action" =>"DISPLAY_MODE_SWITCH"},
  221. },
  222. );
  223. }
  224.  
  225. #### End Config #####################################################
  226.  
  227.  
  228. use IO::Socket;
  229. use Fcntl;
  230. use Term::ReadKey;
  231.  
  232. my %reset_priority_time;
  233. my %reset_priority_value;
  234. my $volume_before_mute = -1;
  235. my ($lcd_width,$lcd_height,$cell_width,$cell_height);
  236. my $filelist_first_line;
  237. my $filelist_cursor_pos_min;
  238. my $filelist_cursor_pos_max;
  239. my $filelist_cursor_pos;
  240. my $display_mode = "filelist"; # other value are "playlist" and "info"
  241. my %files;
  242. my $lock_file="/var/lock/lcdplaylist.lock";
  243. my $playlist_pos = -1;
  244. my $is_playing = 0;
  245. my $is_paused = 0;
  246. my $got_info = 0;
  247. my $time_mode = 0; # 0 = elapesd 1 = remaining 2 = track total
  248. my @async_queue=();
  249. my $async_event_processed = 1;
  250. my $cd_volume_label="";
  251. my $playlist_edit_pos=-1;
  252. my $playlist_is_empty=1;
  253. my %titres_txt_db;
  254. my @dir_pos_history = ();
  255. my $cd_ready = 0;
  256. my $cd_drive_closed_empty = 1;
  257. my $tray_position = "closed"; # other value: "open"
  258.  
  259. $SIG{PIPE}=\&bp;
  260. $SIG{TERM}=\&silent_bp;
  261. $SIG{INT}=\&silent_bp;
  262. $SIG{__DIE__}=\&bp;
  263.  
  264. ## Mixer functions ################################################
  265.  
  266. sub set_mixer_value {
  267. my ($channel,$param) = @_;
  268. system("aumix -$channel $param");
  269. }
  270.  
  271. sub get_mixer_value {
  272. my $channel = $mixer_channels[0];
  273. my $val=`aumix -$channel q`;
  274. my ($l,$r) = ($val =~ /\w+ (\d+), (\d+)/);
  275. return int(($l+$r)/2);
  276. }
  277.  
  278. sub mixer_init {
  279. foreach $c (@mixer_channels) {
  280. &set_mixer_value($c,$mixer_init_level{$c});
  281. }
  282. }
  283.  
  284. sub set_bar {
  285. my($widget,$y,$value,$force) = @_;
  286. my $maxbarwidth = ($lcd_width - 7) * $cell_width;
  287. my $barwidth = int($value*$maxbarwidth/100);
  288. if($force || ($barwidth != $old_barwidth{$widget} && ($barwidth%5 == 0 || $barwidth%5 == 1)) ) {
  289. &send_lcd("widget_set mixer $widget 7 $y $barwidth") ;
  290. $old_barwidth{$widget} = $barwidth;
  291. }
  292. }
  293.  
  294. sub unmute {
  295. if($volume_before_mute != -1) {
  296. &set_mixer_value($mixer_channels[0],$volume_before_mute);
  297. $volume_before_mute = -1;
  298. }
  299. }
  300.  
  301. ## File list functions ##############################################
  302.  
  303. sub find {
  304. my $depth = shift(@_);
  305. my @l = @_;
  306. my @out;
  307.  
  308. foreach $e (@l) {
  309. next if $e =~ /^\./;
  310. next if $e =~ $rejectlist;
  311. if (-d "$e" && $depth != 0) {
  312. opendir(DIR,"$e");
  313. push(@out, find($depth -1, map {$_ = "$e/$_" } grep(!/^\./,readdir(DIR))));
  314. closedir(DIR);
  315. }
  316. else {
  317. push @out, $e;
  318. }
  319. }
  320. return @out;
  321. }
  322.  
  323. sub load_file_list {
  324. my $root = shift(@_);
  325.  
  326. $files{root} = $root;
  327. $files{pos} = 0 if ! defined $files{pos};
  328. $files{list} = [ sort &find(1, $root)];
  329. $files{nbelem} = @{$files{list}};
  330. my $nblines = $filelist_cursor_pos_max - $filelist_cursor_pos_min + 1;
  331. if ($files{nbelem} > $nblines &&
  332. $files{pos} + $nblines > $files{nbelem}) {
  333. $filelist_cursor_pos = $filelist_cursor_pos_max - $files{nbelem} + $files{pos} + 1;
  334. }
  335. else {
  336. $filelist_cursor_pos = $filelist_cursor_pos_min;
  337. }
  338. &show_files();
  339. }
  340.  
  341. sub show_files {
  342. my %file_menu_items=("up" => "^", "down" => "v", "left" => "<", "right" => ">");
  343. my $menu = "Browse: ";
  344.  
  345. my $display_line_amount = $lcd_height - $filelist_first_line + 1;
  346. if (exists $files{"root"}) {
  347. my $beg = $files{pos} - $filelist_cursor_pos;
  348. for($i=0; $i<$display_line_amount; $i++) {
  349. my $left = 2;
  350. my $top = $filelist_first_line + $i;
  351. my $right = $lcd_width;
  352. my $bot = $top;
  353. my $txt = defined $files{list}->[$beg+$i] ? &latin1_to_ascii(&get_last_path_part($files{list}->[$beg+$i])) : " ";
  354. $txt = substr($txt, 0, $right - $left + 1) if $filelist_cursor_pos != $i;
  355. &send_lcd("widget_set filelist line$i $left $top $right $bot h $scroll_speed {$txt}");
  356. }
  357. &send_lcd("widget_set filelist cursor 1 ".($filelist_first_line + $filelist_cursor_pos)." {>}");
  358. $menu .= $file_menu_items{"left"}." " if $files{root} ne $cd_root;
  359. $menu .= $file_menu_items{"right"}." " if -d $files{list}->[$files{pos}];
  360. $menu .= $file_menu_items{"up"}." " if $files{pos} > 0;
  361. $menu .= $file_menu_items{"down"}." " if $files{pos} < $files{nbelem} -1;
  362. }
  363. else {
  364. $menu .= "mount";
  365. }
  366. &show_one_line_menu($menu);
  367. }
  368.  
  369. sub clear_files {
  370. my $display_line_amount;
  371.  
  372. #print "clear_files\n";
  373. $display_line_amount = $lcd_height - $filelist_first_line + 1;
  374.  
  375. for($i=0; $i<$display_line_amount; $i++) {
  376. my $left = 2;
  377. my $top = $filelist_first_line + $i;
  378. my $right = $lcd_width;
  379. my $bot = $top;
  380. &send_lcd("widget_set filelist line$i $left $top $right $bot h $scroll_speed { }");
  381. }
  382. &send_lcd("widget_set filelist cursor 1 ".($filelist_first_line)." { }");
  383. &show_one_line_menu("Browse: mount ");
  384. %files = ();
  385. }
  386.  
  387. sub get_last_path_part {
  388. my $p = shift(@_);
  389. my ($r) = ($p=~/\/([^\/]+)\/?$/);
  390. return $r;
  391. }
  392.  
  393. sub show_one_line_menu {
  394. my $text = shift(@_);
  395. #print "show_one_line_menu\n";
  396. &send_lcd("widget_set filelist header 1 1 {$text}");
  397. }
  398.  
  399. ## Playlist functions ################################################
  400.  
  401. sub playlist_add {
  402. my @l = @_;
  403. my $pl_was_empty = $#playlist == -1 ? 1 : 0;
  404. foreach $file (@l) {
  405. push @playlist, {"title" => "", "length" => "", "file" => $file};
  406. my $titres_txt = &get_titres_txt_path($file);
  407. if ($titres_txt ne "") {
  408. &playlist_get_info($#playlist); # File is the last pushed, which is at
  409. # the end of the list
  410. }
  411. }
  412. if($playlist_pos == -1) {
  413. $playlist_pos = 0;
  414. }
  415. if($pl_was_empty == 1) {
  416. # Just get the first element of the list displayed :)
  417. &dmms_play() if !$DEBUG;
  418. }
  419. }
  420.  
  421. sub get_titres_txt_path {
  422. my $file = shift(@_);
  423. $file =~ s/\/[^\/]+$//;
  424. $titres_txt = "$file/titres.txt";
  425. return -f $titres_txt ? $titres_txt : "";
  426. }
  427.  
  428. sub latin1_to_ascii {
  429. $s = shift(@_);
  430. $s =~ tr//aaaaaaceeeeiiiiooooonuuuuyy/;
  431. $s =~ tr//AAAAAACEEEEIIIIOOOOONUUUUYY/;
  432. $s =~ s//ae/g;
  433. $s =~ s//AE/g;
  434. $s =~ s//ss/g;
  435. return $s;
  436. }
  437.  
  438. sub get_album_from_titres_txt {
  439. my $file = shift(@_);
  440. my $state="title";
  441. my %album;
  442.  
  443. my $titres_txt = &get_titres_txt_path($file);
  444. if ($titres_txt eq "") {
  445. return ();
  446. }
  447.  
  448. if (! exists $titres_txt_cb{$titres_txt}) {
  449. open(TITRES, $titres_txt);
  450. while(<TITRES>) {
  451. s/\r?\n//;
  452. next if /^\s*$/ && $state eq "tracks";
  453.  
  454. if ($state eq "title") {
  455. $album{title} = &latin1_to_ascii($_);
  456. $state = "composer";
  457. }
  458. elsif ($state eq "composer") {
  459. my $tmp = $_;
  460. my ($comp, $year) = ($tmp =~ /(.*), (\d*)$/);
  461. if ($comp eq "") {
  462. $album{composer} = &latin1_to_ascii($tmp);
  463. $album{year} = 0;
  464. }
  465. else {
  466. $album{composer} = &latin1_to_ascii($comp);
  467. $album{year} = $year;
  468. }
  469. $state = "tracks";
  470. }
  471. elsif ($state eq "tracks") {
  472. my $l = $_;
  473. next unless $l =~ /^ ?\d/;
  474. my ($num, $ttitle) = ($l =~ /^( ?\d+)[\. ]?-? ?(.*)/);
  475. next if $num > 999;
  476. $num += 0;
  477. $album{tracks}->[$num] = sprintf("%02d - %s",$num,&latin1_to_ascii($ttitle));
  478. }
  479. }
  480. close(TITRES);
  481.  
  482. if (defined $album{title} && $album{title} !~ /^\s*$/) {
  483. $titres_txt_cb{$titres_txt} = \%album;
  484. }
  485. else {
  486. return ();
  487. }
  488. }
  489.  
  490. return %{$titres_txt_cb{$titres_txt}};
  491. }
  492.  
  493. # Initializes an asynchrous request
  494. sub playlist_get_info {
  495. my $pos = shift(@_);
  496. my $file = $playlist[$pos]->{file};
  497. my %album = &get_album_from_titres_txt($file);
  498.  
  499. if (%album) {
  500. my ($tracknum) = ($file =~ /(\d+)\./);
  501. $tracknum += 0;
  502. &playlist_get_info_cb ($pos, $album{tracks}->[$tracknum],
  503. $album{composer}, $album{title}, 0);
  504. &playlist_refresh_display();
  505. }
  506. else {
  507. push(@async_queue, {"data" => $pos,
  508. "act" => \&playlist_get_info_act,
  509. "cb" => \&playlist_get_info_cb});
  510. }
  511. }
  512.  
  513. # What to do when the asynchronous request is to be processed
  514. sub playlist_get_info_act {
  515. my $data = shift(@_);
  516.  
  517. &send_dmms("INFO ".$playlist[$data]->{file});
  518. }
  519.  
  520. # What to do with the result of the asynchronous request
  521. sub playlist_get_info_cb {
  522. my ($pos, $title, $author, $album, $length) = @_;
  523. my $file = $playlist[$pos]->{file};
  524.  
  525. if ($length >= 0 && defined $title) {
  526. if (! defined $playlist[$pos]->{title} || $playlist[$pos]->{title} eq "") {
  527. $playlist[$pos]->{title} = &latin1_to_ascii(&clean_title($title));
  528. }
  529. $playlist[$pos]->{author} = defined $author ? &latin1_to_ascii($author) : "" if ! defined $playlist[$pos]->{author};
  530. $playlist[$pos]->{album} = defined $album ? &latin1_to_ascii($album) : "" if ! defined $playlist[$pos]->{album};
  531. $playlist[$pos]->{length} = $length;
  532. $playlist[$pos]->{valid} = 1;
  533. &playlist_refresh_display();
  534. }
  535. else {
  536. &playlist_delete($pos);
  537. }
  538. }
  539.  
  540. sub playlist_delete {
  541. my $pos = shift(@_);
  542. splice(@playlist,$pos,1);
  543. $playlist_pos -- if $pos < $playlist_pos;
  544. $playlist_pos = 0 if $playlist_pos < 0 && $#playlist > -1;
  545. $playlist_pos = $#playlist if $playlist_pos > $#playlist;
  546. $playlist_edit_pos = $#playlist if $playlist_edit_pos > $#playlist;
  547. &playlist_refresh_display();
  548. }
  549.  
  550. sub playlist_get_pos {
  551. return $playlist_pos;
  552. }
  553.  
  554. sub clean_title {
  555. my $s = shift(@_);
  556.  
  557. $s =~ s/\....$//;
  558. $s =~ s/_/ /g;
  559. $s =~ s/%20/ /g;
  560. return $s;
  561. }
  562.  
  563. sub playlist_get_album {
  564. my $pos = shift(@_);
  565.  
  566. if($#playlist > -1 && $pos <= $#playlist) {
  567. if(defined $playlist[$pos]->{valid} && $playlist[$pos]->{valid} == 1) {
  568. my $t = $playlist[$pos]->{album} ne "" ? $playlist[$pos]->{album} : "Unknown album";
  569. $t = " " if $t eq "";
  570. return $t
  571. }
  572. else {
  573. return " ";
  574. }
  575. }
  576. else {
  577. return "";
  578. }
  579. }
  580.  
  581. sub playlist_get_author {
  582. my $pos = shift(@_);
  583.  
  584. if($#playlist > -1 && $pos <= $#playlist) {
  585. if(defined $playlist[$pos]->{valid} && $playlist[$pos]->{valid} == 1) {
  586. my $t = $playlist[$pos]->{author} ne "" ? $playlist[$pos]->{author} : "Unknown artist";
  587. $t = " " if $t eq "";
  588. return $t
  589. }
  590. else {
  591. return " ";
  592. }
  593. }
  594. else {
  595. return "";
  596. }
  597. }
  598.  
  599. sub playlist_get_title {
  600. my $pos = shift(@_);
  601.  
  602. if($#playlist > -1 && $pos <= $#playlist) {
  603. if(defined $playlist[$pos]->{valid} && $playlist[$pos]->{valid} == 1) {
  604. my $t = $playlist[$pos]->{title} ne "" ? $playlist[$pos]->{title} : "Unknown title";
  605. $t = " " if $t eq "";
  606. return $t
  607. }
  608. else {
  609. &playlist_get_info($pos);
  610. return &clean_title(&get_last_path_part($playlist[$pos]->{file}));
  611. }
  612. }
  613. else {
  614. return "";
  615. }
  616. }
  617.  
  618. #sub playlist_get_title {
  619. # my $pos = shift(@_);
  620. #
  621. # if($#playlist > -1 && $pos <= $#playlist) {
  622. # if(defined $playlist[$pos]->{valid} && $playlist[$pos]->{valid} == 1) {
  623. # my $t = "";
  624. # if ($info_scroll_line eq "full") {
  625. # $t .= $playlist[$pos]->{author} . ", " if $playlist[$pos]->{author} ne "";
  626. # $t .= $playlist[$pos]->{album} . ": " if $playlist[$pos]->{album} ne "";
  627. # $t .= $playlist[$pos]->{title};
  628. # }
  629. # elsif ($info_scroll_line eq "title") {
  630. # $t .= $playlist[$pos]->{title} ne "" ? $playlist[$pos]->{title} : "Unknown";
  631. # }
  632. # elsif ($info_scroll_line eq "author") {
  633. # $t .= $playlist[$pos]->{author} ne "" ? $playlist[$pos]->{author} : "Unknown";
  634. # }
  635. # elsif ($info_scroll_line eq "album") {
  636. # $t .= $playlist[$pos]->{album} ne "" ? $playlist[$pos]->{album} : "Unknown";
  637. # }
  638. # $t = " " if $t eq "";
  639. # return $t
  640. # }
  641. # else {
  642. # &playlist_get_info($pos);
  643. # return &clean_title(&get_last_path_part($playlist[$pos]->{file}));
  644. # }
  645. # }
  646. # else {
  647. # return "";
  648. # }
  649. #}
  650.  
  651. sub playlist_clear {
  652. if($#playlist > -1) {
  653. $playlist_pos = -1;
  654. $playlist_edit_pos = -1;
  655. $playlist_cursor_pos = $playlist_cursor_pos_min;
  656. undef @playlist;
  657. @playlist = ();
  658. &playlist_refresh_display();
  659. #&display_title("Empty playlist");
  660. &display_title("CD: $cd_volume_label");
  661. &display_info ("","",0);
  662. }
  663. }
  664.  
  665. sub playlist_refresh_display {
  666.  
  667. return if $display_mode ne "playlist";
  668. my $display_line_amount = $lcd_height - $playlist_first_line + 1;
  669.  
  670. if ($#playlist > -1 ) {
  671. $playlist_edit_pos = 0 if $playlist_edit_pos == -1;
  672. &send_lcd("widget_set playlist plinfo 5 1 {".($playlist_edit_pos+1)."/".($#playlist+1)."}");
  673. }
  674. else {
  675. &send_lcd("widget_set playlist plinfo 5 1 {Empty}");
  676. }
  677. my $beg = $playlist_edit_pos - $playlist_cursor_pos;
  678. if ($beg < 0) {
  679. $beg = 0;
  680. $playlist_cursor_pos = $#playlist;
  681. }
  682. $playlist_cursor_pos = $playlist_cursor_pos_min if $playlist_cursor_pos < $playlist_cursor_pos_min;
  683. &send_lcd("widget_set playlist editcursor 1 ".($playlist_first_line + $playlist_cursor_pos)." {-}") if $#playlist > -1;
  684. &send_lcd("widget_set playlist playcursor $lcd_width 1 { }");
  685. for($i=0; $i<$display_line_amount; $i++) {
  686. my $left = 2;
  687. my $top = $playlist_first_line + $i;
  688. my $right = $lcd_width;
  689. my $bot = $top;
  690. my $txt = "";
  691. if($playlist_displayed_info eq "title") {
  692. $txt = &playlist_get_title($beg + $i);
  693. }
  694. elsif($playlist_displayed_info eq "album") {
  695. $txt = &playlist_get_album($beg + $i);
  696. }
  697. elsif ($playlist_displayed_info eq "author") {
  698. $txt = &playlist_get_author($beg + $i);
  699. }
  700. elsif ($playlist_displayed_info eq "full") {
  701. $txt = &playlist_get_author($beg + $i).", ".
  702. &playlist_get_album($beg + $i).": ".
  703. &playlist_get_title($beg + $i);
  704. }
  705. $txt = substr($txt, 0, $right - $left + 1) if $playlist_cursor_pos != $i;
  706. $txt = " " if $txt eq "";
  707. &send_lcd("widget_set playlist line$i $left $top $right $bot h $scroll_speed {$txt}");
  708. if ($#playlist > -1 ) {
  709. if ($playlist_pos == $beg + $i && $txt ne " ") {
  710. if ($playlist_edit_pos != $playlist_pos) {
  711. &send_lcd("widget_set playlist playcursor 1 ".($playlist_first_line + $i)." {|}");
  712. }
  713. else {
  714. &send_lcd("widget_set playlist editcursor 1 ".($playlist_first_line + $i)." {+}");
  715. }
  716. }
  717. }
  718. else {
  719. &send_lcd("widget_set playlist editcursor 1 ".($playlist_first_line + $i)." { }");
  720. }
  721. }
  722. }
  723.  
  724. ## Dmms Player functions ###############################################
  725.  
  726. sub dmms_play {
  727. &send_dmms("PLAY ".$playlist[$playlist_pos]->{file}) if -f $playlist[$playlist_pos]->{file};
  728. }
  729.  
  730. sub dmms_next {
  731. if($playlist_pos < $#playlist && $playlist_pos > -1) {
  732. #print "playlist_pos = $playlist_pos pl_length = ".$#playlist."\n";
  733. $playlist_pos++;
  734. &dmms_play();
  735. }
  736. else {
  737. $is_playing = 0;
  738. }
  739. }
  740.  
  741. sub dmms_prev {
  742. if ($playlist_pos > 0) {
  743. $playlist_pos--;
  744. &dmms_play();
  745. }
  746. }
  747.  
  748. sub dmms_pause {
  749. &send_dmms("PAUSE");
  750. }
  751.  
  752. sub dmms_stop {
  753. &send_dmms("STOP");
  754. }
  755.  
  756. sub dmms_fwd {
  757. my $t = shift(@_);
  758. &send_dmms("FWD $t");
  759. }
  760.  
  761. sub dmms_bwd {
  762. my $t = shift(@_);
  763. &send_dmms("BWD $t");
  764. }
  765.  
  766. ## Info window functions ###############################################
  767.  
  768. sub status_to_symbol {
  769. my $status = shift(@_);
  770.  
  771. if ($status eq "STOP") {
  772. return "[]";
  773. }
  774. elsif ($status eq "PLAY") {
  775. return "|>";
  776. }
  777. elsif ($status eq "PAUSE") {
  778. return "||";
  779. }
  780. else {
  781. return "??";
  782. }
  783. }
  784.  
  785. sub display_time {
  786. my $time = shift(@_);
  787. #my $x = $lcd_width > 16 ? $lcd_width - 10 : $lcd_width - 4;
  788. my $x = $lcd_width - 5;
  789. my $sign = " ";
  790. if ($#playlist > -1) {
  791. if ($time_mode == 1) {
  792. $time = $playlist[$playlist_pos]->{length} - $time;
  793. $sign = "-";
  794. }
  795. elsif ($time_mode == 2) {
  796. $time = $playlist[$playlist_pos]->{length};
  797. }
  798. }
  799. &send_lcd(sprintf("widget_set info time $x 4 {$sign%02d:%02d}",$time/60,$time%60));
  800. }
  801.  
  802. sub display_pl_length {
  803. my $pl_length = shift(@_);
  804.  
  805. if ($lcd_width > 16) {
  806. if ($pl_length < 100) {
  807. &send_lcd("widget_set info pl_length 6 2 {$pl_length}");
  808. }
  809. else {
  810. &send_lcd("widget_set info pl_length 6 2 {}");
  811. }
  812. }
  813. }
  814.  
  815. sub display_length {
  816. my $length = $playlist[$playlist_pos]->{length};
  817. my $x = $lcd_width - 4;
  818.  
  819. if ($lcd_width > 16 && $length < 6000000) {
  820. &send_lcd(sprintf("widget_set info length $x 4 {%02d:%02d}", $length/60, $length%60));
  821. }
  822. }
  823.  
  824. sub display_status {
  825. my $status = shift(@_);
  826.  
  827. &send_lcd("widget_set info status 1 4 {".&status_to_symbol($status)."}");
  828. }
  829.  
  830. sub display_pos {
  831. my $pos = shift(@_);
  832.  
  833. &send_lcd(sprintf("widget_set info pos 4 4 {%#2d}",$pos+1));
  834. }
  835.  
  836. sub display_title {
  837. my ($title) = @_;
  838. return if $title eq "";
  839. if ($info_scroll_line ne "title") {
  840. $title = substr($title,0,$lcd_width);
  841. }
  842. &send_lcd("widget_set info title 1 1 $lcd_width 1 h $scroll_speed {$title}");
  843. }
  844.  
  845. sub display_album {
  846. my ($album) = @_;
  847. return if $album eq "";
  848. if ($info_scroll_line ne "album") {
  849. $album = substr($album,0,$lcd_width);
  850. }
  851. &send_lcd("widget_set info album 1 2 $lcd_width 2 h $scroll_speed {$album}")
  852. }
  853.  
  854. sub display_author {
  855. my ($author) = @_;
  856. return if $author eq "";
  857. if ($info_scroll_line ne "author") {
  858. $author = substr($author,0,$lcd_width);
  859. }
  860. &send_lcd("widget_set info author 1 3 $lcd_width 3 h $scroll_speed {$author}");
  861. }
  862.  
  863. sub display_info {
  864. my ($rate, $freq, $nch) = @_;
  865. if ($rate ne "") {
  866. $rate /= 1000;
  867. $rate = int($rate);
  868. }
  869. $freq = int($freq / 1000) if $freq ne "";
  870. if ($nch == 2 ) {
  871. $nch = "oo";
  872. }
  873. elsif ($nch == 1 ) {
  874. $nch = "o";
  875. }
  876. else {
  877. $nch = "";
  878. }
  879. if($lcd_width < 16) {
  880. &send_lcd("widget_set info info 4 4 {$rate $freq $nch}");
  881. }
  882. else {
  883. &send_lcd("widget_set info info 5 4 {$rate $freq $nch}");
  884. }
  885. }
  886.  
  887. ## LCD Management Functions ############################################
  888.  
  889. sub raise_screen {
  890. my ($scr,$prio,$lower,$lower_prio) = @_;
  891.  
  892. #print "raise_screen\n";
  893. &send_lcd("screen_set $scr name $scr priority $prio");
  894. $reset_priority_time{$scr} = time()+$lower unless $lower eq "noauto";
  895. $reset_priority_value{$scr} = $lower_prio unless $lower eq "noauto";
  896. }
  897.  
  898. sub lower_screen {
  899. my ($scr,$prio) = @_;
  900.  
  901. #print "lower_screen\n";
  902. &send_lcd("screen_set $scr name $scr priority $prio");
  903. $reset_priority_time{$scr} = 0;
  904. }
  905.  
  906. sub show_message {
  907. my ($text) = @_;
  908. #print "show_message\n";
  909. &send_lcd("widget_set message text 1 1 {$text}");
  910. &raise_screen("message",16,5,224);
  911. }
  912.  
  913. ## CD-ROM drive functions #################################################
  914.  
  915. sub open_tray {
  916. system("eject $cd_device");
  917. }
  918.  
  919. sub close_tray {
  920. if ($DEBUG) {
  921. print STDERR "3 seconds to close the tray...\n";
  922. sleep 3;
  923. }
  924. else {
  925. system("eject -t $cd_device");
  926. }
  927. }
  928.  
  929. sub mount {
  930. if (open(CDROM,"$cd_device")) {
  931. close (CDROM);
  932. &display_title("Mounting...");
  933. return system("mount $cd_device") << 8;
  934. }
  935. else {
  936. return -1;
  937. }
  938. }
  939.  
  940. sub umount {
  941. return system("umount $cd_device") << 8;
  942. }
  943.  
  944. sub cd_is_mounted {
  945. my $r = 0;
  946. die "/proc not mounted" unless -e "/proc/mounts";
  947. open(PROC,"/proc/mounts");
  948. while(<PROC>) {
  949. s/\r?\n//;
  950. if($_ =~ $cd_device) {
  951. $r = 1;
  952. last;
  953. }
  954. }
  955. close(PROC);
  956. return $r;
  957. }
  958.  
  959. ## Acting Function ########################################################
  960.  
  961. sub act {
  962. my $t = shift(@_);
  963. my ($act,$params) = ($t =~ /^(\S+)\s*(.*)/);
  964.  
  965. #print "$act\n";
  966. if ($act eq "FILELIST_UP") {
  967. return if $cd_ready == 0;
  968. if ($files{pos} > 0) {
  969. if ($filelist_cursor_pos > $filelist_cursor_pos_min) {
  970. $filelist_cursor_pos--;
  971. }
  972. $files{pos}--;
  973. &show_files();
  974. }
  975. }
  976. elsif ($act eq "PLAYLIST_UP") {
  977. return if $cd_ready == 0;
  978. if ($playlist_edit_pos > 0 && $#playlist > -1) {
  979. if ($playlist_cursor_pos > $playlist_cursor_pos_min) {
  980. $playlist_cursor_pos--;
  981. }
  982. $playlist_edit_pos--;
  983. &playlist_refresh_display();
  984. }
  985. }
  986. elsif ($act eq "FILELIST_DOWN") {
  987. return if $cd_ready == 0;
  988. if ($files{pos} < $files{nbelem}-1) {
  989. if ($filelist_cursor_pos < $filelist_cursor_pos_max) {
  990. $filelist_cursor_pos++;
  991. }
  992. $files{pos}++;
  993. &show_files();
  994. }
  995. }
  996. elsif ($act eq "PLAYLIST_DOWN") {
  997. return if $cd_ready == 0;
  998. if ($playlist_edit_pos < $#playlist && $#playlist > -1) {
  999. if ($playlist_cursor_pos < $playlist_cursor_pos_max) {
  1000. $playlist_cursor_pos++;
  1001. }
  1002. $playlist_edit_pos++;
  1003. &playlist_refresh_display();
  1004. }
  1005. }
  1006. elsif ($act eq "FILELIST_LEFT") {
  1007. return if $cd_ready == 0;
  1008. my $p = $files{root};
  1009. if($p ne $cd_root) {
  1010. my ($np) = ($p=~/^(.*)\/[^\/]+\/?$/);
  1011. $files{pos} = pop(@dir_pos_history);
  1012. &load_file_list($np);
  1013. }
  1014. }
  1015. elsif ($act eq "PLAYLIST_LEFT") {
  1016. return if $cd_ready == 0;
  1017. if($playlist_edit_pos > 0) {
  1018. splice @playlist, $playlist_edit_pos-1, 2, ($playlist[$playlist_edit_pos], $playlist[$playlist_edit_pos-1]);
  1019. if ($playlist_pos == $playlist_edit_pos) {
  1020. $playlist_pos--;
  1021. }
  1022. elsif ($playlist_pos == $playlist_edit_pos - 1) {
  1023. $playlist_pos++;
  1024. }
  1025. if ($playlist_cursor_pos > $playlist_cursor_pos_min) {
  1026. $playlist_cursor_pos--;
  1027. }
  1028. $playlist_edit_pos--;
  1029. &playlist_refresh_display();
  1030. }
  1031. }
  1032. elsif ($act eq "FILELIST_RIGHT") {
  1033. return if $cd_ready == 0;
  1034. my $f = $files{list}->[$files{pos}];
  1035. if(-d $f) {
  1036. push (@dir_pos_history, $files{pos});
  1037. $files{pos} = 0;
  1038. &load_file_list($f);
  1039. }
  1040. }
  1041. elsif ($act eq "PLAYLIST_RIGHT") {
  1042. return if $cd_ready == 0;
  1043. if($playlist_edit_pos < $#playlist) {
  1044. splice @playlist, $playlist_edit_pos, 2, ($playlist[$playlist_edit_pos+1], $playlist[$playlist_edit_pos]);
  1045. if ($playlist_pos == $playlist_edit_pos) {
  1046. $playlist_pos++;
  1047. }
  1048. elsif ($playlist_pos == $playlist_edit_pos + 1) {
  1049. $playlist_pos--;
  1050. }
  1051. if ($playlist_cursor_pos < $playlist_cursor_pos_max) {
  1052. $playlist_cursor_pos++;
  1053. }
  1054. $playlist_edit_pos++;
  1055. &playlist_refresh_display();
  1056. }
  1057. }
  1058. elsif ($act eq "FILELIST_ADD") {
  1059. return if $cd_ready == 0;
  1060. return unless exists $files{root};
  1061. my $f = $files{list}->[$files{pos}];
  1062. #print "f = $f\n";
  1063. if(-f $f) {
  1064. &playlist_add(($f));
  1065. }
  1066. elsif(-d $f) {
  1067. my @dircontent = sort &find(-1, $f);
  1068. #print "adding :\n ".join("\n",@dircontent)."\n";
  1069. &playlist_add(@dircontent);
  1070. }
  1071. }
  1072. elsif ($act eq "PLAYLIST_PLAY") {
  1073. return if $cd_ready == 0;
  1074. if($#playlist > -1) {
  1075. $playlist_pos = $playlist_edit_pos;
  1076. &playlist_refresh_display();
  1077. &dmms_stop();
  1078. sleep(1);
  1079. &dmms_play();
  1080. }
  1081. }
  1082. elsif ($act eq "PLAYLIST_DEL") {
  1083. return if $cd_ready == 0;
  1084. return if $#playlist == -1;
  1085. my $restart_play = 0;
  1086. if($playlist_pos == $playlist_edit_pos && $is_playing == 1) {
  1087. &dmms_stop();
  1088. $restart_play = 1;
  1089. }
  1090. &playlist_delete($playlist_edit_pos);
  1091. $playlist_pos = $#playlist if $#playlist < $playlist_pos;
  1092. if($restart_play == 1) {
  1093. sleep(1);
  1094. &dmms_play();
  1095. }
  1096. else {
  1097. if ($#playlist > -1) {
  1098. &display_title(&playlist_get_title($playlist_pos));
  1099. &display_album(&playlist_get_album($playlist_pos));
  1100. &display_author(&playlist_get_author($playlist_pos));
  1101. }
  1102. else {
  1103. #&display_title("Empty playlist");
  1104. &display_title("CD: $cd_volume_label");
  1105. &display_album(" ");
  1106. &display_author(" ");
  1107. }
  1108. }
  1109. }
  1110. elsif ($act eq "PLAYLIST_CLEAR") {
  1111. return if $cd_ready == 0;
  1112. &dmms_stop();
  1113. &playlist_clear();
  1114. $display_mode = "filelist";
  1115. &lower_screen("playlist",192);
  1116. &raise_screen("filelist",64,"noauto");
  1117. }
  1118. elsif ($act eq "DISPLAY_MODE_SWITCH") {
  1119. ## filelist -> info -> playlist
  1120. ## info is never raised/lowered
  1121. if($display_mode eq "filelist") {
  1122. &lower_screen("filelist",192);
  1123. $display_mode = "info";
  1124. }
  1125. elsif($display_mode eq "info") {
  1126. $display_mode = "playlist";
  1127. &playlist_refresh_display();
  1128. &raise_screen("playlist",64,"noauto");
  1129.  
  1130. }
  1131. elsif($display_mode eq "playlist") {
  1132. &lower_screen("playlist",192);
  1133. &raise_screen("filelist",64,"noauto");
  1134. $display_mode = "filelist";
  1135. }
  1136. }
  1137. elsif ($act eq "DISPLAY_TEXT_SWITCH") {
  1138. # switch sequence:
  1139. # title -> album -> author -> none -> title ...
  1140. return if $cd_ready == 0;
  1141. if ($info_scroll_line eq "title") {
  1142. $info_scroll_line = "album";
  1143. }
  1144. elsif ($info_scroll_line eq "album") {
  1145. $info_scroll_line = "author";
  1146. }
  1147. elsif ($info_scroll_line eq "author") {
  1148. $info_scroll_line = "none";
  1149. }
  1150. elsif ($info_scroll_line eq "none") {
  1151. $info_scroll_line = "title";
  1152. }
  1153. # &playlist_refresh_display();
  1154. &display_title(&playlist_get_title($playlist_pos));
  1155. &display_author(&playlist_get_author($playlist_pos));
  1156. &display_album(&playlist_get_album($playlist_pos));
  1157. }
  1158. elsif ($act eq "PLAYLIST_TEXT_SWITCH") {
  1159. # switch sequence:
  1160. # title -> album -> author -> full -> title ...
  1161. return if $cd_ready == 0;
  1162. if ($playlist_displayed_info eq "title") {
  1163. $playlist_displayed_info = "album";
  1164. }
  1165. elsif ($playlist_displayed_info eq "album") {
  1166. $playlist_displayed_info = "author";
  1167. }
  1168. elsif ($playlist_displayed_info eq "author") {
  1169. $playlist_displayed_info = "full";
  1170. }
  1171. elsif ($playlist_displayed_info eq "full") {
  1172. $playlist_displayed_info = "title";
  1173. }
  1174. &playlist_refresh_display();
  1175. }
  1176. elsif ($act eq "TOGGLE_MOUNT") {
  1177. my $res;
  1178. if(&cd_is_mounted() == 1) {
  1179. &dmms_stop() if $is_playing;
  1180. sleep(1);
  1181. &clear_files();
  1182. &display_title("Eject...");
  1183. &display_author(" ");
  1184. &display_album(" ");
  1185. $res = &umount();
  1186. if ($res == 0) {
  1187. $cd_ready = 0;
  1188. &playlist_clear();
  1189. &lower_screen("filelist",192);
  1190. &lower_screen("playlist",192);
  1191. %titres_txt_db = ();
  1192. $display_mode = "info";
  1193. &open_tray();
  1194. &display_title("Open");
  1195. $tray_position = "open";
  1196. }
  1197. else {
  1198. &show_message("Umount error");
  1199. &display_title("No CD");
  1200. }
  1201. }
  1202. else {
  1203. &close_tray();
  1204. $res = &mount();
  1205. if ($res == 0) {
  1206. $cd_ready = 1;
  1207. $tray_position = "closed";
  1208. $cd_volume_label = &get_cd_volume_label();
  1209. &display_title("CD: $cd_volume_label");
  1210. &display_author(" ");
  1211. &display_album(" ");
  1212. &display_info ("","",0);
  1213. &load_file_list($cd_root);
  1214. &raise_screen("filelist",64,"noauto");
  1215. $display_mode = "filelist";
  1216. }
  1217. else {
  1218. # Mount error
  1219. &display_title("No CD");
  1220. if ($tray_position eq "closed") {
  1221. &open_tray();
  1222. &display_title("Open");
  1223. &display_author(" ");
  1224. &display_album(" ");
  1225. $tray_position = "open";
  1226. }
  1227. else {
  1228. $tray_position = "closed";
  1229. }
  1230. }
  1231. }
  1232. }
  1233. elsif ($act eq "VOLUME_UP") {
  1234. &raise_screen("mixer",32,2,192);
  1235. &unmute();
  1236. foreach $c (@mixer_channels) {
  1237. &get_mixer_value();
  1238. &set_mixer_value($c,"+$params");
  1239. }
  1240. &set_bar("volbar",1,&get_mixer_value($mixer_channels[0]));
  1241. }
  1242. elsif ($act eq "VOLUME_DOWN") {
  1243. &raise_screen("mixer",32,2,192);
  1244. &unmute();
  1245. foreach $c (@mixer_channels) {
  1246. &get_mixer_value();
  1247. &set_mixer_value($c,"-$params");
  1248. }
  1249. &set_bar("volbar",1,&get_mixer_value($mixer_channels[0]));
  1250. }
  1251. elsif ($act eq "MUTE") {
  1252. &raise_screen("mixer",32,1,192);
  1253. if($volume_before_mute == -1) {
  1254. $volume_before_mute = &get_mixer_value($mixer_channels[0]);
  1255. &send_lcd("widget_set mixer volmute 5 1 {X}");
  1256. &set_mixer_value($mixer_channels[0],"0");
  1257. }
  1258. else {
  1259. &set_mixer_value($mixer_channels[0],$volume_before_mute);
  1260. &send_lcd("widget_set mixer volmute 5 1 { }");
  1261. $volume_before_mute = -1;
  1262. }
  1263. }
  1264. elsif ($act eq "TIME") {
  1265. return if $cd_ready == 0;
  1266. $time_mode = ($time_mode + 1) % 3 if $is_playing == 1;
  1267. }
  1268. elsif ($act eq "PLAY") {
  1269. return if $cd_ready == 0;
  1270. return if $#playlist == -1;
  1271. &dmms_play();
  1272. }
  1273. elsif ($act eq "STOP") {
  1274. return if $cd_ready == 0;
  1275. return if $#playlist == -1;
  1276. if ($is_playing == 1) {
  1277. &dmms_stop();
  1278. }
  1279. else {
  1280. $playlist_pos = 0;
  1281. &display_title(&playlist_get_title($playlist_pos));
  1282. &display_author(&playlist_get_author($playlist_pos));
  1283. &display_album(&playlist_get_album($playlist_pos));
  1284. &display_time("0");
  1285. &display_info("", "", 0);
  1286. }
  1287. }
  1288. elsif ($act eq "PAUSE") {
  1289. return if $cd_ready == 0;
  1290. return if $#playlist == -1;
  1291. &dmms_pause();
  1292. }
  1293. elsif ($act eq "NEXT") {
  1294. return if $cd_ready == 0;
  1295. &dmms_next();
  1296. &playlist_refresh_display();
  1297. }
  1298. elsif ($act eq "PREV") {
  1299. return if $cd_ready == 0;
  1300. &dmms_prev();
  1301. &playlist_refresh_display();
  1302. }
  1303. elsif ($act eq "FWD") {
  1304. return if $cd_ready == 0;
  1305. &dmms_fwd($params);
  1306. }
  1307. elsif ($act eq "BWD") {
  1308. return if $cd_ready == 0;
  1309. &dmms_bwd($params);
  1310. }
  1311. }
  1312.  
  1313. ## General Functions ###################################################
  1314.  
  1315. sub write_lock_file {
  1316. open(F,">$lock_file");
  1317. print F "$$";
  1318. close(F);
  1319. }
  1320.  
  1321. sub delete_lock_file {
  1322. unlink("$lock_file");
  1323. }
  1324.  
  1325. sub check_lock_file {
  1326. if(-f "$lock_file") {
  1327. open(F,"$lock_file");
  1328. my $pid = <F>;
  1329. close(F);
  1330. print STDERR "lcdmix is already running with PID $pid ($lock_file)\n";
  1331. return 0;
  1332. }
  1333. else {
  1334. return 1;
  1335. }
  1336. }
  1337.  
  1338. # Signal handler
  1339. sub bp {
  1340. &delete_lock_file();
  1341. ReadMode 0;
  1342. if (defined @_) {
  1343. die @_;
  1344. }
  1345. else {
  1346. exit;
  1347. }
  1348. }
  1349.  
  1350. # Silent signal handler
  1351. sub silent_bp {
  1352. &delete_lock_file();
  1353. ReadMode 0;
  1354. exit;
  1355. }
  1356.  
  1357. sub send_lcd {
  1358. my $s=shift(@_);
  1359. print $lcdsock "$s\n";
  1360. #print STDERR "->LCD: $s\n";
  1361. }
  1362.  
  1363. sub send_dmms {
  1364. my $s=shift(@_);
  1365. print $dmmssock "$s\n";
  1366. #print STDERR "->DMMS: $s\n";
  1367. }
  1368.  
  1369. sub get_ir_key {
  1370. my @l = split(/ /,shift(@_));
  1371. my %key;
  1372. $key{repeat} = hex($l[1]);
  1373. $key{name} = $l[2];
  1374. $key{remote} = $l[3];
  1375. return %key;
  1376. }
  1377.  
  1378. sub get_cd_volume_label {
  1379. my $cd_title;
  1380.  
  1381. open(CD,$cd_device);
  1382. seek(CD,32808,0);
  1383. read(CD,$cd_title,32);
  1384. close(CD);
  1385. $cd_title =~ s/ +$//;
  1386. return $cd_title;
  1387. }
  1388.  
  1389. #########################################################################
  1390. ## Main #################################################################
  1391. #########################################################################
  1392.  
  1393. exit if &check_lock_file() == 0;
  1394. &write_lock_file();
  1395.  
  1396. # Connect to the server...
  1397. $lcdsock = IO::Socket::INET->new(
  1398. Proto => "tcp",
  1399. PeerAddr => "localhost",
  1400. PeerPort => "13666",
  1401. )
  1402. || die "Cannot connect to LCDproc port\n";
  1403.  
  1404. # Make sure our messages get there right away
  1405. $lcdsock->autoflush(1);
  1406.  
  1407. sleep 1; # Give server plenty of time to notice us...
  1408. &send_lcd("hello");
  1409.  
  1410. $irsock = IO::Socket::UNIX->new(
  1411. Type => SOCK_STREAM,
  1412. Peer => "/dev/lircd",
  1413. )
  1414. || die "Cannot connect to /dev/lircd\n";
  1415.  
  1416. $dmmssock = IO::Socket::UNIX->new(
  1417. Type => SOCK_STREAM,
  1418. Peer => "/tmp/dmms",
  1419. )
  1420. || die "Cannot connect to /tmp/dmms\n";
  1421.  
  1422. # Turn off blocking mode...
  1423. fcntl($lcdsock, F_SETFL, O_NONBLOCK);
  1424. fcntl($irsock, F_SETFL, O_NONBLOCK);
  1425. fcntl($dmmssock, F_SETFL, O_NONBLOCK);
  1426.  
  1427. # Set initial mixer values
  1428. &mixer_init() if $DEBUG == 0;
  1429.  
  1430. &send_dmms("PLAY $startup_sound") if -e $startup_sound;
  1431.  
  1432. ReadMode 4;
  1433.  
  1434. while(1) {
  1435.  
  1436. if ($async_event_processed == 1 && $#async_queue > -1) {
  1437. &{$async_queue[0]->{act}}($async_queue[0]->{data});
  1438. $async_event_processed = 0;
  1439. }
  1440.  
  1441. while(defined ($lcdline=<$lcdsock>)) {
  1442. $lcdline=~s/\r?\n//;
  1443. #print STDERR "LCD: $lcdline\n";
  1444. if($lcdline =~ /^connect/) {
  1445. ($lcd_width,$lcd_height,$cell_width,$cell_height) =
  1446. ($lcdline=~/wid (\d+) hgt (\d+) cellwid (\d+) cellhgt (\d+)/);
  1447. $filelist_first_line = $lcd_height > 1 ? 2 : 1;
  1448. $filelist_cursor_pos_min = 0;
  1449. $filelist_cursor_pos_max = $lcd_height - $filelist_first_line;
  1450. $filelist_cursor_pos = $filelist_cursor_pos_min;
  1451. $playlist_first_line = $lcd_height > 1 ? 2 : 1;
  1452. $playlist_cursor_pos_min = 0;
  1453. $playlist_cursor_pos_max = $lcd_height - $playlist_first_line;
  1454. $playlist_cursor_pos = $playlist_cursor_pos_min;
  1455. # Set up some screen widgets...
  1456. &send_lcd("client_set name lcdplaylist");
  1457.  
  1458. &send_lcd("screen_add info");
  1459. &send_lcd("screen_set info name info heartbeat off priority 128");
  1460. &send_lcd("widget_add info time string");
  1461. &send_lcd("widget_add info pl_length string");
  1462. &send_lcd("widget_add info length string");
  1463. &send_lcd("widget_add info status string");
  1464. &send_lcd("widget_add info pos string");
  1465. &send_lcd("widget_add info title scroller");
  1466. &send_lcd("widget_add info album scroller");
  1467. &send_lcd("widget_add info author scroller");
  1468. &send_lcd("widget_add info info string");
  1469. &display_title("No CD");
  1470. &display_status("STOP");
  1471. &display_time("0");
  1472.  
  1473. &send_lcd("screen_add filelist");
  1474. &send_lcd("screen_set filelist name filelist heartbeat off priority 192");
  1475. &send_lcd("widget_add filelist header string");
  1476. &send_lcd("widget_add filelist line0 scroller");
  1477. &send_lcd("widget_add filelist line1 scroller");
  1478. &send_lcd("widget_add filelist line2 scroller");
  1479. &send_lcd("widget_add filelist cursor string");
  1480. &show_files();
  1481.  
  1482. &send_lcd("screen_add playlist");
  1483. &send_lcd("screen_set playlist name playlist heartbeat off priority 192");
  1484. &send_lcd("widget_add playlist header string");
  1485. &send_lcd("widget_add playlist plinfo string");
  1486. &send_lcd("widget_add playlist line0 scroller");
  1487. &send_lcd("widget_add playlist line1 scroller");
  1488. &send_lcd("widget_add playlist line2 scroller");
  1489. &send_lcd("widget_add playlist editcursor string");
  1490. &send_lcd("widget_add playlist playcursor string");
  1491. &send_lcd("widget_set playlist header 1 1 {PL:}");
  1492.  
  1493. &send_lcd("screen_add mixer");
  1494. &send_lcd("screen_set mixer name mixer heartbeat off priority 192");
  1495. &send_lcd("widget_add mixer vollbl string");
  1496. &send_lcd("widget_set mixer vollbl 1 1 {Vol}");
  1497. &send_lcd("widget_add mixer volmute string");
  1498. &send_lcd("widget_add mixer volbar hbar");
  1499. $old_barwidth{volbar}=0;
  1500. &set_bar("volbar",1,&get_mixer_value($mixer_channels[0]),1);
  1501.  
  1502. &send_lcd("screen_add message");
  1503. &send_lcd("screen_set message name message heartbeat off priority 224");
  1504. &send_lcd("widget_add message text string");
  1505. }
  1506. elsif($lcdline =~ /^success/) {
  1507. #print STDERR "LCD: $lcdline\n";
  1508. }
  1509. elsif($lcdline =~ /^listen|^ignore/) {
  1510. #print STDERR "LCD: $lcdline\n";
  1511. }
  1512. else {
  1513. print STDERR "LCD: $lcdline\n";
  1514. }
  1515. }
  1516.  
  1517. while(defined ($irline=<$irsock>)) {
  1518. $irline=~s/\r?\n//;
  1519. my %k=&get_ir_key($irline);
  1520. #print STDERR "IR: $k{name}\n";
  1521. if(exists $ir_action{$k{name}}->{$display_mode}) {
  1522. if(exists $ir_action{$k{name}}->{$display_mode}->{"repeat"}) {
  1523. &act($ir_action{$k{name}}->{$display_mode}->{"action"}) if $k{repeat} % $ir_action{$k{name}}->{$display_mode}->{"repeat"} == 0;
  1524. }
  1525. else {
  1526. &act($ir_action{$k{name}}->{$display_mode}->{"action"}) if $k{repeat} == 0;
  1527. }
  1528. }
  1529. }
  1530.  
  1531. while (defined ($key = ReadKey(-1))) {
  1532. #print "$key\n";
  1533. if ($key eq "q") {
  1534. &dmms_stop() if $is_playing == 1;
  1535. close($dmmssock);
  1536. close($irsock);
  1537. close($lcdsock);
  1538. &silent_bp();
  1539. exit;
  1540. }
  1541. if(exists $kbd_action{$key}->{$display_mode}) {
  1542. #print "$key\n";
  1543. #print "Doing ".$kbd_action{$key}->{$display_mode}->{"action"}."\n";
  1544. &act($kbd_action{$key}->{$display_mode}->{"action"});
  1545. }
  1546. }
  1547.  
  1548. while(defined ($dmmsline=<$dmmssock>)) {
  1549. $dmmsline=~s/\r?\n//;
  1550. #print STDERR "dmms: $dmmsline\n";
  1551. if ($dmmsline =~ /^PLAY/) {
  1552. &display_status("PLAY");
  1553. $is_playing = 1;
  1554. $got_info = 0;
  1555. }
  1556. elsif ($dmmsline =~ /^STOP$/) {
  1557. &display_status("STOP");
  1558. $is_playing = 0;
  1559. $is_paused = 0;
  1560. &display_time("0");
  1561. }
  1562. elsif ($dmmsline =~ /^STOPPED$/) {
  1563. if ($playlist_pos == $#playlist) {
  1564. &dmms_stop();
  1565. &display_status("STOP");
  1566. $is_playing = 0;
  1567. $is_paused = 0;
  1568. &display_time("0");
  1569. }
  1570. else {
  1571. if ($got_info == 1) {
  1572. &dmms_next();
  1573. }
  1574. else {
  1575. # File is probably not handled by dmms or *very* short -> discard
  1576. &playlist_delete($playlist_pos);
  1577. ## Replay, same pos, but actually file after the discarded one
  1578. &dmms_play();
  1579. }
  1580. }
  1581. &playlist_refresh_display();
  1582. }
  1583. elsif ($dmmsline =~ /^PAUSE/) {
  1584. if ($is_playing == 1) {
  1585. if ($is_paused == 0) {
  1586. &display_status("PAUSE");
  1587. }
  1588. else {
  1589. &display_status("PLAY");
  1590. }
  1591. $is_paused = ! $is_paused;
  1592. }
  1593. }
  1594. elsif ($dmmsline =~ /^TIME/) {
  1595. my ($t) = ($dmmsline =~ /^TIME (.*)$/);
  1596. &display_time($t);
  1597. }
  1598. elsif ($dmmsline =~ /^INFO/) {
  1599. my ($author, $album, $title);
  1600. my ($data, $length, $rate, $freq, $nch) =
  1601. ($dmmsline =~ /^INFO ([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)$/);
  1602. if ($data =~ /\|/) {
  1603. ($author, $album, $title) = split(/\|/, $data);
  1604. }
  1605. else {
  1606. $title = $data;
  1607. $author = "";
  1608. $album = "";
  1609. }
  1610. if ($#playlist >= 0) {
  1611. &playlist_get_info_cb($playlist_pos, $title, $author, $album, $length);
  1612. &display_title(&playlist_get_title($playlist_pos));
  1613. &display_author(&playlist_get_author($playlist_pos));
  1614. &display_album(&playlist_get_album($playlist_pos));
  1615. &display_time("0") if $got_info == 0;
  1616. &display_info($rate, $freq, $nch);
  1617. $got_info = 1;
  1618. }
  1619. }
  1620. elsif ($dmmsline =~ /^TITLE/) {
  1621. my ($author, $album, $title);
  1622. my ($length, $data) = ($dmmsline =~ /^TITLE ([-\d.]+) (.*)/);
  1623. if ($data =~ /\|/) {
  1624. ($author, $album, $title) = split(/\|/, $data);
  1625. }
  1626. else {
  1627. $title = $data;
  1628. $author = "";
  1629. $album = "";
  1630. }
  1631. my $ev = shift(@async_queue);
  1632. &{$ev->{cb}}($ev->{data}, $title, $author, $album, $length);
  1633. $async_event_processed = 1;
  1634. }
  1635. elsif ($dmmsline =~ /^HELLO/) {
  1636. #print STDERR "dmms: $dmmsline\n";
  1637. }
  1638. elsif ($dmmsline =~ /^ERR|^UNKN/) {
  1639. #print STDERR "dmms: $dmmsline\n";
  1640. }
  1641. }
  1642.  
  1643. foreach $screen (keys(%reset_priority_time)) {
  1644. if(time() > $reset_priority_time{$screen} && $reset_priority_time{$screen} != 0) {
  1645. &lower_screen($screen,$reset_priority_value{$screen});
  1646. }
  1647. }
  1648. select(undef,undef,undef,0.1);
  1649. }
  1650.  
  1651. ReadMode 0;
  1652.