首页 > 解决方案 > 计算来自给定谐波节点或多个节点的结果频率,四舍五入到小于 n 的最接近谐波

问题描述

我正在开发一种音乐创作工具,如果我在一个或多个位置触摸(创建一个谐波节点)某个任意音高的弦,我需要弄清楚产生的频率是多少。

这些位置需要以 Midi 音高的形式给出。因此,如果我需要创建一个以中间 C 为基频的谐波并在上面的第 5 个(G)处触摸它,可以这样写;

Harmonic(60, 67) #3rd harmonic

鉴于:

def toFreq(m):
   return pow(2, (m-69)/12)
def toMidi(f):
   return 12 * math.log2( f/440 ) + 69

到目前为止,我已经能够实现这一点。

但是,如果我这样做:

Harmonic(60, 64) #5th harmonic

我得到了错误的答案,这是因为我想“四舍五入”下面的谐波 - 比如说 - 20 次谐波以反映真实的乐器。通过限制 a 的分母,我已经取得了一些成功Fraction。但是,如果我使用来自同一谐波的更高节点,这将分崩离析:

Harmonic(60, 69) # also the 5th harmonic, just a different node

我遇到的第二个问题,我没有成功地让它工作,是有多个这样的节点。

Harmonic(60, [67, 65]) # where a 'C' string is touched at the 5th and 4th
# this is the 9th harmonic ( a compound major 2nd )

我想知道是否有人可以帮助这里的算法?实际上的类和 python 的东西是没有问题的——这个算法会进入 initaliser 并设置一个成员self.sounding——我很乐意得到一个答案是伪代码!如果返回是 midi 音高或频率也没关系,它只需要一个根音高和一个或多个节点(如果更容易,限制为两个)。

另外,我希望我没有过度标记这个问题!

标签: pythonalgorithmmathsignal-processingphysics

解决方案


这行得通吗?我对所涉及物理的记忆相当模糊,但基本上,我们找到了所有给定 MIDI 音高的共同泛音的最低音高。

import math


def freq_from_midi(m):
    return 440 * 2 ** ((m - 69) / 12)


def nearest_midi_from_freq(f):
    return round(12 * math.log2(f / 440) + 69)


def midi_overtones(m):
    f = freq_from_midi(m)
    return {nearest_midi_from_freq(h * f) for h in range(1, 21)}


def harmonic(*ms):
    return min(set.intersection(*(midi_overtones(m) for m in ms)))


print(harmonic(60, 67))
print(harmonic(60, 64))
print(harmonic(60, 69))
print(harmonic(60, 67, 65))

推荐阅读