Error message running Csound with Python and PyQt6

Hi everyone,

I’m doing a project where I’m controlling Csound with ctcsound and using PyQt6 to build the GUI. It’s a real-time improvisation system. I’m on a 2021 14" MacBook Pro with the M1 Pro chip running macOS 13.4.1.

On rare occasions my program crashes and I get the error message below. While it doesn’t happen often (maybe once every 100 times I run the program) it would be pretty bad if it happened while performing live. Can anyone help me interpret this error message and suggest what might be wrong? I’m not even sure if this is a Csound, PyQt6, or Python issue. Any help would be appreciated!

libc++abi: terminating due to uncaught exception of type std::__1::system_error: condition_variable wait failed: Invalid argument
backtrace() returned 79 addresses
0 CsoundLib64 0x00000001186618f8 signal_handler + 52
1 libsystem_pthread.dylib 0x0000000188b3bc28 pthread_kill + 288
2 libsystem_c.dylib 0x0000000188a49ae8 abort + 180
3 libc++abi.dylib 0x0000000188af4b84 _ZN10__cxxabiv130__aligned_malloc_with_fallbackEm + 0
4 libc++abi.dylib 0x0000000188ae43b4 _ZL28demangling_terminate_handlerv + 320
5 libobjc.A.dylib 0x00000001887bb03c _ZL15_objc_terminatev + 160
6 libc++abi.dylib 0x0000000188af3f48 _ZSt11__terminatePFvvE + 16
7 libc++abi.dylib 0x0000000188af3eec _ZSt9terminatev + 56
8 QtCore 0x0000000101a47470 _ZN10QSemaphore7acquireEi + 80
9 QtGui 0x00000001088a3890 _ZN20QGenericUnixServices19setApplicationBadgeEx + 157760
10 QtGui 0x00000001085cc81c _ZN18QRasterPaintEngine9drawRectsEPK5QRecti + 1436
11 QtGui 0x00000001085cdb64 _ZN18QRasterPaintEngine8fillRectERK6QRectFP9QSpanData + 424
12 QtGui.abi3.so 0x0000000100f956b8 ZL22meth_QPainter_fillRectP7_objectS0 + 148
13 python3.11 0x00000001004fb118 cfunction_call + 96
14 python3.11 0x00000001005dde34 _PyEval_EvalFrameDefault + 196240
15 python3.11 0x0000000100498da4 _PyFunction_Vectorcall + 476
16 python3.11 0x000000010049e9e8 method_vectorcall + 164
17 sip.cpython-311-darwin.so 0x0000000100e99d98 sip_api_call_procedure_method + 100
18 QtWidgets.abi3.so 0x0000000107e7d044 _ZN10sipQWidget10paintEventEP11QPaintEvent + 132
19 QtWidgets 0x00000001091ce330 _ZN7QWidget5eventEP6QEvent + 132
20 QtWidgets.abi3.so 0x0000000107e7e114 _ZN10sipQWidget5eventEP6QEvent + 224
21 QtWidgets 0x0000000109184fec _ZN19QApplicationPrivate13notify_helperEP7QObjectP6QEvent + 272
22 QtWidgets 0x0000000109186994 _ZN12QApplication6notifyEP7QObjectP6QEvent + 3396
23 QtWidgets.abi3.so 0x0000000107e66548 _ZN15sipQApplication6notifyEP7QObjectP6QEvent + 248
24 QtCore 0x00000001018d3f88 _ZN16QCoreApplication15notifyInternal2EP7QObjectP6QEvent + 292
25 QtWidgets 0x00000001091c144c _ZN14QWidgetPrivate10drawWidgetEP12QPaintDeviceRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 3312
26 QtWidgets 0x00000001091c8adc _ZN14QWidgetPrivate22paintSiblingsRecursiveEP12QPaintDeviceRK5QListIP7QObjectEiRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 864
27 QtWidgets 0x00000001091c154c _ZN14QWidgetPrivate10drawWidgetEP12QPaintDeviceRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 3568
28 QtWidgets 0x00000001091c8adc _ZN14QWidgetPrivate22paintSiblingsRecursiveEP12QPaintDeviceRK5QListIP7QObjectEiRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 864
29 QtWidgets 0x00000001091c154c _ZN14QWidgetPrivate10drawWidgetEP12QPaintDeviceRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 3568
30 QtWidgets 0x00000001091c8adc _ZN14QWidgetPrivate22paintSiblingsRecursiveEP12QPaintDeviceRK5QListIP7QObjectEiRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 864
31 QtWidgets 0x00000001091c154c _ZN14QWidgetPrivate10drawWidgetEP12QPaintDeviceRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 3568
32 QtWidgets 0x00000001091c8adc _ZN14QWidgetPrivate22paintSiblingsRecursiveEP12QPaintDeviceRK5QListIP7QObjectEiRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 864
33 QtWidgets 0x00000001091c154c _ZN14QWidgetPrivate10drawWidgetEP12QPaintDeviceRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 3568
34 QtWidgets 0x00000001091c8adc _ZN14QWidgetPrivate22paintSiblingsRecursiveEP12QPaintDeviceRK5QListIP7QObjectEiRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 864
35 QtWidgets 0x00000001091c89cc _ZN14QWidgetPrivate22paintSiblingsRecursiveEP12QPaintDeviceRK5QListIP7QObjectEiRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 592
36 QtWidgets 0x00000001091c89cc _ZN14QWidgetPrivate22paintSiblingsRecursiveEP12QPaintDeviceRK5QListIP7QObjectEiRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 592
37 QtWidgets 0x00000001091c89cc _ZN14QWidgetPrivate22paintSiblingsRecursiveEP12QPaintDeviceRK5QListIP7QObjectEiRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 592
38 QtWidgets 0x00000001091c89cc _ZN14QWidgetPrivate22paintSiblingsRecursiveEP12QPaintDeviceRK5QListIP7QObjectEiRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 592
39 QtWidgets 0x00000001091c154c _ZN14QWidgetPrivate10drawWidgetEP12QPaintDeviceRK7QRegionRK6QPoint6QFlagsINS_14DrawWidgetFlagEEP8QPainterP21QWidgetRepaintManager + 3568
40 QtWidgets 0x00000001091dec34 _ZN21QWidgetRepaintManager13paintAndFlushEv + 3904
41 QtWidgets 0x00000001091deeb8 _ZN21QWidgetRepaintManager4syncEv + 252
42 QtWidgets 0x00000001091ce7d0 _ZN7QWidget5eventEP6QEvent + 1316
43 QtWidgets.abi3.so 0x0000000107e7e114 _ZN10sipQWidget5eventEP6QEvent + 224
44 QtWidgets 0x0000000109184fec _ZN19QApplicationPrivate13notify_helperEP7QObjectP6QEvent + 272
45 QtWidgets 0x0000000109186994 _ZN12QApplication6notifyEP7QObjectP6QEvent + 3396
46 QtWidgets.abi3.so 0x0000000107e66548 _ZN15sipQApplication6notifyEP7QObjectP6QEvent + 248
47 QtCore 0x00000001018d3f88 _ZN16QCoreApplication15notifyInternal2EP7QObjectP6QEvent + 292
48 QtCore 0x00000001018d5238 _ZN23QCoreApplicationPrivate16sendPostedEventsEP7QObjectiP11QThreadData + 1428
49 libqcocoa.dylib 0x000000011ed03268 qt_plugin_instance + 51964
50 libqcocoa.dylib 0x000000011ed03d40 qt_plugin_instance + 54740
51 CoreFoundation 0x0000000188c1a63c CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION + 28
52 CoreFoundation 0x0000000188c1a5d0 __CFRunLoopDoSource0 + 176
53 CoreFoundation 0x0000000188c1a340 __CFRunLoopDoSources0 + 244
54 CoreFoundation 0x0000000188c18f48 __CFRunLoopRun + 828
55 CoreFoundation 0x0000000188c184b8 CFRunLoopRunSpecific + 612
56 HIToolbox 0x0000000192462c40 RunCurrentEventLoopInMode + 292
57 HIToolbox 0x0000000192462a7c ReceiveNextEventCommon + 648
58 HIToolbox 0x00000001924627d4 _BlockUntilNextEventMatchingListInModeWithFilter + 76
59 AppKit 0x000000018be39d44 _DPSNextEvent + 636
60 AppKit 0x000000018be38ee0 -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 716
61 AppKit 0x000000018be2d344 -[NSApplication run] + 464
62 libqcocoa.dylib 0x000000011ed02644 qt_plugin_instance + 48856
63 QtCore 0x00000001018dd8fc _ZN10QEventLoop4execE6QFlagsINS_17ProcessEventsFlagEE + 532
64 QtCore 0x00000001018d4614 _ZN16QCoreApplication4execEv + 112
65 QtWidgets.abi3.so 0x0000000107f69cb0 ZL22meth_QApplication_execP7_objectS0 + 96
66 python3.11 0x00000001004fb118 cfunction_call + 96
67 python3.11 0x00000001005dde34 _PyEval_EvalFrameDefault + 196240
68 python3.11 0x00000001005ab53c _PyEval_Vector + 464
69 python3.11 0x00000001005ab2dc PyEval_EvalCode + 248
70 python3.11 0x0000000100644610 run_mod + 184
71 python3.11 0x00000001006443dc pyrun_file + 148
72 python3.11 0x0000000100643e08 _PyRun_SimpleFileObject + 268
73 python3.11 0x0000000100643780 _PyRun_AnyFileObject + 216
74 python3.11 0x00000001006672ac pymain_run_file_obj + 260
75 python3.11 0x0000000100666bd4 pymain_run_file + 72
76 python3.11 0x00000001006663d4 Py_RunMain + 1328
77 python3.11 0x0000000100430c00 main + 56
78 dyld 0x00000001887e3f28 start + 2236

