{"id":13433137,"url":"https://github.com/Deep-Symmetry/beat-link","last_synced_at":"2025-03-17T10:33:27.844Z","repository":{"id":50358460,"uuid":"58163805","full_name":"Deep-Symmetry/beat-link","owner":"Deep-Symmetry","description":"Java library for synchronizing and communicating with Pioneer DJ Link equipment","archived":false,"fork":false,"pushed_at":"2024-08-22T12:53:46.000Z","size":3188,"stargazers_count":194,"open_issues_count":4,"forks_count":21,"subscribers_count":15,"default_branch":"main","last_synced_at":"2024-08-22T14:27:35.646Z","etag":null,"topics":["ableton-link","dj-link","java-library","network","pioneer"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Deep-Symmetry.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"Deep-Symmetry","liberapay":"deep-symmetry","custom":"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=LG5NLFL5T372W\u0026source=url"}},"created_at":"2016-05-05T22:13:31.000Z","updated_at":"2024-08-22T12:52:56.000Z","dependencies_parsed_at":"2024-01-17T00:38:54.346Z","dependency_job_id":"100b2120-f48e-457f-b028-bbe000c59253","html_url":"https://github.com/Deep-Symmetry/beat-link","commit_stats":null,"previous_names":[],"tags_count":41,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Deep-Symmetry%2Fbeat-link","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Deep-Symmetry%2Fbeat-link/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Deep-Symmetry%2Fbeat-link/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Deep-Symmetry%2Fbeat-link/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Deep-Symmetry","download_url":"https://codeload.github.com/Deep-Symmetry/beat-link/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221675298,"owners_count":16861860,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ableton-link","dj-link","java-library","network","pioneer"],"created_at":"2024-07-31T02:01:21.533Z","updated_at":"2025-03-17T10:33:27.832Z","avatar_url":"https://github.com/Deep-Symmetry.png","language":"Java","readme":"# beat-link\n\n[![project chat](https://img.shields.io/badge/chat-on%20zulip-brightgreen)](https://deep-symmetry.zulipchat.com/#narrow/stream/275322-beat-link-trigger)\n\u003cimg align=\"right\" width=\"275\" height=\"250\" alt=\"Beat Link\"\n      src=\"assets/BeatLink-logo-padded-left.png\"\u003e\n\nA Java library for synchronizing with beats from Pioneer DJ Link\nequipment, and finding out details about the tracks that are playing.\n\nSee\n[beat-link-trigger](https://github.com/Deep-Symmetry/beat-link-trigger#beat-link-trigger) and\n[beat-carabiner-java](https://github.com/Deep-Symmetry/beat-carabiner-java)\nfor examples of what you can do with this.\n\n[![License](https://img.shields.io/github/license/Deep-Symmetry/beat-link?color=blue)](#licenses)\n\n\n## Installing\n\nBeat-link is available through Maven Central, so to use it in your\nMaven project, all you need is to include the appropriate dependency.\n\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.deepsymmetry/beat-link/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.deepsymmetry/beat-link)\n\nClick the **maven central** badge above to view the repository entry\nfor beat-link. The proper format for including the latest release as a\ndependency in a variety of tools, including Leiningen if you are using\nbeat-link from Clojure, can be found in the **Dependency Information**\nsection.\n\nBeat link uses [slf4j](https://www.slf4j.org/manual.html) to allow you\nto integrate it with whatever Java logging framework your project is\nusing, so you will need to include the appropriate slf4j binding on\nyour class path.\n\nIt also uses the Deep Symmetry projects\n[electro](https://github.com/Deep-Symmetry/electro) (to synchronize\nwith the Ableton Link timeline) and\n[crate-digger](https://github.com/Deep-Symmetry/crate-digger) (to\ndownload and parse rekordbox database files from players).\n\nIf you want to manually install beat-link, you can download the\nlibrary from the\n[releases](https://github.com/Deep-Symmetry/beat-link/releases) page and\nput it on your project\u0026rsquo;s class path, along with\n[electro](https://github.com/Deep-Symmetry/electro/releases),\n[crate-digger](https://github.com/Deep-Symmetry/crate-digger/releases),\n[`slf4j-api.jar`](https://www.slf4j.org/download.html) and the slf4j\nbinding to the logging framework you would like to use.\n\n![Create library jar](https://github.com/Deep-Symmetry/beat-link/workflows/Create%20library%20jar/badge.svg)\n\nYou will also need\n[Remote Tea](https://sourceforge.net/projects/remotetea/) for creating and parsing old-version NFS packets, so Maven\nis by far your easiest bet, because it will take care of _all_ these\nlibraries for you.\n\n## Compatibility\n\nThis is in no way a sanctioned implementation of the protocols. It should be clear, but:\n\n\u003e [!WARNING]\n\u003e Use at your own risk! For example, there are reports that\n\u003e the XDJ-RX (and XDJ-RX2) crash when BLT starts, so don\u0026rsquo;t use\n\u003e it with one on your network. As Pioneer themselves\n\u003e [explain](https://forums.pioneerdj.com/hc/en-us/community/posts/203113059-xdj-rx-as-single-deck-on-pro-dj-link-),\n\u003e the XDJ-RX does not actually implement the protocol:\n\u003e\n\u003e \u0026ldquo;The LINK on the RX (and RX2) is ONLY for linking to rekordbox\n\u003e on your computer or a router with WiFi to connect rekordbox mobile.\n\u003e It can not exchange LINK data with other CDJs or DJMs.\u0026rdquo;\n\nWhile these techniques appear to work for us so far, there are many\ngaps in our knowledge, and things could change at any time with new\nreleases of hardware or even firmware updates from Pioneer.\n\n\u003e [!CAUTION]\n\u003e You should also not expect to be able to run Beat Link, or any\n\u003e project like it, on the same machine that you are running rekordbox,\n\u003e because they will compete over access to network ports. Also, as\n\u003e noted above, the XDJ-RX and XDJ-RX2 do not support the full DJ Link\n\u003e protocol, and so will not work with software like this. And, of\n\u003e course, controllers which don't even have Link (Ethernet) jacks\n\u003e can't work either.\n\n\u003e [!TIP]\n\u003e :white_check_mark: Beat Link seems to work great with CDJ-2000 Nexus\n\u003e and nxs2 gear, and release 7.3.0 supports CDJ-3000 features. It\n\u003e works fairly well (with less information available) with older\n\u003e CDJ-2000s. It has also been reported to work with XDJ-1000 gear, and\n\u003e (starting with version 0.6.0) with the XDJ-XZ as well. If you can\n\u003e try it with anything else, *please* let us know what you learn in\n\u003e [Beat Link Trigger's Zulip chat\n\u003e stream](https://deep-symmetry.zulipchat.com/#narrow/stream/275322-beat-link-trigger),\n\u003e or if you have worked out actionable details about something that\n\u003e could be improved, [open an\n\u003e Issue](https://github.com/Deep-Symmetry/beat-link/issues) or submit\n\u003e a pull request so we can all improve our understanding together.\n\u003e \n\u003e :construction: We have added experimental support for the Opus Quad, which, despite not supporting a DJ-Link network, seems to communicate with rekordbox to support lighting in a way that will allow us to offer good support in Beat Link once we reorganize the code to support working that way.\nThanks to [@cprepos](https://github.com/cprepos) for doing most of this work!\n\nIf something isn\u0026rsquo;t working with your hardware, and you don\u0026rsquo;t yet know\nthe details why, but are willing to learn a little and help figure it\nout, look at the\n[dysentery project](https://github.com/Deep-Symmetry/dysentery#dysentery),\nwhich is where we are organizing the research tools and results which\nmade programs like Beat Link possible.\n\n## Getting Help\n\n\u003ca href=\"https://zulip.com\"\u003e\u003cimg align=\"right\" alt=\"Zulip logo\"\n src=\"assets/zulip-icon-circle.svg\" width=\"128\" height=\"128\"\u003e\u003c/a\u003e\n\nDeep Symmetry\u0026rsquo;s projects are generously sponsored with hosting\nby \u003ca href=\"https://zulip.com\"\u003eZulip\u003c/a\u003e, an open-source modern team\nchat app designed to keep both live and asynchronous conversations\norganized. Thanks to them, you can \u003ca\nhref=\"https://deep-symmetry.zulipchat.com/#narrow/stream/275322-beat-link-trigger\"\u003echat\nwith our community\u003c/a\u003e, ask questions, get inspiration, and share your\nown ideas.\n\n## Usage\n\nSee the [API Documentation](https://deepsymmetry.org/beatlink/apidocs/)\nfor full details, but here is a nutshell guide:\n\n### Finding Devices\n\nThe [`DeviceFinder`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/DeviceFinder.html)\nclass allows you to find DJ Link devices on your network. To activate it:\n\n```java\nimport org.deepsymmetry.beatlink.DeviceFinder;\n\n// ...\n\n  DeviceFinder.getInstance().start();\n```\n\nAfter a second, it should have heard from all the devices, and you can\nobtain the list of them by calling:\n\n```java\n  DeviceFinder.getInstance.getCurrentDevices();\n```\n\nThis returns a list of [`DeviceAnnouncement`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/DeviceAnnouncement.html)\nobjects describing the devices that were heard from. To find out\nimmediately when a new device is noticed, or when an existing device\ndisappears, you can use\n[`DeviceFinder.getInstance().addDeviceAnnouncementListener()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/DeviceFinder.html#addDeviceAnnouncementListener-org.deepsymmetry.beatlink.DeviceAnnouncementListener-).\n\n### Responding to Beats\n\nThe [`BeatFinder`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/BeatFinder.html)\nclass can notify you whenever any DJ Link devices on your network report\nthe start of a new beat:\n\n```java\nimport org.deepsymmetry.beatlink.BeatFinder;\n\n// ...\n\n  BeatFinder.getInstance().addBeatListener(new BeatListener() {\n      @Override\n      public void newBeat(Beat beat) {\n         // Your code here...\n      }\n  });\n\n  BeatFinder.getInstance().start();\n```\n\nThe [`Beat`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/Beat.html)\nobject you receive will contain useful information about the state of the\ndevice (such as tempo) at the time of the beat.\n\n\u003e To fully understand how to respond to the beats, you will want to start\n\u003e [`VirtualCdj`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html)\n\u003e as described in the next section, so it can tell you important\n\u003e details about the states of all the players, such as which one is\n\u003e the current tempo master. Once that is running, you can pass the `Beat`\n\u003e object to [`VirtualCdj.getLatestStatusFor(beat)`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#getLatestStatusFor-org.deepsymmetry.beatlink.DeviceUpdate-)\n\u003e and get back the most recent status update received from the device\n\u003e reporting the beat.\n\nWith just the `Beat` object you can call\n[`getBpm()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/Beat.html#getBpm--)\nto learn the track BPM when the beat occurred,\n[`getPitch()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/Beat.html#getPitch--)\nto learn the current pitch (speed) of the player at that moment, and\n[`getEffectiveTempo()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/Beat.html#getEffectiveTempo--)\nto find the combined effect of pitch and track BPM\u0026mdash;the beats per minute\ncurrently being played. You can also call\n[`getBeatWithinBar()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/Beat.html#getBeatWithinBar--)\nto see where this beat falls within a measure of music.\n\nIf the `VirtualCdj` is active, you can also call the `Beat` object\u0026rsquo;s\n[`isTempoMaster()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/Beat.html#isTempoMaster--)\nmethod to find out if it was sent by the device that is currently in\ncontrol of the master tempo. (If `VirtualCdj` was not started, this\nmethod will throw an `IllegalStateException`.)\n\n### Getting Device Details\n\nTo find some kinds of information, like which device is the tempo master,\nhow many beats of a track have been played, or how many beats there are\nuntil the next cue point in a track, and any detailed information about\nthe tracks themselves, you need to have beat-link create a virtual\nplayer on the network. This causes the other players to send detailed status\nupdates directly to beat-link, so it can interpret and keep track of\nthis information for you.\n\n```java\nimport org.deepsymmetry.beatlink.VirtualCdj;\n\n// ...\n\n    VirtualCdj.getInstance().start();\n```\n\n\u003e The Virtual player is normally created using an unused device number\n\u003e in the range 5\u0026ndash;15 and the name `beat-link`, and announces its\n\u003e presence every second and a half. These values can be changed with\n\u003e [`setDeviceNumber()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#setDeviceNumber-byte-),\n\u003e [`setUseStandardPlayerNumber()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#setUseStandardPlayerNumber-boolean-),\n\u003e [`setDeviceName()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#setDeviceName-java.lang.String-),\n\u003e and\n\u003e [`setAnnounceInterval()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#setAnnounceInterval-int-).\n\nAs soon as it is running, you can pass any of the device announcements returned by\n[`DeviceFinder.getCurrentDevices()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/DeviceFinder.html#getCurrentDevices--)\nto [`getLatestStatusFor(device)`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#getLatestStatusFor-org.deepsymmetry.beatlink.DeviceAnnouncement-)\nand get back the most recent status update received from that device.\nThe return value will either be a\n[`MixerStatus`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/MixerStatus.html)\nor a [`CdjStatus`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/CdjStatus.html),\ncontaining a great deal of useful information.\n\n\u003e As described [above](#responding-to-beats), you can do the same thing\n\u003e with the `Beat` objects returned by [`BeatFinder`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/BeatFinder.html).\n\nIn addition to asking about individual devices, you can find out which\ndevice is the current tempo master by calling\n[`getTempoMaster()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#getTempoMaster--),\nand learn the current master tempo by calling\n[`VirtualCdj.getMasterTempo()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#getMasterTempo--).\n\nIf you want to be notified when either of these values change, or whenever\nthe player that is the current tempo master starts a new beat, you can use\n[`addMasterListener()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#addMasterListener-org.deepsymmetry.beatlink.MasterListener-).\n\nIf you are building an interface that wants to display as much detail\nas possible, you can request every device status update\nas soon as it is received, using\n[`addUpdateListener()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#addUpdateListener-org.deepsymmetry.beatlink.DeviceUpdateListener-).\n\n### Player Remote Control\n\nIn addition to learning about what is happening on other players, the\n`VirtualCdj` can ask them to do a limited number of things. You can\ntell them to load a track from any media currently mounted in a player\nby calling\n[`sendLoadTrackCommand()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#sendLoadTrackCommand-int-int-int-org.deepsymmetry.beatlink.CdjStatus.TrackSourceSlot-org.deepsymmetry.beatlink.CdjStatus.TrackType-).\nYou can cause them to start playing (if they are currently at the cue\nposition), or stop playing and return to the cue position, by calling\n[`sendFaderStartCommand()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#sendFaderStartCommand-java.util.Set-java.util.Set-).\n\nYou can tell players to turn Sync mode on or off by calling\n[`sendSyncModeCommand()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#sendSyncModeCommand-int-boolean-),\nand if there is no DJM on the network you can tell them that their\nchannels are on or off the air by sending\n[`sendOnAirCommand()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#sendOnAirCommand-java.util.Set-).\n(You can do that even with a DJM present, but your command will\nquickly be overridden by the next one sent by the DJM.)\n\n### Requesting Media Slot Details\n\nYou can ask the `VirtualCdj` to find out details about the media\nmounted in a player's media slot by calling\n[`sendMediaQuery()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#sendMediaQuery-org.deepsymmetry.beatlink.data.SlotReference-).\nIn order to receive the response, you have to previously have\nregistered a\n[`MediaDetailsListener`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/MediaDetailsListener.html)\ninstance using\n[`addMediaDetailsListener()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#addMediaDetailsListener-org.deepsymmetry.beatlink.MediaDetailsListener-);\nin general it's easier to just let the `MetadataFinder` take care of\nthis for you as described [below](#media-details).\n\n### Sending Status\n\nAs of version 0.4.0, the `VirtualCdj` can be configured to send status\npackets of its own, so that it can take over the role of tempo master,\nand even send beat packets to synchronize other CDJs with a musical\ntimeline of your choice. For this to work, it must be using a standard\nplayer number in the range 1\u0026ndash;4, configured before you start it as\ndescribed [above](#getting-device-details).\n\n\u003e The only way to be able to use the features that rely on sending\n\u003e status is to configure the `VirtualCdj` to use a device number in\n\u003e the range 1 to 4, like an actual CDJ, using\n\u003e `VirtualCdj.getInstance().setUseStandardPlayerNumber()` as described\n\u003e [above](#getting-device-details). You can only do that if you are\n\u003e using fewer than 4 CDJs, because you need to use a number that is\n\u003e not being used by any actual CDJ.\n\nAs long as that is true, you can turn on this feature by calling\n[`setSendingStatus()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#setSendingStatus-boolean-).\n\nOnce it is sending status, you can adjust details about what it sends\nby calling\n[`setTempo()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#setTempo-double-),\n[`setSynced()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#setSynced-boolean-),\n[`setOnAir()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#setOnAir-boolean-),\nand\n[`jumpToBeat()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#jumpToBeat-int-).\nTo simulate playing a track, call\n[`setPlaying()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#setPlaying-boolean-).\nDuring simulated playback, beat packets will sent at appropriate times\nfor the current tempo, and the beat number will advance appropriately.\nYou can find the current simulated playback time by calling\n[`getPlaybackPosition()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#getPlaybackPosition--).\n\n### Appointing the Tempo Master\n\nAs long as the `VirtualCdj` is sending status packets, you can tell\nplayers to become Tempo Master by calling\n[`appointTempoMaster()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#appointTempoMaster-int-),\nor have it become the Master itself by calling\n[`becomeTempoMaster()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#becomeTempoMaster--).\nWhile acting as tempo master, any tempo changes you make by calling\n[`setTempo()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#setTempo-double-)\nwill be followed by any players that are in Sync mode, and if the\n`VirtualCdj` is playing (and thus sending beats) you can call\n[`adjustPlaybackPosition()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/VirtualCdj.html#adjustPlaybackPosition-int-)\nto keep the players' beat grids aligned with an external sync source\n(this is how Beat Link Trigger aligns CDJs with Ableton Link).\n\nIn addition to responding to the above functions for setting its own\nSync, On-Air, and Master states, the VirtualCdj respects commands sent\nto it by other players or a DJM instructing it to do these things.\nWhile it is in Sync mode, it will stay aligned with the tempo master\nlike other players do.\n\n### Getting Track Metadata\n\nIf you want to be able to retrieve details about loaded tracks, such as\nthe title, artist, genre, length (in seconds), key, and even artwork\nimages, start the `MetadataFinder`, which will also start the\n`VirtualCdj` if it is not already running.\n\n```java\nimport org.deepsymmetry.beatlink.data.MetadataFinder;\n\n// ...\n\n    MetadataFinder.getInstance().start();\n```\n\n\u003e :warning: The only way to reliably retrieve metadata using the\n\u003e `MetadataFinder` on its own is to configure the `VirtualCdj` to use\n\u003e a device number in the range 1 to 4, like an actual CDJ, using\n\u003e `VirtualCdj.getInstance().setUseStandardPlayerNumber()` as described\n\u003e [above](#getting-device-details). You can only do that if you are\n\u003e using fewer than 4 CDJs, because you need to use a number that is\n\u003e not being used by any actual CDJ. If you are using 4 actual CDJs,\n\u003e you will need to leave the `VirtualCdj` using its default number of\n\u003e 5, but that means the `MetadataFinder` will often be unable to talk\n\u003e to the `dbserver` ports on the players.\n\u003e\n\u003e As of version 0.5.0, we finally have a good solution to this\n\u003e problem, which is to start up the `CrateDigger` class, which uses a\n\u003e completely separate mechanism for obtaining metadata, by downloading\n\u003e the entire rekordbox database export and track analysis files from\n\u003e the NFSv2 server that is also running in the players. The NFSv2\n\u003e server is stateless, does not care what player number we are using,\n\u003e and can be used no matter how many players are on the network. So,\n\u003e in addition to the above code, also do this:\n\n```java\nimport org.deepsymmetry.beatlink.data.CrateDigger;\n\n// ...\n\n    CrateDigger.getInstance().start();\n```\n\nOnce the `MetadataFinder`and `CrateDigger` are running, you can access\nall the metadata for currently-loaded tracks by calling\n[`MetadataFinder.getInstance().getLoadedTracks()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/MetadataFinder.html#getLoadedTracks--),\nwhich returns a `Map` from deck references (player numbers and hot cue\nnumbers) to\n[`TrackMetadata`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/TrackMetadata.html)\nobjects describing the track currently loaded in that player slot. You\ncan also call [`MetadataFinder.getLatestMetadataFor(int\nplayer)`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/MetadataFinder.html#getLatestMetadataFor-int-)\nto find the metadata for the track loaded in the playback deck of the\nspecified player. See the\n[`TrackMetadata`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/TrackMetadata.html)\nAPI documentation for all the details it provides.\n\nWith the `MetadataFinder` running, you can also start the `ArtFinder`,\nand use its\ncan call its [`getLoadedArt()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/ArtFinder.html#getLoadedArt--)\nor [`getLatestArtFor()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/ArtFinder.html#getLatestArtFor-int-)\nmethods to get the artwork images associated with the tracks, if there\nare any.\n\n### Getting Other Track Information\n\nYou can use objects in the\n[`org.deepsymmetry.beatlink.data`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/package-summary.html)\npackage to get beat grids and track waveform data, and even to create\nSwing UI components to display the preview and detailed waveform,\nreflecting the current playback position.\n\nTo illustrate the kind of interface that you can now put\ntogether from the elements offered by Beat Link, here is the Player\nStatus window from Beat Link Trigger:\n\n\u003cimg src=\"assets/PlayerStatus.png\" alt=\"Player Status window\" width=\"599\"\u003e\n\n### Media Details\n\nWhile the `MetadataFinder` is running, it also keeps track of which\nplayers have media mounted in their slots. You can access that\ninformation by calling\n[`getMountedMediaSlots()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/MetadataFinder.html#getMountedMediaSlots--).\nIf you want to know immediately when slots mount and unmount, you can\nregister a\n[`MountListener`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/MountListener.html)\nusing\n[`addMountListener()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/MetadataFinder.html#addMountListener-org.deepsymmetry.beatlink.data.MountListener-).\nYou can also find\n[useful information](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/MediaDetails.html)\nabout any mounted media database by calling\n[`getMountedMediaDetails()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/MetadataFinder.html#getMountedMediaDetails--)\nor\n[`getMediaDetailsFor()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/MetadataFinder.html#getMediaDetailsFor-org.deepsymmetry.beatlink.data.SlotReference-).\n\n### Loading Menus\n\nYou can explore the hierarchy of menus served by a player for one of\nits mounted media slots using the\n[`MenuLoader`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/MenuLoader.html),\nstarting with\n[`requestRootMenuFrom()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/MenuLoader.html#requestRootMenuFrom-org.deepsymmetry.beatlink.data.SlotReference-int-)\nto determine what menus are actually available.\n\n\u003e :warning: The only way to get menus is by talking to the `dbserver`\n\u003e on the players, so even if you are using the `CrateDigger` as\n\u003e described [above](#getting-track-metadata), you will only be able to\n\u003e do this reliably if you are using a real player number, which means\n\u003e you can have no more than three actual players on the network.\n\u003e\n\u003e Also note that in order to\n\u003e reliably send the correct message to obtain the root menu for a\n\u003e particular slot, the `MenuLoader` needs to know the type of database\n\u003e that is mounted in that slot, so the `MetadataFinder` should be\n\u003e running and tracking media details for it to check.\n\n## Recognizing Tracks\n\nIf you want to know when a particular track has been loaded on a player,\nindependent of the specific rekordbox export media it was loaded from,\nyou can start the\n[`SignatureFinder`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/SignatureFinder.html),\nand it will do most of the work for you, with the help of the\n`MetadataFinder`, `WaveformFinder`, and `BeatGridFinder`. Whenever a\nrekordbox track is loaded and metadata for it has been obtained, it will\ncompute an SHA-1 hash of the track title, artist, duration, detailed\nwaveform and beat grid. The results boil down to a 40-character string\nof hexadecimal digits which can be used to uniquely identify and\nrecognize that track (for example, using it as a key in a hash map to\nfind cues that should run when that track is playing).\n\nYou can access the signatures of all loaded rekordbox tracks by calling\n[`getSignatures()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/SignatureFinder.html#getSignatures--),\nor look up the signature for a track on a specific player with\n[`getLatestSignatureFor()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/SignatureFinder.html#getLatestSignatureFor-int-).\nIf you want to know immediately when signatures are available for loaded\ntracks, you can register a [`SignatureListener`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/SignatureListener.html)\nusing [`addSignatureListener()`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/data/SignatureFinder.html#addSignatureListener-org.deepsymmetry.beatlink.data.SignatureListener-).\n\n## An Example\n\nHere is the source for `Example.java`, a small class that demonstrates\nhow to watch for changes related to the tempo master (and also shows\nthat printing\n[`DeviceUpdate`](https://deepsymmetry.org/beatlink/apidocs/org/deepsymmetry/beatlink/DeviceUpdate.html)\nobjects can be a useful diagnostic aid):\n\n```java\nimport java.util.Date;\nimport org.deepsymmetry.beatlink.*;\n\npublic class Example {\n\n    public static void main(String[] args) {\n        try {\n            VirtualCdj.getInstance().start();\n        } catch (java.net.SocketException e) {\n            System.err.println(\"Unable to start VirtualCdj: \" + e);\n        }\n\n        VirtualCdj.getInstance().addMasterListener(new MasterListener() {\n                        @Override\n                        public void masterChanged(DeviceUpdate update) {\n                            System.out.println(\"Master changed at \" + new Date() + \": \" + update);\n                        }\n\n                        @Override\n                        public void tempoChanged(double tempo) {\n                            System.out.println(\"Tempo changed at \" + new Date() + \": \" + tempo);\n                        }\n\n                        @Override\n                        public void newBeat(Beat beat) {\n                            System.out.println(\"Master player beat at \" + new Date() + \": \" + beat);\n                        }\n            });\n\n        try {\n            Thread.sleep(60000);\n        } catch (InterruptedException e) {\n            System.out.println(\"Interrupted, exiting.\");\n        }\n    }\n}\n```\n\nCompiling and running this class (with the beat-link and slf4j jars on\nthe class path) watches and reports on tempo master activity for one\nminute, producing output like this:\n\n```\n\u003e java -cp .:beat-link.jar:slf4j-api-1.7.21.jar:slf4j-simple-1.7.21.jar Example\nMaster changed at Sun May 08 20:49:23 CDT 2016: CDJ status: Device 2,\n name: DJ-2000nexus, busy? true, pitch: +0.00%, track: 5, track BPM:\n 128.0, effective BPM: 128.0, beat: 55, beat within bar: 3,\n cue: --.-, Playing? true, Master? true, Synced? true, On-Air? true\nTempo changed at Sun May 08 20:49:23 CDT 2016: 128.0\nTempo changed at Sun May 08 20:49:47 CDT 2016: 127.9359130859375\n    [... lines omitted ...]\nTempo changed at Sun May 08 20:49:51 CDT 2016: 124.991943359375\nTempo changed at Sun May 08 20:49:51 CDT 2016: 124.927978515625\nMaster changed at Sun May 08 20:49:55 CDT 2016: CDJ status: Device 3,\n name: DJ-2000nexus, busy? true, pitch: -0.85%, track: 4, track BPM:\n 126.0, effective BPM: 124.9, beat: 25, beat within bar: 1,\n cue: 31.4, Playing? true, Master? true, Synced? true, On-Air? true\n```\n\n## Research\n\nThis project is being developed with the help of\n[dysentery](https://github.com/Deep-Symmetry/dysentery). Check that\nout for details of the packets and protocol, and for ways you can help\nfigure out more. You can also view [Snapshot API\nDocumentation](https://deepsymmetry.org/beatlink/snapshot/apidocs/) to\nsee what is available in the latest preview release.\n\n### Funding\n\nBeat Link is, and will remain, completely free and open-source. If it\nhas helped you, taught you something, or inspired you, please let us\nknow and share some of your discoveries and how you are using it! If\nyou'd like to financially support its ongoing development, you are\nwelcome (but by no means obligated) to donate to offset the hundreds\nof hours of research, development, and writing that have already been\ninvested. Or perhaps to facilitate future efforts, tools, toys, and\ntime to explore.\n\n\u003ca href=\"https://liberapay.com/deep-symmetry/donate\"\u003e\u003cimg align=\"center\" alt=\"Donate using Liberapay\"\n    src=\"https://liberapay.com/assets/widgets/donate.svg\"\u003e\u003c/a\u003e using Liberapay, or\n\u003ca href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=J26G6ULJKV8RL\"\u003e\u003cimg align=\"center\"\n    alt=\"Donate\" src=\"https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif\"\u003e\u003c/a\u003e using PayPal\n\n\u003e If enough people jump on board, we may even be able to get a newer\n\u003e CDJ to experiment with, although that's an unlikely stretch goal.\n\u003e :grinning:\n\n### Contributing in Other Ways\n\nIf you have ideas, discoveries, or even code you’d like to share,\nthat’s fantastic! Please take a look at the\n[guidelines](CONTRIBUTING.md) and get in touch!\n\n## Licenses\n\n\u003ca href=\"https://deepsymmetry.org\"\u003e\u003cimg align=\"right\" alt=\"Deep Symmetry\"\n src=\"assets/DS-logo-github.png\" width=\"250\" height=\"150\"\u003e\u003c/a\u003e\n\nCopyright © 2016–2024 [Deep Symmetry, LLC](https://deepsymmetry.org)\n\nDistributed under the [Eclipse Public License\n2.0](https://opensource.org/licenses/EPL-2.0). By using this software\nin any fashion, you are agreeing to be bound by the terms of this\nlicense. You must not remove this notice, or any other, from this\nsoftware. A copy of the license can be found in\n[LICENSE.md](LICENSE.md) within this project.\n\n### Library Licenses\n\n[Remote Tea](https://sourceforge.net/projects/remotetea/),\nused for communicating with the NFSv2 servers on players,\nis licensed under the [GNU Library General\nPublic License, version 2](https://opensource.org/licenses/LGPL-2.0).\n\nThe [Kaitai Struct](https://kaitai.io) Java runtime, used for parsing\nrekordbox exports and media analysis files, is licensed under the\n[MIT License](https://opensource.org/licenses/MIT).\n","funding_links":["https://github.com/sponsors/Deep-Symmetry","https://liberapay.com/deep-symmetry","https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=LG5NLFL5T372W\u0026source=url","https://liberapay.com/deep-symmetry/donate","https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=J26G6ULJKV8RL"],"categories":["Java"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDeep-Symmetry%2Fbeat-link","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDeep-Symmetry%2Fbeat-link","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDeep-Symmetry%2Fbeat-link/lists"}