SoundCard Python Library

repository·master·Indexed 20 days ago

https://github.com/bastibe/soundcard

A cross-platform Python library for playing and recording audio using CFFI to interface with native backends: PulseAudio on Linux, CoreAudio on macOS, and WASAPI on Windows. It provides a two-tier abstraction model using handles (Speaker, Microphone) and streams (Player, Recorder) to manage audio hardware, handling all data as Numpy arrays.

Tokens
2.3K
Snippets
5
Records
10
Agent score
23%

What's inside SoundCard

  1. Configure Channel Maps for Multi-channel Audio

    master

    If you need to record or play only a subset of available channels, use the channels parameter.

    Backend Behavior:

    • WASAPI (Windows) & CoreAudio (macOS): Indices refer to the physical output channels of the device.
    • PulseAudio (Linux): Specifiers refer to logical channel positions. You can use integer indices or name strings (e.g., 'left', 'right').

    PulseAudio Channel Names: Common identifiers include: 'mono' (index -1), 'left', 'right', 'center', 'rear-center', 'rear-left', 'rear-right', 'lfe', 'front-left-of-center', 'front-right-of-center', 'side-left', and 'side-right'.

    To see the mapping of positions to indices for your specific backend, call sc.channel_name_map().

    import soundcard as sc
    
    # Record one second of audio from backend channels 0, 1, 2, and 3
    data = default_mic.record(samplerate=48000, channels=[0, 1, 2, 3], numframes=48000)
    
    # Play back the recorded audio in reverse channel order using names (PulseAudio only)
    default_speaker.play(data=data, channels=['right', 'left'], samplerate=48000)
  2. How SoundCard handles and streams work together

    master

    SoundCard uses a two-tier abstraction model to manage audio hardware:

    1. Sound Card Handles (_Speaker and _Microphone): These are lightweight objects that act as references to your audio hardware. They do not perform audio processing themselves. Because hardware configuration can change (e.g., plugging in a USB headset), you should always retrieve fresh instances from the library instead of storing long-lived references.

    2. Sound Streams (_Player and _Recorder): These are context managers that perform the actual heavy lifting of interfacing with backend drivers to play or record audio. You obtain these by calling methods on your handles.

    Workflow Summary:

    • Use soundcard functions to get a handle (_Speaker or _Microphone).
    • Use the handle to create a stream (_Player or _Recorder).
    • Use the stream (typically via a with statement) to process audio.
  3. Setup SoundCard on Headless Raspberry Pi

    master

    Since Raspberry Pi OS Lite does not include PulseAudio by default, you must install it and adjust the configuration to avoid mono-only output:

    1. Install dependencies:
      sudo apt install -y python3-pip python3-numpy pulseaudio
    2. Edit the PulseAudio profile configuration:
      sudo nano /usr/share/pulseaudio/alsa-mixer/profile-sets/default.conf
      Comment out the block [Mapping analog-mono] by adding a ; at the start of the lines.
    3. Start PulseAudio:
      pulseaudio -D
    4. Install SoundCard via pip:
      python3 -m pip install soundcard
    sudo apt install -y python3-pip python3-numpy pulseaudio
    # Edit /usr/share/pulseaudio/alsa-mixer/profile-sets/default.conf to comment out [Mapping analog-mono]
    pulseaudio -D
    python3 -m pip install soundcard
  4. Reduce Audio Latency

    master

    By default, SoundCard uses the operating system's default configuration, which may result in high latency. To request lower latency, follow these steps:

    1. Set blocksize: Pass a blocksize argument to .recorder() or .player(). This tells the OS your desired latency.
    2. Optimize numframes: For optimal latency, use a numframes value significantly lower than the blocksize (e.g., by a factor of 2 or 4).
    3. Immediate Data: Use numframes=None in the .record() function to return whatever audio data is available immediately without buffering.
    4. Windows/WASAPI: Try setting exclusive_mode=True in the player/recorder (experimental).
    5. Linux/PulseAudio: If experiencing choppy audio, consider editing /etc/pulse/default.pa to replace load-module module-udev-detect with load-module module-udev-detect tsched=0 and restarting PulseAudio with pulseaudio -k.
  5. Troubleshoot Known Issues

    master

    Windows/WASAPI

    • Single Channel Recording: Currently records garbage if only a single channel is requested. Use multi-channel or channel maps instead.
    • Blocksize Ignored: blocksize might be ignored unless exclusive_mode=True is used.
    • Buffer Underruns: If underruns occur, use a larger blocksize than numframes.

    macOS

    • Silence during Recording: If you record silence, ensure your terminal or the application running the script has been granted Microphone permissions in System Settings.

    General

    • Error Messages: Errors may report internal CFFI or backend-specific issues.
  6. Record and Play Audio with Speaker and Microphone objects

    master

    The Speaker and Microphone objects provide direct methods for one-off recording and playback.

    Important Data Format: All input and output data are handled as Numpy arrays with the shape (frames × channels). To avoid clipping, ensure all data values are restricted between -1 and 1 (0dBFS).

    import soundcard as sc
    import numpy
    
    default_mic = sc.default_microphone()
    default_speaker = sc.default_speaker()
    
    # Record one second of audio at 48kHz
    data = default_mic.record(samplerate=48000, numframes=48000)
    
    # Play back with normalized volume to prevent clipping
    # (dividing by the max absolute value scales data to [-1, 1])
    default_speaker.play(data/numpy.max(numpy.abs(data)), samplerate=48000)
  7. Continuous Audio Recording and Playback using Recorder and Player

    master

    For continuous audio streams, use the recorder() and player() context managers provided by Microphone and Speaker objects. This approach is more efficient for real-time processing loops.

    import soundcard as sc
    
    default_mic = sc.default_microphone()
    default_speaker = sc.default_speaker()
    
    # Use context managers for continuous streaming
    with default_mic.recorder(samplerate=48000) as mic, \
          default_speaker.player(samplerate=48000) as sp:
        for _ in range(100):
            data = mic.record(numframes=1024)
            sp.play(data)
  8. Create audio streams with Player and Recorder

    master

    To actually play or record audio, you must create stream objects from your device handles. These objects are context managers.

    For Playback:

    • Call _Speaker.play(...) for a quick playback operation.
    • Call _Speaker.player() to get a _Player object for more granular control.

    For Recording:

    • Call _Microphone.record(...) for a quick recording operation.
    • Call _Microphone.recorder() to get a _Recorder object for more granular control.
  9. Discover Speakers and Microphones

    master

    Use soundcard to list all available audio devices or retrieve the system's default devices. You can also search for specific devices using substring or fuzzy matching.

    • sc.all_speakers(): Returns a list of all Speaker objects.
    • sc.default_speaker(): Returns the system's default Speaker object.
    • sc.all_microphones(): Returns a list of all Microphone objects.
    • sc.default_microphone(): Returns the system's default Microphone object.
    • sc.get_speaker(name): Searches for a speaker by name (supports substring and fuzzy matching).
    • sc.get_microphone(name): Searches for a microphone by name (supports substring and fuzzy matching).
    import soundcard as sc
    
    # Get lists and defaults
    speakers = sc.all_speakers()
    default_speaker = sc.default_speaker()
    mics = sc.all_microphones()
    default_mic = sc.default_microphone()
    
    # Search for specific hardware
    speaker = sc.get_speaker('Scarlett')
    microphone = sc.get_microphone('Scarlett')
  10. Retrieve sound card handles

    master

    You can discover and select audio devices using the following entry point functions. These return lightweight _Speaker or _Microphone objects.

    Speakers (Output):

    • default_speaker(): Returns the current system default speaker.
    • get_speaker(name): Returns a specific speaker by name.
    • all_speakers(): Returns a list of all available speakers.

    Microphones (Input):

    • default_microphone(): Returns the current system default microphone.
    • get_microphone(name): Returns a specific microphone by name.
    • all_microphones(): Returns a list of all available microphones.