Thanks!
Jason

Hi!

My guess is that it is a threading problem. Do you use csoundPerformanceThread or create your own thread or execute somehow Csound in the Qt event loop?
How do you call Csound functions? I have worked much with C++ and QML in Qt but not much with Python. I guess there is is similar signal and slot connection system? Signals and slots should be the right way to connect your UI (or other components) with Csound funtions.

Can you attach the Python code? Otherwise it is hard to guess. The reason may be also elsewhere…

tarmo

Kontakt Jason Hallen via The Csound Community (<noreply@forum.csound.com>) kirjutas kuupäeval K, 30. august 2023 kell 21:26:

Thanks for the response, Tarmo. I’m using ctcsound.CsoundPerformanceThread to run the Csound thread. Yes, PyQt has the same signals and slots connection system as in Qt, though so far I’m only using it to send changes in a couple GUIs widgets to Csound with .setControlChannel.

The way I’m reading channels from Csound is currently via a QTimer loop which reads from a few .channelPtrs. I’ve also successfully experimented with 1) using a separate thread in PyQt and 2) using OSC to do communication with Csound, but I’m using QTimer for now because it’s so simple and meets my needs.

Do you think using the signals/slots system more consistently for all Csound communication might help avoid threading errors? Or perhaps using a separate thread or OSC for Csound communication rather than a QTimer loop?

