DJing From My Phone

Last Friday was the second time I DJed at a lindy hop party from my phone. The first time was in January, at a bar in Copenhagen during a local weekly swing night, after a wonderful weekend of teaching taster classes with Lee at the fantastic festival LHTA. That time I had used my special iOS pyhton program (written in Pythonista) and played from my small-screened iPhone 4S. This time, in Ljubljana,  I used my huge-screened Note 4, and a program I whipped up in the two days leading to the party. Did I test the program thoroughly beforehand? No. Was my set ok? Umm, yeah, sure.

Here are the abilities I need for a (good) set:

  1. External sound card
  2. Preview songs
  3. Queue songs
  4. Lock buttons on playing device
  5. Search in media library by title, artist, bpm and comments.

This project of playing from my Note 4 that runs Android started when I realised that I can connect an external usb sound card to it using something called an OTG cable, as in On-The-Go. Brilliant! This solves point no. 1.

A few months ago I spent quite some time in trying to write a native iOS app that will play two songs in the two channels of headphones. There already exists such an app in the app store, but it’s expensive, doesn’t have a lock buttons feature, doesn’t search in comments, and it has turn tables. Enough with the turn tables already! I’m not that kind of DJ… Anyway, I didn’t figure it out before I gave up, and I was sad. But if I play music form my phone – I can just preview on my iPad, using my old python program. Great. This solves point no. 2.

I made the program with two tabs: one to show the media library and one the current playlist. Clicking a song in the media library tab queues a song in the playlist tab, and clicking a song in the playlist tab starts to play it. Unless the lock is on. Which is crucial. Personally, I am always worried about accidental clicks. It doesn’t matter if I’m using my phone, my iPad, or even my MacBook. Having a program that just ignores all clicks, when a lock is on, reduces my anxiety by a big factor. And I know a few other DJs who have confessed to the same stress. But when the lock is on I can still queue songs and change their position in the playlist. So points 3 and 4 are good too.

Of course I’ll need to search for songs. If I’ve already previewed a song on another device and I just want to add it now then I can search by title. But I really like seeing my comments… So I made them show up on the screen. And if they’re there, I should also be able to search the comments tag as well. What if I’m riding shotgun and have to provide the music? I’m not going to preview songs. Being able to search for a low energy groovy song would be nice.

Here’s a screenshot from the end of my set:

Screenshot_2015-12-19-02-59-36

Though everything was solid, the set was stressful for me. I wasn’t sure the app would hold up with no crashes, and if the battery would survive supplying power to a usb card and having the screen on almost constantly. I’m happy to say that the app didn’t crash, and the battery only went down by 20%! That’s it! Granted, I turned off everything else and went into airplane mode (can you imagine getting a call in the middle of a set?). The only snafu was during the first song: I forgot that I made a lock-all-buttons button, and my headphones cable accidentally touched the phone’s screen – that’s it, it just kinda grazed the screen for a split second. And the song changed. Somehow, it was kind of on beat, and with no pause. So it was ok, and then I clicked the lock button.

One more thing about my set: following this post, I decided to use a normal distribution for my bpms. I had python opened up on my MacBook (which I brought fearing that my phone would explode and I would need another device) with numpy imported, and I got my bpms from its random module. For the first half hour, which was between band sets, I used a mean of 170 and sigma 16.66. Then, after the band went off for the last time around 1:20 AM, I reduced the mean to 150. Finally, after an hour and until the end of my set I set the mean to 140. Only two times during the set did I decide to overrule the random procedure. Since I had say in the energy and subgenre of songs, no bpm was supposed to be problem. Except for those two times, that two songs above 190 were just played, and I wanted a slower song to put on next. Overall, I think the room was pretty much swinging and bouncing until the end, at which point a jam of three musicians started on stage. The highest bpm was 202 and the lowest was 124.

Screen Shot 2015-12-20 at 8.37.17 PM

Being able to go have a set without carrying a heavy computer is awesome. Once I add some features, like tagging and saving playlists and maybe even previewing, I will upload the app to the Google play store. But who knows when that will be…

Beats Distribution

