Imprimer les paroles de "Twinkle Twinkle Little Star"

24

Votre objectif est d'imprimer les paroles de la chanson "Twinkle Twinkle Little Star" au fur et à mesure que chaque note est jouée.

Le microphone de l'ordinateur entendra les notes. Si la hauteur (mais pas nécessairement la longueur) de la note est correcte, imprimez la syllabe appropriée. Sinon, ne faites rien. Chaque note durera au moins une demi-seconde et il y aura une pause d'au moins un quart de seconde entre les notes.

Utilisez les notes de musique fournies ici et les paroles suivantes: (Les lignes verticales représentent les sauts de syllabe.)

Twin | kle, twin | kle, lit | tle star,

Comment j'ai gagné | der ce que tu es.

Au-dessus du monde si haut,

Comme un diamant dans le ciel.

Twin | kle, twin | kle, lit | tle star,

Comment j'ai gagné | der ce que tu es.

Un enregistrement de la musique peut être trouvé ici .

Exemple

L'ordinateur entend un C du milieu et imprime "Twin"

Il entend un autre C moyen et imprime "kle"

Ensuite, il entend un autre do du milieu (mauvaise note) et ne fait rien.

Ensuite, il entend le G au-dessus du milieu C et imprime "twin" et ainsi de suite.

Règles

  • La ponctuation doit être comme indiqué.
  • L'espacement doit être comme indiqué (avec des espaces et des retours à la ligne).
  • L'espace peut être imprimé avec la syllabe précédente ou suivante.
Ypnypn
la source
2
Existe-t-il un moyen de se détendre "doit être imprimé avant la fin de la note?" Avec des notes de 1/16 seconde, même si vous consacrez les 3/4 de ce temps à l'échantillonnage, vous n'avez que ~ 47 ms de son pour travailler. Cela donne une résolution de fréquence assez trouble pour les notes de milieu de gamme.
Geobits
@Geobits Bon point; J'ai supprimé cette règle.
Ypnypn
1
Ceci est le premier puzzle utilisant une entrée audio que j'ai pu trouver! Félicitations!
Pas que Charles
1
Le titre est-il mal orthographié exprès pour différencier les deux scintillements?
Rainbolt
1
Pourrions-nous avoir un lien vers un fichier audio pour les tests?
Calvin's Hobbies

Réponses:

7

Python 3 - Solution partielle ( 760 742 734 710 705 657 caractères)

(Dernière modification; je le promets)

Cela semble être un problème vraiment, assez, très difficile (en particulier reconnaître le début ou la fin des notes). La transcription automatique de la musique semble être un sujet de recherche ouvert (pas que je sache quoi que ce soit à ce sujet). Voici donc une solution partielle qui ne fait aucune segmentation de note (par exemple, elle imprime "Twinkle" en une seule fois quand elle entend la fréquence) et ne fonctionne probablement que pour ce fichier ogg spécifique:

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

Cela nécessite...

Modifiez A = -52 (amplitude minimale) sur la ligne supérieure en fonction de votre microphone, de la quantité de son ambiant, de la puissance de la chanson, etc. Sur mon microphone, moins de -57 semble capter beaucoup de bruit étranger et plus de -49 vous oblige à jouer très fort.

Cela pourrait être beaucoup plus joué au golf; Je suis sûr qu'il existe des moyens d'enregistrer un tas de caractères sur le tableau de mots en particulier. Ceci est mon premier programme non trivial en python, donc je ne connais pas encore très bien le langage.

J'ai volé le code de détection de fréquence via l'autocorrélation sur https://gist.github.com/endolith/255291

Non golfé:

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
la source
J'ai écrit une petite aide de syntaxe pour vous. Vérifiez les lignes 14-29 et 80-88. pastebin.com/W9XSYwMJ
seequ
@Sieg - Génial; Merci! Les vieilles habitudes sont difficiles à briser;
Robert Fraser