public class VirtualCdj extends LifecycleParticipant
BeatFinder reports, allowing you to keep track of which player is the tempo master, how many beats of
a track have been played, how close a player is getting to its next cue point, and more. It is also the foundation
for finding out the rekordbox ID of the loaded track, which supports all the features associated with the
MetadataFinder.| Modifier and Type | Field and Description |
|---|---|
static int |
DEVICE_NAME_LENGTH
The length of the device name in the announcement packet.
|
static int |
DEVICE_NAME_OFFSET
The location of the device name in the announcement packet.
|
static int |
DEVICE_NUMBER_OFFSET
The location of the device number in the announcement packet.
|
int |
MAX_BEAT
The longest beat we will report playing; if we are still playing and reach this beat, we will loop back to beat
one.
|
static int |
UPDATE_PORT
The port to which other devices will send status update messages.
|
| Modifier and Type | Method and Description |
|---|---|
void |
addMasterListener(MasterListener listener)
Adds the specified master listener to receive device updates when there are changes related
to the tempo master.
|
void |
addMediaDetailsListener(MediaDetailsListener listener)
Adds the specified media details listener to receive detail responses whenever they come in.
|
void |
addUpdateListener(DeviceUpdateListener listener)
Adds the specified device update listener to receive device updates whenever they come in.
|
void |
adjustPlaybackPosition(int ms)
Nudge the playback position by the specified number of milliseconds, to support synchronization with an
external clock.
|
void |
appointTempoMaster(DeviceUpdate target)
Tell a device to become tempo master.
|
void |
appointTempoMaster(int deviceNumber)
Tell a device to become tempo master.
|
void |
becomeTempoMaster()
Arrange to become the tempo master.
|
Set<DeviceAnnouncement> |
findUnreachablePlayers()
Checks if we can see any players that are on a different network than the one we chose for the Virtual CDJ.
|
int |
getAnnounceInterval()
Get the interval, in milliseconds, at which we broadcast presence announcements on the network to pose as
a virtual CDJ.
|
InetAddress |
getBroadcastAddress()
Return the broadcast address used to reach the DJ Link network.
|
static String |
getDeviceName()
Get the name to be used in announcing our presence on the network.
|
byte |
getDeviceNumber()
Get the device number that is used when sending presence announcements on the network to pose as a virtual CDJ.
|
static VirtualCdj |
getInstance()
Get the singleton instance of this class.
|
Set<DeviceUpdate> |
getLatestStatus()
Get the most recent status we have seen from all devices that are recent enough to be considered still
active on the network.
|
DeviceUpdate |
getLatestStatusFor(DeviceAnnouncement device)
Look up the most recent status we have seen for a device, given its device announcement packet as returned
by
DeviceFinder.getCurrentDevices(). |
DeviceUpdate |
getLatestStatusFor(DeviceUpdate device)
Look up the most recent status we have seen for a device, given another update from it, which might be a
beat packet containing far less information.
|
DeviceUpdate |
getLatestStatusFor(int deviceNumber)
Look up the most recent status we have seen for a device from a device identifying itself
with the specified device number, if any.
|
InetAddress |
getLocalAddress()
Return the address being used by the virtual CDJ to send its own presence announcement broadcasts.
|
Set<MasterListener> |
getMasterListeners()
Get the set of master listeners that are currently registered.
|
double |
getMasterTempo()
Get the current master tempo.
|
List<NetworkInterface> |
getMatchingInterfaces()
Check the interfaces that match the address from which we are receiving DJ Link traffic.
|
Set<MediaDetailsListener> |
getMediaDetailsListeners()
Get the set of media details listeners that are currently registered.
|
org.deepsymmetry.electro.Snapshot |
getPlaybackPosition()
Find details about the current simulated playback position.
|
int |
getStatusInterval()
Check how often we will send status packets, if we are configured to send them.
|
double |
getTempo()
Check the tempo at which we report ourselves to be playing.
|
double |
getTempoEpsilon()
Find out how large a tempo change is required before we consider it to be a real difference.
|
DeviceUpdate |
getTempoMaster()
Check which device is the current tempo master, returning the
DeviceUpdate packet in which it
reported itself to be master. |
Set<DeviceUpdateListener> |
getUpdateListeners()
Get the set of device update listeners that are currently registered.
|
boolean |
getUseStandardPlayerNumber()
When self-assigning a player number, should we try to use a value that is legal for a standard CDJ, in
the range 1 to 4? By default, we do not, to avoid any potential conflict with real players.
|
boolean |
isOnAir()
Checks whether we believe our channel is currently on the air (audible in the mixer output).
|
boolean |
isPlaying()
Check whether we are pretending to be playing.
|
boolean |
isRunning()
Check whether we are presently posing as a virtual CDJ and receiving device status updates.
|
boolean |
isSendingStatus()
Check whether we are currently sending status packets.
|
boolean |
isSynced()
Check whether we are currently staying in sync with the tempo master.
|
boolean |
isTempoMaster()
Check whether we are currently in charge of the tempo and beat alignment.
|
void |
jumpToBeat(int beat)
Moves our current playback position to the specified beat; this will be reflected in any status and beat packets
that we are sending.
|
void |
removeMasterListener(MasterListener listener)
Removes the specified master listener so that it no longer receives device updates when
there are changes related to the tempo master.
|
void |
removeMediaDetailsListener(MediaDetailsListener listener)
Removes the specified media details listener so it no longer receives detail responses when they come in.
|
void |
removeUpdateListener(DeviceUpdateListener listener)
Removes the specified device update listener so it no longer receives device updates when they come in.
|
long |
sendBeat()
Sends a beat packet.
|
long |
sendBeat(org.deepsymmetry.electro.Snapshot snapshot)
Sends a beat packet.
|
void |
sendFaderStartCommand(Set<Integer> deviceNumbersToStart,
Set<Integer> deviceNumbersToStop)
Broadcast a packet that tells some players to start playing and others to stop.
|
void |
sendLoadTrackCommand(DeviceUpdate target,
int rekordboxId,
int sourcePlayer,
CdjStatus.TrackSourceSlot sourceSlot,
CdjStatus.TrackType sourceType)
Send a packet to the target device telling it to load the specified track from the specified source player.
|
void |
sendLoadTrackCommand(int targetPlayer,
int rekordboxId,
int sourcePlayer,
CdjStatus.TrackSourceSlot sourceSlot,
CdjStatus.TrackType sourceType)
Send a packet to the target player telling it to load the specified track from the specified source player.
|
void |
sendMediaQuery(SlotReference slot)
Ask a device for information about the media mounted in a particular slot.
|
void |
sendOnAirCommand(Set<Integer> deviceNumbersOnAir)
Broadcast a packet that tells the players which channels are on the air (audible in the mixer output).
|
void |
sendSyncModeCommand(DeviceUpdate target,
boolean synced)
Tell a device to turn sync on or off.
|
void |
sendSyncModeCommand(int deviceNumber,
boolean synced)
Tell a device to turn sync on or off.
|
void |
setAnnounceInterval(int interval)
Set the interval, in milliseconds, at which we broadcast presence announcements on the network to pose as
a virtual CDJ.
|
void |
setDeviceName(String name)
Set the name to be used in announcing our presence on the network.
|
void |
setDeviceNumber(byte number)
Set the device number to be used when sending presence announcements on the network to pose as a virtual CDJ.
|
void |
setOnAir(boolean audible)
Change whether we believe our channel is currently on the air (audible in the mixer output).
|
void |
setPlaying(boolean playing)
Controls whether we report that we are playing.
|
void |
setSendingStatus(boolean send)
Control whether the Virtual CDJ sends status packets to the other players.
|
void |
setStatusInterval(int interval)
Change the interval at which we will send status packets, if we are configured to send them.
|
void |
setSynced(boolean sync)
Controls whether we are currently staying in sync with the tempo master.
|
void |
setTempo(double bpm)
Controls the tempo at which we report ourselves to be playing.
|
void |
setTempoEpsilon(double epsilon)
Set how large a tempo change is required before we consider it to be a real difference.
|
void |
setUseStandardPlayerNumber(boolean attempt)
When self-assigning a player number, should we try to use a value that is legal for a standard CDJ, in
the range 1 to 4? By default, we do not, to avoid any potential conflict with real players.
|
boolean |
start()
Start announcing ourselves and listening for status packets.
|
void |
stop()
Stop announcing ourselves and listening for status updates.
|
String |
toString() |
addLifecycleListener, deliverLifecycleAnnouncement, ensureRunning, getLifecycleListeners, removeLifecycleListenerpublic static final int UPDATE_PORT
public static final int DEVICE_NAME_OFFSET
public static final int DEVICE_NAME_LENGTH
public static final int DEVICE_NUMBER_OFFSET
public final int MAX_BEAT
public boolean isRunning()
isRunning in class LifecycleParticipantpublic InetAddress getLocalAddress()
IllegalStateException - if the VirtualCdj is not activepublic InetAddress getBroadcastAddress()
IllegalStateException - if the VirtualCdj is not activepublic void setUseStandardPlayerNumber(boolean attempt)
MetadataFinder, and will always have fewer than four real players
on the network, this can be set to true, and a device number in this range will be chosen if it
is not in use on the network during startup.attempt - true if self-assignment should try to use device numbers below 5 when availablepublic boolean getUseStandardPlayerNumber()
MetadataFinder, and will always have fewer than four real players
on the network, this can be set to true, and a device number in this range will be chosen if it
is not in use on the network during startup.public byte getDeviceNumber()
VirtualCdj
should assign itself an unused device number by watching the network when you call
start(). If getUseStandardPlayerNumber() returns true, self-assignment will try to
find a value in the range 1 to 4. Otherwise (or if those values are all used by other players), it will try to
find a value in the range 5 to 15.public void setDeviceNumber(byte number)
Set the device number to be used when sending presence announcements on the network to pose as a virtual CDJ.
If this is set to zero before start() is called, the VirtualCdj will watch the network to
look for an unused device number, and assign itself that number during startup. If you
explicitly assign a non-zero value, it will use that device number instead. Setting the value to zero while
already up and running reassigns it to an unused value immediately. If getUseStandardPlayerNumber()
returns true, self-assignment will try to find a value in the range 1 to 4. Otherwise (or if those
values are all used by other players), it will try to find a value in the range 5 to 15.
The device number defaults to 0, enabling self-assignment, and will be reset to that each time the
VirtualCdj is stopped.
number - the virtual player numberIllegalStateException - if we are currently sending status updatespublic int getAnnounceInterval()
public void setAnnounceInterval(int interval)
interval - the announcement intervalIllegalArgumentException - if interval is not between 200 and 2000public static String getDeviceName()
public void setDeviceName(String name)
name - the device name to report in our presence announcement packets.public DeviceUpdate getTempoMaster()
DeviceUpdate packet in which it
reported itself to be master. If there is no current tempo master returns null. Note that when
we are acting as tempo master ourselves in order to control player tempo and beat alignment, this will
also have a null value, as there is no real player that is acting as master; we will instead
send tempo and beat updates ourselves.IllegalStateException - if the VirtualCdj is not activepublic double getTempoEpsilon()
public void setTempoEpsilon(double epsilon)
epsilon - the BPM fraction that will trigger a tempo change updatepublic double getMasterTempo()
IllegalStateException - if the VirtualCdj is not activepublic List<NetworkInterface> getMatchingInterfaces()
IllegalStateException - if we are not runningpublic Set<DeviceAnnouncement> findUnreachablePlayers()
IllegalStateException - if we are not runningpublic boolean start()
throws SocketException
DeviceFinder to be active in order to find out how to communicate with other devices, so will start
that if it is not already.VirtualCdj, or it was already running.SocketException - if the socket to listen on port 50002 cannot be createdpublic void stop()
public Set<DeviceUpdate> getLatestStatus()
IllegalStateException - if the VirtualCdj is not activepublic DeviceUpdate getLatestStatusFor(DeviceUpdate device)
Note: If you are trying to determine the current tempo or beat being played by the device, you should
either use the status you just received, or
TimeFinder.getLatestUpdateFor(int) instead, because that
combines both status updates and beat messages, and so is more likely to be current and definitive.
device - the update identifying the device for which current status information is desiredIllegalStateException - if the VirtualCdj is not activepublic DeviceUpdate getLatestStatusFor(DeviceAnnouncement device)
DeviceFinder.getCurrentDevices().
Note: If you are trying to determine the current tempo or beat being played by the device, you should
use TimeFinder.getLatestUpdateFor(int) instead, because that
combines both status updates and beat messages, and so is more likely to be current and definitive.
device - the announcement identifying the device for which current status information is desiredIllegalStateException - if the VirtualCdj is not activepublic DeviceUpdate getLatestStatusFor(int deviceNumber)
Note: If you are trying to determine the current tempo or beat being played by the device, you should
use TimeFinder.getLatestUpdateFor(int) instead, because that
combines both status updates and beat messages, and so is more likely to be current and definitive.
deviceNumber - the device number of interestIllegalStateException - if the VirtualCdj is not activepublic void addMasterListener(MasterListener listener)
Adds the specified master listener to receive device updates when there are changes related
to the tempo master. If listener is null or already present in the set
of registered listeners, no exception is thrown and no action is performed.
To reduce latency, tempo master 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 master updates will back up. If you want to perform lengthy processing of any sort, do so on another thread.
listener - the master listener to addpublic void removeMasterListener(MasterListener listener)
listener is null or not present
in the set of registered listeners, no exception is thrown and no action is performed.listener - the master listener to removepublic Set<MasterListener> getMasterListeners()
public void addUpdateListener(DeviceUpdateListener listener)
Adds the specified device update listener to receive device updates whenever they come in.
If listener is 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 device updates will back up. If you want to perform lengthy processing of any sort, do so on another thread.
listener - the device update listener to addpublic void removeUpdateListener(DeviceUpdateListener listener)
listener is null or not present
in the list of registered listeners, no exception is thrown and no action is performed.listener - the device update listener to removepublic Set<DeviceUpdateListener> getUpdateListeners()
public void addMediaDetailsListener(MediaDetailsListener listener)
Adds the specified media details listener to receive detail responses whenever they come in.
If listener is 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.
listener - the media details listener to addpublic void removeMediaDetailsListener(MediaDetailsListener listener)
listener is null or not present
in the list of registered listeners, no exception is thrown and no action is performed.listener - the media details listener to removepublic Set<MediaDetailsListener> getMediaDetailsListeners()
public void sendMediaQuery(SlotReference slot) throws IOException
MetadataFinder when a response is received.slot - the slot holding media we want to know about.IOException - if there is a problem sending the request.public void sendSyncModeCommand(int deviceNumber,
boolean synced)
throws IOException
deviceNumber - the device whose sync state is to be setsynced - true if sync should be turned on, else it will be turned offIOException - if there is a problem sending the command to the deviceIllegalStateException - if the VirtualCdj is not activeIllegalArgumentException - if deviceNumber is not found on the networkpublic void sendSyncModeCommand(DeviceUpdate target, boolean synced) throws IOException
target - an update from the device whose sync state is to be setsynced - true if sync should be turned on, else it will be turned offIOException - if there is a problem sending the command to the deviceIllegalStateException - if the VirtualCdj is not activeNullPointerException - if update is nullpublic void appointTempoMaster(int deviceNumber)
throws IOException
deviceNumber - the device we want to take over the role of tempo masterIOException - if there is a problem sending the command to the deviceIllegalStateException - if the VirtualCdj is not activeIllegalArgumentException - if deviceNumber is not found on the networkpublic void appointTempoMaster(DeviceUpdate target) throws IOException
target - an update from the device that we want to take over the role of tempo masterIOException - if there is a problem sending the command to the deviceIllegalStateException - if the VirtualCdj is not activeNullPointerException - if update is nullpublic void sendFaderStartCommand(Set<Integer> deviceNumbersToStart, Set<Integer> deviceNumbersToStop) throws IOException
deviceNumbersToStart - the players that should start playing if they aren't alreadydeviceNumbersToStop - the players that should stop playingIOException - if there is a problem broadcasting the command to the playersIllegalStateException - if the VirtualCdj is not activepublic void sendOnAirCommand(Set<Integer> deviceNumbersOnAir) throws IOException
deviceNumbersOnAir - the players whose channels are currently on the airIOException - if there is a problem broadcasting the command to the playersIllegalStateException - if the VirtualCdj is not activepublic void sendLoadTrackCommand(int targetPlayer,
int rekordboxId,
int sourcePlayer,
CdjStatus.TrackSourceSlot sourceSlot,
CdjStatus.TrackType sourceType)
throws IOException
targetPlayer - the device number of the player that you want to have load a trackrekordboxId - the identifier of a track within the source player's rekordbox databasesourcePlayer - the device number of the player from which the track should be loadedsourceSlot - the media slot from which the track should be loadedsourceType - the type of track to be loadedIOException - if there is a problem sending the commandIllegalStateException - if the VirtualCdj is not active or the target device cannot be foundpublic void sendLoadTrackCommand(DeviceUpdate target, int rekordboxId, int sourcePlayer, CdjStatus.TrackSourceSlot sourceSlot, CdjStatus.TrackType sourceType) throws IOException
target - an update from the player that you want to have load a trackrekordboxId - the identifier of a track within the source player's rekordbox databasesourcePlayer - the device number of the player from which the track should be loadedsourceSlot - the media slot from which the track should be loadedsourceType - the type of track to be loadedIOException - if there is a problem sending the commandIllegalStateException - if the VirtualCdj is not activepublic int getStatusInterval()
public void setStatusInterval(int interval)
interval - the millisecond interval that will pass between each status packet we sendIllegalArgumentException - if interval is less than 20 or more than 2000public long sendBeat()
BeatSender has determined that it is
time to do so, but it is public to allow experimentation.public long sendBeat(org.deepsymmetry.electro.Snapshot snapshot)
BeatSender has determined that it is
time to do so, but it is public to allow experimentation.snapshot - the time at which the beat to be sent occurred, for computation of its beat numberpublic void setSendingStatus(boolean send)
throws IOException
send - if true we will send status packets, and can participate in (and control) tempo and beat syncIllegalStateException - if the virtual CDJ is not running, or if it is not using a device number in the
range 1 through 4IOException - if there is a problem starting the BeatFinderpublic boolean isSendingStatus()
true if we are sending status packets, and can participate in (and control) tempo and beat syncpublic void setPlaying(boolean playing)
playing - true if we should seem to be playingpublic boolean isPlaying()
true if we are reporting active playbackpublic org.deepsymmetry.electro.Snapshot getPlaybackPosition()
public void adjustPlaybackPosition(int ms)
Nudge the playback position by the specified number of milliseconds, to support synchronization with an external clock. Positive values move playback forward in time, while negative values jump back. If we are sending beat packets, notify the beat sender that the timeline has changed.
If the shift would put us back before beat one, we will jump forward a bar to correct that. It is thus not safe to jump backwards more than a bar's worth of time.
ms - the number of millisecond to shift the simulated playback positionpublic void becomeTempoMaster()
throws IOException
IllegalStateException - if we are not sending status updatesIOException - if there is a problem sending the master yield requestpublic boolean isTempoMaster()
true if we hold the tempo master rolepublic void setSynced(boolean sync)
sync - if true, our status packets will be tempo and beat aligned with the tempo masterpublic boolean isSynced()
true if our status packets will be tempo and beat aligned with the tempo masterpublic void setOnAir(boolean audible)
audible - true if we should report ourselves as being on the air in our status packetspublic boolean isOnAir()
true if we should report ourselves as being on the air in our status packetspublic void setTempo(double bpm)
isSynced() is true and we are not the tempo master, any value set by this method will
overridden by the the next tempo master change.bpm - the tempo, in beats per minute, that we should report in our status and beat packetspublic double getTempo()
public void jumpToBeat(int beat)
beat - the beat that we should pretend to be playingpublic static VirtualCdj getInstance()
Copyright © 2016–2019 Deep Symmetry, LLC. All rights reserved.