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