Home > Mobile >  Always go to the closest number compatible with the target song's BPM
Always go to the closest number compatible with the target song's BPM

Time:10-17

A program I'm developing involves a function that adjusts the BPM (beats per minute) of one song to the BPM of another target song.

The issue is, sometimes adjusting the song to the exact BPM of the target song isn't a good idea, as the first song may be sped up or slowed down too much, so it sounds too fast or too slow. In these situations, it makes sense to adjust the BPM of the song to another closer BPM that is still compatible with the target song's BPM.

Here's an example:

BPM of song 1: 150BPM
BPM of target song: 84BPM

In this case, slowing down a 150BPM song to 84BPM isn't very practical - the audio will become too distorted and sound weird. It makes more sense to adjust song 1 to 168BPM - double the BPM of the target song, as this requires significantly less modification to song 1, and will sound more natural.

Looking at the same example switched around:

BPM of song 1: 84BPM
BPM of target song: 150BPM

In this case, it makes sense to slow song 1 down to 75BPM, half of 150, rather than speeding the song all the way up to 150.


In this program, the BPMs of song 1 and the target song differ based on what songs it is given (obviously). What is want this function to do is always go to the closest number compatible with the target BPM - what's the math I need? I'm using Python. I've had a huge headache today making the rest of the whole program.

CodePudding user response:

I think you have all you need. You can figure it out as a human, so put that into code. The problem often is that you don't exactly know what you're doing, because you're too used to it.

Let's get started with a function definition:

def targetbpm(bpmPlaying: int, bpmTarget: int) -> int:
    pass

And the given expected outcomes of the two examples you provided

assert(targetbpm(150, 84) == 168)
assert(targetbpm(84, 150) == 75)

Let's tackle the first assertion. What options are there? As you said, you could go to 84, but it would be better to double that.

def targetbpm(bpmPlaying: int, bpmTarget: int) -> int:
    choices = [bpmTarget, bpmTarget*2]

Which one to choose? The one with the minimum difference to what is currently playing. So let's get these differences and the minimum of these:

    bpmdiff = [abs(bpmPlaying - choice) for choice in choices]
    minimum = min(bpmdiff)

Now let's go back to the original choices and select the corresponding one

    index = bpmdiff.index(minimum)
    return choices[index]

Now you'll find that the second assertion is not passing yet. The reason is that you have another choice here: half the BPM. So let's add that:

    choices = [bpmTarget, bpmTarget*2, bpmTarget // 2]

Problem solved.

CodePudding user response:

This would be one quick way to do it.

def get_BPM_multiple(base, target):
    distance = abs(target - base)
    multiple = 1

    for i in range(2, 8): # 8 or whatever
        if base < target:
            newdist = abs(target - base * i)
        else:
            newdist = abs(target - base / i)

        if newdist < distance:
            distance = newdist
            multiple = i
        else:
            break

    if base < target:
        print(f"Multiply base {base}BPM by {multiple}: {base * multiple}BPM, "
              f"close to target {target}BPM.")
        return base * multiple
    else:
        print(f"Divide base {base}BPM by {multiple}: {base / multiple}BPM, "
              f"close to target {target}BPM.")
        return base / multiple

get_BPM_multiple(base=300, target=84)
# Divide base 300BPM by 4: 75.0BPM, close to target 84BPM.

get_BPM_multiple(base=84, target=150)
# Multiply base 84BPM by 2: 168BPM, close to target 150BPM.
  • Related