Kara-Moon Forum

Developers & Technology => Musical MIDI Accompaniment (MMA) => Topic started by: louisjb on December 29, 2009, 02:32:44 PM



Title: code for patPlectrum.py: A realistic guitar sound
Post by: louisjb on December 29, 2009, 02:32:44 PM
Hi Bob + others,

Attached is the source code for a new Plectrum patten that I have knocked up. It simulates any type of instrument that you would play with a plectrum eg a guitar or mandolin etc. But I have focused on getting a realistic guitar sound. (Listen to the attached midi so you can judge for your self.)

first would choose the instruments by setting the tuning for each string which also defines the number of strings on the instrument.

     Tunning     e-- a-- d- g- b- e   // A guitar with six strings
     Tunning     g-- d- a- e          // A four string tenor Banjo

You also decide whether you want a capo by adding.

     Capo        5                    // The capo is on the fifth fret.

{NOTE: the Tunning and Capo do not work yet in the attached code}

Then you would define you strumming pattern as follows
Code:
Begin Plectrum-Strumming
    Voice       NylonGuitar
    Octave      4
    Volume      m
    Sequence { \
            1.0   +5     120 120 120 120 120 100; \
            2.0   +5      90  80  80  80  80  80; \
            2.5   -5       -   -  50  50  50  50; \
            3.0   +5      90  80  80  80  80  80; \
            3.5   -5       -   -  50  50  50  50; \
            4.0   +5      90  80  80  80  80  80; \
            4.5   -5       -   -  50  50  50  50; \
    }
End

the first number defines the beat offset, the +5 defines a downward strum with a gap of 5 ticks. -5 denotes an upward strum. The next six numbers are the velocity for each string, put a zero to mute that string and the - means skip that string (i.e. leave it vibrating with the previous note). NOTE: there are no note durations defined, like a real guitar the notes will keep ringing out until ether string is muted (use a velocity value of zero) or when a new note is played on that string.

You can also define finger picking pattens here is an example

Code:
Begin Plectrum-FingerPicking
    Voice       NylonGuitar
    Octave      4
    Volume      m
//  Tunning     e-- a-- d- g- b- e   // A guitar
//  Tunning     g-- d- a- e          // A four string tenor Banjo
//  Capo        0                    // The fret number of the capo
    Sequence { \
            1.0    0       - 100   -   -  90   -; \
            1.5    0       -   -   -  90   -   -; \
            2.0    0       -   -   90  -  90   -; \
            2.5    0       -   -   -  90   -   -; \
            3.0    0       -   -   -   -   -  90; \
            3.5    0       -   -   -   -  90   -; \
            4.0    0       -   -   -   90  -   -; \
            4.5    0       -   -   90  -   -   -; \
    }
End


This is alpha code at the moment as it has not been fully integrated in with MMA (assuming Bob accepts this patch)


Louis


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: folderol on December 29, 2009, 03:52:13 PM
Very impressive work. You clearly have put a lot of thought into this 8)

Wow! Just made a discovery :;

That example MIDI  works very well with something quite different :-



Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on December 29, 2009, 04:25:50 PM
Ahh ... you just wanted to point out to the world that the author of MMA doesn't play a guitar!

Thanks so much :)

This is a nice bit of work. It will be accepted into MMA. But, a few comments/questions first:

 - Is the change to midi.py really needed or could it be done with the existing code. I had a QUICK look and don't see why yet another function is needed.

 - I assume that there are some tests outstanding where MMA does type (ie, CHORD, APREGGIO, etc) testing which need to be done. I don't think this is major since most of the paranoia is "is this a DRUM". The last time I added a track type was with ARIA and I don't recall anything big biting me.

- do you think a new type is needed? Or could CHORD be modified? Probably easiest to do it with a new type, but I wondering????

- I suppose the docs are my problem :)

Seriously, lets do this!

Thanks.


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: louisjb on December 29, 2009, 06:27:40 PM
Hi Bob,

I look at trying to get what I wanted with CHORD first but I hit a problem with strum direction was either UP or DOWN or BOTH but BOTH _always_ alternated. I wanted a "down down up down up down" pattern. Also I could not find out a way make CHORD to play all six notes of a chord like on a guitar (and then just the top four strings of an up strum). CHORD only seemed to want to play only 3 notes of the chord.

