Python
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のImpromptuです。このファイルでは、25450のSAMPLES_PER_BYTEを使用しました。
また、10200のSAMPLES_PER_BYTEでTwinkle、Twinkle、Little Starをエンコードする自由を取りました。
出力
PianoforteおよびBeatboxの即興演奏
aWnxQDg4mWqZWVl6W+LyOThfHOPyQThAe4x5XCqJK1EJ8Rh6jXt5XEMpk1Epe5JqTJJDSisrkkNCSqnSkkJDkiorCZHhCxsq8nlakfEp8vNb8iqLysp6MpJ7s4x7XlxdW4qKMinJKho
リンク
キラキラ光る
HBobGlJSUlJSY2FlYVNRUVFCQkJCQjs5PDksKisqGxoZGVFTUVNRREFDQjs6OjoqKykpKVRRVFJDQkJCOjs6OzksKikpGxobG1JSUlNRZWFlYVNSUVFCQkJDQTw5PDorKisqGhsZGRk
リンク