Track Metadata

Thanks to @EvanPurkhiser, we finally started making progress in retrieving metadata from CDJs, and then some shared code from Austin Wright boosted our understanding considerably, and led to this section.

Connecting to the Database

The first step is to determine the port on which the player is offering its remote database server. That can be determined by opening a TCP connection to port 12523 on the player and sending it sending a packet with the following content:

0123456789abcdef0000000fRemoteDBServ0010er00
DB Server query packet.

The player will send back a two-byte response, containing the high byte of the port number followed by the low byte. So far, the response from a CDJ has always indicated a port number of 1051, but using this query to determine the port to use will protect you against any future changes. The same query can also be sent to a laptop running rekordbox to find the rekordbox database server port, which can also be queried for metadata in the exact same way described below.

To find the metadata associated with a particular track, given its rekordbox ID number, as well as the player and slot from which it was loaded (all of which can be determined from a CDJ status packet received by a virtual CDJ as described in Creating a Virtual CDJ), open a TCP connection to the device from which the track was loaded, using the port that it gave you in response to the DB Server query packet, then send the following four messages. (You can also get metadata for non-rekordbox tracks, even for CD Audio tracks being played in the CD slot, using the variant described in Non-Rekordbox Track Metadata.)

Once you have determined the database port, send it a setup packet with the following content, and the server will send the same five bytes back:

012341100000001
Database connection setup packet.

All further messages have a shared structure. They consist of lists of type-tagged fields (a type byte, followed some number of value bytes, although in the case of the variable-size types, the first four bytes are a big-endian integer that specifies the length of the additional value bytes that make up the field). So far, there are four known field types, and it turns out that the packet we just saw is one of them, it represents the number 1 as a 4-byte integer.

Field Types

The first byte of a field identifies what type of field is coming. The values 0f, 10, and 11 are followed by 1, 2, and 4 byte fixed-length integer fields, while 14 and 26 introduce variable-length fields, a binary blob and a UTF-16 big-endian string respectively.

Number Fields

Number fields are indicated by an initial byte 0f, 10, or 11 which is followed by big-endian integer value of length 1, 2, or 4 bytes respectively, as shown below. So, as noted above, the initial greeting packet sent to and received back from the database server is a number field, four bytes long, representing the value 1.

010fn
Number Field of length 1.
01210n
Number Field of length 2.
0123411n
Number Field of length 4.

Binary Fields

Variable-length binary (blob) fields are indicated by an initial byte 14, followed by a 4 byte big-endian integer which specifies the length of the field payload. This length is followed by the specified number of bytes (for example, an album art image, waveform or beat grid):

0123456789abcdef14lengthBinary Data0010i+00
Binary (Blob) Field.

String Fields

Variable-length string fields are indicated by an initial byte 26, followed by a 4 byte big-endian integer which specifies the length of the string, in two-byte UTF-16 big-endian characters. So the length is folowed by 2 × length bytes containing the actual string characters. The last character of the string is always NUL, represented by 0000:

0123456789abcdef26lengthUTF-16BE string0010i+00
String Field.

Messages

Messages are introduced by a 4 byte Number field containing a “magic“ value (it is always 872349ae). This is followed by another 4 byte number field that contains a transaction ID (TxID), which starts at 1 and is incremented for each query sent, and all messages sent in response to that query will contain the same transaction ID. This is followed by a 2 byte number field containing the message type, a 1 byte number field (n) containing the number of argument fields present in the message, and a blob field containing a series of bytes which identify the types of each argument field (shown as t1 through t12 below). This blob is always twelve bytes long, regardless of how few arguments there are (and presumably this means no message ever has more than twelve arguments). Tag bytes past the actual argument count of the message are set to 0.

This header is followed by the fields that make up the message arguments, if any. In other words, the message body consists of arguments identified by the header.

0123456789abcdef11872349ae11TxID10type0fn1400100000000c (12)t1t2t3t4t5t6t7t8t9t10t11t1220Arguments30i+00
Message structure showing header fields.

The argument type tags use different values than the field type tags themselves, for some reason, and it is not clear why this redundant information is necessary at all—but that is true a number of places in the protocol as we have seen before and will see again. Here are the known tag values and their meanings:

Table 1. Argument tag values.
Tag Meaning

02

A string in UTF-16 big-endian encoding, with trailing NUL (zero) character.

03

A binary blob.

06

A 4 byte big-endian integer.

I am guessing that if we ever see them, a tag of 04 would represent a 1 byte integer, and 05 would represent a 2 byte integer. But so far no such messages have been seen.

Before you can send your first actual query, you need to send a special message which seems to be necessary for establishing the context for queries. It has a type of 0000, a special TxID value of fffffffe, and a single numeric argument, like this:

0123456789abcdefstartTxIDtypeargstags11872349ae11fffffffe1000000f011400100000000c (12)0600000000000000000000002011Dours
Query context setup message.

The value D is, like in the other packets we have seen, a player device number. In this case it is the device that is asking for metadata information. It must be a valid player number between 1 and 4, and that player must actually be present on the network, must not be the same player that you are contacting to request metadata from, and must not be a player that has connected to that player via Link and loaded a track from it. So the safest device number to use is the device number you are using for your virtual CDJ, but since it must be between 1 and 4, you can only do that if there are fewer than four actual CDJs on the network.

The player will respond with a message of type 4000, which is the common “success” response when requested data is available. The response message has two numeric arguments, the first of which is the message type of the request we sent (which was 0000), and the second usually tells you the number of items that are available in response to the query you made, but in this special setup query, it returns its own player number:

0123456789abcdefstartTxIDtypeargstags11872349ae11fffffffe1040000f021400100000000c (12)06060000000000000000000020110000000011Dtheirs
Query context setup response.

Rekordbox Track Metadata

To ask for metadata about a particular rekordbox track, send a packet like this:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1020020f021400100000000c (12)0606000000000000000000002011D01SrTr11rekordbox
Rekordbox track metadata request message.

As described above, TxID should be 1 for the first query packet you send, 2 for the next, and so on.

The first argument of this message requires a little explanation: it is sent as a four-byte number field, but each byte has a separate meaning. D should have the same value you used in your initial query context setup packet, identifying the device that is asking the question. The second byte is referred to as M, and seems to identify the particular menu location on the CDJ where results are going to be displayed. For this kind of metadata request it always has the value 01.

Sr is the slot in which the track being asked about can be found, and has the same values used in CDJ status packets, as described in the CDJ status packet description. Similarly, Tr identifies the type of track we want information about; for rekordbox tracks this always has the value 01.

This first argument format is used for many different requests about tracks, so it is given the acronym DMST in future discussion.

The second message argument, rekordbox, identifies the local rekordbox database ID of the track being asked about, as found in the CDJ status packet.

As suggested in the above description, track metadata requests are built on the mechanism that is used to request and draw scrollable menus on the CDJs, so the request is essentially interpreted as setting up to draw the “menu” of information that is known about the track. The player responds with a success indicator, saying it is ready to send these “menu items” and reporting how many of them are available:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1040000f021400100000000c (12)060600000000000000000000201100002002110000000b
Track metadata available response.

