พิมพ์เนื้อเพลงเป็น“ Twinkle Twinkle Little Star”


24

เป้าหมายของคุณคือการพิมพ์เนื้อเพลงลงในเพลง "Twinkle Twinkle Little Star" ในขณะที่เล่นโน้ตแต่ละอัน

ไมโครโฟนของคอมพิวเตอร์จะได้ยินเสียงบันทึก หากระดับเสียง (แต่ไม่จำเป็นต้องมีความยาว) ของโน้ตนั้นถูกต้องให้พิมพ์พยางค์ที่เหมาะสม มิฉะนั้นไม่ทำอะไรเลย แต่ละโน้ตจะมีความยาวอย่างน้อยครึ่งวินาทีและจะมีการพักอย่างน้อยหนึ่งในสี่ของวินาทีระหว่างโน้ต

ใช้โน้ตดนตรีที่ให้ไว้ที่นี่และเนื้อเพลงต่อไปนี้: (เส้นแนวตั้งแสดงถึงการแบ่งพยางค์)

คู่ | kle, twin | kle, lit | tle star,

ฉันจะชนะ | der สิ่งที่คุณเป็น

ตั้งค่า a | bove โลกสูงมาก

เหมือน dia | mond บนท้องฟ้า

คู่ | kle, twin | kle, lit | tle star,

ฉันจะชนะ | der สิ่งที่คุณเป็น

บันทึกของเพลงที่สามารถพบได้ที่นี่

ตัวอย่าง

คอมพิวเตอร์ได้ยินเสียงกลาง C และพิมพ์ "Twin"

ได้ยินอีก C ตรงกลางและพิมพ์ "kle"

จากนั้นมันจะได้ยินอีกอันตรงกลาง C (โน้ตผิด) และไม่ทำอะไร

จากนั้นจะได้ยินเสียง G เหนือกลาง C และพิมพ์ "twin" และอื่น ๆ

กฎระเบียบ

  • เครื่องหมายวรรคตอนต้องเป็นตามที่แสดง
  • ระยะห่างต้องเป็นไปตามที่แสดง (พร้อมช่องว่างและขึ้นบรรทัดใหม่)
  • ช่องว่างอาจถูกพิมพ์พร้อมกับทั้งก่อนหน้าหรือพยางค์ถัดไป

2
มีวิธีใดบ้างที่จะผ่อนคลาย "ต้องพิมพ์ก่อนที่โน้ตจะจบ?" ด้วยการบันทึกย่อที่ 1/16 วินาทีแม้ว่าคุณจะทุ่มเท 3/4 ของเวลานั้นในการสุ่มตัวอย่างคุณจะมีเพียงเสียงประมาณ 47 มิลลิวินาทีเท่านั้นที่จะใช้งานได้ ที่ให้ความละเอียดความถี่ที่มืดสนิทสำหรับโน้ตระดับกลาง
Geobits

@Geobits จุดดี; ฉันลบกฎนั้นแล้ว
Ypnypn

1
นี่เป็นปริศนาชิ้นแรกที่ใช้อินพุตเสียงที่ฉันหาได้! ยินดีด้วย!
ไม่ใช่ว่า Charles

1
ชื่อสะกดผิดโดยมีจุดประสงค์เพื่อแยกความแตกต่างของสอง twinkle หรือไม่
Rainbolt

1
เรามีลิงก์ไปยังไฟล์เสียงเพื่อทำการทดสอบได้ไหม
งานอดิเรกของ Calvin

คำตอบ:


7

Python 3 - โซลูชันบางส่วน ( 760 742 734 710 705 657 ตัวอักษร)

(แก้ไขล่าสุด; ฉันสัญญา)

ดูเหมือนว่าเป็นปัญหาที่แท้จริงสวยและยากมาก (โดยเฉพาะการจดจำว่าจุดเริ่มต้นหรือจุดสิ้นสุดของบันทึกย่อ) การถอดเสียงเพลงโดยอัตโนมัติดูเหมือนหัวข้อการวิจัยแบบเปิด (ไม่ใช่ว่าฉันรู้อะไรเกี่ยวกับมัน) ดังนั้นนี่คือโซลูชันบางส่วนที่ไม่ทำการแบ่งส่วนโน้ตใด ๆ (เช่นจะพิมพ์ "Twinkle" ทั้งหมดในครั้งเดียวเมื่อได้ยินความถี่) และอาจใช้ได้กับไฟล์ ogg ที่ระบุเท่านั้น:

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

สิ่งนี้ต้องการ ...

เปลี่ยน A = -52 (แอมพลิจูดขั้นต่ำ) ที่บรรทัดบนสุดโดยขึ้นอยู่กับไมโครโฟนของคุณจำนวนเสียงรอบข้างการเล่นเพลงดัง ฯลฯ ไมโครโฟนของฉันน้อยกว่า -57 ดูเหมือนจะมีเสียงรบกวนภายนอกมาก และมากกว่า -49 คุณต้องเล่นมันดังมาก

นี่อาจเป็นเรื่องของกอล์ฟอีกมาก ฉันแน่ใจว่ามีวิธีการบันทึกอักขระจำนวนมากในอาร์เรย์ของคำโดยเฉพาะ นี่เป็นโปรแกรมที่ไม่สำคัญเรื่องแรกของฉันในไพ ธ อนดังนั้นฉันยังไม่คุ้นเคยกับภาษามากนัก

ฉันขโมยรหัสสำหรับการตรวจจับความถี่ผ่านการบันทึกอัตโนมัติจาก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

ฉันเขียนความช่วยเหลือด้านไวยากรณ์เล็กน้อยสำหรับคุณ ตรวจสอบบรรทัดที่ 14-29 และ 80-88 pastebin.com/W9XSYwMJ
seequ

@Sieg - ยอดเยี่ยม; ขอบคุณ! นิสัยเก่านั้นยากที่จะทำลาย
Robert Fraser
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.