1

I am writing a music creation program where the user is allowed to change the tempo throughout the track.

If the user had a set tempo or only changed the tempo at discrete intervals I could easily calculate the time by taking the tempo, and the beat range occupied by that tempo, and dividing the range by the beats per second tempo value. If the tempo changed at some point I would apply the same thing to the next part and add that time on to the first part.

However, I'm struggling to wrap my head around how I would do this with interpolated tempo values. Say for the example the user wants a linear interpolated increase from beat 1.0 to beat 3.0 from 120 BPM to 140 BPM. How do I calculate exactly how long a linear change in tempo would take?

I know that at a steady 120 BPM (2 BPS), this would take a second. At 140 BPM (2.33 BPS) it would take just under that. But how do I work out how long it would take with a linear increase in tempo during that time from the first value to the second?

And what if I was to allow for different interpolation methods, such as some sort of fade in or fade out interpolation? Would things become more complicated?

shnudm
  • 13

1 Answers1

2

The short answer: to arrive at a formula, write your tempo as a function of song location (e.g. beat number) and perform a definite integral of its multiplicative inverse.

A concrete example:

To make the math a little easier, let's number the beats of your whole song starting at $0$. Beat $0$ is the place in your song just before the very first bar. Beat $4$ would be the spot in between the first and second measure (if it's in 4/4, any way). Beat $8$ would be between the second and third measures, etc.

Let's write down a function, $T(b)$, for the tempo at beat number $b$. In your example, $T(0)$ is 120 beats per minute and $T(2)$ is 140 beats per minute. You said you wanted it linearly interpolated. I'll take that to mean "linearly over the beats" (as opposed to linearly over time). So between beats $0$ and $2$: $$ T(b) = 120 + 10b $$ This function takes a beat number, $b$, and tells you the tempo in beats per minute. The function $1/T(b)$ is "minutes per beat" at time $b$.

So to calculate how much time passes, compute this integral: $$ \begin{align} \int_{0}^{2} \frac 1 {T(b)} db &= \int_{0}^{2} \frac 1 {120 + 10b} db \\ &= \frac 1 {10} \log (12+b) \bigg|_0^2 \\ &\approx 0.0154 \end{align} $$ Since $T(b)$ uses time units of minutes, that means those two beats take 0.0154 minutes, or about 0.925 seconds.

More generally, say the tempo at beat $b_0$ is $T_0$ and the tempo at beat $b_1$ is $T_1$. Then between $b_0$ and $b_1$ the tempo value can be linearly interpolated as $$ T(b) = T_0 + \frac {T_1 - T_0} {b_1 - b_0} (b - b_0) $$ So the time that elapses between $b_0$ and $b_1$ is: $$ \begin{align} \int_{b_0}^{b_1} \frac 1 {T(b)} db &= \int_{b_0}^{b_1} \frac 1 {T_0 + \frac {T_1 - T_0} {b_1 - b_0} (b - b_0)} db \\ &= \frac {b_1-b_0} {T_1-T_0} \log (T_0 + \frac {T_1 - T_0} {b_1 - b_0} (b - b_0)) \bigg|_{b_0}^{b_1} \\ &= \frac {b_1-b_0} {T_1-T_0} \log \left(\frac{T_1}{T_0}\right) \end{align} $$ So just plug in $b = b_1$ and $b = b_0$ into that last expression, subtract, and you'll have the time duration (in minutes).

If you wanted different interpolation methods, it would just change what the function $T(b)$ looks like. This will in turn change the difficulty of performing the integral. But the general idea will remain the same.

csd
  • 273
  • Hi! I have the same problem, and this seems to be the answer. I need to translate it into programming code, and I have no problem with the second last formula on the page. Example: beatTempo = tempo0 + ((tempo1 - tempo0) / (beat1 - beat0)) * (beat - beat0); However, I have no success with the last formula on the page. Any ideas how to translate that one into java-style programming code? – Cambiata Dec 11 '14 at 21:59
  • (Edit: I missed a comment here before posting...) – Cambiata Dec 12 '14 at 19:04
  • @Hristo: Thanks, but in your edit of csd's answer - the last formula - the b (beat) seems to be missing. That's the value that represents the interpolation between b0 and b1 - I can't see how this can be reduced from the formula. Am I missing something? – Cambiata Dec 12 '14 at 20:56
  • @Cambiata, nothing is missing. The expression on the left side of the vertical bar is the indefinite integral - itself a function of b - which is then evaluated at the upper bound (b_1) and at the lower bound (b_0) and both values are subtracted (that's the meaning of the vertical bar). The result does not (and should not) depend on b. – Hristo Iliev Dec 12 '14 at 21:02
  • @Hristo: Ah, I think I get it. But I still can't get working code out of it: This is my attempt for measuring the time of one beat accellerating from 60 to 120 bpm: tempo0 = 60; tempo1 = 120; beat0 = 0; beat1 = 1; duration = ((beat1 - beat0) / (tempo1 / tempo0)) * Math.log(tempo1 / tempo0); The duration result that I get out of this is 0.34657... wich doesn't seam reasonable - not in sec nor min units. Clue? – Cambiata Dec 12 '14 at 22:51
  • @Cambiata, you've replaced subtraction with division in the second term: (tempo1 / tempo0) should be (tempo1 - tempo0). It gives 0.012 minutes (= 0.7 seconds) as a result, which looks fine (at least it lies somewhere between 0.5 seconds (120 bpm) and 1 second (60 bpm)) – Hristo Iliev Dec 12 '14 at 23:29
  • Thank you, Hristo! Much appreciated! I have a working class for this now: https://gist.github.com/cambiata/732b5cac984b310cb239 – Cambiata Dec 13 '14 at 11:36