; ------------------------------------------------------------------------------------------------- ; Exactamento.cal ; ------------------------------------------------------------------------------------------------- ; ; Author: Eric von Bayer ; Email: CAL @ ericvonbayer.us (remove the spaces) ; WWW: http://www.ericvonbayer.us ; Dates: January 2005 - January 2005 ; Version: 1.0 ; Description: ; ; Script to generate exact portamento slides. It will convert multiple overlapping notes into a ; single held note where pitch hanges are achieved with pitchbend messages. If there is a gap or ; the jump in pitch is too great, the note will be released and a new note played. The portamento ; speed is dictated by the length of the overlap. ; ; ------------------------------------------------------------------------------------------------- ; Make Sure we're new enough ; ------------------------------------------------------------------------------------------------- (do (if (< VERSION 40) (do (pause "This CAL program requires CAL version 4.0 or higher") (exit) ) ) ; ------------------------------------------------------------------------------------------------- ; Config Section ; ------------------------------------------------------------------------------------------------- (word wBendDepth 12) ; Bend Depth (+/- semitones) (word wTickRes 100) ; Maximum Tick Spacing (word wBendRes 200) ; Approximate Bend Resolution (as long as it fits in 1 tick) ; ------------------------------------------------------------------------------------------------- ; Main processing loop ; ------------------------------------------------------------------------------------------------- (dword dInitTime -999999) (dword dKeyStart dInitTime) (dword dKeyEnd dInitTime) (int iChan 0) (int iKey 0) (int iVel 0) (dword dTmpEnd 0) (dword dOverlap 0) (int iBendDest 0) (dword dDoBendRes 0) (word wDoTickRes 0) (word wBendLast 0) (long lBendRange 0) (long lBendOff 0) (dword dLastIndex 0) ; Calculate the pitch bend semitones (dword dBendSemi (/ 8192 wBendDepth ) ) ; Find the last index (forEachEvent (++ dLastIndex ) ) (forEachEvent (do ; Count down the index (-- dLastIndex) ; Only process notes (if (== Event.Kind NOTE) (do ; Calculate the note end (= dTmpEnd (+ Event.Time Note.Dur ) ) (if (|| (> Note.Key (+ iKey wBendDepth ) ) (< Note.Key (- iKey wBendDepth ) ) ) (do (= dKeyEnd (- Event.Time 1 ) ) ) ) ; New note if there's a gap or the bend is too far (if (|| (|| (>= Event.Time dKeyEnd ) (> Note.Key (+ iKey wBendDepth ) ) ) (< Note.Key (- iKey wBendDepth ) ) ) (do ; Inset the main note now (if (!= dKeyStart dInitTime ) (do ;(pause "Insert? " Note.Key " (" (- iKey wBendDepth ) ".." (+ iKey wBendDepth ) ") " ; Event.Time " (" dKeyStart ".." dKeyEnd ") " Note.Dur ) (insert dKeyStart iChan WHEEL 0 ) (insert dKeyStart iChan NOTE iKey iVel (- dKeyEnd dKeyStart) ) ) ) ; Update the info for a new note (= dKeyStart Event.Time) (= dKeyEnd dTmpEnd) (= iChan Event.Chan) (= iKey Note.Key) (= iVel Note.Vel) (= wBendLast 0) ) (do ; We match the last time ; Calculate the overlap length (= dOverlap (- dKeyEnd Event.Time ) ) ; Be sure the note extends the selection and isn't fully included ; already (if (> dTmpEnd dKeyEnd ) (do ; Calculate the bend depth (= iBendDest (* (- Note.Key iKey) dBendSemi) ) ; Calculate the bend range (= lBendRange (- iBendDest wBendLast ) ) ; Set the initial Tick Res (= wDoTickRes wTickRes ) (= dDoBendRes (+ wBendRes 1) ) ; Calculate the Tick Res (while (&& (> dDoBendRes wBendRes ) (> wDoTickRes 1) ) (do (-- wDoTickRes) (= dDoBendRes (/ (* lBendRange wDoTickRes ) dOverlap ) ) ) ) ;(pause "** Calc Bend: " wDoTickRes " @ " dDoBendRes " <= " wBendRes "? (range=" lBendRange ", dest=" iBendDest ", last=" wBendLast ", pri=" iKey ", note=" Note.Key ")" ) ; Insert the bends (= lBendOff wDoTickRes) (while (< lBendOff dOverlap) (do ;(pause "Do Bend: " (+ Event.Time lBendOff ) " val=" (+ (/ (* lBendRange lBendOff ) (- dKeyEnd dKeyStart) ) wBendLast ) ) (insert (+ Event.Time lBendOff ) iChan WHEEL (+ (/ (* lBendRange lBendOff ) dOverlap ) wBendLast ) ) ; Advance to the next pitch bend location (+= lBendOff wDoTickRes) ) ) ; Insert a spot on pitch bend on the last location (insert dKeyEnd iChan WHEEL iBendDest) ; Update the last bend (= wBendLast iBendDest) ; Extend the held note (= dKeyEnd dTmpEnd ) ) ) ) ) ; Delete the note regardless (delete) ; Inset the main note now (if (&& (!= dKeyStart dInitTime ) (== dLastIndex 0 ) ) (do (insert dKeyStart iChan WHEEL 0 ) (insert dKeyStart iChan NOTE iKey iVel (- dKeyEnd dKeyStart) ) ) ) ) ) ) ) )