When another post on Jive Junction’s Facebook group asked about BPMs for lindy hop parties, I decided to check the distribution from recent saved DJed playlists, 14 in total. These include two from big events (Lindy Shock, Vintage Swing Festival), three from local (to Slovenia) events, and the rest are weekly local dance nights in Tel Aviv and Ljubljana. Here’s the plot:

Photo Dec 15, 2 58 35 AM

\mu=146, \sigma=17.1

Except for the peak in the middle, the distribution is pretty normal. When I DJ, I try to match the speed to what I think will get the most dancers on the floor, in the current song and the next. Over time, I have consciously come to think that 146 is the most ideal BPM for this task – whether after a fast song or a slow song, or even of the same speed, it will bring out the most dancers for this and the next song.

But I haven’t really documented how many people are dancing to each song, and maybe I have it off. So I’ve now started, and I will also randomise my playlists a bit more with the help of a computer, which hopefully is unbiased. I definitely want to try Shana Worel’s suggestion of \mu=170,\sigma=16.6 for a few sessions (“with a few outliers sprinkled in for good measure”).

It would be pretty cool to get a result similar to that well known experiment in the west coast world. 122 was it?

————————–

The above plot was made using a python script that I wrote, that goes over playlists in the iTunes Media Library file. This is an xml file, which on my mac was found in ‘~/Music/iTunes/’. You can run the script as well to see your own statistics. If there are playlists that are not relevant, you can name them and the script will disregard them. I used numpy and matplotlib to generate the plot, and those few lines are not included.

from xml.dom.minidom import parse, parseString
from math import sqrt

# Change this to wherever your 'iTunes Music Library.xml' is.
_itunes = "~/Music/iTunes/iTunes Music Library.xml"

# Irrelevant playlists. For example, the bpms from my
# wedding playlist should not be counted.
irrelevant = ['April','From Videos','Wedding!','WeirdThing']

###########################################################
###########################################################

itunes = parse(_itunes)

_tracks = itunes.childNodes[1].childNodes[1].childNodes[24]
_playlists = itunes.childNodes[1].childNodes[1].childNodes[28]

def getKeyIndex(idict, key):
    cn = idict.childNodes
    for i in range(len(cn)):
        if cn[i].hasChildNodes() and cn[i].tagName == u'key' and cn[i].childNodes[0].data == key:
            return i
    return -1
    
def getValue(idict, key):
    i = getKeyIndex(idict, key)
    if i != -1:
        cn = idict.childNodes
        i = i+1
        while not cn[i].hasChildNodes():
            i = i+1
            if i >= len(cn):
                return -1
        return cn[i].childNodes[0].data
    return -1
    

# Parse tracks
tracks = {}
for track in _tracks.childNodes:
    if track.hasChildNodes() and track.tagName == u'dict':
        id = int(track.childNodes[2].childNodes[0].data)
        bpm = int(getValue(track, u'BPM'))
        tracks[id] = (bpm,)
        
# Parse playlists
playlists = {}
for playlist in _playlists.childNodes:
    badtags = ['Distinguished Kind','Smart Info', 'Folder', 'Master']
    name = getValue(playlist, 'Name')
    itemsIndex = getKeyIndex(playlist, 'Playlist Items')
    
    if set([getKeyIndex(playlist, tag) for tag in badtags]) == set([-1]) and name != -1 and itemsIndex != -1:
        while not playlist.childNodes[itemsIndex].hasChildNodes() or playlist.childNodes[itemsIndex].tagName != u'array':
            itemsIndex += 1
            if itemsIndex >= len(playlist.childNodes):
                break
        
        if itemsIndex >= len(playlist.childNodes):
            continue
            
        ptracks = [int(i.childNodes[0].data) for i in playlist.childNodes[itemsIndex].getElementsByTagName('integer')]
        
        playlists[name] = ptracks
        
for irr in irrelevant:
    playlists.pop(irr)
    
allbpms = []
for p in playlists:
    for id in playlists[p]:
        if tracks[id][0] > 0:
            allbpms.append(tracks[id][0])

mu = (sum(allbpms)+0.0)/len(allbpms)
sigma = sqrt(sum([(x-mu)**2 for x in allbpms])/len(allbpms))

print 'mu:', mu
print 'sigma:', sigma