蟒蛇
对于UTF-8,我没有进行任何特殊处理,因此我的提交通过了140字节的要求。我对我的解决方案的有用性,准确性或效率不做任何声明。
我对输入和输出使用了44100 Hz的采样率。SAMPLES_PER_BYTE控制转换的质量。数值越低,声音的质量越好。我使用的值在结果部分中给出。
用法
编码
输入文件应为wav。它仅编码第一个通道。
twusic.py -e [input file] > output.b64
解码
twusic.py -d [input file] > output.raw
播放解码音乐
aplay -f U8 --rate=[rate of input file] output.raw
代码
#!/usr/bin/env python
SAMPLES_PER_BYTE = 25450
from math import sin, pi, log
from decimal import Decimal
PI_2 = Decimal(2) * Decimal(pi)
FIXED_NOTE = Decimal('220') # A
A = Decimal('2') ** (Decimal('1') / Decimal('12'))
A_LN = A.ln()
def freq(note):
return FIXED_NOTE * (A ** Decimal(note))
def note(freq):
return (Decimal(freq) / FIXED_NOTE).ln() / A_LN
VOLUME_MAX = Decimal('8')
def volume(level):
return Decimal('127') * (Decimal(level+1).ln() / VOLUME_MAX.ln())
def antivolume(level):
x = Decimal(level) / Decimal('127')
y = VOLUME_MAX ** x
return y - 1
NOTES = [freq(step) for step in xrange(-16, 16)]
VOLUMES = [volume(level) for level in xrange(0, VOLUME_MAX)]
def play(stream, data):
t = 0
for x in data:
x = ord(x)
w = PI_2 * NOTES[(x&0xf8) >> 3] / Decimal(16000)
a = float(VOLUMES[x&0x07])
for _ in xrange(0, SAMPLES_PER_BYTE):
stream.write(chr(int(128+(a*sin(w*t)))))
t += 1
NOTE_MAP = {'A': 0b00000000,
'g': 0b00001000,
'G': 0b00010000,
'f': 0b00011000,
'F': 0b00100000,
'E': 0b00101000,
'd': 0b00110000,
'D': 0b00111000,
'c': 0b01000000,
'C': 0b01001000,
'B': 0b01010000,
'a': 0b01011000}
def convert(notes, volume):
result = []
for n in notes:
if n == ' ':
result += '\00'
else:
result += chr(NOTE_MAP[n] | (volume & 0x07)) * 2
return ''.join(result)
TWINKLE = convert('C C G G A A GG' +
'F F E E D D CC' +
'G G F F E E DD' +
'G G F F E E DD' +
'C C G G A A GG' +
'F F E E D D CC', 0x7)
if __name__ == '__main__':
from base64 import b64encode, b64decode
import numpy as np
from numpy.fft import fft, fftfreq
import wave
import sys
if len(sys.argv) != 3:
print 'must specify -e or -d plus a filename'
sys.exit(1)
if sys.argv[1] == '-e':
w = wave.open(sys.argv[2], 'rb')
try:
output = []
(n_channels, sampwidth, framerate, n_frames, comptype, compname) = w.getparams()
dtype = '<i' + str(sampwidth)
# Find max amplitude
frames = np.abs(np.frombuffer(w.readframes(n_frames), dtype=dtype)[::n_channels])
max_amp = np.percentile(frames, 85)
w.rewind()
read = 0
while read < n_frames:
to_read = min(n_frames-read, SAMPLES_PER_BYTE)
raw_frames = w.readframes(to_read)
read += to_read
frames = np.frombuffer(raw_frames, dtype=dtype)[::n_channels]
absolute = np.abs(frames)
amp = np.mean(absolute)
amp = int(round(antivolume(min((amp / max_amp) * 127, 127))))
result = fft(frames)
freqs = fftfreq(len(frames))
while True:
idx = np.argmax(np.abs(result)**2)
freq = freqs[idx]
hz = abs(freq * framerate)
if hz > 0:
break
result = np.delete(result, idx)
if len(result) <= 0:
hz = 220
amp = 0
break
n = int(round(note(hz)))
n &= 0x1F
n <<= 3
n |= amp & 0x07
output.append(chr(n))
finally:
w.close()
print b64encode(''.join(output)).rstrip('=')
else:
with open(sys.argv[2], 'rb') as f:
data = f.read()
data = data + '=' * (4-len(data)%4)
play(sys.stdout, b64decode(data))
输入
我的官方意见是Kevin MacLeod 撰写的《 Pianoforte和Beatbox的即兴》。对于此文件,我使用了SAMPLES_PER_BYTE 25450。
我还自由地编码了Twinkle,Twinkle,Little Star,SAMPLES_PER_BYTE为10200。听起来好多了。
输出
即兴演奏钢琴和节拍器
aWnxQDg4mWqZWVl6W+LyOThfHOPyQThAe4x5XCqJK1EJ8Rh6jXt5XEMpk1Epe5JqTJJDSisrkkNCSqnSkkJDkiorCZHhCxsq8nlakfEp8vNb8iqLysp6MpJ7s4x7XlxdW4qKMinJKho
链接
一闪一闪亮晶晶
HBobGlJSUlJSY2FlYVNRUVFCQkJCQjs5PDksKisqGxoZGVFTUVNRREFDQjs6OjoqKykpKVRRVFJDQkJCOjs6OzksKikpGxobG1JSUlNRZWFlYVNSUVFCQkJDQTw5PDorKisqGhsZGRk
链接