概要
Pythonでは、PydubのAudioSegmentを使って音声データを読み込み・加工したり、保存したりすることができます。ただし、Numpy等のデータの状態からAudioSegmentを作成する方法が分からなかったので調べて整理すると共に、音声データのスペクトルに乱数ノイズを加えた際に音声データにどのような影響が出るのかを検討しました。
音声ファイルは前回同様、スーパーマリオ64のウォーターランドの曲を使わせていただきました。
https://www.nintendo.co.jp/ds/series/dsi/menu/sound/download.html
前回の記事(AudioSegmentを用いた音声の読み込みやフーリエ変換を用いたスペクトログラムの作成)はこちら。
ArrayからAudioSegmentの作成と保存
調べたら以下サイトに書いてありました。
https://github.com/jiaaro/pydub/blob/master/API.markdown
sound = AudioSegment(
# raw audio data (bytes)
data=b'…',
# 2 byte (16 bit) samples
sample_width=2,
# 44.1 kHz frame rate
frame_rate=44100,
# stereo
channels=2
)
どうやら配列をByte型に変換した物と、その他の設定値(bit数とフレームレート、ステレオ・モノラル)を指定すればいいようです。
また、保存は以下のようにexpertコマンドで可能のようです。
#簡単な方法
sound.export("/path/to/output.mp3", format="mp3")
#細かくいろいろ指定することも可能
sound.export("/path/to/output.mp3",
format="mp3",
bitrate="192k",
tags={"album": "The Bends", "artist": "Radiohead"},
cover="/path/to/albumcovers/radioheadthebends.jpg")
サンプルデータを使って試してみます。
#ファイル読み込み
sound = AudioSegment.from_file("ウォーターランド.m4a", "m4a")
#配列データ取り出し
samples = np.array(sound.get_array_of_samples())
#読み込んだ配列データから新しいAudioSegmentを作成
test = AudioSegment(
samples.astype("int16").tobytes(),
sample_width=sound.sample_width,
frame_rate=sound.frame_rate,
channels=sound.channels,
)
#保存
test.export("test.mp3", format="mp3")
配列はsample_widthに合わせて、int16等の型に設定する必要があることに留意してください。保存できたファイルは以下。きちんと再現できていますね。
ノイズを加えた場合の検証
参考として、フーリエ変換して得られたスペクトルデータにノイズを乗せ、それを逆変換して得られる音声データがどのようになるかを検証しました。ノイズの幅であるn_ampとn_argを変えながら検証します。
#乱数のシードを設定
np.random.seed(0)
#ノイズの振幅
n_amp = 1.0
n_arg = 1.0
#音声データ読み込み
sound = AudioSegment.from_file("ウォーターランド.m4a", "m4a")
samples = np.array(sound.get_array_of_samples())
sample = samples[::sound.channels]
#fftでスペクトルに変換
spec = np.fft.fft(sample)
#振幅・偏角を取り出しノイズを印加
amp = np.abs(spec)
amp += np.random.randn(amp.shape[0])*amp.mean()*n_amp
arg = np.angle(spec) + np.random.randn(amp.shape[0])*n_arg
#ノイズを加えた結果をスペクトルに戻す
spec2 = np.exp(1j*arg)*amp
#フーリエ逆変換
sample2 = np.fft.ifft(spec2)
#AudioSegmentを作成し保存
test = AudioSegment(sample2.real.astype("int16").tobytes(), sample_width=sound.sample_width, frame_rate=sound.frame_rate, channels = 1)
test.export("test.mp3", format="mp3")
n_ampを変えた場合
まずは振幅についてだけノイズを加えてみます。後ろに雑音が乗るという感じですね。
n_amp=0.5, n_arg=0.0 の場合
n_amp=1.0, n_arg=0.0 の場合
n_amp=3.0, n_arg=0.0 の場合
n_argを変えた場合
次に偏角についてだけノイズを加えてみます。振幅の場合より影響が大きく、なんだか酔いそうな感じです。
n_amp=0.0, n_arg=0.5 の場合
n_amp=0.0, n_arg=1.0 の場合
n_amp=0.0, n_arg=3.0 の場合
n_ampとn_argの両方を変えた場合
最後に両方にノイズを加えてみます。両方の影響を重ねあわせたという感じです。
n_amp=0.5, n_arg=0.5 の場合
n_amp=1.0, n_arg=1.0 の場合
n_amp=3.0, n_arg=3.0 の場合
まとめ
Numpy等のデータの状態からAudioSegmentを作成する方法と、音声データのスペクトルに乱数ノイズを加えた際に音声データにどのような影響が出るのかを検討し整理しました。偏角のノイズが1.0を超えたあたりからだいぶ聞き取りにくくなるようですね。予測をしていく時は、偏角の誤差を小さくできるように(RMSE1.0以下)務めたほうが良さそうです。