I’ve attached the Python code as a .txt file because I can’t upload a .py file.

aya.txt (61.1 KB)

It’s pretty messy because it’s in early stages of development, but maybe it’ll help. Searching for “csound” in the code will take you to the parts of the code where I’m sending and receiving messages from Csound. I’ve also included the .csd file in case that helps.

aya.csd (2.8 KB)

Thanks for your help!
Jason

Checking, are you calling csoundInitialize with value (CSOUNDINIT_NO_ATEXIT | CSOUNDINIT_NO_SIGNAL_HANDLER)?

Hi Steven, no I’m not calling csoundInitialize with those flags. Should I be?

Thanks,
Jason

Yes, IMO, most every API app should. The signal handlers csound installs are appropriate for the csound CLI app but they can interfere with other signal handlers that get installed by things like Python’s interpreter or Java’s JVM. I don’t know if that is the root cause here (I saw the stack trace mentioned signal_handler), but I’d suggest trying to put it in and seeing if the problem persists.

And to add, do it first thing when your application starts and before doing anything else with Csound like creating a Csound object.

Great, thanks for the suggestion! I’ve added this in, so we’ll see if I get those errors again.

Jason

After adding in the csoundInitialize call at the beginning of the code, I’m now getting this shorter error message when my program crashes. The frequency of the crashing has gone up a lot, around once every 7 times I run the program.

