This is a follow-up article for From Idea to Sketch.

Framework

The first thing to check was a good framework to start with. We decided to go with Javascript/Typescript as Open Sheet Music Display is done in that language as well. And for easily setting up a demo we chose React which is an open source frontend library developed and maintained by Facebook. We also wanted to have a React demo for our renderer a while back so this was on the wish list anyways.  The graphics and visual eye candy will be done using material-UI and Bootstrap which are also open-source projects dealing with the CSS and layouting. This is a heavy burden taken away from us so we can focus on dealing with the code itself.

OSME Explorer

The code we want to write now based on all the previous investigations and discussions is a viewer that shows the structure of our practices and let the user explore it. It will be a very basic UI layer that only shows a list view holding the practice entries and the percentages done by the user. We also show if the best result has already unlocked a new practice.

The Code Behind

We start out by setting our code to be like we discussed in the earlier blog posts:

  const ons = new PracticeContainer(“one-note-samba.mxl”);
  ons.Title = “One Note Samba”;
  ons.Description = “The very beginning”
  ons.AddGoal(goal);
  ons.AddResult(passedResult);

As we see, our container has a constructor that wants the MusicXML file and we can set a title, a description and a goal afterwards. While implementing, we discovered that we need to match our goal against something. So for that reason we have another class that has the same methods (at the moment) as the goal and can be used as a matcher.

The new PracticeContainer class looks like so:

export class PracticeContainer {
  public Goals: Goal[] = [];
  public Results: Result[] = [];
  public MusicXml: string;
  public Title: string = “”;
  public Description: string = “”;}

We have goals to meet, some description and the results for this very practice. So how do we check if we actually passed our practice?

public Passed() {
  if(this.Results.length === 0 || this.Goals.length === 0) {
      return false;
  }
  const bestResult: Result = this.SortedResults[this.SortedResults.length-1];
  return this.Goals.map(g => g.Passed(bestResult)).every(b => b === true);
}

This method checks all our goals using a method goal.Passed() on the last element in our sorted results list. So what does that mean? We sort our results with the logic that the best result is the last in the list. That best run is fed into our goal which verifies itself and returns true. The every() function on the resulting logical array checks every item in the list for true and returns true if all are true.

Let’s have a closer look at our goal class:

export class Goal {
  Speed: SpeedConstraint = new SpeedConstraint();  
  PitchAccuracy: PitchAccuracy = new PitchAccuracy();
  TimeAccuracy: TimeAccuracy = new TimeAccuracy();
  ChordSimultaneity: ChordSimultaneity = new ChordSimultaneity([1, 2, 3, 4]);
  CustomMatch: IConstraint | undefined;
  Mandatory: Boolean = false;
 
  public Passed(practiceResult: Result) {
      const customPass: Boolean = this.CustomMatch ? this.CustomMatch.Passed(practiceResult.CustomMatch) : true;
      const speedPass: Boolean = this.Speed.Passed(practiceResult.Speed);
      const pitchass: Boolean = this.PitchAccuracy.Passed(practiceResult.PitchAccuracy);
      const timePass: Boolean = this.TimeAccuracy.Passed(practiceResult.TimeAccuracy);
      const chordsPass: Boolean = this.ChordSimultaneity.Passed(practiceResult.ChordSimultaneity);
      return customPass && speedPass && pitchass && timePass && chordsPass;
  }
}

A goal is defined by various constraints. These constraints are implementations of the IConstraint interface. It requires methods to validate if one constraint is passed by another. That means we can easily compare the pitch accuracy result with its goal without caring about the implementation (which is currently stubbed anyways, look at the ChordSimultaneity). 

As we see a goal is met if all the constraints are passed compared to the result.

Where next?

The next steps are to check if the implementation fits our needs and can be easily extended using the constraint interface:

import { IConstraint } from “./IConstraint”;

export class SpeedConstraint implements IConstraint{
  public ConstraintValue: number = 90;

  Passed(otherConstraint: IConstraint): Boolean {
      return otherConstraint.ConstraintValue >= this.ConstraintValue;
  }

  IsBetterThan(otherConstraint: IConstraint): Boolean {
      return this.ConstraintValue >= otherConstraint.ConstraintValue;
  }
 
  IsCompatible(otherConstraint: IConstraint): Boolean {
      throw new Error(“Method not implemented.”);
  }
}

We will also implement the correct patterns for these constraints. The biggest missing thing now is the dependency tree that resolves if a practice is mandatory for another one. This will also be included in the OSME explorer UI to show how the file is done.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *