Превращение частот в ноты в Python

#python #frequency

#python #частота

Вопрос:

Я пытаюсь преобразовать значение частоты в заметку, например, ввод 400 Гц, печатающий «A4», но я не хочу писать полную таблицу частот в своем коде. Есть ли какой-нибудь способ добиться этого?

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

1. См. gist.github.com/CGrassin /…

2. @JAV то, что вы связали, противоположно тому, что задал ОП

3. Спасибо, но этот код преобразует не в частоту. Но я хочу обратного.

4. Точно @assembly_wizard, спасибо

5. @PranavHosangadi хотя это правда, что это короткий вопрос, ИМО это эквивалентно вопросу «как вычисляются частоты нот», поэтому это не имеет смысла для «честной попытки», и здесь нет «алгоритма». вопрос в том, что такое алгоритм

Ответ №1:

На основе функции преобразования, найденной в Википедии:

 import math

def freq_to_note(freq):
    notes = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#']

    note_number = 12 * math.log2(freq / 440)   49  
    note_number = round(note_number)
        
    note = (note_number - 1 ) % len(notes)
    note = notes[note]
    
    octave = (note_number   8 ) // len(notes)
    
    return note, octave
  

Пример: freq_to_note(440) возвращает ('A', 4)

Другим подходом будет использование доступных пакетов.
Вы можете использовать пакет librosa:

 import librosa

librosa.hz_to_note(440.0)
# will return ['A5']
  

Или небольшой пакет, который я написал, называется freq_note_converter:

 import freq_note_converter

freq_note_converter.from_freq(440).note
# will return 'A'
  

Кстати, они оба поддерживают округление, например, 430 или 450 все равно вернут ‘A’.

Ответ №2:

Это вся музыкальная информация, которая вам нужна, чтобы иметь возможность вывести формулу, которая сообщает вам, на сколько полутонов отстоит от A4 данная частота:

  • A4 равен 440 Гц (не 400).
  • Соотношение между частотами любых двух соседних полутонов является постоянным (например, A / Ab и Bb / A дают одинаковое число).
  • Соотношение между частотами двух тонов, которые находятся на расстоянии октавы друг от друга, равно 2.
  • В октаве двенадцать полутонов.

(Последние два пункта позволят вам выяснить, каково постоянное соотношение во втором пункте.)

В качестве альтернативы, вы можете использовать это для написания программы, которая просто «шагает» вверх или вниз от A4, пока не достигнет (или не пройдет) заданной частоты.

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

1. Вы правы @Aasmund Eldhuset, я могу использовать (for…in …) функция для поиска точной ноты. Но давайте предположим, что мы записали частоту 440 во входных данных. Как программа узнает, что это примечание A4

2. Есть ли какая-либо библиотека, которая может преобразовывать частоту в ноту и его октаву.

3. @Miles: вы можете подсчитать количество итераций цикла, необходимых для достижения (или передачи) входной частоты с фиксированной начальной точки (например, 440 Гц). Если число больше или равно 12 (или меньше или равно -12), вы перескочили больше, чем на октаву, поэтому вы можете разделить на 12, чтобы выяснить, на сколько октав от вашего начального тона (предположительно A4) вы находитесь, а затем использовать остаток для поискав списке, который содержит все названия тонов.

Ответ №3:

В теории музыки обычное определение составляет 12 нот в каждой октаве, повышение на октаву удваивает частоту, а A4 определяется как 440 Гц. Также важно отметить, что ноты распределяются равномерно по октаве.

Используя это определение, мы можем написать функцию, которая при задании частоты возвращает ноту и октаву.

Поскольку переход от A4 к A5 умножается на 2, а нам нужно, чтобы ноты распределялись равномерно, это означает, что движение вверх по ноте должно проходить ровно 12-ю часть пути до удвоения, поэтому переход от A4 к B4 должен умножить частоту на 12-й корень из 2 ( 2**(1/12) ) .

Написать такую функцию нетривиально, но и не сложно. Я подумал, что, хотя иногда лучше не давать само решение для обучения, в этом случае мне лучше показать решение и объяснить каждую часть.

 import math

def frequency_to_note(frequency):
    # define constants that control the algorithm
    NOTES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] # these are the 12 notes in each octave
    OCTAVE_MULTIPLIER = 2 # going up an octave multiplies by 2
    KNOWN_NOTE_NAME, KNOWN_NOTE_OCTAVE, KNOWN_NOTE_FREQUENCY = ('A', 4, 440) # A4 = 440 Hz

    # calculate the distance to the known note
    # since notes are spread evenly, going up a note will multiply by a constant
    # so we can use log to know how many times a frequency was multiplied to get from the known note to our note
    # this will give a positive integer value for notes higher than the known note, and a negative value for notes lower than it (and zero for the same note)
    note_multiplier = OCTAVE_MULTIPLIER**(1/len(NOTES))
    frequency_relative_to_known_note = frequency / KNOWN_NOTE_FREQUENCY
    distance_from_known_note = math.log(frequency_relative_to_known_note, note_multiplier)

    # round to make up for floating point inaccuracies
    distance_from_known_note = round(distance_from_known_note)

    # using the distance in notes and the octave and name of the known note,
    # we can calculate the octave and name of our note
    # NOTE: the "absolute index" doesn't have any actual meaning, since it doesn't care what its zero point is. it is just useful for calculation
    known_note_index_in_octave = NOTES.index(KNOWN_NOTE_NAME)
    known_note_absolute_index = KNOWN_NOTE_OCTAVE * len(NOTES)   known_note_index_in_octave
    note_absolute_index = known_note_absolute_index   distance_from_known_note
    note_octave, note_index_in_octave = note_absolute_index // len(NOTES), note_absolute_index % len(NOTES)
    note_name = NOTES[note_index_in_octave]
    return (note_name, note_octave)
  

Итак, теперь frequency_to_note(440) возвращает ('A', 4) и frequency_to_note(740) возвращает ('F#', 5) , что кажется правильным.

Однако важно отметить, что эта функция не заботится о том, какие октавы имеют смысл, поэтому frequency_to_note(1) возвращается что-то вроде ('C', -4) , потому что, если бы у нас действительно было пианино, которое обычно использует октавы 1-7, и добавило еще 5 октав влево, нота C в этой самой левой октаве действительно была бы 1 Гц. Поэтому, в зависимости от вашего варианта использования, вы можете захотеть вызвать исключение в конце, если октава не находится между 1 и 7.