音を読み込む、書き込む(wavファイル)

Python

A-D変換、D-A変換

A-D変換(Analog-to-Digital conversion)とD-A変換(Digital-to-Analog conversion)は、アナログ信号とデジタル信号の相互変換を行うために用いられる変換方法です。

A-D変換は、アナログ信号をデジタル信号に変換することで、コンピュータなどのデジタル機器で処理できるようにします。一方、D-A変換は、デジタル信号をアナログ信号に変換することで、スピーカーやアンプなどのアナログ機器で再生することができます。

A-D変換の基本的な仕組み

A-D変換は、アナログ信号を離散的なデジタル値に変換するプロセスです。具体的には、以下のようなステップで行われます。

  1. アナログ信号(Analog Signal)
    物理現象から連続的に生じる信号のことを指します。例えば、音声信号や振動信号がアナログ信号の例です。
  2. 低域通過フィルタ(Low-Pass Filter:LPF)
    信号の周波数成分を制限するフィルタです。次の「標本化」の工程で信号の高周波成分による「エイリアス歪み」という問題を防ぐ目的があります。
    下図ではアナログ信号に含まれていた高周波(細かいギザギザ)がLPFによって除去されています。
  3. 標本化(Sampling)
    アナログ信号を一定周期で観測することを「標本化と呼びます。
    また、1秒間に標本化する回数のことを標本化周波数(サンプリングレート)、標本化する間隔(時間)のことを標本化周期と呼びます。標本化周波数が高ければ高いほど、高い周波数まで忠実に録音することができますが、情報量(ビット数)が膨大になる恐れがあります。また、人間の可聴域(20Hz~20kHz)だけ録音できれば良い場合、「シャノンの標本化定理」に従っておよそ2倍の44.1kHzを設定することがしばしばあります。
    下図では、コンピュータがアナログ信号を観測できた箇所にマーカーと、その箇所のアナログ信号の値を表示しました。
  4. 量子化(Quantization)
    標本化によって観測した数値をある特定のビット数でデジタル化することを「量子化と呼びます。この「特定のビット数」のことを量子化精度と呼び、単位はビット(bit)です。量子化精度が高ければ高いほど、アナログ信号をより忠実に再現した高品質なデジタルデータができあがりますが、情報量(ビット数)が膨大になる恐れがあります。
    下図では、観測した値をコンピュータがデジタル化するときのメモリの粗さを灰点線で表現しています。量子化精度が高ければこのメモリはより細かくなると想像してください。
  5. デジタル信号(Digital Signal)
    量子化された信号をデジタル信号と呼びます。デジタル信号は0と1の2進数で表され、コンピュータが処理することができます。(※下図の表記は16進数)
    また、A-D変換によって得られたデジタル信号はPCM(Pulse Code Modulation)と呼ばれるデータになります。

wavファイルについて知る(.wav)

wavファイルは音楽や音声の記録によく用いられるRIFFベースのコンテナ規格です。
RIFFは「チャンク」と呼ばれるデータの塊(コンテナ)をいくつも集めてひとまとまりのバイナリデータとして保存します。このとき、データの並びはリトルエンディアンとなります。
そのため、wavファイルも「fmtチャンク」「dataチャンク」といったコンテナにパーツが分かれています。

項目byte内容説明
chunkID
chunkSize
fromType
4
4
4
“RIFF”
size + 36
“WAVE”
・chunkを識別するためのID
・「chunkID」「chunkSize」以外のデータサイズの合計
・このファイルがWAVEファイルであることの識別子
chunkID
chunkSize
waveFormatType
channel
samplesPerSec
bytesPerSec
blockSize
bitsPerSample
4
4
2
2
4
4
2
2
“fmt “
16
プロパティ
プロパティ
プロパティ
プロパティ
プロパティ
プロパティ
fmtチャンク・chunkを識別するためのID
・「chunkID」「chunkSize」以外のデータサイズの合計
・音データの形式で、PCMの場合は1
・モノラルであれば1、ステレオならば2
・標本化周波数[Hz]を示す値
・1秒間のサンプルを記録するのに必要なデータ量[byte]
・1サンプルを記録するのに必要なデータ量[byte]
・量子化精度を表す値[bit]
chunkID
chunkSize
data
4
4
size
“data”
size
音データ
dataチャンク・chunkを識別するためのID
・「chunkID」「chunkSize」以外のデータサイズの合計
・音声データ
WAVEファイルの構造

Pythonを使った実装

Pythonにはwavファイルを読み込むためのモジュールが標準的に組み込まれています。
waveモジュールを使用するとwavファイルの読み込み、書き込みが行えます。

import wave

# wavファイルのパス
INPUT_FILENAME = "input.wav"
OUTPUT_FILENAME = "output.wav"

# WAVファイルからデータを読み込む。
with wave.open(INPUT_FILENAME, "rb") as wf:
    fs = wf.getframerate()                  # 標本化周波数(サンプリングレート)
    ch = wf.getnchannels()                  # チャネル数(モノラル:1、ステレオ:2)
    byte_depth = wf.getsampwidth()          # 量子化精度[byte]
    data = wf.readframes(wf.getnframes())   # 音声データ(バイナリデータ)

# WAVファイルにデータを書き込む。
with wave.open(OUTPUT_FILENAME, "wb") as wf:
    wf.setframerate(fs)                     # 標本化周波数(サンプリングレート)
    wf.setnchannels(ch)                     # チャネル数(モノラル:1、ステレオ:2)
    wf.setsampwidth(byte_depth)             # 量子化精度[byte]
    wf.writeframes(data)                    # 音声データ(バイナリデータ)

scipyモジュールを使用するとより短いコードで音声ファイルの読み込み、書き込みが可能です。
読み込み後の音声データはバイナリではなく、Numpyの数値配列となっているためそのままグラフ化することが可能です。

from scipy.io import wavfile
import numpy as np

# wavファイルのパス
INPUT_FILENAME = "input.wav"
OUTPUT_FILENAME = "output.wav"

# WAVファイルからデータを読み込む。
fs, data = wavfile.read(INPUT_FILENAME)

# WAVファイルにデータを書き込む。
wavfile.write(OUTPUT_FILENAME, fs, data.astype(np.int16))

コメント