Kara-Moon Forum

Developers & Technology => Musical MIDI Accompaniment (MMA) => Topic started by: sciurius on May 13, 2020, 12:10:40 PM



Title: After (more)
Post by: sciurius on May 13, 2020, 12:10:40 PM
While trying to find out why this doesn't work:

Code:
After Count=2 Drum-KickDrum2   @rhythm Seq=|3-------3-------3-------3-------|
After Count=2 Drum-PedalHiHat  @rhythm Seq=|--------6---------------6-------|
After Count=2 Drum-RideCymbal1 @rhythm Seq=|7-------7----7--7-------7----7--|

It seems that only one After is possible for a specific location, and that subsequent Afters for the same location are silently ignored. Is that intentional?


Title: Re: After (more)
Post by: bvdp on May 13, 2020, 04:53:46 PM
No, they should not. They are supposed to be stacked. Are you using this is * repeat?

A few lines of context would help.


Title: Re: After (more)
Post by: sciurius on May 13, 2020, 05:06:35 PM
Okay. Yes, it is related to the * repeat stuff.

Code:
After Count=2 print 2A
After Count=2 print 2B
After Count=3 print 3A
After Count=3 print 3B
After Count=4 print 4A
After Count=4 print 4B
  1  z * 6

This prints 2A, 3A, 4A. With 6 lines z instead of the * repeat it prints 2A, 2B, 3A, 3B, 4A, 4B.


Title: Re: After (more)
Post by: sciurius on May 13, 2020, 05:39:38 PM
BTW Are you still considering adding Before? Given the recent changes to After this shouldn't be so hard now :) .


Title: Re: After (more)
Post by: bvdp on May 13, 2020, 06:51:23 PM
Okay. Yes, it is related to the * repeat stuff.

Code:
After Count=2 print 2A
After Count=2 print 2B
After Count=3 print 3A
After Count=3 print 3B
After Count=4 print 4A
After Count=4 print 4B
  1  z * 6

This prints 2A, 3A, 4A. With 6 lines z instead of the * repeat it prints 2A, 2B, 3A, 3B, 4A, 4B.

Right then. I was scratching my head at some of the code when I was doing the * stuff and wondering why it was like it was ... guess that's the answer :) I'll look more!


Title: Re: After (more)
Post by: bvdp on May 13, 2020, 06:52:17 PM
Before? Ummm, not sure ... just what is the point of this?


Title: Re: After (more)
Post by: sciurius on May 13, 2020, 08:38:58 PM
Because I cannot do this:

Code:
After Count=1 Drum-KickDrum2      @rhythm  Seq=|3--3--3--3--|
Groove A
  1    z
Groove B
  2    z

After the 1st measure the Drum-KickDrum2 sequence is altered. But the Groove B statement restores the sequences so the change gets lost.

A hypothetical Before would solve this:

Code:
Before Count=2 Drum-KickDrum2      @rhythm  Seq=|3--3--3--3--|
Groove A
  1    z
Groove B
  2    z

Now the sequence alteration takes place before the 2nd bar, and after the groove change.


Title: Re: After (more)
Post by: bvdp on May 13, 2020, 10:17:39 PM
Okay, I will need to digest this.


Title: Re: After (more)
Post by: bvdp on May 14, 2020, 02:02:50 AM
I think this fixes the after * problem. In after.py at line 148 out-comment the loop. Should look like this then:

Code:
  fd.close()
  afterData = [ x for x in afterData if x.bar != nn]
  MMA.parse.parseFile(name)

I think we were removing discarded events too many times :)

Opps ... editing since it is not quite right. I still need to delete one event since we're getting a duplicate. Well, tomorrow.


Title: Re: After (more)
Post by: bvdp on May 14, 2020, 06:59:10 PM
Hey, that was fun. Keep looking at the recursion, etc. And all along it was just that I wasn't writing all the needed data to the scratch file. The sound you hear is my head and palm connecting :)

Anyway, here's the patch. Let me know.

Next, guess I'll have to look at BEFORE :)


Title: Re: After (more)
Post by: sciurius on May 14, 2020, 07:19:26 PM
Are you sure this is the right patch? It seems targetted to the midi channel allocation problem, not the After behaviour...


Title: Re: After (more)
Post by: sciurius on May 14, 2020, 07:34:35 PM
While at it, would it be possible to allow zero for the Count argument, so this

    After Count=0 Statement

becomes legal and equivalent to

    Statement


Title: Re: After (more)
Post by: sciurius on May 14, 2020, 07:51:43 PM
Can't wait to try this... This is automatically generated out of a MIDI percussion file.


Title: Re: After (more)
Post by: bvdp on May 14, 2020, 08:53:23 PM
Are you sure this is the right patch? It seems targetted to the midi channel allocation problem, not the After behaviour...

Did you grab/process the right file? the first few lines of the file I posted read:

124,125d123
<     stuff = []
<     elns = []
127c125,129
<     nn = gbl.barNum
---
>     barNum = gbl.barNum
>     # Gather all the AFTER events for this point
>     # in the MMA file into stuff[] for pushback

And this is for the AFTER problem. Nothing to do with channel allocation :)


Title: Re: After (more)
Post by: bvdp on May 14, 2020, 08:58:26 PM
While at it, would it be possible to allow zero for the Count argument, so this

    After Count=0 Statement

becomes legal and equivalent to

    Statement


Yeah ... I suppose so ... but why? If you want to do it right away why not just do it? I'm confused :)


Title: Re: After (more)
Post by: sciurius on May 15, 2020, 06:09:34 AM
Did you grab/process the right file? the first few lines of the file I posted read:

Aaargh! I had a file 'patch.txt' in my download location so this one was downloaded as 'patch(1).txt'... I just overlooked it.

Quote
124,125d123
<     stuff = []
<     elns = []
127c125,129
<     nn = gbl.barNum
---
>     barNum = gbl.barNum
>     # Gather all the AFTER events for this point
>     # in the MMA file into stuff[] for pushback

Works like a charm (but please post context diffs, they are easierand more safe to process).


Title: Re: After (more)
Post by: sciurius on May 15, 2020, 06:12:27 AM
Yeah ... I suppose so ... but why? If you want to do it right away why not just do it? I'm confused :)

It makes automatic generation a tiny bit easier. And I like the symmetry.

But no big deal...



Title: Re: After (more)
Post by: bvdp on May 15, 2020, 05:22:24 PM
I've been thinking about the BEFORE and looking at AFTER code. So, I have a few ideas ... before coding I'd love some feedback.

 1. Right now the way AFTER maintains it's stack is pretty dumb. It has a bunch of AFTER events in a list.

 2. When we see if there is a pending event we scan the list for matching time values,

 3. If any are found, they are processed, etc.

 4. Our list is maintained by parsing it again and deleting any events completed based on the timestamps. Lot's of stack recreates.

