In lời bài hát đến Tween Twinkle Twinkle Little Star


24

Mục tiêu của bạn là in lời bài hát cho bài hát "Twinkle Twinkle Little Star" khi mỗi nốt nhạc được phát.

Micrô của máy tính sẽ nghe thấy ghi chú. Nếu cao độ (nhưng không nhất thiết là độ dài) của ghi chú là chính xác, hãy in âm tiết thích hợp. Nếu không, không làm gì cả. Mỗi ghi chú sẽ dài ít nhất nửa giây và sẽ có ít nhất một phần tư giây giữa các ghi chú.

Sử dụng các ghi chú âm nhạc được cung cấp ở đây và lời bài hát sau: (Dòng dọc thể hiện các ngắt âm tiết.)

Sinh đôi | kle, sinh đôi | kle, lit | tle sao,

Làm thế nào tôi giành chiến thắng | der những gì bạn đang có.

Lên một thế giới rất cao,

Giống như một dia | mond trên bầu trời.

Sinh đôi | kle, sinh đôi | kle, lit | tle sao,

Làm thế nào tôi giành chiến thắng | der những gì bạn đang có.

Một bản ghi âm của âm nhạc có thể được tìm thấy ở đây .

Thí dụ

Máy tính nghe thấy chữ C ở giữa và in "Twin"

Nó nghe thấy một chữ C khác và in "kle"

Sau đó, nó nghe thấy một chữ C khác (ghi chú sai) và không làm gì cả.

Sau đó, nó nghe thấy chữ G ở giữa chữ C và in "song sinh", v.v.

Quy tắc

  • Dấu câu phải được hiển thị.
  • Khoảng cách phải được hiển thị (với khoảng trắng và dòng mới).
  • Khoảng trắng có thể được in cùng với âm tiết trước hoặc âm tiết tiếp theo.

2
Có cách nào để thư giãn "phải được in trước khi ghi chú kết thúc?" Với 1/16 ghi chú thứ hai, ngay cả khi bạn dành 3/4 thời gian đó để lấy mẫu, bạn chỉ có ~ 47ms âm thanh để làm việc. Điều đó mang lại độ phân giải tần số khá âm u cho các ghi chú tầm trung.
Geobits

@Geobits Điểm tốt; Tôi đã loại bỏ quy tắc đó.
Ypnypn

1
Đây là câu đố đầu tiên sử dụng đầu vào âm thanh mà tôi có thể tìm thấy! Chúc mừng!
Không phải là Charles

1
Là tiêu đề sai về mục đích để phân biệt hai twink?
Rainbolt

1
Chúng tôi có thể có một liên kết đến một tập tin âm thanh để thử nghiệm?
Sở thích của Calvin

Câu trả lời:


7

Python 3 - Giải pháp một phần ( 760 742 734 710 705 657 ký tự)

(Chỉnh sửa lần cuối; Tôi hứa)

Đây có vẻ như là một vấn đề thực sự, rất, rất khó khăn (đặc biệt là nhận ra nơi các ghi chú bắt đầu hoặc kết thúc). Tự động phiên âm nhạc có vẻ như là một chủ đề nghiên cứu mở (không phải tôi biết gì về nó). Vì vậy, đây là một giải pháp một phần không thực hiện bất kỳ phân đoạn ghi chú nào (ví dụ: nó in "Twinkle" cùng một lúc khi nghe thấy tần số) và có lẽ chỉ hoạt động cho tệp ogg cụ thể đó:

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

Điều này đòi hỏi ...

Thay đổi A = -52 (biên độ tối thiểu) ở dòng trên cùng tùy thuộc vào micrô của bạn, âm thanh của âm thanh xung quanh, âm thanh của bài hát đang phát, v.v. Trên micrô của tôi, có vẻ như -57 thu được rất nhiều tiếng ồn bên ngoài và hơn -49 yêu cầu bạn chơi nó rất to.

Điều này có thể được đánh golf nhiều hơn nữa; Tôi chắc chắn có nhiều cách để lưu một loạt các ký tự trên mảng từ cụ thể. Đây là chương trình không tầm thường đầu tiên của tôi về python, vì vậy tôi chưa quen với ngôn ngữ này.

Tôi đã đánh cắp mã để phát hiện tần số thông qua tự động tương quan từ https://gist.github.com/endolith/255291

Ung dung:

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

Tôi đã viết một cú pháp nhỏ giúp bạn. Kiểm tra các dòng 14-29 và 80-88. pastebin.com/W9XSYwMJ
xem

@Sieg - Tuyệt vời; cảm ơn! Những thói quen cũ khó bỏ;
Robert Fraser
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.