Class 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 what 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.
    Author:
    James Elliott
    • 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
      • 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 class LifecycleParticipant
        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 the VirtualCdj 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 the VirtualCdj 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 to true, 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 to true, 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 the 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 it will try to find a value in the range 7 to 15. 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() 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.

        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 the 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.
        Returns:
        the most recent update from a device which reported itself as the master
        Throws:
        IllegalStateException - if the VirtualCdj 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 the VirtualCdj 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 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.
        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 calling start(), but avoids the race condition which can occur if startup is already in progress, which would lead to an IllegalStateException. 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 the VirtualCdj 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 the VirtualCdj 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 by 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.

        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 the VirtualCdj 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 the VirtualCdj 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 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.

        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. If listener is null 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 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.

        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. If listener is null 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 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.

        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. If listener is null 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 the MetadataFinder 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 set
        synced - 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 device
        IllegalStateException - if the VirtualCdj is not active
        IllegalArgumentException - if deviceNumber 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 set
        synced - 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 device
        IllegalStateException - if the VirtualCdj is not active
        NullPointerException - if update is null
      • 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 device
        IllegalStateException - if the VirtualCdj is not active
        IllegalArgumentException - if deviceNumber 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 device
        IllegalStateException - if the VirtualCdj is not active
        NullPointerException - if update is null
      • 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 already
        deviceNumbersToStop - the players that should stop playing
        Throws:
        IOException - if there is a problem broadcasting the command to the players
        IllegalStateException - if the VirtualCdj 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 players
        IllegalStateException - if the VirtualCdj 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 players
        IllegalStateException - if the VirtualCdj 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 track
        rekordboxId - the identifier of a track within the source player's rekordbox database
        sourcePlayer - the device number of the player from which the track should be loaded
        sourceSlot - the media slot from which the track should be loaded
        sourceType - the type of track to be loaded
        Throws:
        IOException - if there is a problem sending the command
        IllegalStateException - if the VirtualCdj 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 track
        rekordboxId - the identifier of a track within the source player's rekordbox database
        sourcePlayer - the device number of the player from which the track should be loaded
        sourceSlot - the media slot from which the track should be loaded
        sourceType - the type of track to be loaded
        Throws:
        IOException - if there is a problem sending the command
        IllegalStateException - if the VirtualCdj 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 configure
        settings - the device settings that should be loaded
        Throws:
        IOException - if there is a problem sending the command
        IllegalStateException - if the VirtualCdj 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 configure
        settings - the device settings that should be loaded
        Throws:
        IOException - if there is a problem sending the command
        IllegalStateException - if the VirtualCdj 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 - if interval is less than 20 or more than 2000
      • sendBeat

        public long sendBeat()
        Sends a beat packet. Generally this should only be invoked when our BeatSender 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​(org.deepsymmetry.electro.Snapshot snapshot)
        Sends a beat packet. Generally this should only be invoked when our BeatSender 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 - if true 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 4
        IOException - if there is a problem starting the BeatFinder
      • 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 org.deepsymmetry.electro.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 updates
        IOException - 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 - if true, 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. If 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.
        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