Wydrukuj tekst „Twinkle Twinkle Little Star”

24

Twoim celem jest wydrukowanie tekstu piosenki „Twinkle Twinkle Little Star” podczas odtwarzania każdej nuty.

Mikrofon komputera usłyszy notatki. Jeśli wysokość (ale niekoniecznie długość) nuty jest poprawna, wydrukuj odpowiednią sylabę. W przeciwnym razie nie rób nic. Każda nuta będzie miała co najmniej pół sekundy długości, a przerwa między nutami będzie wynosić co najmniej ćwierć sekundy.

Skorzystaj z podanych tutaj nut i następujących tekstów: (Pionowe linie oznaczają przerwy w sylabie).

Twin | kle, twin | kle, lit | tle gwiazda,

Jak wygrałem | der kim jesteś.

W górę | pokonał świat tak wysoko,

Jak dia | mon na niebie.

Twin | kle, twin | kle, lit | tle gwiazda,

Jak wygrałem | der kim jesteś.

Nagranie z muzyką można znaleźć tutaj .

Przykład

Komputer słyszy środkowe C i drukuje „Twin”

Słyszy kolejne środkowe C i drukuje „kle”

Następnie słyszy kolejne środkowe C (zła nuta) i nic nie robi.

Następnie słyszy G powyżej środkowego C i drukuje „twin” i tak dalej.

Zasady

  • Interpunkcja musi być taka, jak pokazano.
  • Odstępy muszą być takie, jak pokazano (ze spacjami i znakami nowej linii).
  • Biała spacja może być wydrukowana wraz z poprzednią lub następną sylabą.
Ypnypn
źródło
2
Czy istnieje sposób na relaks „należy wydrukować przed końcem notatki?” Przy nutach 1/16 sekundy, nawet jeśli poświęcasz 3/4 tego czasu na próbkowanie, masz tylko ~ 47 ms dźwięku do pracy. Daje to dość mętną rozdzielczość częstotliwości dla nut ze średniego zakresu.
Geobits
@Geobits Dobra uwaga; Usunąłem tę zasadę.
Ypnypn
1
To pierwsza zagadka z wejściem audio, którą udało mi się znaleźć! Gratulacje!
Nie to, że Karol
1
Czy tytuł celowo wypaczył, aby rozróżnić dwa błyski?
Rainbolt
1
Czy możemy mieć link do pliku audio do testowania?
Calvin's Hobbies

Odpowiedzi:

7

Python 3 - Częściowe rozwiązanie ( 760 742 734 710 705 657 znaków)

(Ostatnia edycja; obiecuję)

Wydaje się, że to naprawdę bardzo ładny, bardzo trudny problem (zwłaszcza rozpoznawanie, gdzie nuty zaczynają się i kończą). Automatyczna transkrypcja muzyki wydaje się być otwartym tematem badawczym (ale nie o tym nic nie wiem). Oto częściowe rozwiązanie, które nie dokonuje segmentacji nut (np. Drukuje „Twinkle” naraz, gdy słyszy częstotliwość) i prawdopodobnie działa tylko dla tego konkretnego pliku 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

To wymaga...

Zmień A = -52 (minimalna amplituda) w górnym wierszu w zależności od mikrofonu, ilości dźwięku otoczenia, głośności odtwarzanej piosenki itp. Na moim mikrofonie wydaje się, że mniej niż -57 odbiera dużo obcych dźwięków a więcej niż -49 wymaga grania bardzo głośno.

To może być gra w golfa dużo więcej; Jestem pewien, że istnieją sposoby na zapisanie wielu znaków w tablicy słów. To jest mój pierwszy nietrywialny program w Pythonie, więc nie znam jeszcze języka.

Ukradłem kod do wykrywania częstotliwości za pomocą autokorelacji z https://gist.github.com/endolith/255291

Nie golfowany:

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
Robert Fraser
źródło
Napisałem dla ciebie małą pomoc dotyczącą składni. Sprawdź linie 14–29 i 80–88. pastebin.com/W9XSYwMJ
patrz
@Sieg - Awesome; dzięki! Stare nawyki trudno przełamać;
Robert Fraser