gogins / csound-extended

Extensions for Csound including algorithmic composition, Android app, and WebAssembly.
GNU Lesser General Public License v2.1
40 stars 1 forks source link

Playpen #165

Closed gogins closed 3 years ago

gogins commented 3 years ago

This is the old computer music playpen idea springing right back into life after my favorable experience with Python GTK used by python-abx.

The computer music playpen is an integrated development environment (IDE) for computer music that maximizes functionality and minimizes development time. The idea is to have the shortest, easiest "edit, render in real time and listen., edit..." cycle that is possible. Objectives:

Current approaches that implement some of this:

  1. Code editor for HTML5 piece that runs in NW.js. Cons: no form builder, incomplete CsoundAC support, need for script to run pieces. Pros: Full-powered, easily customizable, lightweight.
  2. Code editor for CsoundAC written in Python. Cons: No form builder (but possible with addon to editor), no HTML5 support (but possible with PyWebKit). Pros: easily customizable, lightweight. Doing this one.
  3. QtCreator. Cons: poor HTML5 support in QtWebEngine, more compex coding. Pros: Good debugging.
gogins commented 3 years ago

Investigate:

gogins commented 3 years ago

I think the playpen is quite doable using Python GTK from a decent code editor. The editor should have custom commands for:

This is all doable with a naming pattern (piece.py goes with piece.glade) and with standardized command line options for the piece, as I already have for Python pieces, but internalized in the piece itself.

Then there can be a template for the piece that loads the Glade file, interprets the command options, and has standard menus and windows in the piece.

It seems better to just write a playpen application using Python GTK.

gogins commented 3 years ago

Changed the design to be a Python GTK3 application, as that looks like it might well be cross-platform and everything I have tried has worked pretty well so far. I'm impressed by the cleanness of GTK. Outstanding tasks:

gogins commented 3 years ago

User-specific settings should include:

In reality, the use of a naming convention to derive the output soundfile name means that none of the Csound settings are actually required, although I will keep the audio output device as a convenience. The audio input and MIDI settings I will not implement.

gogins commented 3 years ago

Post-processing tasks, which if any can be done with GStreamer? A glance shows GStreamer harder to implement than adapting my existing script.

gogins commented 3 years ago

For triggers, it may suffice to send 1 for the pressed signal and 0 for the released signal.

It may be cleaner to have different handlers for different signals, or for different widget classes.

gogins commented 3 years ago

GtkSourceView looks usable but also tricky. See https://stackoverflow.com/questions/10524196/load-gui-from-a-glade-with-gtksourceview-in-pygobject. I have to make Glade aware of SourceView like this https://cjenkins.wordpress.com/2012/05/08/use-gtksourceview-widget-in-glade/, but that didn't work. But adding the old directory for the catalog did work.

API is here: https://developer.gnome.org/gtksourceview/stable/.

Looks like I have to create the editor programmatically and swap it in for the current GtkTextView. No, I got Glade to use the GtkSource.View by adding its catalog directory.

Search and replace is now working but you always have to press enter in the search entry field to get stated, even if you are then going to replace that text. This is not quite standard.

gogins commented 3 years ago

Just found out about GJS. But it's not relevant to my use case, nor is Seed.

Wrong! GJS is very relevant -- it contains the API references that I absolutely need. But there is no user guide!!

gogins commented 3 years ago

My question is, considering that ctcsound.py comes with Csound, is there any way to get it into the WebKit JavaScript context without the C compiler?

gogins commented 3 years ago

My problem right now is that I don't really want to re-implement csound.node for WebKit2. My choices seem to be:

  1. Drop HTML support from the playpen.
  2. Re-implement csound.node for WebKit2. But I don't see evidence that a lot of people are doing this kind of thing. I will look harder. But there seems to be SWIG support. I am going to try this.
  3. Implement my own rudimentary RPC protocol just in Python and JavaScript for a Csound object. This ends up re-doing exactly what the QtWebEngine does, and I hate that.
gogins commented 3 years ago

May need this: https://webkitgtk.org/reference/jsc-glib/stable/JSCContext.html.

gogins commented 3 years ago

OK, this is pretty bad. WebKit2Gtk+ deprecated webkit_frame_get_javascript_global_context because the browser now uses GJS instead of JSC. There are now two APIs for JavaScript in WebKit, one from Apple and one from Gtk.

It's only a problem for me because SWIG uses the wrong API, which is not the one used by Gtk. It would be fine if I were writing a C/C++ program to use WebKit, but it is not fine if I am writing such a program to use WebKit2Gtk+.

I may have to code the wrappers by hand.

gogins commented 3 years ago