libc++abi: terminating due to uncaught exception of type std::__1::system_error: condition_variable wait failed: Invalid argument

It’s no longer producing the very long backtrace report, just this short message. I’m still not sure if this is a macOS/PyQt thing or a Csound thing. Perhaps I’m playing fast and loose with the communication between PyQt and Csound, so I’ll experiment with different ways to improve that.

Jason

Amazing. didnt know taht.

Hi Jason,

I was now able to look at your code. Looks cool! At the first glance I don’t see anything suspicious… It seems right to me how you do it.

I tried to run it but could not do it without samples.

Some things I suggest you to try:

  • do not use any audio output (for testing), instead of Coreaudio try -+rtaudio=null If that works, try wiht portaudio or jack

  • tr bigger buffers (-b and -B options in Csound file)

  • try longer QTimer update time

The problem could be still elsewhere, I cannot tell.

As a fallback, Are you a CsoundQt user? Not all, but I believe essential part of the UI could be realized also with the CsoundQt widgets. And if you need more, you could use PythonQt functionality from withing CsoundQt. Last official CsoundQt MacOS build is wihtoug PythonQt support though… See https://csoundqt.github.io/pages/python.html for some information.
OR if you are comfortable with html and javascript you can add missing parts of the UI in html.

Not sure if it helps…

tarmo

Kontakt Cordelia via The Csound Community (<noreply@forum.csound.com>) kirjutas kuupäeval R, 1. september 2023 kell 15:36:

Thanks very much for taking the time to review the code and send your suggestions, Tarmo!

Can you say a little more about why I should not use any audio output for testing? I’m open to that but would appreciate understanding the reasoning. The program does work with -+rtaudio=null, but it doesn’t work with portaudio. I’ve had problems with portaudio on my computer, which is why I’ve been using coreaudio. Do you suggest portaudio and jack because they perform better than coreaudio?

Trying bigger buffers and update times also sounds good to me. I can keep tweaking them to get a balance of performance and responsiveness.

Yes, I’ve been a CsoundQt user in the past. The past couple years I’ve been delving into building my own GUIs in order to have complete control over the programs. I spent a lot of time using JS/HTML/CSS for Csound GUI development but bailed on that because it was getting too complicated (for a novice like me) operating in the browser environment, and PyQt has been a breath of fresh air since then. It’s good to know though that I can return to CsoundQt if I can’t find a way forward with PyQt.

Thanks!
Jason

Hi!

The program does work with -+rtaudio=null

It was a blind guess. I have heard about problems sometimes with Coreaudio. Anyway, seems that your code is fine, the problem is somewhere in communication with the audio driver. Might be that Python is not fast enough sometimes to fulfill some needs, I don’t know.

Yes, try with Jack, Joachim Heintz has found that it works the best on MacOS (at least with CsoundQt that is using Csound API as well).
And make all buffers bigger and do anything that makes the life of the program easier, later you can breing them down to via trial- and error :slight_smile:

I agree with you - creating a custom GUI with Qt is a good way. Performance wise C++ is of course much quicker. My preferred solution recently is to use QML (Qt Meta Language) for the UI and a C++ class that deals with Csound.

Best!
tarmo

Kontakt Jason Hallen via The Csound Community (<noreply@forum.csound.com>) kirjutas kuupäeval L, 2. september 2023 kell 16:56:

