Tracking BPM, Beats, and Position
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
60 bytes. The shorter packets contain information about
which channels are on-air, and
fader start/stop commands to 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
20) has the value
00 here, rather than
02 as we
saw in the startup packets. In packets of
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
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
5f. That seems common in packets with subtype
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 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.|
nextBeat at bytes
27 is the
number of milliseconds in which the very next beat will arrive.
2b) is the number of milliseconds until
the beat after that. nextBar (bytes
2f) reports the
number of milliseconds until the next measure of music begins, which
may be from 1 to 4 beats away. 4thBeat (bytes
reports how many milliseconds will elapse until the fourth upcoming
beat; 2ndBar (bytes
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
3b) tells how many millieconds we
have to wait until the eighth upcoming beat will arrive.
The player’s current pitch adjustment can be found in bytes
labeled Pitch. It represents a four-byte pitch adjustment
0x00100000 represents no adjustment (0%),
0x00000000 represents slowing all the way to a complete stop (−100%,
reachable only in Wide tempo mode), and
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 can be found at
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:
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).
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
(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.