“Twinkle Twinkle Little Star” şarkısını yazdır


24

Amacınız, her nota çalınırken sözleri "Twinkle Twinkle Little Star" şarkısına basmak.

Bilgisayarın mikrofonu notları duyacak. Notun perdesi (ancak mutlaka uzunluğu) doğru değilse, uygun heceyi yazdırın. Aksi takdirde, hiçbir şey yapmayın. Her not en az yarım saniye uzunluğunda olacak ve notlar arasında en az saniyenin dörtte biri kesilecektir.

Burada verilen notaları ve aşağıdaki şarkı sözlerini kullanın: (Dikey çizgiler heceli araları gösterir.)

İkiz | ikiz, ikiz | hafif, yanan | yıldız,

Nasıl kazandım | ne olduğunuzu.

Kadar bir | dünyayı çok yüksek,

Gökyüzündeki bir dia | mond gibi.

İkiz | ikiz, ikiz | hafif, yanan | yıldız,

Nasıl kazandım | ne olduğunuzu.

Müzik kaydı burada bulunabilir . .

Örnek

Bilgisayar orta C'yi duyuyor ve “Twin” yazıyor

Başka bir orta C duyar ve "kle" basar.

Sonra başka bir orta C duyar (yanlış not) ve hiçbir şey yapmaz.

Daha sonra G'yi orta C'nin üstünden duyar ve “ikiz” i basar vb.

kurallar

  • Noktalama işaretleri gösterildiği gibi olmalıdır.
  • Boşluk gösterildiği gibi olmalıdır (boşluklarla ve yeni satırlarla).
  • Boşluk, önceki veya sonraki heceyle birlikte yazdırılabilir.

2
Rahatlamanın bir yolu var mı? "Not bitmeden basılmalı mı?" 1/16 saniyelik notlarla, örnekleme zamanının 3 / 4'ünü ayırmış olsanız bile, çalışacak sadece ~ 47ms sesiniz olur. Bu orta sınıf notları için oldukça bulanık frekans çözünürlüğü verir.
Geobits

@ Geobits İyi nokta; Bu kuralı kaldırdım.
Ypnypn

1
Bu bulabildiğim ses girişini kullanan ilk bulmaca! Tebrikler!
Charles,

1
İki pırıltıyı ayırt etmek amacıyla başlık yanlış yazılmış mı?
Rainbolt

1
Test için bir ses dosyasına bir link verebilir miyiz?
Calvin'in Hobileri

Yanıtlar:


7

Python 3 - Kısmi çözüm ( 760 742 734 710 705 657 karakter)

(Son düzenleme; söz veriyorum)

Bu, gerçekten, oldukça, çok zor bir problem gibi görünüyor (özellikle notların nerede başladığını veya bittiğini kabul ederek). Müziğin otomatik transkripsiyonu açık bir araştırma konusu gibi gözüküyor (onun hakkında hiçbir şey bilmiyorum). Bu yüzden, nota segmentasyonu yapmayan kısmi bir çözüm (örneğin, frekansı duyduğunda bir kerede "Twinkle" yazıyor) ve muhtemelen sadece belirli bir ogg dosyası için çalışıyor:

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

Bu gerektirir...

Mikrofonunuza, ortam sesinin ne kadar yüksek olduğuna, şarkının ne kadar yüksek sesle çektiğine vb. Bağlı olarak üst satırdaki A = -52 (minimum amplitüd) değerini değiştirin. ve -49'dan fazlası, çok yüksek sesle çalmanızı gerektirir.

Bu bir çok daha fazla golf olabilir; Özellikle kelimeler dizisine bir grup karakteri kaydetmenin yolları olduğuna eminim. Bu benim pythondaki ilk önemsiz programım, bu yüzden henüz dili bilmiyorum.

Frekans tespiti için kodunu otomatik ilişkilendirme üzerinden çaldım. https://gist.github.com/endolith/255291 adresinden

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

Sana küçük bir sözdizimi yardımı yazdım. 14-29 ve 80-88 satırlarını kontrol edin. pastebin.com/W9XSYwMJ
seequ 28:14

@Sieg - Başar; Teşekkürler! Eski alışkanlıkları kırmak zordur;
Robert Fraser,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.