Giter Site home page Giter Site logo

Comments (12)

mgeier avatar mgeier commented on August 20, 2024 1

Multiple MIDI events can be generated for the same offset.
See my example midi_chords.py, where three notes are generated at the same offset.

@macdroid53 A few caveats regarding @MaurizioB's code above:

  • Depending on the exact requirements it may be the right thing to use threads, but I would first try to tackle the problem without (additional) threads. You should only create your own threads if you really need them.

  • Within the audio callback, you shouldn't use the blocking Queue.get() method. Use get_nowait() instead. This will throw an exception is the queue is empty (see below).

  • You shouldn't use Queue.empty(), since according to the official docs it's rather useless (since it doesn't guarantee anything): https://docs.python.org/3/library/queue.html#queue.Queue.empty. Just use a try/except block and check for the right exception (queue.Empty). This is more Pythonic anyway (EAFP).

See my example play_file.py for the (hopefully!) proper use of queue.Queue.

from jackclient-python.

mgeier avatar mgeier commented on August 20, 2024

it repeats the MIDI message forever

Yes, that's what you tell it to do.

Starting with client.activate(), the callback function macsend() is called once for each audio block, each time generating a MIDI event.
BTW, you should call client.deactivate() in the end (or use a with statement) to properly stop the audio callback.

I can't seem to find the correct way to send a single midi message under my control.

When do you want to send that message?

If you describe what you actually want to do, I can probably give you some kind of a hint.

from jackclient-python.

macdroid53 avatar macdroid53 commented on August 20, 2024

Yes, I read that it will be called each process loop on the web site. So, I really wasn't surprised that this snippet is doing that. (I cobbled it from one of the examples, so it is lacking deactivate due to my lazy cobbling.) ;o

I read through the examples and they all use the callback to do the work. I'm missing the part that says "the work" is only one midi packet.

The simplest example of what I'm trying to do is: When the user clicks a button, my app must send, for example, a control change message to a specific midi channel. (So, for the event in the example, on my mixer, on midi channel 2 set the slider for channel 1, to -10db.)

from jackclient-python.

MaurizioB avatar MaurizioB commented on August 20, 2024

I usually have a process callback which uses a Queue.
This is an example, taken from a custom program I use.

class JackThread(SomeThreadingClass):
    def __init__(self):
        [...]
        self.client.set_process_callback(self.process)
        self.client.activate()
        [...]

    def output_event(self, event):
        self.equeue.put(event)

    def process(self, frames):
        for port in self.client.midi_outports:
            port.clear_buffer()
        offset = 0
        while not self.equeue.empty():
            event = self.equeue.get()
            self.client.midi_outports[event[0]].write_midi_event(offset, event[1:])
            offset += 1

Then, from the main loop, I just call the output_event method with the midi event as argument; in your case it might be something like this.

    def button_clicked(self):
        self.jack_client.output_event(0, 0xB2, 0x01, 0x40)

from jackclient-python.

macdroid53 avatar macdroid53 commented on August 20, 2024

I haven't tried to run this code, but I do use queues so I get what you're doing.

What does the offset variable and subsequent increment of offset do? Is this actually indexing the jack buffer so the next set of midi bytes are added rather than overwritten when there is more than one event in the queue? (I just looked at the doc and it says the argument is: "Time (in samples) relative to the beginning of the current audio block." Your example increments by 1, so 1 sample per 3 midi bytes? Am I reading/groking that right? )

(I have implemented based on MaurizioB's example, thanks!, that works just I was seeking. I would still like to understand the time/sample argument mentioned above though for future reference.)

from jackclient-python.

MaurizioB avatar MaurizioB commented on August 20, 2024

I'm glad you sorted that out, even if my example wasn't complete.
About your question, I don't exactly know how it (jack midi) works, but as far as I understand, you can only have a certain number of events per audio block, and they have to be ordered, according to the timing in the current block.
As explained in the help, indeed, unordered events are ignored, but I don't actually know what happens if you write two events with the same order, I haven't tested that yet.

from jackclient-python.

MaurizioB avatar MaurizioB commented on August 20, 2024

Thank you @mgeier for those tips!

from jackclient-python.

macdroid53 avatar macdroid53 commented on August 20, 2024

I've looked at play_file.py extensively and I admit that I have not groked every detail. But, I've adjust my code somewhat based on what I think I understand.

At this point I have the following code in my process function
and I have a xrun callback as well:

def process(self, frames):
    self.print_error( 'frames={0}'.format(frames))
    for port in self.client.midi_outports:
        port.clear_buffer()
    offset = 0
    try:
        midievent = self.MIDIsndrqueue.get_nowait()
        # while not self.MIDIsndrqueue.empty():
        #     event = self.MIDIsndrqueue.get()
        print('In midi process: {0} || {1}'.format(midievent[0], midievent[1:]))
        self.client.midi_outports[midievent[0]].write_midi_event(offset, midievent[1:])
        sleep(0.100)
        offset += 1
    except queue.Empty:
        self.print_error('JACK midi queue empty')

But, just siting there it gets xruns:

frames=1024
JACK midi queue empty
An xrun occured, increase JACK's period size?
An xrun occured, increase JACK's period size?
Jack: JackClient::ClientNotify ref = 11 name = jmidi_ShowMixer notify = 3
Jack: JackClient::kXRunCallback
frames=1024
JACK midi queue empty
frames=1024
JACK midi queue empty
An xrun occured, increase JACK's period size?
Jack: JackActivationCount::Signal value = 0 ref = 11
Jack: JackActivationCount::Signal value = 0 ref = 11
frames=1024
JACK midi queue empty
Jack: JackClient::ClientNotify ref = 11 name = jmidi_ShowMixer notify = 3
Jack: JackClient::kXRunCallback
frames=1024
An xrun occured, increase JACK's period size?
JACK midi queue empty
frames=1024
JACK midi queue empty
Jack: JackActivationCount::Signal value = 0 ref = 11
Jack: JackActivationCount::Signal value = 0 ref = 11
frames=1024

So, I don't get why it would xrun when nothing is being processed. And how does one increase JACK's period? Can that be done in code based on buffer size, etc.? Or do the settings have to be adjusted from a JACK controller like QJackctl?

It also gets an xrun every time I something is queued. I basically send 3 byte single midi commands, is the buffer in the client that small by default?

from jackclient-python.

mgeier avatar mgeier commented on August 20, 2024

I don't get why it would xrun when nothing is being processed.

Well that's not quite true, you are doing plenty in your process callback.
Did you try it with an actually empty callback?

Strictly speaking, you shouldn't even print stuff in the process callback.
But I guess the real problem is this:

sleep(0.100)

I don't know which exact function this is (it might be time.sleep() from the standard library?), but are you really sleeping 0.1 seconds here?
Why are you doing that?
You should never sleep for any amount of time in the process callback, except if you want to deliberately cause xruns!
If it's really 0.1 seconds, you should think for a moment what that means: Assuming that you use a sampling rate of 48000 Hz and a block size of 1024 frames, playing back one frame takes 0.021333 seconds. Obviously, your process callback must not take longer than that. Now if you sleep 0.1 seconds, that's five times as long!

Apart from that, you still use offset += 1.
If you don't really need this, please remove it.

And how does one increase JACK's period? Can that be done in code based on buffer size, etc.?

This might work by assigning a new value to self.client.blocksize, see http://jackclient-python.readthedocs.io/#jack.Client.blocksize.
But it probably makes more sense to choose the block size when starting the JACK daemon.

Or do the settings have to be adjusted from a JACK controller like QJackctl?

Yes, that's typically where you would do that.
Or via command line options if you are starting jackd manually.

I basically send 3 byte single midi commands, is the buffer in the client that small by default?

I don't think so. I guess the problem is somewhere else.

You should provide a minimal but runnable code example, then I can also try it and help find the problem.

from jackclient-python.

macdroid53 avatar macdroid53 commented on August 20, 2024

So, I removed the sleep (it was indeed a time.sleep() call). I had put it there attempting to debug what I thought was a timing issue with the moving faders on one of the mixers. The xruns have quieted down to occasionally. (I should have known better having written plenty of interrupt callbacks for micro-controllers.) My bad... :(

I guess I don't get what the offset is, but, I removed the offset += 1 and it seems to work. Does it offset into a ring buffer for large blocks of data when the buffer is not empty?

from jackclient-python.

mgeier avatar mgeier commented on August 20, 2024

Good. You can probably tweak some JACK settings to completely get rid of xruns.

offset is much simpler than you probably think. In the docs it's called time: http://jackclient-python.readthedocs.io/#jack.OwnMidiPort.write_midi_event.
It's just the time when you want your MIDI event to be sent. And this time is given in samples relative to the beginning of the current audio block.
If you want to send your event as soon as possible, just use offset = 0.

Note that write_midi_event() might raise a JackError, you should check for that, just to be sure.
If you want to send multiple events at the same time anyway, it's probably easier to use reserve_midi_event(), which returns an empty buffer if anything goes wrong.

Unless you have more questions, can you please close this issue?

from jackclient-python.

macdroid53 avatar macdroid53 commented on August 20, 2024

Ok, I was definitely making offset out to be more complicated than it is. Thanks for the enlightenment!

And, thanks again for the help.

from jackclient-python.

Related Issues (20)

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.