Code Monkey home page Code Monkey logo

soundcard's Introduction

SoundCard

version python status license

contributors downloads

SoundCard is a library for playing and recording audio without resorting to a CPython extension. Instead, it is implemented using the wonderful CFFI and the native audio libraries of Linux, Windows and macOS.

The input and output data are scaled to 0dBFS (Full Scale). To avoid clipping restrict all data between -1 and 1.

SoundCard is cross-platform, and supports Linux/pulseaudio, Mac/coreaudio, and Windows/WASAPI. While the programming interface is identical across platforms, sound card naming schemes and default block sizes can vary between devices and platforms.

SoundCard is still in development. All major features work on all platforms, but there are a few known issues that still need to be fixed. If you find a bug, please open an Issue, and I will try to fix it. Or open a Pull Request, and I will try to include your fix into SoundCard.

However, please be aware that this is a hobby project of mine that I am developing for free, and in my spare time. While I try to be as accomodating as possible, I can not guarantee a timely response to issues. Publishing Open Source Software on Github does not imply an obligation to fix your problem right now. Please be civil.

SoundCard is licensed under the terms of the BSD 3-clause license
(c) 2016 Bastian Bechtold

open-issues closed-issues open-prs closed-prs

Tutorial

Here is how you get to your Speakers and Microphones:

import soundcard as sc

# get a list of all speakers:
speakers = sc.all_speakers()
# get the current default speaker on your system:
default_speaker = sc.default_speaker()
# get a list of all microphones:
mics = sc.all_microphones()
# get the current default microphone on your system:
default_mic = sc.default_microphone()

# search for a sound card by substring:
>>> sc.get_speaker('Scarlett')
<Speaker Focusrite Scarlett 2i2 (2 channels)>
>>> one_mic = sc.get_microphone('Scarlett')
<Microphone Focusrite Scalett 2i2 (2 channels)>
# fuzzy-search to get the same results:
one_speaker = sc.get_speaker('FS2i2')
one_mic = sc.get_microphone('FS2i2')

All of these functions return Speaker and Microphone objects, which can be used for playback and recording. All data passed in and out of these objects are frames × channels Numpy arrays.

import numpy

>>> print(default_speaker)
<Speaker Focusrite Scarlett 2i2 (2 channels)>
>>> print(default_mic)
<Microphone Focusrite Scarlett 2i2 (2 channels)>

# record and play back one second of audio:
data = default_mic.record(samplerate=48000, numframes=48000)
# normalized playback
default_speaker.play(data/numpy.max(numpy.abs(data)), samplerate=48000)

# alternatively, get a `Recorder` and `Player` object
# and play or record continuously:
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)

Latency

By default, SoundCard records and plays at the operating system's default configuration. Particularly on laptops, this configuration might have extreme latencies, up to multiple seconds.

In order to request lower latencies, pass a blocksize to player or recorder. This tells the operating system your desired latency, and it will try to honor your request as best it can. On Windows/WASAPI, setting exclusive_mode=True might help, too (this is currently experimental).

Another source of latency is in the record function, which buffers output up to the requested numframes. In general, for optimal latency, you should use a numframes significantly lower than the blocksize above, maybe by a factor of two or four.

To get the audio data as quickly as absolutely possible, you can use numframes=None, which will return whatever audio data is available right now, without any buffering. Note that this might receive different numbers of frames each time.

With the above settings, block sizes of 256 samples or ten milliseconds are usually no problem. The total latency of playback and recording is dependent on how these buffers are handled by the operating system, though, and might be significantly higher.

Additionally, it might help to experiment with advice from here: https://askubuntu.com/questions/707171/how-can-i-fix-choppy-audio and edit your /etc/pulse/default.pa file to replace the line saying

load-module module-udev-detect

with

load-module module-udev-detect tsched=0

and then do not forget to restart pulseaudio with

pulseaudio -k

Channel Maps

Some professional sound cards have large numbers of channels. If you want to record or play only a subset of those channels, you can specify a channel map. A channel map consists of a list of channel specifiers, which refer to the channels of the audio backend in use. The index of each of those specifiers in the the channel map list indicates the channel index in the numpy data array used in SoundCard:

# record one second of audio from backend channels 0 to 3:
data = default_mic.record(samplerate=48000, channels=[0, 1, 2, 3], numframes=48000)

# play back the recorded audio in reverse channel order:
default_speaker.play(data=data, channels=[3, 2, 1, 0], samplerate=48000)

The meaning of the channel specifiers depend on the backend in use. For WASAPI (Windows) and CoreAudio (macOS) the indices refer to the physical output channels of the sound device in use. For the PulseAudio backend (Linux) the specifiers refer to logical channel positions instead of physical hardware channels.

The channel position identifiers in the PulseAudio backend are based on: https://freedesktop.org/software/pulseaudio/doxygen/channelmap_8h.html Since the mapping of position indices to audio channels is not obvious, a dictionary containing all possible positions and channel indices can be retrieved by calling channel_name_map(). The positions for the indices up to 10 are:

