-- Version 2.3 --[[---------------------------------------------------------------- This will draw a glissando line between two notes, with optional text above the line. If either of the notes is a chord, the bottom notehead of that chord will be the starting or ending point of the line. @Pen Specifies the type for lines: solid, dot, dash or wavy. The default setting is solid. @Text The text to appear above the glissando line, drawn in the StaffItalic system font. The default setting is "gliss." @Scale The scale factor for the text above the glissando line. This is a value from 5% to 400%, and the default setting is 75%. The text scale factor can be incremented/decremented by selecting the object and pressing the + or - keys. @StartOffsetX This will adjust the auto-determined horizontal (X) position of the glissando's start point. The range of values is -100.00 to 100.00. The default setting is 0. @StartOffsetY This will adjust the auto-determined vertical (Y) position of the glissando's start point. The range of values is -100.00 to 100.00. The default setting is 0. @EndOffsetX This will adjust the auto-determined horizontal (X) position of the glissando's end point. The range of values is -100.00 to 100.00. The default setting is 0. @EndOffsetY This will adjust the auto-determined vertical (Y) position of the glissando's end point. The range of values is -100.00 to 100.00. The default setting is 0. @Weight This will adjust the weight (thickness) of both straight and wavy line types. The range of values is 0.0 to 5.0, where 1 is the standard line weight. The default setting is 1. @Playback This can be used to activate different optional forms of play back. Most play back methods are best when the target (left side) note is muted. PitchBend also supports muting the right side note, which will result in a seamless note event that bends from one pitch to the other. For PitchBend, the staff/instrument definition should establish a 24 semitone pitch bend. For best results, the note pair should also be within ±24 semitones. @GlissDelay This can delay the start of the glissando for a certain percent of the note duration. Default is 0%, maximum is 99%. @EndNoteShift This will adjust the pitch of the ending note upwards or downwards by the specified number of semitones. It is used when a clef change or transposition (i.e. 8va or 8va bassa) occurs between the starting and ending note of the glissando. A value of ±20 would be used for changes between treble and bass clefs. The range of values is -100 to 100. The default setting is 0. --]]---------------------------------------------------------------- local userObjTypeName = ... local nextNote, priorNote = nwc.drawpos.new(), nwc.drawpos.new() local nextNoteidx, priorNoteidx = nwc.ntnidx.new(), nwc.ntnidx.new() local idx = nwc.ntnidx local user = nwcdraw.user local lineStyles = { 'solid', 'dot', 'dash', 'wavy' } local squig = '~' local showBoxes = { edit=true } local pbNeutral = 0x02000 local PlaybackStyle = {'None','Chromatic','WhiteKeys', 'BlackKeys', 'PitchBend'} local KeyIntervals = { None = {}, Chromatic = {0,1,2,3,4,5,6,7,8,9,10,11}, WhiteKeys = {0,2,4,5,7,9,11}, BlackKeys = {1,3,6,8,10}, PitchBend = {24}, } local _spec = { { id='Pen', label='Line Style', type='enum', default=lineStyles[1], list=lineStyles }, { id='Text', label='Text', type='text', default='gliss.' }, { id='Scale', label='Text Scale (%)', type='int', min=5, max=400, step=5, default=75 }, { id='StartOffsetX', label='Start Offset X, Y', type='float', step=0.1, min=-100, max=100, default=0 }, { id='StartOffsetY', label='', type='float', step=0.1, min=-100, max=100, default=0 }, { id='EndOffsetX', label='End Offset X, Y', type='float', step=0.1, min=-100, max=100, default=0 }, { id='EndOffsetY', label='', type='float', step=0.1, min=-100, max=100, default=0 }, { id='Weight', label='Line Weight', type='float', default=1, min=0, max=5, step=0.1 }, { id='Playback', label='Pla&yback', type='enum', default=PlaybackStyle[1], list=PlaybackStyle }, { id='GlissDelay', label='Gliss &Delay (%)', type='int', min=0, max=99, step=1, default=0 }, { id='EndNoteShift', label='End Note Shift', type='int', min=-100, max=100, step=1, default=0 }, } local _spec2 = {} for k, v in ipairs(_spec) do _spec2[v.id] = k end local function _create(t) t.Class = 'Span' end local function _audit(t) if t.Style then if (t.Style == 'Wavy') then t.Pen = 'wavy' end t.Style = nil end t.ap = nil local barSpan = (idx:find('span', 1) or idx:find('last')) and idx:find('prior','bar') and (idx:indexOffset() > 0) t.Class = barSpan and 'Span' or 'Standard' end local function box(x, y, ap, p) local m = (ap == p) and 'strokeandfill' or 'stroke' nwcdraw.setPen('solid', 100) nwcdraw.moveTo(x, y) nwcdraw.beginPath() nwcdraw.roundRect(0.2) nwcdraw.endPath(m) end local stopItems = { Note=1, Chord=1, RestChord=1, Rest=-1, Bar=-1, RestMultiBar=-1, Boundary=-1 } local function hasPriorTargetNote(idx) while idx:find('prior') do local d = stopItems[idx:objType()] if d then return d > 0 end end return false end local function drawGliss(x1, y1, x2, y2, drawText, t) local xyar = nwcdraw.getAspectRatio() local _, my = nwcdraw.getMicrons() local pen, text, weight = t.Pen, t.Text, t.Weight local angle = math.deg(math.atan2((y2-y1), (x2-x1)*xyar)) if drawText and text ~= '' then nwcdraw.alignText('bottom', 'center') nwcdraw.setFontClass('StaffItalic') nwcdraw.setFontSize(nwcdraw.getFontSize()*t.Scale*.01) nwcdraw.moveTo((x1+x2)*.5, (y1+y2)*.5) nwcdraw.text(text, angle) end if pen ~= 'wavy' then if weight ~= 0 then nwcdraw.setPen(pen, my*.3*weight) nwcdraw.line(x1, y1, x2, y2) end else nwcdraw.alignText('baseline', 'left') nwcdraw.setFontClass('StaffSymbols') nwcdraw.setFontSize(nwcdraw.getFontSize()*weight) local w = nwcdraw.calcTextSize(squig) local len = math.sqrt((y2-y1)^2 + ((x2-x1)*xyar)^2) local count = math.floor(len/w/xyar) nwcdraw.moveTo(x1, y1-1) nwcdraw.text(string.rep(squig, count), angle) end end local function _draw(t) local atSpanFront = not user:isAutoInsert() local atSpanEnd = nextNoteidx:find('span', 1) if not hasPriorTargetNote(priorNoteidx) or not atSpanEnd then if not atSpanFront then return 0 end local x, y = -4, 4 if not nwcdraw.isDrawing() then return -x end drawGliss(x, 0, 0, y, true, t) return end if not nwcdraw.isDrawing() then return 0 end local xo, yo = .25, .5 nextNote:find(nextNoteidx) priorNote:find(priorNoteidx) if not atSpanEnd then nextNoteidx:find('last') end local x1 = atSpanFront and priorNote:xyRight() + xo + t.StartOffsetX or -1.25 local y1 = priorNoteidx:notePos(1) local x2 = (atSpanEnd and nextNote:xyAnchor() + t.EndOffsetX or 0) - xo local y2 = nextNoteidx:notePos(1) or 0 local s = y1>y2 and 1 or y1= inOctaveSemiTone then return i-1 end end return 0 end local function CountGlissIntervals(k, v) local o = math.floor(v/12) local i = v % 12 return #k*o + GlissOctaveNearestNextInterval(k, i) end local function GlissNoteFromInterval(k,v) local opitches = #k local o = math.floor(v/opitches) local i = v % opitches return 12*o + k[i+1] end local function isStandAloneMutedNote(n) return n:isMute() and not n:isTieIn() and not n:isTieOut() end -- function `partOfNextPlayGliss` has a side effect on `idx` so be careful when using it local function partOfNextPlayGliss(n) if not idx:find(n) or not idx:find('next','noteOrRest') then return false end return idx:find('prior','user',userObjTypeName,'Playback') and (idx:indexOffset() > 0) end local function setPitchBend(Start, pbValue) local Value = pbValue + pbNeutral local dLSB = math.floor(Value % 128) local dMSB = math.floor(Value/128) nwcplay.midi(Start, 'pitchBend', dLSB, dMSB) end local function _play(t) local playbackt = t.Playback local playback = KeyIntervals[playbackt] if #playback < 1 then return end if not (hasPriorTargetNote(priorNoteidx) and nextNoteidx:find('span', 1)) then return end local startSPP = priorNoteidx:sppOffset() local dur = -startSPP local SweepDelaySPP = math.floor((t.GlissDelay*dur)/100) local v1 = nwcplay.getNoteNumber(priorNoteidx:notePitchPos(1)) local v2 = nwcplay.getNoteNumber(nextNoteidx:notePitchPos(1))+t.EndNoteShift if (not v1) or (not v2) or (v2 == v1) then return end local inc = (v1