Wednesday, July 20, 2011

Making user settings available in cron

When using the unix command-line utility "cron" to run scripts at regular times, one potential headache is that cron will run the jobs as root. So, for example, environment variables defined in your .bashrc file will not be available to the script. There are probably a countable infinity of possible solutions to this problem; a simple one that works for me is to manually run the .bashrc file before the actual script, by calling bash with the "-c" flag. The line in your crontab might look like this:

    0 8 * * 1-5 bash -c '. /home/falloon/.bashrc; /home/falloon/Scripts/my_cron_job.py'

Transcendent Man (2010)

This quasi-autobiographic manifesto by "futurist" Ray Kurzweil expounds upon his ideas about the imminent "singularity" in which artifical intelligence will overtake humanity and profoundly change our way of life. It's a fascinating idea, but I find Kurzweil's efforts to justify his optimism quite unconvincing: simply extrapolating based on the exponential growth that has occurred thus far seems rather naive. Bacteria multiplying in a petri dish also enjoy exponential growth -- right up to the point at which they exhaust their available resources. Likewise stock prices in the run-up to a market crash.

There's something a little unseemly in his desire to resurrect his dead father (via an A.I. simulation), and also prolong his own life indefinitely. He claims that people who accept the "tragedy" of their own mortality are "kidding themselves". Personally, I don't agree with this view: I enjoy being alive but I also recognize that, in the cosmic scheme of things, my own existence is not especially important and no more worth preserving than those of the billions of other human beings that live or have lived. To think otherwise, even if you are a particularly smart guy like Kurweil, seems to be lacking humility.

Kurzweil's fetish for perpetuating his existence leads him to ingest, daily, a cocktail of 200 or so "life-preserving" pills (indeed, through the film he always seems to be slipping one into his mouth). He can afford this -- he's got the money, he can do what he wants -- but I can't help wonder if the money and effort would be better spent on simple tried-and-true measures (clean drinking water, immunisation against diseases that afflict developing countries) that would save lives of those less fortunate than himself. It would be a strange set of scales that could confidently weigh all those lives more lightly than his own.

Bottom line: raises some interesting, thought-provoking ideas; but over-reaches in its predictions.

Monday, July 18, 2011

Scripting Madness

I've recently taken the plunge and decided to dive into the geeky world of AppleScript. I had a couple of specific ideas for useful programs to automate some tedious tasks that I regularly perform in iTunes, so it seemed like as good a time as ever to have a go. The following is a brief summary of the two scripts I have so far produced and some of the things I learned while writing them.
Generalities
I won't purport to turn this into a general purpose AppleScript lesson (which I'm far from qualified to provide at this point), but in case any other noobs out there want to follow in my footsteps, here is a basic orientation.
AppleScript is a scripting language that comes bundled as standard with the Mac OSX operating system. It allows the creation of scripts that can tell OSX applications to do various things. Although not a very powerful language, it has a fairly intuitive object-oriented model and natural language syntax which makes it quite easy to use. Only some applications are "scriptable"; the current list includes the Finder, iTunes, iCal, Mail and some others.
The Script Editor application (in the Applications > AppleScript folder) provides a fairly decent IDE that more than serves the needs of the basic scripter. The syntax highlighting and automatic formatting makes editing and debugging quite pleasant.
One of the fundamental constructs is the "tell" block, which is used to direct commands at particular applications. So in my programs, all the action occurs with an overarching tell block of the form:
tell application "iTunes"
-- code here
end tell
(by the way, "--" is used to denote comments.)
Note that "tell" blocks are not scope-limiting, so variables defined inside them will be available after exiting. They can also be nested (as in the nested "iTunes" and "Finder" blocks in my second script below).
Deploying scripts
There are several ways to save and use scripts. The simplest is to save it as a "script" (extension .scpt). Note that this is not a plain text file so, for example, to use the scripts I list below, you'd need to paste them into a new script in Script Editor and save. Alternatively, you can save it as an "application" (extension .app).
To make scripts available within iTunes, create (if it there already) the folder "~/Library/iTunes/Scripts/", and copy your .scpt or .app file there. You will notice a new menu appears in iTunes, containing the scripts you have put in that folder.
Note also that, within iTunes at least, .scpt and .app versions are processed in a slightly different way. There are some cases where it seems to be necessary to use the .app version to get the expected behaviour. So when in doubt, try saving as an .app.
iTunes gotchas
As intuitive and easy as coding in AppleScript is, it can also be the source of some infuriating "gotchas" that can only really be debugged in a haphazard, trial-and-error fashion.
In working with iTunes, one particular issue I've encountered repeatedly is that when you perform an operation which involves copying a file (e.g. importing into iTunes, copying to an iPod) the script doesn't wait for the process to finish; instead, it blithely skips on to the next command. This can cause problems if your next line happens to rely on that file being there (e.g. you want to modify the track info of the newly-copied file). The most effective way I could see to get around this was to use a "try" block inside a "repeat" loop. In other words, keep trying to perform the operation until it works. The basic construct is:
repeat until <desired operation has been performed>
try
<attempt to perform desired operation>
end try
end repeat
Script #1: create album playlists on an iPod Shuffle
I recently purchased a new iPod Shuffle, which has two nice features: the ability to organize music into playlists, and a "VoiceOver" function that can read out the current track/artist or playlist. There is, however, no way to change between individual albums, so I have found it useful to create playlists for each album.
Unfortunately, manually creating playlists for each album becomes quite tedious, particularly when you're changing the contents of the iPod at least once a week. So this script was written primarily to automate that process. It works by assuming that you've loaded all the desired albums onto the iPod; it then deletes any existing playlists before stepping through each track, creating a playlist for each new album it encounters and copying the track into it.
Added niceties that I added later were the ability to recognize multi-disc albums and create separate playlists for each disc, and the option to insert a prefix into the track name giving the track number in the form "n of N". This last feature is very handy when you want to know where you are in an album while listening to it.
So here, without any further garnish, is the script:
set iPodName to "Gekko MMXI"
set addTrackNumberPrefix to true
tell application "iTunes"
-- get index of ipod source
set myIPod to some source whose kind is iPod and name is iPodName
-- STEP 1: delete existing playlists
delete (every user playlist of myIPod whose smart is false and special kind is none)
--- STEP 2: loop thru tracks on ipod, assigning to new playlist according to artist and album
repeat with trk in tracks of myIPod
-- only consider tracks with a non-empty artist and album
if artist of trk is not "" and album of trk is not "" then
-- get album artist, using the album artist if it's defined
if album artist of trk is not equal to "" then
set albumArtist to (album artist of trk as string)
else
set albumArtist to (artist of trk as string)
end if
-- get album name, appending disc number if defined
if disc number of trk is not equal to 0 then
set albumName to (album of trk as string) & " (disc " & (disc number of trk as string) & ")"
else
set albumName to (album of trk as string)
end if
set listName to albumArtist & " - " & albumName
-- get playlist, creating it if it doesn't exist
if exists (some user playlist of myIPod whose name is listName) then
set plist to (some user playlist of myIPod whose name is listName)
else
set plist to (make user playlist of myIPod with properties {name:listName})
end if
-- copy track to playlist
set ipod_trk to (duplicate trk to plist)
-- add track number to track name
if addTrackNumberPrefix then
set trackNumber to track number of trk
set trackCount to track count of trk
if (trackNumber as string) is not "" and (trackCount as string) is not "" then
set numPrefix to ((trackNumber as string) & " of " & (trackCount as string) & ". ")
if (name of trk as string) does not start with numPrefix then
set name of trk to (numPrefix & (name of trk as string))
end if
end if
end if
end if
end repeat
end tell
Script #2: add ear-training exercises to iTunes and copy to iPod Shuffle
This one is slightly more esoteric but demonstrates some useful actions involving importing files into iTunes. The basic purpose of this script is to import audio files (in this case, ear training mp3s whose creation I described in a previous post), add some track info, and copy into playlists on the iPod Shuffle. You'll notice copious use of the repeat/try trick that I mentioned above. Note also the multiple "tell" blocks: in addition to the main iTunes block, there are several calls to the Finder to get directory/file lists.
set iPodName to "Gekko MMXI"
set fileFormat to "MPEG-4 audio"
set myLabel to "My Ear Training"
-- delete existing ear training tracks from ipod
tell application "iTunes"
-- get index of ipod source
set myIPod to (some source whose kind is iPod and name is iPodName)
-- delete existing ear training tracks
delete (tracks of myIPod whose artist is myLabel and genre is myLabel)
delete (tracks of library playlist 1 whose artist is myLabel and genre is myLabel)
end tell
-- import new files into itunes and copy to ipod
tell application "Finder"
-- get folders of each group of tests
set sourceDirs to folders of folder "EarTraining" of home
end tell
tell application "iTunes"
-- loop over test folders
repeat with sourceDir in sourceDirs
set sourceName to name of sourceDir
set listName to (myLabel & " - " & sourceName)
-- ensure that ipod has a playlist with this name
if not (exists (some user playlist of myIPod whose name is listName)) then
set plist to (make user playlist of myIPod with properties {name:listName})
else
set plist to (some user playlist of myIPod whose name is listName)
end if
-- get source files in this directory
tell application "Finder"
set sourceFiles to (files of sourceDir whose kind is fileFormat)
end tell
-- get number of tracks in this directory
set trackCount to count of sourceFiles
-- import each file into iTunes and set info
set trackNumber to 0
repeat with sourceFile in sourceFiles
set trackNumber to trackNumber + 1
set trackName to (text 1 thru -5 of (name of sourceFile as string))
-- import track into itunes
with timeout of 30 seconds
set trk to (add alias (sourceFile as string) to library playlist 1)
end timeout
-- add track info: use "try" inside a "repeat" loop to overcome the flaky behaviour which occasionally denies write permission to file
repeat while album of trk is not equal to (name of sourceDir as string)
try
set album of trk to (name of sourceDir)
end try
end repeat
repeat while artist of trk is not equal to myLabel
try
set artist of trk to myLabel
end try
end repeat
repeat while genre of trk is not equal to myLabel
try
set genre of trk to myLabel
end try
end repeat
repeat while track count of trk is not equal to trackCount
try
set track count of trk to trackCount
end try
end repeat
repeat while track number of trk is not equal to trackNumber
try
set track number of trk to trackNumber
end try
end repeat
-- finally, explicitly set the track name to the file name, since iTunes will append a counter to it if the same file has been imported multiple times (do this after the previous steps because the change only occurs when the track finished loading)
repeat while name of trk is not equal to trackName
try
set name of trk to trackName
end try
end repeat
set ipod_trk to (duplicate trk to library playlist 1 of myIPod)
-- need a repeated try loop here as the copy takes some time to complete
repeat until exists (some track of plist whose name is trackName)
try
duplicate ipod_trk to plist
end try
end repeat
end repeat
end repeat
end tell

