Lets face it, I’m way to lazy to write blog posts at the moment, so I’m just going to publish some python code that i wrote today.
It’s a program that runs aacgain on all the albums in your iTunes library.
MP3Gain analyzes and adjusts mp3 files so that they have the same volume.MP3Gain does not just do peak normalization, as many normalizers do. Instead, it does some statistical analysis to determine how loud the file actually sounds to the human ear.
Yes, I am tired of all my tracks not having the same output level. I’m looking at you AC/DC!
For one, I wanted to utilize both my cores, which means running aacgain in parallel. I also didn’t want to resort to shell scripting some recursing nastyness, so this is what I came up with.
Save it, change AACGAIN_PARAMS and go make coffee. It takes a while to process.
Oh yeah, it even detects the number of cpus you have, and makes sure to start the appropriate number of worker threads. Neet.
import os import subprocess import threading from Queue import Queue ITUNES_DIR = os.path.expanduser("~/Music/iTunes/iTunes Music/") KNOWN_EXTENSIONS = ('.mp3', '.aac') AACGAIN_PARAMS = ['-a', '-k', '-d', '3'] AACGAIN_LOCATION = 'aacgain' def get_files(): """ A generator that returns tuples with album name and a list of mp3 files. """ for root, dirs, files in os.walk(ITUNES_DIR): files = filter(lambda x: os.path.splitext(x)[1] in KNOWN_EXTENSIONS, files) if files: yield [os.path.join(root, f) for f in files] def get_num_cpus(): """ Returns the number of cpus in the machine, and 1 on failure. """ try: return int(subprocess.Popen(['sysctl', '-n', 'hw.ncpu'], stdout=subprocess.PIPE).communicate()[0].strip()) except: return 1 def run_aacgain(queue): """ Returns a callable that will be called inside a thread. """ def callable(): try: while True: files = queue.get() if files is None: break p = subprocess.Popen([AACGAIN_LOCATION] + AACGAIN_PARAMS + files) p.wait() except KeyboardInterrupt: pass return callable queue = Queue() workers = [] for i in range(get_num_cpus()): worker = threading.Thread(target=run_aacgain(queue)) worker.start() workers.append(worker) for album_files in get_files(): queue.put(album_files) for i in range(get_num_cpus()): queue.put(None)