5

Let me preface by saying that knowing some elementary music theory and music notation may be helpful in grasping the problem at hand.

I'm currently building a Music Notation and Tablature Editor (in Javascript). But I've come to a point where the core parts of the program are more or less there. All functionality I plan to add at this point will really build off the foundation that I've created. As a result, I want to refactor to really solidify my code.

I'm using an API called VexFlow to render notation. Basically I pass the parts of the editor's state to VexFlow to build the graphical representation of the score.

(EDIT: The following diagram is outdated as I began to rework the structure, please see the update at the bottom of the post for the new diagram) Here is a rough and stripped down UML diagram showing you the outline of my program:

In essence, a Part has many Measures which has many Notes which has many NoteItems (yes, this is semantically weird, as a chord is represented as a Note with multiple NoteItems, individual pitches or fret positions). All of the relationships are bi-directional.

There are a few problems with my design because my Measure class contains the majority of the entire application view logic.

  1. The class holds the data about all VexFlow objects (the graphical representation of the score). It contains the graphical Staff object and the graphical notes. (Shouldn't these be placed somewhere else in the program?)

  2. While VexFlowFactory deals with actual creation (and some processing) of most of the VexFlow objects, Measure still "directs" the creation of all the objects and what order they are supposed to be created in for both the VexFlowStaff and VexFlowNotes.

I'm not looking for a specific answer as you'd need a much deeper understanding of my code. Just a general direction to go in.

Here's a thought I had, create an MeasureView/NoteView/PartView classes that contains the basic VexFlow objects for each class in addition to any extraneous logic for it's creation? but where would these views be contained? Do I create a ScoreView that is a parallel graphical representation of everything? So that ScoreView.render() would cascade down PartView and call render for each PartView and casade down into each MeasureView, etc. Again, I just have no idea what direction to go in. The more I think about it, the more ways to go seem to pop into my head.

I tried to be as concise and simplistic as possible while still getting my problem across. Please feel free to ask me any questions if anything is unclear. It's quite a struggle trying to dumb down a complicated problem to its core parts.

UPDATE:--------------------------------------------------------------------------

So I went ahead and started refactoring with my idea from above. Here is an updated Diagram

@JW01 I understand why bi-directional relationships are potentially bad (difficult to maintain, increased complexity), however, I do believe the increased flexibility is worthwhile and I have implemented it in a maintainable way. Each notation "container" (Score, Part, Measure, Note - all contain children of some sort) inherits from Container (this relationship isn't shown in the diagram because the program I use is shitty and it looks super messy). The Container.addItem(item) method mixes in the Traversable class methods and parent property to the item added.

To me this seemed pretty clever and easy to understand. Basically, any item that gets put in a container gets extended functionality. As a mentioned in a previous comment, I may want to pull out a nice music data API out of this, which would allow for "plug-ins" to be easily created. Traversing in both directions would be particularly helpful for functionality like that. Plus these classes are small and maintainable, and unlikely to change.

However, I understand that the less things that depend on these relationships the better. So it's definitely best if I isolate this traversing functionality for use with the Selection module.

I'm noticing that my View classes are doing a lot now. Building, rendering, formatting and handling click. It would probably be wise to factor out a an abstract View module composed of a ViewBuilder, ViewFormatter, ViewRenderer, ClickBehavior?

6
  • "All of the relationships are bi-directional". Wow. That really scares me. I would try and eradicate all bi-directional relationships and that would just make things so much simpler. ...... Also Have you considered adding some kind of "Song", "Timeline" or "Track" entities upon which these things can hang?
    – JW01
    Nov 18, 2012 at 13:43
  • p.s - I like what you are trying to achieve with your project - it sounds ambitous, but really cool if you can pull it off.
    – JW01
    Nov 18, 2012 at 13:47
  • @JW01 The thing is I'm not sure how to solve certain notation logic without it. For instance, a note needs to know about all previous notes in a measure as well as the measure's key to determine their accidentals. And yes, I have a "Editor" entity which will contain the entire app. Probably will end up factoring in an "Instance" class so you could swap between multiple scores. Nov 18, 2012 at 23:16
  • Re: " I'm not sure how to solve certain notation logic without [bi-directional relationships]." I think you should loosen up and just be very liberal about creating new classes (on paper). Start making up new words to describe those 'things' that would have no place if you got rid of a bi-directional relationship. For example: "a note needs to know about .xyz.. to determine their accidentals". Why not put all that stuff into an "AccidentalsMap". Purge those bi-directionals breaking down everything so it has just one responsibility. Add new classes to glue them together, then prune to taste.
    – JW01
    Nov 19, 2012 at 0:14
  • I think two books might be useful to you: 'Design Patterns, by the Gof' and Domain Driven Design by Eric Evans.
    – JW01
    Nov 19, 2012 at 0:17

1 Answer 1

2

It looks like you have taken for granted that your layout of classes should be modeled on how humans percieve sheet music. While we look at a score and think of it as divided into nice manageable chunks called measures, a computer doesn't necessarily need to do that. It can simply percieve the music as one long stream of notes, with some lines put at equal (by some measure) intervals between/among the notes.

When I looked at your layout the first thing that popped into my head was, how does it deal with two notes spanning two measures that are tied?

You could think of the measure bars as a kind of "note", from the point of view of the code. Since the measures are all generally equal and their positions can be mostly implied from the rest of the music, you could place a "note" to mark the beginning of the first measure and declare the time signature, and the subsequence measures can be calculated on-the-fly from there. Then if you want to do anything fancy like place an out-of-signature measure (a measure with 2 beats in a 4/4 song, for example) or a change of signature, you place another "note" where that event happens/begins.

Also, I (mostly) know how to read sheet music but haven't used any interactive score editing software, so I don't have much basis on which to make inferences on the suitability of any particular approach. Consider attempting to get some experience using existing reputable score editing software, for inspiration. Also be prepared to throw away your first prototype - it may be that the only way to find out what you are doing wrong is to do it wrong first :)

3
  • You are right, however, I may want to separate the layers into a public API at some point. So having similar semantics to the traditional model was something I was trying to maintain. Also, for ties I have a Duration class which deals with all duration modification (dots, tuplets, ties, etc) Nov 17, 2012 at 5:33
  • How about to define notes by their perceived length, instead of notational length. Think of piano roll editors in DAWs, where you can draw notes arbitrarily in the granularity defined by the user in relation to a base grid defined by the current tempo. Nov 19, 2012 at 10:12
  • @CyrilSilverman you may find the works of Conlon Nancarrow for player piano interesting - consider "Study No. 40, "Transcendental" - Canon e/pi; Nancarrow's first use of irrational tempo relationships" or "Study No. 22 - Canon 1%/1.5%/2.25%; an acceleration canon in which one voice speeds up at 1% per note, another at 1.5%, and so on; a near-palindrome"
    – user40980
    Apr 30, 2013 at 0:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.