We’ve seen this type of “data available” response already in the query context setup response, but this one is a more typical example. As usual, TxID matches the value we sent in our request, and the first argument, with value 2002, reflects the type field of our request. The second argument reports that there are 11 (0b) entries of track metadata available to be retrieved for the track we asked about, and that the player is ready to send them to us.

If there was no track with ID rekordbox in that media slot, the second argument would have the value ffffffff to let us know. If we messed up something else about the request, we will get a response with a type other than 4000. See Experimenting with Metadata for instructions on how to explore these variations on your own.

But assuming everything went well, we can get the player to send us all eleven of those metadata entries by telling it to render all of the current menu, using a “render menu” request with type 3000 like this:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1030000f061400100000000c (12)0606060606060000000000002011D01SrTr11offset11limit11300000000011total1100000000
Render Menu request message.

As always, the value of TxID should be one higher than the one you sent in your setup packet, while the values of D, Sr, and Tr should be identical to what you sent in it.

The request has six numeric arguments. At this point it is worth talking a bit more about the byte after D in the first argument. This seems to specify the location of the menu being drawn, with the value 1 meaning the main menu on the left-hand half of the CDJ display, while 2 means the submenu (for example the info popup when it is open) which overlays the right-hand half of the display. We don’t yet know exactly what, if any, difference there is between the response details when 2 is used instead of 1 here. And special data requests use different values: for example, the track waveform summary, which is drawn in a strip along the entire bottom of the display, is requested with a menu location number of 8 in this second byte.

As described above, Tr has the value 1 for rekordbox tracks.

The second argument, offset, specifies which menu entry is the first one you want to see, and the third argument, limit, specifies how many should be sent. In this case, since there are only 11 entries, we can request them all at once, so we will set offset to 0 and limit to 11. But for large playlists, for example, you need to request batches of entries that are smaller than the total available, or the player will be unable to send them to you. We have not found what the exact limit is, but getting 64 at a time seems to work on Nexus 2 players.

We don’t know the purpose of the fourth argument, but sending a value of 0 works. The fifth argument, total, seems to usually contain the total number of items reported in the intial menu response, but sending a second copy of limit here works too; it may not matter much. And the sixth and final argument also has an unknown purpose, but 0 works.

So, for our metadata request, the packet we want to send in order to get all the metadata will have the specific values shown here:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1030000f061400100000000c (12)0606060606060000000000002011D01SrTr1100000000110000000b113000000000110000000b1100000000
Render track metadata request message.

This causes the player to send us 13 messages: The 11 metadata items we requested are sent (with type 4101, second figure below), but they are preceded by a menu header message (with type 4001, first figure below), and followed by a menu footer message (with type 4201, third figure below). This wrapping happens with all “render menu” requests, and the menu footer is an easy way to know you are done, although you can also count the messages.

When sending requests which result in multiple response messages, it is important to keep in mind the distinction between message boundaries and network packets. You might receive more than one message in a single network packet (especially when you are talking to rekordbox), so you need to look for message boundaries based on the length information in each message. Failure to do this initially led to a bug in Beat Link. It is also possible that a single message might be split into more than one network packet. I don’t believe this has been seen, but coding defensively to handle it may be the reason why.

The menu item responses all have the same structure, and use all twelve message argument slots, containing ten numbers and two strings, although they generally don’t have meaningful values in all of the slots. They have the general structure shown here, and the arguments are listed in the Table below:

Item 1: Title

The first item returned after the menu header is the track title, so argument 7 has the value 04. Argument 1, which may always be some kind of parent ID, holds the artist ID associated with the track. The second argument seems to always be the main ID, and for this response it holds the rekordbox ID of the track. Argument 4 holds the track title text, and argument 9 holds the album artwork ID if any is available for the track. This ID can be used to retrieve the actual album art image as described in Album Art.

Item 2: Artist

The second item contains artist information, so argument 7 has the value 07. Argument 2 holds the artist ID, and argument 4 contains the text of the artist name.

Item 3: Album Title

The third item contains album title information, so argument 7 has the value 02. Argument 4 contains the text of the album name.

Item 4: Duration

The fourth item contains track duration information, so argument 7 has the value 0b. Argument 2 contains the length, in seconds, of the track when played at normal tempo.

Item 5: Tempo

The fifth item contains tempo information, so argument 7 has the value 0d. Argument 2 contains the track’s starting tempo, in beats per minute, times 100, as reported in BPM values in other packets described earlier.

Item 6: Comment

The sixth item contains comment information, so argument 7 has the value 23. Argument 4 contains the text of the track comment entered by the DJ in rekordbox.

Item 7: Key

The seventh item contains key information, so argument 7 has the value 0f. Argument 4 contains the text of the track’s dominant key signature.

Item 8: Rating

The eighth item contains rating information, so argument 7 has the value 0a. Argument 2 contains a value from 0 to 5 corresponding to the number of stars the DJ has assigned the track in rekordbox.

Item 9: Color

The ninth item contains color information, so argument 7 has a value between 13 and 1b identifying the color, if any, assigned to the track (see Menu Item Types for the color choices), and if the value is anything other than 13, Argument 4 contains the text that the DJ has assigned for that color meaning in rekordbox.

Item 10: Genre

The tenth item contains genre information, so argument 7 has the value 06. Argument 2 contains the numeric genre ID, and argument 4 contains the text of the genre name.

Item 11: Date Added

The eleventh and final item contains information about the the date the track was added to the collection, so argument 7 has the value 2e. Argument 4 contains the date the track was added to the collection in the format “yyyy-mm-dd”. This information seems to propagate into rekordbox from iTunes.

The menu footer message has a type of 4201 and no arguments, so it has a header only, and is always made up of the exact same sequence of bytes (apart from the TxID).

As noted above, the seventh argument in a menu item response identifies the type of the item. The meanings we have identified so far are:

Table 3. Known menu item types.
Type Meaning

0001

Folder (such as in the playlists menu)[1]

0002

Album title

0003

Disc

0004

Track Title

0006

Genre

0007

Artist

0008

Playlist

000a

Rating

000b

Duration (in seconds)

000d

Tempo

000e

Label

000f

Key

0010

Bit Rate

0011

Year

0013

Color None

0014

Color Pink

0015

Color Red

0016

Color Orange

0017

Color Yellow

0018

Color Green

0019

Color Aqua

001a

Color Blue

001b

Color Purple

0023

Comment

0024

History Playlist

0028

Original Artist

0029

Remixer

002e

Date Added

0080

Genre menu

0081

Artist menu

0082

Album menu

0083

Track menu

0084

Playlist menu

0085

BPM menu

0086

Rating menu

0087

Year menu

0088

Remixer menu

0089

Label menu

008a

Original Artist menu

008b

Key menu

008e

Color menu

0090

Folder menu

0091

Search “menu”

0092

Time menu

0093

Bit Rate menu

0094

Filename menu

0095

History menu

00a0

All

0204

Track Title and Album

0604

Track Title and Genre

0704

Track Title and Artist

0a04

Track Title and Rating

0b04

Track Title and Time

0d04

Track Title and BPM

0e04

Track Title and Label

0f04

Track Title and Key

1004

Track Title and Bit Rate

1a04

Track Title and Bit Color

2304

Track Title and Comment

2804

Track Title and Original Artist

2904

Track Title and Remixer

2a04

Track Title and DJ Play Count

2e04

Track Title and Date Added

As noted above, track metadata responses use many of these types. Others are used in different kinds of menus and queries.

It seems that CDJ-3000s send additional information in the two high bytes of the menu item field. We have not yet figured out what this conveys, but to find the correct item type for menu requests from CDJ-3000s, it seems to be necessary to mask the field with 0xffff.

Non-Rekordbox Track Metadata

As discussed in the introduction to this section, you can get metadata for non-rekordbox tracks as well (although they don’t have beat grids, waveforms, or album art available). All you need to do is use a slight variant of the metadata request message, using the value 2202 (instead of 2002) for the message type, and a value of Tr that is appropriate for the kind of track you are asking about (02 for non-rekordbox tracks loaded from media slots, and 05 for CD audio tracks playing in the CD slot, which has a Sr value of 01). After the initial query setup message, the other message types are the same as in the above discussion, but you will continue using the Sr and Tr values appropriate for the slot and media type you are asking about.

Since these tracks don’t have rekordbox IDs, you will need to use the rekordbox value reported in CDJ status packets in order to find out the values used to request the metadata for tracks loaded from solid state media; CD tracks are requested using the simple track number as the rekordbox value.

It seems that to reliably get data back when requesting metadata for non-rekordbox tracks, your virtual CDJ needs to be sending properly-formatted status packets, not just device announcement packets.

Album Art

To request the artwork image associated with a track, send a message with type 2003 containing the artwork ID that was specified in the track title item (as described in Item 1: Title), like this:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1020030f021400100000000c (12)0606000000000000000000002011D08SrTr11artwork
Track artwork request message.

As usual, TxID should be incremented each time you send a query, and will be used to identify the response messages. D should have the same value you used in your initial query context setup packet, identifying the device that is asking the question. Sr and Tr are the slot in which the track being asked about can be found and its track type; each has the same values used in CDJ status packets. Finally, artwork identifies the specific artwork image you are requesting, as it was specified in the track medatata response. As with other graphical requests, the value after D, which identifies the location of the menu for which data is being loaded, is 8.

The response will be a message with type 4002, containing four arguments. The first argument echoes back our request type, which was 2003. The second always seems to be 0. The third contains the length of the image in bytes, which seems redundant, because that is also conveyed in the fourth argument itself, a blob containing the actual bytes of the image data, as shown below. However, if there is no image data, this value will be 0, and the blob field will be completely omitted from the response, so you must not try to read it!

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1040020f041400100000000c (12)060606030000000000000000201100002003110000000011length1430lengthImage bytes40i+00
Track artwork response message.

To experiment with this, start up dysentery in a Clojure REPL and connect to a player as described in Experimenting with Metadata, then evaluate an expression like:

(def art (db/request-album-art p2 3 3))

Replace the arguments with the var holding your player connection, the proper Sr number for the media slot holding the art, and the artwork ID of the album art, and dysentery will open a window like this showing the image:

Album Art

The request and artwork shown here are at the original resolution used by older players, 80x80 pixels. You can also request a higher-resolution version, at 240x240 pixels, by adding an additional number argument with the value 1 at the end of the track artwork request message.

Similarly, to load artwork for a non-rekordbox track, you can add an additional number argument with the value 2 at the end of the track artwork request message (instead of the 1 you would send to request high-resolution rekordbox art).

Beat Grids

The CDJs do not send any timing information other than beat numbers during playback, which has made it difficult to offer absolute timecode information. The discovery of beat grid requests provides a clean answer to the problem. The beat grid for a track is a list of every beat which occurs in the track, along with the point in time at which that beat would occur if the track were played at its standard (100%) tempo. Armed with this table, it is possible to translate any beat packet into an absolute position within the track, and, combined with the tempo information, to generate timecode signals allowing other software (such as video resources) to sync tightly with DJ playback.

To request the beat grid of a track, send a message with type 2204 containing the rekordbox ID of the track:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1022040f021400100000000c (12)0606000000000000000000002011D08SrTr11rekordbox
Track beat grid request message.

As usual, TxID should be incremented each time you send a query, and will be used to identify the response messages. D should have the same value you used in your initial query context setup packet, identifying the device that is asking the question. Sr and Tr are the slot in which the track being asked about can be found and its track type; each has the same values used in CDJ status packets. Finally, rekordbox identifies the specific beat grid you are requesting, as found in a CDJ status packet or playlist response. As with graphical requests, the value after 𝐷, which identifies the location of the menu for which data is being loaded, is 8.

The response will be a message with type 4602, containing four arguments. The first argument echoes back our request type, which was 2204. The second always seems to be 0. The third contains the length of the beat grid in bytes, which seems redundant, because that is also conveyed in the fourth argument itself, which is a blob containing the actual bytes of the beat grid, as shown below. However, if there is no beat grid available, this value will be 0, and the blob field will be completely omitted from the response, so you must not try to read it!

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1046020f041400100000000c (12)060606030000000000000000201100002204110000000011length1430lengthBeat grid bytes40i+00
Track beat grid response message.

The beat grid itself is spread through the value returned as argument 4, consisting of a series of sixteen-byte entries with the following structure:

0123456789abcdefBbtempotimeUnknown
Beat Grid entry.

Each entry begins with a two-byte little-endian[2] integer holding the beat-within-bar number (labeled Bb in the CDJ status packet diagram) of the beat, which is always 1, 2, 3, or 4. This is followed by a two-byte little-endian tempo value, which records the track tempo at the point where this beat occurs, in beats per minute multiplied by 100 (to allow a precision of BPM). Finally, there is a four-byte little-endian time value, which specifies the time at which this beat would occur, in milliseconds, when playing the track at its normal speed.

In other words, the Bb value of the first beat in the track is found at bytes 0x14-15 of argument 4, its tempo at bytes 16-17, and the time at which that beat occurs at bytes 181b. Subsequent beats are found at 0x10-byte intervals, so the second Bb value is found at bytes 24-25, its tempo at bytes 26-27, its time at bytes 28-2b, and so on.

The purpose of the other bytes within the beat grid is so far undetermined. It looks like there may be some sort of monotonically increasing value following the beat millisecond value, but what it means, and why it sometimes skips values, is mysterious.

Requesting Track Waveforms

Waveform data for tracks can be requested, both the preview, which is 400 pixels long, and the detailed waveform, which uses 150 pixels per second of track content. There is also an even tinier 100 pixel preview, which is used by older players such as the pre-Nexus CDJ 900, returned as the final 100 bytes of the preview response.

