http://qs321.pair.com?node_id=32270

   1: #!/usr/bin/perl -w
   2: # I have no idea if it will display ok on unusual term size.
   3: # There are a few functions not implemented yet. Playlists
   4: # can not be saved/loaded, and songs cannot me added. Instead
   5: # cursebox will recursively parse the dir it is started in
   6: # and find all *.mp3 files. It will consider them to be mp3
   7: # files no matter what. It will not follow sym links.
   8: # There are no safeguards despite forks, etc, so it is not
   9: # gauranteed to work properly (although it seems to). It is
  10: # after all v0.9.
  11: # cursebox requires the following:
  12: # -- ncurses
  13: # -- CPAN Curses module
  14: # -- A term capable of color
  15: # Sorry for the ugly and messy code... =)
  16: 
  17: use Curses;
  18: $SIG{CHLD} = 'IGNORE';
  19: 
  20: # CURSES INIT
  21: initscr;
  22: start_color;
  23: noecho;
  24: halfdelay(1);
  25: nl();
  26: intrflush(stdscr, 0);
  27: keypad(stdscr, 1);
  28: init_pair(1,1,0);
  29: init_pair(2,2,0);
  30: init_pair(3,3,0);
  31: init_pair(5,5,0);
  32: 
  33: # CURSEBOX INIT
  34: $mark = -1;
  35: $centersongnr = 0;
  36: $randplay = 0;
  37: $pid=0;
  38: $playingon = 0;
  39: $playno = 0;
  40: $mode = "play";        # play, help, add, ver
  41: $pwd=`pwd`;
  42: chop($pwd);
  43: &parsedir($pwd);
  44: &refreshmid;
  45: &setup;
  46: 
  47: # MAIN
  48: &loop;
  49: exit(1);
  50: 
  51: sub parsedir {
  52:  my $pwd=$_[0];
  53:  chdir($pwd);
  54:  opendir(DIR, $pwd);
  55:  my @files=readdir(DIR);
  56:  closedir(DIR);
  57:  foreach (@files) {
  58:   unless ( $_ =~ m/^\.$/ || $_ =~ m/^\.\.$/ ) {
  59:    if ( -d $_ && !-l $_ ) {
  60:     $npwd="$pwd/$_";
  61:     parsedir($npwd);
  62:     chdir("..");
  63:    } else {
  64:     if ( $_ =~ m/\.mp3$/i ) {
  65:      $mp3s[$#mp3s+1]="$pwd/$_";
  66:     }
  67:    }
  68:   }
  69:  }
  70: }
  71: 
  72: sub loop {
  73:  while ( 1 ) {
  74:   $char = getch();
  75:   unless ($char eq "-1") {
  76:    &interpret;
  77:   }
  78:   unless ( kill 0 => $pid ) {
  79:    if ($playingon) {
  80:     if ( $randplay ) {
  81:      &playrand;
  82:     } else {
  83:      &skip;
  84:     }
  85:    }
  86:   }
  87:  }
  88: }
  89: 
  90: sub interpret {
  91:  SWITCH: {
  92:   if ( $mode eq "help" ) { &interprethelp ; last SWITCH;}
  93:   if ( $mode eq "play" ) { &interpretplay ; last SWITCH;}
  94:   if ( $mode eq "cred"  ) { &interpretcred  ; last SWITCH;}
  95:  }
  96: }
  97: 
  98: sub interpretcred {
  99:  SWITCH: {
 100:   if ( $char eq "c" ) { &setmode("play") ; last SWITCH; }
 101:   &invalid;
 102:  }
 103: }
 104: 
 105: sub interprethelp {
 106:  SWITCH: {
 107:   if ( $char eq "h" ) { &setmode("play") ; last SWITCH; }
 108:   &invalid;
 109:  }
 110: }
 111: 
 112: sub interpretplay {
 113:  SWITCH: {
 114:   if ( $char eq "n" ) { &skip ; last SWITCH; }
 115:   if ( $char eq "m" ) { &movesong ; last SWITCH; }
 116:   if ( $char eq "b" ) { &back ; last SWITCH; }
 117:   if ( $char eq "r" ) { &rand ; last SWITCH; }
 118:   if ( $char eq "c" ) { &setmode("cred") ; last SWITCH; }
 119:   if ( $char eq "s" ) { &stop ; last SWITCH; }
 120:   if ( $char eq "p" ) { &play ; last SWITCH; }
 121:   if ( $char eq "q" ) { &quit ; last SWITCH; }
 122:   if ( $char eq "d" ) { &delsong ; last SWITCH; }
 123:   if ( $char eq "h" ) { &setmode("help") ; last SWITCH; }
 124:   if ( $char eq "g" ) { &gotocent ; last SWITCH; }
 125:   if ( $char eq "259" ) { &up ; last SWITCH; }
 126:   if ( $char eq "258" ) { &down ; last SWITCH; }
 127:   if ( $char eq "\n" ) { &goto ; last SWITCH; }
 128:   &invalid;
 129:  }
 130: }
 131: 
 132: sub setmode {
 133:  $mode=$_[0];
 134:  &refreshmid;
 135: }
 136: 
 137: sub refreshmid {
 138:  if ($mode eq "play") {
 139:   &midplay;
 140:  }
 141:  if ($mode eq "help") {
 142:   &midhelp;
 143:  }
 144:  if ($mode eq "cred") {
 145:   &midcred;
 146:  }
 147:  if ($mode eq "add") {
 148:   &midadd;
 149:  }
 150: }
 151: 
 152: sub midplay {
 153:  if ( $#mp3s == -1 ) {
 154:   &emptylist;
 155:  } else {
 156:   $nooflines = $LINES-7;
 157:   $centerline = int($nooflines/2);
 158:   $songnumber = $centersongnr-$centerline;
 159:   while ($songnumber < 0) {
 160:    $songnumber = $songnumber + $#mp3s+1;
 161:   }
 162:   for ( $i=0 ; $i<$nooflines ; $i++ ) {
 163:    if ($songnumber>$#mp3s) { $songnumber=0; }
 164:    $shownumber = $songnumber+1;
 165:    $playname = $mp3s[$songnumber];
 166:    $nr = length($playname) - ($COLS-8);
 167:    if ( $nr > 0 ) {
 168:     $playname =~ s/^.{$nr}//;
 169:     $playname =~ s/^.{3}/\.\.\./;
 170:    }
 171:    $printwidth = $COLS-3;
 172:    $sprintstring = "%-".$printwidth."s";
 173:    $printnum = sprintf("%-4s", $shownumber);
 174:    $playname = sprintf($sprintstring, " ".$printnum.$playname);
 175:    $attrm=0;$attrc=0;$attrp=0;
 176:    if ($songnumber==$mark && $mark!=-1 ) {$attrm=1;}
 177:    if ($songnumber==$playno || ($songnumber==-1 && $playno==$#mp3s)){$attrp=1;}
 178:    if ($i==$centerline) {$attrc=1;}
 179:    if ($attrm) {attron(COLOR_PAIR(1))}
 180:    if ($attrc) {attron(A_BOLD)}
 181:    if ($attrp) {attron(COLOR_PAIR(2))}
 182:    addstr((6+$i), 1, $playname);
 183:    if ($attrm) {attroff(COLOR_PAIR(1))}
 184:    if ($attrc) {attroff(A_BOLD)}
 185:    if ($attrp) {attroff(COLOR_PAIR(2))}
 186:    $songnumber++;
 187:   }
 188:   refresh;
 189:  }
 190: }
 191: 
 192: sub playsong {
 193:  if ( $#mp3s >= 0 ) {
 194:   if ( $pid == 0 ) {
 195:    if ( $playno < 0 ) {
 196:     $playno = $#mp3s;
 197:    }
 198:    if ( $playno > $#mp3s ) {
 199:     $playno = 0;
 200:    }
 201:    $playname=$mp3s[$playno];
 202:    $playnameqtd = quotemeta( $playname );
 203:    $mpgcommand="mpg123 -q $playnameqtd";
 204:    &refreshmid;
 205:    $pid = fork();
 206:    if ( $pid == 0 ) {
 207:     open(STDERR, "/dev/null");
 208:     exec($mpgcommand);
 209:     exit 1;
 210:    }
 211:   }
 212:  }
 213: }
 214: 
 215: sub setup {
 216:  attron(COLOR_PAIR(1));
 217:  box(stdscr, 0, 0);
 218:  move(5,1);
 219:  hline(0, 78);
 220:  attroff(COLOR_PAIR(1));
 221:  $sprintstring = "The Cursed JukeBox ( cursebox v0.9 )";
 222:  $nr = $COLS/2-length($sprintstring)/2;
 223:  $nr = int($nr);
 224:  attron(COLOR_PAIR(2));
 225:  addstr(0, $nr, $sprintstring );
 226:  attroff(COLOR_PAIR(2));
 227:  addstr(1, 2, "COMMANDS: (N)ext    (B)ack    (S)top    (P)lay".
 228:               "    (R)andom  (M)ark/(M)ove");
 229:  addstr(2, 2, "          (Q)quit   (D)elete  (H)elp    (C)redits (G)oto");
 230:  addstr(3, 2, "          Arrow Keys to scroll, <ENTER> to Select.");
 231:  addstr(4, 2, "RANDOM:           PLAYING:      ");
 232:  attron(COLOR_PAIR(1));
 233:  addstr(4, 12, "OFF");
 234:  addstr(4, 31, "OFF");
 235:  attroff(COLOR_PAIR(1));
 236:  refresh;
 237: }
 238: 
 239: sub emptylist {
 240:  $nooflines = $LINES-7;
 241:  $centerline = int($nooflines/2-1);
 242:  $songnumber = $centersongnr-$centerline;
 243:  $clearpatt = "%".($COLS-2)."s";
 244:  $clearline = sprintf($clearpatt, "");
 245:  for ( $i=0 ; $i<$nooflines ; $i++ ) {
 246:   addstr(($i+6),1,$clearline);
 247:  }
 248:  $sprintstring = "SONGLIST EMPTY";
 249:  $nr = int($COLS/2-length($sprintstring)/2);
 250:  addstr(($LINES/2+3), $nr, $sprintstring );
 251:  refresh;
 252: }
 253: 
 254: sub midcred {
 255:  $nooflines = $LINES-7;
 256:  $centerline = int($nooflines/2-1);
 257:  $songnumber = $centersongnr-$centerline;
 258:  $clearpatt = "%".($COLS-2)."s";
 259:  $clearline = sprintf($clearpatt, "");
 260:  for ( $i=0 ; $i<$nooflines ; $i++ ) {
 261:   addstr(($i+6),1,$clearline);
 262:  }
 263:  addstr(6,1,"The Cursed Jukebox ( cursebox v0.9 )");
 264:  addstr(8,1,"Cursebox uses:");
 265:  addstr(9,1,"  mpg123");
 266:  addstr(10,1,"  perl");
 267:  addstr(11,1,"  Curses, the perl ncurses interface ( Yes, you".
 268:              " need a color Term .)");
 269:  addstr(13,1,"Tools used in coding:");
 270:  addstr(14,1,"  vim ( VI Improved )");
 271:  addstr(15,1,"  man ( RTFM... )");
 272:  addstr(17,1,"Cursebox has been created in vga text mode on a".
 273:              " debian linux i386 machine");
 274:  addstr(18,1,"by Mats Ström. ( d99msr\@efd.lth.se )");
 275:  addstr(20,1,"A big \"hooray\" goes to www.perlmonks.org. Good".
 276:              "place to ask questions.");
 277:  addstr(22,1,"Press C to exit this screen.");
 278:  refresh;
 279: }
 280: 
 281: sub midhelp {
 282:  $nooflines = $LINES-7;
 283:  $centerline = int($nooflines/2-1);
 284:  $songnumber = $centersongnr-$centerline;
 285:  $clearpatt = "%".($COLS-2)."s";
 286:  $clearline = sprintf($clearpatt, "");
 287:  for ( $i=0 ; $i<$nooflines ; $i++ ) {
 288:   addstr(($i+6),1,$clearline);
 289:  }
 290:  addstr(6,1,"Use Up/Down arrow keys to scroll in the playlist.");
 291:  addstr(7,1,"Enter will start playing the song on the highlighted line.");
 292:  addstr(9,1,"N      Next: Play next song. If random is on, it skips to".
 293:             "a random song.");
 294:  addstr(10,1,"B      Back: Play previous song. If random is on, it skips".
 295:              "to a random song.");
 296:  addstr(11,1,"S      Stop: Stop playing.");
 297:  addstr(12,1,"P      Play: Start playing.");
 298:  addstr(13,1,"R      Random: Toggle random play on/off.");
 299:  addstr(14,1,"Q      Quit: Exit program. ( Yes, CTRL-C works as well. )");
 300:  addstr(15,1,"G      Goto: This will move your view to the playing song.");
 301:  addstr(16,1,"H      Help: Display this text.");
 302:  addstr(17,1,"C      Credits: Show credits and information about cursebox.");
 303:  addstr(18,1,"M      Mark: Pressing M once will mark a message for moving.".
 304:              "The line will");
 305:  addstr(19,1,"       turn red. Move with the arrow keys to another song".
 306:              "and press M again.");
 307:  addstr(20,1,"       The marked line will be removed and inserted at new".
 308:              "position.");
 309:  addstr(22,1,"Press H to exit help.");
 310:  refresh;
 311: }
 312: 
 313: sub playrand {
 314:  $playno=int(rand($#mp3s+1));
 315:  if ( $playno == $#mp3s+1 ) {
 316:   $playno=$#mp3s;
 317:  }
 318:  &stop;
 319:  &play;
 320: }
 321: 
 322: sub delsong {
 323:  if ( $#mp3s >= 0 ) {
 324:   splice(@mp3s,$centersongnr,1);
 325:   if ( $centersongnr == $playno ) {
 326:    $playno = $centersongnr;
 327:    if ( $playingon ) {
 328:     &stop;
 329:     &play;
 330:    }
 331:   }
 332:   if ( $centersongnr < $playno ) {
 333:    $playno--;
 334:   }
 335:   if ( $centersongnr == ($#mp3s+1) ) {
 336:    $centersongnr = 0;
 337:   }
 338:   if ( $playno > ($#mp3s+1) ) {
 339:    $playno=$#mp3s+1;
 340:   }
 341:   &refreshmid;
 342:  } else {
 343:   &invalid;
 344:  }
 345: }
 346: 
 347: sub up {
 348:  $centersongnr--;
 349:  if ( $centersongnr < 0 ) {
 350:   $centersongnr = $#mp3s;
 351:  }
 352:  &refreshmid;
 353: }
 354: 
 355: sub down {
 356:  $centersongnr++;
 357:  if ( $centersongnr > $#mp3s ) {
 358:   $centersongnr = 0;
 359:  }
 360:  &refreshmid;
 361: }
 362: 
 363: sub movesong {
 364:  if ( $#mp3s >= 0 ) {
 365:   if ( $mark == -1 ) {
 366:    $mark = $centersongnr;
 367:   } else {
 368:    $tmp = $mp3s[$mark];
 369:    splice(@mp3s,$mark,1);
 370:    splice(@mp3s,$centersongnr,0,$tmp);
 371:    SWITCH: {
 372:     if ( $playno == $centersongnr ) { $playno = $mark; ; last SWITCH; }
 373:     if ( $playno == $mark ) { $playno = $centersongnr; ; last SWITCH; }
 374:     $playno=$playno;
 375:    }
 376:    $mark=-1;
 377:   }
 378:   &refreshmid;
 379:  } else {
 380:   &invalid;
 381:  }
 382: }
 383: 
 384: sub goto {
 385:  if ( $#mp3s >= 0 ) {
 386:   &play;
 387:   $playno = $centersongnr;
 388:   if ( kill 0 => $pid ) {
 389:    if ( $pid != 0 ) {
 390:     &killsong;
 391:    }
 392:    &playsong;
 393:   }
 394:  } else {
 395:   &invalid;
 396:  }
 397: }
 398: 
 399: sub gotocent {
 400:  if ( $#mp3s >= 0 ) {
 401:   $centersongnr = $playno;
 402:   &refreshmid;
 403:  }
 404: }
 405: 
 406: sub stop {
 407:  attron(COLOR_PAIR(1));
 408:  addstr(4, 31, "OFF");
 409:  attroff(COLOR_PAIR(1));
 410:  refresh;
 411:  if ($pid != 0) {
 412:   &killsong;
 413:  }
 414:  $playingon = 0;
 415: }
 416: 
 417: sub play {
 418:  if ( $#mp3s >= 0 ) {
 419:   attron(COLOR_PAIR(2));
 420:   addstr(4, 31, "ON ");
 421:   attroff(COLOR_PAIR(2));
 422:   refresh;
 423:   unless ($playingon) {
 424:    &playsong;
 425:   }
 426:   $playingon = 1;
 427:  } else {
 428:   &invalid;
 429:  }
 430: }
 431: 
 432: sub skip {
 433:  if ( $#mp3s >= 0 ) {
 434:   &play;
 435:   unless ( $randplay ) {
 436:    $playno++;
 437:    unless ( $pid == 0 ){
 438:     &killsong;
 439:    }
 440:    &playsong;
 441:   } else {
 442:    unless ( $pid == 0 ){
 443:     &killsong;
 444:    }
 445:    &playrand;
 446:   }
 447:  } else {
 448:   &invalid;
 449:  }
 450: }
 451: 
 452: sub back {
 453:  if ( $#mp3s >= 0 ) {
 454:   &play;
 455:   unless ( $randplay ) {
 456:    $playno--;
 457:    unless ( $pid == 0 ) {
 458:     &killsong;
 459:    }
 460:    &playsong;
 461:   } else {
 462:    unless ( $pid == 0 ){
 463:     &killsong;
 464:    }
 465:    &playrand;
 466:   }
 467:  } else {
 468:   &invalid;
 469:  }
 470: }
 471: 
 472: sub rand {
 473:  if ( $randplay ) {
 474:   attron(COLOR_PAIR(1));
 475:   addstr(4, 12, "OFF");   refresh;
 476:   attroff(COLOR_PAIR(1));
 477:   $randplay = 0;
 478:  } else {
 479:   attron(COLOR_PAIR(2));
 480:   addstr(4, 12, "ON ");
 481:   attroff(COLOR_PAIR(2));
 482:   refresh;
 483:   $randplay = 1;
 484:  }
 485: }
 486: 
 487: sub killsong {
 488:  unless ( $pid == 0 ){
 489:   while ( kill 0 => $pid ) {
 490:    kill TERM, $pid;
 491:   }
 492:  }
 493:  $pid=0;
 494: }
 495: 
 496: sub quit {
 497:  &killsong;
 498:  endwin;
 499:  exit(1);
 500: }
 501: 
 502: sub invalid {
 503:  beep;
 504:  refresh;
 505: }

Replies are listed 'Best First'.
RE: cursebox v0.9 - switch{} coding style
by Corion (Patriarch) on Sep 13, 2000 at 22:47 UTC

    Update: Please use strict; !

    Though I have not used this code, I have one comment to make in the spirit of TIMTOWTDI (hopefully a good comment) - I find the simulated switch statement to map a keypress to an action rather clumsy, as it does not really allow to easily add other actions later or add other actions dynamically.

    In such situations, I prefer to use a hash of code references, like this :

    my %actions = ( "n" => \&skip, "m" => \&movesong, # ... rest ommited ... "\n" => \&goto, );
    and the routine interpretplay() would then look like this (sadly, untested) :
    sub interpretplay { if (exists $actions{$char}) { &$actions{$char}; } else { &invalid; }; };
    or, if the whole state machine would be changed to use hashes, one could even use a single interpret() method that would simply look at the $current_state hash reference to find the action to be executed.

    Dynamically loaded actions would then simply modify the %actions hash with their entry.

      This is sample code for odie to look at, and will probably be changing. Please don't vote on this node.
      #!/usr/local/bin/perl -w use strict; { my %hash = ('a' => {-func => \&sub_one, -parm => 'subone'}, 'b' => {-func => \&sub_two, -parm => 'two,two_a,two_ +b'}, 'c' => {-func => \&sub_three, -parm => ''}, ); &{$hash {'a'}->{-func}} (split ',', $hash {'a'}->{-parm}); &{$hash {'b'}->{-func}} (split ',', $hash {'b'}->{-parm}); &{$hash {'c'}->{-func}} (split ',', $hash {'c'}->{-parm}); } sub sub_one { print "sub_one got ", scalar @_, " parameters\n"; print "sub_one parameters: $_\n" foreach (@_); } sub sub_two { print "sub_two got ", scalar @_, " parameters\n"; print "sub_two parameters: $_\n" foreach (@_); } sub sub_three { print "sub_three got ", scalar @_, " parameters\n"; print "sub_three parameters: $_\n" foreach (@_); }
      --Chris

      e-mail jcwren
      Well... As I said, it is version 0.9 and the code is a complete mess. The whole thing started as a sinple recursive hack that played every mp3 file found, using mpg123. I honestly didn't expect to continue building it... =)
      I will naturally make the code a lot more efficient, and less spammy, and use strict. This code is still very much in a state of "hack". Version 1.0 will be posted when complete.