#! /usr/bin/perl
#########################################################################
# This Perl script is Copyright (c) 2003, Peter J Billam #
# c/o P J B Computing, www.pjb.com.au #
# #
# This script is free software; you can redistribute it and/or #
# modify it under the same terms as Perl itself. #
#########################################################################
# SCSI: cdda2wav, cdrecord, cdparanoia, idprio, mencoder, mplayer
# $ENV{CDDA_DEVICE} cdrecord -scanbus
# ATAPI: cdcontrol -f /dev/cd0c info, burncd, mkisofs
# $ENV{CDROM} /usr/sbin/pciconf -lv, atacontrol list
# MIDI: timidity, lame or toolame or twolame
#
# timidity -Ow -o sample.wav sample.mid
# normalize-audio -m *.wav
# scp *.wav theflame:/data/cd/
# ssh theflame
# su -
# cd /data/cd/
# cdrecord dev=0,0 -v -dao -pad -speed=12 -copy *.wav
# .mid to .wav to .mp3 ...
# timidity -Ow -o sample.wav sample.mid
# normalize-audio -m *.wav
# lame -h sample.wav sample.mp3
# .wav files can be played with
# sndfile-play whatever.wav or with mplayer or with play (comes with sox)
# .mpg files can be played with
# sndfile-play whatever.mpg or with mplayer or mpg123
my $BigTmp = $ENV{'BIGTMP'} || '/tmp';
use Cwd qw(chdir);
use Term::Clui;
use Term::Clui::FileSelect;
my @PATH = split (":",$ENV{PATH});
my $MidiOutPort = ();
my $aconnect = which('aconnect');
my $alsamixer = which('alsamixer');
my $aplaymidi = which('aplaymidi');
my $arecordmidi = which('arecordmidi');
my $cdda2wav = which('icedax') || which('cdda2wav');
my $cdrecord = which('cdrecord') || which('wodim');
my $dvdbackup = which('dvdbackup');
my $eject = which('eject');
my $festival = which('festival');
my $growisofs = which('growisofs');
my $lame = which('lame');
my $man = which('man');
my $mkisofs = which('mkisofs') || which ('genisoimage');
my $mediainfo = which('dvd+rw-mediainfo');
my $mpg123 = which('mpg123');
my $mplayer = which('mplayer');
my $normalize = which('normalize') || which ('normalize-audio');
my $play = which('play'); # comes with sox
my $rec = which('rec'); # comes with sox
my $sox = which('sox');
my $sndfile_play = which('sndfile-play');
my $startBristol = which('startBristol');
my $su = which('su');
my $timidity = which('timidity');
my $toolame = which('toolame') || which('twolame');
my $mp3_player = $mpg123 || $mplayer || $sndfile_play;
my $wav_player = $play || $sndfile_play || $mplayer;
while (1) {
my $task = choose('Do what ?', tasks());
exit unless $task;
if ($task eq 'Extract and Burn') {
warn "You'll need to be superuser ...\n";
system "$su root -c $0"; exit 0;
} elsif ($task eq 'burn WAV->AudioCD') { burn_wav();
} elsif ($task eq 'burn files->DataCD') { burn_files();
} elsif ($task eq 'change Directory') { changedir();
} elsif ($task eq 'configure Timidity') { configure_timidity();
} elsif ($task eq 'connect MIDIports') { connect_midi_ports();
} elsif ($task eq 'consult Manual') { man();
} elsif ($task eq 'convert MIDI->WAV') { mid2wav();
} elsif ($task eq 'convert MIDI->MP3') { mid2mp3();
} elsif ($task eq 'copy audio CD') { copy_cd();
} elsif ($task eq 'copy video DVD') { copy_dvd();
} elsif ($task eq 'decode MP3->WAV') { mp32wav();
} elsif ($task eq 'edit Makefile') { edit('Makefile');
} elsif ($task eq 'encode WAV->MP2') { wav2mp2();
} elsif ($task eq 'encode WAV->MP3') { wav2mp3();
} elsif ($task eq 'list Soundfont') { list_soundfont();
} elsif ($task eq 'play AudioCD') { play_cd();
} elsif ($task eq 'play MIDI,WAV,MP3') { play();
} elsif ($task eq 'record AudioIn->WAV') { audio2wav();
} elsif ($task eq 'record Keyboard->MIDI') { kbd2mid();
} elsif ($task eq 'rip AudioCD->WAV') { rip_wav();
} elsif ($task eq 'rip MP3CD->MP3') { rip_mp3();
} elsif ($task eq 'run a Bristol synth'){ bristol();
} elsif ($task eq 'run alsamixer') { alsamixer();
} elsif ($task eq 'run Make') { system 'make';
}
}
exit 0;
#----------------------- functionality ------------------------
sub alsamixer {
if (! $alsamixer) { sorry("you need to install alsamixer."); return; }
system $alsamixer;
}
sub bristol {
if (! $startBristol) { sorry("you need to install Bristol."); return; }
if (! open(P, "$startBristol -v -h |")) {
sorry("can't run $startBristol -v -h: $!"); return;
}
my $is_in_emulations = 0;
my %long2short = ();
while (<P>) {
if (/Emulation:/) { $is_in_emulations = 1; next; }
if (!$is_in_emulations) { next; }
if ($is_in_emulations and /Synthesiser:/) { last; }
if (/^\s+-(\w+)\s+-\s(\w.*)$/) { $long2short{$2} = $1; }
}
close P;
my $long = choose("which synth emulation ?", sort keys %long2short);
return unless $long;
my $out_file = ask("save output to wav file (return = don't save) ?");
if (! $out_file) {
system "$startBristol -alsa -$long2short{$long}";
} else {
$out_file =~ s/\.WAV$//i;
system "$startBristol -alsa -$long2short{$long} -o $out_file.raw";
if (!$sox) {
sorry("you need to install sox to convert raw to wav."); return;
}
system "$sox -c 2 -s -r 44100 -2 $out_file.raw $out_file.wav";
}
}
sub burn_wav {
if (!$cdrecord) {sorry("you need to install cdrecord or wodim."); return;}
set_cdda_device() || return;
my @files = select_file(-FPat=>'{*.wav,*.WAV}',-Path=>$ENV{PWD},-Chdir=>0);
return unless @files;
my $files = join "' '", @files;
ask("insert the C D into the drive, and press Return");
system "$cdrecord dev=0,0 -v -dao -pad -speed=12 -copy '$files'";
inform("finished burning the C D");
if ($eject) { system $eject; }
}
sub burn_files {
my $ok = 1;
if (! $mkisofs) {
sorry("you need to install mkisofs or genisoimage."); return;
}
if (!$cdrecord) {sorry("you need to install cdrecord or wodim."); return;}
if (! -e '/dev/cdrom') { sorry("can't find /dev/cdrom"); $ok = 0;
} elsif (! -w '/dev/cdrom') { sorry("can't write to /dev/cdrom"); $ok=0;
}
my $tmpfile = "$BigTmp/cd_$$";
my $tmp_dir = "$BigTmp/mnt_$$";
if (! mkdir $tmp_dir) { sorry("can't mkdir $tmp_dir: $!"); $ok=0; }
return unless $ok;
# must choose_files repeatedly to within size limit
my $max_mb = 800;
while (1) {
my $mb_so_far = `du -ms $tmp_dir`; $mb_so_far =~ s/\s.*$//;
my $remaining = $max_mb - $mb_so_far;
if ($remaining > 1) {
warn "$remaining Mb remaining:\n";
my $f = select_file(SelDir=>1,-Title=>"looking");
if (! $f) { last;
} if (-d $f) { system("cp","-R",$f,"$tmp_dir/");
} else { system("cp",$f,"$tmp_dir/");
}
} elsif ($remaining < 0) {
my $f = select_file(-TopDir=>$tmp_dir -SelDir=>1,
-Title=>"$remaining Mb remaining: Delete which file ");
if (! $f) { last;
} else { system("rm","-rf","$tmp_dir/$f");
}
}
}
system "ls -lR $tmp_dir/*";
system "$mkisofs -gui -r -J -T -allow-limited-size"
." -V DataCD -o $tmpfile $tmp_dir 2>&1 | perl -pe 's/\$/\\e[K\\e[A/'";
print "\n";
system("rm","-rf",$tmp_dir);
# could mount -o loop $tmpfile and check it's OK ...
if ($eject) { system $eject; }
while (1) {
ask("insert blank CD into drive and press Return...");
# suppress line-feeds in the progress-bar (on stderr) ...
# should sleep, try, sleep, retry up to about 15 sec ...
system "$cdrecord dev=/dev/cdrom -v -dao $tmpfile";
if ($eject) { system $eject; }
last unless confirm("do you want to write that to another CD ?");
}
if (!unlink $tmpfile) { warn "can't unlink $tmpfile: $!\n"; }
}
sub copy_cd {
my $ok = 1;
if (!$cdda2wav) {sorry("you need to install cdda2wav or icedax."); $ok=0;}
if (!$cdrecord) {sorry("you need to install cdrecord or wodim."); $ok=0;}
if (! -e '/dev/cdrom') { sorry("can't find /dev/cdrom"); $ok = 0;
} elsif (! -w '/dev/cdrom') { sorry("can't write to /dev/cdrom"); $ok=0;
}
my $tmpdir = "$BigTmp/audio_stuff_$$";
if (! mkdir $tmpdir) { sorry("can't mkdir $tmpdir: $!"); $ok=0; }
my $olddir = 'pwd';
if (! chdir $tmpdir) { sorry("can't chdir $tmpdir: $!"); $ok=0; }
return unless $ok;
ask("insert the C D into the drive, and press Return");
system "$cdda2wav dev=/dev/cdrom -vall cddb=0 -B -Owav";
if ($eject) { system $eject; }
while (1) {
ask("insert blank CD into drive and press Return...");
if (($> == 0) and (! -e '/dev/cdrom')) { # CURSE icedax!
symlink '/dev/sr0', '/dev/cdrom';
}
system "$cdrecord dev=/dev/cdrom -v -dao -useinfo -text *.wav";
if ($eject) { system $eject; }
last unless confirm "do you want to write that to another CD ?";
}
chdir "$oldir";
system "rm -rf $tmpdir";
}
sub copy_dvd {
my $ok = 1;
if (!$dvdbackup) {sorry("you need to install dvdbackup."); $ok=0;}
if (!$mkisofs){sorry("you need to install mkisofs or genisoimage.");$ok=0;}
if (!$growisofs) {sorry("you need to install growisofs."); $ok=0;}
if (! -e '/dev/cdrom') { sorry("can't find /dev/cdrom"); $ok = 0;
} elsif (! -w '/dev/cdrom') { sorry("can't write to /dev/cdrom"); $ok=0;
}
my $tmpfile = "$BigTmp/dvd_$$.iso";
my $tmp_mnt = "$BigTmp/mnt_$$";
if (! mkdir $tmp_mnt) { sorry("can't mkdir $tmp_mnt: $!"); $ok=0; }
return unless $ok;
ask("insert the DVD into drive, and press Return...");
## The old non-dvdcss-capable method using mount ...
# system "mount -t iso9660 -o ro,map=off /dev/cdrom $tmp_mnt";
#my $return_code;
#foreach (1..5) { # sleep, try, sleep, retry up to about 15 sec ...
# sleep 2;
# $return_code = system "mount -t udf -o ro /dev/cdrom $tmp_mnt";
# last unless $return_code;
# sleep 2;
#}
#if ($return_code) { sorry("couldn't mount the DVD"); return 0; }
#if (! -d "$tmp_mnt/VIDEO_TS" and ! -d "$tmp_mnt/video_ts") {
# sorry("not a video DVD; can't see a /VIDEO_TS directory");
# system "ls -lR $tmp_mnt ; umount $tmp_mnt";
# if (! rmdir $tmp_mnt) { warn "can't rmdir $tmp_mnt: $!\n"; }
# return 0;
#}
#system "ls -lR $tmp_mnt/*";
system "$dvdbackup -v -M -o $tmp_mnt -i /dev/cdrom"; # uses libdvdcss!
# discover the DVD's title
my $dh; opendir($dh, $tmp_mnt) or die "can't opendir $tmp_mnt: $!";
my @ds = grep { !/^\./ && -d "$tmp_mnt/$_" } readdir($dh);
closedir $dh;
if (! @ds) { die "no directories found in $tmp_mnt/\n"; }
my $title = $ds[$[];
if (1 != scalar @ds) {
warn "directories @ds found in $tmp_mnt/ , using $title\n";
}
mkdir "$tmp_mnt/$title/AUDIO_TS";
# mkisofs -dvd-video -o i1.img d1/NAQOYQATSI/
# growisofs -dvd-compat -Z /dev/sr0=i1.img
# suppress line-feeds in the progress-bar (on stderr) ...
system "$mkisofs -gui -r -J -T -dvd-video -allow-limited-size"
." -V Video_DVD -o $tmpfile $tmp_mnt/$title"
." 2>&1 | perl -pe 's/\$/\\e[K\\e[A/'";
#system "umount $tmp_mnt";
print "\n";
if ($eject) { system $eject; }
# if (! rmdir $tmp_mnt) { warn "can't rmdir $tmp_mnt: $!\n"; }
use File::Path; File::Path::remove_tree("$tmp_mnt");
system "ls -l $tmpfile";
# to be fussy, could mount -o loop $tmpfile and check it's OK ...
if (! -s $tmpfile) { warn " the iso fs was empty :-(\n"; return; }
while (1) {
ask("insert blank DVD into drive, wait for light to go out, then press Return...");
# suppress line-feeds in the progress-bar (on stderr) ...
# should sleep, try, sleep, retry up to about 15 sec ...
system "growisofs -dvd-compat -Z /dev/cdrom=$tmpfile"
. " 2>&1 | perl -pe 's/\$/\\e[K\\e[A/'";
warn "\n";
if ($eject) { system $eject; }
last unless confirm "do you want to write that to another DVD ?";
}
if (!unlink $tmpfile) { warn "can't unlink $tmpfile: $!\n"; }
}
sub dvd_size {
if (!$mediainfo) {
# could try some other program ?
sorry('you should install dvd+rw-mediainfo'); return undef;
}
my $dev = 'dev/cdrom';
my $size = undef;
foreach (1..5) {
sleep 2;
if (! open(P, "$mediainfo $dev 2>&1 |")) {
sorry("can't run $mediainfo $dev"); return undef;
}
while (<P>) { if (/Legacy lead-out.+=(\d+)$/) { $size = 0+$1; } }
close P;
if ($size) { return $size; }
sleep 2;
}
sorry("no dvd media present in $dev");
return undef;
}
sub speak {
if (!$festival) { return; }
if (!@_) { return; }
if (! open(P, "|$festival --tts")) {
sorry("can't run $festival"); return;
}
print P $_[$[];
close P;
}
sub which_track { my $do_what = $_[$[];
# cdda2wav produces its output on stderr ARRGghhh :-(
if (! open (P, "$cdda2wav -Q -H -g -v toc -J 2>&1 |")) {
die "can't run $cdda2wav: $!\n";
}
my @toc = <P>;
close P;
my @tracks, @header;
foreach (@toc) {
next if /^\s*#/;
next if /not detected/;
next if /not supported/;
next if /Album title: '' from ''/;
chop;
s/^\s+//;
if (/^T\d/) { s/ title '' from ''//; push @tracks, $_;
} else { push @header, $_;
}
}
print join("\n", @header), "\n";
$track = choose("$do_what which track ?", @tracks);
$track =~ s/^\s*T0?//;
$track =~ s/:?\s+.*$//;
if ($track =~ /^\d$/) { $track = "0$track"; }
return $track;
}
sub play_cd {
if (!$cdda2wav) {sorry("you need to install cdda2wav or icedax."); return;}
set_cdda_device() || return;
my $task = choose('Play', 'All tracks', 'Just one track');
return unless $task;
if ($task eq 'All tracks') {
system "$cdda2wav cddb=0 -H -B -e -N"; return;
}
my $track = which_track('Play');
if ($track) { system "$cdda2wav -H -Q -x -e -N -t $track+$track"; }
}
sub rip_wav {
if (!$cdda2wav) {sorry("you need to install cdda2wav or icedax."); return;}
set_cdda_device() || return;
my $task = choose('Extract', 'All tracks', 'Just one track');
return unless $task;
if ($task eq 'All tracks') {
system "$cdda2wav cddb=0 -H -B -Owav"; return;
}
$track = choose('Extract which track ?', @tracks);
$track =~ s/^\s*T0?//;
$track =~ s/:?\s+.*$//;
if ($track =~ /^\d$/) { $track = "0$track"; }
my $track = which_track('Extract');
if ($track) {
my $filename = ask('to what filename ?', "${track}_track.wav");
if ($filename && ($filename !~ /\.wav$/i)) { $filename .= '.wav'; }
system "$cdda2wav -H -Q -x -Owav -t $track+$track $filename";
}
}
sub rip_mp3 {
}
sub wav2mp3 {
if (! $lame) { sorry("you need to install lame."); return; }
my @files = select_file(-FPat=>'*.wav', -Path=>$ENV{PWD}, -Chdir=>0);
foreach my $i (@files) {
my $o = $i; $o =~ s/wav$/mp3/;
if (-f $o && !confirm("OK to overwrite $o ?")) { next; }
system "$lame -h $i $o";
}
}
sub wav2mp2 {
if (! $toolame) { sorry("you need to install toolame."); return; }
my @files = select_file(-FPat=>'*.wav', -Path=>$ENV{PWD}, -Chdir=>0);
foreach my $i (@files) {
my $o = $i; $o =~ s/wav$/mp2/;
if (-f $o && !confirm("OK to overwrite $o ?")) { next; }
system "$toolame $i";
}
}
sub mp32wav {
if (! $lame) { sorry("you need to install lame."); return; }
if (! $normalize) {
sorry("you need to install normalize-audio or normalize."); return;
}
my @files = select_file(-FPat=>'*.mp3', -Path=>$ENV{PWD}, -Chdir=>0);
foreach my $i (@files) {
my $o = $i; $o =~ s/mp3$/wav/;
if (-f $o && !confirm("OK to overwrite $o ?")) { next; }
system "$lame --mp3input --decode $i $o";
system "$normalize '$o'";
}
}
sub mid2wav {
# should also offer replay-through-xv2020, and sox -t alsa hw:4,0
if (! $timidity) { sorry("you need to install timidity."); return; }
if (! $normalize) {
sorry("you need to install normalize-audio or normalize."); return;
}
my @files = select_file(-FPat=>'*.mid', -Path=>$ENV{PWD}, -Chdir=>0);
my $config = timiditycfg();
print "config=$config\n";
if (! $config) { sorry("can't find any timidity.cfg file"); return; }
my @wavs = ();
foreach my $i (@files) {
my $o = $i; $o =~ s/mid$/wav/; push @wavs, $o;
if (-f $o && !confirm("OK to overwrite $o ?")) { next; }
system "$timidity -Ow -c $config -o $o $i";
}
system "$normalize '".join("' '",@wavs)."'";
}
sub mid2mp3 {
if (! $timidity) { sorry("you need to install timidity."); return; }
if (! $normalize) {
sorry("you need to install normalize-audio or normalize."); return;
}
if (! $lame) { sorry("you need to install lame."); return; }
my @files = select_file(-FPat=>'*.mid', -Path=>$ENV{PWD}, -Chdir=>0);
my @wavs = ();
return unless @files;
foreach my $i (@files) {
my $o = $i; $o =~ s/mid$/wav/; push @wavs, $o;
if (-f $o && !confirm("OK to overwrite $o ?")) { next; }
system "$timidity -Ow -o $o $i";
}
system "$normalize '".join("' '",@wavs)."'";
foreach my $o (@wavs) {
my $oo = $o; $oo =~ s/wav$/mp3/;
if (-f $oo && !confirm("OK to overwrite $oo ?")) { next; }
system "$lame -h $o $oo";
unlink $o;
}
}
sub play {
my $file = select_file(-Readable=>1, -Path=>$ENV{PWD},
-FPat=>'{*.wav,*.mp3,*.mid}');
return unless $file;
if ($file =~ /\.mp3$/) {
if ($mpg123) { inform(
's=stop/start b=beginning ,=rewind .=fast-forward q=quit');
system "$mpg123 -C $file"; system "stty sane";
return;
}
if (! $mp3_player) {
sorry("you need to install mpg123 or mplayer or sndfile-play.");
return;
}
system "$mp3_player $file";
return;
} elsif ($file =~ /\.wav$/) {
if (! $wav_player) {
sorry("you need to install sox (play) or sndfile-play or mplayer.");
return;
}
system "$wav_player $file";
return;
}
if (! $aplaymidi) { sorry("you need to install aplaymidi."); return; }
# also needed by metronome below, should factorize this code out ...
if (!open(P,"$aplaymidi -l |")) { die "can't run $aplaymidi -l: $!\n"; }
my (%outport2device, %device2outport);
while (<P>) {
if (/^\s*(\d+:\d)\s+(.*)$/) {
my $port = $1;
my $device = $2; substr ($device,0,32) = ''; $device =~ s/^\s*//;
$outport2device{$port} = $device;
$device2outport{$device} = $port;
}
}
close P;
my @outdevices = sort keys %device2outport;
my $outdevices; my $outport;
if (!@outdevices) {
sorry("aplaymidi can't see any midi output devices."); return;
} elsif (1 == @outdevices) {
inform("using midi device $outdevices[$[]");
$outport = $device2outport{$outdevices[$[]};
} else {
$outport = $device2outport{choose('To which device ?',@outdevices)};
return unless $outport;
}
system "$aplaymidi -p $outport \"$file\"";
}
sub audio2wav {
if (! $rec) {
sorry("you need to install rec (comes with sox)."); return;
}
my $file = ask("To what .wav file ?");
return unless $file;
if ($file !~ /\.WAV$/) { $file =~ s/\.WAV$/\.wav/;
} elsif ($file !~ /\.wav$/) { $file .= '.wav';
}
# could offer options, like gain, compand, autostart on signal...
# must convert to 44100 Hz :-)
system "$rec -c 2 $file rate 44100";
}
sub midi_in_port {
if (! $aconnect) { sorry("you need to install aconnect."); return; }
if (!open(P,"$aconnect -i |")) {die "can't run $aconnect -i: $!\n";}
my $major; my $inport; my $outport;
while (<P>) {
if (/^client\s*(\d+:)/) { $major = $1;
} elsif ($major>0 and /^\s+(\d)\s+'(.*)'/) {
my $minor = $1; my $device = $2; $device =~ s/\s+$//;
$inport2device{"$major$minor"} = $device;
$device2inport{$device} = "$major$minor";
}
}
close P;
my @indevices = sort keys %device2inport;
my $inport;
if (!@indevices) {
sorry("aconnect can't see any midi input devices."); return;
} elsif (1 == @indevices) {
inform("using MIDI-input-port $indevices[$[]");
$inport = $device2inport{$indevices[$[]};
} else {
$inport
= $device2inport{choose('connect from which device ?',@indevices)};
}
return $inport;
}
sub midi_out_port {
if (! $aconnect) { sorry("you need to install aconnect."); return; }
if (!open(P,"$aconnect -o |")) {die "can't run $aconnect -o: $!\n";}
while (<P>) {
if (/^client\s*(\d+:)/) { $major = $1;
} elsif ($major>0 and /^\s+(\d)\s+'(.*)'/) {
my $minor = $1; my $device = $2; $device =~ s/\s+$//;
$outport2device{"$major$minor"} = $device;
$device2outport{$device} = "$major$minor";
}
}
close P;
my @outdevices = sort keys %device2outport;
my $outport;
if (!@outdevices) {
sorry("aconnect can't see any midi output devices."); return;
} elsif (1 == @outdevices) {
inform("using MIDI-output-port $outdevices[$[]");
$outport = $device2outport{$outdevices[$[]};
} else {
$outport
= $device2outport{choose('connect to which device ?',@outdevices)};
}
return $outport;
}
sub connect_midi_ports {
if (! $aconnect) { sorry("you need to install aconnect."); return; }
if (!open(P,"$aconnect -ol |")) {die "can't run $aconnect -ol: $!\n";}
my $major = -1;
my %port2device = ();
my %device2port = ();
my @connections = ();
my $device = '';
while (<P>) {
if (/^client\s*(\d+):/) { $major = $1; next; }
if ($major>0 and /^\s+(\d)\s+'(.*)'/) {
my $minor = $1; $device = $2; $device =~ s/\s+$//;
$port2device{"$major:$minor"} = $device;
$device2port{$device} = "$major:$minor";
next;
}
if (/Connected From:\s+(.+)/) {
foreach (split /,\s*/, $1) {
push @connections, "$port2device{$_} -> $device";
}
}
}
close P;
if (@connections) {
my @disconnect = ();
if (1 == @connections) {
my $msg = "do you want disconnect this one ?";
my $disconnect = choose($msg, @connections);
if ($disconnect) { @disconnect = ($disconnect); }
} else {
my $msg = "do you want disconnect any of these ?";
@disconnect = choose($msg, @connections);
}
my $is_ok = 0;
foreach (@disconnect) {
if (!$_) { last; }
if (/^(.+) -> (.+)/) {
system "$aconnect -d $device2port{$1} $device2port{$2}";
$is_ok = 1;
} else {
warn "unrecognised connection $_\n";
}
}
if ($is_ok) { inform('OK'); }
}
my $inport = midi_in_port();
return unless $inport;
my $outport = midi_out_port();
return unless $outport;
$MidiOutPort = $outport;
system "$aconnect $inport $outport";
}
sub kbd2mid {
my $inport = midi_in_port();
return unless $inport;
my $bpm = choose('crochets (quarter-notes) per minute ?', tempi());
$bpm = $bpm || 120;
my $timesig = choose('time signature ?', '3/8','6/8','9/8','12/8',
'2/4','3/4','4/4','5/4','6/4','7/4','2/2','3/2');
$timesig = $timesig || '4/4';
$timesig =~ s/\//:/;
my $file = ask("To what midifile ?");
return unless $file;
if ($file !~ /\.mid$/) { $file .= '.mid'; }
my $metronome;
if ($MidiOutPort) { $metronome=choose('With a metronome ?','Yes','No'); }
my $ok = ask("<Return> to start recording, <Ctrl-C> to stop ...");
if ($metronome) {
system "arecordmidi -p$inport -b$bpm -i$timesig -m$MidiOutPort $file";
} else {
system "arecordmidi -p$inport -b$bpm -i$timesig $file";
}
}
sub changedir {
my $newdir = select_file(-Path=>$ENV{PWD}, -Directory=>1);
return unless $newdir;
if (! -d $newdir) { sorry("$newdir is not a directory"); return; }
if (! chdir $newdir) { sorry("can't chdir to $newdir: \!"); return; }
# assertively rename *.WAV->*.wav, *.MID->*.mid, *.MP3->*.mp3
if (! opendir (D, '.')) { sorry("can't opendir $newdir: \!"); return; }
my @allfiles = grep { !/^\./ } readdir(D);
closedir D;
my $oldname;
foreach $oldname (grep { /\.WAV$/} @allfiles) {
my $newname = $oldname; $newname =~ s/WAV$/wav/;
rename $oldname, $newname;
}
foreach $oldname (grep { /\.MP3$/} @allfiles) {
my $newname = $oldname; $newname =~ s/MP3$/mp3/;
rename $oldname, $newname;
}
foreach $oldname (grep { /\.MID$/} @allfiles) {
my $newname = $oldname; $newname =~ s/MID$/mid/;
rename $oldname, $newname;
}
}
sub list_soundfont {
eval 'require File::Format::RIFF';
if ($@) { sorry("you need to install File::Format::RIFF."); return; }
my $config = timiditycfg();
my $dir = $ENV{PWD};
if (open (F, $config)) {
while (<F>) { if (/^dir\s+(.+)$/) { $dir = $1; last; } } close F;
} else {
inform("can't find any timidity.cfg file ...");
}
my $file = select_file(
-Title=>'Which Soundfont file ?', -FPat=>'{*.sf2,*.SF2}', -Path=>$dir,
);
return unless $file;
open(IN, $file) or die "Could not open $file: $!\n";
my $riff1 = File::Format::RIFF->read(\*IN);
close(IN);
# $riff1->dump; $pdta->dump;
my $pdta = $riff1->at(2);
my $phdr = $pdta->at(0);
my $data = $phdr->data;
my %t;
while ($data) {
chop;
my $chunk = substr $data,0,38,'';
my $name = substr $chunk,0,20,'';
my ($preset,$bank) = unpack 'SS', $chunk;
$name =~ tr/ 0-9a-zA-Z_//cd;
if ($name =~ /^EOP/) { next; }
my $k = 1000*$bank + $preset;
$t{$k} = sprintf "%5d %5d %s", $preset,$bank,$name;
}
my @t = "$file\nPreset Bank PresetName";
foreach (sort {$a<=>$b} keys %t) { push @t, $t{$_}; }
view("Contents of $file", join("\n", @t)."\n");
}
sub configure_timidity {
if (! $timidity) { sorry("you need to install timidity."); return; }
my $f = timiditycfg();
if (! $f) {
inform("can't find any timidity.cfg ...");
} elsif (-w $f) {
edit($f);
} else {
inform("you don't have write permission to $f ...");
if (!-w $ENV{PWD}) {
inform("and you don't have write permission here in $ENV{PWD}");
return;
}
return unless confirm("Create a local timidity.cfg in $ENV{PWD} ?");
if (! open (O, ">$ENV{PWD}/timidity.cfg")) {
sorry("can't write to $ENV{PWD}/timidity.cfg: $!"); return;
}
if (open (I, $f)) {
while (<I>) { print O $_; } close I;
} else {
print O <<'EOT';
# Sample timidity.cfg - see "man timidity.cfg"
dir /directory/where/you/keep/your/soundfonts
# specify default Soundfont:
soundfont Chaos4m.sf2
# but take bank0 patch0 from SteinwayGrandPiano & patch74 from Ultimate
bank 0
0 %font SteinwayGrandPiano1.2.sf2 0 0
74 %font Ultimate.sf2 0 74
EOT
}
close O;
edit("$ENV{PWD}/timidity.cfg");
}
}
sub man {
my @topics = @_ || (
'aconnect', 'alsamixer', 'aplaymidi', 'arecordmidi', 'atacontrol',
'audio_stuff', 'bristol', 'burncd',
'cdcontrol', 'cdda2wav', 'cdrecord', 'dvd+rw-mediainfo',
'File::Format::RIFF',
'genisoimage', 'icedax', 'lame', 'mencoder', 'mkisofs',
'mplayer', 'normalize', 'normalize-audio', 'pciconf', 'sndfile-play',
'sox', 'soxexam', 'soxeffect',
'Term::Clui', 'Term::Clui::FileSelect', 'timidity', 'timidity.cfg',
'toolame', 'wodim',
);
my $topic = choose('Which topic ?', @topics); return unless $topic;
if ($topic eq 'audio_stuff') { system "perldoc $0";
} elsif ($topic =~ /::/) { system "perldoc $topic";
} elsif ($topic =~ /bristol/) { system "$startBristol -v -h | less";
} else { system "$man $topic";
}
}
#----------------------- infrastructure ------------------------
sub which { my $f; foreach $d (@PATH) {$f="$d/$_[$[]"; return $f if -x $f; }}
sub tempi {
qw(40 42 44 46 48 50 52 54 56 58 60 63 69 72 76 80 84 8 92 96 100 104
108 112 116 120 126 132 138 144 152 160 168 176 184 192 200 208);
}
sub timiditycfg {
return unless $timidity;
my $f;
foreach $f (
"$ENV{PWD}/timidity.cfg",
'/usr/local/share/timidity/timidity.cfg',
'/etc/timidity.cfg',
) {
if (-f $f) { return $f; }
}
if (! open(P, "strings $timidity |")) { return ''; }
while (<P>) {
if (/^Please check (\S+.cfg)/) { close P; return $1; }
}
close P; return '';
}
sub set_cdda_device {
if ($ENV{CDDA_DEVICE}) { return 1; }
inform(" you should set the CDDA_DEVICE environment variable!");
if (-e "/dev/cdrom") {
$ENV{CDDA_DEVICE} = '/dev/cdrom:@';
inform("using $ENV{CDDA_DEVICE} ...");
return 1;
} elsif (-e '/dev/sr0' and ! $>) { symlink '/dev/sr0', '/dev/cdrom'; }
}
system "eject"; system "eject -t"; sleep 3;
if ($>) {
warn " you need to be root to run cdrecord -scanbus\n";
$ENV{CDDA_DEVICE} = '0,0,0';
warn "trying CDDA_DEVICE='0,0,0'\n";
} elsif (! open (P, "$cdrecord -scanbus |")) {
warn "can't run cdrecord -scanbus: $!\n"; return 0;
} else {
my @devices;
while (<P>) {
chop;
s/\t/ /g; s/ +/ /g;
if (/^\s+\d.*[^*]$/) { push @devices, $_; }
}
close P;
my $device = choose("Which Device ?", @devices);
$device =~ s/^\s+//;
$device =~ s/\s.*$//;
$ENV{CDDA_DEVICE} = $device;
}
if (! $ENV{CDDA_DEVICE}) {
$ENV{CDDA_DEVICE} = ask('CDDA_DEVICE ?');
}
if ($ENV{CDDA_DEVICE}) { return 1; } else { return 0; }
}
sub tasks {
my @tasks = (
'run a Bristol synth', 'burn files->DataCD', 'burn WAV->AudioCD',
'change Directory', 'configure Timidity', 'connect MIDIports',
'convert MIDI->MP3', 'convert MIDI->WAV', 'convert Muscript->MIDI',
'copy audio CD', 'copy video DVD', 'decode MP3->WAV',
'edit Muscript', 'encode WAV->MP2', 'encode WAV->MP3', 'play AudioCD',
'rip AudioCD->WAV', 'rip MP3CD->MP3', 'play MIDI,WAV,MP3',
'record AudioIn->WAV', 'record Keyboard->MIDI', 'run alsamixer',
);
if (-e './Makefile') {
push @tasks, ('run Make', 'edit Makefile');
} else {
push @tasks, ('create Makefile');
}
push @tasks, (
'list Soundfont', 'consult Manual',
);
return sort @tasks;
}
__END__
echo ===============================================================
echo Getting rid of spaces in .mp3 files ...
for i in *.[Mm][Pp]3; do mv "$i" `echo $i | tr ' ' '_'`; done
echo Changing .MP3 to .mp3 ...
for i in *.MP3; do mv "$i" `basename $i .MP3`.mp3; done
echo Changing _-_ to _ ...
for i in *_-_*.mp3; do mv "$i" `echo $i | sed s/_-_/_/`; done
echo
echo ===============================================================
echo Decoding .mp3 files to *.wav ...
for i in *.mp3; do lame --decode $i `basename $i .mp3`.wav; done
echo
echo ===============================================================
echo normalising .wav files ...
normalize -m *.wav
echo
echo ===============================================================
echo Total size of .wav files ...
du -kch *.wav | grep total
echo
echo ===============================================================
echo Checking for non-44100-Hz encoded .wav files ...
file *.wav | grep -v 44100
=pod
=head1 NAME
audio_stuff - wrapper for aplaymidi, cdda2wav, cdrecord, lame, timidity etc.
=head1 SYNOPSIS
$ audio_stuff
=head1 DESCRIPTION
This script, which comes along with the I<Term::Clui> Perl-module
in its I<examples> directory,
integrates
various open-source programs for handling
Muscript, Midi, WAV, MP3, CDDA and DVD files
into one ArrowKey-and-Return user-interface,
=head1 FEATURES
burn files->DataCD burn WAV->AudioCD change Directory
configure Timidity connect MIDIports consult Manual
convert MIDI->MP3 convert MIDI->WAV convert Muscript->MIDI
copy audio CD copy video DVD create Makefile
decode MP3->WAV edit Muscript encode WAV->MP2
encode WAV->MP3 list Soundfont play MIDI,WAV,MP3
record AudioIn->WAV record Keyboard->MIDI rip AudioCD->WAV
rip MP3CD->MP3 run a Bristol synth run alsamixer
=over 3
=item I<rip AudioCD-E<gt>WAV> and I<burn WAV-E<gt>AudioCD>
These features use I<cdda2wav> or I<icedax>
and I<cdrecord> and I<wodim> to get files
off AudioCDs into I<.wav> format, or vice-versa.
=item I<copy video DVD>
This feature uses I<mkisofs> or I<genisoimage> to get files off
a Video DVD and I<growisofs> to burn them onto an empty one.
=item I<rip MP3CD-E<gt>MP3> and I<burn MP3-E<gt>MP3CD>
These features use I<cp> and I<cdrecord> or I<wodim> to get files
off MP3-CDs onto local hard-disk, or vice-versa.
=item I<encode WAV-E<gt>MP3> and I<decode MP3-E<gt>WAV>
These features use I<lame> to get files
from I<.wav> format into I<.mp3> format or vice-versa.
=item I<play WAV,MP3,MID>
Depending on which file you select, this feature
either uses I<mplayer> or I<mpg123> to play a I<.mp3> file,
or I<play> or I<sndfile-play> to play a I<.wav> file to the headphones,
or I<aplaymidi> to send a I<.mid> file to a Synthesiser.
=back
=head1 ENVIRONMENT
When copying DVDs some big temporary files are created;
if your I</tmp> is too small you can create a B<BIGTMP>
environment variable to use somewhere else, e.g.:
export BIGTMP=/home/tmp
audio_stuff
=head1 AUTHOR
Peter J Billam www.pjb.com.au/comp/contact.html
=head1 CREDITS
Based on Term::Clui, alsamixer, aplaymidi, arecordmidi, cdrecord or wodim,
cdda2wav or icedax, lame, mkisofs or genisoimage, mpg123,
normalize-audio, sox, sndfile_play, startBristol and timidity.
=head1 SEE ALSO
http://www.pjb.com.au/ ,
http://search.cpan.org/~pjb ,
http://bristol.sourceforge.net ,
Term::Clui,
alsamixer(1),
aplaymidi(1),
arecordmidi(1),
cdrecord(1),
cdda2wav(1),
festival(1),
genisoimage(1),
growisofs(1),
icedax(1),
lame(1),
mpg123(1),
mkisofs(1),
normalize(1),
normalize-audio(1),
sndfile_play(1),
sox(1),
soxexam(7),
soxeffect(7),
timidity(1),
wodim(1)
=cut