Роздрукуйте тексти пісень на "Twinkle Twinkle Little Star"


24

Ваша мета - надрукувати тексти пісні на пісню "Мерехтіння мерехтливої ​​маленької зірки" під час відтворення кожної ноти.

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

Використовуйте подані тут музичні ноти та наступну лірику: (Вертикальні рядки являють собою перерви у складі.)

Близнюк | кле, близнюк | кле, освітлена | зірка тле,

Як я переміг | дер, який ти є.

Вгору | підняти світ так високо,

Як діа | mond у небі.

Близнюк | кле, близнюк | кле, освітлена | зірка тле,

Як я переміг | дер, який ти є.

Запис музики можна знайти тут .

Приклад

Комп'ютер чує середній C і друкує "Twin"

Він чує ще один середній C і друкує "kle"

Потім він чує ще один середній С (неправильна нота) і нічого не робить.

Потім він чує G над середнім С і друкує «близнюк» тощо.

Правила

  • Знаки пунктуації повинні бути такими, як показано.
  • Інтервал повинен бути таким, як показано (з пробілами та новими рядками).
  • Пробіл може бути надрукований разом із попереднім або наступним складом.

2
Чи є якийсь спосіб розслабитися, "має бути надруковано до закінчення нотатки?" З нотами 1/16 секунди, навіть якщо ви присвячуєте 3/4 цього часу вибірці, вам потрібно лише ~ 47 мс звуку для роботи. Це дає досить мутке дозвіл частоти для нот середнього рівня.
Геобіт

@Geobits Хороший момент; Я вилучив це правило.
Ypnypn

1
Це перша головоломка з використанням аудіо входу, яку я міг знайти! Вітаю!
Не те, щоб Чарльз

1
Чи заголовок помилково призначений для розмежування двох блимань?
Rainbolt

1
Чи може ми мати тест на аудіофайл для тестування?
Захоплення Кальвіна

Відповіді:


7

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

(Остання редакція; обіцяю)

Це здається дійсно, досить, дуже важкою проблемою (особливо визнання того, де починаються чи закінчуються ноти). Автоматична транскрипція музики здається відкритою темою дослідження (не те, що я про неї нічого не знаю). Ось ось часткове рішення, яке не робить сегментації примітки (наприклад, воно друкує "Мерехтіння" відразу, коли чує частоту) і, ймовірно, працює лише для цього конкретного файлу 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

Безголівки:

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 - Чудовий; Спасибі! Старі звички важко порушити;
Роберт Фрейзер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.