spatialaudio / jackclient-python

🂻 JACK Audio Connection Kit (JACK) Client for Python :snake:
https://jackclient-python.readthedocs.io/
MIT License
131 stars 26 forks source link

API for starting and controlling a JACK server? #90

Closed jsl303 closed 2 years ago

jsl303 commented 3 years ago

Is there python binding for API to start and control a JACK server? https://jackaudio.org/api/group__ControlAPI.html I wasn't able to find them in the documentation. I'd love to control how it starts the server like setting inputs and outputs, sampling rate, and so on.

mgeier commented 3 years ago

No, I wasn't even aware that such an API exists!

Do you want to implement it?

jsl303 commented 3 years ago

Unfortunately beyond my head...

HaHeho commented 3 years ago

I found this jack_control in the documentation of the Website Repo. Also never heard about or used it. https://github.com/jackaudio/jackaudio.github.com/wiki/WalkThrough_User_jack_control

Maybe the tool is long gone and not maintained anymore. However, there might be code left that is worth checking out. Maybe it can be "revived" for the specific features you are interested in.

jsl303 commented 3 years ago

Is jack_control supposed to come with Jack?

I'm on MacOS, and I don't see jack_control in my system unfortunately.

HaHeho commented 3 years ago

https://github.com/jackaudio/jack2/blob/develop/example-clients/jack_control

Fount this. So it still seems to exist in the current code base. And apparently even Python 3. However, the file has no .py ending. The implementation of the command line parameter parsing is done by hand ... by manually iterating over the parameters. I mean it looks awful, but maybe it's not better with argparse. I kind of doubt that tho. Probably just a very old implementation that nobody really cares to improve.

However, it might provide exactly the functionality you want. Let is know if you were successful or have problems making it work.

mgeier commented 3 years ago

Is jack_control supposed to come with Jack?

On my Debian Linux system it's included with the JACK package.

If I understand correctly, the jack_control script doesn't actually use the "control API" mentioned above, but it uses dbus, right?

I just had a quick look and it looks like in the JACK1 repo there are Python bindings for the "jackctl" API: https://github.com/jackaudio/jack1/blob/master/python/jackctl.py

Be that as it may, we can try to implement the control API in jackclient-python anyway.

@jsl303 Which functions do you actually need? Which ones do you think are nice to have? Which are useless in our case?

jsl303 commented 3 years ago

I think essentials features to start are:

HaHeho commented 3 years ago

It is possible to set the buffer size of instantiated JACK clients with this API (see property below). This can be done before actually starting the client, so no adaptation is required. Doing this for a client will update this setting for the entire jackd server and other clients. Is that correct @mgeier ? https://github.com/spatialaudio/jackclient-python/blob/0151633118518a6b9bae406d1b66428b085e23f7/src/jack.py#L263

However, the option above is only available to the buffer size and not the sampling rate for example. Hence, a proper API to administrate jackd might be desirable. This should also provide to not only set, but also receive information on the parameters and status of the server of course.

Also, one should always keep in mind that it is possible to run multiple jackd servers at a time, right? I have no idea what viable use cases are or if this actually utilized by someone. However, a server API should definitely support that, I guess.

mgeier commented 3 years ago

Doing this for a client will update this setting for the entire jackd server and other clients. Is that correct @mgeier ?

I'm not sure, but I think so.

Here's the call to jack_set_buffer_size():

https://github.com/spatialaudio/jackclient-python/blob/0151633118518a6b9bae406d1b66428b085e23f7/src/jack.py#L281-L284

I've never actually used this, though, so I don't know if that works in practice.

However, the option above is only available to the buffer size and not the sampling rate for example.

Yes, it looks like that. There seems to be only jack_get_sample_rate() but no jack_set_sample_rate(): https://jackaudio.org/api/group__ServerControl.html

The sample rate can change, though, at least theoretically, because there is jack_set_sample_rate_callback(), which is available as Client.set_samplerate_callback().

Also, one should always keep in mind that it is possible to run multiple jackd servers at a time, right?

Yes, I think so. I have never used multiple servers, though.

But there has been an issue with multiple servers: #46.

mgeier commented 3 years ago

It looks like jackctl_server_create() has been deprecated in JACK2 (in favor of jackctl_server_create2()):

https://github.com/jackaudio/jack2/blob/34fb7b6963864380cf1ab48fe575faed62afd2f0/common/jack/control.h#L122-L148

mgeier commented 3 years ago

Did anyone of you try https://github.com/jackaudio/jack1/blob/master/python/jackctl.py?

I'm unable to create a jackctl.Server object because of many errors similar to this:

