| 1 | #!/usr/bin/perl -w | 
|---|
| 2 | use strict; | 
|---|
| 3 |  | 
|---|
| 4 | use IPC::Open2; | 
|---|
| 5 | use IO::Prompt; | 
|---|
| 6 | use Term::ReadKey; | 
|---|
| 7 |  | 
|---|
| 8 | my $FLAC_FILE = shift; | 
|---|
| 9 | my $TRACK = shift || 1; | 
|---|
| 10 |  | 
|---|
| 11 | $|++; | 
|---|
| 12 |  | 
|---|
| 13 | # get the track start positions from the embedded cuesheet | 
|---|
| 14 | open my $CUESHEET, "metaflac --export-cuesheet-to - $FLAC_FILE |"; | 
|---|
| 15 |  | 
|---|
| 16 | my @position_for_track; | 
|---|
| 17 | my $i = 1; | 
|---|
| 18 | while (<$CUESHEET>) { | 
|---|
| 19 | if (my ($m,$s,$f) = /INDEX 01 (\d\d):(\d\d):(\d\d)/) { | 
|---|
| 20 | $position_for_track[$i] = ($m * 60) + $s + ($f / 75); | 
|---|
| 21 | $i++; | 
|---|
| 22 | } | 
|---|
| 23 | } | 
|---|
| 24 |  | 
|---|
| 25 | close $CUESHEET; | 
|---|
| 26 |  | 
|---|
| 27 |  | 
|---|
| 28 | my $track_count = scalar @position_for_track - 1; | 
|---|
| 29 | print "$track_count tracks\n"; | 
|---|
| 30 |  | 
|---|
| 31 | die "There is no track $TRACK" if $TRACK < 1 or $TRACK > $track_count; | 
|---|
| 32 |  | 
|---|
| 33 |  | 
|---|
| 34 | # launch the flac123 player as a daemon | 
|---|
| 35 | my $flac123_pid = open2(my $PLAYER_OUT, my $PLAYER_IN, qw{flac123 -R}); | 
|---|
| 36 |  | 
|---|
| 37 | print "flac123 PID = $flac123_pid\n"; | 
|---|
| 38 |  | 
|---|
| 39 | my $current_track = $TRACK; | 
|---|
| 40 | my $next_track_start = $position_for_track[$current_track + 1]; | 
|---|
| 41 |  | 
|---|
| 42 | print "Track $current_track\n"; | 
|---|
| 43 |  | 
|---|
| 44 | my %commands = ( | 
|---|
| 45 | z => sub { | 
|---|
| 46 | return if $current_track <= 1; | 
|---|
| 47 | $current_track--; | 
|---|
| 48 | $next_track_start = $position_for_track[$current_track + 1]; | 
|---|
| 49 | my $position = $position_for_track[$current_track]; | 
|---|
| 50 | print $PLAYER_IN "JUMP $position\n"; | 
|---|
| 51 | print "Track $current_track\n"; | 
|---|
| 52 | }, | 
|---|
| 53 | x => sub {}, | 
|---|
| 54 | c => sub {}, | 
|---|
| 55 | v => sub {}, | 
|---|
| 56 | b => sub { | 
|---|
| 57 | return if $current_track >= $track_count; | 
|---|
| 58 | $current_track++; | 
|---|
| 59 | $next_track_start = $position_for_track[$current_track + 1]; | 
|---|
| 60 | my $position = $position_for_track[$current_track]; | 
|---|
| 61 | print $PLAYER_IN "JUMP $position\n"; | 
|---|
| 62 | print "Track $current_track\n"; | 
|---|
| 63 | }, | 
|---|
| 64 | q => sub { | 
|---|
| 65 | print $PLAYER_IN "QUIT\n"; | 
|---|
| 66 | ReadMode 'restore'; | 
|---|
| 67 | exit; | 
|---|
| 68 | }, | 
|---|
| 69 | ); | 
|---|
| 70 |  | 
|---|
| 71 |  | 
|---|
| 72 | print $PLAYER_IN "LOAD $FLAC_FILE\n"; | 
|---|
| 73 | print $PLAYER_IN "JUMP $position_for_track[$TRACK]\n"; | 
|---|
| 74 |  | 
|---|
| 75 | ReadMode 4; | 
|---|
| 76 |  | 
|---|
| 77 | while (<$PLAYER_OUT>) { | 
|---|
| 78 | # do a non-blocking read to see if the user pressed a key | 
|---|
| 79 | my $cmd = ReadKey -1; | 
|---|
| 80 | if (defined $cmd && exists $commands{$cmd}) { | 
|---|
| 81 | $commands{$cmd}->(); | 
|---|
| 82 | } | 
|---|
| 83 |  | 
|---|
| 84 | if (/\@F (\d+) (\d+) (\d+\.\d\d) (\d+\.\d\d)/) { | 
|---|
| 85 | my ($frame, $frames_left, $time, $time_left) = ($1, $2, $3, $4); | 
|---|
| 86 | if (defined $next_track_start && $time >= $next_track_start) { | 
|---|
| 87 | $current_track++; | 
|---|
| 88 | $next_track_start = $position_for_track[$current_track + 1]; | 
|---|
| 89 | print "Track $current_track\n"; | 
|---|
| 90 | } | 
|---|
| 91 |  | 
|---|
| 92 | =begin | 
|---|
| 93 |  | 
|---|
| 94 | for (my $track_number = $track_count; $track_number > 0; $track_number--) { | 
|---|
| 95 | #warn $track_number; | 
|---|
| 96 | if ($position_for_track[$track_number] <= $time) { | 
|---|
| 97 | $current_track = $track_number; | 
|---|
| 98 | #print "Track $current_track\n"; | 
|---|
| 99 | last; | 
|---|
| 100 | } | 
|---|
| 101 | } | 
|---|
| 102 |  | 
|---|
| 103 | =cut | 
|---|
| 104 |  | 
|---|
| 105 | } | 
|---|
| 106 | } | 
|---|
| 107 |  | 
|---|
| 108 | waitpid $flac123_pid, 0; | 
|---|
| 109 |  | 
|---|
| 110 | ReadMode 'restore'; | 
|---|
| 111 |  | 
|---|
| 112 | =begin flac123 -R outputs | 
|---|
| 113 |  | 
|---|
| 114 | @R FLAC123 | 
|---|
| 115 | flac123 tagline. Output at startup. | 
|---|
| 116 |  | 
|---|
| 117 | @I ID3:<a><b><c><d><e><f> | 
|---|
| 118 | Prints out the metadata information after loading the flac file. | 
|---|
| 119 | a = title (30 chars) | 
|---|
| 120 | b = artist (30 chars) | 
|---|
| 121 | c = album (30 chars) | 
|---|
| 122 | d = year (4 chars) | 
|---|
| 123 | e = comment (30 chars) | 
|---|
| 124 | f = genre (30 chars) | 
|---|
| 125 |  | 
|---|
| 126 | @I filename | 
|---|
| 127 | Prints out the filename of the flac file, minus the extension. Happens after | 
|---|
| 128 | a flac file has been loaded and there is no metadata available. | 
|---|
| 129 |  | 
|---|
| 130 | @F <current-frame> <frames-remaining> <current-time> <time-remaining> | 
|---|
| 131 | Frame decoding status updates (once per frame). | 
|---|
| 132 | Current-frame and frames-remaining are integers; current-time and | 
|---|
| 133 | time-remaining floating point numbers with two decimal places. | 
|---|
| 134 |  | 
|---|
| 135 | @P {0, 1, 2} | 
|---|
| 136 | Stop/pause status. | 
|---|
| 137 | 0 - playing has stopped. When 'STOP' is entered, or the flac file is finished. | 
|---|
| 138 | 1 - Playing is paused. Enter 'PAUSE' or 'P' to continue. | 
|---|
| 139 | 2 - Playing has begun again. | 
|---|
| 140 |  | 
|---|
| 141 | =cut | 
|---|