Cetak lirik untuk “Twinkle Twinkle Little Star”

24

Tujuan Anda adalah mencetak lirik lagu "Twinkle Twinkle Little Star" saat setiap not dimainkan.

Mikrofon komputer akan mendengar catatan. Jika nada (tetapi tidak harus panjang) dari catatan itu benar, cetak suku kata yang sesuai. Kalau tidak, jangan lakukan apa pun. Setiap not akan memiliki setidaknya setengah detik, dan akan ada jeda setidaknya seperempat detik di antara not.

Gunakan catatan musik yang disediakan di sini , dan lirik berikut: (Garis vertikal mewakili jeda suku kata.)

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

Bagaimana saya memenangkan apa pun Anda.

Naik tinggi dunia,

Seperti dia | mond di langit.

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

Bagaimana saya memenangkan apa pun Anda.

Rekaman musik dapat ditemukan di sini .

Contoh

Komputer mendengar C tengah dan mencetak "Kembar"

Ia mendengar C tengah lainnya dan mencetak "kle,"

Kemudian ia mendengar C tengah lainnya (not yang salah) dan tidak melakukan apa pun.

Kemudian ia mendengar G di atas C tengah dan mencetak "kembar" dan seterusnya.

Aturan

  • Tanda baca harus seperti yang ditunjukkan.
  • Spasi harus seperti yang ditunjukkan (dengan spasi dan baris baru).
  • Spasi putih dapat dicetak bersama dengan suku kata sebelumnya atau berikutnya.
Ypnypn
sumber
2
Apakah ada cara untuk bersantai "harus dicetak sebelum uang kertas berakhir?" Dengan 1/16 catatan kedua, bahkan jika Anda mendedikasikan 3/4 dari waktu itu untuk pengambilan sampel, Anda hanya memiliki ~ 47ms suara untuk digunakan. Itu memberikan resolusi frekuensi cukup keruh untuk catatan mid-range.
Geobits
@Geobits Poin bagus; Saya menghapus aturan itu.
Ypnypn
1
Ini adalah teka-teki pertama menggunakan input audio yang bisa saya temukan! Selamat!
Bukan karena Charles
1
Apakah judul salah eja dengan sengaja untuk membedakan kedua binar?
Rainbolt
1
Bisakah kita memiliki tautan ke file audio untuk pengujian?
Hobi Calvin

Jawaban:

7

Python 3 - Solusi sebagian ( 760 742 734 710 705 657 karakter)

(Edit terakhir; saya janji)

Ini sepertinya masalah yang sangat, sangat, sangat sulit (terutama mengenali di mana catatan mulai atau berakhir). Transkripsi otomatis musik sepertinya adalah topik penelitian terbuka (bukannya saya tidak tahu apa-apa tentang itu). Jadi inilah solusi parsial yang tidak melakukan segmentasi note (misalnya mencetak "Twinkle" sekaligus ketika mendengar frekuensinya) dan mungkin hanya berfungsi untuk file ogg tertentu:

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

Ini membutuhkan ...

Ubah A = -52 (amplitudo minimum) di baris paling atas tergantung pada mikrofon Anda, jumlah suara sekitar, seberapa keras lagu diputar, dll. Di mikrofon saya, kurang dari -57 tampaknya mengambil banyak suara asing dan lebih dari -49 mengharuskan Anda memainkannya dengan sangat keras.

Ini bisa menjadi golf lebih BANYAK; Saya yakin ada cara untuk menyimpan banyak karakter pada susunan kata pada khususnya. Ini adalah program non-sepele pertama saya di python, jadi saya belum terlalu akrab dengan bahasa tersebut.

Saya mencuri kode untuk deteksi frekuensi melalui autocorrelation dari https://gist.github.com/endolith/255291

Tidak Disatukan:

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
sumber
Saya menulis sedikit bantuan sintaks untuk Anda. Periksa baris 14-29 dan 80-88. pastebin.com/W9XSYwMJ
seequ
@ Sieg - Luar Biasa; Terima kasih! Kebiasaan lama sulit dihilangkan;
Robert Fraser