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:
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 |
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 54
–57
,
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.
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.