[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 範例
  1. '''
  2. 播放特定頻率
  3. '''
  4. import numpy as np
  5. import pyaudio
  6.  
  7.  
  8. def sine(frequency, t, sampleRate):
  9. '''
  10. 產生 sin wave
  11.  
  12. :Args:
  13. - frequency: 欲產生的頻率 Hz
  14. - t: 播放時間長度 seconds
  15. - sampleRate: 取樣頻率 1/s
  16. '''
  17. # 播放數量
  18. n = int(t * sampleRate)
  19. # 每秒轉動的角度再細分為取樣間隔
  20. interval = 2 * np.pi * frequency / sampleRate
  21. return np.sin(np.arange(n) * interval)
  22.  
  23.  
  24. def play_tone(stream, frequency=440, t=1, sampleRate=44100):
  25. '''
  26. 播放特定頻率
  27.  
  28. :Args:
  29. - stream:
  30. - frequency: 欲產生的頻率 Hz
  31. - t: 播放時間長度 seconds
  32. - sampleRate: 取樣頻率 1/s
  33. '''
  34. data = sine(frequency, t, sampleRate)
  35. # 因 format 為 pyaudio.paFloat32,故轉換為 np.float32 並轉換為 bytearray
  36. stream.write(data.astype(np.float32).tostring())
  37.  
  38.  
  39. if __name__ == '__main__':
  40. p = pyaudio.PyAudio()
  41. stream = p.open(format=pyaudio.paFloat32,
  42. channels=1, rate=44100, output=True)
  43.  
  44. play_tone(stream,
  45. frequency=1000, #Hz
  46. t=3) #seconds
  47.  
  48. stream.close()
  49. p.terminate()
  50.  
Non-Blocking 範例
  1. '''
  2. non-blocking
  3. 播放特定頻率
  4. '''
  5. import numpy as np
  6. import pyaudio
  7. import time
  8.  
  9. # frames
  10. CHUNK = 1024
  11. # channels
  12. CH = 1
  13.  
  14. def sine(frequency, t, sampleRate):
  15. '''
  16. 產生 sin wave
  17.  
  18. :Args:
  19. - frequency: 欲產生的頻率 Hz
  20. - t: 播放時間長度 seconds
  21. - sampleRate: 取樣頻率 1/s
  22. '''
  23. # 播放數量
  24. n = int(t * sampleRate)
  25. # 每秒轉動的角度再細分為取樣間隔
  26. interval = 2 * np.pi * frequency / sampleRate
  27. return np.sin(np.arange(n) * interval)
  28.  
  29. def sliceData(frame_count, channels):
  30. '''
  31. 切出合適的長度,也就是 frame_count * channels * sampleBytes
  32.  
  33. :Args:
  34. - frame_count: frames 的數目
  35. - channels: channels 數目
  36. '''
  37. data = sine(frequency=1000, t=3, sampleRate=44100)
  38. # 因會再轉換為 np.float32,故無需乘上 sampleBytes
  39. size = channels * frame_count
  40. while True:
  41. dataSlice = data[:size]
  42. # 此時小數點會用 np.float32 4byte 表示,故資料長度會變為 4 倍
  43. dataBytes = dataSlice.astype(np.float32).tostring()
  44. yield dataBytes
  45. # 刪除已輸出資料
  46. data = np.delete(data, range(size))
  47.  
  48. dataGen = sliceData(CHUNK, CH)
  49. def callback(in_data, frame_count, time_info, status):
  50. global dataGen
  51. data = next(dataGen)
  52.  
  53. return (data, pyaudio.paContinue)
  54.  
  55.  
  56. if __name__ == '__main__':
  57. p = pyaudio.PyAudio()
  58. stream = p.open(format=pyaudio.paFloat32,
  59. channels=CH, rate=44100, output=True, frames_per_buffer=CHUNK, stream_callback=callback)
  60.  
  61. # stream.start_stream()
  62. for i in range(1000):
  63. print(i)
  64. stream.stop_stream()
  65. stream.close()
  66. 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

    回覆刪除

張貼留言