[Python] Pyaudio 教學

程式語言:Python
Package:pyaudio
官方網站
官方文件

功能:音訊的播放與錄製

基本知識

  • sampling rate
    • 類比訊號轉成數位訊號的取樣率
  • bit depth
    • 用多少 bit 表現一個 sample
  • channel
    • 聲道
    • 1 => mono 為單聲道
      2 => stereo 為雙聲道或又稱立體聲
  • frame
    • 構成一個聲音的最小單位
      即是一組 samples。與 channel 相關
    • 單聲道 ( mono ) = 1 sample = 1 frame
      雙聲道 ( stereo) = 2 samples = 1 frame
      5.1聲道(左, 中央,  右, 右後, 左後, 低音) = 6 samples = 1 frame
    • frame size = bit depth * channels
Blocking 範例
'''
播放特定頻率
'''
import numpy as np
import pyaudio


def sine(frequency, t, sampleRate):
    '''
    產生 sin wave

    :Args:
     - frequency: 欲產生的頻率 Hz
     - t: 播放時間長度 seconds
     - sampleRate: 取樣頻率 1/s
    '''
    # 播放數量
    n = int(t * sampleRate)
    # 每秒轉動的角度再細分為取樣間隔
    interval = 2 * np.pi * frequency / sampleRate
    return np.sin(np.arange(n) * interval)


def play_tone(stream, frequency=440, t=1, sampleRate=44100):
    '''
    播放特定頻率

    :Args:
     - stream: 
     - frequency: 欲產生的頻率 Hz
     - t: 播放時間長度 seconds
     - sampleRate: 取樣頻率 1/s
    '''
    data = sine(frequency, t, sampleRate)
    
    # 因 format 為  pyaudio.paFloat32,故轉換為 np.float32 並轉換為 bytearray
    stream.write(data.astype(np.float32).tostring())


if __name__ == '__main__':
    p = pyaudio.PyAudio()
    stream = p.open(format=pyaudio.paFloat32,
                    channels=1, rate=44100, output=True)

    play_tone(stream, 
     frequency=1000, #Hz
     t=3) #seconds

    stream.close()
    p.terminate()

Non-Blocking 範例
'''
non-blocking
播放特定頻率
'''
import numpy as np
import pyaudio
import time

# frames
CHUNK = 1024
# channels
CH = 1

def sine(frequency, t, sampleRate):
    '''
    產生 sin wave

    :Args:
     - frequency: 欲產生的頻率 Hz
     - t: 播放時間長度 seconds
     - sampleRate: 取樣頻率 1/s
    '''
    # 播放數量
    n = int(t * sampleRate)
    # 每秒轉動的角度再細分為取樣間隔
    interval = 2 * np.pi * frequency / sampleRate
    
    return np.sin(np.arange(n) * interval)

    
def sliceData(frame_count, channels):  
    '''
    切出合適的長度,也就是 frame_count * channels * sampleBytes

    :Args:
     - frame_count: frames 的數目
     - channels: channels 數目
    '''
    data = sine(frequency=1000, t=3, sampleRate=44100)
    
    # 因會再轉換為 np.float32,故無需乘上 sampleBytes
    size = channels * frame_count
    while True:
        dataSlice = data[:size]
        # 此時小數點會用 np.float32 4byte 表示,故資料長度會變為 4 倍
        dataBytes = dataSlice.astype(np.float32).tostring()
        
        yield dataBytes
        # 刪除已輸出資料
        data = np.delete(data, range(size))

    
dataGen = sliceData(CHUNK, CH)
def callback(in_data, frame_count, time_info, status):
    global dataGen
    data = next(dataGen)

    return (data, pyaudio.paContinue)


if __name__ == '__main__':
    p = pyaudio.PyAudio()
    stream = p.open(format=pyaudio.paFloat32,
                    channels=CH, rate=44100, output=True, frames_per_buffer=CHUNK, stream_callback=callback)

    # stream.start_stream()
    
    for i in range(1000):
        print(i)    
        
    stream.stop_stream()
    stream.close()
    p.terminate()

Module pyaudio

  • Classes
    • pyaudio.PyAudio
    • pyaudio.Stream
  • Host Specific Classes
    • pyaudio.PaMacCoreStreamInfo
  • Stream Conversion Convenience Functions
    • pyaudio.get_sample_size(format)
      • 指定格式的大小 (bytes)
      • format
        • 即是下面的 Portaudio Sample Formats
    • pyaudio.get_format_from_width(width, unsigned=True)
      • 指定 sample 大小的格式
        • 除 pyaudio.paInt32 以外的格式
      • width
        • byte 單位,可為 1, 2, 3, 4
      • unsigned
        • 有無負值
  • PortAudio version
    • pyaudio.get_portaudio_version()
      • portaudio 版本 (Int)
      • "19.5.1" 會得到 16進位 0x00130501
    • pyaudio.get_portaudio_version_text() 
      • portaudio 版本 (String)
  • Portaudio Sample Formats
    • pyaudio.paFloat32 = 1
    • pyaudio.paInt32     = 2
    • pyaudio.paInt24     = 4
    • pyaudio.paInt16     = 8
    • pyaudio.paInt8       = 16
    • pyaudio.paUInt8    = 32
    • pyaudio.paCustomFormat = 65536
  • PortAudio Host APIs
    • pyaudio.paInDevelopment
      • Still in development
    • pyaudio.paDirectSound
      • DirectSound (Windows only)
    • pyaudio.paMME
      • Multimedia Extension (Windows only)
    • pyaudio.paASIO
      • Steinberg Audio Stream Input/Output
    • pyaudio.paSoundManager
      • SoundManager (OSX only)
    • pyaudio.paCoreAudio
      • CoreAudio (OSX only)
    • pyaudio.paOSS
      • Open Sound System (Linux only)
    • pyaudio.paALSA
      • Advanced Linux Sound Architecture (Linux only)
    • pyaudio.paAL
      • Open Audio Library
    • pyaudio.paBeOS
      • BeOS Sound System
    • pyaudio.paWDMKS
      • Windows Driver Model (Windows only)
    • pyaudio.paJACK
      • JACK Audio Connection Kit
    • pyaudio.paWASAPI
      • Windows Vista Audio stack architecture
    • pyaudio.paNoDevice
      • Not actually an audio device
  • PortAudio Error Codes
    • pyaudio.paNoError
    • pyaudio.paNotInitialized
    • pyaudio.paUnanticipatedHostError
    • pyaudio.paInvalidChannelCount
    • pyaudio.paInvalidSampleRate
    • pyaudio.paInvalidDevice
    • pyaudio.paInvalidFlag
    • pyaudio.paSampleFormatNotSupported
    • pyaudio.paBadIODeviceCombination
    • pyaudio.paInsufficientMemory
    • pyaudio.paBufferTooBig
    • pyaudio.paBufferTooSmall
    • pyaudio.paNullCallback
    • pyaudio.paBadStreamPtr
    • pyaudio.paTimedOut
    • pyaudio.paInternalError
    • pyaudio.paDeviceUnavailable
    • pyaudio.paIncompatibleHostApiSpecificStreamInfo
    • pyaudio.paStreamIsStopped
    • pyaudio.paStreamIsNotStopped
    • pyaudio.paInputOverflowed
    • pyaudio.paOutputUnderflowed
    • pyaudio.paHostApiNotFound
    • pyaudio.paInvalidHostApi
    • pyaudio.paCanNotReadFromACallbackStream
    • pyaudio.paCanNotWriteToACallbackStream
    • pyaudio.paCanNotReadFromAnOutputOnlyStream
    • pyaudio.paCanNotWriteToAnInputOnlyStream
    • pyaudio.paIncompatibleStreamHostApi 
  • PortAudio Callback Return Codes
    • pyaudio.paContinue
      • There is more audio data to come
    • pyaudio.paComplete
      • This was the last block of audio data
    • pyaudio.paAbort
      • An error ocurred, stop playback/recording
  • PortAudio Callback Flags
    • pyaudio.paInputUnderflow
      • Buffer underflow in input
    • pyaudio.paInputOverflow
      • Buffer overflow in input 
    • pyaudio.paOutputUnderflow
      • Buffer underflow in output
    • pyaudio.paOutputOverflow
      • Buffer overflow in output
    • pyaudio.paPrimingOutput
      • Just priming, not playing yet

