I am still in the USA, visiting my colleague Kevin Vandecar in Goffstown, New Hampshire, returning to Europe on Tuesday.
Today we worked in his huge garden, loading a bunch of tree stumps on a truck with a tractor, then went for a swim in the beautiful Uncanoonuc Lake:
Back in February I mentioned using Songbird to play music on the Mac, extracting M3U playlists from it, and determining the playlist duration using ffmpeg and mutagen.
I encountered lots of other digital music related issues since then, and here are some notes on a few of them:
Here is a discussion of a topic important to me but completely unconnected to Revit and its API. I hope it is of use to other audiophiles as well. In fact, I would never dream of terming myself audiophile, and I seems to me that it should be of great interest to anybody using the MP3 format, and probably any other digital music format as well. As far as I can tell, that incudes just about everybody I know, and definitely everybody under 25 years of age.
I mentioned that I dabble with DJ-ing and listen to music, which is vastly more handy to manage in digital format, of course.
Until quite recently, I was storing all my music in the lossy compressed MP3 format.
I recently had a bad shock connected with that, though.
My friend Markus gave me a nice jazzy guitar and percussion recording in the lossless FLAC format.
I converted it to MP3 using the standard settings, and was surprised and pretty devastated to find that I could hear a significant difference in quality, just listening to the first few seconds of the original FLAC and the compressed MP3 on the built-in Mac loudspeakers.
This led to a visit to Markus, who stores all his music in FLAC and has the appropriate equipment to listen to it in high quality as well. Further experiments showed that I need to either abandon the MP3 format entirely for any high quality sound archival and listening, or pay a lot more attention to details that I had hitherto completely ignored.
I seriously considered giving up on the MP3 format altogether, which would be painful, since so far I have been converting everything I have to that format, and standardising all my tools to expect and handle that and nothing else, significantly simplifying scripts for creating and analysing playlists and such-like things.
On the other hand, the MP3 format does include options for handling levels of quality that are said to make the compressed version indistinguishable from the original, so I had hopes of making use of them to allow me to happily continue using this format after all.
It was harder than expected, though.
Below are a few things I learned on the way.
I would also like to recommend this old article from 2005 on variable bit rate and getting the best bang for your byte, including the numerous comments providing some interesting suggestions and lots of background information.
As far as I can tell, quite a number of MP3 files use constant bitrate encoding, CBR.
Many MP3 files are encoded using LAME.
Its documentation states that CBR is discouraged, and variable bitrate, VBR, should be used instead.
I tried directly using lame for music conversion, but it does not preserve the metadata tags, so I returned to ffmpeg instead anyway.
Still, reading the lame documentation helps understand which of the multitude of ffmpeg tags to use to control the conversion quality, and how.
Armed with these experiences, I tried converting from FLAC to MP3 using the lame -V 0 quality setting, which is termed 'insanely high quality' in the lame documentation.
I still hear a deterioration of clarity and brilliance, though.
This led me to look for further possibilities to force lame to globally maintain a specified minimum rate.
Happily, such a setting does exist using the -b option, and using it to request a minimum bitrate of 256 kbps finally succeeds in producing an output that I am unable to distinguish from the original.
Later, I noticed a deterioration converting certain M4A files to MP3.
I still have no perfect solution for this.
Some suggestions for LAME encoding arguments that I tried include:
-V9 --vbr-new -q0 -mj -b32 -F --lowpass 19.7 --nspsytune --cwlimit 10.7 --athaa-sensitivity 1 -V2 --vbr-new -q0 --lowpass 19.7 -b96 -V2 --vbr-new -q0 --lowpass 19.7 --cwlimit 10.7 --scale 0.99 -b96
My current solution includes comparing the original M4A file size with the resulting MP3 size, and looking at the bit rate and mode using mediainfo.
Here is the m4a2mp3
script that I am currently using, and often re-adapting:
#!/bin/bash # save and change IFS to allow spaces in filenames OLDIFS=$IFS IFS=$'\n' #for f in *.m4a ; do ffmpeg -i "$f" "$f".mp3 ; done #for f in *.m4a ; do ffmpeg -i "$f" -q:a 0 $(basename -s .m4a "$f").mp3 ; done #for f in *.m4a ; do ffmpeg -i "$f" -b:a 320k $(basename -s .m4a "$f").mp3 ; done #for f in *.m4a ; do ffmpeg -i "$f" -ab 300 $(basename -s .m4a "$f").mp3 ; done #for f in *.m4a ; do ffmpeg -i "$f" -q 0 $(basename -s .m4a "$f").mp3 ; done for f in *.m4a ; do ffmpeg -i "$f" -q 2 $(basename -s .m4a "$f").mp3 ; done # this does not preserve the tags: #for f in *.m4a ; do faad -o - "$f" | lame -V0 -b 256 - "${f%m4a}mp3" ; done # restore IFS IFS=$OLDIFS
Copy and paste the above to an editor or view the browser source code to see the truncated lines in full.
In the last few runs, I have simply changed the '-q 2' argument up or down a bit so that the resulting MP3 quality matches well with the original M4A.
I presented my Songbird M3U playlist extractor when I was still supporting only MP3 file format. I enhanced it to support FLAC as well, and now present the updated version.
I sometime use VirtualDJ to play music to dance to, since it provides the crossfade functionality lacking in Songbird.
I can feed it with an M3U playlist.
But...
The biggest gripe I have about Songbird is that it will not export its playlists to any sensible file format at all, in spite of being able to import M3U.
I searched for workarounds and saw many mentions of plugins for achieving this, but none of them worked for me.
There appears to be a lot of confusion among Songbird and its plugins regarding version compatibility.
I found a pretty easy workaround of my own, though, which I have been using successfully for some time now.
I can simply select all songs in a Songbird playlist and hit Cmd-C to copy them to the pasteboard.
If you select a single track in Songbird and copy and paste it to an editor, you will see that it places a comma delimited string containing the artist, album and track name on the pasteboard. Selecting and copying several tracks generates a linefeed-delimited list of these records, i.e. each track information on a separate line.
This information can be used to generate an M3U playlist file, provided the actual file path structure exactly matches the artist, album and track information read from the MP3 or FLAC music file tags.
Once that is all set up, I can use the following Python script songbird_to_m3u.py
to convert the information copied and pasted from Songbird to an M3U playlist referencing the files in my music library, which I can then import into VirtualDJ or any other player:
#!/usr/bin/env python # # songbird_to_m3u.py - convert the file tags exported # by songbird to m3u playlist # # Jeremy Tammik, Autodesk Inc., 2013-05-12 # # Artist, Album, Title --> # /m/Artist/Album/Track*Title.flac # /m/Artist/Album/Track*Title.mp3 # # cat songbird_export.txt | songbird_to_m3u.py > songbird_export.m3u # import glob, os, sys nOk = 0 nFailed = 0 while True: try: line = raw_input() except: break #print '>', line a = line.strip().split( ', ' ) if 3 != len(a): sys.stderr.write( line + ' - not 3 elements\n' ) nFailed += 1 continue ok = False p = '/m/' + a[0] + '/' + a[1] + '/*' + a[2] a1 = glob.glob( p + '.flac' ) if 1 == len(a1): print a1[0] ok = True else: a2 = glob.glob( p + '.mp3' ) if 1 == len(a2): print a2[0] ok = True if ok: nOk += 1 else: sys.stderr.write( '\n%s\n%s\nglob returned %d (flac) and %d (mp3)\n\n' % (line, p, len(a1), len(a2)) ) nFailed += 1 sys.stderr.write( '%s files passed, %s failed.\n' % (nOk, nFailed) )
Here is the content of the Mac pasteboard after copying a playlist in Songbird:
Gotan Project, La Revancha Del Tango, Chunga's Revenge Outback, Dance The Devil Away, Two In The Bush Marla Glen, Humanology, Fever Brent Lewis, Thunder Down Under, Outback Attack The Chemical Brothers, Hanna, Escape 700 Shakti, Natural Elements, Come On Baby Dance With Me Gabrielle Roth, Tribe, Tsunami 3 - chaos Talvin Singh, Oriental Club Disk 2, OK Martin O, Der mit der Stimme tanzt, Japaner Jazzbit, Unknown, Swingin man Yma Sumac, Ultra Lounge - Leopard Skin Fuzzy Sampler, Taki Rari Folco Orselli, Generi di Conforto, La ballata di Piazzale Maciachini Beatles, White Album CD 2, Good Night Natalie Dessay, Vocalises, Vocalise Büdi Siebert, Namaste, Lotus Call 1 Varja Guru Mantra Jeremy Tammik, Meditation, Stillness One Minute Silence Erik Truffaz, Mantis, Saisir
Running the songbird_to_m3u.py
script on that produces the following output, representing a valid M3U playlist on my file system:
/m/Gotan Project/La Revancha Del Tango/03 - Chunga's Revenge.mp3 /m/Outback/Dance The Devil Away/03 - Two In The Bush.mp3 /m/Marla Glen/Humanology/13 Fever.flac /m/Brent Lewis/Thunder Down Under/01 Outback Attack.mp3 /m/The Chemical Brothers/Hanna/02 Escape 700.mp3 /m/Shakti/Natural Elements/03 - Come On Baby Dance With Me.mp3 /m/Gabrielle Roth/Tribe/03 - Tsunami 3 - chaos.mp3 /m/Talvin Singh/Oriental Club Disk 2/13 - OK.mp3 /m/Martin O/Der mit der Stimme tanzt/07 - Japaner.mp3 /m/Jazzbit/Unknown/00 Swingin man.mp3 /m/Yma Sumac/Ultra Lounge - Leopard Skin Fuzzy Sampler/03 Taki Rari.mp3 /m/Folco Orselli/Generi di Conforto/04 - La ballata di Piazzale Maciachini.mp3 /m/Beatles/White Album CD 2/13 - Good Night.mp3 /m/Natalie Dessay/Vocalises/01 Vocalise.flac /m/Büdi Siebert/Namaste/04 Lotus Call 1 Varja Guru Mantra.mp3 /m/Jeremy Tammik/Meditation/Stillness One Minute Silence.mp3 /m/Erik Truffaz/Mantis/03 Saisir.flac
Copy and paste the above to an editor or view the browser source code to see the truncated lines in full.
Once I have an M3U playlist, I would also like to determine the total duration of all the tracks.
I presented my mp3duration.py Python script achieving that, which I now enhanced to handle FLAC files as well.
Here is the updated version:
#!/usr/bin/python # # mp3duration.py - retrieve the length of the tracks in # the playlist and calculate the total # # Jeremy Tammik, Autodesk Inc., 2013-05-12 # # /j/sh/mp3duration.py playlist.m3u # # http://code.google.com/p/mutagen/wiki/Tutorial # http://www.doughellmann.com/PyMOTW/subprocess # import glob, os, re, subprocess, sys import mutagen.mp3, mutagen.flac _find_duration = re.compile( '.*Duration: ([0-9:]+)', re.MULTILINE ) def min_sec_to_seconds( ms ): "Convert a minutes:seconds string representation to the appropriate time in seconds." a = ms.split(':') assert len( a ) in [1,2] if 1 == len(a): s = float(a[0]) else: s = float(a[0]) * 60 + float(a[1]) return s def seconds_to_min_sec( secs ): "Return a minutes:seconds string representation of the given number of seconds." mins = int(secs) / 60 secs = int(secs - (mins * 60)) return "%d:%02d" % (mins, secs) def retrieve_length( playlist_filename ): "Determine length of tracks listed in the given input files (e.g. playlists)." print playlist_filename + ' duration:' if not os.path.exists( playlist_filename ): print "Error: specified playlist '%s' does not exist.\n" % playlist_filename raise SystemExit(1) f = open( playlist_filename ) lines = f.readlines() f.close() total_count = 0 total_mutagen = 0.0 total_ffmpeg = 0.0 print '%8s%8s%8s %s' % ('mutagen', 'm:s', 'ffmpeg', 'track') for line in lines: path = line.strip() if not path or path[0] == '#': continue if not os.path.exists( path ): print "Error: specified music file '%s' does not exist.\n" % path raise SystemExit(2) if( path.endswith( 'mp3' ) ): audio = mutagen.mp3.MP3( path ) else: assert path.endswith( 'flac' ) audio = mutagen.flac.FLAC( path ) #print audio.info.length, audio.info.bitrate, path seconds = audio.info.length ffmpeg = subprocess.check_output( 'ffmpeg -i "%s"; exit 0' % path, shell = True, stderr = subprocess.STDOUT ) #>>> output = subprocess.check_output( #... 'echo to stdout; echo to stderr 1>&2; exit 0', shell=True) #to stderr #>>> print output #to stdout #print ffmpeg #exit( 1 ) #lines = ffmpeg.split( '\n' ) #ffmpeg -report -i path 2>&1 | awk '/Duration/{print $2}' match = _find_duration.search( ffmpeg ) if match: ffmpeg = match.group( 1 ) else: ffmpeg = '--' ffmpeg = ffmpeg.lstrip('0:') ffmpeg = min_sec_to_seconds( ffmpeg ) print '%8.1f%8s%8s %s' % (seconds, seconds_to_min_sec(seconds), seconds_to_min_sec(ffmpeg), path ) total_count += 1 total_mutagen += seconds total_ffmpeg += ffmpeg s = '-' * 6 print '%8s%8s%8s %s' % (s, s, s, s ) print '%8.1f%8s%8s total for %s tracks' % (total_mutagen, seconds_to_min_sec(total_mutagen), seconds_to_min_sec(total_ffmpeg), total_count ) def main(): "Determine length of tracks listed in the given input files (e.g. playlists)." for pattern in sys.argv[1:]: filelist = glob.glob( pattern ) for filename in filelist: retrieve_length( filename ) if __name__ == '__main__': main()
Here is the output generated by running it on a playlist containing both MP3 and FLAC files:
/j/audio/wave/ $ mp3duration.py LerchenWave-2013-05b.m3u LerchenWave-2013-05b.m3u duration: mutagen m:s ffmpeg track 304.2 5:04 5:04 /m/Gotan Project/La Revancha Del Tango/03 - Chunga's Revenge.mp3 314.9 5:14 5:14 /m/Outback/Dance The Devil Away/03 - Two In The Bush.mp3 244.0 4:04 4:04 /m/Marla Glen/Humanology/13 Fever.flac 315.7 5:15 5:18 /m/Brent Lewis/Thunder Down Under/01 Outback Attack.mp3 599.5 9:59 4:21 /m/The Chemical Brothers/Hanna/02 Escape 700.mp3 119.4 1:59 1:59 /m/Shakti/Natural Elements/03 - Come On Baby Dance With Me.mp3 344.3 5:44 5:44 /m/Gabrielle Roth/Tribe/03 - Tsunami 3 - chaos.mp3 255.5 4:15 4:15 /m/Talvin Singh/Oriental Club Disk 2/13 - OK.mp3 56.1 0:56 0:56 /m/Martin O/Der mit der Stimme tanzt/07 - Japaner.mp3 187.5 3:07 3:07 /m/Jazzbit/Unknown/00 Swingin man.mp3 112.0 1:51 1:52 /m/Yma Sumac/Ultra Lounge - Leopard Skin Fuzzy Sampler/03 Taki Rari.mp3 258.1 4:18 4:18 /m/Folco Orselli/Generi di Conforto/04 - La ballata di Piazzale Maciachini.mp3 191.8 3:11 3:11 /m/Beatles/White Album CD 2/13 - Good Night.mp3 323.9 5:23 5:23 /m/Natalie Dessay/Vocalises/01 Vocalise.flac 1036.4 17:16 7:31 /m/Büdi Siebert/Namaste/04 Lotus Call 1 Varja Guru Mantra.mp3 60.1 1:00 1:00 /m/Jeremy Tammik/Meditation/Stillness One Minute Silence.mp3 392.3 6:32 6:32 /m/Erik Truffaz/Mantis/03 Saisir.flac ------ ------ ------ ------ 5115.7 85:15 69:49 total for 17 tracks
That about covers my current digital music status.
I hope you can find some useful snippets in here.
Happy Sunday!