dsacre/mididings

Arpeggiator

Closed this issue ยท 11 comments

What is the right approach to write an arpeggiator using mididings?

Well, it depends. If you can receive a midi clock, you could create a python class which would be the "arpeggiator" object, which would have attributes for arpeggiator patterns, then create a method that accepts note events (keeping track of currently active notes), and a function for clock events, which cycles the pattern accordingly.
Then it would be something like:

run([Filter(SYSRT_CLOCK)>>Call(clock_function), Filter(NOTEON)>>Call(arpeggiator.add_note), Filter(NOTEOFF)>>Call(arpeggiator.del_note)])

The clock_function() will then use mididings.engine.output_event().

The same thing can be done without a midi clock, but would require a separate thread which will run alongside mididings, again using the output_event() function.
Be careful using threading, because the output_event function is not thread safe, and you might run into problems (usually, the whole mididings thread will hang because the output_event is being called at the same moment, trying to write to the output port at the same time).

Amazing response. It pointed me to the right direction. Thanks! I wish we can share scripts in one common place

You're welcome! Well, you could always share your code here as an attachment or by pasting it :-)
Unfortunally, it looks like Dominic doesn't check in here much, sharing examples in the official repository would be great.
Anyway, I was thinking about creating a personal repository for various mididings scripts and examples written by users. I'd be very interested on how you would implement your arpeggiator.

Hello guys, this is an interresting article and I want to share my experience with mididings with you.

I'm an IT programmer, a musician and a midi lover and I use mididings in a Rush Tribute Band. I recently found it. It's a monstrous software and I depend on it forever. It run on a raspberry pi with Ubuntu Mate as OS... It just lacks examples and advanced documentation.

I also want an Arpeggiator... and a Glissando

My bug : I don't know 'how to sleep X milliseconds' between the notes... i tried a sleep in a loop but it don't work, threading problem...

There is an active support Google Groups https://groups.google.com/forum/#!forum/mididings where I found solutions with Christopher Arndt support. Christopher Arndt is the python-rtmidi author and he help me a lot.

We must setup an example repository by sharing our own code. This is open source. Mine is there : https://github.com/stefets/live-config/blob/evolution/live.py

I have some propositions :

  1. Use the official github wiki - https://github.com/dsacre/mididings/wiki - since it's public, we can setup a nice wiki of examples.
  2. Create a fork of mididings and setup our own wiki on github
  3. Create a web site (i can host website) (not the best imho)
  4. Expand the official documentation and we fill it with our examples.
  5. ?????

Thanks

Indeed, you can't use yield with Process(), since it returns a generator and not an event, as stated in the docs:

Process the incoming MIDI event using a Python function, then continue executing the mididings patch with the events returned from that function.

For the same reason, you can't use a sleep function, since Process waits for events to be returned, will block every processing until the called function ends, resulting in an useless wait that will then send all the note events all at once. You could use Call() (which doesn't block the processing) with output_event() called with a recursive chain of python's threading.Timer(), but, as stated before, you might occur in some random troubles with threading if you are playing other notes at the same time. To avoid that, you could use another external mididings script linked with the one you already use (by creating a dedicated midi port), which will be only dedicated to the glissandos: the main script will filter according to the chain you already created:

_glissando = ChannelFilter(9) >> Filter(CTRL) >> CtrlFilter(20,22) >> Output('gliss_port')

(note: you don't need to use a list with CtrlFilter)
then the dedicated mididings script will be triggered with that event only with Call(gliss_exec)

def gliss_function(note, port, chan, vel):
    output_event(MidiEvent(NOTEOFF if note % 2 else NOTEON, port, chan, note / 2, vel)
    note += 1
    if note < 100:
        Timer(.1, lambda: gliss_function(note, port, chan, vel))
def gliss_exec(event):
    gliss_function(0, ev.port, ev.channel, 100)

Also, at line 40 you don't need to create evcls, since the *Event objects are actually functions that return a MidiEvent, you could just use:

return [MidiEvent(ev.type, ev.port, ev.channel, ev.note + i, ev.velocity) for i in chord_offsets]

And, for line 75 you don't need subprocess, there's System() :-)

Going back to your proposal, I didn't think about the wiki. I've never actually used them, but if is public I suppose that it's the best solution.
I actually got a decent experience with mididings in the last 2 years, doing some hacks, like specific scene switch calls (without the need for init), consolle coloring, advanced routing (4 inputs and 4 outputs with full custom routing triggered by 16 buttons, automating the creation of 65536 scenes, instead of using complex Process computing). I'd really like to share what I accomplished so far.

Thanks MaurizioB for your hints!

The glissando is not working at this moment, there is a strange sound and the gliss_function is called once, I put a print into... I will work on it further... anyway I pushed my code with a little modification....

For the WIKI I agree with you... it's the place... I think we can populate it !

Sorry, I always forget that Timer() returns an object that needs to be started.
This should do it:

def gliss_function(note, port, chan, vel):
    output_event(MidiEvent(NOTEOFF if note % 2 else NOTEON, port, chan, note / 2, vel)
    note += 1
    if note < 100:
        Timer(.1, lambda: gliss_function(note, port, chan, vel)).start()
def gliss_exec(ev):
    gliss_function(0, ev.port, ev.channel, 100)

It works ! I still have a strange behavior... I don't know if I will achieve the desired result, but it's a good start.

Hey folks. I started the GH wiki if anyone wishes to add links to scripts/snippets/systems - https://github.com/dsacre/mididings/wiki

That's a very good initiative. Thanks for adding my site as a reference. I always improve it and i will documented it as well. Can you post the information to the Mididings Google Group ?

Hey, have a look at my brand new Mididings Arpeggiator :) It still needs work, but it's working quite well and giving me such joy.

https://github.com/webpolis/mididings-arpeggiator

I'm adding it to the wiki.

Feel free to contribute!