Recent viewing activity

Haved watched a few things over the past couple of weeks, but nothing that inspired me enough to break out a dedicated post. So, in precis form:

Purple Rain: dated but enjoyable early 80s flick starring Prince.

Bachelor Party: early 80s comedy romp featuring a young Tom Hanks. The title pretty much says it all.

The Baader Meinhof Complex: very good film about the left-wing terrorist movement from the 70s. I don't know as much I probably should about this period in history.

Archer - Season Two: another highly entertaining series of escapades featuring the ISIS intelligence agency. I hope, and expect, that there'll be a Season 3.

Cosmos: recently wrapped up this 13-episode series by the late physicist and science communicator par excellence Carl Sagan. It was a little slow in parts, andome of the re-enactments are dated and a little tedious, But Sagan's enthusiasm and passion for science shine through, and the show is still well worth watching.

Sunday, July 10, 2011

Ear-Training exercises for the iPod Shuffle

I recently had what seemed like a neat idea (to me, at least): wouldn't it be cool if I could create a bunch of mp3 tracks that would allow me to practise ear-training exercises while on the bus, in the park, or even (why not?) at my desk during a quiet spell at work.


The background

The type of thing I had in mind would play a set of musical examples of a given kind (e.g. musical intervals, chords, scales, arpeggios); after each one, it would pause for a few seconds before announcing the correct name (e.g. "major third", "perfect fifth", "minor seventh chord" etc). Each track could contain on the order of 10 to 20 such examples.

Encouraged by the results of my previous efforts to develop an interactive ear-training program within Mathematica (published as a Demonstration here), I suspected that it could serve as a starting point for this new project.

Just to quickly review, here is a rough mock-up of how Mathematica can generate a MIDI object corresponding to a simple interval (in this case a perfect fifth), played in ascending order:

In[1]:= Sound[{SoundNote["C", {0,1}], SoundNote["G", {1,2}]}]

(To actually hear this within Mathematica, you need to pass thie object to the EmitSound function.)

It's also possible to sound multiple notes simultaneously; here is a C Major triad:

In[2]:= Sound[{SoundNote[{"C","E","G"}, {0,1}]}]

With these basic building blocks, and Mathematica's powerful symbolic programming capabilities, it is easy enough to generate all sorts of musical examples with randomized features, varying pitches, instrument sound, and so on.


We have ways of making you talk, OSX

The other piece of the puzzle, automated speech generation, fell into place when I stumbled, quite by accident, on the speech capabilities of Mac OSX. The "say" function on the command line (i.e. in the "Terminal" App) allows you to tell Mac OSX to say something. For example:

