From 38f1beea8e5fe20ca0456e3a4e219757263c9359 Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 16:12:26 -0400
Subject: [PATCH 01/16] changes to get opus working with BLT. Also to get opus
to send metadata packets (and ready for us to request PSSI)
---
.../org/deepsymmetry/beatlink/CdjStatus.java | 2 +
.../deepsymmetry/beatlink/MediaDetails.java | 30 ++-
.../org/deepsymmetry/beatlink/VirtualCdj.java | 12 +
.../beatlink/VirtualRekordbox.java | 244 +++++++++++++-----
.../beatlink/data/CrateDigger.java | 8 +-
.../beatlink/data/MetadataFinder.java | 2 +-
.../beatlink/data/OpusProvider.java | 5 +-
.../org/deepsymmetry/beatlink/TestRunner.java | 22 ++
8 files changed, 248 insertions(+), 77 deletions(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/CdjStatus.java b/src/main/java/org/deepsymmetry/beatlink/CdjStatus.java
index d8559f93..79eef9c4 100644
--- a/src/main/java/org/deepsymmetry/beatlink/CdjStatus.java
+++ b/src/main/java/org/deepsymmetry/beatlink/CdjStatus.java
@@ -642,6 +642,8 @@ public CdjStatus(DatagramPacket packet) {
if (Util.isOpusQuad(deviceName)) {
trackSourcePlayer = translateOpusPlayerNumbers(packetBytes[40]);
trackSourceSlot = findOpusTrackSourceSlot();
+ // isLocalSdLoaded() == true
+ packetBytes[115] = 0;
} else {
trackSourcePlayer = packetBytes[40];
trackSourceSlot = findTrackSourceSlot();
diff --git a/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java b/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
index 75604f75..a9a6cdca 100644
--- a/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
+++ b/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
@@ -9,9 +9,7 @@
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.nio.ByteBuffer;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.*;
/**
* Represents information about the media mounted in a player's slot; returned in response to a media query packet.
@@ -122,6 +120,32 @@ private int getUTF16StringLength(byte[] source, int offset, int maxLength) {
return numBytes;
}
+ /**
+ * Constructor sets all the immutable interpreted fields based on constructor inputs
+ *
+ * @param packet the media response packet that was received
+ */
+
+ /**
+ *
+ * @param slotReference Slot Reference for the
+ * @param mediaType
+ * @param name
+ */
+ public MediaDetails(SlotReference slotReference, CdjStatus.TrackType mediaType, String name) {
+ this.slotReference = slotReference;
+ this.mediaType = mediaType;
+ this.name = name;
+ this.creationDate = "";
+ this.trackCount = 100;
+ this.totalSize = 100;
+ this.playlistCount = 100;
+ this.rawBytes = ByteBuffer.wrap(new byte[]{});
+ this.color = new Color(100);
+ this.freeSpace = 100;
+ this.hasMySettings = false;
+ }
+
/**
* Constructor sets all the immutable interpreted fields based on the packet content.
*
diff --git a/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java b/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
index 5b1d8a35..358b56be 100644
--- a/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
+++ b/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
@@ -1440,6 +1440,18 @@ private void deliverDeviceUpdate(final DeviceUpdate update) {
}
}
+ // This is a way to get mediaDetails from Rekordbox and pass to all MediaDetailsListeners
+ private final MediaDetailsListener mediaDetailsListener = new MediaDetailsListener(){
+ @Override
+ public void detailsAvailable(MediaDetails details) {
+ deliverMediaDetailsUpdate(details);
+ }
+ };
+
+ public MediaDetailsListener getMediaDetailsListener() {
+ return mediaDetailsListener;
+ }
+
/**
* Keeps track of the registered media details listeners.
*/
diff --git a/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java b/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
index 141400fb..f6bf7bbf 100644
--- a/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
+++ b/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
@@ -1,13 +1,14 @@
package org.deepsymmetry.beatlink;
-import org.deepsymmetry.beatlink.data.MetadataFinder;
+import org.deepsymmetry.beatlink.data.OpusProvider;
import org.deepsymmetry.beatlink.data.SlotReference;
+import org.deepsymmetry.cratedigger.Database;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.File;
import java.io.IOException;
import java.net.*;
-import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.*;
@@ -21,6 +22,7 @@
*/
@SuppressWarnings("WeakerAccess")
public class VirtualRekordbox extends LifecycleParticipant {
+ private static Database db = null;
private static final Logger logger = LoggerFactory.getLogger(VirtualRekordbox.class);
@@ -121,7 +123,7 @@ public boolean getUseStandardPlayerNumber() {
* @return the virtual player number
*/
public synchronized byte getDeviceNumber() {
- return keepAliveBytes[DEVICE_NUMBER_OFFSET];
+ return rekordboxLightingKeepAliveBytes[DEVICE_NUMBER_OFFSET];
}
/**
@@ -138,7 +140,7 @@ public synchronized void setDeviceNumber(byte number) {
if (isRunning()) {
throw new IllegalStateException("Can't change device number once started.");
}
- keepAliveBytes[DEVICE_NUMBER_OFFSET] = number;
+ rekordboxLightingKeepAliveBytes[DEVICE_NUMBER_OFFSET] = number;
}
/**
@@ -176,14 +178,14 @@ public void setAnnounceInterval(int interval) {
* and IP address, as described in the
* Packet Analysis document.
*/
- private static final byte[] keepAliveBytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x06, 0x00, 0x62, 0x65, 0x61, 0x74,
- 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x00, 0x36, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x64
+ private static final byte[] rekordboxLightingKeepAliveBytes = {
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x06, 0x00, 0x72, 0x65, 0x6b, 0x6f, 0x72,
+ 0x64, 0x62, 0x6f, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03,
+ 0x00, 0x36, 0x17, 0x01, 0x18, 0x3e, (byte) 0xef, (byte) 0xda, 0x5b, (byte) 0xca, (byte) 0xc0, (byte) 0xa8,
+ 0x02, 0x0b, 0x04, 0x01, 0x00, 0x00, 0x04, 0x08
};
- private static final byte[] initializeRekordboxLightingBytes = {
+ private static final byte[] rekordboxLightingRequestStatusBytes = {
0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x11, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x64,
0x62, 0x6f, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x17,
0x01, 0x04, 0x17, 0x01, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x63, 0x00, 0x62, 0x00, 0x6f, 0x00,
@@ -225,34 +227,34 @@ public void setAnnounceInterval(int interval) {
* @return the device name reported in our presence announcement packets
*/
public static String getDeviceName() {
- return new String(keepAliveBytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH).trim();
+ return new String(rekordboxLightingKeepAliveBytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH).trim();
}
/**
* The initial packet sent three times when coming online.
*/
private static final byte[] helloBytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x0a, 0x00, 0x62, 0x65, 0x61, 0x74,
- 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x04, 0x00, 0x26, 0x01, 0x40
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x0a, 0x00, 0x62, 0x65, 0x61, 0x74,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x04, 0x00, 0x26, 0x01, 0x40
};
/**
* The first-stage device number claim packet series.
*/
private static final byte[] claimStage1bytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x00, 0x00, 0x62, 0x65, 0x61, 0x74,
- 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x03, 0x00, 0x2c, 0x0d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x00, 0x00, 0x62, 0x65, 0x61, 0x74,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x03, 0x00, 0x2c, 0x0d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/**
* The second-stage device number claim packet series.
*/
private static final byte[] claimStage2bytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x02, 0x00, 0x62, 0x65, 0x61, 0x74,
- 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x03, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x02, 0x00, 0x62, 0x65, 0x61, 0x74,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x03, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
0x01, 0x00
};
@@ -260,18 +262,18 @@ public static String getDeviceName() {
* The third-stage (final) device number claim packet series.
*/
private static final byte[] claimStage3bytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x04, 0x00, 0x62, 0x65, 0x61, 0x74,
- 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x03, 0x00, 0x26, 0x0d, 0x00
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x04, 0x00, 0x62, 0x65, 0x61, 0x74,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x03, 0x00, 0x26, 0x0d, 0x00
};
/**
* Packet used to acknowledge a mixer's intention to assign us a device number.
*/
private static final byte[] assignmentRequestBytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x02, 0x01, 0x62, 0x65, 0x61, 0x74,
- 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x02, 0x01, 0x62, 0x65, 0x61, 0x74,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00
};
@@ -279,9 +281,9 @@ public static String getDeviceName() {
* Packet used to tell another device we are already using a device number.
*/
private static final byte[] deviceNumberDefenseBytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x08, 0x00, 0x62, 0x65, 0x61, 0x74,
- 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x08, 0x00, 0x62, 0x65, 0x61, 0x74,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00
};
/**
@@ -312,7 +314,16 @@ private DeviceUpdate buildUpdate(DatagramPacket packet) throws IOException {
case CDJ_STATUS:
if (length >= CdjStatus.MINIMUM_PACKET_SIZE) {
- return new CdjStatus(packet);
+ CdjStatus status = new CdjStatus(packet);
+
+ MediaDetails details = new MediaDetails(
+ SlotReference.getSlotReference(status.getDeviceNumber(), CdjStatus.TrackSourceSlot.SD_SLOT),
+ CdjStatus.TrackType.REKORDBOX,
+ status.getDeviceName());
+
+ deliverMediaDetailsUpdate(details);
+
+ return status;
} else {
logger.warn("Ignoring too-short CDJ Status packet with length " + length + " (we need " + CdjStatus.MINIMUM_PACKET_SIZE +
" bytes).");
@@ -328,7 +339,17 @@ private DeviceUpdate buildUpdate(DatagramPacket packet) throws IOException {
}
case OPUS_METADATA:
- logger.info("Received track load metadata from player " + packet.getData()[0x21]);
+ if (packet.getData()[0x25] == 0x02) {
+ OpusMetadata metadata = new OpusMetadata(packet.getData());
+
+ logger.info("beatgrid");
+ }
+// StringBuilder sb = new StringBuilder();
+// for (byte b : packet.getData()) {
+// sb.append(String.format("%02X ", b));
+// }
+// System.out.println();
+// logger.info("Received track load metadata from player " + packet.getData()[0x21]);
return null;
default:
@@ -337,13 +358,38 @@ private DeviceUpdate buildUpdate(DatagramPacket packet) throws IOException {
}
}
+ public class OpusMetadata {
+ private byte[] packetBytes;
+
+ public OpusMetadata(byte[] packetBytes) {
+ StringBuilder sb = new StringBuilder();
+ for (byte b : packetBytes) {
+ sb.append(String.format("%02X ", b));
+ }
+ System.out.println(sb);
+ this.packetBytes = packetBytes;
+ }
+
+ public byte[] getPacketBytes() {
+ return packetBytes;
+ }
+
+ public String toString(){
+ StringBuilder sb = new StringBuilder();
+ for (byte b : packetBytes) {
+ sb.append(String.format("%02X ", b));
+ }
+ return sb.toString();
+ }
+ }
+
+
/**
* This will send the bytes Rekordbox Lighting sends to a player to acknowledge its existence on the network and
* trigger it to begin sending CDJStatus packets.
- *
*/
public void sendRekordboxLightingPacket() {
- DatagramPacket updatesAnnouncement = new DatagramPacket(initializeRekordboxLightingBytes, initializeRekordboxLightingBytes.length,
+ DatagramPacket updatesAnnouncement = new DatagramPacket(rekordboxLightingRequestStatusBytes, rekordboxLightingRequestStatusBytes.length,
broadcastAddress.get(), UPDATE_PORT);
try {
socket.get().send(updatesAnnouncement);
@@ -494,12 +540,12 @@ private void requestNumberFromMixer(InetAddress mixerAddress) {
}
// Send a packet directly to the mixer telling it we are ready for its device assignment.
- Arrays.fill(assignmentRequestBytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte)0);
+ Arrays.fill(assignmentRequestBytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte) 0);
System.arraycopy(getDeviceName().getBytes(), 0, assignmentRequestBytes, DEVICE_NAME_OFFSET, getDeviceName().getBytes().length);
System.arraycopy(matchedAddress.getAddress().getAddress(), 0, assignmentRequestBytes, 0x24, 4);
- System.arraycopy(keepAliveBytes, MAC_ADDRESS_OFFSET, assignmentRequestBytes, 0x28, 6);
+ System.arraycopy(rekordboxLightingKeepAliveBytes, MAC_ADDRESS_OFFSET, assignmentRequestBytes, 0x28, 6);
// Can't call getDeviceNumber() on next line because that's synchronized!
- assignmentRequestBytes[0x31] = (keepAliveBytes[DEVICE_NUMBER_OFFSET] == 0)? (byte)1 : (byte)2; // The auto-assign flag.
+ assignmentRequestBytes[0x31] = (rekordboxLightingKeepAliveBytes[DEVICE_NUMBER_OFFSET] == 0) ? (byte) 1 : (byte) 2; // The auto-assign flag.
assignmentRequestBytes[0x2f] = 1; // The packet counter.
try {
DatagramPacket announcement = new DatagramPacket(assignmentRequestBytes, assignmentRequestBytes.length,
@@ -526,9 +572,9 @@ void defendDeviceNumber(InetAddress invaderAddress) {
}
// Send a packet to the interloper telling it that we are using that device number.
- Arrays.fill(deviceNumberDefenseBytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte)0);
+ Arrays.fill(deviceNumberDefenseBytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte) 0);
System.arraycopy(getDeviceName().getBytes(), 0, deviceNumberDefenseBytes, DEVICE_NAME_OFFSET, getDeviceName().getBytes().length);
- deviceNumberDefenseBytes[0x24] = keepAliveBytes[DEVICE_NUMBER_OFFSET];
+ deviceNumberDefenseBytes[0x24] = rekordboxLightingKeepAliveBytes[DEVICE_NUMBER_OFFSET];
System.arraycopy(matchedAddress.getAddress().getAddress(), 0, deviceNumberDefenseBytes, 0x25, 4);
try {
DatagramPacket defense = new DatagramPacket(deviceNumberDefenseBytes, deviceNumberDefenseBytes.length,
@@ -553,7 +599,7 @@ private boolean claimDeviceNumber() {
mixerAssigned.set(0);
// Send the initial series of three "coming online" packets.
- Arrays.fill(helloBytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte)0);
+ Arrays.fill(helloBytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte) 0);
System.arraycopy(getDeviceName().getBytes(), 0, helloBytes, DEVICE_NAME_OFFSET, getDeviceName().getBytes().length);
for (int i = 1; i <= 3; i++) {
try {
@@ -583,11 +629,11 @@ private boolean claimDeviceNumber() {
// Send the series of three initial device number claim packets, unless we are interrupted by a defense
// or a mixer assigning us a specific number.
- Arrays.fill(claimStage1bytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte)0);
+ Arrays.fill(claimStage1bytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte) 0);
System.arraycopy(getDeviceName().getBytes(), 0, claimStage1bytes, DEVICE_NAME_OFFSET, getDeviceName().getBytes().length);
- System.arraycopy(keepAliveBytes, MAC_ADDRESS_OFFSET, claimStage1bytes, 0x26, 6);
+ System.arraycopy(rekordboxLightingKeepAliveBytes, MAC_ADDRESS_OFFSET, claimStage1bytes, 0x26, 6);
for (int i = 1; i <= 3 && mixerAssigned.get() == 0; i++) {
- claimStage1bytes[0x24] = (byte)i; // The packet counter.
+ claimStage1bytes[0x24] = (byte) i; // The packet counter.
try {
logger.debug("Sending claim stage 1 packet " + i);
DatagramPacket announcement = new DatagramPacket(claimStage1bytes, claimStage1bytes.length,
@@ -612,14 +658,14 @@ private boolean claimDeviceNumber() {
// Send the middle series of device claim packets, unless we are interrupted by a defense
// or a mixer assigning us a specific number.
- Arrays.fill(claimStage2bytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte)0);
+ Arrays.fill(claimStage2bytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte) 0);
System.arraycopy(getDeviceName().getBytes(), 0, claimStage2bytes, DEVICE_NAME_OFFSET, getDeviceName().getBytes().length);
System.arraycopy(matchedAddress.getAddress().getAddress(), 0, claimStage2bytes, 0x24, 4);
- System.arraycopy(keepAliveBytes, MAC_ADDRESS_OFFSET, claimStage2bytes, 0x28, 6);
- claimStage2bytes[0x2e] = (byte)claimingNumber.get(); // The number we are claiming.
- claimStage2bytes[0x31] = (getDeviceNumber() == 0)? (byte)1 : (byte)2; // The auto-assign flag.
+ System.arraycopy(rekordboxLightingKeepAliveBytes, MAC_ADDRESS_OFFSET, claimStage2bytes, 0x28, 6);
+ claimStage2bytes[0x2e] = (byte) claimingNumber.get(); // The number we are claiming.
+ claimStage2bytes[0x31] = (getDeviceNumber() == 0) ? (byte) 1 : (byte) 2; // The auto-assign flag.
for (int i = 1; i <= 3 && mixerAssigned.get() == 0; i++) {
- claimStage2bytes[0x2f] = (byte)i; // The packet counter.
+ claimStage2bytes[0x2f] = (byte) i; // The packet counter.
try {
logger.debug("Sending claim stage 2 packet " + i + " for device " + claimStage2bytes[0x2e]);
DatagramPacket announcement = new DatagramPacket(claimStage2bytes, claimStage2bytes.length,
@@ -650,11 +696,11 @@ private boolean claimDeviceNumber() {
// Send the final series of device claim packets, unless we are interrupted by a defense, or the mixer
// acknowledges our acceptance of its assignment.
- Arrays.fill(claimStage3bytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte)0);
+ Arrays.fill(claimStage3bytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte) 0);
System.arraycopy(getDeviceName().getBytes(), 0, claimStage3bytes, DEVICE_NAME_OFFSET, getDeviceName().getBytes().length);
- claimStage3bytes[0x24] = (byte)claimingNumber.get(); // The number we are claiming.
+ claimStage3bytes[0x24] = (byte) claimingNumber.get(); // The number we are claiming.
for (int i = 1; i <= 3 && mixerAssigned.get() == 0; i++) {
- claimStage3bytes[0x25] = (byte)i; // The packet counter.
+ claimStage3bytes[0x25] = (byte) i; // The packet counter.
try {
logger.debug("Sending claim stage 3 packet " + i + " for device " + claimStage3bytes[0x24]);
DatagramPacket announcement = new DatagramPacket(claimStage3bytes, claimStage3bytes.length,
@@ -680,14 +726,14 @@ private boolean claimDeviceNumber() {
claimed = true; // If we finished all our loops, the number we wanted is ours.
}
// Set the device number we claimed.
- keepAliveBytes[DEVICE_NUMBER_OFFSET] = (byte)claimingNumber.getAndSet(0);
+ rekordboxLightingKeepAliveBytes[DEVICE_NUMBER_OFFSET] = (byte) claimingNumber.getAndSet(0);
mixerAssigned.set(0);
return true; // Huzzah, we found the right device number to use!
}
- public void sendRekordboxLightingAnnouncement(){
+ public void sendRekordboxLightingAnnouncement() {
if (isRunning()) {
- DatagramPacket announcement = new DatagramPacket(keepAliveBytes, keepAliveBytes.length,
+ DatagramPacket announcement = new DatagramPacket(rekordboxLightingKeepAliveBytes, rekordboxLightingKeepAliveBytes.length,
broadcastAddress.get(), DeviceFinder.ANNOUNCEMENT_PORT);
try {
this.socket.get().send(announcement);
@@ -698,12 +744,80 @@ public void sendRekordboxLightingAnnouncement(){
}
/**
+ * Keeps track of the registered media details listeners.
+ */
+ private final Set detailsListeners =
+ Collections.newSetFromMap(new ConcurrentHashMap());
+
+ /**
+ * Adds the specified media details listener to receive detail responses whenever they come in.
+ * If {@code listener} is {@code null} or already present in the list
+ * of registered listeners, no exception is thrown and no action is performed.
+ *
+ * To reduce latency, device updates are delivered to listeners directly on the thread that is receiving them
+ * from the network, so if you want to interact with user interface objects in listener methods, you need to use
+ * javax.swing.SwingUtilities.invokeLater(Runnable)
+ * to do so on the Event Dispatch Thread.
*
+ * Even if you are not interacting with user interface objects, any code in the listener method
+ * must finish quickly, or it will add latency for other listeners, and detail updates will back up.
+ * If you want to perform lengthy processing of any sort, do so on another thread.
*
+ * @param listener the media details listener to add
+ */
+ public void addMediaDetailsListener(MediaDetailsListener listener) {
+ if (listener != null) {
+ detailsListeners.add(listener);
+ }
+ }
+
+ /**
+ * Removes the specified media details listener so it no longer receives detail responses when they come in.
+ * If {@code listener} is {@code null} or not present
+ * in the list of registered listeners, no exception is thrown and no action is performed.
+ *
+ * @param listener the media details listener to remove
+ */
+ public void removeMediaDetailsListener(MediaDetailsListener listener) {
+ if (listener != null) {
+ detailsListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Get the set of media details listeners that are currently registered.
+ *
+ * @return the currently registered details listeners
+ */
+ public Set getMediaDetailsListeners() {
+ // Make a copy so callers get an immutable snapshot of the current state.
+ return Collections.unmodifiableSet(new HashSet(detailsListeners));
+ }
+
+ /**
+ * Send a media details response to all registered listeners.
+ *
+ * @param details the response that has just arrived
+ */
+ private void deliverMediaDetailsUpdate(final MediaDetails details) {
+ for (MediaDetailsListener listener : getMediaDetailsListeners()) {
+ try {
+ listener.detailsAvailable(details);
+ } catch (Throwable t) {
+ logger.warn("Problem delivering media details response to listener", t);
+ }
+ }
+ }
+
+ /**
* @return true if we found DJ Link devices and were able to create the {@code VirtualRekordbox}.
* @throws Exception if there is a problem opening a socket on the right network
*/
private boolean createVirtualRekordbox() throws Exception {
+ db = new Database(new File("/Users/cprepos/Desktop/PIONEER/rekordbox/export.pdb"));
+ OpusProvider.getInstance().attachMetadataArchive(new File("/Users/cprepos/krisprep/BLT Archive/archive.blm"), CdjStatus.TrackSourceSlot.SD_SLOT);
+ OpusProvider.getInstance().attachMetadataArchive(new File("/Users/cprepos/krisprep/BLT Archive/archive.blm"), CdjStatus.TrackSourceSlot.USB_SLOT);
+ OpusProvider.getInstance().start();
// Find the network interface and address to use to communicate with the first device we found.
matchingInterfaces = new ArrayList();
@@ -711,6 +825,7 @@ private boolean createVirtualRekordbox() throws Exception {
// Forward Updates to VirtualCdj. That's where all clients are used to getting them.
addUpdateListener(VirtualCdj.getInstance().getUpdateListener());
+ addMediaDetailsListener(VirtualCdj.getInstance().getMediaDetailsListener());
for (NetworkInterface networkInterface : Collections.list(NetworkInterface.getNetworkInterfaces())) {
for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) {
@@ -737,13 +852,13 @@ private boolean createVirtualRekordbox() throws Exception {
socket.set(new DatagramSocket(UPDATE_PORT, matchedAddress.getAddress()));
System.arraycopy(getMatchingInterfaces().get(0).getHardwareAddress(),
- 0, keepAliveBytes, MAC_ADDRESS_OFFSET, 6);
+ 0, rekordboxLightingKeepAliveBytes, MAC_ADDRESS_OFFSET, 6);
System.arraycopy(matchedAddress.getAddress().getAddress(),
- 0, keepAliveBytes, 44, 4);
+ 0, rekordboxLightingKeepAliveBytes, 44, 4);
System.arraycopy(getMatchingInterfaces().get(0).getHardwareAddress(),
- 0, initializeRekordboxLightingBytes, MAC_ADDRESS_OFFSET, 6);
+ 0, rekordboxLightingRequestStatusBytes, MAC_ADDRESS_OFFSET, 6);
System.arraycopy(matchedAddress.getAddress().getAddress(),
- 0, initializeRekordboxLightingBytes, 44, 4);
+ 0, rekordboxLightingRequestStatusBytes, 44, 4);
// Copy the chosen interface's hardware and IP addresses into the announcement packet template
broadcastAddress.set(matchedAddress.getBroadcast());
@@ -832,8 +947,8 @@ public void run() {
*/
private void sendAnnouncements() {
try {
- sendRekordboxLightingPacket();
sendRekordboxLightingAnnouncement();
+ sendRekordboxLightingPacket();
Thread.sleep(getAnnounceInterval());
} catch (Throwable t) {
@@ -912,7 +1027,6 @@ public synchronized boolean start() throws Exception {
* driven by receipt of device announcement packets.
*
* @param deviceNumber the device number to try to claim
- *
* @return true if we found DJ Link devices and were able to create the {@code VirtualRekordbox}, or it was already running.
* @throws SocketException if the socket to listen on port 50002 cannot be created
*/
@@ -934,7 +1048,7 @@ public synchronized void stop() {
socket.set(null);
broadcastAddress.set(null);
updates.clear();
- setDeviceNumber((byte)0); // Set up for self-assignment if restarted.
+ setDeviceNumber((byte) 0); // Set up for self-assignment if restarted.
deliverLifecycleAnnouncement(logger, false);
}
}
@@ -949,7 +1063,6 @@ public synchronized void stop() {
* combines both status updates and beat messages, and so is more likely to be current and definitive.
*
* @param deviceNumber the device number of interest
- *
* @return the matching detailed status update or null if none have been received
* @throws IllegalStateException if the {@code VirtualRekordbox} is not active
*/
@@ -1047,12 +1160,13 @@ public static VirtualRekordbox getInstance() {
/**
* Register any relevant listeners; private to prevent instantiation.
*/
- private VirtualRekordbox() {}
+ private VirtualRekordbox() {
+ }
/**
* We have received a packet from a device trying to claim a device number, see if we should defend it.
*
- * @param packet the packet received
+ * @param packet the packet received
* @param deviceOffset the index of the byte within the packet holding the device number being claimed
*/
private void handleDeviceClaimPacket(DatagramPacket packet, int deviceOffset) {
@@ -1069,7 +1183,7 @@ private void handleDeviceClaimPacket(DatagramPacket packet, int deviceOffset) {
* The {@link DeviceFinder} delegates packets it doesn't know how to deal with to us using this method, because
* they relate to claiming or defending device numbers, which is our responsibility.
*
- * @param kind the kind of packet that was received
+ * @param kind the kind of packet that was received
* @param packet the actual bytes of the packet
*/
void handleSpecialAnnouncementPacket(Util.PacketType kind, DatagramPacket packet) {
@@ -1111,7 +1225,7 @@ void handleSpecialAnnouncementPacket(Util.PacketType kind, DatagramPacket packet
logger.warn("Another device is defending a number we are not using, ignoring: " + defendedDevice);
}
} else {
- logger.warn("Received device number defense message for device number " + defendedDevice + " when we are not even running!");
+ logger.warn("Received device number defense message for device number " + defendedDevice + " when we are not even running!");
}
} else {
logger.warn("Received unrecognized special announcement packet type: " + kind);
diff --git a/src/main/java/org/deepsymmetry/beatlink/data/CrateDigger.java b/src/main/java/org/deepsymmetry/beatlink/data/CrateDigger.java
index d7735e37..4129e869 100644
--- a/src/main/java/org/deepsymmetry/beatlink/data/CrateDigger.java
+++ b/src/main/java/org/deepsymmetry/beatlink/data/CrateDigger.java
@@ -815,10 +815,10 @@ private void deliverDatabaseUpdate(SlotReference slot, Database database, boolea
* and register the listeners that hook us into the streams of information we need.
*/
private CrateDigger() {
- MetadataFinder.getInstance().addLifecycleListener(lifecycleListener);
- MetadataFinder.getInstance().addMountListener(mountListener);
- DeviceFinder.getInstance().addDeviceAnnouncementListener(deviceListener);
- VirtualCdj.getInstance().addMediaDetailsListener(mediaDetailsListener);
+// MetadataFinder.getInstance().addLifecycleListener(lifecycleListener);
+// MetadataFinder.getInstance().addMountListener(mountListener);
+// DeviceFinder.getInstance().addDeviceAnnouncementListener(deviceListener);
+// VirtualCdj.getInstance().addMediaDetailsListener(mediaDetailsListener);
downloadDirectory = createDownloadDirectory();
}
diff --git a/src/main/java/org/deepsymmetry/beatlink/data/MetadataFinder.java b/src/main/java/org/deepsymmetry/beatlink/data/MetadataFinder.java
index cf57968a..2d554a95 100644
--- a/src/main/java/org/deepsymmetry/beatlink/data/MetadataFinder.java
+++ b/src/main/java/org/deepsymmetry/beatlink/data/MetadataFinder.java
@@ -1022,7 +1022,7 @@ private void handleUpdate(final CdjStatus update) {
}
// Not in the hot cache so try actually retrieving it, if possible.
- if (ConnectionManager.getInstance().getPlayerDBServerPort(update.getTrackSourcePlayer()) > 0) {
+ if (ConnectionManager.getInstance().getPlayerDBServerPort(update.getTrackSourcePlayer()) > 0 || VirtualRekordbox.getInstance().isRunning()) {
if (activeRequests.add(update.getTrackSourcePlayer())) {
// We had to make sure we were not already asking for this track.
clearDeck(update); // We won't know what it is until our request completes.
diff --git a/src/main/java/org/deepsymmetry/beatlink/data/OpusProvider.java b/src/main/java/org/deepsymmetry/beatlink/data/OpusProvider.java
index 5e5c6a8e..2b7b9d12 100644
--- a/src/main/java/org/deepsymmetry/beatlink/data/OpusProvider.java
+++ b/src/main/java/org/deepsymmetry/beatlink/data/OpusProvider.java
@@ -161,10 +161,7 @@ public synchronized void attachMetadataArchive(File archive, CdjStatus.TrackSour
*/
@API(status = API.Status.STABLE)
public Database findDatabase(DataReference reference) {
- if (reference.player >= 9 && reference.player <= 12) { // This is an Opus Quad deck.
- return findDatabase(reference.getSlotReference());
- }
- return null;
+ return findDatabase(reference.getSlotReference());
}
/**
diff --git a/src/main/test/org/deepsymmetry/beatlink/TestRunner.java b/src/main/test/org/deepsymmetry/beatlink/TestRunner.java
index 3094c8e5..df6bf186 100644
--- a/src/main/test/org/deepsymmetry/beatlink/TestRunner.java
+++ b/src/main/test/org/deepsymmetry/beatlink/TestRunner.java
@@ -1,5 +1,7 @@
package org.deepsymmetry.beatlink;
+import org.deepsymmetry.beatlink.data.CrateDigger;
+import org.deepsymmetry.beatlink.data.MetadataFinder;
import org.deepsymmetry.cratedigger.Database;
import org.deepsymmetry.cratedigger.pdb.RekordboxPdb;
import org.junit.Test;
@@ -12,7 +14,27 @@ public class TestRunner {
// Convenience integration test
@Test
public void TestRunner() throws Exception {
+
VirtualRekordbox.getInstance().start();
+ VirtualRekordbox.getInstance().addUpdateListener(new DeviceUpdateListener() {
+ Database db = new Database(new File("/Users/cprepos/Desktop/PIONEER/rekordbox/export.pdb"));
+ @Override
+ public void received(DeviceUpdate update) {
+ if (update instanceof CdjStatus) {
+ CdjStatus status = (CdjStatus) update;
+ } else {
+
+ StringBuilder sb = new StringBuilder();
+ for (byte b : update.packetBytes) {
+ sb.append(String.format("%02X ", b));
+ }
+ System.out.println(sb);
+ }
+ }
+ });
+
+
+ MetadataFinder.getInstance().start();
System.out.println("Started up!");
try {
Thread.sleep(600000000);
From 449d3e24af5a94624e2f3cbf0a7df69529f9ff07 Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 17:03:25 -0400
Subject: [PATCH 02/16] remove local vars
---
.../deepsymmetry/beatlink/VirtualRekordbox.java | 15 ++++++---------
.../org/deepsymmetry/beatlink/TestRunner.java | 5 ++---
2 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java b/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
index f6bf7bbf..08485b98 100644
--- a/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
+++ b/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
@@ -22,8 +22,6 @@
*/
@SuppressWarnings("WeakerAccess")
public class VirtualRekordbox extends LifecycleParticipant {
- private static Database db = null;
-
private static final Logger logger = LoggerFactory.getLogger(VirtualRekordbox.class);
/**
@@ -316,12 +314,12 @@ private DeviceUpdate buildUpdate(DatagramPacket packet) throws IOException {
if (length >= CdjStatus.MINIMUM_PACKET_SIZE) {
CdjStatus status = new CdjStatus(packet);
- MediaDetails details = new MediaDetails(
- SlotReference.getSlotReference(status.getDeviceNumber(), CdjStatus.TrackSourceSlot.SD_SLOT),
- CdjStatus.TrackType.REKORDBOX,
- status.getDeviceName());
-
- deliverMediaDetailsUpdate(details);
+// MediaDetails details = new MediaDetails(
+// SlotReference.getSlotReference(status.getDeviceNumber(), CdjStatus.TrackSourceSlot.SD_SLOT),
+// CdjStatus.TrackType.REKORDBOX,
+// status.getDeviceName());
+//
+// deliverMediaDetailsUpdate(details);
return status;
} else {
@@ -814,7 +812,6 @@ private void deliverMediaDetailsUpdate(final MediaDetails details) {
* @throws Exception if there is a problem opening a socket on the right network
*/
private boolean createVirtualRekordbox() throws Exception {
- db = new Database(new File("/Users/cprepos/Desktop/PIONEER/rekordbox/export.pdb"));
OpusProvider.getInstance().attachMetadataArchive(new File("/Users/cprepos/krisprep/BLT Archive/archive.blm"), CdjStatus.TrackSourceSlot.SD_SLOT);
OpusProvider.getInstance().attachMetadataArchive(new File("/Users/cprepos/krisprep/BLT Archive/archive.blm"), CdjStatus.TrackSourceSlot.USB_SLOT);
OpusProvider.getInstance().start();
diff --git a/src/main/test/org/deepsymmetry/beatlink/TestRunner.java b/src/main/test/org/deepsymmetry/beatlink/TestRunner.java
index df6bf186..8114a8ab 100644
--- a/src/main/test/org/deepsymmetry/beatlink/TestRunner.java
+++ b/src/main/test/org/deepsymmetry/beatlink/TestRunner.java
@@ -15,9 +15,8 @@ public class TestRunner {
@Test
public void TestRunner() throws Exception {
- VirtualRekordbox.getInstance().start();
- VirtualRekordbox.getInstance().addUpdateListener(new DeviceUpdateListener() {
- Database db = new Database(new File("/Users/cprepos/Desktop/PIONEER/rekordbox/export.pdb"));
+ VirtualCdj.getInstance().start();
+ VirtualCdj.getInstance().addUpdateListener(new DeviceUpdateListener() {
@Override
public void received(DeviceUpdate update) {
if (update instanceof CdjStatus) {
From 24cdae7700777ce527d6fc2dd302be870d451001 Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 18:26:26 -0400
Subject: [PATCH 03/16] Remove personal vars, VirtualCdj is now the starter of
VirtualRekordbox, which is much smoother
---
.../org/deepsymmetry/beatlink/CdjStatus.java | 3 +-
.../java/org/deepsymmetry/beatlink/Util.java | 26 +++++-
.../org/deepsymmetry/beatlink/VirtualCdj.java | 30 ++-----
.../beatlink/VirtualRekordbox.java | 84 +++++--------------
4 files changed, 54 insertions(+), 89 deletions(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/CdjStatus.java b/src/main/java/org/deepsymmetry/beatlink/CdjStatus.java
index 79eef9c4..7987c47b 100644
--- a/src/main/java/org/deepsymmetry/beatlink/CdjStatus.java
+++ b/src/main/java/org/deepsymmetry/beatlink/CdjStatus.java
@@ -511,7 +511,8 @@ private TrackSourceSlot findOpusTrackSourceSlot() {
if (rekordboxId != 0) {
switch (packetBytes[41]){
case 0: return TrackSourceSlot.SD_SLOT;
- case 3: return TrackSourceSlot.USB_SLOT;
+ // TODO, figure out a way to identify more than one USB at a time
+ case 3: return TrackSourceSlot.SD_SLOT;
}
}
diff --git a/src/main/java/org/deepsymmetry/beatlink/Util.java b/src/main/java/org/deepsymmetry/beatlink/Util.java
index 8afce95e..62aee888 100644
--- a/src/main/java/org/deepsymmetry/beatlink/Util.java
+++ b/src/main/java/org/deepsymmetry/beatlink/Util.java
@@ -2,8 +2,7 @@
import java.awt.*;
import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.InetAddress;
+import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.util.Collections;
@@ -959,6 +958,29 @@ public static String phraseLabel(final SongStructureEntry phrase) {
}
}
+ /**
+ * Scan a network interface to find if it has an address space which matches the device we are trying to reach.
+ * If so, return the address specification.
+ *
+ * @param aDevice the DJ Link device we are trying to communicate with
+ * @param networkInterface the network interface we are testing
+ * @return the address which can be used to communicate with the device on the interface, or null
+ */
+ public static InterfaceAddress findMatchingAddress(DeviceAnnouncement aDevice, NetworkInterface networkInterface) {
+ for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) {
+ if (address == null) {
+ // This should never happen, but we are protecting against a Windows Java bug, see
+ // https://bugs.java.com/bugdatabase/view_bug?bug_id=8023649
+ logger.warn("Received a null InterfaceAddress from networkInterface.getInterfaceAddresses(), is this Windows? " +
+ "Do you have a VPN installed? Trying to recover by ignoring it.");
+ } else if ((address.getBroadcast() != null) &&
+ Util.sameNetwork(address.getNetworkPrefixLength(), aDevice.getAddress(), address.getAddress())) {
+ return address;
+ }
+ }
+ return null;
+ }
+
/**
* Transforms an album art path to the version that will contain high resolution art, if that is available.
*
diff --git a/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java b/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
index d147f5ab..27820710 100644
--- a/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
+++ b/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
@@ -563,29 +563,6 @@ void processBeat(Beat beat) {
}
}
- /**
- * Scan a network interface to find if it has an address space which matches the device we are trying to reach.
- * If so, return the address specification.
- *
- * @param aDevice the DJ Link device we are trying to communicate with
- * @param networkInterface the network interface we are testing
- * @return the address which can be used to communicate with the device on the interface, or null
- */
- private InterfaceAddress findMatchingAddress(DeviceAnnouncement aDevice, NetworkInterface networkInterface) {
- for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) {
- if (address == null) {
- // This should never happen, but we are protecting against a Windows Java bug, see
- // https://bugs.java.com/bugdatabase/view_bug?bug_id=8023649
- logger.warn("Received a null InterfaceAddress from networkInterface.getInterfaceAddresses(), is this Windows? " +
- "Do you have a VPN installed? Trying to recover by ignoring it.");
- } else if ((address.getBroadcast() != null) &&
- Util.sameNetwork(address.getNetworkPrefixLength(), aDevice.getAddress(), address.getAddress())) {
- return address;
- }
- }
- return null;
- }
-
/**
* The number of milliseconds for which the {@link DeviceFinder} needs to have been watching the network in order
* for us to be confident we can choose a device number that will not conflict.
@@ -913,7 +890,7 @@ private boolean createVirtualCdj() throws SocketException {
matchedAddress = null;
DeviceAnnouncement aDevice = DeviceFinder.getInstance().getCurrentDevices().iterator().next();
for (NetworkInterface networkInterface : Collections.list(NetworkInterface.getNetworkInterfaces())) {
- InterfaceAddress candidate = findMatchingAddress(aDevice, networkInterface);
+ InterfaceAddress candidate = Util.findMatchingAddress(aDevice, networkInterface);
if (candidate != null) {
if (matchedAddress == null) {
matchedAddress = candidate;
@@ -1118,7 +1095,6 @@ public synchronized boolean start() throws Exception {
// See if there is an Opus Quad on the network, which means we need to be in the limited compatibility mode.
for (DeviceAnnouncement device : DeviceFinder.getInstance().getCurrentDevices()) {
if (Util.isOpusQuad(device.getDeviceName())) {
- proxyingForVirtualRekordbox.set(true);
VirtualRekordbox.getInstance().addLifecycleListener(virtualRekordboxLifecycleListener);
final boolean success = VirtualRekordbox.getInstance().start();
if (success) {
@@ -1126,6 +1102,10 @@ public synchronized boolean start() throws Exception {
matchedAddress = VirtualRekordbox.getInstance().getMatchedAddress();
matchingInterfaces = VirtualRekordbox.getInstance().getMatchingInterfaces();
}
+
+ // This must be set after we start VirtualRekordbox, otherwise we will get an IllegalStateException
+ // because VirtualRekordbox should not be started up if VirtualCdj is still running.
+ proxyingForVirtualRekordbox.set(true);
return success;
}
}
diff --git a/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java b/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
index cc742b53..b1a87d8f 100644
--- a/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
+++ b/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
@@ -314,12 +314,13 @@ private DeviceUpdate buildUpdate(DatagramPacket packet) throws IOException {
if (length >= CdjStatus.MINIMUM_PACKET_SIZE) {
CdjStatus status = new CdjStatus(packet);
-// MediaDetails details = new MediaDetails(
-// SlotReference.getSlotReference(status.getDeviceNumber(), CdjStatus.TrackSourceSlot.SD_SLOT),
-// CdjStatus.TrackType.REKORDBOX,
-// status.getDeviceName());
-//
-// deliverMediaDetailsUpdate(details);
+ // Need to fill in MediaDetails otherwise MetadataFinder won't forward us to OpusProvider
+ MediaDetails details = new MediaDetails(
+ SlotReference.getSlotReference(status.getDeviceNumber(), CdjStatus.TrackSourceSlot.SD_SLOT),
+ CdjStatus.TrackType.REKORDBOX,
+ status.getDeviceName());
+
+ deliverMediaDetailsUpdate(details);
return status;
} else {
@@ -337,17 +338,7 @@ private DeviceUpdate buildUpdate(DatagramPacket packet) throws IOException {
}
case OPUS_METADATA:
- if (packet.getData()[0x25] == 0x02) {
- OpusMetadata metadata = new OpusMetadata(packet.getData());
-
- logger.info("beatgrid");
- }
-// StringBuilder sb = new StringBuilder();
-// for (byte b : packet.getData()) {
-// sb.append(String.format("%02X ", b));
-// }
-// System.out.println();
-// logger.info("Received track load metadata from player " + packet.getData()[0x21]);
+ logger.info("Received track load metadata from player " + packet.getData()[0x21]);
return null;
default:
@@ -356,32 +347,6 @@ private DeviceUpdate buildUpdate(DatagramPacket packet) throws IOException {
}
}
- public class OpusMetadata {
- private byte[] packetBytes;
-
- public OpusMetadata(byte[] packetBytes) {
- StringBuilder sb = new StringBuilder();
- for (byte b : packetBytes) {
- sb.append(String.format("%02X ", b));
- }
- System.out.println(sb);
- this.packetBytes = packetBytes;
- }
-
- public byte[] getPacketBytes() {
- return packetBytes;
- }
-
- public String toString(){
- StringBuilder sb = new StringBuilder();
- for (byte b : packetBytes) {
- sb.append(String.format("%02X ", b));
- }
- return sb.toString();
- }
- }
-
-
/**
* This will send the bytes Rekordbox Lighting sends to a player to acknowledge its existence on the network and
* trigger it to begin sending CDJStatus packets.
@@ -812,29 +777,32 @@ private void deliverMediaDetailsUpdate(final MediaDetails details) {
* @throws Exception if there is a problem opening a socket on the right network
*/
private boolean createVirtualRekordbox() throws Exception {
- OpusProvider.getInstance().attachMetadataArchive(new File("/Users/cprepos/krisprep/BLT Archive/archive.blm"), CdjStatus.TrackSourceSlot.SD_SLOT);
- OpusProvider.getInstance().attachMetadataArchive(new File("/Users/cprepos/krisprep/BLT Archive/archive.blm"), CdjStatus.TrackSourceSlot.USB_SLOT);
OpusProvider.getInstance().start();
- // Find the network interface and address to use to communicate with the first device we found.
- matchingInterfaces = new ArrayList();
- matchedAddress = null;
-
// Forward Updates to VirtualCdj. That's where all clients are used to getting them.
addUpdateListener(VirtualCdj.getInstance().getUpdateListener());
addMediaDetailsListener(VirtualCdj.getInstance().getMediaDetailsListener());
+ // Find the network interface and address to use to communicate with the first device we found.
+ matchingInterfaces = new ArrayList();
+ matchedAddress = null;
+ DeviceAnnouncement aDevice = DeviceFinder.getInstance().getCurrentDevices().iterator().next();
for (NetworkInterface networkInterface : Collections.list(NetworkInterface.getNetworkInterfaces())) {
- for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) {
- if (address.getAddress() instanceof Inet4Address && !address.getAddress().getHostAddress().contains("127.0.0.1")) {
- if (matchedAddress == null) {
- matchedAddress = address;
- }
- matchingInterfaces.add(networkInterface);
+ InterfaceAddress candidate = Util.findMatchingAddress(aDevice, networkInterface);
+ if (candidate != null) {
+ if (matchedAddress == null) {
+ matchedAddress = candidate;
}
+ matchingInterfaces.add(networkInterface);
}
}
+ if (matchedAddress == null) {
+ logger.warn("Unable to find network interface to communicate with " + aDevice +
+ ", giving up.");
+ return false;
+ }
+
logger.info("Found matching network interface " + matchingInterfaces.get(0).getDisplayName() + " (" +
matchingInterfaces.get(0).getName() + "), will use address " + matchedAddress);
if (matchingInterfaces.size() > 1) {
@@ -875,9 +843,6 @@ private boolean createVirtualRekordbox() throws Exception {
return false;
}
- // Now that VirtualRekordbox is running, start up VirtualCdj and it will run in proxy mode.
- VirtualCdj.getInstance().start();
-
// Set up our buffer and packet to receive incoming messages.
final byte[] buffer = new byte[512];
final DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
@@ -985,9 +950,6 @@ public void stopped(LifecycleParticipant sender) {
@SuppressWarnings("UnusedReturnValue")
synchronized boolean start() throws Exception {
if (!isRunning()) {
- if (VirtualCdj.getInstance().isRunning()) {
- throw new IllegalStateException("Cannot start VirtualRekordbox when VirtualCdj has already been started up");
- }
// Set up so we know we have to shut down if the DeviceFinder shuts down.
DeviceFinder.getInstance().addLifecycleListener(deviceFinderLifecycleListener);
From d786a4eabb0411a945bf6eef6995a257f041a67a Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 18:29:02 -0400
Subject: [PATCH 04/16] remove comment thats no longer applicable
---
src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java b/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
index 27820710..3aca5a15 100644
--- a/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
+++ b/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
@@ -1103,8 +1103,6 @@ public synchronized boolean start() throws Exception {
matchingInterfaces = VirtualRekordbox.getInstance().getMatchingInterfaces();
}
- // This must be set after we start VirtualRekordbox, otherwise we will get an IllegalStateException
- // because VirtualRekordbox should not be started up if VirtualCdj is still running.
proxyingForVirtualRekordbox.set(true);
return success;
}
From 216855db7f44d1b61a2c4b2ef9b7f9073b9bbcfb Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 18:46:21 -0400
Subject: [PATCH 05/16] Adjustments for PR
---
.../deepsymmetry/beatlink/MediaDetails.java | 18 ++++----
.../beatlink/VirtualRekordbox.java | 34 +++++++-------
.../org/deepsymmetry/beatlink/TestRunner.java | 44 -------------------
3 files changed, 26 insertions(+), 70 deletions(-)
delete mode 100644 src/main/test/org/deepsymmetry/beatlink/TestRunner.java
diff --git a/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java b/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
index a9a6cdca..cad820b5 100644
--- a/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
+++ b/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
@@ -127,22 +127,24 @@ private int getUTF16StringLength(byte[] source, int offset, int maxLength) {
*/
/**
+ * Custom constructor to emulate an actual MediaDetails object from CDJs. This allows our Status packets
+ * to use OpusProvider to enrich the track data.
*
- * @param slotReference Slot Reference for the
- * @param mediaType
- * @param name
+ * @param slotReference Slot Reference to Emulate
+ * @param mediaType Media Type to Emulate
+ * @param name Name of device
*/
public MediaDetails(SlotReference slotReference, CdjStatus.TrackType mediaType, String name) {
this.slotReference = slotReference;
this.mediaType = mediaType;
this.name = name;
this.creationDate = "";
- this.trackCount = 100;
- this.totalSize = 100;
- this.playlistCount = 100;
+ this.trackCount = 0;
+ this.totalSize = 0;
+ this.playlistCount = 0;
this.rawBytes = ByteBuffer.wrap(new byte[]{});
- this.color = new Color(100);
- this.freeSpace = 100;
+ this.color = new Color(0);
+ this.freeSpace = 0;
this.hasMySettings = false;
}
diff --git a/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java b/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
index b1a87d8f..8dbaef92 100644
--- a/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
+++ b/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
@@ -2,11 +2,9 @@
import org.deepsymmetry.beatlink.data.OpusProvider;
import org.deepsymmetry.beatlink.data.SlotReference;
-import org.deepsymmetry.cratedigger.Database;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.File;
import java.io.IOException;
import java.net.*;
import java.util.*;
@@ -121,7 +119,7 @@ public boolean getUseStandardPlayerNumber() {
* @return the virtual player number
*/
public synchronized byte getDeviceNumber() {
- return rekordboxLightingKeepAliveBytes[DEVICE_NUMBER_OFFSET];
+ return rekordboxKeepAliveBytes[DEVICE_NUMBER_OFFSET];
}
/**
@@ -138,7 +136,7 @@ public synchronized void setDeviceNumber(byte number) {
if (isRunning()) {
throw new IllegalStateException("Can't change device number once started.");
}
- rekordboxLightingKeepAliveBytes[DEVICE_NUMBER_OFFSET] = number;
+ rekordboxKeepAliveBytes[DEVICE_NUMBER_OFFSET] = number;
}
/**
@@ -176,7 +174,7 @@ public void setAnnounceInterval(int interval) {
* and IP address, as described in the
* Packet Analysis document.
*/
- private static final byte[] rekordboxLightingKeepAliveBytes = {
+ private static final byte[] rekordboxKeepAliveBytes = {
0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x06, 0x00, 0x72, 0x65, 0x6b, 0x6f, 0x72,
0x64, 0x62, 0x6f, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03,
0x00, 0x36, 0x17, 0x01, 0x18, 0x3e, (byte) 0xef, (byte) 0xda, 0x5b, (byte) 0xca, (byte) 0xc0, (byte) 0xa8,
@@ -225,7 +223,7 @@ public void setAnnounceInterval(int interval) {
* @return the device name reported in our presence announcement packets
*/
public static String getDeviceName() {
- return new String(rekordboxLightingKeepAliveBytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH).trim();
+ return new String(rekordboxKeepAliveBytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH).trim();
}
/**
@@ -506,9 +504,9 @@ private void requestNumberFromMixer(InetAddress mixerAddress) {
Arrays.fill(assignmentRequestBytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte) 0);
System.arraycopy(getDeviceName().getBytes(), 0, assignmentRequestBytes, DEVICE_NAME_OFFSET, getDeviceName().getBytes().length);
System.arraycopy(matchedAddress.getAddress().getAddress(), 0, assignmentRequestBytes, 0x24, 4);
- System.arraycopy(rekordboxLightingKeepAliveBytes, MAC_ADDRESS_OFFSET, assignmentRequestBytes, 0x28, 6);
+ System.arraycopy(rekordboxKeepAliveBytes, MAC_ADDRESS_OFFSET, assignmentRequestBytes, 0x28, 6);
// Can't call getDeviceNumber() on next line because that's synchronized!
- assignmentRequestBytes[0x31] = (rekordboxLightingKeepAliveBytes[DEVICE_NUMBER_OFFSET] == 0) ? (byte) 1 : (byte) 2; // The auto-assign flag.
+ assignmentRequestBytes[0x31] = (rekordboxKeepAliveBytes[DEVICE_NUMBER_OFFSET] == 0) ? (byte) 1 : (byte) 2; // The auto-assign flag.
assignmentRequestBytes[0x2f] = 1; // The packet counter.
try {
DatagramPacket announcement = new DatagramPacket(assignmentRequestBytes, assignmentRequestBytes.length,
@@ -537,7 +535,7 @@ void defendDeviceNumber(InetAddress invaderAddress) {
// Send a packet to the interloper telling it that we are using that device number.
Arrays.fill(deviceNumberDefenseBytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte) 0);
System.arraycopy(getDeviceName().getBytes(), 0, deviceNumberDefenseBytes, DEVICE_NAME_OFFSET, getDeviceName().getBytes().length);
- deviceNumberDefenseBytes[0x24] = rekordboxLightingKeepAliveBytes[DEVICE_NUMBER_OFFSET];
+ deviceNumberDefenseBytes[0x24] = rekordboxKeepAliveBytes[DEVICE_NUMBER_OFFSET];
System.arraycopy(matchedAddress.getAddress().getAddress(), 0, deviceNumberDefenseBytes, 0x25, 4);
try {
DatagramPacket defense = new DatagramPacket(deviceNumberDefenseBytes, deviceNumberDefenseBytes.length,
@@ -594,7 +592,7 @@ private boolean claimDeviceNumber() {
// or a mixer assigning us a specific number.
Arrays.fill(claimStage1bytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte) 0);
System.arraycopy(getDeviceName().getBytes(), 0, claimStage1bytes, DEVICE_NAME_OFFSET, getDeviceName().getBytes().length);
- System.arraycopy(rekordboxLightingKeepAliveBytes, MAC_ADDRESS_OFFSET, claimStage1bytes, 0x26, 6);
+ System.arraycopy(rekordboxKeepAliveBytes, MAC_ADDRESS_OFFSET, claimStage1bytes, 0x26, 6);
for (int i = 1; i <= 3 && mixerAssigned.get() == 0; i++) {
claimStage1bytes[0x24] = (byte) i; // The packet counter.
try {
@@ -624,7 +622,7 @@ private boolean claimDeviceNumber() {
Arrays.fill(claimStage2bytes, DEVICE_NAME_OFFSET, DEVICE_NAME_LENGTH, (byte) 0);
System.arraycopy(getDeviceName().getBytes(), 0, claimStage2bytes, DEVICE_NAME_OFFSET, getDeviceName().getBytes().length);
System.arraycopy(matchedAddress.getAddress().getAddress(), 0, claimStage2bytes, 0x24, 4);
- System.arraycopy(rekordboxLightingKeepAliveBytes, MAC_ADDRESS_OFFSET, claimStage2bytes, 0x28, 6);
+ System.arraycopy(rekordboxKeepAliveBytes, MAC_ADDRESS_OFFSET, claimStage2bytes, 0x28, 6);
claimStage2bytes[0x2e] = (byte) claimingNumber.get(); // The number we are claiming.
claimStage2bytes[0x31] = (getDeviceNumber() == 0) ? (byte) 1 : (byte) 2; // The auto-assign flag.
for (int i = 1; i <= 3 && mixerAssigned.get() == 0; i++) {
@@ -689,14 +687,14 @@ private boolean claimDeviceNumber() {
claimed = true; // If we finished all our loops, the number we wanted is ours.
}
// Set the device number we claimed.
- rekordboxLightingKeepAliveBytes[DEVICE_NUMBER_OFFSET] = (byte) claimingNumber.getAndSet(0);
+ rekordboxKeepAliveBytes[DEVICE_NUMBER_OFFSET] = (byte) claimingNumber.getAndSet(0);
mixerAssigned.set(0);
return true; // Huzzah, we found the right device number to use!
}
- public void sendRekordboxLightingAnnouncement() {
+ public void sendRekordboxAnnouncement() {
if (isRunning()) {
- DatagramPacket announcement = new DatagramPacket(rekordboxLightingKeepAliveBytes, rekordboxLightingKeepAliveBytes.length,
+ DatagramPacket announcement = new DatagramPacket(rekordboxKeepAliveBytes, rekordboxKeepAliveBytes.length,
broadcastAddress.get(), DeviceFinder.ANNOUNCEMENT_PORT);
try {
this.socket.get().send(announcement);
@@ -817,9 +815,9 @@ private boolean createVirtualRekordbox() throws Exception {
socket.set(new DatagramSocket(UPDATE_PORT, matchedAddress.getAddress()));
System.arraycopy(getMatchingInterfaces().get(0).getHardwareAddress(),
- 0, rekordboxLightingKeepAliveBytes, MAC_ADDRESS_OFFSET, 6);
+ 0, rekordboxKeepAliveBytes, MAC_ADDRESS_OFFSET, 6);
System.arraycopy(matchedAddress.getAddress().getAddress(),
- 0, rekordboxLightingKeepAliveBytes, 44, 4);
+ 0, rekordboxKeepAliveBytes, 44, 4);
System.arraycopy(getMatchingInterfaces().get(0).getHardwareAddress(),
0, rekordboxLightingRequestStatusBytes, MAC_ADDRESS_OFFSET, 6);
System.arraycopy(matchedAddress.getAddress().getAddress(),
@@ -909,12 +907,12 @@ public void run() {
*/
private void sendAnnouncements() {
try {
- sendRekordboxLightingAnnouncement();
+ sendRekordboxAnnouncement();
sendRekordboxLightingPacket();
Thread.sleep(getAnnounceInterval());
} catch (Throwable t) {
- logger.warn("Unable to send announcement packet, flushing DeviceFinder due to likely network change and shutting down.", t);
+ logger.warn("Unable to send announcement packets, flushing DeviceFinder due to likely network change and shutting down.", t);
DeviceFinder.getInstance().flush();
stop();
}
diff --git a/src/main/test/org/deepsymmetry/beatlink/TestRunner.java b/src/main/test/org/deepsymmetry/beatlink/TestRunner.java
deleted file mode 100644
index 8114a8ab..00000000
--- a/src/main/test/org/deepsymmetry/beatlink/TestRunner.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.deepsymmetry.beatlink;
-
-import org.deepsymmetry.beatlink.data.CrateDigger;
-import org.deepsymmetry.beatlink.data.MetadataFinder;
-import org.deepsymmetry.cratedigger.Database;
-import org.deepsymmetry.cratedigger.pdb.RekordboxPdb;
-import org.junit.Test;
-
-import java.io.File;
-import java.io.IOException;
-
-public class TestRunner {
-
- // Convenience integration test
- @Test
- public void TestRunner() throws Exception {
-
- VirtualCdj.getInstance().start();
- VirtualCdj.getInstance().addUpdateListener(new DeviceUpdateListener() {
- @Override
- public void received(DeviceUpdate update) {
- if (update instanceof CdjStatus) {
- CdjStatus status = (CdjStatus) update;
- } else {
-
- StringBuilder sb = new StringBuilder();
- for (byte b : update.packetBytes) {
- sb.append(String.format("%02X ", b));
- }
- System.out.println(sb);
- }
- }
- });
-
-
- MetadataFinder.getInstance().start();
- System.out.println("Started up!");
- try {
- Thread.sleep(600000000);
- } catch (InterruptedException e) {
- System.out.println("Interrupted, exiting.");
- }
- }
-}
From 328591ab783cad3c869e3b94c0c161e1f49601c4 Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 18:58:49 -0400
Subject: [PATCH 06/16] unneeded change
---
src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java b/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
index 3aca5a15..a7be1edf 100644
--- a/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
+++ b/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
@@ -1095,6 +1095,7 @@ public synchronized boolean start() throws Exception {
// See if there is an Opus Quad on the network, which means we need to be in the limited compatibility mode.
for (DeviceAnnouncement device : DeviceFinder.getInstance().getCurrentDevices()) {
if (Util.isOpusQuad(device.getDeviceName())) {
+ proxyingForVirtualRekordbox.set(true);
VirtualRekordbox.getInstance().addLifecycleListener(virtualRekordboxLifecycleListener);
final boolean success = VirtualRekordbox.getInstance().start();
if (success) {
@@ -1102,8 +1103,6 @@ public synchronized boolean start() throws Exception {
matchedAddress = VirtualRekordbox.getInstance().getMatchedAddress();
matchingInterfaces = VirtualRekordbox.getInstance().getMatchingInterfaces();
}
-
- proxyingForVirtualRekordbox.set(true);
return success;
}
}
@@ -1429,7 +1428,7 @@ private void deliverDeviceUpdate(final DeviceUpdate update) {
}
}
- // This is a way to get mediaDetails from Rekordbox and pass to all MediaDetailsListeners
+ // This is a way to get mediaDetails from VirtualRekordbox and pass to all MediaDetailsListeners listening from VirtualCdj.
private final MediaDetailsListener mediaDetailsListener = new MediaDetailsListener(){
@Override
public void detailsAvailable(MediaDetails details) {
From 43937b3e6831202c855e8f8cb3ac3edfe0e6ef99 Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 19:39:24 -0400
Subject: [PATCH 07/16] better name for var
---
src/main/java/org/deepsymmetry/beatlink/Util.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/Util.java b/src/main/java/org/deepsymmetry/beatlink/Util.java
index 62aee888..97f75f13 100644
--- a/src/main/java/org/deepsymmetry/beatlink/Util.java
+++ b/src/main/java/org/deepsymmetry/beatlink/Util.java
@@ -962,11 +962,11 @@ public static String phraseLabel(final SongStructureEntry phrase) {
* Scan a network interface to find if it has an address space which matches the device we are trying to reach.
* If so, return the address specification.
*
- * @param aDevice the DJ Link device we are trying to communicate with
+ * @param announcement the DJ Link device we are trying to communicate with
* @param networkInterface the network interface we are testing
* @return the address which can be used to communicate with the device on the interface, or null
*/
- public static InterfaceAddress findMatchingAddress(DeviceAnnouncement aDevice, NetworkInterface networkInterface) {
+ public static InterfaceAddress findMatchingAddress(DeviceAnnouncement announcement, NetworkInterface networkInterface) {
for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) {
if (address == null) {
// This should never happen, but we are protecting against a Windows Java bug, see
@@ -974,7 +974,7 @@ public static InterfaceAddress findMatchingAddress(DeviceAnnouncement aDevice, N
logger.warn("Received a null InterfaceAddress from networkInterface.getInterfaceAddresses(), is this Windows? " +
"Do you have a VPN installed? Trying to recover by ignoring it.");
} else if ((address.getBroadcast() != null) &&
- Util.sameNetwork(address.getNetworkPrefixLength(), aDevice.getAddress(), address.getAddress())) {
+ Util.sameNetwork(address.getNetworkPrefixLength(), announcement.getAddress(), address.getAddress())) {
return address;
}
}
From 90dc86baca265d2cd36cd4dc86d6822de6fa11fd Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 19:41:39 -0400
Subject: [PATCH 08/16] package private constructor for MediaDetails
---
src/main/java/org/deepsymmetry/beatlink/MediaDetails.java | 2 +-
src/main/java/org/deepsymmetry/beatlink/Util.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java b/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
index cad820b5..1e241546 100644
--- a/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
+++ b/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
@@ -134,7 +134,7 @@ private int getUTF16StringLength(byte[] source, int offset, int maxLength) {
* @param mediaType Media Type to Emulate
* @param name Name of device
*/
- public MediaDetails(SlotReference slotReference, CdjStatus.TrackType mediaType, String name) {
+ MediaDetails(SlotReference slotReference, CdjStatus.TrackType mediaType, String name) {
this.slotReference = slotReference;
this.mediaType = mediaType;
this.name = name;
diff --git a/src/main/java/org/deepsymmetry/beatlink/Util.java b/src/main/java/org/deepsymmetry/beatlink/Util.java
index 97f75f13..aca5880f 100644
--- a/src/main/java/org/deepsymmetry/beatlink/Util.java
+++ b/src/main/java/org/deepsymmetry/beatlink/Util.java
@@ -976,7 +976,7 @@ public static InterfaceAddress findMatchingAddress(DeviceAnnouncement announceme
} else if ((address.getBroadcast() != null) &&
Util.sameNetwork(address.getNetworkPrefixLength(), announcement.getAddress(), address.getAddress())) {
return address;
- }
+ }`
}
return null;
}
From 37abd486341514f1e27691d37b564bae710d002c Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 19:46:00 -0400
Subject: [PATCH 09/16] isLocalUsbLoaded() == true
---
src/main/java/org/deepsymmetry/beatlink/CdjStatus.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/main/java/org/deepsymmetry/beatlink/CdjStatus.java b/src/main/java/org/deepsymmetry/beatlink/CdjStatus.java
index 7987c47b..6f92b1bf 100644
--- a/src/main/java/org/deepsymmetry/beatlink/CdjStatus.java
+++ b/src/main/java/org/deepsymmetry/beatlink/CdjStatus.java
@@ -645,6 +645,8 @@ public CdjStatus(DatagramPacket packet) {
trackSourceSlot = findOpusTrackSourceSlot();
// isLocalSdLoaded() == true
packetBytes[115] = 0;
+ // isLocalUsbLoaded() == true
+ packetBytes[111] = 0;
} else {
trackSourcePlayer = packetBytes[40];
trackSourceSlot = findTrackSourceSlot();
From 4fe429d4e1b8c1bd98ff18eea96adcce6cb91873 Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 19:47:13 -0400
Subject: [PATCH 10/16] typo
---
src/main/java/org/deepsymmetry/beatlink/Util.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/Util.java b/src/main/java/org/deepsymmetry/beatlink/Util.java
index aca5880f..97f75f13 100644
--- a/src/main/java/org/deepsymmetry/beatlink/Util.java
+++ b/src/main/java/org/deepsymmetry/beatlink/Util.java
@@ -976,7 +976,7 @@ public static InterfaceAddress findMatchingAddress(DeviceAnnouncement announceme
} else if ((address.getBroadcast() != null) &&
Util.sameNetwork(address.getNetworkPrefixLength(), announcement.getAddress(), address.getAddress())) {
return address;
- }`
+ }
}
return null;
}
From b39df0c75bf1a5105f69958f663db66c77c02b67 Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 19:53:05 -0400
Subject: [PATCH 11/16] check VirtualRekordbox running for opus findDatabase
---
.../org/deepsymmetry/beatlink/data/OpusProvider.java | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/data/OpusProvider.java b/src/main/java/org/deepsymmetry/beatlink/data/OpusProvider.java
index 2b7b9d12..dcf14464 100644
--- a/src/main/java/org/deepsymmetry/beatlink/data/OpusProvider.java
+++ b/src/main/java/org/deepsymmetry/beatlink/data/OpusProvider.java
@@ -2,9 +2,7 @@
import io.kaitai.struct.RandomAccessFileKaitaiStream;
import org.apiguardian.api.API;
-import org.deepsymmetry.beatlink.CdjStatus;
-import org.deepsymmetry.beatlink.MediaDetails;
-import org.deepsymmetry.beatlink.Util;
+import org.deepsymmetry.beatlink.*;
import org.deepsymmetry.cratedigger.Database;
import org.deepsymmetry.cratedigger.pdb.RekordboxAnlz;
import org.deepsymmetry.cratedigger.pdb.RekordboxPdb;
@@ -161,7 +159,10 @@ public synchronized void attachMetadataArchive(File archive, CdjStatus.TrackSour
*/
@API(status = API.Status.STABLE)
public Database findDatabase(DataReference reference) {
- return findDatabase(reference.getSlotReference());
+ if (VirtualRekordbox.getInstance().isRunning()) {
+ return findDatabase(reference.getSlotReference());
+ }
+ return null;
}
/**
From 96f157032853f03f90f517a16b143b208f0db157 Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 20:01:43 -0400
Subject: [PATCH 12/16] grab data from opusprovider for slot details
---
.../org/deepsymmetry/beatlink/MediaDetails.java | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java b/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
index 1e241546..b7595c06 100644
--- a/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
+++ b/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
@@ -1,7 +1,9 @@
package org.deepsymmetry.beatlink;
import org.deepsymmetry.beatlink.data.ColorItem;
+import org.deepsymmetry.beatlink.data.OpusProvider;
import org.deepsymmetry.beatlink.data.SlotReference;
+import org.deepsymmetry.cratedigger.Database;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -9,6 +11,7 @@
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.nio.ByteBuffer;
+import java.nio.file.FileSystem;
import java.util.*;
/**
@@ -128,20 +131,25 @@ private int getUTF16StringLength(byte[] source, int offset, int maxLength) {
/**
* Custom constructor to emulate an actual MediaDetails object from CDJs. This allows our Status packets
- * to use OpusProvider to enrich the track data.
+ * to use OpusProvider to enrich the track data. OpusProvider must be started up to use this method.
*
* @param slotReference Slot Reference to Emulate
* @param mediaType Media Type to Emulate
* @param name Name of device
*/
MediaDetails(SlotReference slotReference, CdjStatus.TrackType mediaType, String name) {
+ if (!OpusProvider.getInstance().isRunning()) {
+ throw new IllegalStateException("OpusProvider must be started up to use this Constructor");
+ }
+ Database db = OpusProvider.getInstance().findDatabase(slotReference);
+ FileSystem fs = OpusProvider.getInstance().findFilesystem(slotReference);
this.slotReference = slotReference;
this.mediaType = mediaType;
this.name = name;
- this.creationDate = "";
- this.trackCount = 0;
+ this.creationDate = Long.toString(db.sourceFile.lastModified());
+ this.trackCount = db.trackIndex.size();
this.totalSize = 0;
- this.playlistCount = 0;
+ this.playlistCount = db.playlistIndex.size();
this.rawBytes = ByteBuffer.wrap(new byte[]{});
this.color = new Color(0);
this.freeSpace = 0;
From a5427d2416b4e908847165ce20ecf78fced37a5a Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 20:13:16 -0400
Subject: [PATCH 13/16] indents for he
---
.../beatlink/VirtualRekordbox.java | 81 ++++++++++---------
1 file changed, 41 insertions(+), 40 deletions(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java b/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
index 8dbaef92..852b5ca9 100644
--- a/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
+++ b/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
@@ -175,31 +175,32 @@ public void setAnnounceInterval(int interval) {
* Packet Analysis document.
*/
private static final byte[] rekordboxKeepAliveBytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x06, 0x00, 0x72, 0x65, 0x6b, 0x6f, 0x72,
- 0x64, 0x62, 0x6f, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03,
- 0x00, 0x36, 0x17, 0x01, 0x18, 0x3e, (byte) 0xef, (byte) 0xda, 0x5b, (byte) 0xca, (byte) 0xc0, (byte) 0xa8,
- 0x02, 0x0b, 0x04, 0x01, 0x00, 0x00, 0x04, 0x08
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x06, 0x00, 0x72, 0x65, 0x6b, 0x6f, 0x72,
+ 0x64, 0x62, 0x6f, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03,
+ 0x00, 0x36, 0x17, 0x01, 0x18, 0x3e, (byte) 0xef, (byte) 0xda, 0x5b, (byte) 0xca, (byte) 0xc0, (byte) 0xa8,
+ 0x02, 0x0b, 0x04, 0x01, 0x00, 0x00, 0x04, 0x08
};
private static final byte[] rekordboxLightingRequestStatusBytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x11, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x64,
- 0x62, 0x6f, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x17,
- 0x01, 0x04, 0x17, 0x01, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x63, 0x00, 0x62, 0x00, 0x6f, 0x00,
- 0x6f, 0x00, 0x6b, 0x00, 0x20, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x11, 0x72, 0x65, 0x6b, 0x6f, 0x72,
+ 0x64, 0x62, 0x6f, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x17, 0x01, 0x04, 0x17, 0x01, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x63, 0x00, 0x62,
+ 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6b, 0x00, 0x20, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/**
@@ -230,27 +231,27 @@ public static String getDeviceName() {
* The initial packet sent three times when coming online.
*/
private static final byte[] helloBytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x0a, 0x00, 0x62, 0x65, 0x61, 0x74,
- 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x04, 0x00, 0x26, 0x01, 0x40
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x0a, 0x00, 0x62, 0x65, 0x61, 0x74,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x04, 0x00, 0x26, 0x01, 0x40
};
/**
* The first-stage device number claim packet series.
*/
private static final byte[] claimStage1bytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x00, 0x00, 0x62, 0x65, 0x61, 0x74,
- 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x03, 0x00, 0x2c, 0x0d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x00, 0x00, 0x62, 0x65, 0x61, 0x74,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x03, 0x00, 0x2c, 0x0d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/**
* The second-stage device number claim packet series.
*/
private static final byte[] claimStage2bytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x02, 0x00, 0x62, 0x65, 0x61, 0x74,
- 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x03, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x02, 0x00, 0x62, 0x65, 0x61, 0x74,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x03, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
0x01, 0x00
};
@@ -258,18 +259,18 @@ public static String getDeviceName() {
* The third-stage (final) device number claim packet series.
*/
private static final byte[] claimStage3bytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x04, 0x00, 0x62, 0x65, 0x61, 0x74,
- 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x03, 0x00, 0x26, 0x0d, 0x00
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x04, 0x00, 0x62, 0x65, 0x61, 0x74,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x03, 0x00, 0x26, 0x0d, 0x00
};
/**
* Packet used to acknowledge a mixer's intention to assign us a device number.
*/
private static final byte[] assignmentRequestBytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x02, 0x01, 0x62, 0x65, 0x61, 0x74,
- 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x02, 0x01, 0x62, 0x65, 0x61, 0x74,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00
};
@@ -277,9 +278,9 @@ public static String getDeviceName() {
* Packet used to tell another device we are already using a device number.
*/
private static final byte[] deviceNumberDefenseBytes = {
- 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x08, 0x00, 0x62, 0x65, 0x61, 0x74,
- 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x51, 0x73, 0x70, 0x74, 0x31, 0x57, 0x6d, 0x4a, 0x4f, 0x4c, 0x08, 0x00, 0x62, 0x65, 0x61, 0x74,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00
};
/**
From bb7a17cb0e1a2b843d82ec68ed9cde344cfda827 Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 20:28:57 -0400
Subject: [PATCH 14/16] we dont need media detailslisteners. Just proxy to
VirtualCdj to be spread to other clients
---
.../org/deepsymmetry/beatlink/VirtualCdj.java | 16 +----
.../beatlink/VirtualRekordbox.java | 70 +------------------
2 files changed, 4 insertions(+), 82 deletions(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java b/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
index a7be1edf..c5402694 100644
--- a/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
+++ b/src/main/java/org/deepsymmetry/beatlink/VirtualCdj.java
@@ -1428,18 +1428,6 @@ private void deliverDeviceUpdate(final DeviceUpdate update) {
}
}
- // This is a way to get mediaDetails from VirtualRekordbox and pass to all MediaDetailsListeners listening from VirtualCdj.
- private final MediaDetailsListener mediaDetailsListener = new MediaDetailsListener(){
- @Override
- public void detailsAvailable(MediaDetails details) {
- deliverMediaDetailsUpdate(details);
- }
- };
-
- public MediaDetailsListener getMediaDetailsListener() {
- return mediaDetailsListener;
- }
-
/**
* Keeps track of the registered media details listeners.
*/
@@ -1492,11 +1480,11 @@ public Set getMediaDetailsListeners() {
}
/**
- * Send a media details response to all registered listeners.
+ * Send a media details response to all registered listeners. Is also called from VirtualRekordbox in proxy mode.
*
* @param details the response that has just arrived
*/
- private void deliverMediaDetailsUpdate(final MediaDetails details) {
+ void deliverMediaDetailsUpdate(final MediaDetails details) {
for (MediaDetailsListener listener : getMediaDetailsListeners()) {
try {
listener.detailsAvailable(details);
diff --git a/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java b/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
index 852b5ca9..e389c474 100644
--- a/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
+++ b/src/main/java/org/deepsymmetry/beatlink/VirtualRekordbox.java
@@ -319,7 +319,8 @@ private DeviceUpdate buildUpdate(DatagramPacket packet) throws IOException {
CdjStatus.TrackType.REKORDBOX,
status.getDeviceName());
- deliverMediaDetailsUpdate(details);
+ // Forward this to VirtualCdj where it will be sent to clients.
+ VirtualCdj.getInstance().deliverMediaDetailsUpdate(details);
return status;
} else {
@@ -705,72 +706,6 @@ public void sendRekordboxAnnouncement() {
}
}
- /**
- * Keeps track of the registered media details listeners.
- */
- private final Set detailsListeners =
- Collections.newSetFromMap(new ConcurrentHashMap());
-
- /**
- * Adds the specified media details listener to receive detail responses whenever they come in.
- * If {@code listener} is {@code null} or already present in the list
- * of registered listeners, no exception is thrown and no action is performed.
- *
- * To reduce latency, device updates are delivered to listeners directly on the thread that is receiving them
- * from the network, so if you want to interact with user interface objects in listener methods, you need to use
- * javax.swing.SwingUtilities.invokeLater(Runnable)
- * to do so on the Event Dispatch Thread.
- *
- * Even if you are not interacting with user interface objects, any code in the listener method
- * must finish quickly, or it will add latency for other listeners, and detail updates will back up.
- * If you want to perform lengthy processing of any sort, do so on another thread.
- *
- * @param listener the media details listener to add
- */
- public void addMediaDetailsListener(MediaDetailsListener listener) {
- if (listener != null) {
- detailsListeners.add(listener);
- }
- }
-
- /**
- * Removes the specified media details listener so it no longer receives detail responses when they come in.
- * If {@code listener} is {@code null} or not present
- * in the list of registered listeners, no exception is thrown and no action is performed.
- *
- * @param listener the media details listener to remove
- */
- public void removeMediaDetailsListener(MediaDetailsListener listener) {
- if (listener != null) {
- detailsListeners.remove(listener);
- }
- }
-
- /**
- * Get the set of media details listeners that are currently registered.
- *
- * @return the currently registered details listeners
- */
- public Set getMediaDetailsListeners() {
- // Make a copy so callers get an immutable snapshot of the current state.
- return Collections.unmodifiableSet(new HashSet(detailsListeners));
- }
-
- /**
- * Send a media details response to all registered listeners.
- *
- * @param details the response that has just arrived
- */
- private void deliverMediaDetailsUpdate(final MediaDetails details) {
- for (MediaDetailsListener listener : getMediaDetailsListeners()) {
- try {
- listener.detailsAvailable(details);
- } catch (Throwable t) {
- logger.warn("Problem delivering media details response to listener", t);
- }
- }
- }
-
/**
* @return true if we found DJ Link devices and were able to create the {@code VirtualRekordbox}.
* @throws Exception if there is a problem opening a socket on the right network
@@ -780,7 +715,6 @@ private boolean createVirtualRekordbox() throws Exception {
// Forward Updates to VirtualCdj. That's where all clients are used to getting them.
addUpdateListener(VirtualCdj.getInstance().getUpdateListener());
- addMediaDetailsListener(VirtualCdj.getInstance().getMediaDetailsListener());
// Find the network interface and address to use to communicate with the first device we found.
matchingInterfaces = new ArrayList();
From 7614de8ed08cc636b1c4c0c7800da2994274cd34 Mon Sep 17 00:00:00 2001
From: Christopher Prepos
Date: Sat, 4 May 2024 20:37:35 -0400
Subject: [PATCH 15/16] dont need to grab filesystem
---
.../deepsymmetry/beatlink/MediaDetails.java | 33 +++++++++----------
1 file changed, 15 insertions(+), 18 deletions(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java b/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
index b7595c06..9ba3ee85 100644
--- a/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
+++ b/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
@@ -8,6 +8,7 @@
import org.slf4j.LoggerFactory;
import java.awt.*;
+import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.nio.ByteBuffer;
@@ -103,20 +104,19 @@ public ByteBuffer getRawBytes() {
/**
* Given a source byte array, return the UTF-16 string length in bytes by finding a UTF-16 NUL sequence.
* If a UTF-16 NUL sequence is not found, maxLength or source.length-offset is returned, which ever is smaller.
- *
- * @param source the source byte array representing a UTF-16 string
- * @param offset the byte offset to start at in the source byte array
+ *
+ * @param source the source byte array representing a UTF-16 string
+ * @param offset the byte offset to start at in the source byte array
* @param maxLength the maximum length of the UTF-16 string (in bytes)
- *
* @return the length in number of bytes excluding the UTF-16 NUL sequence, else maxLength or source.length-offset, which ever is smaller.
*/
private int getUTF16StringLength(byte[] source, int offset, int maxLength) {
int numBytes = maxLength;
- int clampedMaxLength = Math.min(maxLength, source.length-offset);
-
- for (int i=offset; i
Date: Sat, 4 May 2024 20:46:59 -0400
Subject: [PATCH 16/16] remove comment for nothing
---
src/main/java/org/deepsymmetry/beatlink/MediaDetails.java | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java b/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
index 9ba3ee85..ba51ea0e 100644
--- a/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
+++ b/src/main/java/org/deepsymmetry/beatlink/MediaDetails.java
@@ -124,13 +124,7 @@ private int getUTF16StringLength(byte[] source, int offset, int maxLength) {
}
/**
- * Constructor sets all the immutable interpreted fields based on constructor inputs
- *
- * @param packet the media response packet that was received
- */
-
- /**
- * Custom constructor to emulate an actual MediaDetails object from CDJs. This allows our Status packets
+ * Constructor to emulate an actual MediaDetails object from CDJs. This allows our Status packets
* to use OpusProvider to enrich the track data. OpusProvider must be started up to use this method.
*
* @param slotReference Slot Reference to Emulate