Kara-Moon Forum

Developers & Technology => Musical MIDI Accompaniment (MMA) => Topic started by: sciurius on January 04, 2019, 07:18:21 AM



Title: A perfectly useless plugin :)
Post by: sciurius on January 04, 2019, 07:18:21 AM
However, it may be handy at times...

Plugin Ignore

// Normal
@Ignore Whatever is here

// Track
Bass @Ignore Whatever is here

// Block
Begin @Ignore
   1 the quick brown fox
   2 jumps over the
     lazy dog
End


Title: Re: A perfectly useless plugin :)
Post by: bvdp on January 04, 2019, 04:16:24 PM
An interesting way to out-comment a section of code. :)

You do know that you can get the same with

   Begin Comment
       Cm ...
       ..
    End


Title: Re: A perfectly useless plugin :)
Post by: sciurius on January 04, 2019, 09:37:55 PM
Sure... I told you it was useless  ;D


Title: Re: A perfectly useless plugin :)
Post by: sciurius on April 21, 2020, 01:15:12 PM
Revamping an old thread...

I have a plugin that turns a string like "|3-3-3-3-|3-1-3-1-|" into "Sequence { 1 0 90; 2 0 90; 3 0 90; 4 0 90 } { 1 0 90; 2 0 30; 3 0 90; 4 0 30 }"

I can now use it as follows:

Code:
SeqSize 4

Begin Drum-Snare
  Tone     SnareDrum1
  @rhythm "|3-3-3-3-|3-1-3-1-|"
End

DefGroove Groovy

Neat.

But I also want to use it like this:

Drum-Snare @rhythm "|3-3-3-3-|3-1-3-1-|"

A quick hack to parse.py can solve this:

Code:
*** parse.py~	2020-03-27 10:04:06.211477746 +0100
--- parse.py 2020-04-21 14:52:30.898980710 +0200
***************
*** 176,181 ****
--- 176,184 ----
              if len(l) < 2:
                  error("Expecting argument after '%s'" % name)
              action = l[1].upper()
+             if len(l) > 1 and name == action:
+                 l = l[1:]
+                 action = l[1].upper()
 
              # Got trackname and action
              if action in trackFuncs:  # perfect, execute

This dedupes a duplicate track name at the start of a command.
What do you think? Are there situations were a track command can be identical to the track name?

Alternatively, I can abuse beginPoints/beginData in the plugin code to detect whether I'm in a Begin/End construct...


Title: Re: A perfectly useless plugin :)
Post by: bvdp on April 21, 2020, 04:55:06 PM
I think that track commands and names will always be different.

Currently (correct me if I'm wrong ... I'm into building things with wood mode right now) you cannot strip out the leading command from a line inside the begin/end block. I recall having some problems with this when using COPY ... my my solution was to have mung in a COPY FROM and COPY TO variant. Is this the same thing you're running into here?

I'm assuming that your plugin returns something like "Sequence ...." and inside a begin/end loop this works since the plugin is evaluated first and its output is appended to the Begin stuff. But, in the 2nd example it'll barf ... unlike a macro. Matter of where the MMA code is being evaluated ... macros at the top (before looking for command), and the opposite for plugins. We look at plugins as new (user added) commands, so this makes sense.

Perhaps, a better solution would be to make the stuff in the line BEFORE the @plugin request available to the plugin? In this case, in a Begin/End there would be a null. In your 2nd call there would be "drum-snare". Would this be as simple as duplicating the result of

 
Code:
l = macros.expand(curline

available at a module level (perhaps as "gbl.parse.currentLine") which could then be read by the plugin? Might give plugins a lot more power this way!

 
Would this solve the other issue we were discussing for multi line defines using a plugin? I don't think so ...


Title: Re: A perfectly useless plugin :)
Post by: sciurius on April 21, 2020, 08:13:44 PM
I think that track commands and names will always be different.

Good.

Quote
Currently ... you cannot strip out the leading command from a line inside the begin/end block.

That seems to be my experience as well. That's why I'm trying different approaches.

Quote
I'm assuming that your plugin returns something like "Sequence ...." and inside a begin/end loop this works since the plugin is evaluated first and its output is appended to the Begin stuff. But, in the 2nd example it'll barf ...

Let's start with the easy case:

    Drum-Snare @rhythm ...

This calls the trackRun() method and passes track = 'Drum-Snare' and line = ... . It pushes back the command line 'Drum-Snare Sequence {...}'.

Ok.

Now the begin/end case:

    Begin Drum-Snare
        @rhythm ...
    End


This is expanded to Drum-Snare @rhythm .... Again, the trackRun() method is called, it pushes back 'Drum-Snare Sequence {...}'. Then MMA processes the pushed-back line. First it adds the begin stuff, so the actual line becomes

    Drum-Snare Drum-Snare Sequence {...}

And oops.

What I do now is that in the plugin I inspect parse.beginData. If it has a value and is equal to the 'track' argument the plugin returns the command line without prepending the 'track' argument and everybody is happy again.