> say "fitter, happier, and more productive"

You can choose from a number of built-in voices (go to System Prefs to see complete set):

> say -v Alex "fitter, happier, and more productive"

And, importantly for my purposes, you can tell it to save the output as a sound file (in the AIFF format):

> say -v Alex -o sound_bite.aiff "fitter, happier, and more productive"


So you can see the building blocks are all there: Mathematica's midi capabilities to generate musical sounds (intervals, chords, scales, arpeggios, etc -- whatever I am trying to recognize by ear), and the command-line "say" function to generate spoken fragments (to provide the answers to each example, after a suitable pause).

As an aside, it's probably worth noting that you can access the same speech functionality as "say" (it's an OSX-wide thing), using the Speak function:

In[1]:= Speak["fitter, happier, and more productive"]

However, there doesn't seem to be any way to "capture" the output of this function in a way that allows it to be combined with other sounds, so although this could be useful for a program running within a Mathematica session, it wouldn't be any help for generating mp3 files.


Converting MIDI to AIFF

My initial expectation was that combining the above ingredients would be fairly straightforward and have me on the road with my new mp3 test collection in no time. Unfortunately, as I started playing around I discovered a major obstacle: although you can combine formats such as MIDI, WAVE and AIFF within Mathematica, it is not possible to export these into a single file. Furthermore, Mathematica doesn't handle compressed formats like MP3 and AAC.

Now, you'd think that converting MIDI to WAVE or AIFF (or any format for that matter) should be quite a simple affair in this day and age, but that doesn't seem to be the case. Despondent, I began casting around for a workaround of any description. The best I could find was a shareware command-line program "midi2mp3" (available here). It's easy to download and use and, fortunately, it runs for free provided your sound file is no longer than 60 seconds (which doesn't bother me at all, since my individual musical examples are never more than a few seconds in duration). So, I could use midi2mp3 to convert to WAVE and then import that back into Mathematica.


Converting AIFF to AAC

There was just one remaining step: once Mathematica had created the final complete sound file, it could only be exported in AIFF or WAV format. I needed another external utility to convert this into MP3 or AAC ready for easy import into iTunes. Thankfully, this time there was a built-in OSX command-line function, "afconvert", that could do the trick.


Putting the pieces together

I now had all the pieces I needed to achieve my goal. It had been a long, dark, and at times almost unbearably geeky journey, but I was on the home straight.

