Class TimeFinder
- java.lang.Object
-
- org.deepsymmetry.beatlink.LifecycleParticipant
-
- org.deepsymmetry.beatlink.data.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 Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description void
addTrackPositionListener(int player, TrackPositionListener listener)
Add a listener that wants to closely follow track playback for a particular player.static TimeFinder
getInstance()
Get the singleton instance of this class.TrackPositionUpdate
getLatestPositionFor(int player)
Get the latest information we have for the specified player.TrackPositionUpdate
getLatestPositionFor(DeviceUpdate update)
Get the latest information we have for the player that sent the supplied status update.Map<Integer,TrackPositionUpdate>
getLatestPositions()
Get the latest track position reports available for all visible players.DeviceUpdate
getLatestUpdateFor(int player)
Get the beat or status update reported by the specified player, whichever is most recent.Map<Integer,DeviceUpdate>
getLatestUpdates()
Get the latest device updates (either beats or status updates) available for all visible players.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.long
getTimeFor(int player)
Get the best guess we have for the current track position on the specified player.long
getTimeFor(DeviceUpdate update)
Get the best guess we have for the current track position on the player that sent the specified update.boolean
isOwnBeatListener(BeatListener listener)
Checks whether the specified beat listener belongs to the TimeFinder.boolean
isRunning()
Check whether we are currently running.void
removeTrackPositionListener(TrackPositionListener listener)
Remove a listener that was following track playback movement.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.void
start()
Start interpolating playback position for all active players.void
stop()
Stop interpolating playback position for all active players.String
toString()
-
Methods inherited from class org.deepsymmetry.beatlink.LifecycleParticipant
addLifecycleListener, deliverLifecycleAnnouncement, ensureRunning, getLifecycleListeners, removeLifecycleListener
-
-
-
-
Method Detail
-
isRunning
public boolean isRunning()
Check whether we are currently running.- Specified by:
isRunning
in classLifecycleParticipant
- 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 initialTrackPositionUpdate
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 valuenull
).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 inlistener
- 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. TheBeatFinder
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
, andVirtualCdj
if they are not already running, because we need them to perform our calculations. This in turn starts theDeviceFinder
, 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.
-
-