#!/usr/bin/perl -w # NOTE: This is obsolete! Mplayer since 1.0_rc2 has a scaletempo audio filter. # Play media at different speeds without changing audio pitch. # # Usage: fastplay # eg: fastplay 1.6 clip.avi # # Requires that your system have /dev/fd/# file descriptor accessors # # XXX This is a bloody mess. It should be re-written in scsh. # # Pipe diagram: # # mplayer1 --> --> sox1 --> soundstretch --> sox2 --> /dev/dsp # | ^ # | : (rate information) # v : # mplayer2 --> # # mplayer1 reads the media file, displays the video, and dumps the # audio into this crazy pipeline # mplayer2 just tells us what the audio samplerate is. # sox1 back-converts the audio to the correct rate, undoing the # effects of mplayer1's -speed # soundstretch does the sound stretching # sox2 dumps the audio into the sound device use POSIX qw(pipe); $sounddevice = "/dev/dsp"; $syncmagic = 9000; $speed = shift @ARGV; $delay = 1 / $speed; ($READFROMMPLAYER1,$MPLAYER1WRITES) = pipe; if (fork) { POSIX::close $READFROMMPLAYER1; # Launch mplayer exec "mplayer", "-autosync", $syncmagic, "-delay", $delay, "-speed", $speed, "-ao", "pcm:waveheader:file=/dev/fd/$MPLAYER1WRITES", @ARGV; ; die "exec failed"; } else { POSIX::close $MPLAYER1WRITES; # Split the stream. Send the first block to mplayer2 to extract # the audio sample rate from it. Send the complete stream to sox1. ($SOX1READS, $WRITETOSOX1) = pipe; $blocksize = 4096; open READFROMMPLAYER1, "<&$READFROMMPLAYER1"; sysread READFROMMPLAYER1,$buf,$blocksize; if (fork) { POSIX::close $SOX1READS; # Pass the stream through unmodified open WRITETOSOX1, ">&$WRITETOSOX1"; syswrite WRITETOSOX1,$buf; while (sysread READFROMMPLAYER1,$buf,$blocksize) { syswrite WRITETOSOX1,$buf; } close (READFROMMPLAYER1); close (WRITETOSOX1); POSIX::close $READFROMMPLAYER1; POSIX::close $WRITETOSOX1; exit; } else { POSIX::close $WRITETOSOX1; # Duplicate the wav header and give it to another mplayer. (Is # there a less heavy-weight program that could be used here?) ($MPLAYER2READS, $WRITETOMPLAYER2) = pipe; ($READFROMMPLAYER2, $MPLAYER2WRITES) = pipe; if (fork) { POSIX::close $MPLAYER2READS; POSIX::close $READFROMMPLAYER2; POSIX::close $MPLAYER2WRITES; # Write the first chunk to mplayer open WRITETOMPLAYER2, ">&$WRITETOMPLAYER2"; syswrite WRITETOMPLAYER2,$buf; close WRITETOMPLAYER2; POSIX::close $WRITETOMPLAYER2; exit; } elsif (fork) { POSIX::close $WRITETOMPLAYER2; POSIX::close $READFROMMPLAYER2; # Launch mplayer open STDOUT, ">&$MPLAYER2WRITES"; open STDERR, ">&$MPLAYER2WRITES"; exec "mplayer -demuxer audio -ao null /dev/fd/$MPLAYER2READS"; die "exec failed"; } else { POSIX::close $MPLAYER2READS; POSIX::close $WRITETOMPLAYER2; POSIX::close $MPLAYER2WRITES; # Parse mplayer's output open READFROMMPLAYER2, "<&$READFROMMPLAYER2"; while () { if (/^AUDIO: ([0-9]+) Hz/) { $wrongsamplerate = $1; $rightsamplerate = $wrongsamplerate / $speed; close READFROMMPLAYER2; POSIX::close $READFROMMPLAYER2; # Found the sample rate. Launch # 1) sox to change the rate in the wav header # 2) soundstretch to do the stretching # 3) Another sox to shunt it to the output device ($SOUNDSTRETCHREADSFROMSOX1,$SOX1WRITESTOSOUNDSTRETCH) = pipe; ($SOX2READSFROMSOUNDSTRETCH,$SOUNDSTRETCHWRITESTOSOX2) = pipe; if (fork) { POSIX::close $SOUNDSTRETCHREADSFROMSOX1; POSIX::close $SOX2READSFROMSOUNDSTRETCH; POSIX::close $SOUNDSTRETCHWRITESTOSOX2; exec "sox -t wav -r $rightsamplerate /dev/fd/$SOX1READS -t wav /dev/fd/$SOX1WRITESTOSOUNDSTRETCH"; die "exec failed"; } elsif (fork) { POSIX::close $SOX1WRITESTOSOUNDSTRETCH; POSIX::close $SOX2READSFROMSOUNDSTRETCH; exec "soundstretch /dev/fd/$SOUNDSTRETCHREADSFROMSOX1 /dev/fd/$SOUNDSTRETCHWRITESTOSOX2 -tempo=".(($speed - 1) * 100); die "exec failed"; } else { POSIX::close $SOUNDSTRETCHREADSFROMSOX1; POSIX::close $SOX1WRITESTOSOUNDSTRETCH; POSIX::close $SOUNDSTRETCHWRITESTOSOX2; exec "sox -t wav /dev/fd/$SOX2READSFROMSOUNDSTRETCH -t ossdsp -w -s $sounddevice"; die "exec failed"; } } } die "MPlayer output did not contain the magic string"; } } }