Sunday, March 15, 2009

Tabula recompositoria: Amending/Improving Compositions Resembles Software ‘Refactoring’

W    hile composers were [in earlier Centuries] certainly able to work out music in their minds without recourse to writing, at some point the music had to assume a graphic form so that it could be preserved, transmitted, and performed. The evidence suggests that composers employed two different kinds of surfaces for writing down their music: (1) an ‘erasable tablet’ such as a slate that could be used many times and (2) paper. In the period [1450-1600 C.E.] we are considering, the pencil had not yet been invented, so writing on paper meant using pen and ink. Erasure could only be achieved by scraping the ink from the surface of the paper, often resulting in holes in the paper. Paper was therefore a ‘write-once’ medium. Despite the differences in the properties of the two kinds of writing surfaces, composers seem to have used them both in much the same way for all the written stages of a composition, from the earliest sketches to the final version.”
  —  Jessie Ann Owens, p. 74.
 Mozart K.581 Mvt.3 Trio II, mm. 1-12

    [20-sec clip, Umesh Shankar et al, UC Berkeley, 2003, Mozart, K. 581, Mvt. 3, ‘Trio II’, 0.4MB MP3]

M usical sourcecode (the musical score for a composition) is not sacrosanct: we honor the text in our reading of it, but we also take advantage of the interpretive latitude that it affords.

A nd any composer/arranger performs edits upon edits, to clean-up and perfect a work over a period of days to years. Or you create new versions to suit new and different purposes. It’s matter of what software developers would call ‘emergent design’.

Y ou may want to generate a piano reduction, exploding and imploding sectional structures, changing durations and meters, moving layers, manage instrument doublings, etc. .... Johnson’s book (link at bottom of the CMT post) has a good synopsis of these use-cases for MusicXML.

A lternatively, you may want to programmatically change your orchestration and timbres/sonorities, simplify or obfuscate/complexify the rhythms and horizontal relationships, de-risk the voice-leading to make it more natural to perform or sing, change the key to address the realities with regard to black-key biomechanics on keyboards or string-change biomechanics on stringed instruments. More and more use-cases for arrangers and composers!

S uzanne Clercx was the first to document the use of ‘erasable tablets’ by composers when in 1953 she published the discovery of a slate with six staves on each side [termed a ‘tabula compositoria’, ‘cartella’, ‘palierten schiffer stein’, ‘palimpsestus compositorius’, or ‘ardoyse’, depending on the country and century]. MusicXML is simply a latter-day tabula compositoria, that’s all.

W orking as I do on software in my day-job, I am impressed that compositions are homeomorphic to today’s object-oriented software and today’s data networks, including so-called ‘cloud’ computing or ‘software-as-a-service’ (‘SaaS’). All can be complex and difficult to manage.

I  suggest that the root of these problems lies in the complexity of the control and management planes—the orchestration and voice-leading and quasi-protocols coordinating the musical parts; the software and protocols coordinating the objects and network and distributed-services elements—and particularly the way the decision logic and the distributed-systems issues are inexorably intertwined.

I  am impressed that ‘refactoring’ the functionality can improve a composition just as it can improve a piece of software. There is no great profundity or earth-shattering novelty in such a view. It is just something that naturally occurs to any person who spends her/his days working on both software and music.

I  propose three key principles:

  • network-level objectives,
  • network-wide views, and
  • direct control
that I believe should underlie any [new/refactored] architecture, be it an object-oriented software system or a musical composition.

F ollowing these principles, I identify a design pattern that I call ‘4D’, after the architecture’s four planes: decision, dissemination, discovery, and data. A 4D architecture completely separates a network’s/composition’s decision logic from the compositional protocols and performance-practice/runtime protocols that govern the interactions among the network/musician elements.

I n creating a ‘4D’ design pattern, it’s important to remember that MusicXML score files do not represent ‘presentation-level’ primitives such as pages and staff-systems ... such details of formatting will change based on different paper and display sizes. But MusicXML does represent syntactic and semantic concepts of voices (parts) and synchronization among them, segmented into ‘movements’. In the MusicXML environment, in other words, formatting is handled separately from structure and semantics. The same applies for detailed interpretive performance information. Separate MusicXML supersets could be developed to represent individual printings and performances.

R efactoring of MusicXML can, of course, be done off-line—with generation of printed parts and score ahead of performance runtime. My MusicXML source for K.581 Mvt.3 Trio II is here. This (and several Beethoven string quartets) is what I have been testing my prototype MusicXML refactoring engine with this week.

O r the refactoring of MusicXML can be done in realtime—with refactored parts rendered with streaming MusicXML interpreter to be sight-read by the performers, with (I suppose) considerable surprise for artists who are deeply familiar with the original texts—and for listeners as well.

T he realtime MusicXML refactoring works okay so long as the lead-time for the canonical refactoring service is at least one measure ahead of where the musicians are playing. That, to me, seems to be about the minimum for proper scansion and so that phrasing and other effects are not too disrupted.

O f course, this depends on the meter and tempo and the prevailing complexity—things that are making cognitive demands on each performer. If a streaming realtime refactoring is too elaborate, then the aesthetics (and the fun) will undoubtedly suffer.

B eyond this, in principle I suppose one could mike each member of the ensemble and do analog-to-digital conversion of each signal—perform digital signal processing (DSP) and pattern-recognition to impute pitch and interval and rhythm and dynamics deviations from the score—and then use those imputed deviations to drive a refactoring server, to further permute the parts or effect meta-alteration of the downstream score. That’s beyond anything that I’m prepared to attempt right now, but CMT readers with enough studio gear and computational resources may be interested to try it.

    K. 581 Clarinet Quintet (‘Stadler’ Quintet) [1789]
  1. Allegro (A major)
  2. Larghetto (D major)
  3. Menuetto (A major), Trio I (A minor), Trio II (A major)
  4. Allegretto con variazioni (A major)
T he clarinet predominates in K.581 as ‘primus inter pares’ (‘first amongst equals’) as Alfred Einstein once wrote, yet the parts’ roles are more discursive and autonomous than they would be if this were a ‘concertante’ form where the soloist rules.

M y simple experiments initially have dwelt on Mozart’s K. 581, primarily because it is straightforward and familiar. Its interpretive history and performance practice are pretty transparent and well-understood. I wondered, “What if we refactored the Trio II, to render it as an exuberant/obstinate ‘recovery’ from the A-minor Trio I of the 3rd movement?” Okay. Here’s what I get with my canonical XML parser—scanning for and phrase and repeat boundaries in the tokenized MusicXML source.

 Mozart K.581 Mvt.3 Trio II, mm. 1-12
becomes  

 Mozart K.581 Mvt.3 Trio II, mm. 1-12
B asically, my “light” refactoring of K.581 treats every ‘repeat’ structure as a MusicXML ‘class’. You play it through the first time as originally written—that’s the first ‘invocation’ or programmatic ‘call’ to the ‘repeat’ class method with the contents of the bars between the repeat-delimiters as an argument. When you get to the end of the first time through, the method in the ‘main’ class (call it ‘A’) bumps an ‘excursion counter’ and calls the repeat class (call it ‘B’)—with a canonical refactoring argument to dynamically alter the articulation syntax in each part, according to rules I created for my refactoring engine to use.

 DSM Refactoring Engine Architecture
B ut the simple as-written MusicXML source then involves a ‘cycle’ between the ‘main’ class ‘A’ and the ‘repeat’ class ‘B’. If this were a software refactoring, generally we try to eradicate cycles, to localize and manage features and dependencies and to improve reliability and for other reasons. How about doing the same thing in MusicXML, with similar justification? Sure...

 DSM – removing cycles with dependency-inversion DIP
W hat my refactoring of the score does is this:
  • Extract a ‘repeat interface’ BI class from class B. BI contains the methods of class B that are needed by A, to implement the articulations (or other canonical changes that propagate through the quintet’s 5 parts) that we want to be associated with the repeats. The new, refactored class B implements BI.
  • Set all references in class A that aren’t required for object generation from B to BI.
A  simple ‘toy’ example, to be sure, but it is non-trivial—non-trivial in terms of the parser and its embedded logic, and non-trivial in terms of the expressive effects that this has.

I n the end, what we get in this example is a canonical cascading propagation of accents, tenutos, and decrescendos—explicitly amending the expressive sense of the K.581 Trio II. Cool. It works. And it suggests how we could implement other, fancier refactorings and canonical edits.

 Mozart K.581 Mvt.3 Trio II, mm. 1-12 repeat, after canonical XML mark-up with accents, tenutos, diminuendos
