Вычислить результирующую частоту из заданного гармонического узла или нескольких узлов, округленную до ближайшей гармоники меньше n

#python #algorithm #math #signal-processing #physics

#python #алгоритм #математика #обработка сигналов #физика

Вопрос:

Я работаю над инструментом музыкальной композиции, и мне нужно выяснить, какой будет результирующая частота, если я коснусь (создам гармонический узел) некоторой произвольной строки в одном или нескольких местах.

Эти позиции должны быть заданы в виде миди-шагов. Итак, если бы мне нужно было создать гармонику с основой среднего C и коснуться ее на 5-м месте выше (G), это было бы написано так;

 Harmonic(60, 67) #3rd harmonic
 

который я ожидал бы вернуть 79, учитывая:

 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 не проблема — этот алгоритм войдет в инициализатор и установит элемент self.sounding -, и я бы с радостью получил ответ — псевдокод! Также не имеет значения, является ли возвращаемый сигнал midi-шагом или частотой, ему просто нужно взять корневой шаг и один или несколько узлов (ограничено двумя, если это проще).

Кроме того, я надеюсь, что я не пометил этот вопрос!

Ответ №1:

Работает ли это? Мои воспоминания о задействованной физике довольно смутны, но в основном мы находим самую низкую высоту звука, которая является общим обертоном всех заданных 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))
 

Комментарии:

1. Спасибо! Я опубликовал слегка измененную версию этого, чтобы учесть тот факт, что затронутые узлы не являются точными и будут округляться до ближайшего. Это и пальцы бесконечно маленькие!

Ответ №2:

Отправка ответа на собственный вопрос. Основываясь на ответе Дэвида Эйзенштата, принимая во внимание, что инструмент будет слегка перемещать узлы, чтобы найти самую низкую гармонику:

 import math                                               
    
    
def freq_from_midi(m):    
    return 440 * 2 ** ((m - 69) / 12)    
    
    
def nearest_midi_from_freq(f):    
    return  round( 2 * (12 * math.log2(f / 440)   69 )) / 2    
    
    
def midi_overtones(m):    
    f = freq_from_midi(m)    
    return { nearest_midi_from_freq(h * f) for h in range(1, 25)}    
    
    
def midi_overtones_flexible(m):    
    float_range = lambda start, stop, step: [start   step*r for r in range(0,int((stop - start)/step))]    
    
    freqs = (freq_from_midi(m   r) for r in float_range(-0.5, 0.5, 0.125) )    
    
    return { nearest_midi_from_freq(h * f) for f in freqs for h in range(1, 25)}    
    
    
def harmonic(base, *nodes):    
    overtones_of_base = midi_overtones(base)    
    overtones_of_nodes = [midi_overtones_flexible(m) for m in nodes]    
    r = set.intersection( overtones_of_base, *overtones_of_nodes )    
    return min(r)    
    
    
print(harmonic(60, 64, 66))  
print(harmonic(60, 65, 67))    
 

Спасибо тебе, Дэвид!