[Python] wave 教學

程式語言:Python
Package:wave
官方文件

功能:讀寫 WAV 檔

基礎知識

參考此篇 [Python] Pyaudio 教學

讀檔範例

# -*- coding: utf-8 -*-
import wave
import matplotlib.pyplot as plt
import numpy as np

# 讀檔
f = wave.open(r"c:\WINDOWS\Media\ding.wav", "rb")

# 格式資訊
# (nchannels, sampwidth, framerate, nframes, comptype, compname)
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]

# 波形數據
str_data = f.readframes(nframes)
f.close()

# bytes to np.short
wave_data = np.fromstring(str_data, dtype=np.short)
# 轉換為雙聲道
wave_data.shape = -1, 2
wave_data = wave_data.T
# 總時間長度
time = np.arange(0, nframes) * (1.0 / framerate)

# 音訊資訊
print(nchannels, sampwidth, framerate, nframes)

# 畫波形
plt.subplot(211) 
plt.plot(time, wave_data[0])
plt.subplot(212) 
plt.plot(time, wave_data[1], c="g")
plt.xlabel("time (seconds)")
plt.show()

寫檔範例

# -*- coding: utf-8 -*-
import wave
import numpy as np
import matplotlib.pyplot as plt

time = 0.1 # second
framerate = 44100 # Hz

def sinSweepFrq(f0, f1, framerate, time):
    '''
    依線性產生從 f0 到 f1 的 sine
    f0 : 起始頻率
    f1 : 結束頻率
    framerate: 取樣率
    time: 時間長度
    '''
    nframes = time * framerate
    k = (f1-f0)/time
    t = np.linspace(0, time, num=nframes)
    # f = f0 + kt
    # w = 2 * pi * f = 2 * pi * f0 + 2 * pi * kt
    # w 對 t 積分得 phase
    # phase = 2 * pi * f0 * t + pi * kt^2
    phase = 2 * np.pi * f0 * t + np.pi * k * t * t
    
    return np.sin(phase)

nframes = time * framerate

n = np.linspace(0, time, num=nframes)

f_start = 20 # Hz        
f_stop = 1000 # Hz

sinus_f_start = np.sin(2*np.pi*f_start*n)
sinus_f_stop = np.sin(2*np.pi*f_stop*n)
# 產生 10秒 44.1kHz 的 20Hz - 1kHz 的 sine
sinus_f_sweep = sinSweepFrq(f_start, f_stop, framerate, time)

# 先放大以免轉換 np.short 為 0,並且太小聲
wave_data = sinus_f_sweep * 10000
wave_data = wave_data.astype(np.short)

# 寫入檔案
f = wave.open(r"sweep.wav", "wb")

# 設定聲道數、sample 大小(byte) 和取樣頻率
f.setnchannels(1)
f.setsampwidth(2)
f.setframerate(framerate)
# 寫入波形數據
f.writeframes(wave_data.tostring())
f.close()

# 畫圖確認
fig = plt.figure()

fig.add_subplot(411)
plt.plot(n, sinus_f_start)
fig.add_subplot(412)
plt.plot(n, sinus_f_stop)
fig.add_subplot(413)
plt.plot(n, sinus_f_sweep)
fig.add_subplot(414)
plt.plot(n, wave_data)
plt.show()

wave

  • wave.open(file, mode=None)
    • file
      • string
      • file-like object
    • mode
      • 'rb' returns a Wave_read object
      • 'wb' returns a Wave_write object
  • wave.openfp(file, mode)
    • for 相容性,功能同 open()
  • exception
    • wave.Error
      • 違反 WAV 規範或應用上的錯誤

Wave_read

  • 由 open(file, 'rb') 所回傳
  • Wave_read.close()
    • 關閉
  • Wave_read.getnchannels()
    • 取得聲道數 (1 for mono, 2 for stereo)
  • Wave_read.getsampwidth()
    • 取得 sample width (bytes)
  • Wave_read.getframerate()
    • 取得取樣率
  • Wave_read.getnframes()
    • 取得 frames 數目
  • Wave_read.getcomptype()
    • 取得壓縮格式,目前只支援 'NONE',也就是無壓縮
  • Wave_read.getcompname()
    • 取得可讀的壓縮格式,通常為 'not compressed'
  • Wave_read.getparams()
    • 取得所有參數
      (nchannels, sampwidth, framerate, nframes, comptype, compname)
  • Wave_read.readframes(n)
    • 讀取 n frames 為 bytes object
  • Wave_read.rewind()
    • 回到開頭
  • Wave_read.getmarkers()
    • 回傳 None,單純只為了相容 aifc module
  • Wave_read.getmark(id)
    • Raise an error,單純只為了相容 aifc module
  • Wave_read.setpos(pos) 
    • 設定讀取位置
  • Wave_read.tell()
    • 取得目前讀取位置

Wave_write

  • 由 open(file, 'wb') 所回傳
  • 一旦寫入資料,不可再設定任何參數
  • Wave_write.close() 
    • 需確認寫入資料與 nframes 一致,才可關閉
  • Wave_write.setnchannels(n)
    • 設定聲道數 (1 for mono, 2 for stereo)
  • Wave_write.getnchannels()
    • 取得聲道數 (1 for mono, 2 for stereo)
  • Wave_write.setsampwidth(n)
    • 設定 sample width (bytes)
  • Wave_write.getsampwidth(n)
    • 取得 sample width (bytes)
  • Wave_write.setframerate(n)
    • 設定取樣率,非整數自動四捨五入
  • Wave_write.getframerate(n)
    • 取得取樣率
  • Wave_write.setnframes(n)
    • 設定 frames 數目
    • 若不一致,且 stream 可 seek,則會自動更正
  • Wave_write.getnframes(n)
    • 取得 frames 數目
  • Wave_write.setcomptype(type, name)
    • 只支援 type NONE
  • Wave_write.getcomptype()
    • 取得壓縮格式,目前只支援 'NONE',也就是無壓縮
  • Wave_write.getcompname()
    • 取得可讀的壓縮格式,通常為 'not compressed'
  • Wave_write.setparams(tuple)
    • 設定所有參數
      (nchannels, sampwidth, framerate, nframes, comptype, compname) 
  • Wave_write.getparams()
    • 取得所有參數
      (nchannels, sampwidth, framerate, nframes, comptype, compname)
  • Wave_write.tell()
    • 取得目前寫入位置
  • Wave_write.writeframesraw(data)
    • 寫入資料,但不更正 nframes,格式可為 bytes-like object
  • Wave_write.writeframes(data)
    • 寫入資料,並確認 nframes 的正確性
      stream 可 seek 的則會自動更正,格式可為 bytes-like object

參考

声音的输入输出

留言