M y particular example refactoring of the MusicXML sourcecode for K.581 Trio II is directly analogous to what in the Roock-Lippert software refactoring book is called ‘dependency inversion’ or DIP (see. pp.130-43). Again, I was only exploring here what can be done with current-version MusicXML and refactoring techniques operating on music scores; I am not saying that anything wonderful has been done to K.581 with this attempt. I am only saying that it is interesting, and that it works, and that it may be of interest to you as a composer/arranger or as a music theorist. For CMT readers who are performing artists and avid users of PC-based digital music readers (like Hugh Sung or Nicholas Kitchen) maybe this stuff has interest as well.

T o me, the effort so far seems worthwhile. Yes, MusicXML is not ideal. It’s not a ‘causal encoding format’ and therefore and commands are [must be] used to encode single parts with multiple staves or multiple voices—and so on. While not ideal, MusicXML is the reigning standard that has market-share; it is the ‘bandwagon’ to jump on.

I t’s possible that these refactoring explorations will lead to new MusicXML enhancement-requests or new requirements-specifications for future versions of MusicXML. I’d welcome dialogue with you if you have observations or concerns in that regard. Please email me or comment here if you like. Thanks!

B    rian Foote suggested the name [‘speculative generality’] for a smell to which we are very sensitive. You get this smell when people say, ‘Oh, I think we need the ability to do this kind of thing someday’ and consequently specify all sorts of ‘hooks’ and special-case requirements—to handle things that aren’t in fact required. The result is often harder to understand and maintain. If all this machinery were being used, it would be worth it. But if it isn’t being used, it isn’t [worth it]. The extra machinery [to provide abstract generality] just gets in the way [of clean, effective, testable, maintainable source-code and non-defective solutions] ... Any structure or class that isn’t doing enough to pay for itself should be eliminated ...”
  —  Martin Fowler, p. 83.

A   ndrew Monk and others at University of York in the U.K. It is useful to think about software usability problems as forming a hierarchy with each successive level in the hierarchy requiring more contextualized knowledge. By contextualized knowledge we mean knowledge about a particular user and the goals and priorities which are relevant when the problems are identified. An example of a problem at the bottom of the hierarchy is a user having difficulty in correcting typographical errors because of inadequate delete facilities. This may be a quite general problem independent of the particular user and the task being performed. An example of a problem near the top of the hierarchy is the difficulty that a user might have in understanding how to carry out a database search task on some new system. The problem arises from the user's experience with the system, with other systems and with the task it is being used for and so depends very much on the context. Such problems often have a large impact on usability. By definition, these problems require information about the user's knowledge and beliefs in order to diagnose their cause, and to recommend the changes needed. Work at University of York has attempted to develop methods which elicit increasing amounts of context-sensitive data which can be used to diagnose problems at successively higher levels of this hierarchy and have identified four diagnostic levels. At the first level are problems that can be identified from system logs recorded from unknown users during free use. An approach was developed which identifies problems by isolating redundant user input. These problems can be diagnosed with minimal user-specific or task-specific information. A second level involved the analysis of log data from users completing tasks which have been set by the evaluator of the system. When these tasks are well constrained the system evaluator can use them to gain partial information about the users’ plans and goals when problems occur. This method is not always adequate since strategic differences amongst subjects can make plans and goals difficult to infer. A third level used verbal protocol methods to elicit users’ plans and goals directly. Two methods have been used re-enactment. Re-enactment proved particularly useful for diagnosing problems that could not be diagnosed from log data alone. In this method the user and evaluator discuss problems that the user had experienced during previous sessions.

Techniques that allow for more abstraction
  • Encapsulate Field - force code to access the field with getter and setter methods
  • Generalize Type - create more general types to allow for more code sharing
  • Replace type-checking code with State/Strategy
  • Replace conditional with polymorphism
Techniques for breaking code apart into more logical pieces
  • Extract Method, to turn part of a larger method into a new method. By breaking down code in smaller pieces, it is more easily understandable. This is also applicable to functions
  • Extract Class moves part of the code from an existing class into a new class
Techniques for improving names and location of code
  • Move Method or Move Field - move to a more appropriate Class or source file
  • Rename Method or Rename Field - changing the name into a new one that better reveals its purpose
  • Pull Up - in object-oriented programming (OOP), move to a superclass
  • Push Down - in OOP, move to a subclass
  • Shotgun surgery - in OOP, when a single change affects many classes—all affected code is kept in a single class, so that the change is made only in one place.”


No comments:

Post a Comment