Class TimeFinder


  • public class TimeFinder
    extends LifecycleParticipant

    Watches the beat packets and transport information contained in player status update to infer the current track playback position based on the most recent information available, the time at which that was received, and the playback pitch and direction that was in effect at that time.

    Takes advantage of precise position packets from the CDJ-3000 to support a much more precise and robust tracking of playback (and idle) position for those players.

    Can only operate properly when track metadata and beat grids are available, as these are necessary to convert beat numbers into track positions.

    Author:
    James Elliott
    • Method Detail

      • isRunning

        public boolean isRunning()
        Check whether we are currently running.
        Specified by:
        isRunning in class LifecycleParticipant
        Returns:
        true if track playback positions are being kept track of for all active players
      • getLatestPositions

        public Map<Integer,​TrackPositionUpdate> getLatestPositions()
        Get the latest track position reports available for all visible players.
        Returns:
        the track position information we have been able to calculate for all current players
        Throws:
        IllegalStateException - if the TimeFinder is not running
      • getLatestUpdates

        public Map<Integer,​DeviceUpdate> getLatestUpdates()
        Get the latest device updates (either beats or status updates) available for all visible players.
        Returns:
        the latest beat or status update, whichever was more recent, for each current player
      • getLatestPositionFor

        public TrackPositionUpdate getLatestPositionFor​(int player)
        Get the latest information we have for the specified player. This incorporates both status updates and beat packets we have received from the player, and so is the most definitive source of time and tempo information from the player.
        Parameters:
        player - the player number whose position information is desired
        Returns:
        the consolidated track position information we have for the specified player, or null if we do not have enough information to calculate it
        Throws:
        IllegalStateException - if the TimeFinder is not running
      • getLatestPositionFor

        public TrackPositionUpdate getLatestPositionFor​(DeviceUpdate update)
        Get the latest information we have for the player that sent the supplied status update. The result incorporates both status updates and beat packets we have received from the player, and so is the most definitive source of time and tempo information from the player.
        Parameters:
        update - the device update from a player whose position information is desired
        Returns:
        the consolidated track position information we have for the specified player, or null if we do not have enough information to calculate it
        Throws:
        IllegalStateException - if the TimeFinder is not running
      • getLatestUpdateFor

        public DeviceUpdate getLatestUpdateFor​(int player)
        Get the beat or status update reported by the specified player, whichever is most recent. This is available even when we do not have a beat grid available in order to calculate detailed track position information.
        Parameters:
        player - the player number whose most recent status is desired
        Returns:
        the latest beat or status reported by the specified player, or null if we have not heard any
        Throws:
        IllegalStateException - if the TimeFinder is not running
      • getTimeFor

        public long getTimeFor​(int player)
        Get the best guess we have for the current track position on the specified player.
        Parameters:
        player - the player number whose position is desired
        Returns:
        the milliseconds into the track that we believe playback has reached, or -1 if we don't know
        Throws:
        IllegalStateException - if the TimeFinder is not running
      • getTimeFor

        public long getTimeFor​(DeviceUpdate update)
        Get the best guess we have for the current track position on the player that sent the specified update.
        Parameters:
        update - the device update from a player whose position is desired
        Returns:
        the milliseconds into the track that we believe playback has reached, or -1 if we don't know
        Throws:
        IllegalStateException - if the TimeFinder is not running
      • addTrackPositionListener

        public void addTrackPositionListener​(int player,
                                             TrackPositionListener listener)
        Add a listener that wants to closely follow track playback for a particular player. The listener will be called as soon as there is an initial TrackPositionUpdate for the specified player, and whenever there is an unexpected change in playback position, speed, or state on that player.

        To help the listener orient itself, it is sent a TrackPositionListener.movementChanged(TrackPositionUpdate) message immediately upon registration to report the current playback position, even if none is known (in which case it will be called with the value null).

        If the same listener was previously registered (for example, to listen to a different player), this call replaces the former registration with the new one.

        Parameters:
        player - the player number that the listener is interested in
        listener - the interface that will be called when there are changes in track playback on the player
      • removeTrackPositionListener

        public void removeTrackPositionListener​(TrackPositionListener listener)
        Remove a listener that was following track playback movement.
        Parameters:
        listener - the interface that will no longer be called for changes in track playback
      • getSlack

        public long getSlack()
        Check how many milliseconds our interpolated time is allowed to drift from what is being reported by a player before we consider it a significant enough change to report to listeners that are trying to closely track a player's playback position.
        Returns:
        the maximum number of milliseconds we will allow our listeners to diverge from the reported playback position before reporting a jump
      • setSlack

        public void setSlack​(long slack)
        Set how many milliseconds our interpolated time is allowed to drift from what is being reported by a player before we consider it a significant enough change to report to listeners that are trying to closely track a player's playback position.
        Parameters:
        slack - the maximum number of milliseconds we will allow our listeners to diverge from the reported playback position before reporting a jump
      • isOwnBeatListener

        public boolean isOwnBeatListener​(BeatListener listener)
        Checks whether the specified beat listener belongs to the TimeFinder. The BeatFinder uses this to notify the TimeFinder before any other listeners when a beat is received so that other listeners can rely on the TimeFinder having an up-to-date beat number when they want it. There's no reason for any other class to call this method (and the only reason it is public is that the BeatFinder is in a different package).
        Parameters:
        listener - the beat listener implementation to be checked
        Returns:
        true if the listener is ours
      • start

        public void start()
                   throws Exception

        Start interpolating playback position for all active players. Starts the BeatGridFinder, BeatFinder, and VirtualCdj if they are not already running, because we need them to perform our calculations. This in turn starts the DeviceFinder, so we can keep track of the comings and goings of players themselves.

        Throws:
        Exception - if there is a problem starting the required components
      • stop

        public void stop()
        Stop interpolating playback position for all active players.
      • getInstance

        public static TimeFinder getInstance()
        Get the singleton instance of this class.
        Returns:
        the only instance of this class which exists.