Dear Reader,
I was reading The New Fatherhood recently and came across this brilliant video1:
The whole thing is definitely worth 2 minutes of your time but I was particularly taken by this quote:
“Everybody I know who does interesting, creative work, they went through years where they had really good taste and they could tell that what they were making wasn't as good as they wanted it to be. They knew it fell short. Everybody goes through that.
The most important thing you can do is do a lot of work. Do a huge volume of work. Put yourself on a deadline so that every week or every month you know you're going to finish one story. It is only by going through a volume of work that you're going to catch up and close that gap, and the work you're making will be as good as your ambitions.”
I’ve always been a big proponent of the “real artists ship” mentality but sometimes you have to take a step back and say “this isn’t ready yet”. So it is with Music Library Tracker v2.0 which I was hoping to have launched yesterday. Instead it’ll be launching in early February.
Today’s issue will be shorter than usual2 as I’ve been in crunch mode to get the app update finished but I hope you’ll find something of interest. I look forward to hearing any feedback you may have.
— Ben
Contents
An update on Music Library Tracker v2.0
Moving to Mastodon
Questions
Recommended Links
Roadmap
An update on Music Library Tracker v2.0
As mentioned in the introduction, I’ve been hard at work on Music Library Tracker v2.0 and pushed out a couple of updates on TestFlight at the start of the week. I still have some UI tidying I want to do and one extra feature I want to add but for now I thought I’d do a deep dive into some of the new features added in the past 2 weeks.
Speed Improvements
The main draw of the app is that it can detect changes in your music library. This is not done via some fancy API though; it’s a very blunt comparison check3. The app goes through a 4 step process:
Fetching: The media library is retrieved using MPMediaLibrary and stored in a local database
Changing: I check each track to see if it was previously stored in the database; if it was, then I check each piece of metadata to see if it changed; if it wasn’t, then I add it as a new track.
Deleting: I then do the reverse of the “changing” step by checking if any tracks were present in the database previously but are not in the latest fetch indicating that they have been deleted.
Writing: The generated logs for all of the changes are written to the database along with the latest fetch information so when this runs again I can see the fresh changes.
One key part of this process is that it can run in the background. To do this, the app is sent a silent push notification every hour which wakes the app up in the background and starts this process. Apps that are woken like this only have 30 seconds to do their work and have significantly less system resources available to them which means this entire process is roughly 5-10x slower. Some users have huge libraries (i.e. over 100k songs) so this process would quickly stop working in the background if it weren’t for the batch system I’ve created which means that data is saved at each step after every 500 iterations and can then be resumed if need be. For example, lets say in the fetching phase I’m churning through 80k tracks; after I’ve fetched 500, I save this data locally before moving to the next 500 and so on. After each batch, I can check how much time is left4 and then make a decision as to whether I want to try for another batch or abandon early.
It turns out that with my library of around 8k tracks, the fetching phase was by far the slowest taking up 4.5 seconds of the overall 4.7 seconds required when running in the foreground.
Previously I would use item.value(forProperty:)
to get each piece of metadata but when working on the Spatial Audio features last month I noticed I could just access them directly via properties i.e. using item.title
instead of item.value(forProperty: MPMediaItemPropertyTitle)
.
This led to some marginal gains that were within the margins of error so I took another look at the documentation and found there was a function named enumerateValues(forProperties:using:)
which Apple describes as:
Anytime the app accesses more than one property, enumerating over a set of property keys is more efficient than fetching each individual property
It looks like I’d tried this previously as I had some commented out code using this feature. I decided to rewrite it and found it was crashing the app. Hmm. This is likely why it was commented out 🤣
It turns out that the feature is broken due to the bridge between Objective-C and Swift. Luckily Stephen Heaps had put together a fix in Objective-C so all I had to do was import that via a bridging header and use the fixed method.
The speed improvements were dramatic. What was previously taking 4.5 seconds was now taking 0.6 seconds. An almost 10x speed boost! This is especially important in the background as it now means the entire process can complete in less than a 30-second window meaning the tracking happens much more reliably. For my small library this means I get a full 24 scans per day but for those with huge libraries this should still mean the entire library gets done daily which is the key thing.
Background Spatial Audio
With the scanning engine dramatically sped up, it gave me the headroom required to think about how I could check for Spatial Audio in the background. Back in Issue #1 I’d said:
As Music Library Tracker is a one time purchase it isn’t feasible to allow for checking to happen in the background all the time in the same way it does for checking your local media as a) it is likely too slow given the number of network requests and b) all the bandwidth usage literally costs me money so I’d end up paying more than people paid for the app (especially if they bought it on sale 6 years ago when it first launched). I don’t really want to do subscription pricing for this sort of thing so I think the only option is to have the check be manual from within the app and perhaps limited to once per day. That way you can go and see what songs have been updated when you think to do it and there isn’t wasted bandwidth being used in the background by every user. Perhaps in the future I could look to adding a cheap subscription for those users who do want to have automatic notifications but that’s definitely for much further in the future.
I’m happy to say that I’ve been able to get it all working without the need for any subscriptions; it’s free for every user of the app!
The first thing to do was to add a new spatialAudio step between fetching and changing. This fetches a batch of 2500 ids from the local database and uploads them to my server for checking, exactly as described in Issue #2. With the response, I then check against the local track stored in the database and update it with the new value so that when the changing step happens it will notice that is now or now not a Spatial Audio track. At the same point in the changing step, if a track is found to now be Spatial Audio a record is created so that it appears in the Spatial Audio tab (and similarly removed if the track is no longer in Spatial Audio).
This raised a problem though. During the changing step I no longer have access to the MPMediaItem which is retrieved during fetching but I need this in order to add the track to the Apple Music playlist. When this feature was only in the foreground it was trivial for me to just fetch all the tracks again and store them in memory but this won’t work in the background without doubling the time the process takes. Instead, I took another look at the documentation and found there is a method named addItem(withProductID:) which looked as though it would work as I do have the id at this point in the process.
It does work but it adds another minor headache. Whereas adding an item with an MPMediaItem is practically instant (as it’s done locally on the device), adding an item via it’s product ID seems to make a network request to the Apple Music API to add the track. This means it’s asynchronous and not instant. Thankfully everything seems to work even if I throw 6k ids at it in the space of a few seconds but it can take up to half an hour before those tracks are fully ready in the playlist.
In the end I decided this was a trade-off worth having as in most instances you are not going to be cognisant of the fact that a track has been added until several hours after the fact as I only tell you about changes that happened yesterday (thus the track may start trying to add at 00.30, be added a few minutes later, and then you’ll get a notification the following day at around 08.00).
The final step in adding Spatial Audio to the background scanner was to ensure that any songs that weren’t in my database were uploaded. To do this, I add the identifiers of missing tracks to the local database and then upload them in batches of 5000 in a new step between spatialAudio and checking. Again, the whole process is done in batches so it can be terminated and resumed at a moments notice.
Whilst I haven’t added limits to this feature when running in the foreground, I have set this up so that Spatial Audio is only checked once per day when running in the background. This prevents thousands of requests hitting the server every hour and means you’ll still get notified when audio in your library is upgraded without you needing to open the app every day.
Spatial Audio Privacy
One thing I’m very aware of is that the new Spatial Audio features add an element of privacy invasion that I only feel comfortable doing with express permission. Specifically, the app sends the identifiers of your tracks to a remote server in order to match which ones are upgraded or not. Whilst I do not get any personally identifiable information and do not store the ids longer than it takes to serve the request, it is still important to me that the user opts-in as it is not impossible to fingerprint a user from a hash of their library identifiers.
To solve that, I have built what is hopefully a very clear screen indicating that the user needs to opt-in to Spatial Audio scanning along with a link to my privacy policy. The user can also opt-out from the settings page at any time:
I haven’t yet added this to the onboarding flow as that is part of the UI I want to redesign before launch. As you can see in the screenshots above, I’ve also added a prompt for whether the user wants to enable the automatic playlist within Apple Music.
Ch-ch-ch-changes
As I eluded to above, Spatial Audio is now listed as a metadata change which means it will show up as a change alongside things like title, album, artist, etc. This means it will also tell you when a track has Spatial Audio removed, something I mentioned as a potential problem in Issue #3. I’ve also added support for track number and disc number and I’ve stopped minor diacratic alterations from showing up as changes (so a genre updatin from Electrónica to Electronica will no longer show as a change).
Other minor updates
When you choose to “Regenerate Apple Music Playlist” within settings, the app will now open the Apple Music app and take you to the Library tab. It felt like something needed to happen once the loading box had closed! I wanted this to go straight to the playlist but that isn’t possible unfortunately.
You may remember I mentioned that the playlist was being created with the author name as “MusicLog”. For some reason I completely missed that there was an
authorDisplayName
property onMPMediaPlaylistCreationMetadata
🤦🏻♂️. There was also adescriptionText
property so now the playlist has a description as well!I fixed an issue with sort filters not working properly within the “Deleted” section
I moved the settings page out of the tab bar and put it in the top left hand corner. The reason for this is that I needed to remove the Spatial Audio tab from macOS (as mentioned previously) and so I thought it better just to remove the tab bar entirely in that circumstance.
10x faster scanning, Spatial Audio in the background, privacy improvements, more metadata tracking, and some other fixes. I think that’ll do for now!
The last few things I want to sort before launch are UI improvements across the board (especially on iPad which is in sore need of some attention) and adding a new notification for Spatial Audio. At the moment you get notifications when tracks are changed but I’d like to add a separate notification that can say something like “3 tracks have been upgraded to Spatial Audio” as I suspect there will be some users who don’t care about other changes to their libraries.
I hope and expect to get that all finished early next week so it can be submitted. Then I can begin on a campaign of promotion to try and get it in front of as many reviewers as possible for a big launch on the 13th February.
If you have not yet tried out the update, you can use the button below to download the beta for free.
* Music Library Tracker v2.0 will run on any iOS device running iOS 13 or greater. If you already have Music Library Tracker installed then this will update that version; you won’t be able to downgrade. This beta will expire when the app update is publicly released.
I would greatly appreciate any feedback you may have. You can always comment on this post, provide feedback directly through TestFlight, or email me via ben@bendodson.com.
Also, feel free to publicly share any screenshots, videos, or thoughts on the app update (although I’d obviously prefer you share any issues you encounter with me first!)
Moving to Mastodon
I’ve mostly steered clear of any of the discussion around Twitter and it’s new direction as I mostly assumed things would stick to the status quo. Whilst I’ve noticed over the last few months that things tend to get less engagement there due to the algorithm (one of the factors in starting this newsletter in fact), it’s recently been shown that the infrastructure can’t keep up with new tweets:
It’s worse than that, though, because if you were delivered a newspaper with random stories scissored out, you’d know that there were missing stories. You wouldn’t know what they were, but you’d see the gaping holes in the paper. With Twitter now, there’s no indication that you’re missing tweets — let alone a huge number of tweets. And to be clear, Frum is talking about the “Following” timeline, not the “For You” algorithmic timeline.
This means that I’m missing things that people I follow are posting and when I tweet I may as well be shouting into a void. They also cut off the 3rd party Twitter client I was using so that along with the recent release of Ivory has led me to set up an account on Mastodon:
I’m not planning on abandoning Twitter altogether as there are lots of people I follow there who are not on Mastodon and I’m not going to dump 5k followers even if most of them probably aren’t seeing my content!
I’m not entirely sold on the premise of Mastodon as a federated system as it adds a significant barrier of entry, especially when the most popular server – mastodon.social – isn’t accepting new sign ups. I decided to go all in and set up my own server using Masto.host as that way I could use the domain bendodson.com. The process was very straightforward requiring me only to set up a CNAME with my DNS provider for the subdomain mastodon.bendodson.com. I then had to add a simple text file and contact Masto.host so they could ensure there was a redirect; that way I could use the domain @bendodson.com directly rather than the mastodon subdomain.
With a full domain in place it may be that in future I’ll create accounts for each of my apps so they can post updates, etc. I can then boost them on my own profile. That’s definitely something to think about at a later date!
The only other thing this might impact is the @NewSpatialAudio account which tweets whenever a new Spatial Audio track is released. Obviously this is likely missing a number of tweets and so I’m tinkering with an idea on how to display this in a better way as well as providing fuller access to my audio database. More on that in the future!
Questions
I noticed that I’d got some feedback from the Music Library Tracker beta via TestFlight but there was no email address attached so I had no way of replying:
Q: Not sure what to make of this, but every so often I find a Dolby album or track that isn’t showing up in the Finder. This one is a full album in Dolby that doesn’t get picked up. If it helps, I’m listening in 🇺🇸
A: This is a good question. I was initially going to reply that the album in question was in Dolby Audio whereas it needs to be in Dolby Atmos to be classed as Spatial Audio.
Dolby Atmos is full object based 3D audio whereas Dolby Audio is channel based. It is not immediately obvious that there is a difference between the two as the logos are incredibly similar:
This is impossible to distinguish on macOS where both formats use the same Dolby icon5:
My understanding has always been that “Spatial Audio” is a marketing term created by Apple that denotes their own usage of 3D audio formats. Spatial Audio can offer things like head tracking so as you move your head the audio stays in the same place almost as if it were coming from a stage. Whilst all Spatial Audio tracks use Dolby Atmos, they don’t have to as it’s just a term around 3D audio so any future format (and there are lots of them) would work if Apple supported it.
Except, that isn’t quite true!
Dolby Atmos is required if you want to do dynamic head tracking but you can use fixed Spatial Audio if a song supports Dolby Audio:
In both cases, Apple is referring to this as “Spatial Audio” despite the fact it only ever references Dolby Atmos with this term on it’s website 🤦🏻♂️
So, at the moment Music Library Tracker and the Spatial Audio Finder only treat Dolby Atmos tracks as being Spatial Audio. I do have a record of which tracks are in Dolby Audio so it should be relatively easy to add that as a feature but it will likely be for a later version!
Recommended Links
Video Games
Hi-Fi Rush - This was a surprise release immediately after the Xbox & Bethesda Developer_Direct last week. It’s a rhythm-based action game that gives me strong Sunset Overdrive vibes in the best possible way. You run around a beautiful cartoon environment hitting enemies in time to a constant beat. It’s very forgiving though so worth a try even if you don’t have a good ear! I was sold the minute the game opened with Lonely Boy by The Black Keys.
Squiggle Drop - An interesting little mobile game which tasks you with doing small doodles that fall down once completed to solve puzzles. Bit tricky to explain now I think about it but it’s free on Apple Arcade so just give it a try.
Goldeneye 007 - Bond is back! The Switch version is basically a straight up emulation of the N64 version with a lot of control problems (unless you have the wireless N64 pad) but it does have online multiplayer. Meanwhile, it’s also available on Xbox with Game Pass with some slightly better textures, a 16:9 aspect ratio, and no online functionality. It’s an absolute mess of licensing agreements but it’s here and it’s still a lot of fun on Xbox, surprisingly so!
Board Games
Darkest Dungeon - I briefly mentioned this in Issue #2 but I’ve had a chance to play it now and yes it’s very good. I particularly like the battle system which really captures the flavour of the video game. I’m going to play a ton of this and am definitely considering building an app to speed along solo play as there are almost too many cards to fit on my table.
TV Shows
Our Flag Means Death - I started watching this in June last year6 but only got through an episode or two before passing it by for other things. It's become a bit more popular in the UK this past month as it's now available on BBC iPlayer. I jumped back in as it was something I could watch whilst sleeving the cards for Darkest Dungeon as it didn’t require my undivided attention. It's a comedy about pirates and whilst the initial two episodes are fine it definitely picks up by the end with quite a few surprisingly emotional story arcs. Taika Waititi's Blackbeard is incredibly good (would make a good Jack Sparrow replacement actually) but Rhys Darby's gentleman pirate steals the show.
Videos
Virgil Fox playing Bach’s “Gigue” Fugue - "And mind you I heard a swishing in the hall and I turned around and where they had been seated upon blankets they kicked their blankets aside, and at the end of this piece there were 400 young doing a divine gigue to a fugue of Bach. Let's do it!"
Roadmap
The roadmap is my way of committing to what I’m going to do over the next 2 weeks.
19th January - 1st February
Release Music Library Tracker v2.0 ❌
Unfortunately not this time around. I’m not a fan of feature creep but I think adding the automatic background scanning for Spatial Audio was worth a couple weeks delay. Can you guess what the plan is for next issue?
2nd February - 15th February (Issue #6)
Release Music Library Tracker v2.0
Start prototyping an app for the Darkest Dungeon board game (I have another big music app but I need a break from the Apple Music API for a while 🤣)
That wraps it up for this issue. I hope you found something of interest and that you’ll be able to recommend the newsletter to your friends, family, and colleagues. You can always comment on this issue or by emailing me directly via ben@bendodson.com
I found the video in the article The Art of Noticing.
Narrator: It wasn’t.
There is a beginGeneratingLibraryChangeNotifications()
method on MPMediaLibrary but this doesn’t detail the changes nor does it always trigger correctly when changes are made to tracks by Apple (it seems only to trigger when a user initiates a change).
Thank you backgroundTimeRemaining.
I’m guessing they have to show the Dolby logo for licensing reasons but it’s bizarre to me that they don’t show the Spatial Audio icon as well.
I know this because I took a screenshot of it when initially revealing Chaise Longue to 5K 🤣