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
This approach only works when the CDJ is willing to respond to our requests, which often means we need to be using a real player number (and thus limits how many actual players can be on the network). Today we can also use Crate Digger to download and parse the entire database and track analysis files from a player USB as an alternate way of getting metadata. |
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:
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:
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.
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):
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 followed by 2 × length bytes containing the actual string characters.
The last character of the string is always NUL
, represented by 0000
:
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.
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:
Tag | Meaning |
---|---|
|
A string in UTF-16 big-endian encoding, with trailing NUL (zero) character. |
|
A binary blob. |
|
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:
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 belong to a (different) 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.
We suspect the reason for these limitations is that the database query protocol is stateful—you sometimes set up a query, then send multiple subsequent messages to page through the results—and so the players need a table to maintain that state for each database client. And old players had very little RAM, so the table probably has a fixed size, just big enough for the three other players that it might expect to talk to.
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:
Rekordbox Track Metadata
To ask for metadata about a particular rekordbox track, send a packet like this:
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:
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 the current menu, using a “render menu” request with type 3000
like this:
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. See Non-Rekordbox Track Metadata for other values that can be used.
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 initial 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 available metadata will have the specific values shown here:
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 the slots. They have the general structure shown here, and the arguments are listed in the Table below:
Arg | Type | Meaning |
---|---|---|
1 |
number |
Parent ID, such as an artist for a track item. |
2 |
number |
Main ID, such as rekordbox for a track item. |
3 |
number |
Length in bytes of Label 1. |
4 |
string |
Label 1 (main text, such as the track title or artist name, as appropriate for the item type). |
5 |
number |
Length in bytes of Label 2. |
6 |
string |
Label 2 (secondary text, e.g. artist name for playlist entries, where Label 1 holds the title). |
7 |
number |
Type of this item (see Menu Item Types). |
8 |
number |
Some sort of flag field, details still unclear. |
9 |
number |
Holds artwork ID when type is Track Title. |
10 |
number |
Playlist position when relevant, e.g. when listing a playlist. |
11 |
number |
Unknown. |
12 |
number |
Unknown. |
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 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.
Menu Footer Response
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).
Menu Item Types
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:
Type | Meaning |
---|---|
|
Folder (such as in the playlists menu)[1] |
|
Album title |
|
Disc |
|
Track Title |
|
Genre |
|
Artist |
|
Playlist |
|
Rating |
|
Duration (in seconds) |
|
Tempo |
|
Label |
|
Key |
|
Bit Rate |
|
Year |
Color None |
|
|
Color Pink |
|
Color Red |
|
Color Orange |
|
Color Yellow |
|
Color Green |
|
Color Aqua |
|
Color Blue |
|
Color Purple |
|
Comment |
|
History Playlist |
|
Original Artist |
|
Remixer |
|
Date Added |
Genre menu |
|
|
Artist menu |
|
Album menu |
|
Track menu |
|
Playlist menu |
|
BPM menu |
|
Rating menu |
|
Year menu |
|
Remixer menu |
|
Label menu |
|
Original Artist menu |
|
Key menu |
|
Date Added menu |
|
Color menu |
|
Folder menu |
|
Search “menu” |
|
Time menu |
|
Bit Rate menu |
|
Filename menu |
|
History menu |
|
Hot cue bank menu |
|
All |
|
Track Title and Album |
|
Track Title and Genre |
|
Track Title and Artist |
|
Track Title and Rating |
|
Track Title and Time |
|
Track Title and BPM |
|
Track Title and Label |
|
Track Title and Key |
|
Track Title and Bit Rate |
|
Track Title and Color |
|
Track Title and Comment |
|
Track Title and Original Artist |
|
Track Title and Remixer |
|
Track Title and DJ Play Count |
|
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.
All players may be able to offer album art embedded in the audio file, and CDJ-3000s make beat grids and waveforms available once they have completed their own analysis of the tracks.
To get the basic metadata 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 from older (nexus-era) hardware, your virtual CDJ needs to be sending properly-formatted status packets, not just device announcement packets. This limitation does not affect CDJ-3000s (which can also even offer beat grids and waveforms for tracks they have analyzed locally, without rekordbox). |
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:
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 metadata response. As with other requests for graphical or binary objects, 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!
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:

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 extra 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 extra 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 (prior to the CDJ-3000) 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.
As noted above, you can request beat grids even for tracks that were not analyzed by rekordbox when talking to CDJ-3000s; just use the track ID the player is reporting in its status packet, and correctly reflect the “unanalyzed” track type in your Tr byte. |
To request the beat grid of a track, send a message with type 2204
containing the rekordbox ID of the track:
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!
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:
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 18
–1b
.
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, and CDJ-3000 3-band waveforms. |
As noted above, you can request waveforms (of all types and styles) even for tracks that were not analyzed by rekordbox when talking to CDJ-3000s; just use the track ID the player is reporting in its status packet, and correctly reflect the “unanalyzed” track type in your Tr byte. |
Waveform Previews
To request the monochrome waveform preview of a track, send a message with type 2004
containing the rekordbox ID of the track:
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 D, 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.
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 CDJ-2000 nexus 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:

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?

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:
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:
The content of the waveform detail is simpler and more compact than the waveform preview. Every byte represents 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 the 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.
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:
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.
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 the waveform tags.
See the discussion on GitHub for how the analysis was performed.
@jan2000
created an audio file containing a ten-second sine wave sweep from 20 Hz to 20 kHz, and analyzed that in rekordbox. The results looked like this:

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” reflects @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:
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.
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:
Requesting CDJ-3000 3-Band Waveforms
With the help of some additional analysis we know now how to request and interpret the 3-band style waveforms introduced with the CDJ-3000.
Both the 3-band waveform preview and detail are requested using variations on the same request type, which asks for specific content from the ANLZ0000.2EX
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 3-band waveform preview of a track, send a message with type 2c04
containing the rekordbox ID of the track, the tag type identifier PWV6
and the file extension identifier 2EX
encoded as four-byte numbers, holding the ASCII in big-endian (backwards) order:
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 PWV6
tag (which holds the 3-band waveform preview) from the track’s 2EX
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 3-band 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 3-band waveform preview, as shown below.
The fifth argument has an unknown purpose and so far seems to always have the value 0.
The 3-band preview data begins at byte 34
and is 3,600 (decimal) bytes long, representing 1,200 columns of waveform preview information.
The three-band waveform preview entries each consist of three one-byte height values representing the mid-range, high, and low frequencies, in that order. There is some scaling involved, and they seem to be drawn stacked on top of each other, with the lows in dark blue, the mid-range in amber, and the highs in white.
Requesting the detailed 3-band 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 PWV7
.
The full request is shown here:
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 PWV7
tag (which holds the 3-band waveform detail) from the track’s 2EX
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 3-band 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 3-band waveform segments, as shown below.
The fifth argument has an unknown purpose and so far seems to always have the value 0.
The 3-band waveform detail data begins at byte 34
and has a variable length, but the same structure as the preview data.
Each detail entry is three bytes long, holding a series of one-byte height values representing the mid-range, high, and low frequencies, in that order.
There is a different kind of scaling involved in drawing these, and it seems to be non-linear.
We have not yet found an approach that perfectly matches what we see in rekordbox, but we are getting close and the results are already useful. The colors for low, mid-range, and high frequencies are the same as in the preview, but they are drawn on the same axis rather than being stacked. The area where low and mid-range frequencies overlap is drawn in brown, and their pure colors are seen where there is no overlap. The white high frequency is drawn last so it obscures any low or mid-range information underneath it. Recordbox actually seems to do some light blending, but it is so dim that we have not bothered to reproduce it so far.
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:
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 database 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!
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:
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:
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 database 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!
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]
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 (which would represent 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.
c 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 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 analysis, 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, specifying PSSI
(1230197584) for the tag type and, as usual, EXT
for the file extension:
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.
The song structure data is as described in the raw file.
Requesting New Tags
As new players started adding new features that used new analysis 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:
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.
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
:
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 from the ones seen with 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:
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, |
8 |
number |
some sort of flag 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 different 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:
Sort | Type | Sorted By | Arg 1 | Arg 6 (Label 2) |
---|---|---|---|---|
|
|
Title |
Artist ID |
Artist name |
|
|
Artist name |
Artist ID |
Artist name |
|
|
Album name |
Album ID |
Album name |
|
|
BPM |
BPM × 100 |
empty |
|
|
Rating |
Rating |
empty |
|
|
Genre |
Genre ID |
Genre name |
|
|
Comment |
Comment ID |
Comment text |
|
|
Time (track duration) |
Length in seconds |
empty |
|
|
Remixer |
Remixer ID |
Remixer name |
|
|
Label |
Label ID |
Label name |
|
|
Original Artist |
Original Artist ID |
Original Artist name |
|
|
Key |
Key ID |
Key text |
|
|
Bit rate |
Bit rate |
empty |
|
|
DJ play count |
Play count |
empty |
|
|
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:
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 retrieve 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:
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, |
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.
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.