Skip to main content
Topic: Sibling : example of a usertool scripted in LUA  (Read 2191 times) previous topic - next topic

Sibling : example of a usertool scripted in LUA

"This tool mutes the active staff and adds 2 copies with all its relevant information into 2 new sibling staves with two different instrument patches."    -- added as suggested --

For the people who want to learn LUA as a scripting language for Noteworthy Composer, I can distribute a LUA script I made myself a few months ago. I didn't publish it because it is all hard coded and therefore only usable for people who can make their own customization. I created this tool to speed up things, and didn't want it to slow down with user input boxes, etc.
Therefore it is not usable as a universal user tool. If you want to "tune" the tool, you will have to hack the code.

I must admit that it took a lot of time. There are only a few examples in the documentation and it was - for me - not the most comprehensive source of information. For creating my script, I combined the LUA documentation ( with the examples I found in the cloud. But most of my knowledge I got from examples that Rick G and Opagust posted in the forum. Without their work I wouldn't be able to create this script.

As far as I understand,  LUA is the preferred scripting language for user tools.  I am not aware if it is also a good choice for user objects.

There are different modes for LUA scripts and the mode should correspond with the option how a user tool should run. In this example FileText mode is used. A check for this mode is made with the method "nwcut.getprop('Mode')" in the first line of the main processing module.

LUA is a nested table oriented tool and has more flexibility in (nested) table manipulation as in text strings. These nested tables (also defined as objects) can be mapped on the structure of NWC files.
If "nwc.setlevel(2)" is specified, nwcobjects are interpreted as objects (or nested tables). You can force your script with a lower level (1) in text mode but it turned out - for me - that this is less flexible.

The structure of my script is as follows:

(1) check mode and returnmode
(2) define an object (or empty nested table) "mt" where the whole existing nwc-file will be staged and which can be printed out at the end of the script.
(3) loop over the nwc-file and collect interesting properties in variables, modify the properties - if desired -, and stage the result (original or modified) in "mt"
(3.1) all non-current staves are staged (almost) unmodified in "mt"
(3.2) the "current" staff is staged modified (muted) in "mt"
(3.3) the "current" staff is twice duplicated as "siblings" and - with modified visual and sound properties - staged in table "ST1" and "ST2".
(3.4) existing siblings of the "current" staff are skipped.
(4) at the end, all staged staves are "printed out".  If nwcut.status is rc_succes the existing score is overwritten, if nwcut.status = rc_Report, the output is sent to the log window - wich is very useful in debugging mode.

I make a detour to strings before using "" to achieve real duplicates and not new pointers to the same content. This was a real pitfall for me!

Code: [Select · Download]
-- $NWCUT$CONFIG: FileText $
nwcut.status = nwcut.const.rc_Succes
--nwcut.status = nwcut.const.rc_Report

local progname = 'Sibling'
local ASI   = 0              -- Active  Staff Parameters
local ASN   = "UnDefined";
local CSI   = 0              -- Current Staff Parameters
local CSN   = "UnDefined";
local DSG  = "SIBLINGS"
local DSPN = "SiblingInstrument"
local HelpMsg = [[
This tool mutes the active staff and adds 2 copies all its relevant information into 2 new sibling staves.
The active staff is slightly modified if it is not already in this state: the instrument is explicitely specified [line 33] and the staff is muted [line 34].
If staves with sibling name <original_name>_Sibling1 or <original_name>_Sibling1 already exist, they will be removed [line 36].
2 (hidden) sibling staves are added at the end of the score based on the ActiveStaff (named : <original_name>_sibling<i>) [line 67 and line 68].
The midi channels used for sibling staves are 15 and 16 [line 48] and since they are at the end of the score, they will override midi definitions for staves that were assigned earlier to these channels.
The sibling staves are made invisible to avoid sibling them again [line 52].
The sibling staves receive Instrument Patches 00 (piano) and 89 (Pad 2 warm) [line 56].
Instrument changes in the ActiveStaff are skipped in the sibling [line 58].

-- Main processing -------------------
assert(nwcut.getprop('Mode')       == nwcut.const.mode_FileText, "Input type must be 'File Text'")
assert(nwcut.getprop('ReturnMode') == nwcut.const.mode_FileText, "Under 'Options', check 'Returns File Text'")
mt = {} ; mt[0]={}
for item in nwcut.items() do
    if item:Is('Editor')          then  ASI = item.Opts.ActiveStaff + 0 ; end--if
if item:Is('AddStaff')        then  CSI = CSI + 1 ; mt[CSI] = {} ; CSN = item.Opts.Name.Text ; CSOGN = item.Opts.Group.Text ; if CSI == ASI then ASN = CSN end ; end--if
if item:Is('StaffInstrument') then  if not item.Opts.Patch then item.Opts.Patch=0 end ; if not item.Opts.Name  then item.Opts.Name="\"NN\"" end ; end--if
    if item:Is('StaffProperties') and ( CSI == ASI ) and ( item.Opts.Muted ) then item.Opts.Muted="Y" end--if

if not ( string.gsub(CSN,'%-','_') == string.gsub(ASN,'%-','_').."_sibling1" or string.gsub(CSN,'%-','_') == string.gsub(ASN,'%-','_').."_sibling2" ) then table.insert(mt[CSI],tostring(item)) end--if

if CSI == ASI then
       SS1 = ; SS2 =
       if item:Is('AddStaff')  then
          SS1.Opts.Name = CSN .. "_sibling1" ; SS2.Opts.Name = CSN .. "_sibling2" ;
          SS1.Opts.Group = DSG ; SS2.Opts.Group = DSG  
          ST1 = {} ; ST2 = {} ;
       if item:Is('StaffProperties') then
          if ( item.Opts.Muted or item.Opts.Channel or item.Opts.Volume or item.Opts.StereoPan)  then
            SS1.Opts.Muted = "N"    ; SS2.Opts.Muted = "N"               
            SS1.Opts.Channel = 15   ; SS2.Opts.Channel = 16          
            SS1.Opts.Volume = 100   ; SS2.Opts.Volume = 100  
            SS1.Opts.StereoPan = 64 ; SS2.Opts.StereoPan = 64
          elseif item.Opts.Visible then
            SS1.Opts.Visible = "N"  ; SS2.Opts.Visible = "N"
       if item:Is('StaffInstrument') then
          SS1.Opts.Patch = 89    ; SS2.Opts.Patch = 0 ; 
          SS1.Opts.Name  = DSPN  ;  SS2.Opts.Name  = DSPN             
       if item:Is('TempoVariance') and item.Opts.Pause then SS1.Opts.Pause = 0 ; SS2.Opts.Pause = 0 ; end--if
       if not item:Is('Instrument') then table.insert(ST1,tostring(SS1)) ; table.insert(ST2,tostring(SS2)) ; end--if

for j=0,CSI do
   for i=1,#mt[j] do nwcut.writeline (mt[j][i]) end--for
for i=1,#ST1 do nwcut.writeline (ST1[i]) end--for
for i=1,#ST2 do nwcut.writeline (ST2[i]) end--for

Re: Sibling : example of a usertool scripted in LUA

Reply #1
Interesting tool...two things:

  • you might want to put the top source code comment at the top of your post:

    This tool mutes the active staff and adds 2 copies all its relevant information into 2 new sibling staves.

  • Lua is the only option for user objects.