As a proof of concept... Jesus that was hard! Finally got "hello" to say "Hello from Csound!"

  1. You must use these APIs: https://webkitgtk.org/reference/jsc-glib/stable/.
  2. But you must figure how to follow this pattern: https://karhm.com/javascriptcore_c_api/.
  3. And then you must add the created function back into the JavaScript context:
        jsc_context_set_value (js_context,
                       "hello",
                       hello);    
gogins commented 3 years ago

I am now trying to see if this machinery is accessible from Python, which would save me from packaging a native module. See https://gjs.guide/.

I can do this if and only if the GJS wrappers for Python implement registering a Python function as a JavaScript function and/or method in the Web page's JavaScript context.

gogins commented 3 years ago

https://github.com/cztomczak/cefpython is an option. It can introspect Python objects and dynamically create JavaScript bindings to classes and methods. Unfortunately it does not support Python later than version 3.7 on Linux.

There is a mad plethora of GUI frameworks, browser frameworks, and form builders for Python, not all of which work and none of which constitute a version-compatible set of libraries with all features that I need (compatible with ctcsound, embeddable form builder, full-featured browser that exposes ctcsound).

gogins commented 3 years ago

There's a hack for supporting cefpython3 on Python3.8 that works locally, just add the version number in init.py, and a symbolic link from the Python3.8 site-packages.

Now message.html plays from cefpython. But the GUI responsiveness gets turned off. I think I can deal with this by performing bufferwise and yielding to the event loop.

No, I can't get that to work. There are too many sharp edges with cefpython.

gogins commented 3 years ago

Of course there is no user guide for using GJS... Perhaps people just write native code and register it for GObject Introspection, I could try doing that with Csound.

gogins commented 3 years ago

Back with cefpython. Everything is working in Python and in CefPython using my new SWIG wrapper for CsoundThreaded.

Not everything is working! Getting messages to print for the user by polling the internal Csound message queue works fine in Python, but is not working in JavaScript. Although everything else is working in javaScript.

Print statement debugging shows everything is fine in CsoundThread right up until the number of messages pending is returned. Evidently it is properly returned from C++ to Python but not from Python to JavaScript.

gogins commented 3 years ago

Plan:

gogins commented 3 years ago

This installed pip for python3.7:

curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
sudo python3.7 get-pip.py
sudo apt install libgirepository1.0-dev
sudo python3.7 -m pip install --ignore-installed PyGObject

Now I can run the Python stuff in the playpen with Python 3.7, but gtk3.py still segfaults.

gogins commented 3 years ago

My choices have now become:

gogins commented 3 years ago

OK, the method implementation works and the pattern is now clear.

gogins commented 3 years ago

Can't seem to get callbacks set from Python and called from JavaScript working. But I can try a workaround, the callback is actually not a callable but just the name of the callback and it is called simply by executing JavaScript code from the CsoundThread inner callback. Can't get this to work either, leaving the message callback unimplemented.

This is not a show-stopper, as the messages are printed in the terminal.

Removed the messages pane from the UI, leaving all messages printing to stdout and stderr.

I may come back to this.

gogins commented 3 years ago

Remaining issues before cleaning up:

gogins commented 3 years ago

Cleanup:

gogins commented 3 years ago

Examples:

gogins commented 3 years ago

The XML parser will read the .glade file into memory and listen for value changes on control widgets, updating the XML in response. When saving, the .glade file will be written from the DOM and not from the .glade file previous read. This will work only if:

gogins commented 3 years ago

The hard part is looking up elements from widgets. Some kind of extra metadata might simplify this. I can't arbitrarily add properties to the ui xml. But I could create a dictionary when I connect the signals.

gogins commented 3 years ago

The widget classes that I will save and restore, how their values can be accessed, and the xpath to the ui element that stores the value. This xpath begins at the element with the same id as the control channel name. "gk_channel" here stands for all channel names. For every get there is a corresponding set. Some of these are subclasses of others, but I need the specific class names in order to handle setting the values properly.

There are three categories: the get_value method, the get_active method, and the get_text method.

gogins commented 3 years ago

There is a fairly serious problem with the UI XML. Default widget values are not saved from ElementTree. I may be able to work around this, or I may use another method of storing the control channel values, e.g. with JSON for the map of channel names and values.

There are no Glade settings to control this, so ElementTree and GTK Builder are almost certainly not the problem.

I could work around this by constructing the elements for widget values in the XML, but I think I will go with a separate file, that should be less fragile.

gogins commented 3 years ago

I have a simple Python dict holding elementary type values for string channel names. This is saved and restored using JSON from piece.ui.channels. It is a separate file and I hate that, but the solution is simple and should be robust.

gogins commented 3 years ago

I'm closing this now. I'm going to start using it. No doubt there will be bugs. They will become separate issues.