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