Skip to content

Fix callback handling and flag initialization - #629

Open
IzaCarlos wants to merge 2 commits into
spatialaudio:masterfrom
IzaCarlos:master
Open

IzaCarlos wants to merge 2 commits into
spatialaudio:masterfrom
IzaCarlos:master

Conversation

@IzaCarlos

@IzaCarlos IzaCarlos commented May 4, 2026 •

Copy link
Copy Markdown

Problem

On Python 3.14, cffi changed how it passes arguments to native callbacks
and how it validates __init__ return values. This caused two distinct errors:

  1. TypeError: __init__() should return None, not 'NoneType'
    cffi now intercepts and validates the return of __init__ at a lower
    level, causing it to spuriously reject the standard None return.

  2. TypeError: unsupported operand type(s) for &: '_CDataBase' and 'int'
    The status argument now arrives as a raw _CDataBase object instead
    of a plain Python int, breaking bitwise operations in _hasflag.

Both errors surface in _wrap_callback and occur unpredictably during
stream callbacks, including during silence and active playback.

Fix

Bypass CallbackFlags.__init__ entirely in _wrap_callback using
__new__, then set _flags directly via int() cast. This avoids
the cffi __init__ interception and the _CDataBase bitwise issue
in one shot.

Changes

In _wrap_callback, replace:

args = args[:-1] + (CallbackFlags(args[-1]),)

With:

cf = CallbackFlags.__new__(CallbackFlags)
cf._flags = int(status)

Tested on

  • Python 3.14.4, Sounddevice 0.5.5
  • Extended idle periods (no audio) no longer trigger the error
  • Active playback confirmed working after fix

@mgeier

mgeier commented May 23, 2026

Copy link
Copy Markdown
Member

Thanks for this PR!

How can I reproduce the errors? Does this need a certain CFFI version?

@IzaCarlos

Copy link
Copy Markdown
Author

How can I reproduce the errors? Does this need a certain CFFI version?