Above is simplified ... but, I'd like to suggest a few changes:

 1. Add a event-done flag to reduce the reliance on timestamps. When an event is processed, just toggle the flag.

 2. Don't recreate the list to often. I think that we could have a rebuild-needed flag in the module which signals that the stack is getting pretty dirty. Probably just base this on the number of event-done items and check this from time to time. Maybe if event-done > 50% of stack size?

 3. Add a option flag: place=[before, after] with after being the default (I don't like before/after, better word?).

 4. With place==after we do just like we do now, and ignore <before> events.

 5. Add a check at the start of <must be a chord to process> code in parse.py. This will check for place==before events and process them.

Nothing too dramatic here. But, let me think a bit more and do some coding later.

Thoughts?


Title: Re: After (more)
Post by: sciurius on May 15, 2020, 06:58:13 PM
Ok, some thoughts...

Redesign the code only when it is required for maintentance and flexibility. Performance is not an issue for MMA.

Sorting the list on time values may be helpful. So you only need to inspect the first element and then apply (=pop) all elements with suitable timestamps.

An option flag place=[before/after] looks confusing to me. The command is already called After. Saying After ... place=before... reminds me of the stupid LOGOUT  /YES=NO command (I don't recall if this was on RSTS/E, RSX or VAX/VMS).

The need for Before disappears when execution of After would apply to chord statements only. For example:

Code:
After Count=1 Print Foo
  1  C
Print Bar
  2  D

This would print Bar Foo instead of Foo Bar, since the Print Bar statement does not contribute to the 'number of bars processed'.

Feasable? Or just insane?


Title: Re: After (more)
Post by: bvdp on May 16, 2020, 01:52:48 AM
I've reworked the code a bit ... more to make it more clear to me than anything else :) And, a bit more future/feature proof. A diff is attached (with context!).

As to changing the check point ... could you try this (not tested here). Both are in parse.py:
   1. Out-comment the call to MMA.after.check() at line 113.
   2. Insert a call to MMA.after.check() at line 202 (right before "if action.isdigit....)

Now, it "should" only work before processing a chord line. I HAVE NOT TRIED THIS!

The problem is that it will never trigger after a chord line now :)


Title: Re: After (more)
Post by: bvdp on May 16, 2020, 03:33:10 AM
Just noticed a minor bug ... the REMOVE option isn't working properly. Simple fix for tomorrow :)


Title: Re: After (more)
Post by: sciurius on May 16, 2020, 06:50:36 PM
Patch applied and everything seems fine.

I haven't tried the check point change.

As for the progress from my side: I'm now quite successful at extracting percussion patterns from (arbitrary) MIDI files and turn this into MMA code. This code can use the Rhythm plugin, or be generated to use native MMA Sequences.
Patterns can be inserted before the chords, or all at once at the beginning using After statements.

I've attached examples for your amusement. All examples should sound identical. The patterns are extracted from a MIDI file generated by iRealPro.

See https://github.com/sciurius/mma-plugins/blob/master/rhythm/midi2mma.pl for the conversion program.


Title: Re: After (more)
Post by: bvdp on May 17, 2020, 06:10:06 PM

The need for Before disappears when execution of After would apply to chord statements only. For example:


Moving the call around in parse would accomplish that. BUT, then if there was no chord, the command would not be run.  I think we may still need a BEFORE command (which I really don't like since it complicates a long list of main options and means a bit more code to keep consistent ... ) or, my preference, an option for AFTER. I think that it's more of an issue in coming up with an agreeable option name: AtChord=True (default false) or something? Then, in the main parse loop we'd just add a call when a chord was found. I will try that later and see what happens (it's a rainy day here).


Title: Re: After (more)
Post by: bvdp on May 17, 2020, 06:29:25 PM
I went to the github page to grab the needed plugins. I'm being very dumb, maybe, today ... but  I see no way to download or sync it.


Title: Re: After (more)
Post by: sciurius on May 17, 2020, 06:47:09 PM
I assume you want to download the Rhythm plugin?
https://github.com/sciurius/mma-plugins/blob/master/rhythm/plugin.py
Then click on [Raw]
Then use right-click Save Page As...



Title: Re: After (more)
Post by: sciurius on May 17, 2020, 06:58:04 PM
BTW, the file 'native.mma' does not require the Rhythm plugin.


Title: Re: After (more)
Post by: bvdp on May 17, 2020, 07:06:56 PM
I assume you want to download the Rhythm plugin?
https://github.com/sciurius/mma-plugins/blob/master/rhythm/plugin.py
Then click on [Raw]
Then use right-click Save Page As...



Of course :) Like I said ... dark gloomy day here :)

The samples you posted work just great! (Note: you do need to be running python3 for the plugin to work). Glad to see you are having a lot fun!

I did some quick work testing my idea about "before" and it'll get quite complicated quite fast. I think I'll leave it for now.


Title: Re: After (more)
Post by: sciurius on May 17, 2020, 07:28:57 PM
Note: you do need to be running python3 for the plugin to work

Good catch. Actually it is good it crashed since it revealed another Python2 incompatibility, making the generated MIDI sound different for the python2 and python3 cases...

Fixed on Github.