Page 1 of 1

Chord Pads v1 - can you all critique my coding?

Posted: 08 Jul 2014 22:46
by duck_waddle
Hey! I just uploaded my first Lemur project - it's called Chord Pads (I know how awful a name it is...). Here's the link:

http://liine.net/en/community/user-library/view/499/

It was inspired by the small pads that Cubasis employs, for playing chords by tapping a single finger. As a keyboard player in a funk/hip-hop group, I thought this would be a great way to get that sampled, "MPC" sound in real time. I imagine this has been done before (I couldn't find an example, however) - but my project is unique in that I used classic jazz voicings. I'd like to eventually add another page with more chords, and some way to control synth parameters. I've considered more faders, but I think some kind of y or z value on the pads that controls volume or cutoff would be cool. I'm open to suggestions, and criticisms..

Which brings me to my next point - PLEASE tell me how bad of a job I did at coding and designing this! I have found some really good info on here, and I think I did an okay job. But I'm sure there are tons of tricks and techniques that I should have employed here. Anything helps ;)

If you dig this, let me know! I can't wait to use this live this weekend. Thanks everyone!

Re: Chord Pads v1 - can you all critique my coding?

Posted: 09 Jul 2014 17:17
by Softcore
Much more complicated than my first ever so I'd say "well done"!!!!!

In future works, as a suggestion, try to check if anything you want to achieve can be achieved with less objects and a more script-orientated approach....

i.e could this multitude of Pads be replaced by ONE Pad object (with rows and columns) and have ONE script deal with all the triggers and chords? (instead of one script in each pad object)....

Im not saying it could (I would need to study deeper in your template)....but if it might....its best to go down that route.

;)

Re: Chord Pads v1 - can you all critique my coding?

Posted: 10 Jul 2014 05:11
by duck_waddle
Softcore wrote:Much more complicated than my first ever so I'd say "well done"!!!!!

In future works, as a suggestion, try to check if anything you want to achieve can be achieved with less objects and a more script-orientated approach....

i.e could this multitude of Pads be replaced by ONE Pad object (with rows and columns) and have ONE script deal with all the triggers and chords? (instead of one script in each pad object)....

Im not saying it could (I would need to study deeper in your template)....but if it might....its best to go down that route.

;)
Thank you so much! I did spend a good amount of time on it, and I'm glad it came out halfway decent.

That is a great idea! I was really pining after some kind of functionality similar to 'functions' like in other OO programming languages. Using one pad object sounds like it could have provided that! Would have made the actual arranging of the pads less of a headache, too. Thanks again!

Re: Chord Pads v1 - can you all critique my coding?

Posted: 10 Jul 2014 05:14
by duck_waddle
Softcore wrote:Much more complicated than my first ever so I'd say "well done"!!!!!

In future works, as a suggestion, try to check if anything you want to achieve can be achieved with less objects and a more script-orientated approach....

i.e could this multitude of Pads be replaced by ONE Pad object (with rows and columns) and have ONE script deal with all the triggers and chords? (instead of one script in each pad object)....

Im not saying it could (I would need to study deeper in your template)....but if it might....its best to go down that route.

;)
Sorry to double post, but I just noticed that you are the one behind Advanced Keys?! You're awesome haha! That's a great program, I love it! I actually want to incorporate some of those techniques into a new version of my chord pad program.

Re: Chord Pads v1 - can you all critique my coding?

Posted: 10 Jul 2014 09:59
by oldgearguy
To continue along what Softcore said -- if you find yourself cutting and pasting the exact same (or almost exactly the same) set of lines in a script, you should consider making it a 'subroutine' (really in Lemur they are all just scripts).

For example, your chord() and chordOFF() are exactly the same with the exception of 0 or 127. you could create a new script called sendChord(velocity) that had the same guts except that in place of the 0/127 you use the velocity parameter: noteout(lemurPort,a,velocity,midiChannel);

Make that script execute manually and then your chord() and chordOFF() scripts are reduced to simply:

<SCRIPT name="chord()" script="
sendChord(127);
" trigger_script="x" trigger_type="0" trigger="2" clock="0" clock_div="4" osc_message="/maj7/chord" midi_message="0x90,0x90,0,0" midi_target="-1" flag="1"/>

<SCRIPT name="chordOFF()" script="
sendChord(0);
" trigger_script="x" trigger_type="0" trigger="3" clock="0" clock_div="4" osc_message="/maj7/chordOFF" midi_message="0x90,0x90,0,0" midi_target="-1" flag="1"/>

