Stampa il testo di "Twinkle Twinkle Little Star"


24

Il tuo obiettivo è stampare il testo della canzone "Twinkle Twinkle Little Star" mentre ogni nota viene suonata.

Il microfono del computer sentirà le note. Se l'altezza (ma non necessariamente la lunghezza) della nota è corretta, stampare la sillaba appropriata. Altrimenti, non fare nulla. Ogni nota avrà una durata di almeno mezzo secondo e ci sarà un'interruzione di almeno un quarto di secondo tra le note.

Utilizzare le note musicali fornite qui e i seguenti testi: (Le linee verticali rappresentano interruzioni di sillabe.)

Twin | kle, twin | kle, acceso | tle star,

Come ho vinto | der quello che sei.

Fino a | bove il mondo così in alto,

Come un dia | mond nel cielo.

Twin | kle, twin | kle, acceso | tle star,

Come ho vinto | der quello che sei.

Una registrazione della musica può essere trovata qui .

Esempio

Il computer sente una C centrale e stampa "Twin"

Sente un altro C centrale e stampa "kle"

Quindi sente un'altra C centrale (nota sbagliata) e non fa nulla.

Quindi sente la G sopra la C centrale e stampa "gemello" e così via.

Regole

  • La punteggiatura deve essere come mostrato.
  • La spaziatura deve essere come mostrato (con spazi e newline).
  • Lo spazio bianco può essere stampato insieme alla sillaba precedente o successiva.

2
C'è un modo per rilassarsi "deve essere stampato prima che la nota finisca?" Con note di 1/16 di secondo, anche se dedichi 3/4 di quel tempo al campionamento, hai solo ~ 47ms di suono con cui lavorare. Ciò fornisce una risoluzione di frequenza piuttosto torbida per le note di gamma media.
Geobits,

@Geobits Un buon punto; Ho rimosso quella regola.
Ypnypn,

1
Questo è il primo puzzle che utilizza l'ingresso audio che ho trovato! Congratulazioni!
Non che Charles

1
Il titolo è stato scritto erroneamente per differenziare i due scintillanti?
Rainbolt,

1
Potremmo avere un collegamento a un file audio per il test?
Hobby di Calvin

Risposte:


7

Python 3 - Soluzione parziale ( 760 742 734 710 705 657 caratteri)

(Ultima modifica; lo prometto)

Questo sembra un problema davvero, molto, molto difficile (soprattutto riconoscendo dove iniziano o finiscono le note). La trascrizione automatica della musica sembra un argomento di ricerca aperto (non che io ne sappia nulla). Quindi, ecco una soluzione parziale che non esegue alcuna segmentazione delle note (ad esempio, stampa "Scintillio" tutto in una volta quando sente la frequenza) e probabilmente funziona solo per quel file ogg specifico:

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

Questo richiede...

Cambia A = -52 (ampiezza minima) sulla riga superiore in base al microfono, alla quantità di suono ambientale, alla potenza del brano in riproduzione, ecc. Sul mio microfono, meno di -57 sembra raccogliere molto rumore estraneo e più di -49 ti richiedono di suonare molto forte.

Questo potrebbe essere giocato molto di più; Sono sicuro che ci sono modi per salvare un sacco di personaggi sull'array di parole in particolare. Questo è il mio primo programma non banale in Python, quindi non ho ancora molta familiarità con la lingua.

Ho rubato il codice per il rilevamento della frequenza tramite autocorrelazione da 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

Ho scritto un piccolo aiuto per la sintassi per te. Controllare le linee 14-29 e 80-88. pastebin.com/W9XSYwMJ
seequ,

@Sieg - Fantastico; Grazie! Le vecchie abitudini sono difficili da rompere;
Robert Fraser,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.