顯示聲波圖形
這篇文章會使用 Python 內建的 wave 函式庫讀取的 wav 聲音檔,並透過 matplotlib 第三方函式庫,顯示聲音檔案 ( 錄音檔、mp3...等 ) 的聲波圖形。
因為程式中會使用到電腦的麥克風,所以請使用本機環境 ( 參考:使用 Python 虛擬環境 ) 或使用 Anaconda Jupyter 進行實作 ( 參考:使用 Anaconda ),如果不需錄音,則可使用 Google Colab 實作,不用安裝任何軟體 ( 參考:使用 Google Colab )。
安裝 matplotlib 函式庫
輸入下列指令安裝 matplotlib 函式庫 ( 依照各人環境可使用 pip、pip3 ),相關教學可參考:matplotlib 圖表教學
!pip install matplotlib
開啟 wav 聲音檔,顯示聲波圖形
載入 matplotlib、numpy 和 wave 函式庫之後,參考下方的程式碼,執行後就能繪製出聲波圖形。
import os
os.chdir('/content/drive/MyDrive/Colab Notebooks')  # 使用 Colab 要換路徑使用
import matplotlib.pyplot as plt
import numpy as np
import wave
fig, ax = plt.subplots()  # 建立單一圖表
# 建立繪製聲波的函式
def visualize(path):
    raw = wave.open(path)          # 開啟聲音
    signal = raw.readframes(-1)    # 讀取全部聲音採樣
    signal = np.frombuffer(signal, dtype ="int16")  # 將聲音採樣轉換成 int16 的格式所組成的 np 陣列
    f_rate = raw.getframerate()    # 取得 framerate
    time = np.linspace(0, len(signal)/f_rate, num = len(signal))  # 根據聲音採樣產生成對應的時間
    ax.plot(time, signal)          # 畫線,橫軸時間,縱軸陣列值
    plt.title("Sound Wave")        # 圖表標題
    plt.xlabel("Time")             # 橫軸標題
    plt.show()
visualize('oxxostudio.wav')        # 讀取聲音
顯示 mp3 聲波圖形
如果要顯示 mp3 的聲波圖形,必須要先使用 pydub 函式庫,將 mp3 轉換成 wav,接著再使用同樣的方法開啟 wav,就能顯示聲波圖形。
import os
os.chdir('/content/drive/MyDrive/Colab Notebooks')  # 使用 Colab 要換路徑使用
import matplotlib.pyplot as plt
import numpy as np
import wave
from pydub import AudioSegment                 # 載入 pydub 的 AudioSegment 模組
song = AudioSegment.from_mp3("oxxostudio.mp3") # 讀取 mp3 檔案
song.export("oxxostudio.wav", format="wav")    # 轉換並儲存為 wav
fig, ax = plt.subplots()
def visualize(path):
    raw = wave.open(path)
    signal = raw.readframes(-1)
    signal = np.frombuffer(signal, dtype ="int16")
    f_rate = raw.getframerate()
    time = np.linspace(0, len(signal)/f_rate, num = len(signal))
    ax.plot(time, signal)
    plt.title("Sound Wave")
    plt.xlabel("Time")
    plt.show()
visualize('oxxostudio.wav')
顯示錄音檔案聲波圖形
參考「麥克風錄音」文章,在程式中加入繪製聲波圖形的功能,就能在錄音之後,即時顯示錄音檔案的聲波圖形 ( 先將錄音檔存成 wav,然後再讀取進行顯示 ),此外,程式裡使用了「平行任務處理」的技巧 ( 參考:輸入文字,停止函式執行 ),開始錄音之後,只要按下鍵盤的 a 就會停止錄音。
import pyaudio
import wave
from concurrent.futures import ThreadPoolExecutor
import numpy as np
import matplotlib.pyplot as plt
chunk = 1024                     # 記錄聲音的樣本區塊大小
sample_format = pyaudio.paInt16  # 樣本格式,可使用 paFloat32、paInt32、paInt24、paInt16、paInt8、paUInt8、paCustomFormat
channels = 2                     # 聲道數量
fs = 44100                       # 取樣頻率,常見值為 44100 ( CD )、48000 ( DVD )、22050、24000、12000 和 11025。
filename = "oxxostudio.wav"      # 錄音檔名
p = pyaudio.PyAudio()            # 建立 pyaudio 物件
# 開啟錄音串流
stream = p.open(format=sample_format, channels=channels, rate=fs, frames_per_buffer=chunk, input=True)
frames = []                      # 建立聲音串列
run = True                       # 設定開始錄音
fig, ax = plt.subplots()         # 建立單一圖表
# 定義錄音的函式
def record():
    global run, stream, p, frames, wf
    print("錄音開始...")
    while run:
        data = stream.read(chunk)
        frames.append(data)        # 將聲音記錄到串列中
    stream.stop_stream()           # 停止錄音
    stream.close()                 # 關閉串流
    p.terminate()
    print('錄音結束...')
    wf = wave.open(filename, 'wb') # 開啟聲音記錄檔
    wf.setnchannels(channels)      # 設定聲道
    wf.setsampwidth(p.get_sample_size(sample_format))  # 設定格式
    wf.setframerate(fs)              # 設定取樣頻率
    wf.writeframes(b''.join(frames)) # 存檔
    wf.close()
    visualize(filename)              # 執行畫圖函式
# 定義鍵盤按鍵函式
def keyin():
    global run
    if input() == 'a':
        run = False                # 如果按下 a,就上 run 等於 False
# 定義繪製圖表函式
def visualize(path):
    print('畫圖...')
    raw = wave.open(path)          # 開啟聲音
    signal = raw.readframes(-1)    # 讀取全部聲音採樣
    signal = np.frombuffer(signal, dtype ="int16")  # 將聲音採樣轉換成 int16 的格式所組成的 np 陣列
    f_rate = raw.getframerate()    # 取得 framerate
    time = np.linspace(0, len(signal)/f_rate, num = len(signal))  # 根據聲音採樣產生成對應的時間
    ax.plot(time, signal)          # 畫線,橫軸時間,縱軸陣列值
    plt.title("Sound Wave")        # 圖表標題
    plt.xlabel("Time")             # 橫軸標題
    plt.show()
executor = ThreadPoolExecutor()    # 平行任務處理
e2 = executor.submit(keyin)
e1 = executor.submit(record)
executor.shutdown()
意見回饋
如果有任何建議或問題,可傳送「意見表單」給我,謝謝~