Evan Desjardins

Doing Things the Easy Way (…the hard way) (Analyzing WAV files using Python, Power Menu and AppleScript)

Preamble

I’m really bad at programming but I won’t let that stop me. Today’s half-baked script is a “shortcut” for generating an Audio File Log, a data read-out requested by an audiobook client. It’s meant to accompany deliverables and tally up running times for billing. I made a game with QBASIC in high school so how hard can this be? I know just enough Python and shell commands to get myself into trouble. Let’s find trouble.

I try to keep learning by never learning. (credit: xkcd )

The client suggested simply dragging the deliverables from the file manager into a media player and taking screenshot of the resultant playlist. This manual process would probably take a minute tops, once per delivery. This is easy and according to the client, totally acceptable.

This works well but—if only there were some way to make this harder on myself…

Python & ChatGPT

Laugh all you want. I followed along ages ago with the most popular, most elementary Python books. I get a kick out of automating the boring stuff but since most of my time is spent doing my actual job I only dip my toes in from time to time. As a consequence I have a hard time retaining syntax and the endless list of modules. For a bozo like me, ChatGPT has been an absolute boon.

If I ask ChatGPT for help I know that I’ll need to tailor the output to my needs but still, I’ve saved myself hours and hours of sifting through search results, manuals and Stack Overflow by leaning on our AI overlords for help. Nobody needs to run my scrappy code except for me. If it works then that’s good enough for me.

Remember to ask nicely.

Key Libraries

By the grace of God, I came upon these handy modules that made my task a lot easier than it might have been:

  • soundfile – “The soundfile module can read and write sound files.”
  • openpyxl – “openpyxl is a Python library to read/write Excel 2010 xlsx/xlsm/xltx/xltm files.”

These were pretty simple to implement but I realized that I need to hit the books again if I’m really going to wrap my head about objects and classes. I sort of stumbled through this by trial and error.


I began writing a basic script to enumerate file durations using the wave module from the Standard Library…

def get_wav_duration(file_path):
    with wave.open(file_path, 'rb') as wav_file:
        frames = wav_file.getnframes()
        rate = wav_file.getframerate()
        duration = frames / float(rate)
        return duration

Then I asked my friend:

but my attempts to format a table in plaintext were pretty clumsy. I didn’t know how to accommodate for differing file name lengths.

Of course I sought help:

And the result was impressive:

That is, unless you needed to make any changes to the text:

I asked for help outputting a PDF instead.

I had a similar issue here. The method suggested by ChatGPT essentially had me drawing boxes and then drawing text on top of them. If the text didn’t fit within the box it would flow over the boundary. Pretty clumsy, and while I’m sure there was a right way to do it, hell if I was going to learn how. The PDF library was inscrutable to me. I’ve made several aborted attempts at learning LaTeX in the past and I could see this going the same way.

If there’s one program that’s good at making tables…

And with enough hand-holding to get the motor turning, I could see a way forward.

Now it seemed to me that having put that much effort in already, I might as well extend the script to be more useful:

If I included information on each file such as channel count, sample rate and bit depth then these log files could double as a final QC sanity check, making sure that my output conformed to the submission requirements. I wonder, could I also calculate waveform stats like integrated LUFS, true peak, LRA, etc? Maybe I could add more audiobook-specific QC—a go or no-go check on noise-floor level or digital silence. Another day perhaps.

But so anyway, we might as well pretty it up a little:

Not bad, I think. Good enough for now. I want to flesh it out of course—add a title above the table header, maybe some alternating row shading, conditional formatting, a company logo…

Convenience – Running from the Finder

That’s all well and good but if I want to execute my python script I would need to do so from the Terminal which is not optimal. Why waste precious seconds typing out commands and folder paths when I could spend several hours troubleshooting shell scripts instead?

Power Menu

Enter Power Menu, a tiny utility app from macOS developer FIPLAB which adds a customizable entry to the Finder’s context menu. One of the things you can get it to do is run a custom shell script.

Applescript

So now all I need to do is right-click inside my deliverables folder to initiate the following sequence of events using Applescript:

  • Check whether Terminal is open and open an instance if not;
  • Execute the python3 script in the Terminal;
  • Wait until the Terminal window is no longer busy;
  • When the script is finished (ie the window is no longer busy):
    • if the Terminal was not open to begin with, quit it.
    • if the terminal had been open, leave it open.
I get by with a little help from my friend.
tell application "System Events"
	set isRunning to (count (every process whose name is "Terminal")) > 0
end tell

set inputPath to quoted form of (system attribute "inputPath")
set scriptPath to "/Path/To/GenerateAudioLogFile.py"

tell application "Terminal"
	if not (exists window 1) then reopen
	activate
	delay 0.5
	do script "cd " & inputPath in window 1
	do script "python3 " & scriptPath in window 1
	
	repeat
		delay 0.5
		if not busy of window 1 then exit repeat
	end repeat
	
	if not isRunning then
		quit
	end if
end tell

There’s the final applescript that I’m triggering with Power Menu as seen in the screenshot above. There may be a more elegant way around this but it’s working for me!

Closing Thoughts

Programmers with knowledge and experience might turn their noses up at noobs like me leaning so hard on LLMs for support. Or they might not? Who cares, I guess. Today’s misadventure might have taken the better part of a day but on the other hand now I have a target for a more comprehensive pre-delivery QC check. Coded with state of the art artificial intelligence just like everything else these days.

In the immortal words of Walter Sobchak, “If you will it, it is no dream.” If you stuck with me to the end, I hope I’ve been able to teach you a thing or two about “saving time!”

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *