初投稿になります。
経験がないので至らない点も多々あると思いますが、よろしくお願いします。
間違いがありましたら、連絡ください。
はじめに
今回はpythonを用いた、waveファイルの扱い方について
授業でwavファイルを扱ったのでその中身と簡単な手順についてまとめたいと思う。
流れは以下の通り
1.wavファイルを入力、およびヘッダについて
2.波形の確認
3.wavファイルに戻す
WAVファイルとは
wavファイルはバイナリの音声ファイルの一種
圧縮されていないためサイズはmp3等と比較し大きくなる。
サイト等で変換できる。
音源を手に入れる
まずは音源を用意します。
フリー音源でググって用いています。
今回は以下のサイト
wavファイルの取り込み
wavファイルを扱うには二通りある。
- 1.waveライブラリを用いる方法
- 2.with openを使う方法
簡易的にdataを取得したい場合waveをimportして使うとよい。
バイナリファイルでヘッダの情報を確認したい場合は後者の実際にwith openする。
まずは使うライブラリのインポート
import wave
import numpy as np
import matplotlib.pyplot as plt
ライブラリがない場合は各自pipでダウンロードしましょう。
(waveは標準ライブラリなのでもともと入っているはず)
1.waveライブラリの利用した方法
まずは読み込み
以下の方法で読み込まれる。waveライブラリは有能なので勝手にヘッダ部分を解読してくれる。
wf = wave.open('魔王魂 効果音 システム49.wav','rb')
print('type: ', type(wf))
print('チャンネル数:', wf.getnchannels()) # モノラル: 1,ステレオ: 2
print('サンプル幅:', wf.getsampwidth()) # バイト数 (1byte=8bit)
print('サンプリング周波数:', wf.getframerate())
print('フレーム数:', wf.getnframes()) # フレームの数
print('パラメータ:', wf.getparams()) # 残りのパラメータをタプルに
自分の結果は以下のものとなった。
type: <class 'wave.Wave_read'>
チャンネル数: 2
サンプル幅: 3
サンプリング周波数: 44100
フレーム数: 32896
パラメータ: _wave_params(nchannels=2, sampwidth=3, framerate=44100, nframes=32896, comptype='NONE', compname='not compressed')
中の数字が実際どうなっているかは以下で確認できる。
data=wf.readframes(-1)
#ー1の時すべての中身を取り出すことができる。
以上終わり。
2.with open の利用した方法
まず、with openで開く。modeにはバイナリデータを示す'rb'とする
with open('魔王魂 効果音 システム49.wav', mode='rb') as f: #バイナリデータとして開く
data=f.read()
f.close()
print(data)
b'RIFF\xe8\x03\x03\x00WAVEJUNK\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00fmt \x10\x00\x00\x00\x01\x00\x02\x00D\xac\x00\x00\x98\t\x04\x00\x06\x00\x18\x00data\x00\x03\x03\x00\x00\xa2\xfe\x00\xa2\xfe\x00\xba\xfe\x00\xba\xfe\x00\xfd\xfe\x00\xfd\xfe\x00:\xff\x00:\xff\x00\x86\xff\x00\x86\xff\x00\xe6\xff\x00\xe6\xff\x00)\x00\x00)\x00\x00@\x00\x00@\x00\x004\x00\x004\x00\x009\x00\x009\x00\x00U\x00\x00U\x00\x00:\x00\x00:\x00\x00\xdb\xff\x00\xdb\xff\x00m\xff\x00m\xff\x00J\xff\x00J\xff\x00V\xff\x00V\xff\x00)\xff\x00)\xff\x00\x1e\xff\x00\x1e\xff\x00e\xff\x00e\xff\x00\xac\xff\x00\xac\xff\x00\xfb\xff\x00\xfb\xff\x00O\x00\x00O\x00\x00_\x00\x00_\x00\x00"\x00\x00"\x00\x00\xef\xff\x00\xef\xff\x00\xff\xff\x00\xff\xff\x00\x1a\x00\x00\x1a\x00
前から順番に読み解いていく。
wavファイルは以下の構造の繰り返しになっている。[例外もある]
データ | データ数 | 内容 |
---|---|---|
識別子 | 4 | アルファベット4文字でデータの中身を示す |
データ数 | 4 | データ数を16進数で示す |
データの内容 | データ数分 | データの実際の中身 |
最初の「RIFF」は様々な種類や形式のデータの格納方式を定めた汎用のファイル形式を示している。
「\xe8\x03\x03\x00」は16進数を示している。
wav方式はリトルエディアンであるため、順番を逆に並べ替え、
\xe8\x03\x03\x00
⇓
\x00\x03\x03\xe8
エスケープシーケンスの「\x」を取り除き、16進数を10進数に変換する。
000303e8
⇓
197608
同様に求めると
RIFF 197608
WAVE
JUNK 28 00000000000000000000000000000000000000000000000000000000
fmt 16 1 2 44100 6 24
data 197376 .....
のようにデータの意味が分かる。
詳しくは以下のサイトを参考に
実際のデータはヘッダを除いたデータである。
本音源は80番目からである。(各自数えてほしい)
[dataの5つ後ろのデータから]
data = data[80:]
これは1の結果と同じになる。
ステレオの分解
ステレオデータを2つのものモノラルデータに分解する。
データは交互に入っているため、偶数奇数で取り出すようにする。
data = np.frombuffer(data, dtype='int16')
data1=[]
data2=[]
for i in range(len(data)):
if i%2==0:
data1.append(data[i])
if i%2==1:
data2.append(data[i])
グラフのプロット
fig = plt.figure(figsize=(7,4),dpi=500)
plt.plot(data1[:4410])
plt.xlim(0,4410)
plt.ylim(-40000,40000)
plt.tick_params(length=0)
plt.xticks(list(range(0,4411,441)),list(range(0,101,10)))
plt.xlabel(" Time [sec]")
plt.ylabel("wavelength")
同様にdata2でplotを行う。
いかに結果を示す
これで分離できた。
wavファイルに戻す
戻すときはwaveライブラリを用いる。
(時間があれば、with openを用いた復元も追記したいと思う)
wf = wave.open('魔王魂 効果音 システム49.wav','rb')
data=wf.readframes(-1)
data=np.frombuffer(data, dtype='int16')
#ここから復元ゾーン
data_binary = np.array(np.array(data)/x).astype('int16').astype(np.int16).tobytes()
ch = wf.getnchannels()
width = wf.getsampwidth()
fr = wf.getframerate()
fn = wf.getnframes()
ww = wave.open('data.wav', 'w')
ww.setnchannels(1)
ww.setsampwidth(2)
ww.setframerate(fr)
ww.writeframes(data_binary)
ww.close()
以上、時間があれば詳しく解説したいと思う。