Tracking BPM, Beats, and Position

Beat Packets

For some time before this analysis was written, Afterglow was able to synchronize its light shows with music being played on Pioneer equipment by observing packets broadcast by the mixer to port 50001.

Using just that approach, however, it was not possible to tell which player was the master, so there was no way to determine the down beat (the start of each measure). Now that it is possible to to determine which CDJ is the master player using the CDJ status packets, these beat packets have become far more useful, and Afterglow now uses Beat Link, the library that resulted from this analysis, to track the down beat based on the beat number reported by the master player.

To track beats, open a socket and bind it to port 50001. The devices seem to broadcast two different kinds of packets to this port, a shorter packet containing 2d bytes of data, and a longer packet containing 60 bytes. The shorter packets contain information about which channels are on-air, and fader start/stop commands to the players.

The 60-byte packets are sent on each beat, so even the arrival of the packet is interesting information, it means that the player is starting a new beat. (CDJs send these packets only when they are playing and only for rekordbox-analyzed tracks. The mixer sends them all the time, acting as a backup metronome when no other device is counting beats.) These packets have the following structure:

0123456789abcdef5173707431576d4a4f4c280010Device Name (padded with 00)012000DlenrnextBeat2ndBeatnextBar304thBeat2ndBar8thBeatffffffff40ffffffffffffffffffffffffffffffff50ffffffffPitch0000BPMBb0000D
Beat packet.

This introduces our first packet structure variant around the device name. The packet header ends immediately after the packet type indicator in byte 0a, so the device name begins one byte earlier at byte 0b (this byte had the value 00 in the startup packets). As always, the byte after the name has the value 01, but the subtype value which follows that (at byte 20) has the value 00 here, rather than 02 as we saw in the startup packets. In packets of subtype 00 the subtype indicator is followed by the Device Number D at byte 21; this is the Player Number as displayed on the CDJ itself, or 21 for the mixer, or another value for a computer running rekordbox. And that is followed by a different kind of length indicator: lenr at bytes 22-23 reports the length of the rest of the packet, in other words, the number of bytes which come after lenr. In this packet lenr has the value 003c. For some reason, there is a redundant copy of D at the end of the packet, in byte 5f. That seems common in packets with subtype 00, and is one of many inefficiencies in the protocol.

To facilitate synchronization of variable-tempo tracks, the number of milliseconds after which a variety of upcoming beats will occur are reported.

The timing values for all these upcoming beats and bars are always reported as if the track was being played at normal speed, with a pitch adjustment of 0%. If a pitch adjustment is in effect, you will need to perform the calculation to scale the beat timing values yourself.

If the track ends before an upcoming beat or bar, then a timing value of 0xffffffff is reported for that entry.

nextBeat at bytes 24-27 is the number of milliseconds in which the very next beat will arrive. 2ndBeat (bytes 28-2b) is the number of milliseconds until the beat after that. nextBar (bytes 2c-2f) reports the number of milliseconds until the next measure of music begins, which may be from 1 to 4 beats away. 4thBeat (bytes 30-33) reports how many milliseconds will elapse until the fourth upcoming beat; 2ndBar (bytes 34-37) the interval until the second measure after the current one begins (which will occur in 5 to 8 beats, depending how far into the current measure we have reached); and 8thBeat (bytes 38-3b) tells how many millieconds we have to wait until the eighth upcoming beat will arrive.

The player’s current pitch adjustment[1] can be found in bytes 5457, labeled Pitch. It represents a four-byte pitch adjustment percentage, where 0x00100000 represents no adjustment (0%), 0x00000000 represents slowing all the way to a complete stop (−100%, reachable only in Wide tempo mode), and 0x00200000 represents playing at double speed (+100%).

The pitch adjustment percentage represented by Pitch is calculated by multipyling the following (hexadecimal) equation by decimal 100:

The current BPM of the track playing on the device[2] can be found at bytes 5a-5b (labeled BPM). It is a two-byte integer representing one hundred times the current track BPM. So, the current track BPM value to two decimal places can be calculated as (in this case only the byte offsets are hexadecimal):

In order to obtain the actual playing BPM (the value shown in the BPM display), this value must be multiplied by the current pitch adjustment. Since calculating the effective BPM reported by a CDJ is a common operation, here a simplified hexadecimal equation that results in the effective BPM to two decimal places, by combinining the BPM and Pitch values:[3]

The counter Bb at byte 5c counts out the beat within each bar, cycling 1 → 2 → 3 → 4 repeatedly, and can be used to identify the down beat if it is coming from the master player (and if the DJ has properly established the track’s beat grid).

Absolute Position Packets

Prior to the CDJ-3000 the only way that it was possible to figure out the playback position of a player (for the purposes of synchronizing timecode, video, lighting cues, and such things) was to watch for beat packets and combine them with a downloaded beat grid to translate beat numbers into times within the track. And in between receiving the (fairly rare) beat packets, we had to interpolate our best guess as to the current track position, with the help of pitch updates we might receive in status packets. This mostly worked, well enough to run shows with, but would break down if the DJ was doing anything creative with scratching, reverse play, or loops on anything other than precise beat boundaries.

The CDJ-3000 makes tracking position far more practical and reliable, thanks to this new packet type discovered by David Ng. Absolute Position packets are only transmitted while a track is loaded on a player, but unlike Beat packets, they are sent even while not playing. This packet is sent to all connected devices on port 50001 every 30 milliseconds, which is often enough to keep good tabs on what the player is up to, even when playing backwards, scratching, looping, or needle jumping in between beats.

0123456789abcdef5173707431576d4a4f4c0b0010Device Name (padded with 00)022000DlenrTrackLengthPlayheadPitch300000000000000000BPM
Absolute Position packet.

D indicates the number of the player sending the packet.

lenr reports the length of the rest of the packet, in other words, the number of bytes which come after lenr.

TrackLength reports the length of the loaded track in seconds, rounded down to the nearest second.

Playhead reports the absolute position of the playhead in milliseconds.

Pitch is a 32-bit signed integer representing the value of the pitch slider as shown on the player screen multiplied by 64 (decimal 100). For example, if the pitch slider is at 3.26%, this value is 0146 (326 in decimal). This is a much more sensible and friendly format for pitch information than we have seen in other packets, and is analogous to the way tempo is handled.

BPM reports the BPM value as shown on the player screen (in other words, the current effective tempo, which is the track tempo adjusted by the playback pitch) multiplied by 0a (decimal 10). For example a temppo of 120.2 BPM is represented as 4b2 (1202 in decimal). Tracks with unknown BPM have the value ffffffff. This is the same as tempo values we have seen in other packets.

1. The mixer always reports a pitch of +0%.
2. The mixer passes along the BPM of the master player.
3. Since the mixer always reports a pitch adjustment of +0%, its BPM value can be used directly without this additional step.