I read carefully the thread "Acoustic guitar style"  on this forum. The resulting mma looked very complicated. involving defining two instruments. I wanted something that was very easy to use and behaved exactly like a real guitar for example a Gmaj is g-- b-- d- g- b-  g on a guitar and not g-- b-- d- g- b-  d.

The other difficulty was CHORD needed the note duration for every note involving a lot of very complicated maths. This brings me on the reason for adding the method addNoteOnToTrack to midi.py.
I do not know the duration of the note until the next chord is played (and if that note is not plucked or muted it could be even longer before the duration is known). There where two choices either add addNoteOnToTrack to midi.py or alternatively delay sending the note pair until the duration is known. But this would involve messing with Beatadjust because the notepair is sent late.

There is still some issues with the code that I do not know how to fix. no note offs are send when changing to a different groove or at the end of the piece. patPlectrum endAllVibrations should be called to do this.


I also have a another thought about the chord syntax could be changed to accept barre chords perhaps "^5Gmaj" would play a barre Gmaj chord on the 5th fret, ie the chord would be raised by five semitones. So "^12Gmaj" would be identical to "+Gmaj". Negative fret number could also be allowed ^-5Gmaj which would be impossible on a real guitar. To do this you need to set the value barreChordFretNo to patPlectrum fretboardNote

Louis


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on December 29, 2009, 07:25:46 PM
Hi Bob,

I look at trying to get what I wanted with CHORD first but I hit a problem with strum direction was either UP or DOWN or BOTH but BOTH _always_ alternated. I wanted a "down down up down up down" pattern. Also I could not find out a way make CHORD to play all six notes of a chord like on a guitar (and then just the top four strings of an up strum). CHORD only seemed to want to play only 3 notes of the chord.


Good points.

CHORD plays the notes in the current chord. So, if it's a triad (ie C,
D, Em, etc) it plays 3 notes. A 7th chord will play 4 notes, etc. But,
you are correct saying that this is a problem.

As far as up/down. You could define a longer sequence (ie, 8 bar
sequence). Then set the pattern like:

           Direction Down Down Up Down Down Up Down Up

Quote

I read carefully the thread "Acoustic guitar style"  on this forum. The resulting mma looked very complicated. involving defining two instruments. I wanted something that was very easy to use and behaved exactly like a real guitar for example a Gmaj is g-- b-- d- g- b-  g on a guitar and not g-- b-- d- g- b-  d.

Yes, I agree.

Quote
The other difficulty was CHORD needed the note duration for every note involving a lot of very complicated maths. This brings me on the reason for adding the method addNoteOnToTrack to midi.py.
I do not know the duration of the note until the next chord is played (and if that note is not plucked or muted it could be even longer before the duration is known). There where two choices either add addNoteOnToTrack to midi.py or alternatively delay sending the note pair until the duration is known. But this would involve messing with Beatadjust because the notepair is sent late.

Just looking quickly at the code ... am I right that the notes are
turned off the next time though the playbar code (ie, the next
beat/chord)? I avoided this with addPairToTrack() ... you are right
that prediction is hard and just waiting around for the next strum is
much easier.



Quote
There is still some issues with the code that I do not know how to
fix. no note offs are send when changing to a different groove or at
the end of the piece. patPlectrum endAllVibrations should be called to
do this.

I'll have to think a bit about the eof of file and groove change
problem. I'd like to think they can be handled at the same time, but I
don't think so. For now, I'm thinking that some special flags need to
be set:

   - for the eof of file. You could do something in main.py, similar
   to the code at line 296. Or, change doMidiClear() to check the new
   track-type. That might be easiest:

            if self.type == ... turn off notes

   - for the groove change? Not sure without tracing a bunch of
   code. But, similar I'd think?

Quote
I also have a another thought about the chord syntax could be changed to accept barre chords perhaps "^5Gmaj" would play a barre Gmaj chord on the 5th fret, ie the chord would be raised by five semitones. So "^12Gmaj" would be identical to "+Gmaj". Negative fret number could also be allowed ^-5Gmaj which would be impossible on a real guitar. To do this you need to set the value barreChordFretNo to patPlectrum fretboardNote

Louis

What happens to "other" tracks? It was my thought at one time to have
the ability to set differnt chords for different tracks. Still not
sure if this would be a silly thing, and needlessly complicated.

But, here's another thought: if you expand PLECTRUM a little bit could
we combine the cappo and barre? So, using a silly example, we could
have a data line:

        C / Gm

