WireTap allows you to record any audio signals on your mac. You can set it to record all audio passing through the sound device, or only audio that's playing in a particular application (say, a streaming radio station playing in Safari). A really nice feature of WireTap is that you can mute the other audio sources while it's recording. So for example, I can using WireTap to record an audio stream in Firefox while listening to a playlist in iTunes. The two audio signals will not interfere with one another. So what I'm doing here is streaming an entire album from one of the sites listed above (in their flash browser music player), and having WireTap record the audio stream from the browser, and split out the individual tracks.
Once that's done, the script I wrote (included below) uses a directory of MP3s (from WireTap) as input, and prompts you for band/album name. With that info it queries the MusicBrainz database to look up album and track data for MP3 tagging purposes. It then adds the new tracks to your iTunes library so they don't have to be manually added. From there they can be easily added to an iPod.
The sound isn't great-quality (96kbps I believe) but its a good way to listen to music to see if you like it or not. Then, of course, you buy it. Just a reminder: never distribute copyrighted music.
Step 1: Set up a new WireTap recording session. Open WireTap and Select "Window" menu -> Recording Sessions. When the "Recording Sessions" dialog is open, press the "+ New" button, then the "Saving" tab. Enter these settings:
Description: Enter "Browser Record"
Select "Name Automatically"
Save Files To: Library
Select "Prefix" and enter "0"
Suffix: Increment
Split: On Silence (Configure: -64.0db Minimum Silence Duration: 0.7s)
Un-check "Record Lossless Original"
Un-check "Open Recording In Editor"
NOTE ON "SPLIT" CONFIGURATION: The duration of silence is what tells WireTap that a song has ended and another begins. Without this, you'd end up with one long track as your album. I've found 0.7s to be a good duration, but you may need to configure.
Step 2: Configure sound source. Click the "source" tab and use these settings:
Source One: select Safari (or Firefox)
Source Two: none
Record: Trim All Silence (leave configuration as-is
Mute Mac Audio Sources: checked
Step 3: Configure format. Select "Format" tab. I use 192kbps so that little or no quality is lost in the encoding of the stream. You're probably OK with 128kbps is you want a smaller filesize though. Save your new recording session.
Step 4: Start the recording. In the WireTap "recording session" window, select the "Browser Record" session you jsut created and click the "start" button. It will start "listening" to your configured source (browser), but it won't actually record until sound starts streaming. A small "on air" logo will appear on the left side of your session.
Find your desired album on one of the sites mentioned above. If it's lala.com just click the "Queue" button on the album's page and it adds all tracks to your queue (you only get one free play on lala, so don't screw up!). Then press the play button on the queue at the top of the page. On Deezer, just press play on the first track. This will begin the actual recording and you will see a duration timer in your recording session. It starts anew with each new track.
Step 5: Export recordings from WireTap. Once your recording is done, click the "Stop" button in the "recording sessions" window. Select Window -> Library and you will see your tracks in there. The last track will probably have a length of 00.00 and can safely be deleted. Highlight all tracks and click "Local" (at bottom) to export as mp3s. In the file dialog that pops up navigate to your Desktop and click "New Folder" and name it "output".
Step 6: Run script to name and tag tracks. So now we have a set of MP3s which is great, but they need to be a) re-named so the filenames reflect what they are b) tagged with ID3 info so they show properly in the iTunes library c) physically moved into your music library. At this point you could manually re-name the files, drag them into iTunes and tag them manually by right-clicking each one and going Get Info -> "Info" tab. That's the thing to do if you're not script-inclined. But if you are, the script below automates the job.
The script below can be saved somewhere in your user directory (I have a ~/bin folder for scripts like this) as "cddb.pl", even though it really has nothing to do with the actual CDDB. After chmod-ing it to 755 (also adjust the $libdir variable to the location of your music library), you can call it with the first argument being the location of the folder we just created. For example:
~/bin/cddb.pl ~/Desktop/output/You will then be prompted with the info it needs to name and tag the MP3s. The screenshot below illustrates a sample session. Click to enlarge.
Click "expand source" to view script's code.
#!/usr/bin/perl
# HOW TO SET UP A WIRETAP BROWSER RECORD
#
# Open the Wiretap Recording Sessions window. Click "New" button. Add the following info to
# the "Saving" tab:
#
# Description: Enter "Browser Record"
# Select "Name Automatically"
# Save Files To: Library
# Select "Prefix" and enter "0"
# Suffix: Increment
# Split: On Silence (Configure: -64.0db Minimum Silence Duration: 0.7s)
# Un-check "Record Lossless Original"
# Un-check "Open Recording In Editor"
#
# In the "Source" tab:
#
# Source One: Safari (or Firefox)
# Source Two: None
# Record: Trim All Silence (Configure: -64.0db Minimum Silence Duration: 2.0s)
# Check "Mute Mac Audio Sources"
#
# In the "Format" tab:
#
# Presets: MP3-High Quality
#
# Leave the "Effects", "Processing" and "Schedule" tabs at their defaults and click OK.
#
# To record, FIRST click on the "Browser Record" Line in Recording Sessions and then "Start"
# THEN press "play" on whatever you are playing in the browser. You will not hear sound,
# because it's muted. Let the recording play in its entirety, and press "Stop" in
# Recording Sessions when it's done. If everything went well, one track per each song
# will appear in the Library and should be ready for export.
#
# Note: Often the last track is empty - look at the times of the track and delete it if it's there.
#
# HOW TO SPLIT TRACKS IN WIRETAP SO THAT THEY WILL BE NAMED PROPERLY HERE
#
# When a recorded track in wiretap actually contains two tracks it needs to be manually split.
# Double-click on the track in the Library to load it in the editor. Find where the track is
# to be split (by track length or just by looking) and click there to set the cursor at that point.
# Then in the "Editor" menu choose "add marker". Then in the Editor menu select "Split Audio"
# In the dialog that appears, in the Name field add the original name of the track. Select
# Save to Library, and Split On "Marker". Click OK and it will create two new tracks in the Library:
#
# [track name]_split
# [track name]_New Marker [numbered if more than one]
# ...
#
# The one with the "_split" suffix is the first track, and the "_New Marker" is the second (and so forth).
# Re-name them as such:
#
# [track name]a
# [track name]b
# ...
#
# Then delete the original track.
#
# OTHER NOTES
#
# - To use the non-default (wiretap) source folder specify it when calling this script:
# $ cddb.pl /Users/guest/Desktop/new_folder/
# This is useful when you want to tag and add a set of mp3s recorded from somewhere else.
# One caveat is they *have* to appear in alphabetical order when you type ls -al
#
use strict;
use WebService::MusicBrainz::Release;
use WebService::MusicBrainz::Artist;
use WebService::MusicBrainz::Track;
use MP3::Tag;
use File::Copy;
# location of raw mp3 files
my $datadir = '/Users/user/Music/Import/wiretap/';
my $libdir = '/Volumes/numchux/mp3/';
# itunes stuff
my $add_to_itunes = "Yes"; # works only on Mac OS X - set to No for all other OS's
my $itunes_bookmarkable = "No";
# check if we're supposed to use an alternate data dir
my $altdir = 0;
if ( ($ARGV[0]) && (-d $ARGV[0]) ) {
$altdir = 1;
$datadir = $ARGV[0];
}
# put the data files in an array sorted by date/time created - accessible by $fullpaths[]
my @filelist = ();
my @fullpaths = ();
my @todelete = ();
# only do the following for the wiretap records:
if (!$altdir) {
# rename the files so we can sort by name
opendir(DIR, $datadir) || die "can't opendir $datadir: $!";
my @prefiles = readdir(DIR);
closedir(DIR);
my $mp3count = 0;
foreach my $prefile (@prefiles) {
my $curfile = $datadir . $prefile;
if ($prefile =~ /\.[Mm][PpOo][3Vv]$/) {
$mp3count++;
my $skip = 0;
my $final_file;
my $quoted = quotemeta($curfile);
my $basename = `/usr/bin/basename $quoted`;
$basename = &stripCrap($basename);
if ($basename =~ /^0\.original\.([Mm][PpOo][3Vv])$/) {
$final_file = $datadir . '00' . '.original.' . $1;
} elsif ($basename =~ /^0 (\d\d\d?)\.original\.([Mm][PpOo][3Vv])$/) {
$final_file = $datadir . $1 . '.original.' . $2;
} elsif ($basename =~ /^0 (\d\d\d?[a-zA-Z])\.original\.([Mm][PpOo][3Vv])$/) {
$final_file = $datadir . $1 . '.original.' . $2;
} elsif ($basename =~ /^0 (\d)\.original\.([Mm][PpOo][3Vv])$/) {
$final_file = $datadir . '0' . $1 . '.original.' . $2;
} elsif ($basename =~ /^0 (\d[a-zA-z])\.original\.([Mm][PpOo][3Vv])$/) {
$final_file = $datadir . '0' . $1 . '.original.' . $2;
} else {
$skip = 1;
}
if (!$skip) {
# move file to its new, properly aplh'd name
move($curfile,$final_file);
}
# if it's a .mov file we need to convert it
if ($final_file =~ /^(.*?)\.([Mm][Oo][Vv])$/) {
my $newfile = $1 . '.mp3';
my $output = `/sw/bin/ffmpeg -i $final_file -ar 44100 -ab 160k $newfile`;
if (-e $newfile) {
unlink($final_file);
$final_file = $newfile;
$curfile = $newfile;
}
} # end check for .mov
# add files to array so we delete after we;'re done
push(@todelete,$curfile);
}
if ($prefile =~ /\.meta\.plist$/) {
push(@todelete,$curfile);
}
}
# if we don't have an mp3count, die
if ($mp3count==0) {
print "no mp3s to convert\n"; exit;
}
} # end if for !$altdir (is a wiretap record)
# sort data dir by name
opendir(DH, $datadir) || die "Can't open directory $datadir: $!\n";
@filelist = sort { "$datadir/$b" <=> "$datadir/$a" } grep { -f "$datadir/$_" && /\.[Mm][PpOo][3Vv]$/ } readdir DH;
closedir(DH);
my $i=1;
my $tracks = @filelist;
foreach my $file (@filelist) {
$fullpaths[$i] = $datadir . $file;
#print "$fullpaths[$i]\n";
$i++;
}
# prompt user for artist name
my $artist_name = &promptUser("Enter Artist Name ","");
if ( !$artist_name ) {
print "No artist name entered, quitting\n";
exit;
}
my $at = WebService::MusicBrainz::Artist->new();
my $response = $at->search({ NAME => $artist_name, LIMIT => 15 });
my @artists = $response->artist_list(); # get first in list
# get the most relevant search results for query
my @artist_id_arr = ();
my @artist_name_arr = ();
my $i = 1;
my ($artist_id,$artist_name);
foreach my $artist (@artists) {
$artist_id_arr[$i] = $artist->{id};
my $tmpname = $artist->{name};
$tmpname =~ s/[^[:ascii:]]+//g;
$artist_name_arr[$i] = $tmpname;
print "[${i}] $tmpname\n";
$i++;
}
my $artist_number = &promptUser("Matching Artist ","1");
if ($artist_id_arr[$artist_number]) {
$artist_id = $artist_id_arr[$artist_number];
$artist_name = $artist_name_arr[$artist_number];
} else {
print "Invalid artist number entered, quitting\n";
exit;
}
# get the releases for specified artist
my $rl = WebService::MusicBrainz::Release->new();
my $response = $rl->search({ ARTISTID => "$artist_id" });
my @releases = $response->release_list();
my @release_id_arr = ();
my @release_name_arr = ();
my @release_date_arr = ();
my $i = 1;
my ($release_id, $release_name, $release_date);
foreach my $release (@releases) {
$release_id_arr[$i] = $release->{id};
$release_name_arr[$i] = $release->{title};
my $date = "unknown date";
if ($release->{release_event_list}->{events}[0]->{date}) {
$release->{release_event_list}->{events}[0]->{date} =~ /^([0-9]{4})/;
$date = $1;
}
$release_date_arr[$i] = $date;
print "[${i}] $release->{title} ($date, $release->{track_list}->{count} tracks)\n";
$i++;
}
my $release_number = &promptUser("Album Name ","1");
if ($release_id_arr[$release_number]) {
$release_id = $release_id_arr[$release_number];
$release_name = $release_name_arr[$release_number];
$release_date = $release_date_arr[$release_number];
} else {
print "Invalid release number entered, quitting\n";
exit;
}
# List the tracks
my $release_search = $rl->search({ MBID => "$release_id", INC => 'tracks' });
my $release_search_first = $release_search->release();
my $track_ws = WebService::MusicBrainz::Track->new();
my $track_num = 0;
my @final_tracks = ();
foreach my $track (@{ $release_search_first->track_list()->tracks() }) {
$track_num++;
# query track data with artist details
# artist at the release level could be "Various Artists",
# so we need to query each track for it's artist
my $track_response = $track_ws->search({ MBID => $track->id(), INC => 'artist' });
my $found_track = $track_response->track();
print $track_num, ": ", $found_track->title(), " - ", $artist_name, " - ", $release_name, " - ", $release_date, "\n";
# write this info to the file
&id3v2tag($fullpaths[$track_num],$found_track->title(),$release_date,$artist_name,$release_name,"rock","lame");
# rename the mp3 file
my $curtrack = $track_num;
if ($curtrack < 10) { $curtrack = '0' . $curtrack; }
my $curtrackname = replaceSpace(stripNonAlph($found_track->title()));
my $trackfinal = $datadir . $curtrack . '_' . $curtrackname . '.mp3';
move($fullpaths[$track_num],$trackfinal) or die "Move failed: $!";
push(@final_tracks,$trackfinal);
}
# if our libdir is attached, let's move the stuff there.
if (not(-d $libdir)) { print "library not attached, not moving files. But they are done.\n"; exit; }
my ($artistlib,$albumlib,$tmp1artist,$tmp2artist,$tmp3artist,$tmp1album,$tmp2album,$tmp3album);
# check if the artist dir exists
$tmp1artist = $libdir . $artist_name . '/';
$tmp2artist = $libdir . replaceSpace(stripNonAlph(lc($artist_name))) . '/';
$tmp3artist = $libdir . capWords($artist_name) . '/';
if (-d $tmp1artist) {
$artistlib = $tmp1artist;
} elsif (-d $tmp2artist) {
$artistlib = $tmp2artist;
} elsif (-d $tmp3artist) {
$artistlib = $tmp3artist;
} else {
system("mkdir $tmp2artist");
$artistlib = $tmp2artist;
}
print "ARTIST: $artistlib\n";
# check if the album dir exists
$tmp1album = $artistlib . $artist_name . '/';
$tmp2album = $artistlib . replaceSpace(stripNonAlph(lc($release_name))) . '/';
$tmp3album = $artistlib . capWords($release_name) . '/';
if (-d $tmp1album) {
$albumlib = $tmp1album;
} elsif (-d $tmp2album) {
$albumlib = $tmp2album;
} elsif (-d $tmp3album) {
$albumlib = $tmp3album;
} else {
system("mkdir $tmp2album");
$albumlib = $tmp2album;
}
# move the tracks to the library
print "MOVING TRACKS TO: $albumlib\n";
foreach my $fulltrack (@final_tracks) {
move($fulltrack,$albumlib) or die "Move failed: $!";
}
# clean up files
foreach my $delete (@todelete) {
unlink($delete);
}
# add to itunes library
if ($add_to_itunes eq 'Yes') {
# prompt user for what playlist to add (if any)
my $add_to_itunes_playlists = &promptUser("Enter playlist you want to add these to [blank for none] ","");
# add all new tracks to an array
my @additions = ();
opendir(DH, $albumlib) || die "Can't open directory $albumlib: $!\n";
my @newtrax = sort { "$albumlib/$b" <=> "$albumlib/$a" } grep { -f "$albumlib/$_" && /\.[Mm][Pp][3]$/ } readdir DH;
closedir(DH);
# loop thru array and add tracks to itunes
foreach my $newtrack (@newtrax) {
my $fullnewtrack = $albumlib . $newtrack;
my $return = &iTunes($fullnewtrack,$add_to_itunes_playlists,$add_to_itunes,$itunes_bookmarkable);
if ($return) {
print "track $fullnewtrack added to itunes\n";
}
}
} # end if for add to itunes
print "Done\n";
exit;
################### SUBS ###################
# for getting the basename of a file without using a module
sub basename {
my $file = shift;
$file =~ s!^(?:.*/)?(.+?)(?:\.[^.]*)?$!$1!;
return $file;
} # end sub basename
sub stripNonAlph {
my($text) = shift;
$text =~ s/([^0-9a-zA-z ])//g;
return ($text);
} # end sub strip non alph
sub replaceSpace {
my ($text) = shift;
$text =~ s/ /_/g;
return $text;
}
sub capWords {
my ($text) = shift;
$text =~ s/\b(\w)/\u$1/g;
return $text;
}
sub trim {
my $text = shift;
$text =~ s/^\s+//;
$text =~ s/\s+$//;
return $text;
}
sub stripCrap {
my $string = shift;
$string =~ s/\n//g;
$string =~ s/\r//g;
$string =~ s/\r\n//g;
$string =~ s/\012//g;
$string =~ s/\015//g;
$string =~ s/\012\015//g;
return($string);
}
sub id3v2tag {
# adds id3v2 tag to mp3s
my ($file,$title,$year,$author,$album,$genre,$encoder) = @_;
my $mp3 = MP3::Tag->new($file);
$mp3->get_tags();
$mp3->new_tag("ID3v2");
$mp3->{ID3v2}->add_frame("TALB", "$album");
$mp3->{ID3v2}->add_frame("TIT2", "$title");
$mp3->{ID3v2}->add_frame("TPE1", "$author");
$mp3->{ID3v2}->add_frame("TCON", "$genre");
$mp3->{ID3v2}->add_frame("TSSE", "$encoder");
$mp3->{ID3v2}->add_frame("TYER", "$year");
$mp3->{ID3v2}->write_tag;
$mp3->close();
} # end sub id3v2tag
sub promptUser {
#-------------------------------------------------------------------#
# two possible input arguments - $promptString, and $defaultValue #
# make the input arguments local variables. #
#-------------------------------------------------------------------#
my ($promptString,$defaultValue) = @_;
#-------------------------------------------------------------------#
# if there is a default value, use the first print statement; if #
# no default is provided, print the second string. #
#-------------------------------------------------------------------#
if ($defaultValue) {
print $promptString, "[", $defaultValue, "]: ";
} else {
print $promptString, ": ";
}
$| = 1; # force a flush after our print
$_ = ; # get the input from STDIN (presumably the keyboard)
#------------------------------------------------------------------#
# remove the newline character from the end of the input the user #
# gave us. #
#------------------------------------------------------------------#
chomp;
#-----------------------------------------------------------------#
# if we had a $default value, and the user gave us input, then #
# return the input; if we had a default, and they gave us no #
# no input, return the $defaultValue. #
# #
# if we did not have a default value, then just return whatever #
# the user gave us. if they just hit the key, #
# the calling routine will have to deal with that. #
#-----------------------------------------------------------------#
if ("$defaultValue") {
return $_ ? $_ : $defaultValue; # return $_ if it has a value
} else {
return $_;
}
} # end sub promptuser
#################################################################################################
################### ITUNES-SPECIFIC SUBROUTINES #####################
#################################################################################################
sub iTunes {
# handles vairous itunes tasks that will only work on a mac os x
my ($file,$add_to_itunes_playlists,$add_to_itunes,$itunes_bookmarkable) = @_;
# check that we're on Darwin and only do this stuff if we are
my $darwin = checkForDarwin();
if ( ($darwin) && ($add_to_itunes eq 'Yes') ) {
# if the files exists, add to itunes library
my $finder;
if (-e $file) {
$finder = addToiTunes($file);
}
# set the track to bookmarkable
if (($finder) && ($itunes_bookmarkable eq 'Yes')) {
my $result = setBookMarkable($finder);
}
# add it to a itunes playlist(s)
if ( ($add_to_itunes_playlists) && ($finder) ) {
# split the string into an array on commas (can contain multiple playlists)
my @playlists = split(/,/,$add_to_itunes_playlists);
# loop thru the playlists...
foreach my $itunes_playlist (@playlists) {
$itunes_playlist = trim(stripNonAlph($itunes_playlist));
# check if the pl exists
my $pl_check = checkPlaylistExists($itunes_playlist);
if ( (not($pl_check =~/^\d/)) || (not($pl_check >= 1 )) ) {
# create the playlist
$pl_check = createPlaylist($itunes_playlist);
} # end if for playlist does not exist
if (($pl_check =~/^\d/) && ($pl_check >=1 )) {
# check to make sure playlist(s) exist
my $return = addToPlaylist($finder,$itunes_playlist);
} # end if for playlist exists
} # end loop thru itunes playlists
} # end if for add to playlists
} # end if for is a mac and wants stuff added to lib
return 1;
} # end sub iTunes
sub checkForDarwin {
# checks that we're running darwin
my $darwin = `uname -s`;
$darwin = trim(stripNonAlph(lc($darwin)));
if ($darwin =~ /^darwin/) {
return 1;
} else {
return 0;
}
} # end sub for check for darwin
sub addToiTunes {
# adds the file to the itunes library
my $file = shift;
my $ref = `/usr/bin/osascript -e 'tell application "iTunes" to add POSIX file "$file"'`;
$ref = &stripNonAlph($ref);
sleep 2; # sleep so we can allow itunes to process is
return ($ref);
} # end sub addToiTunes
sub setBookMarkable {
# sets the track so it remembers its last playback position (set the bookmarkable flag to true)
my $finder = shift;
my $ref = `/usr/bin/osascript -e 'tell application "iTunes" to set bookmarkable of $finder to true'`;
$ref = &stripNonAlph($ref);
return ($ref);
} # end setBookMarkable
sub addToPlaylist {
# adds the file to a particular playlist
my ($finder,$playlist) = @_;
my $ref = `/usr/bin/osascript -e 'tell application "iTunes" to add (get location of $finder) to playlist "$playlist"'`;
$ref = &stripNonAlph($ref);
return ($ref);
} # end sub addtoplaylist
sub checkPlaylistExists {
# checks if the itunes playlist exists
my $playlist = shift;
$playlist = &stripNonAlph($playlist);
my $exists=`/usr/bin/osascript <<END
tell application "iTunes"
set myCount to 0
repeat with i from 1 to (the count of the playlists)
set this_playlist to playlist i
try
if the name of this_playlist is "$playlist" then
set myCount to myCount + 1
end if
end try
end repeat
return myCount
end tell
END`;
$exists = &stripNonAlph($exists);
return ($exists);
} # end check if playlist exist
sub createPlaylist {
# creates an itunes playlist
my $playlist = shift;
$playlist = &stripNonAlph($playlist);
my $newpl=`/usr/bin/osascript <<END
tell application "iTunes"
set myList to make new playlist
set name of myList to "$playlist"
return 1
end tell
END`;
$newpl = &stripNonAlph($newpl);
return ($newpl);
} # end sub createplaylist
sub checkForiTunes {
# check to see if itunes is installed
# going to work only on a mac of cours
my $installed=`/usr/bin/osascript <<END
tell application "Finder"
try
exists application file id "hook"
return 1
on error
return 0
end try
end tell
END`;
$installed = &stripNonAlph($installed);
return ($installed);
} # end sub check for itunes install
When the script is done running, the "output" folder on your desktop can be safely deleted, and the new tracks will be accessible in iTunes.


0 comments:
Post a Comment