Class Metronome
- java.lang.Object
-
- org.deepsymmetry.electro.Metronome
-
- All Implemented Interfaces:
Snapshot
public class Metronome extends Object implements Snapshot
A beat management tool. Tell it what BPM you want, and it will compute beat, bar, and phrase timestamps accordingly. If you only need a single piece of information, you can obtain it directly from the metronome. If you need to work with two or more values, you need to call
getSnapshot()
and ask the snapshot for them or you will see inconsistent results, since time will move on between each question you ask the metronome itself.Inspired by Jeff Rose's work in Overtone.
- Author:
- James Elliott
-
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description void
adjustStart(long ms)
Adds a number of milliseconds to the start time of the metronome.static double
beatsToMilliseconds(long beats, double tempo)
Calculate the number of milliseconds taken by the specified number of beats at the specified tempo.double
distanceFromBar()
Determine how far in time the metronome is from its closest bar boundary.double
distanceFromBeat()
Determine how far in time the metronome is from the closest beat.double
distanceFromPhrase()
Determine how far in time the metronome is from its closest phrase boundary.static double
enhancedPhase(long markerNumber, double markerPhase, double desiredRatio)
Helper function to calculate phase with respect to multiples or fractions of a marker (beat, bar, or phrase), given the phase with respect to that marker, the marker number, and the desired ratio.static double
enhancedPhase(long markerNumber, double markerPhase, long numerator, long denominator)
Helper function to calculate phase with respect to multiples or fractions of a marker (beat, bar, or phrase), given the phase with respect to that marker, the marker number, and the desired ratio.static double
findClosestDelta(double delta)
Figures out the least disruptive phase shift that ends up in a target phase.long
getBar()
Get the current bar being played.double
getBarInterval()
Get the number of milliseconds a bar lasts given the current configuration and tempo.double
getBarPhase()
Determine the distance traveled into the current bar as a phase number in the range [0.0, 1.0).int
getBarsPerPhrase()
Get the number of bars per phrase in the metronome's beat grid.int
getBarWithinPhrase()
Return the current bar number relative to the start of the phrase: the phrase starts with bar 1, and the range goes up to the value ofgetBarsPerPhrase()
.long
getBeat()
Get the current beat being played.double
getBeatInterval()
Get the number of milliseconds a beat lasts given the current tempo.double
getBeatPhase()
Determine the distance traveled into the current beat as a phase number in the range [0.0, 1.0).int
getBeatsPerBar()
Get the number of beats per bar in the metronome's beat grid.int
getBeatWithinBar()
Return the current beat number relative to the start of the bar: the down beat is 1, and the range goes up to the value ofgetBeatsPerBar()
.int
getBeatWithinPhrase()
Return the current beat number relative to the start of the phrase: the phrase starts with beat 1, and the range goes up to the value ofgetBeatsPerBar()
timesgetBarsPerPhrase()
.long
getInstant()
Checks when a snapshot was taken; since you are working with a live metronome, always returns the current time.String
getMarker()
Returns the current count of the metronome as "phrase.bar.beat".long
getPhrase()
Get the current phrase being played.double
getPhraseInterval()
Get the number of milliseconds a phrase lasts given the current configuration and tempo.double
getPhrasePhase()
Determine the distance traveled into the current phrase as a phase number in the range [0.0, 1.0).Snapshot
getSnapshot()
Take a snapshot of the current beat, bar, phrase, and phase state, so coherent calculations about them can be performed with respect to a static point in time.Snapshot
getSnapshot(long instant)
Take a snapshot of the beat, bar, phrase, and phase state that the metronome would have at the specified millisecond timestamp, so coherent calculations about them can be performed with respect to that static point in time.long
getStartTime()
Returns the time at which this metronome was effectively started (tempo changes will shift this, as will a variety of methods which adjust the timeline).double
getTempo()
Get the tempo at which this metronome is running.long
getTimeOfBar(long bar)
Determine the millisecond timestamp at which a particular bar will occur.long
getTimeOfBeat(long beat)
Determine the millisecond timestamp at which a particular beat will occur.long
getTimeOfPhrase(long phrase)
Determine the millisecond timestamp at which a particular phrase will occur.boolean
isDownBeat()
Checks whether the current beat is the first beat in its bar.boolean
isPhraseStart()
Checks whether the current beat is the first beat in its phrase.void
jumpToBar(long bar)
Restarts the metronome at the start of the specified bar, keeping the beat phase unchanged in case it is being synchronized to an external source.void
jumpToBeat(long beat)
Restarts the metronome at the beginning of the specified beat number.void
jumpToPhrase(long phrase)
Restarts the metronome at the start of the specified phrase, keeping the beat phase unchanged in case it is being synchronized to an external source.static long
markerNumber(long instant, long start, double interval)
Helper function to calculate the beat, bar, or phrase number in effect at a given instant (in milliseconds) given a timeline starting point (start, also in milliseconds), and the interval (also in milliseconds) between beats, bars, or phrases.static double
markerPhase(long instant, long start, double interval)
Helper function to calculate the beat, bar, or phrase phase at a given instant (in milliseconds), given a timeline starting point (start, also in milliseconds), and the interval (also in milliseconds) between beats, bars, or phrases.static double
normalizePhase(double phase)
Ensure that a phase falls in the range [0.0, 1.0).void
setBarPhase(double phase)
Nudge the metronome so that it has reached the specified part of its current bar.void
setBarsPerPhrase(int barsPerPhrase)
Establish a new number of bars per phrase in the metronome's beat grid.void
setBeatPhase(double phase)
Nudge the metronome so that it has reached the specified part of its current beat.void
setBeatsPerBar(int beatsPerBar)
Establish a new number of beats per bar in the metronome's beat grid.void
setPhrasePhase(double phase)
Nudge the metronome so that it has reached the specified part of its current phrase.void
setTempo(double bpm)
Establish a new tempo for the metronome.String
toString()
-
-
-
Constructor Detail
-
Metronome
public Metronome()
Create a new metronome with default configuration. Its start time is now, its tempo is 120.0 beats per minute, counting four beats per bar, and eight bars per phrase.
-
Metronome
public Metronome(Metronome template)
Create a metronome which is a copy of another metronome, that is sharing the same start time, tempo, beats per bar, and bars per phrase. Once created, the metronomes are independent, so changes to one will not affect the other.- Parameters:
template
- the metronome whose configuration is to be copied
-
-
Method Detail
-
getStartTime
public long getStartTime()
Returns the time at which this metronome was effectively started (tempo changes will shift this, as will a variety of methods which adjust the timeline).- Specified by:
getStartTime
in interfaceSnapshot
- Returns:
- the millisecond timestamp at which the beat grid originates.
-
adjustStart
public void adjustStart(long ms)
Adds a number of milliseconds to the start time of the metronome. Useful to nudge it back into synchronization with an external source.- Parameters:
ms
- the number of milliseconds to add to the start time
-
getTempo
public double getTempo()
Get the tempo at which this metronome is running.
-
setTempo
public void setTempo(double bpm)
Establish a new tempo for the metronome. The start time will be adjusted so that the current beat and phase are unaffected by the tempo change.- Parameters:
bpm
- the number of beats per minute at which the metronome should now run
-
getBeatsPerBar
public int getBeatsPerBar()
Get the number of beats per bar in the metronome's beat grid. The default value is four.- Specified by:
getBeatsPerBar
in interfaceSnapshot
- Returns:
- the number of beats per bar being counted
-
setBeatsPerBar
public void setBeatsPerBar(int beatsPerBar)
Establish a new number of beats per bar in the metronome's beat grid.- Parameters:
beatsPerBar
- a positive number of beats per bar being counted- Throws:
IllegalArgumentException
- ifbeatsPerBar
is not greater than zero
-
getBarsPerPhrase
public int getBarsPerPhrase()
Get the number of bars per phrase in the metronome's beat grid.- Specified by:
getBarsPerPhrase
in interfaceSnapshot
- Returns:
- the number of bars per phrase being counted
-
setBarsPerPhrase
public void setBarsPerPhrase(int barsPerPhrase)
Establish a new number of bars per phrase in the metronome's beat grid.- Parameters:
barsPerPhrase
- a positive number of bars per phrase being counted- Throws:
IllegalArgumentException
- ifbarsPerPhrase
is not greater than zero
-
beatsToMilliseconds
public static double beatsToMilliseconds(long beats, double tempo)
Calculate the number of milliseconds taken by the specified number of beats at the specified tempo.- Parameters:
beats
- the number of beats to timetempo
- the number of that play in a minute- Returns:
- the number of milliseconds that would pass while that many beats play at that tempo
-
markerNumber
public static long markerNumber(long instant, long start, double interval)
Helper function to calculate the beat, bar, or phrase number in effect at a given instant (in milliseconds) given a timeline starting point (start, also in milliseconds), and the interval (also in milliseconds) between beats, bars, or phrases.- Parameters:
instant
- the time (in milliseconds) for which a marker number is desiredstart
- the time (in milliseconds) at which the metronome started countinginterval
- the time (in milliseconds) between markers- Returns:
- the marker number currently in effect for the metronome at the specified instant
-
markerPhase
public static double markerPhase(long instant, long start, double interval)
Helper function to calculate the beat, bar, or phrase phase at a given instant (in milliseconds), given a timeline starting point (start, also in milliseconds), and the interval (also in milliseconds) between beats, bars, or phrases. A marker phase starts at 0.0 at the beginning of the beat, bar, or phrase, rises linearly during the beat, bar, or phrase, but never reaches 1.0, because that is the start of the next beat, bar, or phrase.- Parameters:
instant
- the time (in milliseconds) for which a marker phase is desiredstart
- the time (in milliseconds) at which the metronome started countinginterval
- the time (in milliseconds) between markers- Returns:
- the phase in effect for the specified marker at the specified instant, in the range [0.0, 1.0)
-
enhancedPhase
public static double enhancedPhase(long markerNumber, double markerPhase, double desiredRatio)
Helper function to calculate phase with respect to multiples or fractions of a marker (beat, bar, or phrase), given the phase with respect to that marker, the marker number, and the desired ratio. A
desiredRatio
of1.0
returns the phase unchanged;0.5
(1/2) oscillates twice as fast,0.75
(3/4) oscillates 4 times every 3 markers...See the Ratios illustration in the Afterglow documentation for more details with graphs.
Only positive values were considered for the ratio when writing this algorithm, the results you'll get if you pass in zero or a negative value, are not likely meaningful.
- Parameters:
markerNumber
- the current marker number being considered, as returned bymarkerNumber(long, long, double)
markerPhase
- the current phase with respect to the marker, as returned bymarkerPhase(long, long, double)
desiredRatio
- the ratio by which to oscillate the phase- Returns:
- the oscillated phase
- Since:
- 0.1.1
- See Also:
enhancedPhase(long, double, long, long)
-
enhancedPhase
public static double enhancedPhase(long markerNumber, double markerPhase, long numerator, long denominator)
Helper function to calculate phase with respect to multiples or fractions of a marker (beat, bar, or phrase), given the phase with respect to that marker, the marker number, and the desired ratio. A ration of 1/1 returns the phase unchanged; 1/2 oscillates twice as fast, 3/4 oscillates 4 times every 3 markers...
See the Ratios illustration in the Afterglow documentation for more details with graphs.
Only positive values were considered for the numerator and denominator when writing this algorithm, the results you'll get if you pass in zero or a negative value, are not likely meaningful.
- Parameters:
markerNumber
- the current marker number being considered, as returned bymarkerNumber(long, long, double)
markerPhase
- the current phase with respect to the marker, as returned bymarkerPhase(long, long, double)
numerator
- over how many markers should an oscillation cycle spandenominator
- how many oscillations should occur in that span- Returns:
- the oscillated phase
- Since:
- 0.1.1
- See Also:
enhancedPhase(long, double, double)
-
normalizePhase
public static double normalizePhase(double phase)
Ensure that a phase falls in the range [0.0, 1.0). Values outside the range will have their non-fractional part discarded.- Parameters:
phase
- a phase value that may require normalization to within the unit range- Returns:
- the normalized phase
-
getBeatInterval
public double getBeatInterval()
Get the number of milliseconds a beat lasts given the current tempo.- Specified by:
getBeatInterval
in interfaceSnapshot
- Returns:
- the duration of a beat
-
getBarInterval
public double getBarInterval()
Get the number of milliseconds a bar lasts given the current configuration and tempo.- Specified by:
getBarInterval
in interfaceSnapshot
- Returns:
- the duration of a bar
-
getPhraseInterval
public double getPhraseInterval()
Get the number of milliseconds a phrase lasts given the current configuration and tempo.- Specified by:
getPhraseInterval
in interfaceSnapshot
- Returns:
- the duration of a phrase
-
getBeat
public long getBeat()
Get the current beat being played.
-
jumpToBeat
public void jumpToBeat(long beat)
Restarts the metronome at the beginning of the specified beat number.- Parameters:
beat
- the beat to which the metronome should jump; the first beat is beat 1
-
getTimeOfBeat
public long getTimeOfBeat(long beat)
Determine the millisecond timestamp at which a particular beat will occur.- Specified by:
getTimeOfBeat
in interfaceSnapshot
- Parameters:
beat
- the number of the beat whose start time is desired- Returns:
- the time at which the specified beat begins, to the nearest millisecond
-
getBeatPhase
public double getBeatPhase()
Determine the distance traveled into the current beat as a phase number in the range [0.0, 1.0).- Specified by:
getBeatPhase
in interfaceSnapshot
- Returns:
- the current beat phase
-
findClosestDelta
public static double findClosestDelta(double delta)
Figures out the least disruptive phase shift that ends up in a target phase.- Parameters:
delta
- the amount to be added to our current phase to achieve a desired phase- Returns:
- an amount that will yield the same phase while changing our position the least
-
setBeatPhase
public void setBeatPhase(double phase)
Nudge the metronome so that it has reached the specified part of its current beat. If the value supplied is outside the range of a beat phase (less than zero or greater than or equal to one), it will be normalized to fit into that range by ignoring the non-fractional part.- Parameters:
phase
- the desired beat phase, in the range [0.0, 1.0).
-
getBar
public long getBar()
Get the current bar being played.
-
jumpToBar
public void jumpToBar(long bar)
Restarts the metronome at the start of the specified bar, keeping the beat phase unchanged in case it is being synchronized to an external source.- Parameters:
bar
- the bar to which the metronome should jump; the first bar is bar 1
-
getTimeOfBar
public long getTimeOfBar(long bar)
Determine the millisecond timestamp at which a particular bar will occur.- Specified by:
getTimeOfBar
in interfaceSnapshot
- Parameters:
bar
- the number of the bar whose start time is desired- Returns:
- the time at which the specified bar begins, rounded to the nearest millisecond
-
getBarPhase
public double getBarPhase()
Determine the distance traveled into the current bar as a phase number in the range [0.0, 1.0).- Specified by:
getBarPhase
in interfaceSnapshot
- Returns:
- the current bar phase
-
setBarPhase
public void setBarPhase(double phase)
Nudge the metronome so that it has reached the specified part of its current bar. If the value supplied is outside the range of a bar phase (less than zero or greater than or equal to one), it will be normalized to fit into that range by ignoring the non-fractional part.- Parameters:
phase
- the desired bar phase, in the range [0.0, 1.0).
-
getPhrase
public long getPhrase()
Get the current phrase being played.
-
jumpToPhrase
public void jumpToPhrase(long phrase)
Restarts the metronome at the start of the specified phrase, keeping the beat phase unchanged in case it is being synchronized to an external source.- Parameters:
phrase
- the phrase to which the metronome should jump; the first phrase is phrase 1
-
getTimeOfPhrase
public long getTimeOfPhrase(long phrase)
Determine the millisecond timestamp at which a particular phrase will occur.- Specified by:
getTimeOfPhrase
in interfaceSnapshot
- Parameters:
phrase
- the number of the phrase whose start time is desired- Returns:
- the time at which the specified phrase begins, rounded to the nearest millisecond
-
getPhrasePhase
public double getPhrasePhase()
Determine the distance traveled into the current phrase as a phase number in the range [0.0, 1.0).- Specified by:
getPhrasePhase
in interfaceSnapshot
- Returns:
- the current phrase phase
-
setPhrasePhase
public void setPhrasePhase(double phase)
Nudge the metronome so that it has reached the specified part of its current phrase. If the value supplied is outside the range of a phrase phase (less than zero or greater than or equal to one), it will be normalized to fit into that range by ignoring the non-fractional part.- Parameters:
phase
- the desired phrase phase, in the range [0.0, 1.0).
-
getSnapshot
public Snapshot getSnapshot()
Take a snapshot of the current beat, bar, phrase, and phase state, so coherent calculations about them can be performed with respect to a static point in time.- Returns:
- a representation of the detailed metronome state at the current moment
-
getSnapshot
public Snapshot getSnapshot(long instant)
Take a snapshot of the beat, bar, phrase, and phase state that the metronome would have at the specified millisecond timestamp, so coherent calculations about them can be performed with respect to that static point in time.- Parameters:
instant
- the point in time which this snapshot should capture- Returns:
- a representation of the detailed metronome state at the specified moment
-
getMarker
public String getMarker()
Returns the current count of the metronome as "phrase.bar.beat".
-
getInstant
public long getInstant()
Checks when a snapshot was taken; since you are working with a live metronome, always returns the current time. If you are doing computations around this, you probably want to callgetSnapshot()
and work with that instead.- Specified by:
getInstant
in interfaceSnapshot
- Returns:
- the current system time in milliseconds
-
getBeatWithinBar
public int getBeatWithinBar()
Return the current beat number relative to the start of the bar: the down beat is 1, and the range goes up to the value ofgetBeatsPerBar()
.- Specified by:
getBeatWithinBar
in interfaceSnapshot
- Returns:
- the beat number within the current bar being counted
-
isDownBeat
public boolean isDownBeat()
Checks whether the current beat is the first beat in its bar.- Specified by:
isDownBeat
in interfaceSnapshot
- Returns:
true
we are currently in the first beat of a bar
-
getBeatWithinPhrase
public int getBeatWithinPhrase()
Return the current beat number relative to the start of the phrase: the phrase starts with beat 1, and the range goes up to the value ofgetBeatsPerBar()
timesgetBarsPerPhrase()
.- Specified by:
getBeatWithinPhrase
in interfaceSnapshot
- Returns:
- the beat number within the current phrase being counted
-
isPhraseStart
public boolean isPhraseStart()
Checks whether the current beat is the first beat in its phrase.- Specified by:
isPhraseStart
in interfaceSnapshot
- Returns:
true
if we are currently in the first beat of a phrase
-
getBarWithinPhrase
public int getBarWithinPhrase()
Return the current bar number relative to the start of the phrase: the phrase starts with bar 1, and the range goes up to the value ofgetBarsPerPhrase()
.- Specified by:
getBarWithinPhrase
in interfaceSnapshot
- Returns:
- the bar number within the current phrase being counted
-
distanceFromBeat
public double distanceFromBeat()
Determine how far in time the metronome is from the closest beat. The result will be positive if the beat has already occurred, and negative if it is coming up.- Specified by:
distanceFromBeat
in interfaceSnapshot
- Returns:
- the distance in milliseconds from the closest beat on the metronome's timeline
-
distanceFromBar
public double distanceFromBar()
Determine how far in time the metronome is from its closest bar boundary. The result will be positive if the bar has already started, and negative if it is coming up.- Specified by:
distanceFromBar
in interfaceSnapshot
- Returns:
- the distance in milliseconds from the closest bar boundary on the metronome's timeline
-
distanceFromPhrase
public double distanceFromPhrase()
Determine how far in time the metronome is from its closest phrase boundary. The result will be positive if the phrase has already started, and negative if it is coming up.- Specified by:
distanceFromPhrase
in interfaceSnapshot
- Returns:
- the distance in milliseconds from the closest phrase boundary on the metronome's timeline
-
-