1
4

More than 1 year has passed since last update.

Pythonを用いたwavファイルの扱いについて

Last updated at Posted at 2022-06-01

初投稿になります。
経験がないので至らない点も多々あると思いますが、よろしくお願いします。
間違いがありましたら、連絡ください。

はじめに

今回はpythonを用いた、waveファイルの扱い方について
授業でwavファイルを扱ったのでその中身と簡単な手順についてまとめたいと思う。
流れは以下の通り

1.wavファイルを入力、およびヘッダについて
2.波形の確認
3.wavファイルに戻す

WAVファイルとは

wavファイルはバイナリの音声ファイルの一種

圧縮されていないためサイズはmp3等と比較し大きくなる。
サイト等で変換できる。

音源を手に入れる

まずは音源を用意します。
フリー音源でググって用いています。
今回は以下のサイト

魔王魂無料で使える森田交一の音楽

wavファイルの取り込み

wavファイルを扱うには二通りある。

  • 1.waveライブラリを用いる方法
  • 2.with openを使う方法

簡易的にdataを取得したい場合waveをimportして使うとよい。
バイナリファイルでヘッダの情報を確認したい場合は後者の実際にwith openする。

まずは使うライブラリのインポート

usewav.py
import wave
import numpy as np
import matplotlib.pyplot as plt

ライブラリがない場合は各自pipでダウンロードしましょう。
(waveは標準ライブラリなのでもともと入っているはず)

1.waveライブラリの利用した方法

まずは読み込み
以下の方法で読み込まれる。waveライブラリは有能なので勝手にヘッダ部分を解読してくれる。

usewav.py
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()) # 残りのパラメータをタプルに

自分の結果は以下のものとなった。

output
type:  <class 'wave.Wave_read'>
チャンネル数: 2
サンプル幅: 3
サンプリング周波数: 44100
フレーム数: 32896
パラメータ: _wave_params(nchannels=2, sampwidth=3, framerate=44100, nframes=32896, comptype='NONE', compname='not compressed')

中の数字が実際どうなっているかは以下で確認できる。

usewav.py
data=wf.readframes(-1)
#ー1の時すべての中身を取り出すことができる。

以上終わり。

2.with open の利用した方法

まず、with openで開く。modeにはバイナリデータを示す'rb'とする

useopen.py
with open('魔王魂 効果音 システム49.wav', mode='rb') as f:  #バイナリデータとして開く
    data=f.read()
f.close()
print(data)
output
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 .....

のようにデータの意味が分かる。
詳しくは以下のサイトを参考に

wavファイルのヘッダについての説明

実際のデータはヘッダを除いたデータである。
本音源は80番目からである。(各自数えてほしい)
[dataの5つ後ろのデータから]

useopen.py
data = data[80:]

これは1の結果と同じになる。

ステレオの分解

ステレオデータを2つのものモノラルデータに分解する。
データは交互に入っているため、偶数奇数で取り出すようにする。

process_wav.py
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])

グラフのプロット

get_gragh1.py
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を行う。
いかに結果を示す

data.png

data.png

これで分離できた。

wavファイルに戻す

戻すときはwaveライブラリを用いる。
(時間があれば、with openを用いた復元も追記したいと思う)

towav.py
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()

以上、時間があれば詳しく解説したいと思う。

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4