#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))
Спасибо тебе, Дэвид!