and above that a special plectrum line:

       PLECTRUM-V1  barre 3

Now, this would apply to all the chords, forever.

So, to effect just the 3rd/4th beat:

     PLECTRUM-V1  barre 0,0,3,4

or, you could even set this for patterns:

    PLECTRUM-V1  barre 0  0  4  5

And, then when you want "normal"

     PLECTRUM-V1 barre 0

Note the difference between 0,0,4,0 and 0 0 4 0 !

Also, I'm not excited about the terms PLECTRUM or BARRE. Is there
something better???? If not, we'll stick with this.

I'll let you work a bit more at your end. Then we'll try to do a merge
into my main code. Then a beta release.

Best,


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: louisjb on December 30, 2009, 11:24:59 PM
Hi Bob and others,

Just a quick note as I was out today and I will be away all of tomorrow. I'll reply in detail in the new year.

By the way I think permanent barre chord effects should be referred to as CAPO not BARRE.

folderol I really enjoyed the HandBells.ogg.

Louis


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: louisjb on January 01, 2010, 03:47:43 PM
Ok Bob,

I have done a bit more work on the PLECTRUM pattern and integrated it with code from mma-bin-1.5b. To try it out unzip the attached MMA-1.5c-dev1.zip file over the files in the MMA directory.

Quote
Also, I'm not excited about the terms PLECTRUM or BARRE. Is there
something better?

BARRE is the correct term for a barre chord see http://en.wikipedia.org/wiki/Barre_chord

But I was not suggesting adding BARRE as a key word only TUNNING and CAPO  but lets deal with my ideas for barre chords later.

Regarding the name PLECTRUM I considered the name GUITAR but that was no good as I also wanted it work with deferent types of string instruments like Lute, Mandolin, Banjo. I considered STRINGS but that was associated with violins. Other ideas were STRUM (you already use that) STRUMMER, PICKING, PLUCKING. Can you think of a better name? I now quite like the name PLECTRUM as it associated with the Guitar and Banjo etc. And you can talk about PLECTRUM patterns :).

The most pressing problem is to call patPlectrum.closeGroove whenever that groove stops playing which will add note offs for all the outstanding notes. Adding closeGroove to the base class PC would be a good idea. I have a quick look at your code to try and implement this call but I really need your help with this. Basically we need to store the currently playing groove in a variable somewhere and whenever that currently playing groove is about to change to a new groove then to call pat.closeGroove() for the current groove just before it is replaced by the new groove.

Finally I would like to add the new keywords TUNING and CAPO which would call patPlectrum.setTuning() and patPlectrum.setCapo(). We really need noteNameToMidiValue("e--") method somewhere, that can be called from SetTunning. Again I really need your help with this one.

Code:
Begin Plectrum-FingerPicking
    Voice       NylonGuitar
    Octave      4
    Volume      m
    Tunning     e-- a-- d- g- b- e   // A guitar
   //Tunning     g-- d- a- e          // A four string tenor Banjo
    Capo        5                    // The fret number of the capo

And very finally I have changed mma.py as follows.

Code:
if platform == 'Windows':
    #dirlist = ( "c:/mma", "c:/program files/mma", "." )
    midiPlayer = ['']   # must be a list!
else:
    #dirlist = ( "/usr/local/share/mma", "/usr/share/mma", ".")
    midiPlayer = ["aplaymidi"] # Must be a list!

print 'sys.path[0] =', sys.path[0]
dirlist = [sys.path[0]]  # This the directory that contains the mma.py file.

