Drucken Sie die Texte zu "Twinkle Twinkle Little Star"

24

Ihr Ziel ist es, die Texte zum Lied "Twinkle Twinkle Little Star" zu drucken, während jede Note gespielt wird.

Das Mikrofon des Computers hört Notizen. Wenn die Tonhöhe (aber nicht unbedingt die Länge) der Note korrekt ist, drucken Sie die entsprechende Silbe. Sonst nichts tun. Jede Note ist mindestens eine halbe Sekunde lang, und zwischen den Noten liegt eine Pause von mindestens einer Viertelsekunde.

Verwenden Sie die hier angegebenen Noten und den folgenden Text: (Vertikale Linien stehen für Silbenumbrüche.)

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

Wie ich gewinne, was du bist.

Hoch über der Welt,

Wie ein Mond am Himmel.

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

Wie ich gewinne, was du bist.

Eine Aufnahme der Musik finden Sie hier .

Beispiel

Der Computer hört ein mittleres C und druckt "Twin"

Es hört ein weiteres mittleres C und gibt "kle" aus.

Dann hört es ein weiteres mittleres C (falsche Note) und tut nichts.

Dann hört es das G über dem mittleren C und druckt "Zwilling" und so weiter.

Regeln

  • Die Zeichensetzung muss wie gezeigt sein.
  • Der Abstand muss wie gezeigt sein (mit Leerzeichen und Zeilenumbrüchen).
  • Das Leerzeichen kann zusammen mit der vorherigen oder der nächsten Silbe gedruckt werden.
Ypnypn
quelle
2
Gibt es eine Möglichkeit zum Entspannen "muss gedruckt werden, bevor die Notiz endet?" Bei einer Note von 1/16 Sekunde haben Sie, auch wenn Sie 3/4 dieser Zeit für das Sampling verwenden, nur ~ 47 ms Sound zur Verfügung. Das ergibt eine ziemlich trübe Frequenzauflösung für Noten im mittleren Bereich.
Geobits
@ Geobits Guter Punkt; Ich habe diese Regel entfernt.
Ypnypn
1
Dies ist das erste Puzzle mit Audio-Eingang, das ich finden konnte! Glückwunsch!
Nicht dass Charles
1
Ist der Titel absichtlich falsch geschrieben, um die beiden Blitze zu unterscheiden?
Rainbolt
1
Könnten wir einen Link zu einer Audiodatei zum Testen haben?
Calvins Hobbys

Antworten:

7

Python 3 - Teillösung ( 760 742 734 710 705 657 Zeichen)

(Letzte Änderung; Ich verspreche es)

Dies scheint ein wirklich, hübsches, sehr schwieriges Problem zu sein (insbesondere das Erkennen, wo die Noten beginnen oder enden). Die automatische Transkription von Musik scheint ein offenes Forschungsthema zu sein (nicht, dass ich etwas darüber weiß). Hier ist eine Teillösung, die keine Notensegmentierung vornimmt (z. B. "Twinkle" auf einmal ausgibt, wenn die Frequenz abgehört wird) und wahrscheinlich nur für diese bestimmte ogg-Datei funktioniert:

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

Dafür braucht man...

Ändern Sie die A = -52 (minimale Amplitude) in der oberen Zeile abhängig von Ihrem Mikrofon, der Lautstärke des Umgebungsgeräuschs, der Lautstärke des Songs usw. Bei meinem Mikrofon scheint weniger als -57 viel Fremdgeräusch aufzunehmen und mehr als -49 erfordert, dass Sie es sehr laut spielen.

Dies könnte viel mehr golfen werden; Ich bin mir sicher, dass es Möglichkeiten gibt, eine Reihe von Zeichen im Wortfeld zu speichern. Dies ist mein erstes nicht-triviales Programm in Python, daher bin ich mit der Sprache noch nicht sehr vertraut.

Ich habe den Code für die Frequenzerkennung per Autokorrelation von https://gist.github.com/endolith/255291 gestohlen

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
Robert Fraser
quelle
Ich habe eine kleine Syntaxhilfe für Sie geschrieben. Überprüfen Sie die Zeilen 14-29 und 80-88. pastebin.com/W9XSYwMJ
seequ
@ Sieg - Super; Vielen Dank! Alte Gewohnheiten sind schwer zu brechen;
Robert Fraser