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:
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 folowed 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 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:
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 all of
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.
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:
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:
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 flags 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 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 |
|
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 Bit 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 (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:
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!
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 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:
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. |
Waveform Previews
To request the 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 𝐷, 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 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 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.
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 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:
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:
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 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 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!
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 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!
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, 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:
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 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:
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 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:
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 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:
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 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:
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.