We also know how to request Nxs2-style higher resolution and color waveforms, see Requesting Nxs2 Track Waveforms for details.

Waveform Previews

To request the waveform preview of a track, send a message with type 2004 containing the rekordbox ID of the track:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1020040f051400100000000c (12)0606060603000000000000002011D08SrTr110000000411rekordbox113000000000
Waveform preview request message.

As usual, TxID should be incremented each time you send a query, and will be used to identify the response messages. D should have the same value you used in your initial query context setup packet, identifying the device that is asking the question. Sr and Tr are the slot in which the track being asked about can be found and its track type; each has the same values used in CDJ status packets. Finally, rekordbox identifies the specific waveform preview you are requesting, as found in a CDJ status packet or playlist response. As with other graphical requests, the value after 𝐷, which identifies the location of the menu for which data is being loaded, is 8.

You may have noticed that the argument list of the above message specifies that there are five arguments, but in fact the message contains only the first four, numeric, arguments. The fifth, blob, argument is missing. This seems to imply the blob is empty, and this very strange feature of the protocol is, in fact, the way the track metadata is requested. The fifth argument must be specified in the message header but not actually present. When reading messages from a player, the same rules apply: There is always a numeric field right before a blob field, and it always contains a seemingly-redundant copy of the blob length, and if that numeric field has the value 0, you must not try to read the blob field. Instead, expect the next field to follow the numeric field (or, if the blob was the last field, expect the message to end with the numeric field).

The second argument has an unknown purpose, but we have seen values of 3 or 4 for it. The fourth argument is the size of the binary blob argument we are supposedly going to send; since we are not actually sending a blob, we always send a 0 here.

The response will be a message with type 4402, containing four arguments. The first argument echoes back our request type, which was 2004. The second always seems to be 0. The third contains the length of the waveform preview in bytes. If this value is 0, the fourth argument will be omitted from the response. When present, the fourth argument is a blob containing the actual bytes of the waveform preview, as shown below.

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1044020f041400100000000c (12)060606030000000000000000201100002004110000000011length1430lengthWaveform preview bytes40i+00
Waveform preview response message.

For this kind of waveform preview request, there are 900 (decimal) bytes of waveform data returned. The first 800 of them contain 400 columns of waveform data, in the form of two-byte pairs, where the first byte is the pixel height of the waveform at that column (a value ranging from 0 to 31), and the second byte represents whiteness, where 0 is blue and 7 is fully white. My players seem to only pay attention to the highest bit of whiteness, drawing the waveform as either very dark or light blue accordingly.

To experiment with this, start up dysentery in a Clojure REPL and connect to a player as described in Experimenting with Metadata, then evaluate an expression like:

(db/request-waveform-preview p2 3 1060)

Replace the arguments with the var holding your player connection, the proper Sr number for the media slot the track is found in, and the rekordbox ID of the track, and dysentery will open a window like this showing the waveform preview:

Waveform preview

The remaining hundred bytes appear to contain an even more compact 100-column preview representation of the waveform which is shown on pre-Nexus CDJ 900 players as shown below. Our best guess is that for each byte, the four low- order bits encode the height of the waveform in that column, and the high-order bits may encode saturation again?

Tiny waveform preview

Detailed Waveforms

Requesting the detailed waveform is very similar to requesting the preview, but the request type and arguments are slightly different. To request the detailed waveform of a track, send a message with type 2904 containing the rekordbox ID of the track, like the one shown below:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1029040f031400100000000c (12)0606060000000000000000002011D08SrTr11rekordbox1100000000
Waveform detail request message.

As usual, TxID should be incremented each time you send a query, and will be used to identify the response messages. D should have the same value you used in your initial query context setup packet, identifying the device that is asking the question. Sr and Tr are the slot in which the track being asked about can be found and its track type; each has the same values used in CDJ status packets. Finally, rekordbox identifies the specific waveform preview you are requesting, as found in a CDJ status packet or playlist response. Since this is a graphical request, I would expect the value after D, which identifies the location of the menu for which data is being loaded, to be 8 like it is for others, but for some reason it is 1, which usually means the main menu…​ maybe because the scrolling waveform appears in the same location on the display as the main menu? In many ways this protocol is a mystery wrapped in an enigma.

The waveform detail response is essentially identical to the waveform preview response, with just the type numbers changed. It will be a message with type 4a02, containing four arguments. The first argument echoes back our request type, which was 2904. The second always seems to be 0. The third contains the length of the waveform detail in bytes. If this value is 0, the fourth argument will be omitted from the response. When present, the fourth argument is a blob containing the actual bytes of the waveform detail, as shown below:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID104a020f041400100000000c (12)060606030000000000000000201100002904110000000011length1430lengthWaveform bytes40i+00
Waveform detail response message.

The content of the waveform detail is simpler and more compact than the waveform preview. Every byte reperesents one segment of the waveform, and there are 150 segments per second of audio. (These seem to correspond to “half frames” following the seconds in the player display.) Each byte encodes both a color and height. The three high-order bits encode the color, ranging from darkest blue at 0 to near-white at 7. The five low-order bits encode the height of the waveform at that point, from 0 to 31 pixels.

Frames

There are various places in the protocol and exported database files that refer to track playback positions in terms of frames or half frames. This probably comes from the language of digital audio files. In the case of CDJs, audio is played at the rate of 75 frames per second, and the formats can address locations that are at half-frame boundaries, or 150 distinct track locations per second of audio.

You can see this reflected on the player display where time is counted in units of minutes, seconds, and frames, and the frame field has a decimal point, but the value displayed always ends with .0 or .5, and ranges from 00.0 to 74.5.

Requesting Nxs2 Track Waveforms

Thanks to some wonderful analysis by @jan2000 we now know how to request and interpret the higher resolution and color waveforms supported by the nxs2 series of players.

Both the enhanced waveform preview and enhanced detail are requested using variations on the same request type, which asks for specific content from the ANLZ0000.EXT file created by rekordbox. The Crate Digger project which has its own analysis document, is focused on obtaining and interpreting these files.

To request the enhanced waveform preview of a track, send a message with type 2c04 containing the rekordbox ID of the track, the tag type identifier PWV4 and the file extension identifier EXT encoded as four-byte numbers, holding the ASCII in big-endian (backwards) order:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID102c040f041400100000000c (12)0606060600000000000000002011D01SrTr11rekordbox1134565750113000545845
Nxs2 waveform preview request message.

As usual, TxID should be incremented each time you send a query, and will be used to identify the response messages. D should have the same value you used in your initial query context setup packet, identifying the device that is asking the question. Sr and Tr are the slot in which the track being asked about can be found and its track type; each has the same values used in CDJ status packets. For some reason the value after D, which identifies the location of the menu for which data is being loaded, is 1 in this case, rather than the 8 we would expect to see for a graphical data request. rekordbox identifies the specific track whose analysis you are requesting, as found in a CDJ status packet or playlist response, and the final two numeric arguments specify that you are interested in the PWV4 tag (which holds the enhanced waveform preview) from the track’s EXT extended analysis file.