Here’s an update in case this helps anyone in the future. The program kept crashing in the same way as in my initial post. I noticed that the backtrace report often suggested that the crash came when the QtGui module was running the drawText function. In my GUI there’s an event loop that uses the drawText function, and it’s pulling a value from Csound to draw.

I wondered if the Csound value couldn’t be pulled in time to draw the text, so I commented out all the drawText code. That was a few days ago, and there hasn’t been a crash since. I’m not confident the problem won’t re-emerge, but at least for now the crashing has stopped.

I’ve also incorporated Eduardo’s csoundengine to control Csound with Python, and I’m sure his mechanisms for communicating with Csound are more informed and efficient than mine were. That’s probably also helping reduce crashes.

Thanks,
Jason

Hilariously, after just posting my previous message my program just crashed in the same way again for the first time in a few days. Gotta figure out how these PyQt drawing functions are related to the crashing and whether it involves Csound. Time for more debugging…

Jason

I would guess that it is a threading issue. You should not call csound from the gui thread. Instead read any value from csound within a qtimer and put that on a queue. Then when drawing read from that queue. Or do all communication via osc.

Thanks for the tips, Eduardo! I’ll experiment with different ways to read Csound values from outside of the GUI thread. I’ve been able to use QThreads and OSC to read Csound values in separate threads in previous PyQt experiments but was hoping I could keep things simpler for this project. I guess not!

Jason

Hi,

I think uou are close - if you have csound running in separate thread, the Qt way would be to send a signal when a value changes from that thread and and connect it to a drawing slot in your UI thread.

Tarmo

T, 12. september 2023 18:27 Jason Hallen via The Csound Community <noreply@forum.csound.com> kirjutas:

Hi all,

I’ve changed the code to only read Csound data from a separate QThread and send it to the GUI thread via signals/slots. However, I’m still getting crashes. I think I’m misunderstanding how to manage concurrency and thread communication. If I explain what I’m doing can you let me know what I’m doing wrong?

Threads

  1. The main GUI thread is a PyQt QWidget.
  2. Csound is run in a performance thread managed by csoundengine.Engine().session().
  3. Csound data is read in a separate QThread.

Reading Csound Data
The QThread reads data from Csound. Specifically, the instruments defined in the Session use Csound tables to read/write the p-arguments, so the QThread reads the data from these tables with .get(). The data is sent from the QThread to the GUI thread via signals/slots. The slots that receive the data in the GUI trigger updates to the widgets.

Here’s where I’m wondering if I’m doing it wrong. In order to know what Csound data to read in the QThread, I pass the main GUI thread object (main) into the QThread upon instantiation. It looks like this:

class WorkerAPI(QObject):
    '''Runs a loop in a separate thread that reads Csound data.'''
    playback = pyqtSignal(tuple)
    main_volume = pyqtSignal(float)

def __init__(self, main):
    super(WorkerAPI, self).__init__()
    self.main = main

def run(self):
    self._active = True
    while self._active:
        for track in self.main.tracks_panel.track_list:
            if track.synth is not None:
                playback_position = track.synth.get("kplayback_position")
                if playback_position is not None:
                    self.playback.emit((track, playback_position))
        meter_maximum = self.main.mixer.get("kmeter_maximum")
        self.main_volume.emit(meter_maximum)

        time.sleep(0.05)

Does it defeat the purpose of using a QThread to read Csound data if I’m still using the objects defined in the main GUI thread (i.e. self.main.tracks_panel.track_list and self.main.mixer) within the QThread? How else would I approach this? I’m struggling to know how to fully isolate the Csound communication from the main GUI.

Sending Csound Data
Control signals are sent from the GUI thread to Csound within the GUI thread using synth.set(). I was thinking it’s okay to send control signals from the GUI thread because timing isn’t important as long as Csound eventually receives the control signal and the GUI thread is not awaiting a response from synth.set(). But maybe I should also be sending all control signals from the QThread?

Thanks as always for your guidance!
Jason

I’m exploring a new solution. Will report back soon.