pyalsaaudio

repository·main·Indexed 42 days ago

https://github.com/larsimmisch/pyalsaaudio

Python wrappers for the ALSA (Advanced Linux Sound Architecture) API, version 0.11.0. It provides tools for interacting with PCM devices for audio playback and capture, as well as managing audio mixers on Linux systems.

Tokens
5.8K
Snippets
18
Records
28
Agent score
52%

What's inside pyalsaaudio

  1. Understand Sample Size and Bit Depth

    main

    Audio samples are defined by three different bit-width concepts:

    • physical_bits: The actual physical space a sample occupies in memory.
    • nominal_bits: The number of least significant bits used (often referred to as the bit depth or format width).
    • significant_bits: The number of most significant bits used, representing the actual sample resolution.

    Visual Representation:

    MSB |00000000 XXXXXXXX XXXXXXXX 00000000| LSB
                      |--significant--|
                      |---------nominal---------|
            |-------------physical--------------|
  2. Manage latency and stability with Periods and Buffers

    main

    The relationship between buffer size and period size determines the performance and stability of your audio application:

    • Period: A chunk of frames processed by the CPU. Completing a period triggers a CPU interrupt.
    • Buffer: The total memory allocated for the audio stream. The kernel buffer must hold at least two periods (one being processed by hardware, one by the CPU).
    • Xruns (Overrun/Underrun): Occur when the data rate requirement is not met.
      • Overrun: Occurs during capture (input) when the buffer fills up faster than the CPU can read it.
      • Underrun: Occurs during playback (output) when the CPU fails to provide data fast enough.

    Trade-offs

    ConfigurationLatencyXrun ResilienceCPU Usage
    Smaller Period SizeLowerLowerHigher
    Larger Period SizeHigherHigherLower
    Larger Buffer SizeHigherHigherN/A

    Note: Increasing the number of periods within a buffer can improve resilience without necessarily increasing latency as much as increasing the total buffer size.

  3. Configure Period Size in pyalsaaudio

    main

    In pyalsaaudio, the period size is defined in frames, not bytes.

    When you set a period size (e.g., to 32), you are instructing the API that:

    1. Each write operation should ideally contain exactly that many frames.
    2. Each read operation will return either that many frames or nothing at all.

    Incorrectly calculating this value based on bytes instead of frames will lead to unexpected audio behavior or errors.

  4. Understand PCM audio units: Samples, Frames, and Rates

    main

    When working with pyalsaaudio, it is essential to distinguish between these fundamental audio units:

    • Sample: The amplitude of a single channel at a specific point in time. Precision varies (e.g., 8-bit to 64-bit) and formats can be big/little endian integers or floating point.
    • Frame: A collection of exactly one sample per channel.
      • Mono: 1 frame = 1 sample.
      • Stereo: 1 frame = 2 samples.
    • Rate (Sample Rate): How many frames are processed per second (measured in Hz). For example, 44100 Hz means 44,100 frames per second.
    • Frame Size: The size in bytes of a single frame.
      • Calculation: (number of channels) * (bytes per sample).
    • Data Rate: The total bytes per second required for the stream.
      • Calculation: rate * frame_size.
  5. Verify PCM Playback and Recording

    main

    Before using PyAlsaAudio, verify that your system's ALSA configuration is working correctly using the aplay command.

    Testing Playback:

    1. Ensure aplay can play a file through your target soundcard.
    2. Run the provided playback script:
      python playwav.py <filename.wav>
    3. If you need to specify a specific device (e.g., for Raspberry Pi HiFiBerry), use the -d flag:
      python playwav.py -d hw:CARD=sndrpihifiberry,DEV=0 <filename.wav>

    Testing Recording:

    1. Verify your microphone is functional.
    2. Run the recording test script:
      python recordtest.py -d <device> <filename>
    3. Interrupt the recording using Ctrl-C.
    4. Verify the recording by playing it back:
      python playbacktest.py -d <device> <filename>
    # Playback with specific device
    $ python playwav.py -d hw:CARD=sndrpihifiberry,DEV=0 <filename.wav>
    
    # Recording
    $ python recordtest.py -d <device> <filename>
    
    # Playback test
    $ python playbacktest.py -d <device> <filename>
  6. Manual installation of pyalsaaudio

    main

    Manual installation requires a kernel with ALSA support, as well as the ALSA library and headers.

    Prerequisites by distribution:

    • Debian/Ubuntu: Install libasound2-dev.
    • Arch: Install alsa-lib.
    • Other: Ensure your system has libasound.so and asoundlib.h available.

    Installation steps:

    1. Clone the repository.
    2. Navigate to the source directory.
    3. Build and install using pip install ..
    $ git clone https://github.com/larsimmisch/pyalsaaudio.git
    $ cd pyalsaaudio
    $ pip install .
  7. Install PyAlsaAudio

    main

    PyAlsaAudio requires the ALSA user-space C library (libasound) and ALSA headers to be present on your system for compilation.

    Prerequisites:

    • Ensure /usr/lib/libasound.so and /usr/include/alsa (or equivalent paths) exist.
    • On Debian or Ubuntu, install the development package: sudo apt-get install libasound2-dev.
    • A Linux kernel with ALSA support (standard in kernel 2.6+).

    To install from the source directory, use pip:

    $ pip install .
  8. Explore pyalsaaudio API examples

    main

    The source distribution includes several example programs to demonstrate different capabilities of the API:

    • playwav.py: Plays back a .wav file.
    • playbacktest.py: Plays back raw sound data read from stdin.
    • recordtest.py: Captures sound from the microphone and writes raw data to stdout.
    • mixertest.py: Demonstrates how to manipulate ALSA mixers.
  9. Initialize a PCM object for playback or capture

    main

    The PCM class represents a device used for playing (PCM_PLAYBACK) or recording (PCM_CAPTURE) sound. When instantiating a PCM object, you can specify parameters like sampling rate, channels, and format.

    Important: ALSA may not support your exact requested configuration. It is highly recommended to call the .info() method after instantiation to verify the actual realized configuration (e.g., rate, channels, format) against your requirements.

    import alsaaudio
    
    # Example: Initialize a playback device
    player = alsaaudio.PCM(
        type=alsaaudio.PCM_PLAYBACK,
        mode=alsaaudio.PCM_NORMAL,
        rate=44100,
        channels=2,
        format=alsaaudio.PCM_FORMAT_S16_LE
    )
    
    # Verify the actual configuration
    print(player.info())
  10. Handle enumerated mixer controls

    main

    Some mixer controls are enumerated (they have a list of selectable items rather than a range of values).

    Methods:

    • getenum() -> tuple[str, list[str]]: Returns a tuple containing the currently selected item and a list of all available items. Returns an empty tuple if the control is not enumerated.
    • setenum(index: int) -> None: Sets the currently selected item using the index from the list returned by getenum.
    import alsaaudio
    
    # Example: Selecting a mono output mode
    m = alsaaudio.Mixer('Mono Output Select')
    current, items = m.getenum()
    print(f"Current: {current}, Options: {items}")
    
    # Set to the first available item
    m.setenum(0)
  11. Play audio using PCM.write()

    main

    To play sound, use PCM.write(data: bytes).

    Requirements & Behavior:

    • data must be a multiple of the frame size.
    • It is recommended that data is exactly the size of one period.
    • Return Value: Returns the number of frames written.
    • Blocking Behavior: In PCM_NORMAL mode, this blocks if the kernel buffer is full. In PCM_NONBLOCK mode, it returns 0 immediately if the buffer is full.
    • Errors: Returns -EPIPE in case of a buffer underrun. To recover, call write again with the same data.

    Best Practice: For simple playback, use PCM_NORMAL mode and write as much data as possible. For GUI applications, preload the buffer with a few periods and use a timer to write one period's worth of data at a time to avoid underrun clicks.

    # Writing a chunk of data
    # data should be bytes
    frames_written = player.write(audio_bytes)
    if frames_written < 0:
        print("Underrun occurred!")