1

I'd like to store the duration of each note into some domain classes, and then render the duration of such a stored note.

For example, dealing with this melody:

const textMeasures = ['rest/4 B4/16 A4/16 G#4/16 A4/16',
    'C5/8 rest/8 D5/16 C5/16 B4/16 C5/16',
    'E5/8 rest/8 F5/16 E5/16 D#5/16 E5/16',
    'B5/16 A5/16 G#5/16 A5/16 B5/16 A5/16 G#5/16 A5/16',
    'C6/4 A5/8 C6/8',
    'B5/8 A5/8 G5/8 A5/8',
    'B5/8 A5/8 G5/8 A5/8',
    'B5/8 A5/8 G5/8 F#5/8',
    'E5/4'];

the second note B4/16 has a duration of 16.

I could simply add a duple unit n to the duration as in 16n to render the duration.

But I'd like to store the duration in a more generic way.

I went as far as having these domain classes to keep the duration:

export enum Subdivisions {

  SIXTY_FOURTH = 64,
  THIRTY_SECOND = 32,
  SIXTEENTH = 16,
  EIGHTH = 8,
  QUARTER = 4,
  HALF = 2,
  WHOLE = 1,
  NONE = 0

}

export class Subdivision {

  static readonly THIRTY_SECOND = new Subdivision(Subdivisions.THIRTY_SECOND, 0);
  static readonly DOTTED_THIRTY_SECOND = new Subdivision(Subdivisions.THIRTY_SECOND, Subdivisions.SIXTY_FOURTH);
  static readonly SIXTEENTH = new Subdivision(Subdivisions.SIXTEENTH, 0);
  static readonly DOTTED_SIXTEENTH = new Subdivision(Subdivisions.SIXTEENTH, Subdivisions.THIRTY_SECOND);
  static readonly EIGHTH = new Subdivision(Subdivisions.EIGHTH, 0);
  static readonly DOTTED_EIGHTH = new Subdivision(Subdivisions.EIGHTH, Subdivisions.SIXTEENTH);
  static readonly QUARTER = new Subdivision(Subdivisions.QUARTER, 0);
  static readonly DOTTED_QUARTER = new Subdivision(Subdivisions.QUARTER, Subdivisions.EIGHTH);
  static readonly HALF = new Subdivision(Subdivisions.HALF, 0);
  static readonly DOTTED_HALF = new Subdivision(Subdivisions.HALF, Subdivisions.QUARTER);
  static readonly WHOLE = new Subdivision(Subdivisions.WHOLE, 0);
  static readonly NONE = new Subdivision(Subdivisions.NONE, 0);

  public readonly left: number;
  public readonly right: number;

  private constructor(left: number, right: number) {
    this.left = left;
    this.right = right;
  }

}

export enum TempoUnit {

  HERTZ = 'hz',
  TICK = 'i',
  SECOND = 's',
  BPM = 'bpm',
  DUPLE = 'n',
  TRIPLET = 't',
  MEASURE = 'm'

}

export class Duration {

  subdivision: Subdivision;
  unit: TempoUnit;

  constructor(subdivision: Subdivision, tempoUnit: TempoUnit) {
    this.subdivision = subdivision;
    this.unit = tempoUnit;
  }
}

Now, given the above 16 duration from the second B4/16 note, I need to transform it into some unit and subdivision.

I tried this function:

private subdivision(duration: string): Subdivision {
  console.log('Get subdivision for duration: ' + duration);
  const intValue: number = parseInt(duration, 10);
  if (intValue === Subdivision.EIGHTH.left) { // TODO Add more if cases
    return Subdivision.EIGHTH;
  } else if (intValue === Subdivision.QUARTER.left) {
    return Subdivision.QUARTER;
  } else if (intValue === Subdivision.SIXTEENTH.left) {
    return Subdivision.SIXTEENTH;
  } else if (intValue === Subdivision.HALF.left) {
    return Subdivision.HALF;
  } else if (intValue === Subdivision.THIRTY_SECOND.left) {
    return Subdivision.THIRTY_SECOND;
  } else {
    throw new Error('Unknown subdivision for duration: ' + duration);
  }
}

But it's a work in progress and I miss the music theory to complete it. I'm not even sure I'm on the right track there.

Here is how I intend to use it:

private duration(duration: string, tempoUnit: TempoUnit) {
  return new Duration(this.subdivision(duration), tempoUnit);
}

