Распечатать текст песни «Twinkle Twinkle Little Star»

24

Ваша цель - напечатать текст песни «Twinkle Twinkle Little Star», когда звучит каждая нота.

Микрофон компьютера будет слышать ноты. Если высота (но не обязательно длина) ноты правильна, выведите соответствующий слог. В противном случае ничего не делать. Каждая нота будет длиться не менее полсекунды, а между нотами будет перерыв не менее четверти секунды.

Используйте представленные здесь музыкальные ноты и следующий текст: (Вертикальные линии представляют разрывы слогов.)

Твин | Кле, Твин | Кле, освещенная звезда,

Как я выиграл, кто ты.

Вверх над миром,

Как бриллиант в небе.

Твин | Кле, Твин | Кле, освещенная звезда,

Как я выиграл, кто ты.

Запись музыки можно найти здесь .

пример

Компьютер слышит средний С и печатает "Твин"

Он слышит еще один средний C и печатает "kle"

Затем он слышит другую среднюю букву С (неправильная нота) и ничего не делает.

Затем он слышит G выше среднего C и печатает «близнец» и так далее.

правила

  • Пунктуация должна быть такой, как показано.
  • Интервал должен быть таким, как показано (с пробелами и символами новой строки).
  • Пробел может быть напечатан вместе с предыдущим или следующим слогом.
Ypnypn
источник
2
Есть ли способ расслабиться "должен быть напечатан до окончания заметки?" С нотами 1/16 секунды, даже если вы посвятите 3/4 этого времени сэмплингу, у вас будет только ~ 47 мс звука для работы. Это дает довольно мутное разрешение по частоте для нот среднего уровня.
Geobits
@Geobits Хороший вопрос; Я удалил это правило.
Ypnypn
1
Это первая головоломка с использованием аудио входа, которую я смог найти! Congrats!
Не то чтобы Чарльз
1
Название неправильно введено с целью разграничить два блеска?
Рейнболт
1
Можем ли мы получить ссылку на аудиофайл для тестирования?
Увлечения Кэлвина

Ответы:

7

Python 3 - Частичное решение ( 760 742 734 710 705 657 символов)

(Последнее редактирование; я обещаю)

Это кажется очень, очень, очень трудной проблемой (особенно с учетом того, где начинаются или заканчиваются ноты). Автоматическая транскрипция музыки кажется открытой темой исследования (не то, чтобы я что-то знал об этом). Итак, вот частичное решение, которое не выполняет никакой сегментации нот (например, оно печатает «Twinkle» сразу, когда слышит частоту) и, вероятно, работает только для этого конкретного файла ogg:

A=-52
F=44100
C=4096
import pyaudio as P
import array
import scipy.signal as G
import numpy as N
import math
L=math.log
i=0
j=[9,2,0,2,4,5,7,9]
k=[2,4,5,7]
n=j+k+k+j
w="Twinkle, |twinkle, |little |star,\n|How I |wonder |what you |are.\n|Up a|bove the |world so |high,\n|Like a |diamond |in the |sky.\n".split('|')
w+=w[:8]
e=P.PyAudio().open(F,1,8,1,0,None,0,C)
while i<24:
 g=array.array('h',e.read(C));b=sum(map(abs,g))/C
 if b>0 and 20*L(b/32768,10)>A:
  f=G.fftconvolve(g,g[::-1])[C:];d=N.diff(f);s=0
  while d[s]<=0:s+=1
  x=N.argmax(f[s:])+s;u=f[x-1];v=f[x+1]
  if int(12*L(((u-v)/2/(u-2*f[x]+v)+x)*F/C/440,2))==n[i]+15:print(w[i],end='',flush=1);i+=1

Это требует...

Измените значение A = -52 (минимальная амплитуда) в верхней строке в зависимости от вашего микрофона, количества окружающего звука, громкости песни и т. Д. На моем микрофоне менее -57, похоже, воспринимает много посторонних шумов. и более -49 требует от вас играть очень громко.

Это может быть намного больше в гольфе; Я уверен, что есть способы сохранить кучу символов в массиве слов, в частности. Это моя первая нетривиальная программа на python, поэтому я пока не очень знаком с языком.

Я украл код для определения частоты с помощью автокорреляции из https://gist.github.com/endolith/255291

Ungolfed:

import pyaudio
from array import array
import scipy.signal
import numpy
import math
import sys

MIN_AMPLITUDE = -52
FRAMERATE = 44100

def first(list):
    for i in range(len(list)):
        if(list[i] > 0):
            return i
    return 0

# Based on: https://en.wikipedia.org/wiki/Decibel#Acoustics
def getAmplitude(sig):
    total = 0;
    elems = float(len(sig))
    for x in sig:
        total += numpy.abs(x) / elems
    if(total == 0):
        return -99
    else:
        return 20 * math.log(total / 32768., 10)    

# Based on: https://en.wikipedia.org/wiki/Piano_key_frequencies
def getNote(freq):
    return int(12 * math.log(freq / 440, 2) + 49)

# --------------------------------------------------------------------------
# This is stolen straight from here w/ very slight modifications: https://gist.github.com/endolith/255291
def parabolic(f, x):
    return 1/2. * (f[x-1] - f[x+1]) / (f[x-1] - 2 * f[x] + f[x+1]) + x

def getFrequency(sig):
    # Calculate autocorrelation (same thing as convolution, but with
    # one input reversed in time), and throw away the negative lags
    corr = scipy.signal.fftconvolve(sig, sig[::-1], mode='full')
    corr = corr[len(corr)/2:]

    # Find the first low point
    diffs = numpy.diff(corr)

    # Find the next peak after the low point (other than 0 lag). This bit is
    # not reliable for long signals, due to the desired peak occurring between
    # samples, and other peaks appearing higher.
    # Should use a weighting function to de-emphasize the peaks at longer lags.
    start = first(diffs)
    peak = numpy.argmax(corr[start:]) + start
    return parabolic(corr, peak) * (FRAMERATE / len(sig))
# --------------------------------------------------------------------------

# These are the wrong keys (ie it is detecting middle C as an A), but I'm far too lazy to figure out why.
# Anyway, these are what are detected from the Wikipedia .ogg file:
notes = [73,          66,           64,       66,         68,       69,        71,          73,       66,     68,          69,         71,         66,        68,         69,        71      ] 
words = ["Twinkle, ", "twinkle, ", "little ", "star,\n",  "How I ", "wonder ", "what you ", "are.\n", "Up a", "bove the ", "world so ", "high,\n", "Like a ", "diamond ", "in the ", "sky.\n"]
notes += notes[:8]
words += words[:8]

pa = pyaudio.PyAudio()
stream = pa.open(format=pyaudio.paInt16, channels = 1, rate = FRAMERATE, input = True, frames_per_buffer = 4096)
idx = 0
while(idx < len(notes)):
    # Read signal
    sig = array('h', stream.read(4096))
    if(getAmplitude(sig) > MIN_AMPLITUDE):
        note = getNote(getFrequency(sig))
        if(note == notes[idx]):
            sys.stdout.write(words[idx])
            sys.stdout.flush()
            idx += 1
Роберт Фрейзер
источник
Я написал небольшую справку по синтаксису для вас. Проверьте строки 14-29 и 80-88. pastebin.com/W9XSYwMJ
seequ
@Sieg - Потрясающе; Благодарность! Старые привычки трудно сломать;
Роберт Фрейзер,