The response will be a message with type 4f02, containing five arguments. The first argument echoes back our request type, which was 2c04. The second always seems to be 0. The third contains the length of the requested tag (holding the enhanced waveform preview) in bytes. If this value is 0, the fourth argument will be omitted from the response. When present, the fourth argument is a blob containing the actual bytes of the enhanced waveform preview, as shown below. The fifth argument has an unknown purpose and so far seems to always have the value 0.

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID104f020f051400100000000c (12)060606030600000000000000201100002c04110000000011length1430lengthExtended waveform preview bytes40i+001100000000
Nxs2 waveform preview response message.

The extended preview data begins at byte 34 and is 7,200 (decimal) bytes long, representing 1,200 columns of waveform preview information.

The color waveform preview entries are the most complex of any of the waveform tags. See the discussion on Github for how the analysis was performed. @jan2000 created an audio file containing a 10 second sine wave sweep from 20 Hz to 20 kHz, and analyzed that in rekordbox. The results looked like this:

Sine sweep preview

As a summary, the top six stripes plot the values of each six channels of waveform preview information. The first byte of data is the first column of the top stripe, the next byte is the first column of the second stripe, and so on, until we reach the seventh byte, which is the second column of the first stripe.

We are not sure what the top two stripes represent, but they do seem to have an effect on the blue version of the waveform preview, so they somehow encode “whiteness”. The next stripe, corresponding to byte 2 of each column, indicates how much sound energy is present in the bottom half of the frequency range (it drops around 10 KHz). The stripe corresponding to byte 3 reflects how much sound energy is present in the bottom third of the frequency range, byte 4 reflects how much sound energy is in the middle of the frequency range, and byte 5 tracks the sound energy in the top of the frequency range.

The stripe labeled “color” reflect’s @jan2000’s algorithm for combining bytes 3, 4, and 5 into a color preview, and the bottom stripe is his approach for deriving the blue preview from that and the other two stripes.

The calculations used by Beat Link to build its own color previews can be found in the segmentColor and segmentHeight methods of the WaveformPreview class, and the way they are used to draw the actual graphical representation can be found in the updateWaveform method of the WaveformPreviewComponent class. These produce attractive results, but it is certainly possible that refinements can be found in the future.

As mentioned, requesting the detailed color waveform is a simple variant of the request used to obtain the preview. The same request type is used, and the only difference is that the tag type requested to obtain the scrollable detail view is PWV5. The full request is shown here:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID102c040f041400100000000c (12)0606060600000000000000002011D01SrTr11rekordbox1135565750113000545845
Nxs2 waveform detail request message.

As usual, TxID should be incremented each time you send a query, and will be used to identify the response messages. D should have the same value you used in your initial query context setup packet, identifying the device that is asking the question. Sr and Tr are the slot in which the track being asked about can be found and its track type; each has the same values used in CDJ status packets. For some reason the value after D, which identifies the location of the menu for which data is being loaded, is 1 in this case, rather than the 8 we would expect to see for a graphical data request. rekordbox identifies the specific track whose analysis you are requesting, as found in a CDJ status packet or playlist response, and the final two numeric arguments specify that you are interested in the PWV5 tag (which holds the enhanced waveform detail) from the track’s EXT extended analysis file.

The response will be a message with type 4f02, containing five arguments. The first argument echoes back our request type, which was 2c04. The second always seems to be 0. The third contains the length of the requested tag (holding the enhanced waveform detail) in bytes. If this value is 0, the fourth argument will be omitted from the response. When present, the fourth argument is a blob containing the actual bytes of the enhanced waveform segments, as shown below. The fifth argument has an unknown purpose and so far seems to always have the value 0.

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID104f020f051400100000000c (12)060606030600000000000000201100002c04110000000011length1430lengthExtended waveform detail bytes40i+001100000000
Nxs2 waveform detail response message.

The extended waveform detail data begins at byte 34 and has a variable length, but a vastly simpler structure than the waveform preview. Every pair of bytes represents one segment of the waveform, and there are 150 (decimal) segments per second of audio. (These seem to correspond to “half frames” following the seconds in the player display.) Each pair of bytes encodes the height of the waveform at that segment as a five bit value, along with three bits each of red, green, and blue intensity, arranged as shown below:

fedcba9876543210redgreenblueheight0000
Nxs2 waveform detail segment bits.

Requesting Cue Points and Loops

This section discusses how to obtain cue points and loops which are compatible with original nexus players. See Requesting Nxs2 Cue Points and Loops for how you can obtain a newer format which includes hot cue colors, DJ-assigned comment text, and hot cues beyond C.

The locations of the cue points and loops stored in a track can be obtained with a request like this:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1021040f021400100000000c (12)0606000000000000000000002011D08SrTr11rekordbox
Cue point request message.

As always, TxID should be incremented each time you send a query, and will be used to identify the response messages. D should have the same value you used in your initial query context setup packet, identifying the device that is asking the question. Sr and Tr are the slot in which the track being asked about can be found and its track type; each has the same values used in CDJ status packets. The value after D, which identifies the location of the menu for which data is being loaded, is 8 which is what we expect for a graphical or data request. rekordbox is the datbase ID of the specific track whose cue points you are requesting, as found in a CDJ status packet or playlist response.

The response will be a message with type 4702, containing nine arguments. The first argument echoes back our request type, which was 2104. The second always seems to be 0. The third contains the length of the blob containing cue and loop points in bytes, which seems redundant, because that is also conveyed in the fourth argument itself, which is a blob containing the actual bytes of the cue and loop points, as shown below. However, if there are no cue or loop points, this value will be 0, and the following blob field will be completely omitted from the response, so you must not try to read it!

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1047020f091400100000000c (12)060606030606060603000000201100002104110000000011length11430length1Cue and loop point bytes40i+00110000002411numhot11numcue11i+00i+10length214length2Unknown bytesi+20i+00
Cue point response message.

The fifth argument is a number with uncertain purpose. It always seems to have the value 0x24, which may be telling us the size of each cue/loop point entry in argument 4 (they do seem to each take up 24 bytes, as shown below). The sixth argument, shown as numhot, seems to contain the number of hot cue entries found in argument 4, and the seventh, numcue seems to contain the number of ordinary memory point cues. The eighth argument is a number containing the length of the second binary field which follows it. We don’t know the meaning of the final, binary, argument.

As described above, the first binary field in the cue point response is divided up in to 24-byte entries, each of which potentially holds a cue or loop point. They are not in any particular order, with respect to the time at which they occur in the track. They each have the structure shown below:

0123456789abcdefFlFcH000000000000000000cue0010loop0000000000000000000000002000000000
Cue/loop point entry.