This gets round the problem with fixed paths. The files in /usr/share/mma/*.py where being called instead of the ones I changed in ./mma/*.py

Happy new year.

Louis


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 01, 2010, 08:40:22 PM
Good go! And I hope the New Year is a good one for you!

I'll do the merge this afternoon and give the code a run.

Comments will probably be later today ... or tomorrow. It's snowing like &*&$&$ here and I'll probably have to do some clearing later (my driveway is about 700m).

Just some quicky points:

  Naming: Plectrum is fine. But, STRUM would work (there wouldn't be a conflict with the option). I thought of GUITAR and STRINGS as well. Hmmm, the only other one I came up with was FRETTED. Really up to you ... and for users it won't matter much since it's all buried in style files.

 Options: Not a big deal to add. The setter should be in your file. Just that the variable storage is easier in pat.py since there isn't a "check other tracks for saving/restoring". There should have been one, I think, but it's not a big deal ... so we'll leave it alone and put added variables in pat.py.

 Let me look at the save when a new groove is loaded issue. The current groove name is stored in grooves.py.

Later ... need to dl your code before my web connect dies due to the snow (I'm on a wireless system ... my only choice other than satellite!).





Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 01, 2010, 09:30:46 PM
Here's code for a notename -> midi value. I'd think it belongs in midiM.py. You might want to fiddle with the "middle" value.

Oh, and midiM.py will need to include the error code (from MMA.common import *) ... or return -1 on error. Probably better to just return -1 and let your code worry about it.

Code:
def error(s):
    import sys
    print s
    sys.exit(0)

def noteNameToMidiValue(s):
    """ Convert a name ('e', 'g#') to a MIDI value. """

    tb = { 'c': 0,  'c#': 1,  'd&': 1, 'd': 2, 'd#': 3,  'e&': 3,
           'e': 4,  'f&': 4,  'e#': 5, 'f': 5, 'f#': 6,  'g&': 6,
           'g': 7,  'g#': 8,  'a&': 8, 'a': 9, 'a#': 10, 'b&': 10,
           'b': 11, 'b&': 11, 'c&': 11,  'b#': 0 }

    # strip and count trailing '+' and '-'

    if '-' in s and '+' in s:
        error("Can't have both + and - in note name.")

    adjust = 0
    while s.endswith('-'):
        adjust -= 12
        s=s[:-1]
    while s.endswith('+'):
        adjust += 12
        s=s[:-1]

    try:
        value = tb[s] + 60  # puts into middle octave 60==5*12
    except:
        error ( "No such name: %s" % s)

    return value + adjust

for a in ('a', 'b', 'b#', 'g', 'g-', 'g++', 'q'):
    print noteNameToMidiValue(a)



Is this what you wanted?

There is another converter in patSolo.py, but I think it's easier to do it with a separate function.

Let me know.


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 01, 2010, 10:25:34 PM
To turn off vibrating strings, add the following to the start of doMidiClear() in pat.py (line 966):

[
Code:
   def doMidiClear(self):
        """ Reset MIDI settings. """

        if self.vtype == 'PLECTRUM':
            self.closeGroove(gbl.tickOffset)
      ....

This is called whenever a new groove is loaded and at the end of the file. I don't think it'll be needed anywhere else????

The call crashes for me, but that is the patPlectrum code :)

A few things remain: mainly decisions on:

   - what items in your style to save/restore with a groove,
   - do you want to have different setting in each bar of a sequence?

I listened to a few samples generated with the new code and I think it sounds very, very good! This will definitely make MMA much better!


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: louisjb on January 02, 2010, 12:36:06 AM
change the offending line to

        for stringNo in range(len(self._vibrating)):

But now it fails with

Code:
  
File "/home/louis/active/src/mma-bin-1.5c-dev1/MMA/midi.py", line 533, in addNoteOnToTrack
    onEvent  = chr(0x90 | self.channel) + chr(note) + chr(v)
ValueError: chr() arg not in range(256)

because  self.channel = -1

Quote
I listened to a few samples generated with the new code and I think it sounds very, very good! This will definitely make MMA much better!
Yes I am very please my self. Basically it is very difficult to get realistic guitar sound on a midi tracks so I think MMA may get a lot more attention from people who want a realistic guitar sound on midi tracks.

Quote
It's snowing like &*&$&$ here and I'll probably have to do some clearing later (my driveway is about 700m).
I am very worried that you will get snowed in because you are too busy on the computor.

More post tomorrow because it getting late here now.


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 02, 2010, 12:48:52 AM
To the code line at doMidiClear() add a channel test:

      if self.vtype == 'PLECTRUM' and self.channel != -1:

But, it still crashes :) But, later :)

Did the first snow-clearing this afternoon. Got a snowblower, so it's not too big a deal. Still, hard work and quite a lot of time away from the computer :)





Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 02, 2010, 01:14:29 AM
Just a quick thought (over dinner I was thinking about this ... don't tell my wife!).

If you want to change instruments, you need to set a new tuning string with something like:

   Plectrum-Guitar Tuning e f g ...

Why not add to this with some predefined instruments. I'd suggest that things like:

      Plectrum-Guitar Tuning Banjo
      Plectrum-Guitar Tuning 12Guitar

etc. be permitted. Then in the setTuning code you'd just need something like:

      if len(..) == 1:
          if tuningstring.upper() == 'BANJO' ...
      etc.
      else:
          assume each string is specified.

Probably easier to all this with some tables. But, it's your code :)

I'd just like to be able to avoid having users balk at using alternate instrument configs.

Best ... getting late here as well.


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 02, 2010, 05:12:49 PM
Instead of changing the doMidiClear() in pat.py, we should make life simpler by overriding it. So, in patPlectrum.py add the new function:


    def doMidiClear(self):
        """ Reset MIDI settings, special hook for stopping strings. """
 
        self.closeGroove(gbl.tickOffset)
        PC.doMidiClear(self)


Still doesn't work. But, it's prettier :)


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: louisjb on January 02, 2010, 07:19:31 PM
Quote
Did the first snow-clearing this afternoon. Got a snowblower, so it's not too big a deal.

I forgot you know how to deal with snow over there. Over here you get half and inch of snow and it is on the front pages of all the papers and all the roads are gridlocked.

Attached is the latest version plectrum. In debug mode it now prints the chord shapes. I have renamed closeGroove to grooveFinish. I have shoved noteNameToMidiPitch at the end of the file but it probably belongs in util or somewhere.

The following three lines really need to be comment out but I left them in for debugging. It crashes when they are commented out because gbl.mtrks[self.channel].channel  is -1.

Code:
        print "grooveFinish offset=", offset, "self.channel=", self.channel, "gbl.mtrks[self.channel].channel=", gbl.mtrks[self.channel].channel
        if gbl.mtrks[self.channel].channel == -1:
            return

yes overiding is much better,

I really would like to get the grooveFinish code working as it sounds odd without it. I think you are passing the wrong offset to me eg 10752. It should should not be be bigger than 192*4 and offset should be reset to zero every bar.

Also I have got setTuning and capoFretNo working now. is it possible for you to connect them up to Tuning  and Capo in the plectrum definition in the mma file as I don't really know the best way to do this. Feel free to change patPlectrum.py

Louis


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 02, 2010, 10:05:07 PM
Ahh, I see the offset problem. This should fix that:

    def doMidiClear(self):
        """ Reset MIDI settings, special hook for stopping strings. """
 
        if self.channel != -1:
            self.grooveFinish(gbl.QperBar * gbl.BperQ)
        PC.doMidiClear(self)

This passes the current end of the bar offset. Then the addNote..() figures the current offset from that.

Seems to be working fine here, now.

Leaving the noteName..() in the plectrum file is fine. Nothing else is calling it.

Look forward to the next installment!


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: louisjb on January 02, 2010, 11:39:31 PM
Attached is the latest patPlectrum

No quite right Bob you need to do the following:

  def doMidiClear(self):
        """ Reset MIDI settings, special hook for stopping strings. """
 
        if gbl.mtrks[self.channel].channel != -1:
            self.grooveFinish(0)
        PC.doMidiClear(self)


self.channel has a value of 0 zero, but gbl.mtrks[self.channel].channel has a value of -1 very confusing.


Also passing a offset of zero to self.grooveFinish(0) put the notes off in the right place.


I have also found a BUG with MMA in that it puts the note off (a note on with v=0) on the wrong channel. Please see MMA_CHANNEL_BUG.zip attached.

Louis



Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 03, 2010, 02:05:54 AM
Oh, let me clear up 2 things.

the test should be

    def doMidiClear(self):
        """ Reset MIDI settings, special hook for stopping strings. """
 
        if self.channel:
            self.grooveFinish(0)
        PC.doMidiClear(self)

MIDI channels are assigned to self.channel as needed. When a Mtrk() is created the channel number is passed and that is saved, -1, as the midi channel. self.channel is 1-16, the mtrk value is 0-15.

All my fault in suggesting to use -1 in the first place.

The offset I gave was for the end of the current bar. You're right that it should be 0, which is the start of the current bar. Confusion here is that mma keeps track of the current bar TOTAL offset in gbl.tickOffset (which I used originally). But, the routine you're calling is based on the current bar and an offset into that.

Anyway, this should work now.

I will have a look at the other bug. Probably in the morning. Will let you know what's going on. I'm sure there is more that one bug still in MMA!!!!

Best.


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 03, 2010, 06:58:56 PM
Okay, I'm flummoxed. I've spent the better part of the morning trying to trace down the problem with all notes not being turned off. I've finally rewritten

Code:
    # endAllVibrations (ie output all outstanding note off)
    def grooveFinish(self, offset):
        print "grooveFinish offset=", getOffset(offset), "Name", self.name, "self.channel=", self.channel, "OnNotes",
        for s in self._vibrating:
            print s.note,
        print

        if not self.channel:
            return

        for stringNo in range(len(self._vibrating)):
            self.endVibration(stringNo, offset)

I don't think the problem is with MMA. Rather it appears that your code in not updating the table listing the ON notes properly.

But, I've looked at that as well. Right now I just don't see it.

Maybe you can have a look as well. But, in the "Fail" file the single note is not being turned off. Here's a cutting from the midi file via mf2t:

MTrk
0 Meta TrkName "Plectrum-Strumminga"
768 PrCh ch=15 p=25
768 On ch=15 n=43 v=90
768 Meta TrkEnd
TrkEnd

Which confirms the debugging code I added to grooveFinish().

Your turn :)


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 03, 2010, 08:42:34 PM
I think I found it. It's the problem with python and arrays. Kicks me all the time!

Here's the first few lines of your code which now work:

Code:

class Plectrum(PC):
    """ Pattern class for a Raw MIDI track. """


    vtype = 'PLECTRUM'
    # We have vibrating strings (a string in python refers to text not a guitar string)
    #_vibrating = []
    #_tuning = []
    #capoFretNo = 0  # The number that the capo is on (0 for open strings)

    def __init__(self, nm):
        PC.__init__(self, nm)
       
        self._vibrating = []
        self._tuning = []
        self.capoFretNo = 0  # The number that the capo is on (0 for open strings)
        self.setTuning("e-- a-- d- g- b- e")

Also, I'd like to change this:

Code:
   def setTuning (self, stringPitchNames):
        """ for standard guitar tuning use setTuning("e-- a-- d- g- b- e") """

        #clear the old tuning
        #del self._tuning[:]
        #del self._vibrating[:]
        self.tuning=[]
        self._vibration=[]

        for pitchName in stringPitchNames.split():
            self.addString(pitchName)


Testing appears to show that this now works :) Let me know.


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: louisjb on January 03, 2010, 10:55:01 PM
I have changed parse.py and pat.py (and patPlectrum.pat) to get TUNING and CAPO working. just in case you are working on the same thing. but the code is not ready yet.

It might be easier to put your code on subversion. which would make all this much easier. I use sourceForge to host my code.

Regarding the MIDI CHANNEL BUG. the missing Note off is not really missing but rather it has been sent out on the wrong channel. There is a extra note off on channel chan=16. Have you done a comparison between PlectrumTestFail.mma and PlectrumTestWork.mma.

Louis




Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 04, 2010, 12:06:34 AM

Let me know what you're doing on the pat.py and parse.py ... I assume that you're just adding some hooks/functions.

To store you'll also need to modify the ugly tables in pat.py.

Yes, I should use subversion, etc. Let's get this sorted out for now (the hard way) and then another step for me ... I've never used this.

Also, did you apply my patches from earlier. The reason your code wasn't working is that each instance of the guitar track was using the same array for the note storage. Creating the array in __init__() creates a different one for each track. Let me know if this doesn't make sense, or if I'm wrong. BTW, you'll get different results with different computers, etc. depending on how the arrays are/aren't allocated.


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 05, 2010, 06:34:55 PM
I'm assuming the Louis is busy making his improvements. But, I had a bit of time today to play and create some real midi files using the plectrum code. I did have to make one change, to the getPgroup() function I had to add:
 
      a.duration = 1

to satisfy some other code. Not sure why it's working with the original test code ... but that is another matter.

Also, I re-tuned the guitar up an octave.

Remember, this is not ready for primetime yet. But, it is very close.

But, I managed to modify my bolero.mma code to use a plectrum track instead of the chord I was using. Changes in sound are subtle, but nice. I've attached 2 midi files. Sound difference should be obvious. Remember, all I changed was one track definition in the library.

Please give a listen and then send some nice comments to Louis!



Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: louisjb on January 05, 2010, 10:43:05 PM
Hi Bob,

Thanks for your kind words in your previous post

Ok here is my latest offering.

I have included all the files I have changed since I started (so others can just drop the .py files over the files in MMA from version mma-bin-1.5b) bob this time I have only changed pat.py and parse.py/

Tuning and capo commands now work unlike a real guitar you can have a negative capo value which makes the guitar neck longer! I have improved the chord shape algorithm and improved the chord shape debug print.

i have also allowed some short cut on the Plectrum patterns (I like that name now) so

n-m:v  where n is the start string number and m is the end string number and v is the volume so

"1.0 0 2:50" is the same as ""1.0 0 - - - - 50 -"

"1.0 -5 2-4:80" is the same as ""1.0 -5 - - 80 80 80 -"

"1.0 +5 :90" is the same as ""1.0 +5 90 90 90 90 90 90"

This makes putting in guitar finger picking patterns really easy.

Bob I have made the changes in your previous post (sorry I did not read it properly the first time round). It all seems to work pretty well now. There are just a few minor issues that need to be sorted out but that wait a few days now.

I think we should start a new thread and start collecting up different strumming and finger picking patterns that sound as much like a real guitar as possible. I would be interested to here what others can do.

Oops, I nearly forgot the attachment!

Louis 


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 06, 2010, 01:51:07 AM
Okay. Getting late here too, so I'll have to look at the new code tomorrow.

A couple of points came up when I was doing the bolero thing this morning:

1. I think that you should honor the Octave setting. Set the neck patterns to be zero based and then add the current octave setting to them. Easy that way to do silly things like a 5 string, strumming bass guitar. Seriously, it does make it easier to make 1+ or 1- changes.

2. Do you need a strum variable in the sequence definition? There already is a strum setting. I suppose that the way you have it set you could have a different value for each pattern in a sequence, but I wonder it that'll be an issue?

3. The .duration setting is needed by the documentation routines, esp. for drawing the little graphs. We can just stick in a dummy value, but I'm not sure what to do for the graphs? Would be nice to have it calculated to "fill out the bar" somehow. Not sure how that would work.

4. And could we add a defualt to setting code? I'm thinking that if you have (your example from above):

 1.0 +5 90 90 90 90 90 90

the parser should be able to handle 

  1.0 +5 90

and fill the remaining strings with 90.

Same for a "-".

Okay?

I'll let you know tomorrow on the current changes.

Thanks for the time on this. I'll have to start some docs later this week to keep pace with you!

Later,


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 06, 2010, 07:02:22 PM
A few minor changes:

in pat.py I've changed 2 functions as follows. Just to generate proper error messages:

Code:
    def setPlectrumTuning(self, ln):
        """ This method does nothing in the base class but it is
            overriden in the derived class"""
       
        error("TUNING not permitted in %s tracks." % self.vtype)


    def setPlectrumCapo(self, ln):
        """ This method does nothing in the base class but it is
            overriden in the derived class"""

        error("CAPO not permitted in %s tracks." % self.vtype)


in patPlectrum.py

Code:
   def getPgroup(self, ev):
        """ Get group for rawmid pattern.

            Fields - start, length, note, volume

        """

        if len(ev) < 3:  # we need offset, strum and at least one pattern
            error("There must be n groups of 3 or more in a pattern definition, "
                  "not <%s>" % ' '.join(ev) )
       
        a = struct()
        a.duration = 0   # this is a dummy value to keep docs happy
        a.offset   = self.setBarOffset(ev[0])
        a.strum = stoi(ev[1], "Strum in Plectrum definition not int")

        self.decodePlectrumPatterns(a, ev[2:] )

        return a

This is a error catcher for args, and sets the duration.

I think we can avoid some calls in parse.py for the new functions. I'll look at that later.

Also, we need some storage for the neck setups (cappo and tuning) in pat.py. And we need to decide if the cappo and tuning settings are global (ie forever, regardless of groove), per bar or per sequence. I'd suggest that we treat them just like anything else in mma and have the option to have them saved in grooves. But, I really don't see a need to have different settings for each bar in a sequence (plus, that does complicate the parser a bit) ... mind you, we can do that for just about every other setting so why not for this as well?? Comments?

I'm thinking that a merge with my main source tree is getting very close. I'd like to get the minor things figured out, then I can write some docs and do the merge. At that point I'll put up a new devel release and wait for comments?

Okay?


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: louisjb on January 06, 2010, 10:53:16 PM
Ok bob,

Here is may latest offering. I think i have completed everything that I want to do with it now. Including improving the chord shape algorithm. It now tries to put the 7th or the top note any chord with four notes up nearest to the top string. previously it just search for the notes nearest the guitar nut . I have got the + - prefix working on the chords. see:

Code:

            chordBarreFretNo = 0
            if ct.name.startswith('+'):
                chordBarreFretNo = 12
            if ct.name.startswith('-'):
                chordBarreFretNo = -12
produces this
Code:
     Plectrum  +C  chord  [12, 16, 19]
    64 | - - - - - - - - - - - * - - - - - - - 1  76
    59 | - - - - - - - - - - - : * - - - - - - 0  72
    55 | - - - - - - - - - - - * - - - - - - - 2  67
    50 | - - - - - - - - - - - : - * - - - - - 1  64
    45 | - - - - - - - - - - - : - - * - - - - 0  60
    40 | - - - - - - - - - - - * - - - - - - - 1  52


       Plectrum  -D  chord  [-10, -6, -3]
    64 : - * - - - - - - - - - | - - - - - - - - - - - - - - - - - - - 1  54
    59 : - - * - - - - - - - - | - - - - - - - - - - - - - - - - - - - 0  50
    55 : - * - - - - - - - - - | - - - - - - - - - - - - - - - - - - - 2  45
    50 * - - - - - - - - - - - | - - - - - - - - - - - - - - - - - - - 0  38
    45 * - - - - - - - - - - - | - - - - - - - - - - - - - - - - - - - 2  33
    40 : - * - - - - - - - - - | - - - - - - - - - - - - - - - - - - - 1  30



I would still like to have the ability to put in barred guitar codes as my code already handles it.  Guitarist often move up the fret board when playing.

Maybe we could use something like "Gmaj:barre5" which is preferable to "^5Gmaj" that I suggested earlier. All your other patterns would ignore the everything after and including the trailing ':'. Please note there is no transposing here it is still a Gmaj chord (but played using a different chord shape further up the neck.)

So my code is finished now the only thing I can thing that I can think of that is outstanding is handling very wide strums values like 150 and so the notes overlap a the next and previous strum. I have tested and it seems to cause no major problems. so it is Ok to merge now. Please make all your suggested changes first to PLECTRUM as I have not done so.

Answering you points Capo and tuning should be stored like everything else in mma. I think I implemented this when I changed pat.py and parse.py.

bob>1. I think that you should honor the Octave setting.

I think this will only add confusion as we can change the pitch with tuning and also the capo settings (
octave can be achieved with a +12 or a -12 capo (yes negative capos  are allowed) using the OCTIVE variable should in my opinion print a warning when attempted.

bob> 2. Do you need a strum variable.

No I think the strum variable should be disallowed with plectrum to avoid confusion  (print a warning when attempted).

bob> the parser should be able to handle "1.0 +5 90"

Done

Please be a aware that doing a "1.0 +5 90" causes half the notes to be played BEFORE the beat. (I think you send a program change on the beat when starting up a new groove. so the first few notes may be played as a piano sound.)

Finally I think we could drop the trailing \ at the end of each line. I don't think it is needed when the { and } mark the beginning and end of the of the sequence.

By the way I really like that magic-is-the-moonlight-plectrum.mid, it really fills out the whole sound now. It is great hear my code working for real. (By the way perhaps it could do with some "up strums" to sound more natural - unless that was an effect you deliberately intended)

Louis


 


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on January 08, 2010, 10:17:00 PM
Louis and I have taken this discussion private ... a tad quicker for both of us.

Here's the schedule on this:

 1. We're doing some code cleanup and just plain old "making sure it works". No new features.
 2. I'll write some docs which I'm sure will take us back to (1)
 3. Once the docs and code have settled down, I'll modify some of the existing grooves to
     take advantage of the plectrum tracks. This will be limited to things using guitars and banjos.
     I have to be careful since we don't want to change existing grooves, just enhance them. And,
     I hope we don't go back to (1) too many times :)
 4. We'll do a devel release. I'm hoping to do this by the end of January or very early in Feb.
 
After that point we've got a few enhancements planned. Major one being able to specify capo settings on a per chord basis.

And, then I hope we can do a 1.6 release in the early spring.

Comments and suggestions are welcome!


Title: Re: code for patPlectrum.py: A realistic guitar sound
Post by: bvdp on February 15, 2010, 12:26:51 AM
Just to be neat and tidy, this thread has been very useful! A new version of mma is the result! Don't forget to check the new examples page at

   http://mellowood.ca/mma/examples.html