'mono': -1,
'left': 0,
'right': 1,
'center': 2,
'rear-center': 3,
'rear-left': 4,
'rear-right': 5,
'lfe': 6,
'front-left-of-center': 7,
'front-right-of-center': 8,
'side-left': 9,
'side-right': 10

The identifier mono or the index -1 can be used for mono mix of all channels for both playback and recording. (CoreAudio/macOS defines channel -1 as silence for both playback and recording.) In addition to the indices, the PulseAudio backend allows the use of the name strings to define a channel map:

# This example plays one second of noise on each channel defined in the channel map consecutively.
# The channel definition scheme using strings only works with the PulseAudio backend!

# This defines a channel map for a 7.1 audio sink device
channel_map = ['left', 'right', 'center', 'lfe', 'rear-left', 'rear-right', 'side-left', 'side-right']

num_channels = len(channel_map)
samplerate = 48000

# Create the multi channel noise array.
noise_samples = 48000
noise = numpy.random.uniform(-0.1, 0.1, noise_samples)
data = numpy.zeros((num_channels * noise_samples, num_channels), dtype=numpy.float32)
for channel in range(num_channels):
    data[channel * noise_samples:(channel + 1) * noise_samples, channel] = noise

# Playback using the 7.1 channel map.
default_speaker.play(data=data, channels=channel_map, samplerate=samplerate)

The available channels of each PulseAudio source or sink can be listed by

> pactl list sinks
> pactl list sources

The Channel Map property lists the channel identifier of the source/sink.

> pactl list sinks | grep  "Channel Map" -B 6

Sink #486
    State: SUSPENDED
    Name: alsa_output.usb-C-Media_Electronics_Inc._USB_Advanced_Audio_Device-00.analog-stereo
    Description: USB Advanced Audio Device Analog Stereo
    Driver: PipeWire
    Sample Specification: s24le 2ch 48000Hz
    Channel Map: front-left,front-right
--
Sink #488
        State: RUNNING
        Name: alsa_output.pci-0000_2f_00.4.analog-surround-71
        Description: Starship/Matisse HD Audio Controller Analog Surround 7.1
        Driver: PipeWire
        Sample Specification: s32le 8ch 48000Hz
        Channel Map: front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right

FAQ

Q: How to make it work on a headless Raspberry Pi?

