VexFlow - Music Engraving for HTML5 Copyright Mohit Muthanna 2010 Author Radosaw Eichler 2012 Implements tempo marker.

/**
 * @constructor
 * @param {Object} tempo Tempo parameters: { name, duration, dots, bpm }
 */
Vex.Flow.StaveTempo = (function() {
  function StaveTempo(tempo, x, shift_y) {
    if (arguments.length > 0) this.init(tempo, x, shift_y);
  }

  Vex.Inherit(StaveTempo, Vex.Flow.StaveModifier, {
    init: function(tempo, x, shift_y) {
      StaveTempo.superclass.init.call(this);

      this.tempo = tempo;
      this.position = Vex.Flow.Modifier.Position.ABOVE;
      this.x = x;
      this.shift_x = 10;
      this.shift_y = shift_y;
      this.font = {
        family: "times",
        size: 14,
        weight: "bold"
      };
      this.render_options = {
        glyph_font_scale: 30  // font size for note
      };
    },

    getCategory: function() { return "stavetempo"; },
    setTempo: function(tempo) { this.tempo = tempo; return this; },
    setShiftX: function(x) { this.shift_x = x; return this; },
    setShiftY: function(y) { this.shift_y = y; return this; },

    draw: function(stave, shift_x) {
      if (!stave.context) throw new Vex.RERR("NoContext",
        "Can't draw stave tempo without a context.");

      var options = this.render_options;
      var scale = options.glyph_font_scale / 38;
      var name = this.tempo.name;
      var duration = this.tempo.duration;
      var dots = this.tempo.dots;
      var bpm = this.tempo.bpm;
      var font = this.font;
      var ctx = stave.context;
      var x = this.x + this.shift_x + shift_x;
      var y = stave.getYForTopText(1) + this.shift_y;

      ctx.save();

      if (name) {
        ctx.setFont(font.family, font.size, font.weight);
        ctx.fillText(name, x, y);
        x += ctx.measureText(name).width;
      }

      if (duration && bpm) {
        ctx.setFont(font.family, font.size, 'normal');

        if (name) {
          x += ctx.measureText(" ").width;
          ctx.fillText("(", x, y);
          x += ctx.measureText("(").width;
        }

        var code = Vex.Flow.durationToGlyph(duration);

        x += 3 * scale;
        Vex.Flow.renderGlyph(ctx, x, y, options.glyph_font_scale, code.code_head);
        x += code.head_width * scale;

Draw stem and flags

        if (code.stem) {
          var stem_height = 30;

          if (code.beam_count) stem_height += 3 * (code.beam_count - 1);

          stem_height *= scale;

          var y_top = y - stem_height;
          ctx.fillRect(x, y_top, scale, stem_height);

          if (code.flag) {
            Vex.Flow.renderGlyph(ctx, x + scale, y_top, options.glyph_font_scale,
                                 code.code_flag_upstem);

            if (!dots) x += 6 * scale;
          }
        }

Draw dot

        for (var i = 0; i < dots; i++) {
          x += 6 * scale;
          ctx.beginPath();
          ctx.arc(x, y + 2 * scale, 2 * scale, 0, Math.PI * 2, false);
          ctx.fill();
        }

        ctx.fillText(" = " + bpm + (name ? ")" : ""), x + 3 * scale, y);
      }

      ctx.restore();
      return this;
    }
  });

  return StaveTempo;
}());
h