You can reproduce (I'm very sure) by installing Python v3.14.4 w/cffi 2.0.0 and Sounddevice with 0.5.5, then use any application that opens a stream for a long time.
Not to advertise or anything but one of my personal applications that monitors sound does use a continous stream and after being left idling it would eventually lead to this cffi error (pardon some mispells I had to OCR this since I couldn't copy paste from the box):

Exception ignored from cffi callback <function _StreamBase._init_.<locals>.callback_ptr at 0x000001DBC8B22140>:
Traceback (most recent call last):
File: "C:\Users\carlo\AppData\Local\Programs\Python\Python314\Li b\site-packages\sounddevice.py", line 862, in callback_ptr Exception ignored from cffi callback <function StreamBase. init <locals>.callback_ptr at 0x000001DBC8B22A30>:
Traceback (most recent call last):
File: "C:\Users\carlo\AppData\Local\Programs\Python\Python314\Lib\site-packages\sounddevice.py", line 878, in callback_ptr return_wrap_callback(callback, data, frames, time, status)
File: "C:\Users\carlo\AppData\Local\Programs\Python\Python314\Lib\site-packages\sounddevice.py", line 2775, in_wrap_callback return_wrap_callback(callback, data, frames, time, status)
File: "C:\Users\carlo\AppData\Local\Programs\Python\Python314\Li b\site-packages\sounddevice.py", line 2775, in_wrap_callback
args = tuple(args[:-1]) + (CallbackFlags(args[-1]),)

TypeError: __init__() should return None, not 'NoneType'

As mentioned I applied some fixes to my sd installation and it has not been causing any more issues.

@mgeier

mgeier commented Aug 29, 2026

Copy link
Copy Markdown
Member

I could not reproduce this on a Windows VM using any host API, so this bug might be more specific to your hardware.

I have the feeling your fix just hides an underlying problem, which should be fixed in CFFI or PortAudio (I don't know where the bug actually is).

Can you show the output of python -m sounddevice?

Can you try it with different host APIs?

Can you try it with different sound cards?

When the error appears, can you check which exact type the last argument in _wrap_callback() has?

You could try this by creating a file named instrumented.py in the same directory as your code:

import faulthandler

import sounddevice as sd

faulthandler.enable()

_wrap_callback = sd._wrap_callback


def _instrumented_wrap_callback(callback, *args):
    status = args[-1]
    if type(status) is not int:
        print('ERROR: status is a', type(status))
        return sd._lib.paAbort
    try:
        str(sd.CallbackFlags(status))
    except BaseException as e:
        print('ERROR:', repr(e))
        return sd._lib.paAbort
    return _wrap_callback(callback, *args)


sd._wrap_callback = _instrumented_wrap_callback

... and import it at the very top or your code:

import instrumented
... # the rest of your program, as you ran it before

@IzaCarlos

Copy link
Copy Markdown
Author

Hi @mgeier
Sorry for the delayed follow-up on this! I wanted to properly track down what was happening under the hood before getting back to you. (and I, admittedly, got side tracked by other projects)

  1. python -m sounddevice Output

Here is the complete output from my environment:

Click to expand python -m sounddevice
    0 Asignador de sonido Microsoft - Input, MME (2 in, 0 out)
>   1 Voicemeeter Out B2 (VB-Audio Vo, MME (8 in, 0 out)
    2 Voicemeeter Out A4 (VB-Audio Vo, MME (8 in, 0 out)
    3 Voicemeeter Out B3 (VB-Audio Vo, MME (8 in, 0 out)
    4 Auriculares con micrófono (Crus, MME (1 in, 0 out)
    5 Voicemeeter Out B1 (VB-Audio Vo, MME (8 in, 0 out)
    6 Micrófono (USB/XLR Condenser Mi, MME (2 in, 0 out)
    7 Virtual Mic (Virtual Mic for Au, MME (2 in, 0 out)
    8 Voicemeeter Out A3 (VB-Audio Vo, MME (8 in, 0 out)
    9 Voicemeeter Out A2 (VB-Audio Vo, MME (8 in, 0 out)
   10 Voicemeeter Out A5 (VB-Audio Vo, MME (8 in, 0 out)
   11 Voicemeeter Out A1 (VB-Audio Vo, MME (8 in, 0 out)
   12 Asignador de sonido Microsoft - Output, MME (0 in, 2 out)
<  13 Voicemeeter AUX Input (VB-Audio, MME (0 in, 8 out)
   14 Voicemeeter Input (VB-Audio Voi, MME (0 in, 8 out)
   15 Voicemeeter In 1 (VB-Audio Voic, MME (0 in, 8 out)
   16 Voicemeeter In 4 (VB-Audio Voic, MME (0 in, 8 out)
   17 Voicemeeter In 5 (VB-Audio Voic, MME (0 in, 8 out)
   18 Salida digital (High Definition, MME (0 in, 2 out)
   19 Auriculares (Crusher Evo), MME (0 in, 2 out)
   20 Virtual Speakers (Virtual Speak, MME (0 in, 2 out)
   21 Voicemeeter In 2 (VB-Audio Voic, MME (0 in, 8 out)
   22 Voicemeeter VAIO3 Input (VB-Aud, MME (0 in, 8 out)
   23 Voicemeeter In 3 (VB-Audio Voic, MME (0 in, 8 out)
   24 1 - QM2382F (2- AMD High Defini, MME (0 in, 2 out)
   25 Controlador primario de captura de sonido, Windows DirectSound (2 in, 0 out)
   26 Voicemeeter Out B2 (VB-Audio Voicemeeter VAIO), Windows DirectSound (8 in, 0 out)
   27 Voicemeeter Out A4 (VB-Audio Voicemeeter VAIO), Windows DirectSound (8 in, 0 out)
   28 Voicemeeter Out B3 (VB-Audio Voicemeeter VAIO), Windows DirectSound (8 in, 0 out)
   29 Auriculares con micrófono (Crusher Evo), Windows DirectSound (1 in, 0 out)
   30 Voicemeeter Out B1 (VB-Audio Voicemeeter VAIO), Windows DirectSound (8 in, 0 out)
   31 Micrófono (USB/XLR Condenser Mic A04 Gen2), Windows DirectSound (2 in, 0 out)
   32 Virtual Mic (Virtual Mic for AudioRelay), Windows DirectSound (2 in, 0 out)
   33 Voicemeeter Out A3 (VB-Audio Voicemeeter VAIO), Windows DirectSound (8 in, 0 out)
   34 Voicemeeter Out A2 (VB-Audio Voicemeeter VAIO), Windows DirectSound (8 in, 0 out)
   35 Voicemeeter Out A5 (VB-Audio Voicemeeter VAIO), Windows DirectSound (8 in, 0 out)
   36 Voicemeeter Out A1 (VB-Audio Voicemeeter VAIO), Windows DirectSound (8 in, 0 out)
   37 Controlador primario de sonido, Windows DirectSound (0 in, 2 out)
   38 Voicemeeter AUX Input (VB-Audio Voicemeeter VAIO), Windows DirectSound (0 in, 8 out)
   39 Voicemeeter Input (VB-Audio Voicemeeter VAIO), Windows DirectSound (0 in, 8 out)
   40 Voicemeeter In 1 (VB-Audio Voicemeeter VAIO), Windows DirectSound (0 in, 8 out)
   41 Voicemeeter In 4 (VB-Audio Voicemeeter VAIO), Windows DirectSound (0 in, 8 out)
   42 Voicemeeter In 5 (VB-Audio Voicemeeter VAIO), Windows DirectSound (0 in, 8 out)
   43 Salida digital (High Definition Audio Device), Windows DirectSound (0 in, 2 out)
   44 Auriculares (Crusher Evo), Windows DirectSound (0 in, 2 out)
   45 Virtual Speakers (Virtual Speakers for AudioRelay), Windows DirectSound (0 in, 2 out)
   46 Voicemeeter In 2 (VB-Audio Voicemeeter VAIO), Windows DirectSound (0 in, 8 out)
   47 Voicemeeter VAIO3 Input (VB-Audio Voicemeeter VAIO), Windows DirectSound (0 in, 8 out)
   48 Voicemeeter In 3 (VB-Audio Voicemeeter VAIO), Windows DirectSound (0 in, 8 out)
   49 1 - QM2382F (2- AMD High Definition Audio Device), Windows DirectSound (0 in, 2 out)
   50 Voicemeeter Input (VB-Audio Voicemeeter VAIO), Windows WASAPI (0 in, 2 out)
   51 Voicemeeter In 1 (VB-Audio Voicemeeter VAIO), Windows WASAPI (0 in, 2 out)
   52 Voicemeeter In 4 (VB-Audio Voicemeeter VAIO), Windows WASAPI (0 in, 2 out)
   53 Voicemeeter In 5 (VB-Audio Voicemeeter VAIO), Windows WASAPI (0 in, 2 out)
   54 Salida digital (High Definition Audio Device), Windows WASAPI (0 in, 2 out)
   55 Auriculares (Crusher Evo), Windows WASAPI (0 in, 2 out)
   56 Virtual Speakers (Virtual Speakers for AudioRelay), Windows WASAPI (0 in, 2 out)
   57 Voicemeeter In 2 (VB-Audio Voicemeeter VAIO), Windows WASAPI (0 in, 2 out)
   58 Voicemeeter VAIO3 Input (VB-Audio Voicemeeter VAIO), Windows WASAPI (0 in, 2 out)
   59 Voicemeeter In 3 (VB-Audio Voicemeeter VAIO), Windows WASAPI (0 in, 2 out)
   60 Voicemeeter AUX Input (VB-Audio Voicemeeter VAIO), Windows WASAPI (0 in, 2 out)
   61 1 - QM2382F (2- AMD High Definition Audio Device), Windows WASAPI (0 in, 2 out)
   62 Voicemeeter Out A4 (VB-Audio Voicemeeter VAIO), Windows WASAPI (2 in, 0 out)
   63 Voicemeeter Out B3 (VB-Audio Voicemeeter VAIO), Windows WASAPI (2 in, 0 out)
   64 Auriculares con micrófono (Crusher Evo), Windows WASAPI (1 in, 0 out)
   65 Voicemeeter Out B1 (VB-Audio Voicemeeter VAIO), Windows WASAPI (8 in, 0 out)
   66 Micrófono (USB/XLR Condenser Mic A04 Gen2), Windows WASAPI (2 in, 0 out)
   67 Virtual Mic (Virtual Mic for AudioRelay), Windows WASAPI (2 in, 0 out)
   68 Voicemeeter Out A3 (VB-Audio Voicemeeter VAIO), Windows WASAPI (2 in, 0 out)
   69 Voicemeeter Out A2 (VB-Audio Voicemeeter VAIO), Windows WASAPI (2 in, 0 out)
   70 Voicemeeter Out A5 (VB-Audio Voicemeeter VAIO), Windows WASAPI (2 in, 0 out)
   71 Voicemeeter Out B2 (VB-Audio Voicemeeter VAIO), Windows WASAPI (2 in, 0 out)
   72 Voicemeeter Out A1 (VB-Audio Voicemeeter VAIO), Windows WASAPI (2 in, 0 out)
   73 Headphones (SteelSeries_Sonar_VAD Aux Wave Speaker), Windows WDM-KS (0 in, 8 out)
   74 Line Out (SteelSeries_Sonar_VAD Stream Wave), Windows WDM-KS (0 in, 2 out)
   75 SteelSeries Sonar - Stream (SteelSeries_Sonar_VAD Stream Wave), Windows WDM-KS (2 in, 0 out)
   76 Line Out (SteelSeries_Sonar_VAD Chat Capture Wave), Windows WDM-KS (0 in, 2 out)
   77 SteelSeries Sonar - Microphone (SteelSeries_Sonar_VAD Chat Capture Wave), Windows WDM-KS (2 in, 0 out)
   78 Output (SteelSeries_Sonar_VAD Chat Wave Speaker), Windows WDM-KS (0 in, 2 out)
   79 Headphones (SteelSeries_Sonar_VAD Media Wave Speaker), Windows WDM-KS (0 in, 8 out)
   80 Headphones (SteelSeries_Sonar_VAD Game Wave Speaker), Windows WDM-KS (0 in, 8 out)
   81 Output (Voicemeeter Point 3), Windows WDM-KS (0 in, 8 out)
   82 Input (Voicemeeter Point 3), Windows WDM-KS (8 in, 0 out)
   83 Output (Voicemeeter Point 6), Windows WDM-KS (0 in, 8 out)
   84 Input (Voicemeeter Point 6), Windows WDM-KS (8 in, 0 out)
   85 Voicemeeter Out 1 (Voicemeeter Point 1), Windows WDM-KS (8 in, 0 out)
   86 Voicemeeter Out 4 (Voicemeeter Point 4), Windows WDM-KS (8 in, 0 out)
   87 Voicemeeter Out 7 (Voicemeeter Point 7), Windows WDM-KS (8 in, 0 out)
   88 Output (Voicemeeter Point 1), Windows WDM-KS (0 in, 8 out)
   89 Input (Voicemeeter Point 1), Windows WDM-KS (8 in, 0 out)
   90 Output (Voicemeeter Point 4), Windows WDM-KS (0 in, 8 out)
   91 Input (Voicemeeter Point 4), Windows WDM-KS (8 in, 0 out)
   92 Output (Voicemeeter Point 7), Windows WDM-KS (0 in, 8 out)
   93 Input (Voicemeeter Point 7), Windows WDM-KS (8 in, 0 out)
   94 Voicemeeter Out 2 (Voicemeeter Point 2), Windows WDM-KS (8 in, 0 out)
   95 Voicemeeter Out 5 (Voicemeeter Point 5), Windows WDM-KS (8 in, 0 out)
   96 Voicemeeter Out 8 (Voicemeeter Point 8), Windows WDM-KS (8 in, 0 out)
   97 Output (Voicemeeter Point 2), Windows WDM-KS (0 in, 8 out)
   98 Input (Voicemeeter Point 2), Windows WDM-KS (8 in, 0 out)
   99 Output (Voicemeeter Point 5), Windows WDM-KS (0 in, 8 out)
  100 Input (Voicemeeter Point 5), Windows WDM-KS (8 in, 0 out)
  101 Output (Voicemeeter Point 8), Windows WDM-KS (0 in, 8 out)
  102 Input (Voicemeeter Point 8), Windows WDM-KS (8 in, 0 out)
  103 Voicemeeter Out 3 (Voicemeeter Point 3), Windows WDM-KS (8 in, 0 out)
  104 Voicemeeter Out 6 (Voicemeeter Point 6), Windows WDM-KS (8 in, 0 out)
  105 Output (AMD HD Audio HDMI out #0), Windows WDM-KS (0 in, 2 out)
  106 Virtual Mic (AudioRelay Wave), Windows WDM-KS (2 in, 0 out)
  107 Audífono de los auriculares con micrófono (Xiaomi Sound Outdoor), Windows WDM-KS (0 in, 1 out)
  108 Micrófono de los auriculares con micrófono (Xiaomi Sound Outdoor), Windows WDM-KS (1 in, 0 out)
  109 Line Out (AudioRelay Wave), Windows WDM-KS (0 in, 2 out)
  110 SPDIF Out (HD Audio SPDIF out), Windows WDM-KS (0 in, 2 out)
  111 Altavoces (), Windows WDM-KS (0 in, 2 out)
  112 Auriculares (), Windows WDM-KS (0 in, 2 out)
  113 Micrófono (USB/XLR Condenser Mic A04 Gen2), Windows WDM-KS (2 in, 0 out)
  114 Auriculares con micrófono (Crusher Evo), Windows WDM-KS (0 in, 1 out)
  115 Auriculares con micrófono (Crusher Evo), Windows WDM-KS (1 in, 0 out)
As requested I also ran some test with `instrumented.py` and hundreds of callbacks a second, and so far I can say that I'm no longer able to reproduce the issues I had back in May under the same python, cffi and sounddevice version. Looking back at my use of during the time the PR was open I realized that my usage of sounddevice was quite egregious (repeatedly opening fallback streams during buffer starvation). I had my suspicions it was maybe the driver, as I switched to WASAPI instead of MME, but even that gave me no results after about 630k callbacks.

You were right that bypassing __init__ via __new__ was addressing a symptom of that adverse state. Since this is not reproducing under normal conditions, even so after reverting my own patches. For the moment I have nothing more to say, and I appreciate your patience during this.

Though I still do believe, that, if for some reason, some way, a non-int flag arrives from external bindings, it would be better to keep the defensive int() cast:

class CallbackFlags:
    __slots__ = '_flags'
    def __init__(self, flags=0x0):
        self._flags = int(flags)

You may close this PR if you'd like no further discussion though.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants