org.deepsymmetry.beatlink.DeviceUpdate Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of beat-link Show documentation
Show all versions of beat-link Show documentation
A Java library for communicating with Pioneer DJ Link equipment.
package org.deepsymmetry.beatlink;
import java.net.DatagramPacket;
import java.net.InetAddress;
/**
* Represents a device status update seen on a DJ Link network.
*
* @author James Elliott
*/
public abstract class DeviceUpdate {
/**
* The address from which this device update was received.
*/
final InetAddress address;
/**
* When this update was received.
*/
@SuppressWarnings("WeakerAccess")
final long timestamp;
/**
* The name of the device sending the update.
*/
final String deviceName;
/**
* The player/device number sending the update.
*/
final int deviceNumber;
/**
* The packet data containing the device update.
*/
final byte[] packetBytes;
/**
* Constructor sets all the immutable interpreted fields based on the packet content.
*
* @param packet the device update packet that was received
* @param name the type of packet that is being processed, in case a problem needs to be reported
* @param length the expected length of the packet
*/
@SuppressWarnings("WeakerAccess")
public DeviceUpdate(DatagramPacket packet, String name, int length) {
timestamp = System.nanoTime();
if (packet.getLength() != length) {
throw new IllegalArgumentException(name + " packet must be " + length + " bytes long");
}
address = packet.getAddress();
packetBytes = new byte[packet.getLength()];
System.arraycopy(packet.getData(), 0, packetBytes, 0, packet.getLength());
deviceName = new String(packetBytes, 11, 20).trim();
deviceNumber = Util.unsign(packetBytes[33]);
}
/**
* Get the address of the device from which this update was seen.
*
* @return the network address from which the update was sent
*/
public InetAddress getAddress() {
return address;
}
/**
* Get the timestamp recording when the device update was received.
*
* @return the nanosecond timestamp at which we received this update
*/
public long getTimestamp() {
return timestamp;
}
/**
* Get the name reported by the device sending the update.
*
* @return the device name
*/
public String getDeviceName() {
return deviceName;
}
/**
* Get the player/device number reporting the update.
*
* @return the player number found in the update packet
*/
public int getDeviceNumber() {
return deviceNumber;
}
/**
* Get the raw data bytes of the device update packet.
*
* @return the data sent by the device to update its status
*/
public byte[] getPacketBytes() {
byte[] result = new byte[packetBytes.length];
System.arraycopy(packetBytes, 0, result, 0, packetBytes.length);
return result;
}
/**
* Get the device pitch at the time of the update. This is an integer ranging from 0 to 2097152, which corresponds
* to a range between completely stopping playback to playing at twice normal tempo. The equivalent percentage
* value can be obtained by passing the pitch to {@link Util#pitchToPercentage(long)}, and the corresponding
* fractional scaling value by passing it to {@link Util#pitchToMultiplier(long)}. Mixers always report a pitch
* of +0%, so tempo changes are purely reflected in the BPM value.
*
* @return the raw effective device pitch at the time of the update
*/
public abstract int getPitch();
/**
* Get the playback BPM at the time of the update. This is an integer representing the BPM times 100, so a track
* running at 120.5 BPM would be represented by the value 12050. Mixers always report a pitch of +0%, so tempo
* changes are purely reflected in the BPM value.
*
* When the CDJ has just started up and no track has been loaded, it will report a BPM of 65535.
*
* @return the track BPM to two decimal places multiplied by 100
*/
public abstract int getBpm();
/**
* Is this device reporting itself to be the current tempo master?
*
* @return {@code true} if the device that sent this update is the master
* @throws IllegalStateException if called with a {@link Beat} and the {@link VirtualCdj} is not running, since
* that is needed to find the latest status update from the device which sent the beat packet.
*/
public abstract boolean isTempoMaster();
/**
* Is this device reporting itself synced to the current tempo master?
*
* @return {@code true} if the device that sent this update is synced
* @throws IllegalStateException if called with a {@link Beat} and the {@link VirtualCdj} is not running, since
* that is needed to find the latest status update from the device which sent the beat packet.
*/
public abstract boolean isSynced();
/**
* If this packet indicates the device in the process of yielding the tempo master role to another player,
* this will hold the device number of that player, otherwise it will be {@code null}.
*
* @return the device number, if any, this update is yielding the tempo master role to
*/
public abstract Integer getDeviceMasterIsBeingYieldedTo();
/**
* Get the effective tempo reflected by this update, which reflects both its track BPM and pitch as needed.
*
* @return the beats per minute this device is reporting
*/
public abstract double getEffectiveTempo();
/**
* Get the position within a measure of music at which the most recent beat fell (a value from 1 to 4, where 1 represents
* the down beat). This value will be accurate for players when the track was properly configured within rekordbox
* (and if the music follows a standard House 4/4 time signature). The mixer makes no effort to synchronize
* down beats with players, however, so this value is meaningless when coming from the mixer. The usefulness of
* this value can be checked with {@link #isBeatWithinBarMeaningful()}.
*
* @return the beat number within the current measure of music
*/
public abstract int getBeatWithinBar();
/**
* Returns {@code true} if this update is coming from a device where {@link #getBeatWithinBar()} can reasonably
* be expected to have musical significance, because it respects the way a track was configured within rekordbox.
*
* @return true for status packets from players, false for status packets from mixers
*/
@SuppressWarnings("WeakerAccess")
public abstract boolean isBeatWithinBarMeaningful();
@Override
public String toString() {
return "DeviceUpdate[deviceNumber:" + deviceNumber +
", deviceName:" + deviceName + ", address:" + address.getHostAddress() +
", timestamp:" + timestamp + ", beatWithinBar:" + getBeatWithinBar() +
", isBeatWithinBarMeaningful: " + isBeatWithinBarMeaningful() + ", effectiveTempo:" +
getEffectiveTempo() + ", isTempoMaster:" + isTempoMaster();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy