今回はリングモジュレータを実装していく。
リングモジュレータ
入力と関係ない音程を出すエフェクター。マルチ・エフェクターに組み込まれていることもある。かなり前衛的な演奏ができる。(weblio引用
音としてはこんな感じ
元の音
エフェクト音
理論としては入力の音に対してある周期波形を乗算してめちゃくちゃな音程にするエフェクトです。
変調波形を正弦波として表すとこのような感じ
A:正弦波の振幅
f0:正弦波の周波数[Hz]
n:その時のサンプル
fs:サンプリング周波数[Hz]
エフェクトの波形
- 上段:入力音
- 中段:変調波形(200Hzの正弦波)
- 下段:出力音
かけている正弦波の影響を受けて波形が変形している。
ソースコード
処理の流れはこのようになる
- 元データがステレオなのでwavファイルをLRにわける
- モノラル化する(LRを半分にして加算)
- 正弦波を作る
- エフェクトをかける
- wavに格納しなおす
import wave import audio_func as af import scipy import struct import numpy as np from pylab import * wf = wave.open("GS04.wav", "r")#wav 扱うならお決まりのやつ num_data = scipy.fromstring(wf.readframes(wf.getnframes()),dtype = "int16") / 32768.0#正規化 if(wf.getnchannels() == 2): left = num_data[::2]#1 スライス right= num_data[1::2] in_data = left*0.5 + right*0.5#2ステレオモノラル化 #1:スライスの説明 #a[1,2,3,4,5]ていうリストがあったとして #a[::2] -> 1,3,5 #a[1::2] -> 2,4 #2:ステレオ->モノラル化 #もともとギターの録音で左右に同じ音ふってるのでLR分解して半分にして足せば同じようなもん fs = wf.getframerate() AM_WAVE = []#振幅変調波形のメモリ確保 f0 = 200 #正弦波の周波数[Hz] #フレームの長さ分だけ正弦波を作る for n in np.arange(wf.getnframes()): sine = 0.2 * np.sin(2 * np.pi * f0 * n / fs) AM_WAVE.append(sine*(1/0.2)) #リングモジュレータなので振幅変調する out_data = in_data * AM_WAVE subplots_adjust(hspace=0.5) subplot(311) plot(in_data[0:6000],label="input") legend() subplot(312) plot(AM_WAVE[0:6000],"red") subplot(313) plot(out_data[0:6000],label="output") legend() show() #正規化したデータを元に戻す out_data = [int(x * 32768.0) for x in out_data] out_data = struct.pack("h" * len(out_data), *out_data) #af.play(out_data,wf.getsampwidth(),1,wf.getframerate()) #再生 af.save(out_data, fs,1,"ring.wav")
audio_funcは一例です。(かなり雑な実装なため
import wave import pyaudio from pylab import * def printWaveInfo(wf): """WAVEファイルの情報を取得""" print("チャンネル数 : "+ str(wf.getnchannels())) print("サンプル幅 : "+ str(wf.getsampwidth())) print("サンプルレート : "+ str(wf.getframerate())) print("フレーム数 : "+ str(wf.getnframes())) print("総パラメータ(一括表示用) : "+ str(wf.getparams())) print("再生時間 : "+ str(float(wf.getnframes()) / wf.getframerate())) def play (data,sampleWidth,Channel,sampleRate): # ストリームを開く p = pyaudio.PyAudio() stream = p.open(format=p.get_format_from_width(sampleWidth), channels=Channel, rate=int(sampleRate), output= True) # チャンク単位でストリームに出力し音声を再生 chunk = 1024 sp = 0 # 再生位置ポインタ buffer = data[sp:sp+chunk] while buffer != '': stream.write(buffer) sp = sp + chunk buffer = data[sp:sp+chunk] stream.close() p.terminate() def save(data, fs,Channel,filename): """波形データをWAVEファイルへ出力""" wf = wave.open(filename, "w") wf.setnchannels(Channel) wf.setsampwidth(2) wf.setframerate(fs) wf.writeframes(data) wf.close()