/usr/share/denemo/actions/denemo-modules/abstractionmovement.scm is in denemo-data 2.0.2-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | (use-modules (srfi srfi-1)) ; List library
#!(define-module (actions denemo-modules abstractionmovement3)
#:export (
CreateAbstractionMovement
PasteAbstractionMovement
SearchForSimultaneousInterval
SearchForSimultaneousIntervalFromBase
SearchForConsecutiveIntervalProgression
)) !#
; This is the prototype of a system that creates an abstraction of the movement.
; In the end there is a list of list of musobj-objects which are vertically aligned. Different durations are equalized out by inserting repeated notes.
; Only notes, chords and rests are saved currently. Duration is ignored. The start-tick value is used to check if notes need repetition.
; Uses ANS (Abstract Note System, defined in actions/ans.scm)
; Uses musobj (A structure for musical objects, defined in actions/denemo.scm)
; Currently all functions are wrapped in a single big program. This way they do not pollute the global namespace, but they are called each time an Abstraction-Movement gets created, which is only needed after a score-change.
; Functions in this file.
;;(CreateListStaffTicksInMeasures)
;;(ListStaffTicksInMeasures->StaffOffsetTickList listy)
;;(insert-into-list listy position what )
;;(duplicate-item-in-list listy position)
;;(list-equalizer! what . lists)
;;(insert-deep movement staffnumber position what)
;;(duplicate-deep movement staffnumber position)
;;(GetStartTickMinimum listy)
;;(Abstraction::CreateMusObj)
;;(createFinalList)
;;(fill-with-redundancy! movement)
;;(CreateAbstractionMovement)
(define mainBlock #f)(define notMinimum #f) ;;; RTS - I don't know what these are, but for some reason they appear below in a context where they need to be defined.
(define (CreateAbstractionMovement)
;Nearly any of the following functions are just crude prototypes. They are crude because they work with lists instead of a good data type like an red/black-tree. We need a data type here that is a list of list or allows simultanious items, allows refering to a single item and allows inserting. The result here is that all is done with list, appending, splitting, copying complete lists around instead of micro-changes.
;Create a list which holds a tick-sum for each measure. It counts the actual length, even if the measure is overful or underful. It assumes 100% fill for an empty measure.
(define (CreateListStaffTicksInMeasures)
(define return #f)
(d-PushPosition)
(d-MoveToBeginning)
(set! return
(let loop ((listy (list #f)) )
(if (EmptyMeasure?)
(append! listy (list (duration::GetWholeMeasureInTicks)))
(append! listy (list (GetMeasureTicks))))
(if (d-MoveToMeasureRight)
(loop listy)
(list-tail listy 1))))
(d-PopPosition)
return)
;Wants a list created by (CreateListStaffTicksInMeasures).
;returns the tick offset for each measure. Each list position is equal to a measure number. There is no measure 0, so its #f
;The offset is each measure, added to all measure before it.
(define (ListStaffTicksInMeasures->StaffOffsetTickList listy)
(define lstlength (length listy))
(let loop ((counter 0)(return (list #f)))
(if (= counter (1+ lstlength))
return
(loop (1+ counter) (append! return (list (apply + 0 (list-head listy counter)))))))) ; list-head excludes the given position. This means if you give a measure number this measure will be included. Same counting as (lenght list)
;Create a list of OffsetTickLists. Each list is a staff, which is a staff of measures.
;All countings from 1. Positions 0 are #f for both the outer and all inner lists.
(define (CreateOffsetTickListMovement)
(define return (list #f))
(d-PushPosition)
(d-MoveToMovementBeginning)
(let loop ()
(append! return (list (ListStaffTicksInMeasures->StaffOffsetTickList (CreateListStaffTicksInMeasures))))
(if (and (d-MoveToStaffDown) (d-MoveToBeginning))
(loop)))
(d-PopPosition)
return)
;Very slow version of insert-into-list
(define (insert-into-list listy position what )
(append
(list-head listy position)
(copy-tree (list what))
(list-tail listy position)))
;Variant of insert-into-list that duplicates an item
(define (duplicate-item-in-list listy position)
(append
(list-head listy position)
(copy-tree (list (list-ref listy position)))
(list-tail listy position)
))
;Make all lists equal in length by appending 'what' to their tails
;;Usage with a list of lists: (apply list-equalizer! #f listoflists)
(define (list-equalizer! what . lists)
(define longest (reduce max 0 (map length lists)))
(define (extend listy)
(append! listy (copy-tree (make-list (- longest (length listy)) what)))) ; compare given list with longest and fill with 'what'
(map extend lists)
)
;Nearly any of the following functions are just crude prototypes. They are crude because they work with lists instead of a good data type like an red/black-tree. We need a data type here that is a list of list or allows simultanious items, allows refering to a single item and allows inserting. The result here is that all is done with list, appending, splitting, copying complete lists around instead of micro-changes.
;A slow version to change one list in the list of lists. Replaces the whole inner list with a new one instead of modifying the original one.
(define (insert-deep movement staffnumber position what)
(list-set!
movement ; outer list, the movement
staffnumber
(insert-into-list (list-ref movement staffnumber) position what))) ; replaced with a new staff
;Variant of insert-deep to duplicate one item in an inner list. Like insert-deep its slow.
(define (duplicate-deep movement staffnumber position)
(list-set!
movement ; outer list, the movement
staffnumber
(duplicate-item-in-list (list-ref movement staffnumber) position))) ; create new list to replace the old one on the same position
;; Find the minimum value in a list of numbers. No check if only numbers!
(define (GetStartTickMinimum listy)
(reduce min 0 (map musobj.start listy)))
;; Create a MusObj but directly change its start-tick value to include the offset generated by (CreateOffsetTickListMovement)
(define (Abstraction::CreateMusObj)
(define (getCurrentOffset musobj)
(list-ref (list-ref OffsetTickList (musobj.staff musobj)) (musobj.measure musobj))) ; inner list refs gets the staff as list from OffsetTickList, outer list-ref the measure
(define musobj (CreateMusObj))
(set!musobj.start musobj (+ (getCurrentOffset musobj) (musobj.start musobj)))
musobj)
;; creates a list of list of musobj(ects). Each first level list is one staff.
;; All append! need an additional (list) because append merges only with lists.
(define (createFinalList)
(define (staffLoop) ;; Subproc to gather the information for one staff and return a list.
(define stafflist (list #f))
(if (or (Music?) (MeasureEmpty?)) ; if the first object is already a music item start right now. This prevents also a crash if the staff starts with a directive or else.
(append! stafflist (list (Abstraction::CreateMusObj))))
(let loop ()
(if (d-MoveCursorRight)
(begin
(if (or (Music?) (MeasureEmpty?))
(append! stafflist (list (Abstraction::CreateMusObj))))
(loop))
(list-tail stafflist 1)))) ; Return list minus initial #f
;;; Body
(d-MoveToMovementBeginning)
(let loop ((final_list (list #f )) )
(append! final_list (list (staffLoop))) ; First Staff is guaranteed to work, after first staff test for more staffs:
(if (and (d-MoveToStaffDown) (d-MoveToBeginning))
(loop final_list)
(list-tail final_list 1)) ; If there is no staff, return the final list minus the initial #f
); Final Loop end
); createFinalList end
(define (fill-with-redundancy! movement)
; prepare variables-
(define (ansrest? musobject)
(and
(equal? (list +inf.0) (musobj.pitch musobject))
(not (inf? (musobj.start musobject)))
))
(define positioncounter 0)
(define staffcounter 0)
(define MusObjectsOfCurrentPosition #f) ; All musobj of list-ref positioncounter
(define minimum #f) ;The current lowest start-tick
; Two sub-procs
(define (insertRestBeforeFalse musobject)
(if (not musobject) ; if the object is a #f fill in a infinity rest before/to the current position
(insert-deep movement staffcounter positioncounter (make-musobj 'pitch (list +inf.0) 'movement #f 'staff #f 'measure #f 'horizontal #f 'metricalp #f 'start +inf.0 'duration #f 'baseduration #f 'dots #f)))
(set!musobj.pitch (list-ref (list-ref movement staffcounter) positioncounter) (musobj.pitch (list-ref (list-ref movement staffcounter) (- positioncounter 1))))
(set! staffcounter (+ staffcounter 1))) ; the next for-each iteration needs another staff
(define (checkAndChange listy) ; the main program to check and alter MusObjectsOfCurrentPosition
(if (ansrest? (list-ref listy positioncounter)) ; rests need to gain a real pitch
(if (= 0 positioncounter)
(set!musobj.pitch (list-ref listy positioncounter) (musobj.pitch (list-ref listy (+ positioncounter 1)))) ;or next object, for a leading rest in a staff
(set!musobj.pitch (list-ref listy positioncounter) (musobj.pitch (list-ref listy (- positioncounter 1)))))) ;should take the pitch of the item before it
(if (= (musobj.start (list-ref listy positioncounter)) minimum) ; starts on minimum tick?
(set! staffcounter (+ staffcounter 1)) ;Current musobj starts at minimum tick, good.
(begin notMinimum
(if (not (inf? (musobj.start (list-ref listy positioncounter)))) ; if infinity-rest just change the tick
(begin
(duplicate-deep movement staffcounter (- positioncounter 1)) ; else copy a new musobj
(set!musobj.start (list-ref listy (- positioncounter 1)) minimum)) ; change the created item to min start-tick
(begin
(set!musobj.pitch (list-ref listy positioncounter) (musobj.pitch (list-ref listy (- positioncounter 1)))) ; copy pitch
(set!musobj.movement (list-ref listy positioncounter) (musobj.movement (list-ref listy (- positioncounter 1))))
(set!musobj.staff (list-ref listy positioncounter) (musobj.staff (list-ref listy (- positioncounter 1))))
(set!musobj.measure (list-ref listy positioncounter) (musobj.measure (list-ref listy (- positioncounter 1))))
(set!musobj.horizontal (list-ref listy positioncounter) (musobj.horizontal (list-ref listy (- positioncounter 1))))
(set!musobj.metricalp (list-ref listy positioncounter) (musobj.metricalp (list-ref listy (- positioncounter 1))))
(set!musobj.start (list-ref listy positioncounter) minimum)
)
); if end
(set! staffcounter (+ staffcounter 1))
)); begin notMinimum & if minimum
); checkAndChange subProc.
(let loop ()
(set! staffcounter 0); reset staffcounter before for-each, which loops through staffs
(set! MusObjectsOfCurrentPosition (map (lambda (lst) (list-ref lst positioncounter)) movement))
(if (member #f MusObjectsOfCurrentPosition)
(if (every not MusObjectsOfCurrentPosition) ; If all are #f the movement is at its end
"Abstraction Movement ready" ; The End. "movement" is ready.
(begin
(for-each insertRestBeforeFalse MusObjectsOfCurrentPosition) (loop))) ;fill the tail with an infinity rest for each #f on current position.
(begin mainBlock ; no #f on current position
(set! minimum (GetStartTickMinimum MusObjectsOfCurrentPosition))
(for-each checkAndChange movement) ; always feed with the current state of the whole movement
(set! positioncounter (1+ positioncounter))
(loop)
)); if member #f? & begin mainBlock
)); fill-with-redundancy! end
; Create the abstraction movement in multiple steps. They are all desctructive.
;;0 Create a helper list to with the tick offset for each measure.
;;1 parse everything, save the music as musobj. Notes, Chords, Rests
;;2 make all length equal by adding rests in the infinity to the ends.
;;3 one final #f to all staffs. The end is reached when all position return #f instead of a musobj
;;4 check if all start-ticks are the same, if not duplicate notes to fill the gaps
;;5 delete the tailing #f from the movement
(define OffsetTickList #f)
(define movement #f)
(set! OffsetTickList (CreateOffsetTickListMovement))
(set! movement (createFinalList))
(apply list-equalizer! (make-musobj 'pitch (list +inf.0) 'movement #f 'staff #f 'measure #f 'horizontal #f 'metricalp #f 'start +inf.0 'duration #f 'baseduration #f 'dots #f) movement)
(map (lambda (lst) (append! lst (list #f))) movement)
(fill-with-redundancy! movement)
(set! movement (map (lambda (lst) (drop-right lst 1)) movement))
movement)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Functions that use the abstractionmovement;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;PasteAbstractionMovement creates a new Denemo-tab and visualises the contents of an abstractionmovement there.
(define (PasteAbstractionMovement abstractionmovement)
; For each staff (primary list in abstractionmovement)
; do inserting the pitch for every (for-each) object
(d-NewWindow) ; creates a new movement with the same number of staffs
(d-MoveToMovementBeginning)
(for-each (lambda (staff)
(d-AddAfter)
(d-MoveToBeginning)
(for-each (lambda (object)
(if (not object)
(d-MoveToStaffDown) ; move one down for the next iteration
(ANS::InsertNotes (musobj.pitch object) 0 384)))
staff))
abstractionmovement))
;ApplyTestsToAbstractionMovementPositions takes functions and applies each to a value and returns a list of returnvalues.
;;Used in MapToAbstractionMovement
(define (ApplyTestsToAbstractionMovementPositions previous current next . functions)
(filter (lambda (x) (not (not x)))
(concatenate (concatenate
(map (lambda (proc) (call-with-values (lambda () (proc previous current next)) list))
functions)))))
;MapToAbstractionMovement wants an abstractionsmovement and functions
;; Each function will be applied to a vertical position (all notes which sound at the same time). The return values will be gathered in a list and then it advcances to the next vertical position until the end of the abstractionmovement.
;; Compatible functions must accept three parameters: Three lists of MusObjs: All notes from the previous, current and next position. Even if the functions discard one or two of these position internaly.
;; Compatible functions must return a list which members are either #f or pairs (cons 'tag data-you-like). In the end any #f will be deleted automatically. "Data-you-like" is mainly there to let Denemo find the place where the error is so you better include musobjs there.
(define (MapToAbstractionMovement abstractmovement . functions)
(define return #f)
(set! abstractmovement (apply map list abstractmovement)) ; sync the voices vertically "in chords" by creating new list from each of the sublists. They all have the same length.
(set! return (map ; this needs srfi-1 map which handles unequal list lengths
(lambda (previous current next) (apply ApplyTestsToAbstractionMovementPositions previous current next functions))
(cons #f abstractmovement)
abstractmovement
(append (cdr abstractmovement) (list (last abstractmovement)))))
(filter (lambda (x) (not (null? x))) (concatenate return)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Tests for MapToAbstractionMovement;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Basic functions
;; "AM::CapitalCamelCase"
;Find consecutive interval progressions in two pairs of notes
(define* (AM::TestConsecutiveIntervalProgression previous current next proc interval tag #:optional (staffs #f))
(map
(lambda (pair-current pair-next)
(if (proc
(car (musobj.pitch (cdr pair-current))) ;low1
(car (musobj.pitch (car pair-current))) ;high1
(car (musobj.pitch (cdr pair-next))) ;low2
(car (musobj.pitch (car pair-next))) ;high1
interval)
(cons tag (cons pair-current pair-next))
#f))
(if staffs ;its possible to only check certain voices which may be in the optional staffs var.
(GetUniquePairs (map (lambda (x) (list-ref current x)) staffs))
(GetUniquePairs current))
(if staffs
(GetUniquePairs (map (lambda (x) (list-ref next x)) staffs))
(GetUniquePairs next))))
(define (AM::TestSimultaneousIntervalFromBaseMetricalMain previos current next interval tag)
(define pairlist (GetUniquePairsFilterLowest current MusObj::minPitch))
(map
(lambda (pair)
(if (and (duration::MetricalMain? (musobj.metricalp (car pair))) (duration::MetricalMain? (musobj.metricalp (cdr pair))) (= interval (MusObj::GetInterval (car pair) (cdr pair)))) ; if interval and both notes are on a metrical main position
(cons tag pair)
#f))
pairlist))
(define (AM::TestSimultaneousIntervalFromBaseMetricalFirst previos current next interval tag)
(define pairlist (GetUniquePairsFilterLowest current MusObj::minPitch))
(map
(lambda (pair)
(if (and (= 1 (musobj.metricalp (car pair))) (= 1 (musobj.metricalp (cdr pair))) (= interval (MusObj::GetInterval (car pair) (cdr pair)))) ; if interval and both notes are on the first metrical position
(cons tag pair)
#f))
pairlist))
;AM::GenerateStaffList converts numbers and special symbols to a list of numbers which represents which of the staffs should be used for an AM::test
;;needs to know how many staffs there are and as many parameters as needed
;;parameters are plain numbers or special symbols
;; 'last the last staff
;; 'first the first staff, but better use 0.
;; '-3-5 from 3 to 5. Note the leading dash.
(define (AM::GenerateStaffList staffcount . parameter)
(set! parameter (delete-duplicates parameter))
parameter)
;Real Tests that can be used as MapToAbstractionMovement functions
;;"AM::lowerCamelCase"
;Just display all "chords", made of musobj
;;Debug only. Has a wrong return format.
(define (AM::display previous current next)
(list current))
;Display all "chords" as lilypond pitches
;;Debug only. Has a wrong return format.
(define (AM::displayLilypond previous current next)
(map (lambda (x)
(ANS::Ans2Ly (car (musobj.pitch x))))
current))
(define (AM::consecutive5th previous current next)
(AM::TestConsecutiveIntervalProgression previous current next ANS::ConsecutiveOpen? 1 'consecutive5th))
(define (AM::consecutive8th previous current next)
(AM::TestConsecutiveIntervalProgression previous current next ANS::ConsecutiveOpen? 0 'consecutive8th))
(define (AM::crossed5th previous current next)
(AM::TestConsecutiveIntervalProgression previous current next ANS::ConsecutiveCrossed? 1 'crossed5th))
(define (AM::crossed8th previous current next)
(AM::TestConsecutiveIntervalProgression previous current next ANS::ConsecutiveCrossed? 0 'crossed8th))
(define (AM::hidden5th previous current next)
(AM::TestConsecutiveIntervalProgression previous current next ANS::ConsecutiveHidden? 1 'hidden5th))
(define (AM::hidden8th previous current next)
(AM::TestConsecutiveIntervalProgression previous current next ANS::ConsecutiveHidden? 0 'hidden8th))
(define (AM::anti5th previous current next)
(AM::TestConsecutiveIntervalProgression previous current next ANS::ConsecutiveAnti? 1 'anti5th))
(define (AM::anti8th previous current next)
(AM::TestConsecutiveIntervalProgression previous current next ANS::ConsecutiveAnti? 0 'anti8th))
(define (AM::hiddencrossed5th previous current next)
(AM::TestConsecutiveIntervalProgression previous current next ANS::ConsecutiveHiddenCrossed? 1 'hiddencrossed5th))
(define (AM::hiddencrossed8th previous current next)
(AM::TestConsecutiveIntervalProgression previous current next ANS::ConsecutiveHiddenCrossed? 0 'hiddencrossed8th))
(define (AM::simultaneousFromBaseMetricalMain4th previos current next)
(AM::TestSimultaneousIntervalFromBaseMetricalMain previos current next -1 'simultaneousBaseMain4th))
(define (AM::simultaneousFromBaseMetricalFirst8th previos current next)
(AM::TestSimultaneousIntervalFromBaseMetricalFirst previos current next 0 'simultaneousBaseFirst8th))
(define (AM::simultaneousFromBaseMetricalFirst5th previos current next)
(AM::TestSimultaneousIntervalFromBaseMetricalFirst previos current next 1 'simultaneousBaseFirst5th))
;This function is not a test itself but generates a test-function
(define (AM::generateHidden5th stafflist)
(lambda (previous current next) (AM::TestConsecutiveIntervalProgression previous current next ANS::ConsecutiveHidden? 1 'hidden5th stafflist)))
;This function is not a test itself but generates a test-function
(define (AM::generateHidden8thstaffs stafflist)
(lambda (previous current next) (AM::TestConsecutiveIntervalProgression previous current next ANS::ConsecutiveHidden? 0 'hidden8th stafflist)))
|