# Calculate Beats

### Calculate Beats

Some high level parts of the timeline, such as unmetered Hits, Spans, or Actions use `time` in absolute seconds to specify when they occur. However, most other areas use `beat`s because it allows events to be more easily coordinated in “musical time,” taking into account tempo, meter, phrasing, etc. There are often times when it is useful to to know what `beat` is at a given `time` and vice versa.

#### What Is Tempo?

All tempo information in the timeline is provided in beats per minute (BPM), which is the standard unit for tempo. BPM (and tempo in general) measures speed.

Although BPM is the natural unit for talking about tempo, when doing calculations, it is far easier to work with beats per second (BPS). The reason is that 60 BPM = 1 BPS and this prevents needing to divide every result by 60.

Note: It is important to keep in mind that since tempo is a rate, you are often working with the difference (delta) between two points in time, and not necessarily absolute time.

#### The Anatomy of Tempo

Tempo information lives in two places in the timeline.

First is the `tempo` key of a metered Span. This is the initial tempo and will always be present since metered Spans by definition must have a tempo. When creating a timeline, it is possible to omit the initial tempo, in which case an appropriate one will be chosen for you. However, any timeline returned from creating a Composition (see Composition vs. Render) is guaranteed to have a `tempo` value.

The second place where tempo information is defined is in the `tempo_changes` key of a metered Span. Where `tempo` defines the initial value, `tempo_changes` provides a list of times at which the tempo changes. Starting from the initial tempo, which is at the `time` of the Span, the tempo at any given time is linearly interpolated between two points in time: the one at or before the time in question, and the one at a time after.

Note: While these are the only places where persistent information about tempo exists, it is important to remember that some Actions can alter tempo (specifically `add_region`, `copy_region`, `copy_tempo_from_time`, `ramp_tempo`, and `set_tempo`). If you are using Actions that alter tempo and you are also interested in calculating beats, you will be required to create a Composition and do your calculations based on the resulting timeline, especially if you are asking for any random tempo.

#### Conversions

Here are the basic steps for finding the beat at the next time (in pseudocode):

``````Beat find_target_beat(BPM bpm, Second start_time, Second target_time, Beat start_beat) {
// convert to BPS
bps = bpm / 60

// calculate the change in time between the start and target second
delta_time = target_time - start_time

// calculate the change in beats based on the tempo
delta_beats = delta_time * bps

// calculate the next beat
target_beat = start_beat + delta_beats

return target_beat
}
``````

where

• `bpm` is the the “current” BPM value from the timeline (see later sections)
• `start_time` is the second you are starting from
• `target_time` is the second you wish to find the beat of
• `start_beat` is the beat that corresponds to `start_time`

Note: The only piece of information for the above function that is not explicitly in a timeline is `start_beat`. The beat at the start of any Span is always 0.0!

By using this, you can find any beat at any time after any beat which you already know the time of.

There are also times when it is useful to convert a beat into a time. For instance, since anything inside a Span is defined in terms of beats, you may wish to display that event in relation to other things which are defined in seconds.

Here are the basic steps for finding the time at the next beat (in pseudocode):

``````Second find_target_time(BPM bpm, Beat start_beat, Beat target_beat, Second start_time) {
// convert to BPS
bps = bpm / 60

// convert to seconds per beat (SPB)
spb = 1 / bps

// calculate the change in beats between the start and target
delta_beats = target_beat - start_beat

// calculate the change in beats based on the tempo
delta_time = delta_beats * spb

// calculate the next beat
target_time = start_time + delta_time

return target_time
}
``````

where

• `bpm` is the the “current” BPM value from the timeline (see later sections)
• `start_beat` is the beat you are starting from
• `target_beat` is the beat you wish to find the second of
• `start_time` is the second that corresponds to `start_beat`

By using either `find_target_beat` and `find_target_time` you can freely convert seconds to beats and beats to seconds.

#### Constant Tempo

The simplest, and most common, situation to calculate is a constant tempo. If you are manually setting the `tempo` key of your metered Span and not using any `tempo_changes`, then the BPM at any time will always be the same. In this case (using the variable names from the above pseudocode):

• `start_time` will always be the `time` of the Span
• `bpm` will always be the `tempo` of the Span
• `start_beat` will always be 0.0 (since the beat at the start of any metered Span is always 0.0).

With that, every `target_time` can be easily converted it to a `target_beat` and vice versa for anywhere in the Span.

#### Abrupt Tempo Changes

You may want to create sudden dramatic tempo changes that make beat/time calculations more complicated. The main difficulty is that, unlike with a constant tempo, the `bpm`, `start_time`, and `start_beat` values are different every time the tempo changes.

When finding the beats of events in a timeline, you already know the `bpm` and `start_time` because they will either be the `tempo` and `time` of the Span or of the Tempo Change at or before the second in question. What you do not know directly is the `start_beat`. As with a constant tempo, the beat at the start of any Span is always 0.0, but in order to know the `start_beat` for any Tempo Change, you must calculate the beat at the `time` of it. Since you can only calculate beats at times when the BPM is constant, this means that you must always start from the beginning of the Span and calculate the `beat` for every Tempo Change before you can do anything else.

#### Tempo Ramps

The last situation that can happen is gradually changing from one tempo to an other. This is useful for creating smooth transitions between seconds, adding expressive speedups and slowdowns, etc. It does, however, add one more level of complication when calculating beats.

The tempo between any two Tempo Changes (or between the start of the Span and the first Tempo Change) is linearly interpolated between the times of the changes. This means that, unlike with abrupt changes, the BPM at any given time between Tempo Changes is changing.

Here are the basic steps for finding the BPM at any second (in pseudocode):

``````BPM interpolated_bpm(BPM start_bpm, BPM end_bpm, Second start_time, Second end_time, Second target_time) {
// calculate the change in BPM
delta_bpm = end_bpm - start_bpm

// calculate the change in time
delta_time = end_time - start_time

// calculate the slope of the line
slope = delta_bpm / delta_time

// y = mx+b
target_bpm = slope * target_time + start_bpm

return target_bpm
}
``````

where

• `start_bpm` is the BPM at or before the time in question
• `end_bpm` is the next known BPM
• `start_time` is the time of `start_bpm`
• `end_time` is the time of `end_bpm`
• `target_time` is the time at which you want to know the BPM

Most of the information needed for these calculations comes from Tempo Changes. The two special cases are:

• the initial values, which are the `tempo` and `time` of the Span
• the final values, which are the last BPM in the Span and the `time` of the next Span

The only other question is “how often should the BPM be calculated?” This depends on how accurately you wish to represent the mapping between beats and seconds. Typically 8 times per second is a good balance between accuracy and effort.

Note: Since the constant and abrupt cases are subsets of what is required to support ramping tempo, if you ever plan on supporting all three it may make sense to treat every case as if it contains ramps.

#### Tempo Memoization

Since converting between beats and seconds always requires knowing the same information, and only altering Tempo Changes has any effect on that, it sometimes makes sense to calculate the arguments required for all other conversions when you receive a timeline and store it for later (at least until receiving a new timeline or altering any Tempo Changes). By storing the (second,beat,BPM) triple from every time the BPM was sampled, you can easily create a lookup for the (second,BPM) at any beat or the (bead,BPM) at any second for all events that altered the tempo, which is everything you need to convert between beats and seconds.