So, without further ado, here is the complete solution that I ended up with (again, I'll just show a mock-up which demonstrates the key idea):

1. Generate midi sound in Mathematica, and export it as a midi object:

In[1]:= Export["sound.mid", Sound[{Sound["C", {0,1}], Sound["G", {1,2}]}]]

2. Use an external converter to get this into WAV format (here I'm calling it from with Mathematica):

In[2]:= Run["midi2mp3 sound.mid -e wave"]; mySound = Import["sound.mid.wav"]

3. Get speech fragment:

In[3]:= Run["say -v Alex -o speech.aiff \"the correct answer is perfect fifth\""]; myAnswer = Import["speech.aiff"]

4. Combine the two pieces and export in AIFF format:

In[4]:= Export["test.aiff", Sound[{mySound, myAnswer}]

5. Convert from AIFF to AAC (I had to poke around the net to find out how to use this function -- converting to MP3 instead of AAC is similar):

In[5]:= Run["afconvert -f 'm4af' -d 'aac' -b 98304 \"test.aiff\""]

The end result should be a file called "test.m4a" containing our simple interval followed by its name spoken.


Conclusion

So after a fair amount of pfaffing around, I have been able to generate sets of ear-training tests to listen to on my iPod. I've been using them for a couple of weeks now and so far they're working a treat. At first I found it difficult to recognize intervals whenever I'm unable to sing it out loud to myself (for obvious reasons, this simply isn't feasible on the bus or in the office) and being unable to replay it multiple times (rewinding on the iPod Shuffle is too clunky to be useful; however, I can hit pause to give myself more time to think). But I'm getting better at that with more practice.

Note that in the above, although I had recourse to several external scripts (midi2mp3, afconvert, say), I was still able to use Mathematica as the framework to bring them all together. This is very convenient, since it makes it possible to automate the process to a high degree.

Finally, I've glossed over many details (e.g. putting a pause of desired length between sound and answer; randomly choosing pitches, intervals; etc) but these were all fairly routine once the above steps had been worked out.

Check back again soon as I will endeavour to post a couple of sample files very soon.

EDIT: I've uploaded a couple of examples here.

Carbon freeze: new tax leaves coal industry out in the cold?

I couldn't resist a chuckle upon reading in today's Australian that the coal industry "has slammed the Gillard government's carbon tax, arguing the details announced yesterday would see the sector pay $18bn over the next nine years, with minimal assistance compared with other industries".

It's a carbon tax, stupid! I'm no chemist, but even I know that coal is basically pure carbon, which is dug up for the express purpose of being burnt and thereby released into the atmosphere. So it wouldn't be much of a tax if it didn't hit the coal industry harder than others, would it?

Of course there will be aspects of the putative carbon tax that need to be debated and, hopefully, hammered out swiftly. But listening to the bleatings of the fossil fuel lobby on this issue seems akin to asking a serial killer whether he supports the death penalty. I guess serial killers just need a stronger lobby group.

Monday, July 4, 2011

What do you want to do today?

Another 4HWW goodie:

For the past 33 years, I have looked in the mirror every morning and asked myself: "If today were the last day of my life, would I want to do what I am about to do today?" And whenever the answer has been "No" for too many days in a row, I know I need to change something . . . almost everything—all external expectations, all pride, all fear of embarrassment or failure—these things just fall away in the face of death, leaving only what is truly important. Remembering that you are going to die is the best way I know to avoid the trap of thinking you have something to lose. —STEVE JOBS, college dropout and CEO of Apple Computer, Stanford University Commencement, 200578

Sunday, July 3, 2011

The parable of the Mexican fisherman

A nice one from "The 4 Hour Workweek":

An American businessman took a vacation to a small coastal Mexican village on doctor's orders. Unable to sleep after an urgent phone call from the office the first morning, he walked out to the pier to clear his head. A small boat with just one fisherman had docked, and inside the boat were several large yellowfin tuna. The American complimented the Mexican on the quality of his fish. "How long did it take you to catch them?" the American asked. 

"Only a little while," the Mexican replied in surprisingly good English.

"Why don't you stay out longer and catch more fish?" the American then asked.

"I have enough to support my family and give a few to friends," the Mexican said as he unloaded them into a basket.

"But... What do you do with the rest of your time?"

The Mexican looked up and smiled. "I sleep late, fish a little, play with my children, take a siesta with my wife, Julia, and stroll into the village each evening, where I sip wine and play guitar with my amigos. I have a full and busy life, senor."

The American laughed and stood tall. "Sir, I'm a Harvard M.B.A. and can help you. You should spend more time fishing, and with the proceeds, buy a bigger boat. In no time, you could buy several boats with the increased haul. Eventually, you would have a fleet of fishing boats." He continued, "Instead of selling your catch to a middleman, you would sell directly to the consumers, eventually opening your own cannery. You would control the product, processing, and distribution. You would need to leave this small coastal fishing village, of course, and move to Mexico City, then to Los Angeles, and eventually New York City, where you could run your expanding enterprise with proper management."

The Mexican fisherman asked, "But, sefior, how long will all this take?"

To which the American replied, "15-20 years. 25 tops."

"But what then, senor?"

The American laughed and said, "That's the best part. When the time is right, you would announce an IPO and sell your company stock to the public and become very rich. You would make millions."

"Millions, senor? Then what?"

"Then you would retire and move to a small coastal fishing village, where you would sleep late, fish a little, play with your kids, take a siesta with your wife, and stroll to the village in the evenings where you could sip wine and play your guitar with your amigos ..."

Amen.

Friday, July 1, 2011

Precious (2009)

A bleak, harrowing but excellent film. 

Set in Harlem in the 80s, it tells the story of "Precious" a (morbidly) obese teenage Aftrican-American girl whose family life is dysfunctional, to say the least. She has two illegitimate children (one with Down's Syndrome), both the product of being raped by her father. She lives with her housebound mother who is domineering and violent. Despite this, Precious is determined to acquire a basic education and be able to raise her children.

It's a grim, you might say unrealistically grim, storyline: it's hard to see how life could have conspired to be any worse for Precious. Still, it shines a spotlight on issues of entrenched poverty and welfare in a way that few, if any, films that I've seen before have done.

The performances are excellent throughout, particularly Gabourey Sidibe in the lead role of Precious and Mo'Nique as her mother. Mariah Carey has a surprisingly convincing turn as a concerned social worker, and Paula Patton catches the eye as a teacher in the special school that Precious attends.

Bottom Line: recommended viewing, but not one for a romantic first date.