VexFlow - Copyright (c) Mohit Muthanna 2010.
This file implements different types of pedal markings. These notation elements indicate to the performer when to depress and release the a pedal.
In order to create “Sostenuto”, and “una corda” markings, you must set custom text for the release/depress pedal markings.
Vex.Flow.PedalMarking = (function() {
function PedalMarking(type) {
if (arguments.length > 0) this.init(type);
}To enable logging for this class. Set Vex.Flow.PedalMarking.DEBUG to true.
function L() { if (PedalMarking.DEBUG) Vex.L("Vex.Flow.PedalMarking", arguments); }Glyph data
PedalMarking.GLYPHS = {
"pedal_depress": {
code: "v36",
x_shift:-10,
y_shift:0
},
"pedal_release": {
code: "v5d",
x_shift:-2,
y_shift:3
},
};
PedalMarking.Styles = {
TEXT: 1,
BRACKET: 2,
MIXED: 3
};Create a sustain pedal marking. Returns the defaults PedalMarking. Which uses the traditional “Ped” and “*”” markings.
PedalMarking.createSustain = function(notes) {
var pedal = new PedalMarking(notes);
return pedal;
};Create a sostenuto pedal marking
PedalMarking.createSostenuto = function(notes) {
var pedal = new PedalMarking(notes);
pedal.setStyle(PedalMarking.Styles.MIXED);
pedal.setCustomText("Sost. Ped.");
return pedal;
};Create an una corda pedal marking
PedalMarking.createUnaCorda = function(notes){
var pedal = new PedalMarking(notes);
pedal.setStyle(PedalMarking.Styles.TEXT);
pedal.setCustomText("una corda", "tre corda");
return pedal;
}; PedalMarking.prototype = {
init: function(notes) {
this.notes = notes;
this.style = Vex.Flow.PedalMarking.TEXT;
this.line = 0;Custom text for the release/depress markings
this.custom_depress_text = "";
this.custom_release_text = "";
this.font = {
family: "Times New Roman",
size: 12,
weight: "italic bold"
};
this.render_options = {
bracket_height: 10,
text_margin_right: 6,
bracket_line_width: 1,
glyph_point_size: 40,
color: "black"
};
},Set custom text for the depress/release pedal markings. No text is
set if the parameter is falsy.
setCustomText: function(depress, release) {
this.custom_depress_text = depress || "";
this.custom_release_text = release || "";
return this;
},Set the pedal marking style
setStyle: function(style){
if (style < 1 && style > 3) {
throw new Vex.RERR("InvalidParameter",
"The style must be one found in PedalMarking.Styles");
}
this.style = style;
return this;
},Set the staff line to render the markings on
setLine: function(line) { this.line = line; return this; },Set the rendering context
setContext: function(context) { this.context = context; return this; },Draw the bracket based pedal markings
drawBracketed: function() {
var ctx = this.context;
var is_pedal_depressed = false;
var prev_x;
var prev_y;
var pedal = this;Iterate through each note
this.notes.forEach(function(note, index, notes) {Each note triggers the opposite pedal action
is_pedal_depressed = !is_pedal_depressed;Get the initial coordinates for the note
var x = note.getAbsoluteX();
var y = note.getStave().getYForBottomText(pedal.line + 3);Throw if current note is positioned before the previous note
if (x < prev_x) throw new Vex.RERR('InvalidConfiguration',
'The notes provided must be in order of ascending x positions');Determine if the previous or next note are the same as the current note. We need to keep track of this for when adjustments are made for the release+depress action
var next_is_same = notes[index+1] === note;
var prev_is_same = notes[index-1] === note;
var x_shift = 0;
if (is_pedal_depressed) {Adjustment for release+depress
x_shift = prev_is_same ? 5 : 0;
if (pedal.style === PedalMarking.Styles.MIXED && !prev_is_same) {For MIXED style, start with text instead of bracket
if (pedal.custom_depress_text) {If we have custom text, use instead of the default “Ped” glyph
var text_width = ctx.measureText(pedal.custom_depress_text).width;
ctx.fillText(pedal.custom_depress_text, x - (text_width/2), y);
x_shift = (text_width / 2) + pedal.render_options.text_margin_right;
} else {Render the Ped glyph in position
drawPedalGlyph('pedal_depress', ctx, x, y, pedal.render_options.glyph_point_size);
x_shift = 20 + pedal.render_options.text_margin_right;
}
} else {Draw start bracket
ctx.beginPath();
ctx.moveTo(x, y - pedal.render_options.bracket_height);
ctx.lineTo(x + x_shift, y);
ctx.stroke();
ctx.closePath();
}
} else {Adjustment for release+depress
x_shift = next_is_same ? -5 : 0;Draw end bracket
ctx.beginPath();
ctx.moveTo(prev_x, prev_y);
ctx.lineTo(x + x_shift, y);
ctx.lineTo(x, y - pedal.render_options.bracket_height);
ctx.stroke();
ctx.closePath();
}Store previous coordinates
prev_x = x + x_shift;
prev_y = y;
});
},Draw the text based pedal markings. This defaults to the traditional “Ped” and “*”” symbols if no custom text has been provided.
drawText: function() {
var ctx = this.context;
var is_pedal_depressed = false;
var pedal = this;The glyph point size
var point = pedal.render_options.glyph_point_size;Iterate through each note, placing glyphs or custom text accordingly
this.notes.forEach(function(note) {
is_pedal_depressed = !is_pedal_depressed;
var stave = note.getStave();
var x = note.getAbsoluteX();
var y = stave.getYForBottomText(pedal.line + 3);
var text_width = 0;
if (is_pedal_depressed) {
if (pedal.custom_depress_text) {
text_width = ctx.measureText(pedal.custom_depress_text).width;
ctx.fillText(pedal.custom_depress_text, x - (text_width/2), y);
} else {
drawPedalGlyph("pedal_depress", ctx, x, y, point);
}
} else {
if (pedal.custom_release_text) {
text_width = ctx.measureText(pedal.custom_release_text).width;
ctx.fillText(pedal.custom_release_text, x - (text_width/2), y);
} else {
drawPedalGlyph("pedal_release", ctx, x, y, point);
}
}
});
},Render the pedal marking in position on the rendering context
draw: function() {
if (!this.context) throw new Vex.RERR("NoContext",
"Can't draw PedalMarking without a context.");
var ctx = this.context;
ctx.save();
ctx.setStrokeStyle(this.render_options.color);
ctx.setFillStyle(this.render_options.color);
ctx.setFont(this.font.family, this.font.size, this.font.weight);
L("Rendering Pedal Marking");
if (this.style === PedalMarking.Styles.BRACKET ||
this.style === PedalMarking.Styles.MIXED) {
ctx.setLineWidth(this.render_options.bracket_line_width);
this.drawBracketed();
} else if (this.style === Vex.Flow.PedalMarking.Styles.TEXT) {
this.drawText();
}
ctx.restore();
}
};Draws a pedal glyph with the provided name on a rendering context
at the coordinates x and `y. Takes into account the glyph data
coordinate shifts.
function drawPedalGlyph(name, context, x, y, point) {
var glyph_data = PedalMarking.GLYPHS[name];
var glyph = new Vex.Flow.Glyph(glyph_data.code, point);
glyph.render(context, x + glyph_data.x_shift, y + glyph_data.y_shift);
}
return PedalMarking;
}());