概要
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以下)務めたほうが良さそうです。