The first byte, Fl, has the value 01 if this entry specifies a loop, or 00 otherwise. The second byte, Fc, has the value 01 if this entry contains a cue point, and 00 otherwise. Entries with loops have the value 01 in both of these bytes, because loops also act as cue points. If both values are 00, the entry is ignored (it is probably a leftover cue point that was deleted by the DJ). The third byte, labeled H, is 00 for ordinary cue points, but has a non-zero value if this entry defines a hot cue. Hot cues A through C are represented by the values 01, 02, and 03.

The actual location of the cue and loop points are in the values cue and loop. These are both 4-byte integers, and like beat grid positions, but unlike essentially every other number in the protocol, they are sent in little-endian byte order. For non-looping cue points, only cue has a meaning, and it identifies the position of the cue point in the track, in second units. For loops, cue identifies the start of the loop, and loop identifies the end of the loop, again in second units.

Requesting Nxs2 Cue Points and Loops

For tracks that have been exported since the introduction of the nxs2 series of players, rekordbox includes a richer set of information about cue points and loops. In addition to the information described in Requesting Cue Points and Loops, you can learn about any custom colors a DJ has assigned to their hot cues, as well as optional text comments describing hot cues, memory points, and loops. And while older players only supported hot cues A through C, this new format returns more.

In order to work well in mixed-player environments, the firmware of even older players has been updated to return this information if it is found in the exported data, so it is worth trying to ask for it. If the query described in this section fails, then you can fall back on the one shown in Requesting Cue Points and Loops.

Enhanced information about the cue points and loops stored in a track can be obtained with a request like this:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID102b040f031400100000000c (12)0606060000000000000000002011D08SrTr11rekordbox1100000000
Extended cue point request message.

As always, TxID should be incremented each time you send a query, and will be used to identify the response messages. D should have the same value you used in your initial query context setup packet, identifying the device that is asking the question. Sr and Tr are the slot in which the track being asked about can be found and its track type; each has the same values used in CDJ status packets. The value after D, which identifies the location of the menu for which data is being loaded, is 8 which is what we expect for a graphical or data request. rekordbox is the datbase ID of the specific track whose extended cue information you are requesting, as found in a CDJ status packet or playlist response.

The response will be a message with type 4e02, containing five arguments. The first argument echoes back our request type, which was 2b04. The second always seems to be 0. The third contains the length of the blob containing cue and loop points in bytes, which seems redundant, because that is also conveyed in the fourth argument itself, which is a blob containing the actual bytes of the cue and loop points, as shown below. However, if there are no cue or loop points, this value will be 0, and the following blob field will be completely omitted from the response, so you must not try to read it!

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID104e020f051400100000000c (12)060606030600000000000000201100002b04110000000011length1430lengthCue and loop point bytes40i+0011numentries
Extended cue point response message.

The fifth argument reports the number of cue point entries found in the blob. Because each extended cue entry can include a comment string, they have variable lengths, so each entry needs to be examined in order to figure out where it ends, and therefore the next one begins. The extended entry structure is shown below:[3]

0123456789abcdeflengthH00Flcue0010loop20cid3040lenccomment50i+00crgbi+00i+10
Extended cue/loop point entry.

The first four bytes, length, hold the length of the current entry, and so can be used to find the start of the next entry. Like other numbers in cue point responses (and unlike most numbers in the protocol), this is sent in little-endian byte order. The fourth byte, labeled H, is 00 for ordinary cue points, but has a non-zero value if this entry defines a hot cue. Hot cues A through H are represented by the values 01 through 08.

Fl, at byte 06 has the value 01 if this entry specifies a memory point and 02 if it is a loop. If both H and Fl are 00, the entry should be ignored (it is probably a leftover cue point that was deleted by the DJ).

The actual location of the cue and loop points are in the values cue at bytes 0c-0f and loop at bytes 10-13. These are both 4-byte integers, and as noted above, they are sent in a little-endian byte order. For non-looping cue points, only cue has a meaning, and it identifies the position of the cue point in the track, in second units. For loops, cue identifies the start of the loop, and loop identifies the end of the loop, again in second units.

cid at byte 22 is used only for ordinary memory points and loops. If it has a non-zero value, it identifies a row in the color table that specifies the color the user assigned to the memory point or loop. Hot cues have their own color information later in the entry.

Some extended cue entries are incomplete, and their length indicates they end before the comment, or include the comment but end before the hot cue color information. Code that processes them needs to be prepared to handle this, and treat such partial cues as having no comment and/or hot cue color.

lenc at byte 48 is a two-byte, little-endian integer that contains the length of the comment. If it is zero, there is no comment. Otherwise, comment will follow lenc, taking up that many bytes, as a string (in UTF-16 encoding with a trailing 0000 character). So if lenc isn’t zero, it will be an even number with minimum value four, representing a comment that is one character long.

Four bytes past the end of comment (in other words, starting lenc + 4e past the start of the entry) there are four one-byte values containing hot cue color information. 𝑐 appears to be a code identifying the color with which rekordbox displays the cue, by looking it up in a table. There have been sixteen codes identified, and their corresponding RGB colors can be found by looking at the findRecordboxColor static method in the Beat Link library’s CueList class. The next three bytes, r, g, and b, make up an RGB color specification which is similar, but not identical, to the color that rekordbox displays. We believe these are the values used to illuminate the RGB LEDs in a player that has loaded the hot cue. When no color is associated with the hot cue, all four of these bytes have the value 00.

We do not know what, if anything, is sent in the remaining bytes of the the entry, so they are shown as blank in the byte field diagram above.

Requesting Song Structure (Phrase Information)

Starting with rekordbox version 6, when DJs export tracks on which they have performed phrase analyisis, that information is exported into the .EXT file as a PSSI tag. (Pioneer Song Structure Information?) This data can be obtained using an analysis tag request, specifyign PSSI (1230197584) for the tag type and, as usual, EXT for the file extension:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID102c040f041400100000000c (12)0606060600000000000000002011D01SrTr11rekordbox1149535350113000545845
Song structure request message.

As usual, TxID should be incremented each time you send a query, and will be used to identify the response messages. D should have the same value you used in your initial query context setup packet, identifying the device that is asking the question. Sr and Tr are the slot in which the track being asked about can be found and its track type; each has the same values used in CDJ status packets. For some reason the value after D, which identifies the location of the menu for which data is being loaded, is 1 in this case, rather than the 8 we would expect to see for a graphical data request. rekordbox identifies the specific track whose analysis you are requesting, as found in a CDJ status packet or playlist response, and the final two numeric arguments specify that you are interested in the PSSI tag (which holds the song structure information) from the track’s EXT extended analysis file.

The response will be a message with type 4f02, containing five arguments. The first argument echoes back our request type, which was 2c04. The second always seems to be 0. The third contains the length of the requested tag (holding the enhanced waveform preview) in bytes. If this value is 0, the fourth argument will be omitted from the response. When present, the fourth argument is a blob containing the actual bytes of the song structure information, as shown below. The fifth argument has an unknown purpose.

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID104f020f051400100000000c (12)060606030600000000000000201100002c04110000000011length1430lengthSong structure bytes40i+001100000000
Song structure response message.

