local progname= 'Rehearsal.og' local Testing = false --[[ -- Version 1.1 new in version 1.1: - test on NWC version: it has to be > 2.75 - correction of a bug: dynamic velocities from the parameters were not copied into the staff instruments. With this plugin you can transform a (vocal) score into a rehearsal score, by specifying different channel volume, stereopan and dynamic volumes and velocities for each staff, according to its 'Staff Type'. You can also specify a different color for a 'Foreground voice'. To do that you have to create one rehearsal object subtype 'Parameters' (anywhere in the score), and a subtype 'Staff Type' in each staff you want to change. Hint: create rehearsal objects in your template file. Staff Types are: 'Backgroud voice', 'Center voice', 'Foreground voice', 'Velocity instrument' and 'Volume Instrument'. Parameters are: Foreground color, Channel Volume and Stereopan for each staff type and dynamic velocities for each dynamic (ppp ... fff). They all have predefined default settings, which you can change or leave unchanged. After creation of your objects, they can be processed by the integrated 'Rehearsal.og' user tool in the .Plugins group (which you can invoke via 'Tools/User Tool...' or ALT-F8). Velocities and Volumes of the dynamics are calculated as follows (for more information: see document 'Dynamics.pdf' in ): - for a voice staff or volume instrument staff: ° The Velocity of each dynamic is set equal to the highest Staff Velocity ('fff'). ° The Volume is set using Tina Billet's formula : Volume = Velocity * Staff Volume) / highest Staff Velocity. - for a velocity staff: the custom velocity and volume settings are set to nil, which means that they get the dynamic velocity and channel volume from the parameters (which are stored in the staff properties). --]]---------------------------------------------------------------- local ToolHelpMsg = [[ With this tool you can transform a (vocal) score into a rehearsal score, by specifying different channel volume, stereopan and dynamic volumes and velocities for each staff, according to its 'Staff Type'. You can also specify a different color for a 'Foreground voice'. To do that you first have to create one rehearsal object subtype 'Parameters' (anywhere in the score), and a subtype 'Staff Type' in each staff you want to change. If you haven't done that, leave this tool by pressing 'Cancel' and choose 'Insert/object' on the menu tab or press 'j' and select 'Rehearsal.og'. Hint: create rehearsal objects in your template file. Staff Types are: 'Backgroud voice', 'Center voice', 'Foreground voice', 'Velocity instrument' and 'Volume Instrument'. Parameters are: Foreground color, Channel Volume and Stereopan for each staff type and dynamic velocities for each dynamic (ppp ... fff). They all have predefined default settings, which you can change or leave unchanged. After creation of your objects, they can be processed by this user tool. Velocities and Volumes of the dynamics are calculated as follows (for more information: see document 'Dynamics.pdf' in ): - for a voice staff or volume instrument staff: ° The Velocity of each dynamic is set equal to the highest Staff Velocity ('fff'). ° The Volume is set using Tina Billet's formula : Volume = Velocity * Staff Volume) / highest Staff Velocity. - for a velocity staff: the custom velocity and volume settings are set to nil, which means that they get the dynamic velocity and channel volume from the parameters (which are stored in the staff properties). PRESS 'OK' TO PROCEED OF 'CANCEL' TO LEAVE. ]] local function split(str, sep) -- returns a table with the parts of the string 'str', seperated by 'sep' local tab = {} local nsep = #sep if string.sub(sep,1,1) == '%' then nsep = nsep - 1 end--if if str ~= '' then local i = 1 local f = 1 local s if string.sub(str,1,1) == '"' or string.sub(str,1,1) == "'" then str = string.sub(str,2,-2) end--if if string.sub(str,1,1) == sep then str = string.sub(str,#sep+1,-1) end--if repeat s = string.find(str, sep, f) or 0 tab[i] = s == 0 and string.sub(str, f, #str) or string.sub(str, f, s - 1) f = s + nsep i = i + 1 until s == 0 end return tab end--split local function join(tab, sep) local str = '' for _,v in pairs(tab) do if str == '' then str = v else str = str..sep..v end--if end--for return str end-- join local Colors = nwc.txt.ItemColor local StaffTypes = { 'Background voice', 'Center voice', 'Foreground voice', 'Velocity instrument', 'Volume instrument' } local ObjSubTypes = { Parm = 'Parameters', StaffType = 'Staff Type' } local Defaults = { ForegroundVolume = 120, ForegroundStereoPan = 0, BackgroundVolume = 60, BackgroundStereoPan = 127, CenterVolume = 90, CenterStereoPan = 64, InstrumentVolume = 60, InstrumentStereoPan = 64, ForegroundColor = Colors[3], -- = Highlight 2 StaffType = StaffTypes[1], ppp = 25, pp = 35, p = 45, mp = 55, mf = 68, f = 80, ff = 92, fff = 108 } local ToolParms = {} for k,v in pairs(Defaults) do ToolParms[k] = v end--for local obj_spec = { {id='SubType', label = "SubType (don't change)", type = 'txt',width=16}, {id='despos', label='Description position', type='int', min = 1, max = 20, separator = ' '}, {id='ForegroundColor', label='Foreground Color',type='enum', list = Colors, obj_type=ObjSubTypes.Parm, separator='-'}, {id='ForegroundVolume',label='Foreground Ch. Volume', type='int', min = 0, max = 127, obj_type=ObjSubTypes.Parm,}, {id='ForegroundStereoPan',label='Foreground Stereo Pan', type='int', min = 0, max = 127, obj_type=ObjSubTypes.Parm, separator = ' '}, {id='CenterVolume',label='Center Ch. Volume', type='int', min = 0, max = 127, obj_type=ObjSubTypes.Parm}, {id='CenterStereoPan',label='Center Stereo Pan', type='int', min = 0, max = 127, obj_type=ObjSubTypes.Parm, separator = ' '}, {id='BackgroundVolume',label='Background Ch. Volume', type='int', min = 0, max = 127, obj_type=ObjSubTypes.Parm}, {id='BackgroundStereoPan',label='Background Stereo Pan', type='int', min = 0, max = 127, obj_type=ObjSubTypes.Parm, separator = ' '}, {id='InstrumentVolume',label='Instrument Ch. Volume', type='int', min = 0, max = 127, obj_type=ObjSubTypes.Parm}, {id='InstrumentStereoPan',label='Instrument Stereo Pan', type='int', min = 0, max = 127, obj_type=ObjSubTypes.Parm, separator = ' '}, {id='ppp', label='Dynamic Velocities: ppp',type='int', min=0, max=127, obj_type=ObjSubTypes.Parm, separator = '-'}, {id='pp', type='int', min=0, max=127, obj_type=ObjSubTypes.Parm, separator = ' '}, {id='p', type='int', min=0, max=127, obj_type=ObjSubTypes.Parm, separator = ' '}, {id='mp', type='int', min=0, max=127, obj_type=ObjSubTypes.Parm, separator = '/n'}, {id='mf', type='int', min=0, max=127, obj_type=ObjSubTypes.Parm, separator = ' '}, {id='f', type='int', min=0, max=127, obj_type=ObjSubTypes.Parm, separator = '/n'}, {id='ff', type='int', min=0, max=127, obj_type=ObjSubTypes.Parm, separator = ' '}, {id='fff', type='int', min=0, max=127, obj_type=ObjSubTypes.Parm, separator = ' '}, {id='StaffType', label='Staff Type', type='enum', list=StaffTypes, obj_type=ObjSubTypes.StaffType}, } if nwcut then -- This is the user tool entry point local userObjTypeName = arg[1] or progname nwcut.setlevel(2) strversion = nwcut.nwcversion() version = 0 k = 1 while true do try = tonumber(string.sub(strversion,1, k)) if not try then break end--if version = try k = k + 1 end--while assert(version > 2.75, "NWC version should be > 2.75") do --redefine tostring() Luatostring = tostring tostring = function(t, tab) tab = tab and tab.." " or "" if type(t) ~= 'table' then return Luatostring(t) end--if local s = tab.."{" for k, v in pairs(t) do if s ~= tab.."{" then s = s..", " end--if if string.sub(s, #s-2) == "}, " then s = s.."\n"..tab.." " end--if local s1 = tostring(k) local s2 = tostring(v, tab) s = s..s1.." = "..s2 end--for return "\n"..s.."}" end--tostring end--do function ShowVar(name, var) if Testing then nwcut.warn(name == "" and "" or tostring(name).." ==> ", tostring(var),"\n") end--if end -- ShowVar local CurrentStaff = 0 local Staffs = {} local Staff local FileHeader = {} local NumProp = 0 local DynVel = {} local FVolumes = {} local BVolumes = {} local CVolumes = {} local IVolumes = {} local DynNum = {'ppp', 'pp','p','mp','mf','f','ff','fff'} local addstaff = 1 local staffproperties1 = 2 local staffproperties2 = 3 local staffinstrument = 4 local function IsFileHeader(item) return item:Is("Locale") or item:Is("Editor") or item:Is("SongInfo") or item:Is("PgSetup") or item:Is("Font") or item:Is("PgMargins") end--IsFileHeader local function IsStaffHeader(item) return item:Is("AddStaff") or item:Is("StaffProperties") or item:Is("StaffInstrument") or string.sub(item.ObjType,1,5) == "Lyric" end--IsStaffHeader --Color : in StaffProperties 1 --Volume, StereoPan : in StaffProperties 2 --Dynamic Velocities: in StaffInstrument, array DynVel met index 1 => ppp, 2 => pp,... -------------------------------------------------- local function ProcessAddStaff(item) CurrentStaff = CurrentStaff + 1 Staffs[CurrentStaff] = {} Staff = Staffs[CurrentStaff] Staff.Name = item.Opts.Name end -- ProcessAddStaff(item) local function ProcessStaffProperties(item) NumProp = NumProp + 1 if NumProp == 1 then Staff.Color = item.Opts.Color else Staff.Volume = item.Opts.Volume Staff.StereoPan = item.Opts.StereoPan end--if end-- ProcessStaffProperties local function ProcessStaffInstrument(item) Staff.DynVel = item.Opts.DynVel end-- ProcessStaffProperties local function UpdateToolParms(object) for k, opt in pairs(object.Opts) do if ToolParms[k] then ToolParms[k] = opt end--if end--for ShowVar("ToolParms", ToolParms) end--UpdateToolParms local function ProcessObject(item) if item.Opts.SubType == ObjSubTypes.Parm then UpdateToolParms(item) elseif item.Opts.SubType == ObjSubTypes.StaffType or item.Opts.SubType == ObjSubTypes.InstrumentType then Staff.Type = item.Opts.StaffType and item.Opts.StaffType end--if end--ProcessObject local function AllocateItems() for item in nwcut.items() do if IsFileHeader(item)then table.insert(FileHeader, item) elseif IsStaffHeader(item)then if item:Is("AddStaff") then ProcessAddStaff(item) elseif item:Is("StaffProperties") then ProcessStaffProperties(item) elseif item:Is("StaffInstrument") then ProcessStaffInstrument(item) end--if table.insert(Staff, item) else if item:Is('User') and item.UserType == progname then ProcessObject(item) end--if table.insert(Staff, item) end--if end --for item end -- AllocateItems local function CalcDynVel() for i, v in ipairs(DynNum) do DynVel[i] = ToolParms[v] end--for ShowVar("DynVel", DynVel) end--CalcDynVel local function CalcVolumes() CalcDynVel() for i, vel in ipairs(DynVel) do FVolumes[i] = math.floor(vel * ToolParms.ForegroundVolume / DynVel[8] + 0.5) BVolumes[i] = math.floor(vel * ToolParms.BackgroundVolume / DynVel[8] + 0.5) CVolumes[i] = math.floor(vel * ToolParms.CenterVolume / DynVel[8] + 0.5) IVolumes[i] = math.floor(vel * ToolParms.InstrumentVolume / DynVel[8] + 0.5) end--for ShowVar("FVolumes", FVolumes) ShowVar("BVolumes", BVolumes) ShowVar("CVolumes", CVolumes) end-- local function ChangeStaffProps(s) if not s.Type then return end--if ShowVar('s.Name', s.Name) ShowVar('s.Type', s.Type) ShowVar('s[staffproperties2] before', s[staffproperties2]) if s.Type == 'Foreground voice' then s[staffproperties1].Opts.Color = ToolParms.ForegroundColor ShowVar('ToolParms.Color', ToolParms.ForegroundColor) s[staffproperties2].Opts.Volume = ToolParms.ForegroundVolume s[staffproperties2].Opts.StereoPan = ToolParms.ForegroundStereoPan elseif s.Type == 'Background voice' then s[staffproperties1].Opts.Color = ToolParms.BackgroundColor s[staffproperties2].Opts.Volume = ToolParms.BackgroundVolume s[staffproperties2].Opts.StereoPan = ToolParms.BackgroundStereoPan elseif s.Type == 'Center voice' then s[staffproperties1].Opts.Color = 'Default' s[staffproperties2].Opts.Volume = ToolParms.CenterVolume s[staffproperties2].Opts.StereoPan = ToolParms.CenterStereoPan elseif s.Type == 'Velocity instrument' then s[staffproperties1].Opts.Color = 'Default' s[staffproperties2].Opts.Volume = ToolParms.InstrumentVolume s[staffproperties2].Opts.StereoPan = ToolParms.InstrumentStereoPan elseif s.Type == 'Volume instrument' then s[staffproperties1].Opts.Color = 'Default' s[staffproperties2].Opts.Volume = ToolParms.InstrumentVolume s[staffproperties2].Opts.StereoPan = ToolParms.InstrumentStereoPan end--if ShowVar('s[staffproperties2 after]', s[staffproperties2]) end-- function Changestaffprops local function ChangeDynVel(s) if not s.Type then return end--if for i, dyn in ipairs(s[staffinstrument].Opts.DynVel) do s[staffinstrument].Opts.DynVel[i] = ToolParms[DynNum[i]] end--for end--ChangeDynVol local function SetDynVol(s,item) if not s.Type then return end--if -- if item.Opts.Opts and item.Opts.Opts.Volume and item.Opts.Opts.Volume == '0' then return end--if local style = item.Opts.Style local ind for i,v in ipairs(DynNum) do if v == style then ind = i break end--if end--for local opts = item.Opts.Opts local velocity, volume velocity = opts and opts.velocity or nil volume = opts and opts.Volume or nil item:Provide("Opts") if s.Type == 'Velocity instrument' then if item.Opts.Opts then item.Opts.Opts.Velocity = nil item.Opts.Opts.Volume = nil end--if else item.Opts.Opts.Velocity = Staffs[addstaff].DynVel[8] item.Opts.Opts.Velocity = Staffs[addstaff].DynVel[8] item.Opts.Opts.Volume = s.Type == 'Foreground voice' and FVolumes[ind] or s.Type == 'Background voice' and BVolumes[ind] or s.Type == 'Center voice' and CVolumes[ind] or s.Type == 'Volume instrument' and IVolumes[ind] end--if end-- SetDynVol -------------------------------------- -- Main processing ------------------- -------------------------------------- nwcut.status = nwcut.const.rc_Succes 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'") nwcut.msgbox(ToolHelpMsg) -- for item in nwcut.items() do AllocateItems() -- end --for item CalcVolumes() for _, item in ipairs(FileHeader) do nwcut.writeline(item) end--for for _, s in ipairs(Staffs) do ChangeStaffProps(s) ChangeDynVel(s) for _,item in ipairs(s) do if item:Is('Dynamic') then SetDynVol(s, item) end--if nwcut.writeline(item) end--for end--for return end--nwcut local userObjTypeName = ... -------------------------------------------------------------------- local prompt = nwcui.prompt local drawpos = nwcdraw.user local dr = nwcdraw local objidx = nwc.ntnidx local function do_spin(t,d) t.despos = t.despos or 1 t.despos = math.max(t.despos + d, 1) t.despos = math.min(t.despos, 20) end--do_spin local function do_draw(t) if t.Visible == 'N' then return end--if local w, h, width local showInTargets = {edit=1,selector=1} -- local media = nwcdraw.getTarget() if showInTargets[media] and not dr.isAutoInsert() then local txt1 = progname local txt2 if t.SubType == ObjSubTypes.StaffType then local i = string.find(t.StaffType,' ') txt2 = string.sub(t.StaffType, 1, i) else for k,subtype in pairs(ObjSubTypes) do if t.SubType == subtype then txt2 = k end--if end--for end--if dr.setFontClass('PageSmallText') -- dr.opaqueMode(true) local xyar = dr.getAspectRatio() local h1,w1 = dr.calcTextSize(txt1) local w2,h2 = dr.calcTextSize(txt2) local hd = t.despos w1 = w1 or 0 w2 = w2 or 0 h1 = h1 or 0 h2 = h2 or 0 w = w1 / xyar h = h1*xyar if not dr.isDrawing() then return w end local rx,ry = 0.5 * w, 0.5 * h + w/2 dr.moveTo(-rx,0) dr.beginPath() dr.roundRect(rx,ry,0.1 * w1) dr.endPath('stroke') dr.setPen('solid', 150) dr.alignText("bottom","center") dr.moveTo(0,0) dr.text(txt1,90) rx, ry = -w1/2, w1 + 3*h2/2 + hd, dr.alignText("bottom","left") dr.moveTo(rx,ry) dr.setFont('Arial',4,'r') dr.text(txt2) end--if return w end local function set_default(id, default) for i,v in ipairs(obj_spec) do if v.id == id then v.default = default return end--if end--for end-- set_default local function do_create(t) local spin_pos = {} local pos = 0 for _,v in pairs(ObjSubTypes) do spin_pos[v] = pos pos = pos + 3 end--for t.SubType = nwcui.prompt('Wich subtype do you want to create? ', '|'..join(ObjSubTypes, '|')) for i, spec in ipairs(obj_spec) do if spec.obj_type and spec.obj_type ~= t.SubType then set_default(spec.id, nil) t[spec.id] = nil elseif spec.obj_type then set_default(spec.id, Defaults[spec.id]) t[spec.id] = spec.default spec.default = nil end--if end--for t.despos = spin_pos[t.SubType] t.pos = 0 end-- do_create ------------------------------------------------------------------- -- all object plug-ins must return the methods that they support in a -- standard method table return { nwcut = {['Create a rehearsal score'] = 'FileText'}, spec = obj_spec, create = do_create, draw = do_draw, width = do_draw, spin = do_spin }