Class VirtualCdj
- java.lang.Object
-
- org.deepsymmetry.beatlink.LifecycleParticipant
-
- org.deepsymmetry.beatlink.VirtualCdj
-
public class VirtualCdj extends LifecycleParticipant
Provides the ability to create a virtual CDJ device that can lurk on a DJ Link network and receive packets sent to players, monitoring the detailed state of the other devices. This detailed information is helpful for augmenting whatBeatFinder
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 theMetadataFinder
.- Author:
- James Elliott
-
-
Field Summary
Fields Modifier and Type Field 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.static int
MAC_ADDRESS_OFFSET
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.
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method 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(int deviceNumber)
Tell a device to become tempo master.void
appointTempoMaster(DeviceUpdate target)
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(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.DeviceUpdate
getLatestStatusFor(DeviceAnnouncement device)
Look up the most recent status we have seen for a device, given its device announcement packet as returned byDeviceFinder.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.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.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 theDeviceUpdate
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(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
sendLoadSettingsCommand(int targetPlayer, PlayerSettings settings)
Send a packet to the target player telling it to apply the supplied settings.void
sendLoadSettingsCommand(DeviceUpdate target, PlayerSettings settings)
Send a packet to the target device telling it to apply the supplied settings.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
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
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
sendOnAirExtendedCommand(Set<Integer> deviceNumbersOnAir)
Broadcast a packet that tells the players which channels are on the air (audible in the mixer output).void
sendSyncModeCommand(int deviceNumber, boolean synced)
Tell a device to turn sync on or off.void
sendSyncModeCommand(DeviceUpdate target, 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.boolean
start(byte deviceNumber)
Start announcing ourselves as a specific device number (if we can claim it) and listening for status packets.void
stop()
Stop announcing ourselves and listening for status updates.String
toString()
-
Methods inherited from class org.deepsymmetry.beatlink.LifecycleParticipant
addLifecycleListener, deliverLifecycleAnnouncement, ensureRunning, getLifecycleListeners, removeLifecycleListener
-
-
-
-
Field Detail
-
UPDATE_PORT
public static final int UPDATE_PORT
The port to which other devices will send status update messages.- See Also:
- Constant Field Values
-
MAC_ADDRESS_OFFSET
public static final int MAC_ADDRESS_OFFSET
- See Also:
- Constant Field Values
-
DEVICE_NAME_OFFSET
public static final int DEVICE_NAME_OFFSET
The location of the device name in the announcement packet.- See Also:
- Constant Field Values
-
DEVICE_NAME_LENGTH
public static final int DEVICE_NAME_LENGTH
The length of the device name in the announcement packet.- See Also:
- Constant Field Values
-
DEVICE_NUMBER_OFFSET
public static final int DEVICE_NUMBER_OFFSET
The location of the device number in the announcement packet.- See Also:
- Constant Field Values
-
MAX_BEAT
public final 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. If we are told to jump to a larger beat than this, we map it back into the range we will play. This would be a little over nine hours at 120 bpm, which seems long enough for any track.- See Also:
- Constant Field Values
-
-
Method Detail
-
isRunning
public boolean isRunning()
Check whether we are presently posing as a virtual CDJ and receiving device status updates.- Specified by:
isRunning
in classLifecycleParticipant
- Returns:
- true if our socket is open, sending presence announcements, and receiving status packets
-
getLocalAddress
public InetAddress getLocalAddress()
Return the address being used by the virtual CDJ to send its own presence announcement broadcasts.- Returns:
- the local address we present to the DJ Link network
- Throws:
IllegalStateException
- if theVirtualCdj
is not active
-
getBroadcastAddress
public InetAddress getBroadcastAddress()
Return the broadcast address used to reach the DJ Link network.- Returns:
- the address on which packets can be broadcast to the other DJ Link devices
- Throws:
IllegalStateException
- if theVirtualCdj
is not active
-
setUseStandardPlayerNumber
public 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. However, if the user is intending to use features (like becoming tempo master) which require Beat Link to operate with a device number in the standard range, and will always have fewer than four real players on the network, this can be set totrue
, and a device number in this range will be chosen if it is not in use on the network during startup.- Parameters:
attempt
- true if self-assignment should try to use device numbers below 5 when available
-
getUseStandardPlayerNumber
public 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. However, if the user is intending to use features (like becoming tempo master) which require Beat Link to operate with a device number in the standard range, and will always have fewer than four real players on the network, this can be set totrue
, and a device number in this range will be chosen if it is not in use on the network during startup.- Returns:
- true if self-assignment should try to use device numbers below 5 when available
-
getDeviceNumber
public byte getDeviceNumber()
Get the device number that is used when sending presence announcements on the network to pose as a virtual CDJ. This starts out being zero unless you explicitly assign another value, which means that theVirtualCdj
should assign itself an unused device number by watching the network when you callstart()
. IfgetUseStandardPlayerNumber()
returnstrue
, self-assignment will try to find a value in the range 1 to 4. Otherwise it will try to find a value in the range 7 to 15 (so that it does not even conflict with newer CDJ-3000 networks, which can use channels 5 and 6). Even when told to use a standard player number, if all device numbers in that range are already in use, we will be forced to use a larger number.- Returns:
- the virtual player number
-
setDeviceNumber
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. Used during the startup process; cannot be set while running. If set to zero, will attempt to claim any free device number, otherwise will try to claim the number specified. If the mixer tells us that we are plugged into a channel-specific Ethernet port, we will honor that and use the device number specified by the mixer.
If
getUseStandardPlayerNumber()
returnstrue
, 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 7 to 15 (so that it does not even conflict with newer CDJ-3000 networks, which can use channels 5 and 6).The device number defaults to 0, enabling self-assignment, and will be reset to that each time the
VirtualCdj
is stopped.- Parameters:
number
- the virtual player number- Throws:
IllegalStateException
- if we are currently running
-
getAnnounceInterval
public int getAnnounceInterval()
Get the interval, in milliseconds, at which we broadcast presence announcements on the network to pose as a virtual CDJ.- Returns:
- the announcement interval
-
setAnnounceInterval
public void setAnnounceInterval(int interval)
Set the interval, in milliseconds, at which we broadcast presence announcements on the network to pose as a virtual CDJ.- Parameters:
interval
- the announcement interval- Throws:
IllegalArgumentException
- if interval is not between 200 and 2000
-
getDeviceName
public static String getDeviceName()
Get the name to be used in announcing our presence on the network.- Returns:
- the device name reported in our presence announcement packets
-
setDeviceName
public void setDeviceName(String name)
Set the name to be used in announcing our presence on the network. The name can be no longer than twenty bytes, and should be normal ASCII, no Unicode.- Parameters:
name
- the device name to report in our presence announcement packets.
-
getTempoMaster
public DeviceUpdate getTempoMaster()
Check which device is the current tempo master, returning theDeviceUpdate
packet in which it reported itself to be master. If there is no current tempo master returnsnull
. Note that when we are acting as tempo master ourselves in order to control player tempo and beat alignment, this will also have anull
value, as there is no real player that is acting as master; we will instead send tempo and beat updates ourselves.- Returns:
- the most recent update from a device which reported itself as the master
- Throws:
IllegalStateException
- if theVirtualCdj
is not active
-
getTempoEpsilon
public double getTempoEpsilon()
Find out how large a tempo change is required before we consider it to be a real difference.- Returns:
- the BPM fraction that will trigger a tempo change update
-
setTempoEpsilon
public void setTempoEpsilon(double epsilon)
Set how large a tempo change is required before we consider it to be a real difference.- Parameters:
epsilon
- the BPM fraction that will trigger a tempo change update
-
getMasterTempo
public double getMasterTempo()
Get the current master tempo.- Returns:
- the most recently reported master tempo
- Throws:
IllegalStateException
- if theVirtualCdj
is not active
-
getMatchingInterfaces
public List<NetworkInterface> getMatchingInterfaces()
Check the interfaces that match the address from which we are receiving DJ Link traffic. If there is more than one value in this list, that is a problem because we will likely receive duplicate packets that will play havoc with our understanding of player states.- Returns:
- the list of network interfaces on which we might receive player packets
- Throws:
IllegalStateException
- if we are not running
-
findUnreachablePlayers
public 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. If so, we are not going to be able to communicate with them, and they should all be moved onto a single network.- Returns:
- the device announcements of any players which are on unreachable networks, or hopefully an empty list
- Throws:
IllegalStateException
- if we are not running
-
start
public boolean start() throws SocketException
Start announcing ourselves and listening for status packets. If already active, has no effect. Requires theDeviceFinder
to be active in order to find out how to communicate with other devices, so will start that if it is not already.- Returns:
- true if we found DJ Link devices and were able to create the
VirtualCdj
, or it was already running. - Throws:
SocketException
- if the socket to listen on port 50002 cannot be created
-
start
public boolean start(byte deviceNumber) throws SocketException
Start announcing ourselves as a specific device number (if we can claim it) and listening for status packets. If already active, has no effect. Requires the
DeviceFinder
to be active in order to find out how to communicate with other devices, so will start that if it is not already.This version is shorthand for calling
setDeviceNumber(byte)
with the specified device number and then immediately callingstart()
, but avoids the race condition which can occur if startup is already in progress, which would lead to anIllegalStateException
. This is not uncommon when startup is being driven by receipt of device announcement packets.- Parameters:
deviceNumber
- the device number to try to claim- Returns:
- true if we found DJ Link devices and were able to create the
VirtualCdj
, or it was already running. - Throws:
SocketException
- if the socket to listen on port 50002 cannot be created
-
stop
public void stop()
Stop announcing ourselves and listening for status updates.
-
getLatestStatus
public 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.- Returns:
- the most recent detailed status update received for all active devices
- Throws:
IllegalStateException
- if theVirtualCdj
is not active
-
getLatestStatusFor
public 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.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.- Parameters:
device
- the update identifying the device for which current status information is desired- Returns:
- the most recent detailed status update received for that device
- Throws:
IllegalStateException
- if theVirtualCdj
is not active
-
getLatestStatusFor
public DeviceUpdate getLatestStatusFor(DeviceAnnouncement device)
Look up the most recent status we have seen for a device, given its device announcement packet as returned byDeviceFinder.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.- Parameters:
device
- the announcement identifying the device for which current status information is desired- Returns:
- the most recent detailed status update received for that device
- Throws:
IllegalStateException
- if theVirtualCdj
is not active
-
getLatestStatusFor
public 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.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.- Parameters:
deviceNumber
- the device number of interest- Returns:
- the matching detailed status update or null if none have been received
- Throws:
IllegalStateException
- if theVirtualCdj
is not active
-
addMasterListener
public void addMasterListener(MasterListener listener)
Adds the specified master listener to receive device updates when there are changes related to the tempo master. If
listener
isnull
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.
- Parameters:
listener
- the master listener to add
-
removeMasterListener
public 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. Iflistener
isnull
or not present in the set of registered listeners, no exception is thrown and no action is performed.- Parameters:
listener
- the master listener to remove
-
getMasterListeners
public Set<MasterListener> getMasterListeners()
Get the set of master listeners that are currently registered.- Returns:
- the currently registered tempo master listeners
-
addUpdateListener
public void addUpdateListener(DeviceUpdateListener listener)
Adds the specified device update listener to receive device updates whenever they come in. If
listener
isnull
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.
- Parameters:
listener
- the device update listener to add
-
removeUpdateListener
public void removeUpdateListener(DeviceUpdateListener listener)
Removes the specified device update listener so it no longer receives device updates when they come in. Iflistener
isnull
or not present in the list of registered listeners, no exception is thrown and no action is performed.- Parameters:
listener
- the device update listener to remove
-
getUpdateListeners
public Set<DeviceUpdateListener> getUpdateListeners()
Get the set of device update listeners that are currently registered.- Returns:
- the currently registered update listeners
-
addMediaDetailsListener
public void addMediaDetailsListener(MediaDetailsListener listener)
Adds the specified media details listener to receive detail responses whenever they come in. If
listener
isnull
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.
- Parameters:
listener
- the media details listener to add
-
removeMediaDetailsListener
public void removeMediaDetailsListener(MediaDetailsListener listener)
Removes the specified media details listener so it no longer receives detail responses when they come in. Iflistener
isnull
or not present in the list of registered listeners, no exception is thrown and no action is performed.- Parameters:
listener
- the media details listener to remove
-
getMediaDetailsListeners
public Set<MediaDetailsListener> getMediaDetailsListeners()
Get the set of media details listeners that are currently registered.- Returns:
- the currently registered details listeners
-
sendMediaQuery
public void sendMediaQuery(SlotReference slot) throws IOException
Ask a device for information about the media mounted in a particular slot. Will update theMetadataFinder
when a response is received.- Parameters:
slot
- the slot holding media we want to know about.- Throws:
IOException
- if there is a problem sending the request.
-
sendSyncModeCommand
public void sendSyncModeCommand(int deviceNumber, boolean synced) throws IOException
Tell a device to turn sync on or off.- Parameters:
deviceNumber
- the device whose sync state is to be setsynced
- true if sync should be turned on, else it will be turned off- Throws:
IOException
- if there is a problem sending the command to the deviceIllegalStateException
- if theVirtualCdj
is not activeIllegalArgumentException
- ifdeviceNumber
is not found on the network
-
sendSyncModeCommand
public void sendSyncModeCommand(DeviceUpdate target, boolean synced) throws IOException
Tell a device to turn sync on or off.- Parameters:
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 off- Throws:
IOException
- if there is a problem sending the command to the deviceIllegalStateException
- if theVirtualCdj
is not activeNullPointerException
- ifupdate
isnull
-
appointTempoMaster
public void appointTempoMaster(int deviceNumber) throws IOException
Tell a device to become tempo master.- Parameters:
deviceNumber
- the device we want to take over the role of tempo master- Throws:
IOException
- if there is a problem sending the command to the deviceIllegalStateException
- if theVirtualCdj
is not activeIllegalArgumentException
- ifdeviceNumber
is not found on the network
-
appointTempoMaster
public void appointTempoMaster(DeviceUpdate target) throws IOException
Tell a device to become tempo master.- Parameters:
target
- an update from the device that we want to take over the role of tempo master- Throws:
IOException
- if there is a problem sending the command to the deviceIllegalStateException
- if theVirtualCdj
is not activeNullPointerException
- ifupdate
isnull
-
sendFaderStartCommand
public void sendFaderStartCommand(Set<Integer> deviceNumbersToStart, Set<Integer> deviceNumbersToStop) throws IOException
Broadcast a packet that tells some players to start playing and others to stop. If a player number is in both sets, it will be told to stop. Numbers outside the range 1 to 4 are ignored.- Parameters:
deviceNumbersToStart
- the players that should start playing if they aren't alreadydeviceNumbersToStop
- the players that should stop playing- Throws:
IOException
- if there is a problem broadcasting the command to the playersIllegalStateException
- if theVirtualCdj
is not active
-
sendOnAirCommand
public void sendOnAirCommand(Set<Integer> deviceNumbersOnAir) throws IOException
Broadcast a packet that tells the players which channels are on the air (audible in the mixer output). This version sends the packet which was used by mixers older than the DJM-V10, and supports devices 1-4. Numbers outside the range 1 to 4 are ignored. If there is an actual DJM mixer on the network, it will be sending these packets several times per second, so the results of calling this method will be quickly overridden.- Parameters:
deviceNumbersOnAir
- the players whose channels are currently on the air- Throws:
IOException
- if there is a problem broadcasting the command to the playersIllegalStateException
- if theVirtualCdj
is not active
-
sendOnAirExtendedCommand
public void sendOnAirExtendedCommand(Set<Integer> deviceNumbersOnAir) throws IOException
Broadcast a packet that tells the players which channels are on the air (audible in the mixer output). This version sends the packet used by the DJM-V10 mixer, and supports devices 1-6. Numbers outside the range 1 to 6 are ignored. If there is an actual DJM mixer on the network, it will be sending these packets several times per second, so the results of calling this method will be quickly overridden.- Parameters:
deviceNumbersOnAir
- the players whose channels are currently on the air- Throws:
IOException
- if there is a problem broadcasting the command to the playersIllegalStateException
- if theVirtualCdj
is not active
-
sendLoadTrackCommand
public void sendLoadTrackCommand(int targetPlayer, int rekordboxId, int sourcePlayer, CdjStatus.TrackSourceSlot sourceSlot, CdjStatus.TrackType sourceType) throws IOException
Send a packet to the target player telling it to load the specified track from the specified source player.- Parameters:
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 loaded- Throws:
IOException
- if there is a problem sending the commandIllegalStateException
- if theVirtualCdj
is not active or the target device cannot be found
-
sendLoadTrackCommand
public void sendLoadTrackCommand(DeviceUpdate target, int rekordboxId, int sourcePlayer, CdjStatus.TrackSourceSlot sourceSlot, CdjStatus.TrackType sourceType) throws IOException
Send a packet to the target device telling it to load the specified track from the specified source player.- Parameters:
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 loaded- Throws:
IOException
- if there is a problem sending the commandIllegalStateException
- if theVirtualCdj
is not active
-
sendLoadSettingsCommand
public void sendLoadSettingsCommand(int targetPlayer, PlayerSettings settings) throws IOException
Send a packet to the target player telling it to apply the supplied settings.- Parameters:
targetPlayer
- the device number of the player that you want to configuresettings
- the device settings that should be loaded- Throws:
IOException
- if there is a problem sending the commandIllegalStateException
- if theVirtualCdj
is not active or the target device cannot be found
-
sendLoadSettingsCommand
public void sendLoadSettingsCommand(DeviceUpdate target, PlayerSettings settings) throws IOException
Send a packet to the target device telling it to apply the supplied settings.- Parameters:
target
- an update from the player that you want to configuresettings
- the device settings that should be loaded- Throws:
IOException
- if there is a problem sending the commandIllegalStateException
- if theVirtualCdj
is not active
-
getStatusInterval
public int getStatusInterval()
Check how often we will send status packets, if we are configured to send them.- Returns:
- the millisecond interval that will pass between status packets we send
-
setStatusInterval
public void setStatusInterval(int interval)
Change the interval at which we will send status packets, if we are configured to send them. You probably won't need to change this unless you are experimenting. If you find an environment where the default of 200ms doesn't work, please open an issue.- Parameters:
interval
- the millisecond interval that will pass between each status packet we send- Throws:
IllegalArgumentException
- ifinterval
is less than 20 or more than 2000
-
sendBeat
public long sendBeat()
Sends a beat packet. Generally this should only be invoked when ourBeatSender
has determined that it is time to do so, but it is public to allow experimentation.- Returns:
- the beat number that was sent, computed from the current (or stopped) playback position
-
sendBeat
public long sendBeat(Snapshot snapshot)
Sends a beat packet. Generally this should only be invoked when ourBeatSender
has determined that it is time to do so, but it is public to allow experimentation.- Parameters:
snapshot
- the time at which the beat to be sent occurred, for computation of its beat number- Returns:
- the beat number that was sent
-
setSendingStatus
public void setSendingStatus(boolean send) throws IOException
Control whether the Virtual CDJ sends status packets to the other players. Most uses of Beat Link will not require this level of activity. However, if you want to be able to take over the tempo master role, and control the tempo and beat alignment of other players, you will need to turn on this feature, which also requires that you are using one of the standard player numbers, 1-4.- Parameters:
send
- iftrue
we will send status packets, and can participate in (and control) tempo and beat sync- Throws:
IllegalStateException
- 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 theBeatFinder
-
isSendingStatus
public boolean isSendingStatus()
Check whether we are currently sending status packets.- Returns:
true
if we are sending status packets, and can participate in (and control) tempo and beat sync
-
setPlaying
public void setPlaying(boolean playing)
Controls whether we report that we are playing. This will only have an impact when we are sending status and beat packets.- Parameters:
playing
-true
if we should seem to be playing
-
isPlaying
public boolean isPlaying()
Check whether we are pretending to be playing. This will only have an impact when we are sending status and beat packets.- Returns:
true
if we are reporting active playback
-
getPlaybackPosition
public Snapshot getPlaybackPosition()
Find details about the current simulated playback position.- Returns:
- the current (or last, if we are stopped) playback state
-
adjustPlaybackPosition
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.
- Parameters:
ms
- the number of millisecond to shift the simulated playback position
-
becomeTempoMaster
public void becomeTempoMaster() throws IOException
Arrange to become the tempo master. Starts a sequence of interactions with the other players that should end up with us in charge of the group tempo and beat alignment.- Throws:
IllegalStateException
- if we are not sending status updatesIOException
- if there is a problem sending the master yield request
-
isTempoMaster
public boolean isTempoMaster()
Check whether we are currently in charge of the tempo and beat alignment.- Returns:
true
if we hold the tempo master role
-
setSynced
public void setSynced(boolean sync)
Controls whether we are currently staying in sync with the tempo master. Will only be meaningful if we are sending status packets.- Parameters:
sync
- iftrue
, our status packets will be tempo and beat aligned with the tempo master
-
isSynced
public boolean isSynced()
Check whether we are currently staying in sync with the tempo master. Will only be meaningful if we are sending status packets.- Returns:
true
if our status packets will be tempo and beat aligned with the tempo master
-
setOnAir
public void setOnAir(boolean audible)
Change whether we believe our channel is currently on the air (audible in the mixer output). Only meaningful if we are sending status packets. If there is a real DJM mixer on the network, it will rapidly override any value established by this method with its actual report about the channel state.- Parameters:
audible
-true
if we should report ourselves as being on the air in our status packets
-
isOnAir
public boolean isOnAir()
Checks whether we believe our channel is currently on the air (audible in the mixer output). Only meaningful if we are sending status packets. If there is a real DJM mixer on the network, it will be controlling the state of this property.- Returns:
- audible
true
if we should report ourselves as being on the air in our status packets
-
setTempo
public void setTempo(double bpm)
Controls the tempo at which we report ourselves to be playing. Only meaningful if we are sending status packets. IfisSynced()
istrue
and we are not the tempo master, any value set by this method will overridden by the the next tempo master change.- Parameters:
bpm
- the tempo, in beats per minute, that we should report in our status and beat packets
-
getTempo
public double getTempo()
Check the tempo at which we report ourselves to be playing. Only meaningful if we are sending status packets.- Returns:
- the tempo, in beats per minute, that we are reporting in our status and beat packets
-
jumpToBeat
public 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. An incoming value less than one will jump us to the first beat.- Parameters:
beat
- the beat that we should pretend to be playing
-
getInstance
public static VirtualCdj getInstance()
Get the singleton instance of this class.- Returns:
- the only instance of this class which exists
-
-