The song structure data is as described in the raw file.

Requesting New Tags

As new players started adding new features that used new analyis data at a rapid pace, it would have become cumbersome to introduce new protocol messages to request each, and to update the firmware of older players to add support for all these new messages. Instead, a general mechanism was added which allows a player to request a tag from an analysis file by identifying the track, the file extension, and the four-character code which identifies the tag (section) desired.

This is how nxs2 waveforms are requested, for example, and more recently, song structure information as well.

To request a particular analysis file section for a track, send a message with type 2c04 containing the rekordbox ID of the track, the section type identifier tag, and the file extension identifier extension encoded as four-byte numbers, holding the ASCII in big-endian (backwards) order. If there are fewer than four characters, for example in the case of the file extensions, pad the ASCII bytes with nul (zero) characters at the end before reversing them into big-endian order:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID102c040f041400100000000c (12)0606060600000000000000002011D01SrTr11rekordbox11tag1130extension
Analysis tag request message.

As usual, TxID should be incremented each time you send a query, and will be used to identify the response messages. D should have the same value you used in your initial query context setup packet, identifying the device that is asking the question. Sr and Tr are the slot in which the track being asked about can be found and its track type; each has the same values used in CDJ status packets. For some reason the value after D, which identifies the location of the menu for which data is being loaded, is 1 in this case, rather than the 8 we would expect to see for a graphical data request. rekordbox identifies the specific track whose analysis you are requesting, as found in a CDJ status packet or playlist response, and the final two numeric arguments specify the particular tag you want and which file it can be found in. To look up a track’s nxs2 waveform preview, tag would hold the numerical representation of PWV4, 878073680, and extension would hold the numerical representation of EXT, 5527621.

The response will be a message with type 4f02, containing five arguments. The first argument echoes back our request type, which was 2c04. The second always seems to be 0. The third contains the length of the requested tag in bytes. If this value is 0, the fourth argument will be omitted from the response. When present, the fourth argument is a blob containing the actual bytes of the tag you requested. The fifth argument has an unknown purpose.

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID104f020f051400100000000c (12)060606030600000000000000201100002c04110000000011length1430lengthAnalysis tag bytes40i+001100000000
Analysis tag response message.

The tag data begins at byte 34.

Requesting All Tracks

If you want to cache all the metadata associated with a media stick, this query is a good starting point. Send a packet with type 1004:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1010040f021400100000000c (12)0606000000000000000000002011D01SrTr11sort
Full track list request message.

As always, TxID should be 1 for the first query packet you send, 2 for the next, and so on. D should have the same value you used in your initial query context setup packet, identifying the device that is asking the question. Sr is the slot in which the track being asked about can be found, and Tr is the type of the track; these two bytes have the same values used in CDJ status packets. The sort parameter determines the order in which the tracks are sorted, and that also affects the item type returned, along with the secondary information (beyond the title) that it contains about the track, as described in Alternate Track List Sort Orders below. We will start out assuming the tracks are being requested in title order, which can be done by sending a sort argument value of 0, and that the DJ has configured the media device to show artists as the second column.

Track list requests (just like metadata requests) are built on the mechanism that is used to request and draw scrollable menus on the CDJs, explored in more breadth in Menu Requests. The player responds with a success indicator, saying it is ready to send these “menu items” and reporting how many of them are available, much like shown in the metadata available response, although the first argument will be 1004 to reflect the message type we just sent, rather than 2002 as it was for the metadata request.

As with metadata, the next step is to send a “render menu” request to get the actual results. But the number of results available is likely to be much higher than in the earlier example, because we have asked about all tracks in the media slot. That means we will probably need more than one “render menu” request to get them all.

I don’t know how many items you can safely ask for at one time. I have had success with values as high as 64 on my CDJ-2000 nexus players, but they failed when asking for numbers in the thousands. So to be safe, you should ask for the results in chunks of 64 or smaller, by setting limit and total to the lesser of 64 and the remaining number of results you want, and incrementing offset by 64 in each request until you have retrieved them all.

As with metadata requests, you will get back two more messages than you ask for, because you first get a menu header message (with type 4001), then the requested menu items are sent (with type 4101), and finally these are followed by a menu footer message (with type 4201). This wrapping happens with all “render menu” requests, and the menu footer is an easy way to know you are done, although you can also count the messages.

The details of the menu items are slightly different than in the case of a metadata request. In the example where you are retrieving tracks in the default order, with the second column configured to be artists, they will have the content shown in the following table:

Table 4. Track List entries with Artists.
Arg Type Meaning

1

number

artist id

2

number

rekordbox id of track

3

number

length in bytes of Label 1

4

string

Label 1: Track Title

5

number

length in bytes of Label 2

6

string

Label 2: Artist Name

7

number

type of this item, 0704 for “Title and Artist”

8

number

some sort of flags field, details still unclear

9

number

unknown

10

number

unknown

11

number

unknown

12

number

unknown

Alternate Track List Sort Orders

As noted above, you can request the track list in a diferent order by supplying a different value for the sort parameter. The value 0 gives the order just described, with the default second column information configured for the media. The sort values discovered so far are shown in the following table, and return menu items with the specified item type values in argument 7.

In all these alternate orders, Arg 2 still holds the ID of the track, and Arg 4 (Label 1) its title, but Arg 1 holds a different ID depending on the sort chosen, and Arg 6 (Label 2) a different string, as described in the table:

Table 5. Sort Orders.
Sort Type Sorted By Arg 1 Arg 6 (Label 2)

01

0704

Title

Artist ID

Artist name

02

0704

Artist name

Artist ID

Artist name

03

0204

Album name

Album ID

Album name

04

0d04

BPM

BPM × 100

empty

05

0a04

Rating

Rating

empty

06

0604

Genre

Genre ID

Genre name

07

2304

Comment

Comment ID

Comment text

08

0b04

Time (track duration)

Length in seconds

empty

09

2904

Remixer

Remixer ID

Remixer name

0a

0e04

Label

Label ID

Label name

0b

2804

Original Artist

Original Artist ID

Original Artist name

0c

0f04

Key

Key ID

Key text

0d

1004

Bit rate

Bit rate

empty

10

2a04

DJ play count

Play count

empty

11

2e04

Date added

Date ID

Date text

To experiment with this, start up dysentery in a Clojure REPL and connect to a player as described in Experimenting with Metadata, then evaluate an expression like:

(db/request-track-list p2 3)

Replace the arguments with the var holding your player connection and the proper Sr number for the media slot containing the tracks you want to list. You can also add a third argument to specify a sort order, like this to sort all the tracks in the USB slot by BPM:

(db/request-track-list p2 3 4)

Playlists

If you want to allow your user to browse the content of the media, you can navigate the playlist folder hierarchy and deal with only specific playlists. This process is essentially the same as asking for all tracks, except that in the playlist request you specify the playlist or folder that you want to list. To start at the root of the playlist folder hierarchy, you request folder 0. A playlist request has the following structure:

0123456789abcdefstartTxIDtypeargstags11872349ae11TxID1011050f041400100000000c (12)0606060600000000000000002011D01SrTr11sort11id1130folder?
Playlist request message.

As always, TxID should be 1 for the first query packet you send, 2 for the next, and so on. D should have the same value you used in your initial query context setup packet, identifying the device that is asking the question. Sr is the slot in which the track being asked about can be found, and Tr is the type of the track; these two bytes have the same values used in CDJ status packets. You specify the ID of the playlist or folder you want to list in the id argument, and set folder? 1 if you are asking for a folder, and 0 if you are asking for a playlist. As noted above, to get the top-level list of playlists, ask for folder 0, by passing an id of 0 and passing folder? as 1.

Much as when listing all tracks, the response may tell you there are more entries in the playlist than you can retreive in a single request, so you should follow the procedure outlined in Requesting All Tracks to request your results in smaller batches. The followup queries that you send are identical for playlists as they are described in that section. The actual menu items returned when you are asking for a folder have the content shown in the following table:

Table 6. Folder List entries.
Arg Type Meaning

1

number

parent folder id

2

number

id of playlist or folder

3

number

length in bytes of Label 1

4

string

Label 1: Name of playlist or folder

5

number

length in bytes of Label 2

6

string

Label 2: empty

7

number

type of this item, 01 for folder, 08 for playlist

8

number

unknown

9

number

unknown

10

number

playlist position

11

number

unknown

12

number

unknown

When you have requested a playlist (by passing its id and a value of 0 for folder?) the responses you get are track list entries, just like when you request all tracks as shown in Requesting All Tracks. And just as in that section, you can get the results in a particular order by specifying a value for sort. The supported values and corresponding item types returned seem to be the same as described there. Additionally, if you pass a sort value of 09, the playlist entries will come back sorted by track title, Label 2 will be empty, and the item type will be 2904.

To experiment with this, start up dysentery in a Clojure REPL and connect to a player as described in Experimenting with Metadata, then evaluate an expression like:

(db/request-playlist p2 3 1)

Replace the arguments with the var holding your player connection, the proper Sr number for the media slot containing the playlist you want to list, and the playlist ID. You can also add a third argument to specify that you want to list a folder, like this using folder ID 0 to request the root folder:

(db/request-playlist p2 3 0 true)

Finally, you can add a fourth argument to specify a sort order, like this to sort all the tracks in playlist 12 by genre:

(db/request-playlist p2 3 12 false 6)

Disconnecting

If you want to be polite about the fact that you are done talking to the dbserver, you can send it a message like the one shown below. This will cause the player to disconnect from its side.

0123456789abcdefstartTxIDtypeargstags11872349ae11fffffffe1001000f001400100000000c (12)000000000000000000000000
Connection teardown message.

Note that this uses the same “magic” TxID value, fffffffe, that is used by the query context setup message.

Experimenting with Metadata

The best way to get a feel for the details of working with these messages is to load dysentery into a Clojure REPL, as described on the project page, and play with some of the functions in the dysentery.dbserver namespace. Have no more than three players connected and active on your network, so you have an unused player number for dysentery to use. In this example, player number 1 is available, so we set dysentery up to pose as player 1 (user input is shown in bold):

> lein repl
nREPL server started on port 53806 on host 127.0.0.1 -
 nrepl://127.0.0.1:53806
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.8.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_77-b03
dysentery loaded.

dysentery.core> (view/watch-devices :player-number 1)
Looking for DJ Link devices...
Found:
   DJM-2000nexus 33 /172.16.42.3
   CDJ-2000nexus 2 /172.16.42.4
To communicate create a virtual CDJ with address
  /172.16.42.2, MAC address 3c:15:c2:e7:08:6c,
  and use broadcast address /172.16.42.255
:socket #object[java.net.DatagramSocket 0x22b952b1
                 "java.net.DatagramSocket@22b952b1"],
:watcher #future[:status :pending, :val nil 0x3eb8f41b]

dysentery.core> (def p2 (db/connect-to-player 2 1))
Transaction: 4294967294, message type: 0x4000
 (requested data available), argument count: 2, arguments:
  number:          0 (0x00000000) [request type]
  number:          2 (0x00000002) [# items available]
#'dysentery.core/p2

dysentery.core> (def md (db/request-metadata p2 2 1))
Sending > Transaction: 1, message type: 0x2002
 (request track metadata), argument count: 2, arguments:
  number:   16843265 (0x01010201) [player, menu, media, 1]
  number:          1 (0x00000001) [rekordbox ID]
Received > Transaction: 1, message type: 0x4000
 (requested data available), argument count: 2, arguments:
  number:       8194 (0x00002002) [request type]
  number:         11 (0x0000000b) [# items available]
      Sending > Transaction: 2, message type: 0x3000
       (render menu), argument count: 6, arguments:
  number: 16843265 (0x01010201) [player, menu, media, 1]
  number:        0 (0x00000000) [offset]
  number:       11 (0x0000000b) [limit]
  number:        0 (0x00000000) [unknown (0)?]
  number:       11 (0x0000000b) [len_a (= limit)?]
  number:        0 (0x00000000) [unknown (0)?]
Received 1  > Transaction: 2, message type: 0x4001
 (rendered menu header), argument count: 2, arguments:
  number:          1 (0x00000001) [unknown]
  number:          0 (0x00000000) [unknown]
Received 2  > Transaction: 2, message type: 0x4101
 (rendered menu item), argument count: 12, arguments:
  number:        1 (0x00000001) [numeric 1 (parent id)]
  number:        1 (0x00000001) [numeric 2 (this id)]
  number:       80 (0x00000050) [label 1 byte size]
  string: "Escape ft. Zoë Phillips" [label 1]
  number:        2 (0x00000002) [label 2 byte size]
  string: "" [label 2]
  number:        4 (0x00000004) [item type: Track Title]
  number: 16777216 (0x01000000) [column configuration?]
  number:        0 (0x00000000) [album art id]
  number:        0 (0x00000000) [playlist position]
  number:      256 (0x00000100) [unknown]
  number:        0 (0x00000000) [unknown]
...

Received 13  > Transaction: 2, message type: 0x4201
 (rendered menu footer), argument count: 0, arguments:
#'dysentery.core/md
dysentery.core>

In this interaction, after setting up the watcher so we can find players on the network, we set the var p2 to be a connection to player 2, in which we are posing as player 1. Then we submit a metadata request to p2, requesting the track in slot 2 (SD card), with rekordbox id 1. You can see the messages being sent and received to accomplish that. For more functions that you can call, including the very flexible experiment function, look at the source for the dysentery.dbserver namespace. Most of the response messages containing track metadata were omitted for brevity; you will get more meaningful results trying it with your own tracks, and then you can see all the details.


1. A nested list of playlists rather than an individual playlist.
2. Yes, unlike almost all numbers in the protocol, beat grid and cue point times are little-endian.
3. Thanks to Matt Positive, https://soundcloud.com/mattpositive, for packet captures!