Package org.deepsymmetry.beatlink.data
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.
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 voidaddTrackPositionListener(int player, TrackPositionListener listener)Add a listener that wants to closely follow track playback for a particular player.static TimeFindergetInstance()Get the singleton instance of this class.TrackPositionUpdategetLatestPositionFor(int player)Get the latest information we have for the specified player.TrackPositionUpdategetLatestPositionFor(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.DeviceUpdategetLatestUpdateFor(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.longgetSlack()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.longgetTimeFor(int player)Get the best guess we have for the current track position on the specified player.longgetTimeFor(DeviceUpdate update)Get the best guess we have for the current track position on the player that sent the specified update.booleanisOwnBeatListener(BeatListener listener)Checks whether the specified beat listener belongs to the TimeFinder.booleanisRunning()Check whether we are currently running.voidremoveTrackPositionListener(TrackPositionListener listener)Remove a listener that was following track playback movement.voidsetSlack(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.voidstart()Start interpolating playback position for all active players.voidstop()Stop interpolating playback position for all active players.StringtoString()-
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:
isRunningin 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
nullif 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
nullif 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
nullif 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 initialTrackPositionUpdatefor 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 aTrackPositionListener.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. TheBeatFinderuses 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:
trueif the listener is ours
-
start
public void start() throws ExceptionStart interpolating playback position for all active players. Starts the
BeatGridFinder,BeatFinder, andVirtualCdjif 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.
-
-