音を揺らす(トレモロ、ビブラート)

Python

トレモロ、ビブラートの原理

トレモロ

ギターなどの弦楽器で同じ音を繰り返し早引きすることで、音が震えているかのように聴かせる「トレモロ奏法」というものがあります。ここからヒントを得て、音声波形の振幅を周期的に変化させることで音を揺らす「トレモロ」と呼ばれる音声効果が生まれました。

入力信号を\( s_0(n) \)、出力信号を\( s_1(n) \)、変調信号を\( a(n) \)とするとトレモロは次のような式で表せます。

\( s_1(n) = a(n)s_0(n) \)

これは、入力信号\( s_0(n) \)と変調信号を\( a(n) \)を単純に掛け合わせていることを意味しているため、複雑な計算や処理を必要としません。

グラフからも読み取れるように、入力信号の概形(エンベロープ)が変調信号の形に変形されています。今回は入力信号を440Hz、変調信号を10Hzにしているため、「ラ4:440Hz」の音が1秒間に10回震えているように聴こえるはずです。

これはラジオの通信方式として利用されているAM変調(Amplitude Modulation)とまったく同じ原理でできており、通信の場合は入力信号に該当する波形を搬送波、変調信号の該当する波形を変調波、出力信号に該当する波形を振幅変調波と呼んだりします。

import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
from scipy.io import wavfile

OUTPUT_FILENAME = "output.wav"

# パラメータの設定
RATE = 16000        # 標本化周波数(サンプリングレート)
DURATION = 3        # 録音時間[s]
FREQ1 = 440         # 入力波周波数
AMP1 = 0.8          # 入力波振幅
FREQ2 = 10          # 変調波周波数
AMP2 = 0.5          # 変調波振幅
MARGIN = 0.5        # 変調波のマージン 
FRAMES = 2000       # 描画する範囲

# 時間軸の作成
time = np.arange(RATE * DURATION) / RATE
# 入力信号
input_wave = AMP1 * np.sin(2 * np.pi * FREQ1 * time)
# 変調信号
modulation_wave = MARGIN + AMP2 * np.sin(2 * np.pi * FREQ2 * time)
# 出力信号
output_wave = input_wave * modulation_wave

# 描画設定
fig, axs = plt.subplots(2, 1, figsize=(8, 4))
axs[0].plot(time[:FRAMES], input_wave[:FRAMES], label="input wave")
axs[0].plot(time[:FRAMES], modulation_wave[:FRAMES], label="modulation wave")
axs[1].plot(time[:FRAMES], output_wave[:FRAMES], label="output wave")

axs[0].legend(loc='upper right')
axs[1].legend(loc='upper right')
plt.tight_layout()
plt.show()

output_wave = output_wave * (2**15-1)
output_wave = output_wave.astype("int16")
wavfile.write(OUTPUT_FILENAME, RATE, output_wave)

ビブラート

ギターやバイオリンといった弦楽器の演奏法に、弦を抑えた指で弦を上げたり下げたりすることで音の高さを揺らす「ビブラート奏法」というものがあります。
トレモロは音の大きさを周期的に変化していたのに対し、ビブラートは音の高さを周期的に変化させます。

同様に入力信号を\( s_0(n) \)、出力信号を\( s_1(n) \)、変調信号を\( τ(n) \)とするとビブラートは次のような式で表せます。

\( s_1(n) = s_0(n – τ(n)) \)

これは変調信号\( τ(n) \)によって、入力信号\( s_0(n) \)は時間軸方向の遅延を受けることになります。

グラフを見ると、波形に「密」な部分と「疎」な部分ができています。これにより密な部分はより音が高く、疎な部分はより音が低く聴こえます。

ビブラートもラジオの通信方式のFM変調(Frequency Modulation)とまったく同じ原理でできています。

import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
from scipy.io import wavfile

OUTPUT_FILENAME = "output.wav"

# パラメータの設定
RATE = 16000        # 標本化周波数(サンプリングレート)
DURATION = 3        # 録音時間[s]
FREQ1 = 50          # 入力波周波数
AMP1 = 0.8          # 入力波振幅
FREQ2 = 5           # 変調波周波数
AMP2 = 0.5          # 変調波振幅
MARGIN = 0.5        # 変調波のマージン 
FRAMES = 3000       # 描画する範囲

# 時間軸の作成
time = np.arange(RATE * DURATION) / RATE
# 変調信号
modulation_wave = MARGIN + AMP2 * np.sin(2 * np.pi * FREQ2 * time)
# 入力信号
input_wave = AMP1 * np.sin(2 * np.pi * FREQ1 * time)
# 出力信号
output_wave = AMP1 * np.sin(2 * np.pi * FREQ1 * (time - modulation_wave))

# 描画設定
fig, axs = plt.subplots(2, 1, figsize=(8, 4))
axs[0].plot(time[:FRAMES], input_wave[:FRAMES], label="input wave")
axs[0].plot(time[:FRAMES], modulation_wave[:FRAMES], label="modulation wave")
axs[1].plot(time[:FRAMES], output_wave[:FRAMES], label="output wave")

axs[0].legend(loc='upper right')
axs[1].legend(loc='upper right')
plt.tight_layout()
plt.show()

output_wave = output_wave * (2**15-1)
output_wave = output_wave.astype("int16")
wavfile.write(OUTPUT_FILENAME, RATE, output_wave)

コメント