2018/12/06(木) [n年前の日記]
#1 [python] Pythonでwavファイルに対して何か処理その2
Python + Pydub で wavファイルを読み込んで分割するところまではできたけど、その分割したデータをどうやって TIC-80用に加工するか、というところで悩んでいたり。
◎ フーリエ解析。 :
とりあえず、以下を参考にしてフーリエ解析とやらをするところまではできたのだけど。環境は Windows10 x64 ; Python 2.7.15 + Pydub 0.23.0 + Numpy 1.15.4。
_高速フーリエ変換(FFT) - 人工知能に関する断創録
_fft_test.py
しかし、これができても…。任意の周波数が複数鳴らせるサウンド仕様だったら使い道があるかもしれんけど、TIC-80はそういうサウンド仕様じゃないし…。
_高速フーリエ変換(FFT) - 人工知能に関する断創録
_fft_test.py
import os
import sys
from pydub import AudioSegment
# from pydub.playback import play
import numpy as np
import matplotlib.pyplot as plt
draw_wave_enable = 0
window_enable = 0
def fft(dt, rate):
"""FFT."""
specs = []
freqlists = []
ampspecs = []
phasespecs = []
for d in dt:
n = len(d)
if window_enable:
# use Hamming window
wdw = np.hamming(n) # Hamming Window
windowedData = wdw * d
spec = np.fft.fft(windowedData) # FFT
else:
# not use window
spec = np.fft.fft(d) # FFT
freqlist = np.fft.fftfreq(n, d=1.0 / rate)
ampspec = [np.sqrt(c.real ** 2 + c.imag ** 2) for c in spec]
phasespec = [np.arctan2(int(c.imag), int(c.real)) for c in spec]
specs.append(spec)
freqlists.append(freqlist)
ampspecs.append(ampspec)
phasespecs.append(phasespec)
return specs, freqlists, ampspecs, phasespecs
def draw_fft(ampspecs, freqlists, rate, iadd):
"""Draw FFT."""
row = 8
for i in range(row):
plt.subplot(row, 1, i + 1)
plt.plot(freqlists[iadd + i], ampspecs[iadd + i])
plt.axis([0, rate / 2, 0, 100])
plt.xlabel("[Hz]")
plt.ylabel("amp spec")
plt.grid()
plt.show()
def draw_wave(dt, iadd):
row = 8
for i in range(row):
plt.subplot(row, 1, i + 1)
plt.plot(dt[iadd + i])
plt.ylim(ymax=1.0)
plt.grid()
plt.show()
def main():
"""Main."""
if len(sys.argv) != 2:
print("Usage: python %s WAV_filename" % os.path.basename(__file__))
sys.exit()
infile = sys.argv[1]
sound = AudioSegment.from_wav(infile) # read wave file
# play(sound)
data = np.array(sound.get_array_of_samples())
x = data[::sound.channels] # get mono channel
# normlize
x = (x - x.min()).astype(float) / (x.max() - x.min()).astype(float)
rate = sound.frame_rate
sample_len = len(x)
frm = rate / 60
n = sample_len / frm
print("-- Input file : %s" % infile)
print("-- Channel : %d" % sound.channels)
print("-- Sampling rate : %d Hz" % rate)
print("-- Duration : %f msec" % len(sound))
print("-- Sample length : %d point" % sample_len)
print("-- 1 Frame length : %d point" % frm)
print("-- n : %d" % n)
# divide
dt = []
for i in range(0, sample_len, frm):
dt.append(x[i:i + frm])
if draw_wave_enable:
# draw wave
draw_wave(dt, 0)
else:
# FFT
specs, freqlists, ampspecs, phasespecs = fft(dt, rate)
draw_fft(ampspecs, freqlists, rate, 0)
if __name__ == "__main__":
main()
> python fft_test.py hello.wav -- Input file : hello.wav -- Channel : 1 -- Sampling rate : 48000 Hz -- Duration : 511.000000 msec -- Sample length : 24514 point -- 1 Frame length : 800 point -- n : 30
しかし、これができても…。任意の周波数が複数鳴らせるサウンド仕様だったら使い道があるかもしれんけど、TIC-80はそういうサウンド仕様じゃないし…。
◎ 一定個数でデータを抜き出してみたり。 :
とりあえず、1/60秒単位で分割したwavデータから、TIC-80 の波形メモリのスペックに合わせて32ポイントずつデータを取り出して、その32ポイントのデータを元の長さに並べ直してから鳴らしてみたり。
_divide_composite.py
しかし…これはちょっと聞けたものじゃない…。
_hello_9600hz.wav
_hello_9600hz_output.wav
_crt_ooo_13440hz.wav
_crt_ooo_13440hz_output.wav
最初は、32個 x n個のデータを全部加算してから n で割って波形を求めてたけど、それだと聞けたものじゃなく。単に一ヶ所取り出しただけのほうがまだマシかもしれないなと…。しかしそれでも、この状態…。
以下は、元波形、加工後波形、元波形、加工後波形…の順で並べたもの。
こうして見ると、波形はそんなに違っていない…とも言えないか。結構違うな…。何にせよ、この方法ではそれらしい音にならないな…。
_divide_composite.py
import os
import sys
from pydub import AudioSegment
from pydub.playback import play
import numpy as np
import matplotlib.pyplot as plt
ave_enable = 0
draw_wave_enable = 0
play_sound = 1
print_table = 0
export_wav = 1
def draw_wave(dt):
row = 8
for i in range(row):
plt.subplot(row, 1, i + 1)
plt.plot(dt[i])
plt.grid()
plt.show()
def composie(dt):
ndt = []
nwave = []
for src in dt:
n = len(src) / 32
nd = np.array([0.0] * 32)
nn = 0
if ave_enable:
# get average value
for i in range(0, len(src), 32):
for j in range(32):
if i + j < len(src):
nd[j] += src[i + j]
nn += 1
nd = nd / nn
else:
# get 32point data only
for j in range(32):
if j < len(src):
nd[j] += src[j]
nd[0] = 0.50 * nd[0] + 0.50 * src[32]
nd[1] = 0.75 * nd[1] + 0.25 * src[33]
# nd[0] = 0.25 * nd[0] + 0.75 * src[32]
# nd[1] = 0.50 * nd[1] + 0.50 * src[33]
# nd[2] = 0.75 * nd[2] + 0.25 * src[34]
nn += 1
ndt.append(nd)
dd = np.array([])
for i in range(n):
dd = np.append(dd, nd)
nwave.append(dd)
return ndt, nwave
def get_sound_from_numpy_arrays(dt, rate):
dt = dt.astype("int16")
# print("min = %f , max = %f" % (dt.min(), dt.max()))
sound = AudioSegment(
dt.tobytes(),
sample_width=2, # 2 byte (16 bit) samples
frame_rate=rate, # sampling rate
channels=1 # mono
)
return sound
def main():
"""Main."""
if len(sys.argv) != 2:
print("Usage: python %s WAV_filename" % os.path.basename(__file__))
sys.exit()
infile = sys.argv[1]
sound = AudioSegment.from_wav(infile) # read wave file
data = np.array(sound.get_array_of_samples())
src = data[::sound.channels] # get mono channel
# normlize
src = (src - src.min()).astype(float) / (src.max() - src.min()).astype(float)
rate = sound.frame_rate
frm = rate / 60
sample_len = len(src)
print("-- Input file : %s" % infile)
print("-- Channel : %d" % sound.channels)
print("-- Sampling rate : %d Hz" % rate)
print("-- 1 Frame length : %f point" % frm)
print("-- Duration : %f msec" % len(sound))
print("-- Sample length : %f point" % sample_len)
print("-- n : %d" % (sample_len / frm))
# divide
dt = []
for i in range(0, sample_len, frm):
dt.append(src[i:i + frm])
ndt, nwave = composie(dt)
# draw graph
if draw_wave_enable:
iadd = 0
row = 10
for i in range(0, row, 2):
plt.subplot(row, 1, i + 1)
plt.plot(dt[iadd + (i / 2)])
plt.ylim(ymax=1.0)
plt.grid()
plt.subplot(row, 1, i + 2)
plt.plot(nwave[iadd + (i / 2)])
plt.ylim(ymax=1.0)
plt.grid()
plt.show()
nw = np.array([])
for d in nwave:
nw = np.append(nw, d)
org_src = (src - 0.5) * 0x0ffff
org_sound = get_sound_from_numpy_arrays(org_src, rate)
nww = ((nw - 0.5) * 0x0f).astype("int16") * 0x0fff
new_sound = get_sound_from_numpy_arrays(nww, rate)
if play_sound:
# play(sound)
play(org_sound)
play(new_sound)
if export_wav:
fn = "_output.wav"
new_sound.export(fn, format="wav")
print("-- output : %s" % fn)
if print_table:
print("tbl={")
for src in ndt:
d = (src * 0x0f).astype("int16")
# print("min,max=%d,%d" % (d.min(), d.max()))
# print(d)
s = " {"
for i in range(0, 32, 2):
v = ((d[i + 1] & 0x0f) << 4) | (d[i] & 0x0f)
if i < 30:
s += "0x%02x," % v
else:
s += "0x%02x" % v
s += "},"
print(s)
print("}")
if __name__ == "__main__":
main()
しかし…これはちょっと聞けたものじゃない…。
_hello_9600hz.wav
_hello_9600hz_output.wav
_crt_ooo_13440hz.wav
_crt_ooo_13440hz_output.wav
最初は、32個 x n個のデータを全部加算してから n で割って波形を求めてたけど、それだと聞けたものじゃなく。単に一ヶ所取り出しただけのほうがまだマシかもしれないなと…。しかしそれでも、この状態…。
以下は、元波形、加工後波形、元波形、加工後波形…の順で並べたもの。
こうして見ると、波形はそんなに違っていない…とも言えないか。結構違うな…。何にせよ、この方法ではそれらしい音にならないな…。
[ ツッコむ ]
以上です。