Could not open component .so '/usr/lib/x86_64-linux-gnu/jack/jack_alsa.so': /usr/lib/x86_64-linux-gnu/jack/jack_alsa.so: undefined symbol: _ZTIN4Jack15JackAudioDriverE
Could not open component .so '/usr/lib/x86_64-linux-gnu/jack/jack_alsa.so': /usr/lib/x86_64-linux-gnu/jack/jack_alsa.so: undefined symbol: _ZTIN4Jack15JackAudioDriverE
jack_get_descriptor : dll jack_alsa.so is not a driver
jack_get_descriptor returns null for 'jack_alsa.so'

When I try to manually call jackctl_server_create()/jackctl_server_create2() (using CFFI instead of ctypes), I get the same errors.

Apart from these problems, it's a bit strange to include this in the jackclient-python project, because:

It would probably be better to create a separate project for this?

HaHeho commented 3 years ago

Did anyone of you try https://github.com/jackaudio/jack1/blob/master/python/jackctl.py?

No.

Apart from these problems, it's a bit strange to include this in the jackclient-python project, because:

* it's not a "client"

* it needs a separate library named `libjackserver.so` (instead of `libjack.so`)

It would probably be better to create a separate project for this?

Totally.

abarker commented 3 years ago

I wrote this simple wrapper API for the jack_control module to allow it to be imported and run from Python. It may be useful to someone. To the extent that the jack_control script works and is maintained, this gives you the same commands. It's a little ugly (to work around the fact that the main() function in jack_control always looks at sys.argv and writes to sys.stdout), but should be reliable.


"""

Run the jack_control program, with source code linked below, from Python.
   https://github.com/jackaudio/jack2/blob/develop/tools/jack_control

"""

import sys
import io
import jack_control as jc # Must be in `sys.path` list with `.py` extension.

class FakeSys:
    pass

jc.sys = FakeSys # Override the `sys` module that was imported in jc.

def jack_control(*args):
    """Run `jack_control` as if it were called from the command-line with the
    parameter strings as command-line arguments.  Returns the exit code and
    the string that would have been written to stdout."""
    args = [str(a) for a in args] # Convert any ints which were passed in.
    jc.sys.argv = ["jack_control"] + args

    old_sys_stdout = sys.stdout
    sys.stdout = io.StringIO()
    retcode = jc.main() # Call the real `jack_control` function.
    msg = sys.stdout.getvalue()
    sys.stdout = old_sys_stdout

    return retcode, msg

# Test.
if __name__ == "__main__":

    exit_code, msg = jack_control("help") # Get help message.
    print(msg) # Print the help message (redirected from stdout).

    # Toggle start/stop state.
    status_code, msg = jack_control("status")
    if status_code == 0:
        jack_control("stop")
    else:
        jack_control("start")
    print("Status msg is:\n", msg)

    # Get the long description for the engine parameter "driver".
    exit_code, msg = jack_control("epd", "driver")
    print(msg)

    # Set the "rate" parameter.
    exit_code, msg = jack_control("dps", "rate", 44100)
    print("Rate set exit code was:", exit_code)
    print("Rate set msg:\n", msg, sep="")
takanoha001 commented 3 years ago

I agree to @jsl303 it would be so nice if we could control sampling rate, buffer size, and so on from this python layer. : )

mgeier commented 3 years ago

I think it would be better to have this functionality in a separate module (in a separate project) because this is done by a different library (see https://github.com/spatialaudio/jackclient-python/issues/90#issuecomment-671547742).

@takanoha001 Do you want to start such a project?

vrslev commented 2 years ago

Hey there! I created a package to control JACK server with Python. It uses Jack Control API and is kind of modern replacement for jackctl.

@jsl303 @mgeier @abarker @HaHeho @takanoha001 Check it out guys! https://github.com/vrslev/jack_server

mgeier commented 2 years ago

Cool @vrslev, I've added a link to the docs: #114.

I guess we can close this issue now?

vrslev commented 2 years ago

Amazing, thanks! I plan to maintain that package, so probably yes.

vrslev commented 2 years ago

@mgeier Should the issue be closed?

mgeier commented 2 years ago

Yes, I think this can be closed.

If there are further questions or suggestions, everyone should feel free to add a comment here or create a new issue.

Hi-Angel commented 8 months ago

However, the option above is only available to the buffer size and not the sampling rate for example.

Yes, it looks like that. There seems to be only jack_get_sample_rate() but no jack_set_sample_rate(): https://jackaudio.org/api/group__ServerControl.html

FTR, in JACK implementation by Pipewire there is a jack_set_sample_rate function since 0.3.84 release