Quote
Perhaps, a better solution would be to make the stuff in the line BEFORE the @plugin request available to the plugin? In this case, in a Begin/End there would be a null. In your 2nd call there would be "drum-snare".

Isn't this what parse.beginData is? Although I must admit that a formally defined parse method to access its content would be nice.

Quote
Might give plugins a lot more power this way!

Jay! Even more power!

Current state of the plugin (with un-modified MMA):

    @rhythm Groove, Seq, Bpm=4, Level=9, RTime=0, RVolume=0, Clear=0, SeqSize=0, Debug=0

This defines a groove according to the seq, with optional values for RTime etc.

For example:

    @rhythm G1, Debug=1, Level=3, Clear=1, RTime=3, RVolume=2, \
      Seq=SnareDrum1 |-3-2| KickDrum1 |3-3-|


This is identical to:

Code:
SeqClear
Begin Drum-SnareDrum1
    Tone SnareDrum1
    RTime 3
    RVolume 2
    Sequence { 2 0 90; 4 0 60 }
End
Begin Drum-KickDrum1
    Tone KickDrum1
    RTime 3
    RVolume 2
    Sequence { 1 0 90; 3 0 90 }
End
DefGroove G1

Note about Seq : Space separated pairs of instrument and sequence data. Instrument can be one of the MMA built-in percussion tones, or a decimal number between 1 and 16. In the latter case Zoom compliant names for tracks and tones are used.

Note about Level: This indicates the maximum volume level in the sequence. Normally volumes are 0..9, corresponding to silent..90. Zoom tabs use volumes 0,1,2,3, so with level=3 these become volumes 0,30,60,90.

As a track plugin:

    Track @rhythm Seq, Bpm=4, Level=9, Debug=0

or

    Begin Track
       @rhythm Seq, Bpm=4, Level=9, Debug=0
    End


For example:

    Drum-Snare @rhythm |9-9-6-9-|9-6-9--9|

is identical to:

    Drum-Snare Sequence { 1 0 90; 2 0 90; 3 0 60; 4 0 90 } { 1 0 90; 2 0 60; 3 0 90; 4.5 0 90 }

As for the sequences: a sequence consists of one or more bars separated by vertical bars.
Each bar is divided into equal divisions, corresponding to the number of characters in the bar.
Each division has either a decimal number indicating that the instrument must sound, or a '-' to do nothing.

Special bars are:
|| repeat the previous bar. If this is the first, use a silent bar.
|-| a silent bar
|*| use the currently defined sequence

Sounds interesting?


Title: Re: A perfectly useless plugin :)
Post by: bvdp on April 21, 2020, 09:12:28 PM
You'll need to change the subject of this ... not useless at all! Exactly what plugins are designed for :)

I think you are right about using parse.beginData[] in your plugin. It'll be up-to-date no matter how many levels of begin/end there are. The current line will not ... silly me ... I'm trying to make things more complicated :)

So, it's all working then with MMA code as is? Love to hear the resulting patterns ... as good in MMA as on the Zoom?


Title: Re: A perfectly useless plugin :)
Post by: sciurius on April 22, 2020, 06:53:36 PM
Love to hear the resulting patterns ... as good in MMA as on the Zoom?

The Zoom is a bit limited. It has nice sounds for 16 instruments but plays like a real computer. It produces audio only. MMA can add some liveliness with RTime/RVolume, and the generated MIDI can be played using many soundfonts. There are very good soundfonts for drumsets.


Title: Re: A perfectly useless plugin :)
Post by: sciurius on April 23, 2020, 02:33:57 PM
BTW, did you know:

If a plugin (or some other module) does:

Code:
from MMA.parse import beginData
...
if beginData:
    # We're in a BEGIN block
    ....

This works. Once. After the first END, beginData keeps its old value and never changes again.

The reason is, apparently, the above import returns the dict item 'beginData' from parse.py. When a BEGIN is processed, the begin stuff is appended to beginData. This is seen in the other modules.

However, after processing END, parse.py does:

Code:
    beginData = beginData[:beginPoints.pop(-1)]

This effectively creates a new dict item, discarding the old one. No problem, except that the other modules keep looking at the old item.

The following is failsafe:

Code:
from MMA import parse
...
if parse.beginData:
    # We're in a BEGIN block
    ....


Title: Re: A perfectly useless plugin :)
Post by: bvdp on April 23, 2020, 04:36:06 PM
I think this is just how python works. Read up on mutable and immutable for gory details.

However, I would not be importing a specific item like this. Just import the module and then you module can treat it "normally". Methinks you are getting too fancy :)


Title: Re: A perfectly useless plugin :)
Post by: sciurius on April 23, 2020, 05:22:06 PM
In this particular case best would be a formal access method in parse.py, e.g. something similar to:

Code:
     def in_block():
        return beginData && len(beginData) > 0