class pyaudio.PyAudio

  • 利用此 class 開關 streams
  • Stream Management
    • open(rate, channels, format, input=False, output=False, input_device_index=None, output_device_index=None, frames_per_buffer=1024, start=True, input_host_api_specific_stream_info=None, output_host_api_specific_stream_info=None, stream_callback=None)
      • rate
        • 取樣率 (Hz)
      • channels
        • channels 數目
      • format
        • 即是上面的 Portaudio Sample Formats
      • input
        • 是否為 input
      • output
        • 是否為 output 
      • input_device_index
        • 輸入裝置的 index,None 表預設
      • output_device_index
        • 輸出裝置的 index,None 表預設
      • frames_per_buffer
        • buffer 共有多少 frames
      • start
        • stream 是否立即開始
      • input_host_api_specific_stream_info
        • 使用在 PaMacCoreStreamInfo
      • output_host_api_specific_stream_info
        • 使用在 PaMacCoreStreamInfo 
      • stream_callback
        • 運用在 non-blocking
        • Function 格式如下
      def callback(in_data, # 若 input=True 則傳進錄製的資料,不然為 None
               frame_count, # number of frames
               time_info,   # dictionary
               status_flags)# PortAudio Callback Flags
      
          # 不可使用 Stream.read() or Stream.write()
      
          # out_data 長度應為 frame_count * channels * sampleBytes
          return (out_data, flag)
      
    • close(stream)
      • 通常使用 Stream.close()
    • terminate()
      • 中斷 PortAudio 釋放資源
  • Host API
    • get_host_api_count()
      • Host API 的總數量
    • get_default_host_api_info()
      • 預設 Host API 的資訊
    • get_host_api_info_by_type(host_api_type)
      • 指定 Host API  的資訊
      • host_api_type
        • 即是上面的 PortAudio Host APIs
    • get_host_api_info_by_index(host_api_index)
      • 指定 Host API  的資訊
      • host_api_index
        • 第幾個 Host API
    • get_device_info_by_host_api_device_index(host_api_index, host_api_device_index)
      • 指定 Host API 的指定裝置資訊
      • host_api_index
        • 第幾個 Host API
      • host_api_device_index 
        • Host API 的第幾個裝置
  • Device API
    • get_device_count()
      • 裝置的總數量
    • is_format_supported(rate, input_device=None, input_channels=None, input_format=None, output_device=None, output_channels=None, output_format=None)
      • 格式是否支援
      • rate
        • 取樣率 (Hz)
      • input_device
        • 輸入裝置的 index
      • input_channels
        • 輸入 channels 數目
      • input_format
        • 輸入格式            
      • output_device
        • 輸出裝置的 index           
      • output_channels
        • 輸出 channels 數目           
      • output_format
        • 輸出格式
    • get_default_input_device_info()
      • 預設輸入裝置的資訊
    • get_default_output_device_info()
      • 預設輸出裝置的資訊
    • get_device_info_by_index(device_index)
      • 指定裝置的資訊
      • device_index
        • 第幾個裝置
  • Stream Format Conversion
    • get_sample_size(format)
      • 指定格式的大小 (bytes)
      • format
        • 即是上面的 Portaudio Sample Formats
    • get_format_from_width(width, unsigned=True)
      • 指定 sample 大小的格式
        • 除 pyaudio.paInt32 以外的格式
      • width
        • byte 單位,可為 1, 2, 3, 4
      • unsigned
        • 有無負值

class pyaudio.Stream

  • PortAudio Stream Wrapper
  • 需用 PyAudio.open() 建立
  • Opening and Closing
    • __init__(PA_manager, rate, channels, format, input=False, output=False, input_device_index=None, output_device_index=None, frames_per_buffer=1024, start=True, input_host_api_specific_stream_info=None, output_host_api_specific_stream_info=None, stream_callback=None)
      • 必須由 PyAudio.open() 呼叫,可為 input or output 甚至皆可
      • PA_manager
        • PyAudio instance
      • rate
        • 取樣率 (Hz)
      • channels
        • channels 數目
      • format
        • 即是上面的 Portaudio Sample Formats
      • input
        • 是否為 input
      • output
        • 是否為 output 
      • input_device_index
        • 輸入裝置的 index,None 表預設
      • output_device_index
        • 輸出裝置的 index,None 表預設
      • frames_per_buffer
        • buffer 共有多少 frames
      • start
        • stream 是否立即開始
      • input_host_api_specific_stream_info
        • 使用在 PaMacCoreStreamInfo
      • output_host_api_specific_stream_info
        • 使用在 PaMacCoreStreamInfo 
      • stream_callback
        • 運用在 non-blocking
        • Function 格式如下
      def callback(in_data, # 若 input=True 則傳進錄製的資料,不然為 None
               frame_count, # number of frames
               time_info,   # dictionary
               status_flags)# PortAudio Callback Flags
      
          # 不可使用 Stream.read() or Stream.write()
      
          # out_data 長度應為 frame_count * channels * sampleBytes
          return (out_data, flag)
      
    • close()
      • 關閉 stream
  • Stream Info
    • get_input_latency()
      • input 延遲
    • get_output_latency()
      • output 延遲
    • get_time()
      • stream 時間
    • get_cpu_load() 
      • CPU 使用率,只適用於 non-blocking
  • Stream Management
    • start_stream()
      • 開始執行 stream
    • stop_stream()
      • 停止執行 stream
    • is_active()
      • stream 是否啟動
    • is_stopped() 
      • stream 是否停止
  • Input Output
    • write(frames, num_frames=None, exception_on_underflow=False)
      • 寫入資料於 stream
      • 不可使用在 non-blocking mode
      • frames
        • 寫入的資料
      • num_frames
        • 寫入的 frames 數目,None 表示自動計算
      • exception_on_underflow
        • underflow 是否丟出 exception
    • read(num_frames, exception_on_overflow=True)
      • 從 stream 讀取資料
      • 不可使用在 non-blocking mode
      • num_frames
        • 讀取的 frames 數目
      • exception_on_overflow
        • overflow 是否丟出 exception
    • get_read_available()
      • 無需等待的可讀 frames 數目
    • get_write_available()
      • 無需等待的可寫 frames 數目

參考

Android Audio 裡的各種設定和其關係 (一)

留言

  1. 感謝您的分享!我參考您的程式碼寫了產生雙音多頻訊號的程式,這是我寫的筆記 https://keejko.blogspot.com/2019/08/python.html

    回覆刪除

張貼留言