A: PulseAudio is not installed by default on the Raspberry Pi OS Lite distribution (https://www.raspberrypi.org/software/operating-systems/). In order to use soundcard, you have to install PulseAudio first, and edit the configuration (with a fix to avoid the main output to be in mono-only).

sudo apt install -y python3-pip python3-numpy pulseaudio
sudo nano /usr/share/pulseaudio/alsa-mixer/profile-sets/default.conf
# comment the block [Mapping analog-mono] with ';'
pulseaudio -D
python3 -m pip install soundcard

Known Issues:

  • Windows/WASAPI currently records garbage if you record only a single channel. The reason for this is yet unknown. Multi-channel and channel maps work, though.
  • Windows/WASAPI silently ignores the blocksize in some cases. Apparently, it only supports variable block sizes in exclusive mode.
  • Windows/WASAPI may underrun its buffers even if blocksize and nframes are matched. Use a larger blocksize than nframes if this happens.
  • Error messages often report some internal CFFI/backend errors. This will be improved in the future.
  • macOS Records silence happens when you run your script with an app that doesn't ask for microphone permission to solve it, go to settings and give microphone permission to the app you are running the script.

Changelog

  • 2018-04-25 implements fixed block sizes when recording (thank you, Pariente Manuel!)
  • 2018-05-10 adds a test suite and various fixes for Windows
  • 2018-05-11 various fixes for macOS
  • 2018-06-27 Adds latency property to Linux/pulseaudio (Thank you, Pariente Manuel!)
  • 2018-07-17 adds loopback support for Windows (Thank you, Jan Leskovec!)
  • 2018-10-16 adds bug fix for IPython on Windows (Thank you, Sebastian Michel!)
  • 2018-11-28 adds Sphinx/Readthedocs documentation
  • 2019-03-25 adds support for Python 3.5 (Thank you, Daniel R. Kumor!)
  • 2019-04-29 adds experimental support for exclusive mode on Windows
  • 2019-05-13 fixes sample rate conversion on macOS
  • 2019-05-15 fixes silence recording on macOS
  • 2019-06-11 fixes exception when monitoring default device on Linux (Thank you, Inti Pelupessy!)
  • 2019-06-18 fixes crash when opening many streams on Linux
  • 2019-08-23 fixes attribute error when accessing stream state on Linux (Thank you, Davíð Sindri Pétursson!)
  • 2019-10-08 fixes inconsistent dtypes when recording on Linux
  • 2020-01-06 fixes silent recordings on Windows
  • 2020-04-28 get and set the pulseaudio program name on Linux (Thank you, Philipp A.!)
  • 2020-05-14 fixes error with unicode soundcard names on Windows (Thank you, BAKEZQ!)
  • 2020-05-18 adds support for pyinstaller (v4) (Thank you, Bob Thomas!)
  • 2020-05-19 adds compatibility with Windows 7 (Thank you, demberto!)
  • 2020-07-22 fixes freezing bug on Linux during startup (Thank you, zhujisheng!)
  • 2020-08-01 improves error reporting on Linux (Thank you, Rik van Riel!)
  • 2020-08-13 fixes crash due to use-after-free on Linux (Thank you, Rik van Riel!)
  • 2021-01-13 fixes unicode error on Windows (Thank you, paulzzh!)
  • 2021-11-24 adds compatibility with NixOS library naming (Thank you, shithead!)
  • 2021-12-23 fixes deprecation for Python 3.10 (Thank you, Nekyo!)
  • 2022-04-29 fixes deprecation in recent Numpy
  • 2024-03-16 fixes empty soundcard list on macOS (Thank you, Patrice Brend'amour!)

soundcard's People

Contributors

alfonsocv12 avatar alucard2169 avatar andreicociuba avatar bastibe avatar bob-thomas avatar clpsplug avatar cyberic99 avatar devzeb avatar dkumor avatar flying-sheep avatar ipelupessy avatar janleskovec avatar joergbitzer avatar josephernest avatar koharuyuzuki avatar minmotech avatar mpariente avatar olcina avatar paulzzh avatar porterk avatar rikvanriel avatar shithead avatar szlop avatar tern-davidp avatar willstott101 avatar zhqu1148980644 avatar zhujisheng avatar zngguvnf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

soundcard's Issues

TypeError(f'Invalid kind: {kind}' observed when importing soundcard

I have installed python soundcard module and when importing soundcard I see the below error:

C:\Users\HP>pip install soundcard
Requirement already satisfied: soundcard in c:\python27\lib\site-packages (0.1.2)
Requirement already satisfied: numpy in c:\python27\lib\site-packages (from soundcard) (1.13.1+mkl)
Requirement already satisfied: cffi in c:\python27\lib\site-packages (from soundcard) (1.11.5)
Requirement already satisfied: pycparser in c:\python27\lib\site-packages (from cffi->soundcard) (2.18)

C:\Users\HP>python
Python 2.7.12 (v2.7.12:d33e0cf91556, Jun 27 2016, 15:24:40) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

import soundcard as sd
Traceback (most recent call last):
File "", line 1, in
File "C:\Python27\lib\site-packages\soundcard_init_.py", line 8, in
from soundcard.mediafoundation import *
File "C:\Python27\lib\site-packages\soundcard\mediafoundation.py", line 196
raise TypeError(f'Invalid kind: {kind}')
^
SyntaxError: invalid syntax

Publish version 0.1.2

It looks like Version 0.1.2 was not published on PyPi yet, right now I have to use dependency_links in my setup.py to point on the github repository to use the latest version (with channel maps). It would be great if you could publish that version! Thank you! And thank you for this amazing library!

Channel-specification does not work on Windows / WASAPI

I need a player that can play a wave-file to a specified sound-channel (e.g. channel 8), so I created this using SoundCard and python, and it works well in Linux and on Mac, but not under windows:

The issue is reported from: ...soundcard/mediafoundation.py, line 477, in init
raise TypeError('Due to limitations of WASAPI, channel maps on Windows '
TypeError: Due to limitations of WASAPI, channel maps on Windows must be a combination of range(0, x).

The workaround seems to be to reprogram to send silence to any non-used channel, but would it not be better if this was handled by the library, so that the same call could be performed on any platform?

Fixed block size

Hi,

Is there a way to record continuously with a fixed blocksize?
I notice that even when specifying the blocksize in the recorder, the actual blocksize varies which is not convenient in my case. I saw that it is specified in the doc of the function itself but I wonder if there is a way to force the blocksize to be fixed.

Thanks in advance

Partial incompatibility with Python 3.7

Playing a sound hangs once in a while for several seconds after playback under Python 3.7, but not under 3.5. or 3.6, at least on MacOS.

import soundcard
soundcard.default_speaker().play(data, samplerate)

Latency increasing over time

I'm having difficulty keeping consistent low latency while streaming audio (input and output) with SoundCard. It appears as if something occurs periodically causing a small bump in latency which is never recovered. Flushing the microphone has some effect, but in the long run, the latency continues climbing upward. Has anyone encountered this problem, or know the cause of it?

Below I've posted a scatter plot showing the increase in latency over time.

I'm using Ubuntu 18.04, kernel 4.15.0-46-lowlatency, SoundCard version 0.3.2. Code to reproduce this plot is posted below. I've managed to replicate it on 2 different computers, although both Ubuntu 18.04, with the lowlatency kernel.

internal

import soundcard as sc
import matplotlib.pyplot as plt


out = sc.default_speaker()
inp = sc.default_microphone()
fs = 48000
frame_size = 768


def record(seconds, flush=False):
    # Convert seconds to frames
    n = int(seconds * fs / frame_size)
    if flush:
        flush = int(flush * fs / frame_size)
    t, y = [], []

    with inp.recorder(samplerate=fs, blocksize=frame_size) as mic, \
          out.player(samplerate=fs, blocksize=frame_size) as sp:
        for i in range(n):
            data = mic.record(numframes=frame_size)
            sp.play(data)

            if i % 20 == 0:
                latency = mic.latency
                t.append(frame_size * i / fs)
                y.append(1000 * latency)
                print(f"Latency = {1000 * latency:.1f} ms\t[{i} / {n}]")

            if flush and i > 0 and i % flush == 0:
                mic.flush()
                print("mic flush")

    return t, y


t, y = record(600, flush=False)
plt.scatter(t, y, alpha=0.2)

t, y = record(600, flush=75)
plt.scatter(t, y, alpha=0.2)

plt.legend(["No flush", "Flush every 75 s"])
plt.title("Latency vs running time")
plt.xlabel("Running time, seconds")
plt.ylabel("Latency, ms")
plt.show()

provide alternative to context manager style stream

I'm attempting to integrate soundcard as a backend library in my own sound api.
This means I'm wrapping various classes in my own.

Recorder and Stream are only context managers and this is a little problematic, because that doesn't mix well with code that is using them over a longer period of time and across many scopes; I don't have a single localized place where I can use a context manager for a short time. I want to be able to keep the backend stream open all the time (until my api is closed)

I can do this by manually calling __enter__() and __exit__() but it would be nice if there also is a more traditional init() / close() method pattern or something similar.

*edit: Actually, I managed to solve this without having to jump through some hoops, by using a loop that pulls data from elsewhere , running within the context manager, in a thread. But it still is a bit convoluted *

No Input on OS X

I have the following code that I'm trying to run on OS X 10.14.1 (Mojave) via Python 3.7.0. It is essentially taken from the tutorial:

import soundcard as sc

mic = sc.default_microphone()
data = mic.record(samplerate=48000, numframes=48000)

When I inspect data, it's an array of all -0.'s, and this is true for other sample rates too. I've checked the microphone being used and it is the built-in microphone as expected, and the built-in microphone seems to be picking up my voice when I speak into it from the OS X System Preferences menu.

Any ideas about what's going wrong? I have restarted my machine and tried the pysoundcard library as well, but the same thing is happening. I can provide debug information as needed.

Is Soundcard specific to Python 3 ?

I installed soundcard in Python 2.7.

If I run import soundcard, it raises :
NotImplementedError: SoundCard does not support linux2 yet
which comes from __init__.py.

In Python 3, sys.platform return 'linux' for every version of it but in Python 2.7 sys.platform will return 'linux2' or 'linux3'.
If this is the only part of the code which is not compatible with Python 2.7, we can replace the line by
if sys.platform.startswith('linux'):

Saving

Hi, is there any way to save the outputted data as a wav or an mp3 file ? thank you !

what is minimum version of OSX ?

Hello, not really an issue, but i wanted to know if OSX versions prior to macOS (mine is 10.10) were susceptible to work with SoundCard. If not, any idea if it was possible to make it backward-compatible ; and the amount of work that it would require ? Thanks ! ps: sorry for polluting the issues section with this. Not sure where this kind of question should go.

Request: to support python 2.7

Awesome project, this is the only python library that does not depend on port audio and seems to work on ubuntu touch, sailfish OS mobile platform, request to please support python 2.7 i would really like to get this working with https://github.com/MycroftAI/

Also is it possible to set the record time to 2 minutes maximum and save the recorded data to wav file a small example would really help ! Thanks

Don't hide common classes / hide imports

If trying to create a higher level script with custom classes, it is helpful to have direct access to the common classes of SoundCard, e.g.:

import soundcard as sc
# ...
class AudioChannel(object):
    def play(self, speaker):
        assert isinstance(speaker, sc.Speaker), 'speaker must be a speaker'
        # ...
```
Currently, those classes are hidden and not accessible. However, internals (imports) like cffi, re, os, ... should not be visible to the developer trying out that package using ipython or similar.

interruptible play()

Hi

I'd like to be able to interrupt a play() at any time.
I have a dirty hack to do it, but would you be interested in having this feature, @bastibe?

The idea is just to break the while loop, when a specific flag is set.

Thx

import error

I started working with this library today and i don't have a large experience with python. Someone could help me? This error appear when i use

import soundcard as sc

error:
`
raise RuntimeError('Error {}'.format(hex(hresult+2**32)))

RuntimeError: Error 0x100000001

`

Publish new version?

Would it be possible to publish a new version on pypi (0.2.3)? I'd like to be able to do pip install of soundcard on python 3.5.

This would also close #21

f-strings

f-strings are quite a new thing and machines without the latest python (3.5 is close, but not enough...) will not be able to use this. I think it would be great to change those 6 occurrences of them to ˋ''.format()ˋ which has better support. Also, you are not using fancy formats, so why stick to f-strings while excluding older versions...?

file: lines
test_soundcard.py: 43, 61
mediafoundation.py: 196, 220, 380, 410

Stream creation sometimes fails with a segfault

The error message is the same as: #20

Here is a script that can reproduce the crash 100% of the time:

import soundcard as sc
from time import sleep

speaker = sc.default_speaker()
while True:
    with speaker.player(48000, channels=1, blocksize=1024) as p:
        print(p)
        sleep(0.5)

When the following patch is applied (with a few print statements that are not included here)

diff --git a/soundcard/pulseaudio.py b/soundcard/pulseaudio.py
index fc91815..77f0849 100644
--- a/soundcard/pulseaudio.py
+++ b/soundcard/pulseaudio.py
@@ -86,6 +86,12 @@ class _PulseAudio:
         while self._pa_operation_get_state(operation) == _pa.PA_OPERATION_RUNNING:
             time.sleep(0.001)

+    def _last_error(self):
+        error_code = self._pa_context_errno(self.context)
+        error_as_char_array = _pa.pa_strerror(error_code)
+        error_as_string = _ffi.string(error_as_char_array).decode('utf-8')
+        return (error_code, error_as_string)
+
     @property
     def source_list(self):
         """Return a list of dicts of information about available sources."""
@@ -180,6 +186,7 @@ class _PulseAudio:
     _pa_context_get_sink_info_by_name = _lock_and_block(_pa.pa_context_get_sink_info_by_name)
     _pa_context_get_server_info = _lock_and_block(_pa.pa_context_get_server_info)
     _pa_context_get_state = _lock(_pa.pa_context_get_state)
+    _pa_context_errno = _lock(_pa.pa_context_errno)
     _pa_context_drain = _lock(_pa.pa_context_drain)
     _pa_context_disconnect = _lock(_pa.pa_context_disconnect)
     _pa_context_unref = _lock(_pa.pa_context_unref)
@@ -570,6 +577,9 @@ class _Stream:
             raise RuntimeError('invalid channel map')

         self.stream = _pulse._pa_stream_new(_pulse.context, self._name.encode(), samplespec, channelmap)
+        if self.stream == _ffi.NULL:
+            raise RuntimeError("Stream creation failed, returned NULL pointer."
+                               f" The most recent error code is: {_pulse._last_error()}")
         bufattr = _ffi.new("pa_buffer_attr*")
         bufattr.maxlength = 2**32-1 # max buffer length
         numchannels = self.channels if isinstance(self.channels, int) else len(self.channels)
diff --git a/soundcard/pulseaudio.py.h b/soundcard/pulseaudio.py.h
index 40ad766..75b1570 100644
--- a/soundcard/pulseaudio.py.h
+++ b/soundcard/pulseaudio.py.h
@@ -155,6 +155,7 @@ pa_mainloop_api *pa_threaded_mainloop_get_api(pa_threaded_mainloop *m);
 typedef struct pa_context pa_context;
 pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name);
 void pa_context_unref(pa_context *c);
+int pa_context_errno (pa_context *c);

the output of the script is the following:

[arnig][~/Development/soundcard-segf…]% python main.py
Creating stream object...
After _pulse._pa_stream_new(...), self.stream is: <cdata 'pa_stream *' 0x5586486a1500>
Inside _Player._connect_stream(), self.stream is: <cdata 'pa_stream *' 0x5586486a1500>
<soundcard.pulseaudio._Player object at 0x7f365bffc790>
Creating stream object...
After _pulse._pa_stream_new(...), self.stream is: <cdata 'pa_stream *' 0x5586486cd910>
Inside _Player._connect_stream(), self.stream is: <cdata 'pa_stream *' 0x5586486cd910>
<soundcard.pulseaudio._Player object at 0x7f365c00ee90>
Creating stream object...
After _pulse._pa_stream_new(...), self.stream is: <cdata 'pa_stream *' 0x5586486f96a0>
Inside _Player._connect_stream(), self.stream is: <cdata 'pa_stream *' 0x5586486f96a0>
<soundcard.pulseaudio._Player object at 0x7f366f63ea10>
Creating stream object...
After _pulse._pa_stream_new(...), self.stream is: <cdata 'pa_stream *' 0x5586486fcd00>
Inside _Player._connect_stream(), self.stream is: <cdata 'pa_stream *' 0x5586486fcd00>
<soundcard.pulseaudio._Player object at 0x7f365c02e190>
Creating stream object...
After _pulse._pa_stream_new(...), self.stream is: <cdata 'pa_stream *' 0x558648700830>
Inside _Player._connect_stream(), self.stream is: <cdata 'pa_stream *' 0x558648700830>
<soundcard.pulseaudio._Player object at 0x7f365c023110>
Creating stream object...
After _pulse._pa_stream_new(...), self.stream is: <cdata 'pa_stream *' NULL>
Traceback (most recent call last):
  File "main.py", line 10, in <module>
    with speaker.player(48000, channels=1, blocksize=1024) as p:
  File "/usr/lib/python3.7/site-packages/SoundCard-0.3.2-py3.7.egg/soundcard/pulseaudio.py", line 583, in __enter__
    raise RuntimeError("Stream creation failed, returned NULL pointer."
RuntimeError: Stream creation failed, returned NULL pointer. The most recent error code is: (3, 'Invalid argument')

@bastibe Any idea what's going on?

Expose callback events?

Would it be possible to add support for callback-style recording (the user would just specify the desired chunk size (or not) and provide a function that would be called when there is new data available)? I have been looking through the code and I think I could help implement it, I would just need some pointers on where to start. Also I can only test this on Windows and Linux.

Prevent audio volume changing when recording with include_loopback

Is there a way to get the audio volume (as a system setting) and increase the volume when recording to adjust for the lower audio volume? This wouldn't be an issue, it is just a good idea to preserve consistency with Windows, since the volume of the recording does not change when changing the system volume.

Problems with the demo

I'd like to get the loopback example on the front page working, but I am getting the error below when trying to run it. I am attempting to run this using Python 3.6 on Linux. Not sure if this is related to this issue or not.

File "/usr/local/lib/python3.6/dist-packages/soundcard/pulseaudio.py", line 417, in record chunk = self._record_chunk() File "/usr/local/lib/python3.6/dist-packages/soundcard/pulseaudio.py", line 371, in _record_chunk readable_bytes = self._pulse._pa_stream_readable_size(self.stream) AttributeError: '_Recorder' object has no attribute '_pulse'

record random length voice

can you show us an example how to record voice with soundcard.default_microrphone().recorder()?
I am trying to start recording when I press, let's say 's' key on keyboard and stop recording when I press 'e'.

macOS recording error

Just trying out the lib and getting an error immediately upon trying to record. error during recording: -10863

Here's my code

import soundcard as sc

default_mic = sc.default_microphone()
with default_mic.recorder(48000, channels=[0]) as mic:
    data = mic.record(1024)
    print(data)

Here's the stack trace:

Traceback (most recent call last):
  File "main.py", line 5, in <module>
    data = mic.record(1024)
  File "/usr/local/lib/python3.7/site-packages/soundcard/coreaudio.py", line 877, in record
    data = numpy.concatenate(blocks, axis=0)
ValueError: all the input arrays must have same number of dimensions

I'm on macOS 10.14.3 and running Python 3.7.4. Any suggestions?

cannot playback on linux

hi,

I'm trying out the library on linux, the playback does not seem to work.
I tryed your example, everything work but when i do the playback i get:
default_speaker.play(data/numpy.max(data), samplerate=48000)
Assertion 's' failed at pulse/stream.c:1412, function pa_stream_connect_record(). Aborting.

what can I do?
How can I select jack instead of pulseaudio? I guess that might fix the problem...

thanks and best wishes.

output stream latency

I am using the player context manager (loopback_player) with a Blocksize = None I am printing the latency of the player which shows it as 1.6 seconds. When the same block of code is run with Blocksize = 80 latency is much lower (~2 ms)but the sound is chopped up and long.

I am running this in linux and I need minimal latency streaming of the audio output.

Any ideas whats happening here ?

Mix several sounds in a Stream

Hello

Is it possible to keep a Stream running, and play simultaneous sounds in it?

Otherwise, is there a way to play a sound with a specific volume?

Thank you

Feature Information Query: Reading Audio buffer by name

Hi,
Thanks for the library. I guess this is a great stuff considering that it works on multiple OSes.

One query. I have a need to read the audio buffer after starting the playback. i.e. if there are CODEC issues, I want to capture through that. This is only for windows.
In audio pipeline I want to get buffer , just when the device is writing to the end point. (I guess, at API-level it is "IAudioRenderClient::GetBuffer " ? )
Please let me know if this library supports this functionality at python-level.

Thanks
Ishwar

numpy-int16 as recording output

Hi, I am working on an speak/word detection project and want to test related frameworks and libraries. The microphone must be part of a bluetooth headset and (regarding python libraries) this is sometimes problematic: Currently I am testing Procupine. Procupine (like many other detection frameworks) uses PyAudio for the soundcard connection but PyAudio seems to detect only the ALSA devices (the internal soundcard) but to ignore the PulseAudio layer (the bluetooth headset).

Headset detection works (in combination with Ubuntu 18.04 and PulseAudio soundserver) like a charm with your SoundCard library! So I would like to replace this PyAudio part from Procupine (and perhaps other word detection libraries) with SoundCard:

pa = pyaudio.PyAudio()

audio_stream = pa.open(
    rate=porcupine.sample_rate,
    channels=1,
    format=pyaudio.paInt16,
    input=True,
    frames_per_buffer=porcupine.frame_length,
    input_device_index=self._input_device_index)

while True:
    pcm = audio_stream.read(porcupine.frame_length)
    pcm = tuple(numpy.frombuffer(pcm, 'int16'))   # same as:  struct.unpack_from("h" * porcupine.frame_length, pcm)

As you can see: The result that will be feeded to the word detection is an int16 tuple (numpy array). But SoundCard returns a float32 numpy array. My short and very naive approach was to adjust SoundCard's sample and numpy format parts but of course without getting the desired result :)

Unfortunately I don't have any experience and knowledge regarding sound related coding and also don't have the time to familiarise myself with this topic because I must concentrate on my project goals. So it would be really great if you could give me a pointer how to adjust the SoundCard functions to return int16 arrays :) Thank you very much.

error during recording and playback

Trying to record or playback spams this error:
error during recording: -10863
and then:
/Users/CWT/Documents/VÆRKER/Sound+probe/Main.py:15: RuntimeWarning: invalid value encountered in true_divide default_speaker.play(data/numpy.max(data), samplerate=48000) /anaconda3/lib/python3.6/site-packages/SoundCard-0.2.2-py3.6.egg/soundcard/coreaudio.py:366: RuntimeWarning: invalid value encountered in greater data[data>1] = 1 /anaconda3/lib/python3.6/site-packages/SoundCard-0.2.2-py3.6.egg/soundcard/coreaudio.py:367: RuntimeWarning: invalid value encountered in less data[data<-1] = -1

I can see that error code -10863 = kAudioUnitErr_CannotDoInCurrentContext . But haven't been able to find out more.

first record results in float64, subsequent in float32

the following

import soundcard

mics=soundcard.all_microphones()
mic=mics[1]
with mic.recorder(samplerate=48000, blocksize=4096, channels=1) as m:
  data=m.record(1024)
  print(data.dtype)
  data=m.record(1024)
  print(data.dtype)
  data=m.record(1024)
  print(data.dtype)

gives as output:

float64
float32
float32

while expected is:

float32
float32
float32

I am having a run time error using play function

Hello i am getting a run time error while using play() funciton

Here is the piece of code and error message
default_speaker.play(data/numpy.max(data), samplerate=48000)


raise RuntimeError('Error {}'.format(hex(hresult+2**32)))

RuntimeError: Error 0x80070006

RuntimeError on "import soundcard"

I built an application around this awesome SoundCard library yesterday.
Yesterday everything was working properly.
Today I started the application again and it throws the following runtime error when I'm importing the package with "import soundcard" (see attached file).

Windows 10 (17134) 64 bit, Python 3.7.0
SoundCard-master branch from 26. September 2018

runtime error on import soundcard.txt

Interface appearing with only 2 channels

Hello,
Thanks a lot for the package. I'm trying to use it with a Lynx Aurora (n) interface, 24 channels, Thunderbolt connection ; I'm getting 2 channels only for input and output. I'm on OS X (10.14), the system sees the interface fine.
Any ideas where that can come from?

_test1 = soundcard.all_microphones()

_test1 = {list} <class 'list'>: [<Microphone Built-in Microphone (2 channels)>, <Microphone 1: Aurora(n) (2 channels)>, <Microphone MJRecorder Device (2 channels)>]

  • 0 = {_Microphone} <Microphone Built-in Microphone (2 channels)>
  • 1 = {_Microphone} <Microphone 1: Aurora(n) (2 channels)>
    -- _id = {int} 64
    -- channels = {int} 2
    -- id = {int} 64
    -- isloopback = {bool} False
    -- name = {str} '1: Aurora(n)'
  • 2 = {_Microphone} <Microphone MJRecorder Device (2 channels)>

Thanks,
Armand

PS: no such issue with other interfaces (Expert Sleepers ES-8)

Error when importing Python-Audio

Hi,
I use Windows 10 and Python 2.7.13 (Anaconda 4.3.1, 64-bit). When I try to import Python-Audio like this
import pythonaudio as pa
Python raises an error message saying:

File "C:\Users\[...]\Anaconda2\lib\site-packages\pythonaudio\mediafoundation.py", line 192
   raise TypeError(f'Invalid kind: {kind}')

multithreading using soundcard

I am new to GitHub, I wanted to share my experience regarding sounddevice python
I use sounddevice python to control beihringer fca1616 sound card.
I am able to send tone to any output in my card (device), but when trying to send a different frequency tone to each of my outputs( 8 outputs) I had to use multithreading which makes my pc crash.
I used ASIO API and i could send a same tone to all 8 outputs but my goal is to send different tones to each device (output)
any multithreading soundcard example ?

How to stop streaming?

Hi Bastibe

First of all, thanks a lot for sharing this project.

In pyaduio, there is a way to start or stop streaming. Here, I'd like to know if there is a way to allow an Microphone object to explicitly exit the real-time streaming.

The example code is like the following:

import soundcard as sc
import numpy

# get the current default microphone on your system:
default_mic = sc.default_microphone()
data = default_mic.record(samplerate=48000, numframes=48000)
# Can I stop streaming?

Thanks for your time!

Jun

Simultaneously play and record

Is it possible to playback audio and record it simultaneously? I didn't find a non-blocking way to call .record() (except perhaps passing None to numframes?) so that I can start playing back sound after the recording is active. For context, I want to playback a sweep signal and record it to later on calculate the impulse response and do some processing, I initially used threading (and then multiprocessing) with a threading.Event to sync them, but identical playback/recording sessions have slight different, non-deterministic offsets in the range of +/-10ms.

I am running this on Linux with Python 3.6.7, and soundcard=0.2.2.

Is there a way to achieve this directly on soundcard without resorting to threading or other ways to parallelize?

Thanks!!

SoundCard or PySoundCard

Hi Bastian,

I don't really know where to put this but here :
I want to record and play sound in "real-time" (with as little delay as possible), I'm hesitating for the choice of library and I thought you could advise me.

I want to code an analysis-synthesis pipeline, where I would record audio buffer by buffer, compute the FFT, make some modifications on the FFT, compute the iFFT and make the resynthesis with the overlapp-add method.
The processing delay for my case is crucial. Which library would you use for my case? Between PyAudio, PySoundCard and SoundCard, I cannot decide which one to use.

Thank you in advance for your answer.

Great peace of software

Thank you for this beaitiful python package!
I like the intuitive API. And the selection of pulse for linux.
Windows10 users may get a strange error in the beginning this is due to the privacy settings. The user needs to allow the app to access the microphone.

PA_STREAM_ADJUST_LATENCY and `_block_size` in `_Recorder`

I was trying to understand Latency Control and reading the docs of PulseAudio.
I found that when the _pa.PA_STREAM_ADJUST_LATENCY flag is set (almost everywhere in Soundcard), we have to specify the latency we want in the buffer_atr.

Since the changes in the _Recorder, we can specify blocksize which will be used to compute bufattr.fragsize, but won't be the latency we require in case we also specify numframes in the record method.

If you recall, we talked about the value of blocksize and numframes during the PR.

Another question, what should be the value of blocksize for realtime recording/playback loop?
Should it be similar to numframes or much smaller?

I was thinking about this, too. It should probably be much smaller than your expected numframes. We'll have to experiment with the finished code to find a reasonable value.

Is this a problem? What could it cause?

I'm researching on this because I try to find a way to specify the total latency of a Recording/Playback system. But I don't think this flag will do this as it is only related to the device's buffer size. Any idea how I could specify the system's total latency I want?

Recording with numframes=None does not work on Linux

input.record(None, samplerate) does not work in Linux. I get the following error:

Traceback (most recent call last):
  File "ffttest.py", line 24, in <module>
    data = input.record(None, samplerate)
  File "/home/jan/Development/musicled/audiotest/soundcard/pulseaudio.py", line 212, in record
    return r.record(numframes)
  File "/home/jan/Development/musicled/audiotest/soundcard/pulseaudio.py", line 425, in record
    [-1, self.channels]))
TypeError: 'list' object cannot be interpreted as an integer

I went into the source and changed the line: return numpy.reshape(numpy.concatenate([self.flush(), self._record_chunk()], [-1, self.channels])) to return numpy.reshape(self._record_chunk(), [-1, self.channels]) and it works but it now ignores the buffer I guess.

CFFI's ABI vs API level

I'm new to CFFI so this might be a simple question.
I quote from CFFI's docs :

The ABI mode accesses libraries at the binary level, whereas the faster API mode accesses them with a C compiler.

If using a C compiler to install your module is an option, it is highly recommended to use the API mode described in the next paragraph. (It is also faster.)

If I'm not mistaken, what is used in soundcard is the in-line ABI, what are the reasons for this choice, knowing that the out-of-line API is supposed to be faster?
Thanks

error getting default mic

on linux, I get the following error when trying to call default_microphone():

  File "/usr/lib/python3.7/site-packages/soundcard/pulseaudio.py", line 161, in _match_soundcard
    raise IndexError('no soundcard with id {}'.format(id))
IndexError: no soundcard with id alsa_output.pci-0000_00_1f.3.analog-stereo.monitor

default_speaker works, I can get all_microphones (which only contains 1)...

Python 3.5 Support

Hi! I would like to use this library on a Raspberry Pi to gather audio data. Unfortunately, the Pi is currently stuck at Python 3.5 - I specifically get a syntax error on 1_000_000.

Based on your knowledge, is this library's dependence on 3.6 based only on a couple syntax features? If so, would you be amenable to either changing these features, or accepting a pull request that uses 3.5 alternatives?

Windows install

Great lib!

After pip install SoundCard I'm getting this:

$ python -c "import soundcard"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\Ofek\Desktop\q\Miniconda3\lib\site-packages\soundcard\__init__.py", line 8, in <module>
    from soundcard.mediafoundation import *
  File "C:\Users\Ofek\Desktop\q\Miniconda3\lib\site-packages\soundcard\mediafoundation.py", line 14, in <module>
    _combase = _ffi.dlopen('combase')
  File "C:\Users\Ofek\Desktop\q\Miniconda3\lib\site-packages\cffi\api.py", line 141, in dlopen
    lib, function_cache = _make_ffi_library(self, name, flags)
  File "C:\Users\Ofek\Desktop\q\Miniconda3\lib\site-packages\cffi\api.py", line 802, in _make_ffi_library
    backendlib = _load_backend_lib(backend, libname, flags)
  File "C:\Users\Ofek\Desktop\q\Miniconda3\lib\site-packages\cffi\api.py", line 797, in _load_backend_lib
    raise OSError(msg)
OSError: ctypes.util.find_library() did not manage to locate a library called 'combase'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.