The beauty of doing something like this is that later if you wanted to send a chord with a different velocity, you can call sendChord() with the new number without having to change it in multiple places.

That's a simple optimization, but if you start thinking like that, you'll make it easier to do updates/fixes since you don't have to hunt through all your scripts looking for the same change. Plus if you create a core set of scripts, then adding new functionality becomes much easier since you already did the hard work creating the core pieces.

Once you get comfortable with that, you could pass in values that make up the triad for that particular core type or create an array of triads and then reference which set to use at any given time.

Re: Chord Pads v1 - can you all critique my coding?

Posted: 10 Jul 2014 11:25
by oldgearguy
While I'm thinking about it, there's a couple additional points.

If you know you only want velocities of 0 or 127, you can replace all 3 scripts with 1.

Keep the sendChord() from above, remove the velocity parameter, make the script execute 'On Expression', 'x', 'any' and change the noteout to: noteout(lemurPort,a,(x * 127),midiChannel); Since x goes from 0 to 1 on press, 1 * 127 will send full velocity. When you release the button, x goes from 1 to 0, the script is called again and 0 * 127 = 0, so you get your note off.

There's lots of ways to solve the same problem. It depends on what you want to achieve. The first version is more flexible, the second much more concise.

Finally - once you get it working, the next step is to make it bulletproof. :(

Try this with your current version -- pick a root, press and hold a chord button. While holding the chord button, slide to a different root. Now release the button. If you're just writing the app for yourself, you know not to do things like that. If you're releasing it to the public, you *may* want to address it - either in code or documentation; of course you're not obligated to, but it helps the overall experience to human-proof the application as much as possible. I probably spend 1/3 of the coding time writing the core functionality and 2/3 making it bulletproof (adding error checking, preventing users from accidentally changing/losing data, etc) and flexible (removing hardcoded choices where it makes sense).

(couple hints - either save off the Root.x value on press and use the saved value for release or use something like setexpression(Root, 'attraction', 0); to lock out the Root fader on press and then use setexpression(Root, 'attraction', 1); on release to allow movement).

Re: Chord Pads v1 - can you all critique my coding?

Posted: 10 Jul 2014 14:50
by duck_waddle
oldgearguy wrote:To continue along what Softcore said -- if you find yourself cutting and pasting the exact same (or almost exactly the same) set of lines in a script, you should consider making it a 'subroutine' (really in Lemur they are all just scripts).

For example, your chord() and chordOFF() are exactly the same with the exception of 0 or 127. you could create a new script called sendChord(velocity) that had the same guts except that in place of the 0/127 you use the velocity parameter: noteout(lemurPort,a,velocity,midiChannel);

Make that script execute manually and then your chord() and chordOFF() scripts are reduced to simply:

<SCRIPT name="chord()" script="
sendChord(127);
" trigger_script="x" trigger_type="0" trigger="2" clock="0" clock_div="4" osc_message="/maj7/chord" midi_message="0x90,0x90,0,0" midi_target="-1" flag="1"/>

<SCRIPT name="chordOFF()" script="
sendChord(0);
" trigger_script="x" trigger_type="0" trigger="3" clock="0" clock_div="4" osc_message="/maj7/chordOFF" midi_message="0x90,0x90,0,0" midi_target="-1" flag="1"/>

The beauty of doing something like this is that later if you wanted to send a chord with a different velocity, you can call sendChord() with the new number without having to change it in multiple places.

That's a simple optimization, but if you start thinking like that, you'll make it easier to do updates/fixes since you don't have to hunt through all your scripts looking for the same change. Plus if you create a core set of scripts, then adding new functionality becomes much easier since you already did the hard work creating the core pieces.

Once you get comfortable with that, you could pass in values that make up the triad for that particular core type or create an array of triads and then reference which set to use at any given time.
Hey thank you so much for your time! That kind of constructive advice is exactly what I need - I think I'm almost comfortable enough with some syntax and programming concepts and techniques that I really just need some thoughtful feedback. Your second post, about being bulletproof was awesome. I will do my best to think like that from now on.

The first post, though, left me with a few questions. I knew my code repeated itself way too much, but I wasn't quite sure how to address that. The script you suggest, sendChord(), would that live globally? Is that where I should be putting my "functions?" I've seen some people mention that they write scripts in containers that apply to the objects inside the container. Is that technique interchangeable with global scripts (at least in this example)?

Also, that language you're writing in that resembles HTML - where and when can I use that? Can you point me in the direction to read more about those tags and syntax you used? Again, I can't thank you enough!

Re: Chord Pads v1 - can you all critique my coding?

Posted: 10 Jul 2014 15:32
by oldgearguy
duck_waddle wrote:
oldgearguy wrote:To continue along what Softcore said -- if you find yourself cutting and pasting the exact same (or almost exactly the same) set of lines in a script, you should consider making it a 'subroutine' (really in Lemur they are all just scripts).

For example, your chord() and chordOFF() are exactly the same with the exception of 0 or 127. you could create a new script called sendChord(velocity) that had the same guts except that in place of the 0/127 you use the velocity parameter: noteout(lemurPort,a,velocity,midiChannel);

Make that script execute manually and then your chord() and chordOFF() scripts are reduced to simply:

<SCRIPT name="chord()" script="
sendChord(127);
" trigger_script="x" trigger_type="0" trigger="2" clock="0" clock_div="4" osc_message="/maj7/chord" midi_message="0x90,0x90,0,0" midi_target="-1" flag="1"/>

<SCRIPT name="chordOFF()" script="
sendChord(0);
" trigger_script="x" trigger_type="0" trigger="3" clock="0" clock_div="4" osc_message="/maj7/chordOFF" midi_message="0x90,0x90,0,0" midi_target="-1" flag="1"/>

The beauty of doing something like this is that later if you wanted to send a chord with a different velocity, you can call sendChord() with the new number without having to change it in multiple places.

That's a simple optimization, but if you start thinking like that, you'll make it easier to do updates/fixes since you don't have to hunt through all your scripts looking for the same change. Plus if you create a core set of scripts, then adding new functionality becomes much easier since you already did the hard work creating the core pieces.

Once you get comfortable with that, you could pass in values that make up the triad for that particular core type or create an array of triads and then reference which set to use at any given time.
Hey thank you so much for your time! That kind of constructive advice is exactly what I need - I think I'm almost comfortable enough with some syntax and programming concepts and techniques that I really just need some thoughtful feedback. Your second post, about being bulletproof was awesome. I will do my best to think like that from now on.

The first post, though, left me with a few questions. I knew my code repeated itself way too much, but I wasn't quite sure how to address that. The script you suggest, sendChord(), would that live globally? Is that where I should be putting my "functions?" I've seen some people mention that they write scripts in containers that apply to the objects inside the container. Is that technique interchangeable with global scripts (at least in this example)?

Also, that language you're writing in that resembles HTML - where and when can I use that? Can you point me in the direction to read more about those tags and syntax you used? Again, I can't thank you enough!
In reverse order -- the syntax was actually cut-n-pasted out of the raw .jzml file. Under the hood, Lemur uses an XML style formatting for all the objects and scripts you create in the editor. If you are somewhere (like a workplace) that doesn't allow you to install Lemur to view/edit, you can still work with the raw .jzml file. It's more dangerous since there's no syntax checking or anything, but for quick views or checking something, it is fine. Once you get comfortable, it's sometimes faster to work with the rawjzml if you want to do something like rename a script and all the places that call it or if you have some tedious editing to do. Wordpad/notepad supports things like Home/End, cut/paste, and other standard editing conventions that either don't work or work weirdly in the Lemur editor.

Given the way you coded the objects, each fader would need its own sendChord(velocity) script. To use a global script (i.e. just have one copy), it would have to be smarter and/or take more parameters to distinguish between the various types (maj7, min6, etc).

I'll often take multiple passes through the code if I'm not sure - bang out a 'it just works but it is ugly' version to get a proof of concept, then go back and clean up the code by pulling obviously common things out into a single script. Then later, once I have a good handle on where the app is going and what functionality I need, I'll look at it to determine if further consolidation makes sense and is possible.

Since chord and scale structure is all about the math and intervals, you should be able to consolidate even more if you wanted by setting up arrays (vectors) with the various offsets and then passing in an index to the array based on whether you want major, minor, 6th, 7th, etc. On the flip side, if you want to just get something done and move on and use it, what you have certainly is good enough.

If you just want to use what you wrote, spending days preventing unlikely combinations from causing unexpected behavior might be overkill. The worst that happens (in this case) is hung notes. If you are writing an editor or realtime recorder, loss of data is a bigger deal. In addition to writing software as my day job, I have done a lot of beta testing for Alesis, Akai, and other companies. Once you get good at exploring edge cases and what-if's, you become adept at breaking things. If you can document how to break it, then the developer can usually figure out a way to prevent/fix the break. That's how this TX-802 editor I've been writing turned into a 7 month long job (working in various free moments over that time). The core functionality was there in the first month or two. The rest of the time has been spent adding some features and making it more bulletproof as I tested it and broke stuff.