const duration: Duration = this.duration(16, TempoUnit.DUPLE);

If only I knew a set of possible input values as duration then I suppose I could guess what the subdivision() method is supposed to return for each.

7
  • How will you "render the duration"? I'm a programmer, and a quick read of this question leaves me wondering about what you're trying to achieve, and whether you realize that a quarter note has the duration of two eighth notes rather than the other way around. The title title of the question mentions bpm, but the body does not, except in passing as a member of the TempoUnit enum (which mixes several related but distinct concepts). Can you take a step back and explain at a higher level what you're trying to achieve, or tell us how you understand "bpm," "duration," and "subdivision"? – phoog Mar 15 '20 at 18:40
  • Basically I would like to store the duration of a note. I understand I should split my TempoUnit enum in two ? – Stephane Mar 15 '20 at 18:47
  • I guess what I'm trying to get at is that it isn't clear what system of measurement you want to use to denote and store the duration. The fractional note names are one system of measurement; traditional units of time are another; and the relationship between the two systems is set (for a given musical excerpt) by defining the beats per minute (which may even be a function of time in the case of accelerando and decelerando). – phoog Mar 15 '20 at 18:54
  • I'm a complete noob in music theory. The fractional names are my subdivisions ? What sort of traditional unit of time is there ? I understand I'm mixing two recipes here. Which way you'd go to represent time ? What should I prune from my model classes ? – Stephane Mar 15 '20 at 20:01
  • I think I have an idea for an answer. I'm running aroound outside at the moment. I'll try to post one in a few hours. – phoog Mar 15 '20 at 20:41
2

Your question betrays some confusion about concepts related to duration in music, so this answer will cover the basics.

In modern European music notation, as you seem to be aware, the base duration of each successively shorter note is half of the base duration of the preceding note. For example, two half notes have the same duration as a whole note, and two quarter notes have the same duration as a half note.

There are modifications possible: adding a dot to the note increases its duration by one note of the next smaller type, so a dotted half note has the duration of three quarter notes. Additional dots increase the duration by one note of increasingly smaller types, so a doubly dotted half note has the duration of seven eighth notes, and a triply dotted half note has the duration of fifteen sixteenth notes.

Triplet notes introduce a factor of three: three triplet quarter notes, for example, have the same duration as two duple quarter notes, so one triplet quarter note has 2/3 the duration of one "regular" quarter note.

All of these durations are relative. By themselves, they tell you nothing about how many notes of a given type will add up to one minute, or any other span of time. To specify this, you can specify "beats per minute," also known as a metronome marking. This consists of two parts: the first is a note value, which could be a quarter note, a half note, a dotted quarter note, or, theoretically at least, any other note. The second part is a number, which is the number of those notes in one minute. (This has the same dimension as Hz, namely the inverse of time, but differs by a factor of 60. In practice, Hz is used for something completely different in music, namely specifying audible pitch.)

The metronome marking gives more information than simply the duration of each note type; it also tells you about the music's rhythmic organization. For example, the markings half note = 60 and quarter note = 120 are equivalent from the point of view of duration, but they imply something different about the feeling of the music.

Another concept that appears in your TempoUnit enum is that of the measure. A measure has the duration of some number of notes of some type, and that is determined by its time signature. To know the duration of a measure, you must know both the metronome marking and the time signature.

It's still not clear to me what you mean by "store the duration in a more generic way." You should decide whether you want to store the duration in terms of musical rhythm notation (i.e., sixteenth note) or in terms of time (i.e., 125 milliseconds, which, at quarter note = 120 is the duration of a sixteenth note). One advantage of the former approach is that you can store things as rational numbers and avoid rounding errors: if you divide 125 milliseconds by 3, you don't get an integer result.

It's also not clear to me what the left and right properties of your Subdivision class represent. This may be because the class's declared constructor takes two parameters, but the example given at the end of the question calls that constructor with only one argument. In the languages I'm familiar with, the code would not compile.

Indeed, it's not clear why you even have a subdivision class: digitized music can readily be represented as a stream of durations without regard to meter or subdivision.

What I would do is have a system that allows you to represent quarter notes, half notes, etc., with or without any number of dots, with or without the triplet modification, in isolation. Then I would separately have a way of converting between that system and a duration in time (seconds, milliseconds, ticks, what have you). For the example given in the question, you don't have to worry about dots or triplets, because there aren't any, so to start, I would leave those features out, following the principle of YAGNI (you ain't gonna need it).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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