Skip to content

Use RingBuffer in midi_monitor.py example #15

Closed

Description

@mgeier

Printing to the terminal isn't allowed in the process callback.
The data should be transferred to a non-realtime thread for printing.

This could also be a nice usage example for the RingBuffer.

Activity

  1. amstan commented on May 23, 2015

    @amstan
    Contributor

    Would Queue be an easier thing to use in python? Not sure what the timing implications are, but i think one can get a nonblocking version of it.

    I know we have it (and my project actually needs it since it uses ringbuffers in the C portion), but Queue might be a better choice if you want to move structured data.

  2. mgeier commented on May 24, 2015

    @mgeier
    MemberAuthor

    but i think one can get a nonblocking version of it

    I think that's the point, as far as I understand, Python's queue module uses locking.

    Where would we get a non-blocking queue?

    Queue might be a better choice if you want to move structured data.

    Sure, but that's why I think it would be a good usage example, because there we could show how to use a RingBuffer with data items which are larger than a byte.

    BTW, I don't know yet how to do that ...

  3. added 2 commits that reference this issue on May 25, 2015
    4db3324
    e29b9f6
  4. amstan commented on May 25, 2015

    @amstan
    Contributor

    Sure, but that's why I think it would be a good usage example, because there we could show how to use a RingBuffer with data items which are larger than a byte.

    Well, I implemented both examples for you.

    As I predicted, the ringbuffer one is a lot bigger and uglier. I don't think there are many advantages on using that one (besides the fact that maybe it has a leaner implementation? but who cares, we're using python anyway). I also had to use struct to pack and unpack the time offsets and lengths for the packets.

    The ringbuffer based branch also has a couple of other fixes/commits I needed.

  5. amstan commented on May 25, 2015

    @amstan
    Contributor

    Ringbuffers might be better suited to move audio data around (since individual samples don't really need to be time labeled) and everything (bigger chunks too) can be kept in buffer form. Then pure python data objects doesn't have to go through the ringbuffer.

    Something where the main thread reads audio samples from a file then passes them to the process thread for sending to jack might be a good use of it.

  6. mgeier commented on May 27, 2015

    @mgeier
    MemberAuthor

    Thanks for the example implementations, that's really interesting!

    Python's built-in Queue uses locks, right?
    If yes, it's as bad as directly using print() in the process callback, so we don't gain anything with Queue.

    It is indeed much more complicated than I imagined to use the jack.RingBuffer for this.

    Due to an apparent lack of a blocking "read" function, we cannot wait on the RingBuffer (on the non-realtime side), which makes the code even worse.

    In the light of this I think it's best to just keep the print() calls as they are and add a comment that this is actually bad practice and shouldn't be used in real code.

    In the future we might find a more meaningful example which sends audio data over a RingBuffer.

  7. amstan commented on May 27, 2015

    @amstan
    Contributor

    Python's built-in Queue uses locks, right?
    If yes, it's as bad as directly using print() in the process callback, so we don't gain anything with Queue.

    It probably does use locks, but it's non-blocking (the stuff inside process shouldn't wait for a long time all the sudden). So it should be fine.

    I do think it's better to use Queue than no Queue and just print.

  8. mgeier commented on May 28, 2015

    @mgeier
    MemberAuthor

    I had a quick look at the CPython source, and Queue indeed uses a lock (named self.mutex): https://hg.python.org/cpython/file/3.4/Lib/queue.py#l38
    Queue.put_nowait() acquires this lock, therefore it should not be used in the process callback.

    The problem is that using Queue may look like a proper solution but it isn't.

    I'd prefer to either show an actual solution (which turned out to be too complicated with jack.RingBuffer) or show an obviously bad (but very simple) solution and mention this fact in a comment.

    Unless we find a simple way to do the former, we should do the latter.

  9. amstan commented on May 28, 2015

    @amstan
    Contributor

    Queue.put_nowait() acquires this lock, therefore it should not be used in the process callback.

    process() itself also aquires the GIL. Why are you not worried about that, but are worried about Queue?

  10. mgeier commented on May 29, 2015

    @mgeier
    MemberAuthor

    I am worried about it, but there is nothing I can do about it (except probably trying the GIL-less STM PyPy).

    I'm worried about Queue because it might give a false sense of safety.
    The example doesn't get any more useful/readable/understandable/safe/beautiful/... by using a Queue instead of naively using print().

    To make the limitations explicit, I created PR #18, what do you think about it?

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions