precise timing for Python opcodes
Date | 2015-05-14 07:45 |
From | Chuckk Hubbard |
Subject | precise timing for Python opcodes |
Attachments | csd-mtc25.py mtcrecord.py None None |
Hello. I'm trying to send MIDI timecode data. I'm using pygame for MIDI since it allows simultaneous output to different MIDI devices, but I want it to be synchronized to an instance of Csound, so I'm sending the messages with pycall. It works, but the timing is way, way off, some messages have the same timestamp, 3 or 4 in a row, then others have leaps of 100 ms. If I use the output to sync Cubase, it starts and stops repeatedly. I set ksmps to 1 and -b to 4, but it doesn't help. I could send all of the messages as one long write from pygame.midi, with the timestamps manually calculated to send at the appropriate time, but that would defeat the purpose of synchronizing with Csound. I might want Csound and MIDI output together. For these files, you'll probably need different output numbers for the MIDI in- and outputs than what I used. This will list them: pygame.midi.init() for dev in range(0, pygame.midi.get_count()): print '%d %s' % (dev, pygame.midi.get_device_info(dev)) What am I missing? Is the timing sadness from Csound or somewhere else? Thank you for any observations! -Chuckk |
Date | 2015-05-14 16:55 |
From | Jacob Joaquin |
Subject | Re: precise timing for Python opcodes |
Attachments | None None |
I'm having a similar latency issue with the Csound Python API. I'm using the rtmidi2 lib in my case. I believe the issue isn't rtmidi2, but either latency introduced by Python or with Csound. The process I use is that when the script receives a MIDI note-on event, it generates Csound event string and then sends it to Csound via csnd6.ReadScore(). I should run a test to see what kind of latency I get by triggering a MIDI device other than Csound with rtmidi2. On Wed, May 13, 2015 at 11:45 PM, Chuckk Hubbard <badmuthahubbard@gmail.com> wrote:
|
Date | 2015-05-14 17:21 |
From | Chuckk Hubbard |
Subject | Re: precise timing for Python opcodes |
Attachments | None None |
There are some warnings in the Csound manual that MIDI timing is dependent on audio processing, and I get the feeling this must also apply to Python processing. As I understand it, Csound gathers the info it needs for the next buffer, then calculates all of it before checking for changes, so I believe this affects both input and output... right? Of course, if I set ksmps, -b and -B to 1, then it takes several seconds to process 10 milliseconds, even with no audio variables. But the discrepancies I see in the MIDI messages output using the Python API don't even seem to match my ksmps or -b values. -Chuckk On Thu, May 14, 2015 at 6:55 PM, Jacob Joaquin <jacobjoaquin@gmail.com> wrote:
|
Date | 2015-05-15 00:12 |
From | Andres Cabrera |
Subject | Re: precise timing for Python opcodes |
Attachments | None None |
You won't be able to get timing accuracy this way. If there is a way to use the midi device callback from the python API you might have a chance to get good timing, but I'm not sure if this is possible. Cheers,On Thu, May 14, 2015 at 9:21 AM, Chuckk Hubbard <badmuthahubbard@gmail.com> wrote:
|
Date | 2015-05-15 07:34 |
From | Chuckk Hubbard |
Subject | Re: precise timing for Python opcodes |
Attachments | None None |
I suspected as much. From what I can see, the API MIDI callback functions are for calling functions *when* Csound uses MIDI, but I need callbacks that trigger MIDI. Like I said, I need access to multiple MIDI devices, which Csound has never had. So, another question - Is there any interface through which the Csound API can interact with the outside world with accurate timing? Aside from audio, I mean. It seems ludicrous to create a separate audio channel for triggering MIDI. If not, then I guess the best case would be to include a switch in my program to use either MIDI or Csound, to not allow both. Or just remove Csound from the project. Then, if people want to use the MIDI output to control Csound, it's their business. Most likely they won't be looking for 120 messages per second. -Chuckk On Fri, May 15, 2015 at 2:12 AM, Andres Cabrera <mantaraya36@gmail.com> wrote:
|
Date | 2015-05-15 08:36 |
From | Chuckk Hubbard |
Subject | Re: precise timing for Python opcodes |
Attachments | None None |
What about 'schedule' or 'scoreline', in a 'while' loop? I'd need to call an instrument many times, but in uninterpreted seconds, not score time, no? -Chuckk On Fri, May 15, 2015 at 9:34 AM, Chuckk Hubbard <badmuthahubbard@gmail.com> wrote:
|
Date | 2015-05-15 09:07 |
From | Oeyvind Brandtsegg |
Subject | Re: precise timing for Python opcodes |
I am not really sure I understand the cause of the problem. It sounds like something might be odd with your implementation. The timing accuracy of midi handling (and of Python handling) should be consistent with ksmps and it is indeed possible to synchronize Python and Csound this way. Again, perhaps it would be good to have a a simplified example of what is not working for you. 2015-05-15 9:36 GMT+02:00 Chuckk Hubbard |
Date | 2015-05-15 09:10 |
From | Victor Lazzarini |
Subject | Re: precise timing for Python opcodes |
I think the latencies you are experiencing have to do with using python through pycall. You could try to do it directly from your Python program by putting the code inside a process callback (if you are using csoundPerformanceThread) or in between calls to performKsmps() if you are calling these yourself, or using a csoundYield() callback if you are calling csoundPerform(). ======================== Dr Victor Lazzarini Dean of Arts, Celtic Studies and Philosophy, Maynooth University, Maynooth, Co Kildare, Ireland Tel: 00 353 7086936 Fax: 00 353 1 7086952 > On 15 May 2015, at 07:34, Chuckk Hubbard |
Date | 2015-05-15 10:07 |
From | David Worrall |
Subject | Re: precise timing for Python opcodes |
Attachments | None None |
Hi Chuckk, I have no experience of MIDI and Csound so I have not replied before, but your question prompts me: On 15.05.2015, at 08:34, Chuckk Hubbard <badmuthahubbard@gmail.com> wrote:
I _do_ work extensively with udp (osc if you like) and Csound in realtime and do not have a latency problem. I'm using python to continuously pull large amounts of data (sometime 100's of events/sec) from a couple of FIFOs and process it before sending simultaneous commands through csdn6.Csound() instruments and a graphical display - all in (quasi-realtime). I haven't seen an example of what you're doing, but I doubt the latency you experience would be caused by python or the csound API as such. David ______________________________________ Prof. Dr. David Worrall International Audio Laboratories Erlangen Fraunhofer-Institut für Integrierte Schaltungen IIS Am Wolfsmantel 33 91058 Erlangen www: iis.fraunhofer.de --- Adjunct Senior Research Fellow School of Music, Australian National University |
Date | 2015-05-15 10:32 |
From | Chuckk Hubbard |
Subject | Re: precise timing for Python opcodes |
Attachments | mtcrecord.py csd-mtc25.py None None |
Thanks everyone for the ideas. I really hope I can make it work. I attached examples to my original post yesterday. Here they are again. One is for sending MTC, the other with the name mtcrecord is for recording the messages. Both use pygame for MIDI. The input and output numbers I used may not match others' systems. I was using MIDIYoke. -Chuckk On Fri, May 15, 2015 at 12:07 PM, David Worrall <david.worrall@iis.fraunhofer.de> wrote:
|
Date | 2015-05-15 10:41 |
From | Chuckk Hubbard |
Subject | Re: precise timing for Python opcodes |
Attachments | None None |
David, I didn't mean latency; I was asking if there is something preventing Csound from making function calls continuously, if it only happens at certain points. On Fri, May 15, 2015 at 12:07 PM, David Worrall <david.worrall@iis.fraunhofer.de> wrote:
|
Date | 2015-05-15 11:02 |
From | Chuckk Hubbard |
Subject | Re: precise timing for Python opcodes |
Attachments | None None |
Thanks, Victor. I was sure that using the pycall opcodes for the already running interpreter would be the most efficient, but I'm flexible. My program defaults to sr = 44100 and ksmps = 16. In that case, performKsmps should be being called 2756.25 times a second. My MTC messages will be sent 96-120 times a second, so rather than calculating messages to be sent while performKsmps is operating, I'd only have to tell Python at which breaks to send one single MTC message and when not... Does that make sense? I send one MTC message at a time, but I have to determine when and when not to send it. Thank you for the suggestion. -Chuckk On Fri, May 15, 2015 at 11:10 AM, Victor Lazzarini <Victor.Lazzarini@nuim.ie> wrote: I think the latencies you are experiencing have to do with using python through pycall. You |
Date | 2015-05-15 11:26 |
From | Oeyvind Brandtsegg |
Subject | Re: precise timing for Python opcodes |
Yes, it would be better to avoid using pycall for this. You could perhaps do a time reading in the PerformKsmps loop, and if the required amount of time has passed since the last time reading, then call midisendmtcrt. The PerformKsmps loop is tightly synchronized with Csound, it will tick once for each ksmps period ...well, it might be more corret to say that this is the clock tick driving each ksmps period in Csound, but it is Csound (PerformKsmps) that will make the clock go at the right speed since it takes exactly 1/kr secs to return. 2015-05-15 12:02 GMT+02:00 Chuckk Hubbard |
Date | 2015-05-15 11:35 |
From | Victor Lazzarini |
Subject | Re: precise timing for Python opcodes |
You have to remember this: you need to make sure that Csound will run its ksmps cycles tightly in a steady way. So this means that if there is buffering somewhere, Csound can run very quickly to fill the buffer, then wait until it needs to be run again; anything that uses that timing to send triggers to the output will be jittery. This also depends on the audio IO backend (whether it is alsa, jack, mme, coreaudio, portaudio, etc), some will be better at keeping the timing tight. The best results are actually in systems where csound is run inside the audio device callback and that callback is issued regularly (e.g. in iOS, and probably with the jacko opcodes). The next best thing is probably rtalsa with a well matched buffer size, and rtjack, rtauhal, with small buffers. Next is probably rtpa, and worse (much worse) is rtmme. In general, this can be improved by having a -B and -b set to the minimum your system can handle without dropouts. On OSX for instance, -b 128 -B 512 should result in a fairly steady rate of PerfomKsmps() calls (it can go down to -b32 -B128 I guess). best regards ======================== Dr Victor Lazzarini Dean of Arts, Celtic Studies and Philosophy, Maynooth University, Maynooth, Co Kildare, Ireland Tel: 00 353 7086936 Fax: 00 353 1 7086952 > On 15 May 2015, at 11:02, Chuckk Hubbard |
Date | 2015-05-15 11:35 |
From | Victor Lazzarini |
Subject | Re: precise timing for Python opcodes |
I’d say so, but note what I also said about buffering in my previous email. ======================== Dr Victor Lazzarini Dean of Arts, Celtic Studies and Philosophy, Maynooth University, Maynooth, Co Kildare, Ireland Tel: 00 353 7086936 Fax: 00 353 1 7086952 > On 15 May 2015, at 11:26, Oeyvind Brandtsegg |
Date | 2015-05-15 12:34 |
From | Chuckk Hubbard |
Subject | Re: precise timing for Python opcodes |
Attachments | pygame-midi-time.py csd-mtc-ksmps.py sentrecord.txt pygame-alone.txt None None |
I understand. I had just noticed this, I believe: sending pygame messages in between performKsmps, everything else timed perfectly but pygame's time reading was all over the place; several messages on the same millisecond, then a jump of 100 ms after 10 messages; but running a loop with pygame and time.sleep() to check pygame's timing without loading Csound, pygame was perfect. If I understand what you're saying about filling the buffer, then, in fact, using GetChannel to read a channel fed by "times" in a Csound orchestra won't work - the Csound instrument reads the Csound instance's time in seconds, but it may be doing this faster or slower than actual time! So I don't think this is a solution. I'd avoided using a separate OSC-to-MIDI converter, but if David can process 100's of events per second without the problems I'm seeing, maybe I can too. Although I am not the end user for this software, and I'd like it to work without dictating specific platforms and setups that no one needs for other programs that do the same thing, because, finally, people won't do it. Here are my files for testing. The first two columns in sentrecord.txt are in seconds and the third column is pygame's milliseconds since it was initiated. pygame-alone.txt shows pygame's milliseconds with time.sleep(0.01) between readings. I will try with -b 128 -B 512... -Chuckk On Fri, May 15, 2015 at 1:35 PM, Victor Lazzarini <Victor.Lazzarini@nuim.ie> wrote: You have to remember this: you need to make sure that Csound will run its ksmps cycles tightly in a steady way. So |
Date | 2015-05-18 01:24 |
From | Chuckk Hubbard |
Subject | Re: precise timing for Python opcodes |
Attachments | None None |
I have an interesting update. I've been doing lots of probing, reading, testing, thinking, how to better send MIDI timecode to another sequencer. I'm now looking at running my timing from a Python threading object with a list of wait times and so on, and sending notes to a Csound orchestra or to MIDI whenever necessary. BUT I noticed something. Any way that I sent that timecode, Cubase was skipping ahead past a couple of beats and then holding steady, no matter what I did. So I tried with Reaper, and Reaper does the same, it catches up only after a few beats. Then I hooked up Cubase and Reaper to each other, to try it without any of my own software, and the behavior was the same. I downloaded 2 other virtual MIDI devices instead of MIDIYoke and the behavior is the same. I tried sending MIDI Machine Control and other messages to make sure the other software knows when to start and where to start from, but it seems to crash pygame's robust MIDI. The workaround will be to tell my users that, if they need precise sync on the first few notes, they should cut and paste everything one bar later! until I find something better. -Chuckk On Fri, May 15, 2015 at 2:34 PM, Chuckk Hubbard <badmuthahubbard@gmail.com> wrote:
|
Date | 2015-05-18 05:09 |
From | David Worrall |
Subject | Re: precise timing for Python opcodes |
Attachments | None None |
So, Chuckk, are you saying that the problem is with Pygame's MIDI implementation? D. On 18.05.2015, at 02:24, Chuckk Hubbard <badmuthahubbard@gmail.com> wrote:
______________________________________ Prof. Dr. David Worrall International Audio Laboratories Erlangen Fraunhofer-Institut für Integrierte Schaltungen IIS Am Wolfsmantel 33 91058 Erlangen www: iis.fraunhofer.de --- Adjunct Senior Research Fellow School of Music, Australian National University |
Date | 2015-05-19 12:07 |
From | Chuckk Hubbard |
Subject | Re: precise timing for Python opcodes |
Attachments | None None |
I'm not saying quite so much. I don't know yet. And if it ain't broke, I won't be finding out any time soon.
-Chuckk |
Date | 2015-05-22 18:14 |
From | Chuckk Hubbard |
Subject | Re: precise timing for Python opcodes |
Attachments | None None |
Just an update in case anyone has the same issues later. I don't think the timing inaccuracy I was seeing was from pygame's MIDI implementation; I believe it must be like Victor said, that Csound's completion of performKsmps can be earlier or later depending on how much is being processed. I have switched to python-rtmidi instead of pygame for MIDI output for other reasons, but I can still use pygame to see timestamps on MIDI input. Doing that, I get decent timing just like this: mout = md.MidiOut() mout.open_port(1) ind = 0 start = time.clock() while ind < 1100: diff = time.clock() - start if diff > (ind+1)/100.0: mout.send_message((241, msglist[ind][1])) ind += 1 time.sleep(.001) ...which I never expected on Windows. It may not be a real solution either; I imagine it puts some load on the CPU. But, both looking at the timestamps and listening to Cubase synced to this timecode, it's pretty good timing, within 1 or 2 milliseconds for the most part. If I don't encounter problems with this, my program will now calculate tempo on its own and send events to both MIDI and Csound. Thanks for all the info! -Chuckk On Tue, May 19, 2015 at 2:07 PM, Chuckk Hubbard <badmuthahubbard@gmail.com> wrote:
|
Date | 2015-05-22 21:32 |
From | Oeyvind Brandtsegg |
Subject | Re: precise timing for Python opcodes |
Good to hear you found a solution, Chuckk! 2015-05-22 19:14 GMT+02:00 Chuckk Hubbard |