function getMelodyArray(melodyIndex) { var ngram = $("#contentContainer .gwt-TextBox").val(); ngram = ngram.replace(/\s{2,}/g, ' '); ngram = ngram.replace(/\-{2,}/g, '-'); ngram = ngram.replace(/^\s+|\s+$/g, ''); var melodies = ngram.split(','); if (melodyIndex >= melodies.length) return; var mel_str = melodies[melodyIndex]; var ints = mel_str.replace(/\s{2,}/g, ' ').replace(/^\s+|\s+$/g, '').split(' '); return ints; } var renderInput = function() { $('#canvas_div').empty(); var ngram = $("#contentContainer .gwt-TextBox").val(); if (!ngram && ngram != "") { return; } if (ngram.split(',').length > 3) { var notes = ngram.split(','); var newNgram = notes[0]; for (var i = 1; i < notes.length; i++) { var diff = - - notes[i] - notes[i - 1]; newNgram += " " + diff; } ngram = newNgram; $("#contentContainer .gwt-TextBox").val(newNgram); } ngram = ngram.replace(/\s{2,}/g, ' '); ngram = ngram.replace(/\-{2,}/g, '-'); ngram = ngram.replace(/^\s+|\s+$/g, '');//trim melodies = ngram.split(','); for (var m = 0; m < melodies.length; m++) { var canvas; if (navigator.appName == 'Microsoft Internet Explorer') { canvas = $('#canvas_div'); } else { canvas = $(''); $('#canvas_div').append(canvas); } renderMel(melodies[m], canvas[0]); } if (navigator.appName == 'Microsoft Internet Explorer' || getMelodyArray(1) != null) return; $('#canvas_div canvas').bind("click", scoreOnClick); var scuts = $('
Keyboard shortcuts
'); var scutsContent = '
Keyboard shortcuts
'+ '
previous note next note
raise pitch lower pitch
' + 'Shift shorter duration
Shift longer duration
' + 'space copy current note
del delete current note
' +'
'; $(".popups-close").live('click', function() { $("#shortcut-popup").detach(); $("#shortcuts-text").show(); _gaq.push(['_trackEvent', "Site", "Hide keyboard shortcuts"]); }); scuts.bind('click', function() { $('#canvas_div').append($(scutsContent)); $("#shortcuts-text").hide(); _gaq.push(['_trackEvent', "Site", "Show keyboard shortcuts"]); }); $('#canvas_div').append(scuts); } function switchNgramRepresentation() { var ngramType = $("#ngramTypeContainer select").val(); var ngram = $("#contentContainer .gwt-TextBox").val(); if (!ngram && ngram != "") return; var ngram = ngram.replace(/\s{2,}/g, ' '); ngram = ngram.replace(/\-{2,}/g, '-'); ngram = ngram.replace(/^\s+|\s+$/g, '');//trim melodies = ngram.split(','); var out = ""; for (m in melodies) { melody = melodies[m]; var ints = melody.replace(/\s{2,}/g, ' '). replace(/^\s+|\s+$/g, '').split(' '); var b = ints[0].split('_')[0]; if (b < 20 && ints.length > 1) { b = 57; ints.unshift(57); } out += ints.join(" ") + ", "; } out = out.substring(0, out.length - 2); $("#contentContainer .gwt-TextBox").val(out); } var timer1, timer2; function onGwtReady() { renderInput(); flashPianoInit(); //setTimeout(addFBLikeButton, 1000); //setTimeout(loadDisqus, 8000); $("#ngramTypeContainer select").change(function() { switchNgramRepresentation(); renderInput(); }); $("#showCommentsContainer").show(); $("#showComments").click(function() { $("#commentsContainer").toggle(); if ($("#commentsContainer").is(':visible')) { $("#showComments").html("Hide comments"); _gaq.push(['_trackEvent', "Site", "Show comments"]); } else { $("#showComments").html("Show comments"); _gaq.push(['_trackEvent', "Site", "Hide comments"]); } }); var contd = $("#contentContainer .gwt-TextBox"); contd.keydown(function(e) { if (e.which == '32') { renderInput(); } }); contd.live('keyup', function(e){ var c = e.which; var ngramField = $("#contentContainer .gwt-TextBox"); if (c < 32 && c != 8) { return; } if (c != 8 && c != 32 && c != 37 && c != 39 && (c < '48' || c > '57')) { var v = ngramField.val(); var last = v.charAt(v.length - 1); if (last.match(/[\-0-9 ,_)(wrdhq81632]/g) == null) { ngramField.val(v.substring(0, v.length - 1)); } return; } if (c == 8 && playedMelody.length > 0) { var ns = ngramField.val(); // this means we have deleted a symbol (e.g. "-2") if (ns.charAt(ns.length - 1) == " ") { playedMelody.pop(); noteTimes.pop(); } if (ns.length == 0) { playedMelody = []; noteTimes = []; } clearTimeout(timer1); timer1 = setTimeout("renderInput();", 300); return; } clearTimeout(timer1); timer1 = setTimeout("renderInput();", 300); clearTimeout(timer2); if (ngramField.val().length > 0 && (typeof something === "undefined")) { timer2 = setTimeout("submitQuery();", 2000); } }) contd.blur(renderInput); } var clearSubmitTimeout = function () { clearTimeout(timer2); } var submitQuery = function () { $('#sendButtonContainer .sendButton').click(); prevNotePiano = null; } var ctx; var renderMel = function(mel_str, canvas) { if (typeof Vex == 'undefined') return; var ngramType = $("#ngramTypeContainer select").val(); var ints = mel_str.replace(/\s{2,}/g, ' ').replace(/^\s+|\s+$/g, '').split(' '); var b = ints[0].split('_')[0].split('(')[0]; var d = ints[0].split('_')[0].split('(')[1]; if (b < 20 && ints.length > 1) { if (b == "" && d != "") { } else { ints.unshift(57); } b = 57; } music = new Vex.Flow.Music(); notes = []; var totalDuration = 0; var totalDots = 0; var totalAccidentals = 0; // compute min and max pitches var minPitch = 127; var maxPitch = 0; var abs_pos; for (var i = 0; i < ints.length; i++) { var chord_raw = "" + ints[i]; var chord_params = chord_raw.split("("); var notes_raw = chord_params[0]; var chord = notes_raw.split("_"); for (var j = 0; j < chord.length; j++) { if (ints[i] == "") continue; var curr_note = chord[j]; abs_pos = (i == 0 && j == 0 ? b : abs_pos - - curr_note); maxPitch = Math.max(maxPitch, abs_pos); minPitch = Math.min(minPitch, abs_pos); } } var clef; if (maxPitch < 53 && minPitch < 43) { clef = "bass"; } else { clef = "treble"; } for (var i = 0; i < ints.length + (mel_str.length > 0 ? 1 : 0); i++) { if (i >= ints.length) continue; var abs_pos; var keys = []; var accidentals = []; if (ints[i] == "") continue; var chord_raw = "" + ints[i]; var chord_params = chord_raw.split("("); if (chord_params.length > 2) { continue; } var notes_raw = chord_params[0]; var duration = "q"; if (chord_params.length == 2) { if (chord_params.indexOf(')') == 0) { continue; } var duration_raw = chord_params[1]; var duration_int = duration_raw.substring(0, duration_raw.indexOf(')')); for (var durInd in durations) { var d = durations[durInd]; if (d == duration_int) { duration = durationsVex[durInd]; } } } var chord = notes_raw.split("_"); for (var j = 0; j < chord.length; j++) { var curr_note = chord[j]; abs_pos = (i == 0 && j == 0 ? b : abs_pos - - curr_note); abs_pos_clef = abs_pos - - (clef == "bass" ? 21 : 0); var rel_pos = abs_pos % 12; var key = music.getCanonicalNoteName(rel_pos); if (key == undefined) continue; var parts = music.getNoteParts(key); var acc = parts['accidental']; accidentals[j] = acc; var octave = Math.floor(abs_pos / 12); keys.push(key + "/" + octave); } notes[i] = new Vex.Flow.StaveNote({ clef: clef, keys: keys, duration: duration }); var durationBeats = notes[i].getTicks() / Vex.Flow.RESOLUTION * 4; totalDuration = totalDuration + durationBeats; notes[i] = notes[i].setStemDirection(abs_pos - - (clef == "bass" ? 21 : 0) > 59 ? Vex.Flow.StaveNote.STEM_DOWN : Vex.Flow.StaveNote.STEM_UP); if (durationIsDotted(duration)) { notes[i].addDotToAll(); totalDots++; } var gotAccidental = false; for (var j = 0; j < chord.length; j++) { var acc = accidentals[j]; if (acc) { notes[i] = notes[i].addAccidental(j, new Vex.Flow.Accidental(acc)); gotAccidental = true; } } if (gotAccidental) totalAccidentals++; } var width = 33 * (ints.length + 1 + (mel_str.length > 0 ? 1 : 0)) + 33 * (totalAccidentals*0 + 3) + 10 * totalDots; canvas.width = width + 30; var stave; if (navigator.appName == 'Microsoft Internet Explorer') { ctx = Vex.Flow.Renderer.getRaphaelContext(canvas, width + 20, 120); stave = new Vex.Flow.Stave(10, 0, width); stave.setContext(ctx); } else { var renderer = new Vex.Flow.Renderer(canvas, Vex.Flow.Renderer.Backends.CANVAS); ctx = renderer.getContext(); stave = new Vex.Flow.Stave(10, 0, width); } stave.addClef(clef).setContext(ctx).draw(); noteOffsetLeft = stave.start_x + stave.glyph_start_x; // Create a voice in 4/4 var voice = new Vex.Flow.Voice({ num_beats: totalDuration, beat_value: 4, resolution: Vex.Flow.RESOLUTION }); // Add notes to voice voice.addTickables(notes); // Format and justify the notes formatter = new Vex.Flow.Formatter().joinVoices([voice]).format([voice], width - 30); // Render voice voice.draw(ctx, stave); } //flash piano interaction var playedMelody = []; var noteTimes = []; var playedNote = function(pitch) { prevNotePiano = pitch; var prevNgram = $("#contentContainer .gwt-TextBox").val(); prevNgram = prevNgram.replace(/^\s+|\s+$/g, ''); if (playedMelody.length > 0) { var prevNote = playedMelody[playedMelody.length - 1]; diff = (0 - - pitch) - (0 - - prevNote); var newNgram = prevNgram + " " + diff; $("#contentContainer .gwt-TextBox").val(newNgram); } else /* if (prevNgram != "") { var newNgram = prevNgram + ", " + pitch; $("#contentContainer .gwt-TextBox").val(newNgram); } else if (prevNgram == "") */ { $("#contentContainer .gwt-TextBox").val(pitch); } renderInput(); playedMelody.push(pitch); noteTimes.push(new Date().getTime()); } var flashPianoInit = function() { $('#sendButtonContainer .sendButton').click(function() { if (noteTimes.length > 0) { var noteTimeDiffs = [noteTimes[0]]; var pitchDiffs = [playedMelody[0]]; for (var i = 1; i < noteTimes.length; i++) { noteTimeDiffs[i] = noteTimes[i] - noteTimes[i-1]; pitchDiffs[i] = playedMelody[i] - playedMelody[i-1]; } var s = '{"p":['+pitchDiffs.toString()+'],"t":['+noteTimeDiffs.toString()+']}'; _gaq = _gaq || []; _gaq.push(['_trackEvent', "Search Parameters", "Piano input", s]); } clearTimeout(timer1); clearTimeout(timer2); prevNotePiano = null; playedMelody = []; noteTimes = []; }); } function addFBLikeButton() { var fbHtml = ''; $("#fb-like-button").html(fbHtml); } var disqus_shortname = 'peachnote'; var disqus_url = 'http://www.peachnote.com'; var disqus_identifier = "/"; function loadDisqus() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); } function disqus_config() { this.callbacks.onNewComment = [function() { _gaq.push(['_trackEvent', "Site", "New comment"]); }]; } //GetSatisfaction feedback //$("#fdbk_tab").live('click', function(){ // _gaq.push(['_trackEvent', "Site", "Feedback button"]); //}); var tickIndex = 0; var canvasOffset; var noteOffsetLeft = 0; var cursorHeight = 150; var selectedNoteIndex; var isScoreEditMode = false; function scoreOnBlur() { isScoreEditMode = false; $('[id!="canvas_div"]').unbind("focus", scoreOnBlur); $(window).unbind("keydown", onScoreKeyPress); selectedNoteIndex = null; renderInput(); } function onScoreKeyPress(e) { if ((e.which < 37 || e.which > 40) && e.which != 46 && e.which != 32) return; if (e.which == 38) changeMelodyNote(selectedNoteIndex, 1); if (e.which == 40) changeMelodyNote(selectedNoteIndex, -1); if (e.which == 37 && !e.shiftKey && selectedNoteIndex > 0) changeSelectedNote(-1); if (e.which == 39 && !e.shiftKey) changeSelectedNote(1); if (e.which == 37 && e.shiftKey) changeSelectedNoteDuration(-1); if (e.which == 39 && e.shiftKey) changeSelectedNoteDuration(1); if (e.which == 46) removeNote(selectedNoteIndex); if (e.which == 32) addNoteAfter(selectedNoteIndex); renderInput(); highlightNote(selectedNoteIndex); return false; } function addNoteAfter(noteIndex) { var ints = getMelodyArray(0); var chord = "" + ints[noteIndex]; var chordBase = chord; var duration = ""; if (chord.indexOf("(") > 0 && chord.indexOf(")") > chord.indexOf("(")) { duration = chord.substring(chord.indexOf("(") + 1, chord.indexOf(")")); chordBase = chord.substring(0, chord.indexOf("(")); } else { duration = chord.substring(chord.indexOf("(") + 1, chord.indexOf(")")); chordBase = "57"; } var diff = -chordBase.split("_")[0]; $.each(chordBase.split("_"), function() { diff += 0 - - this; }); var chordTail = chordBase.split("_"); chordTail.splice(0, 1); var newBaseChord = -diff + (chordTail.length > 0 ? "_" + chordTail.join("_") : "") var newDuration = (duration != "" ? "(" + duration + ")": ""); var newChord = newBaseChord + newDuration; ints.splice(noteIndex + 1, 0, newChord); selectedNoteIndex += 1; var new_mel_str = ints.join(" "); $("#contentContainer .gwt-TextBox").val(new_mel_str); _gaq.push(['_trackEvent', "Search parameters", "Visual score editor: note copied"]); } function changeSelectedNote(amount) { var ints = getMelodyArray(0); if (ints.length > selectedNoteIndex + amount) { selectedNoteIndex = selectedNoteIndex + amount; } _gaq.push(['_trackEvent', "Search parameters", "Visual score editor: selected note changed"]); } var durationsVex = ["w", "hd", "h", "qd", "q", "8d", "8", "16d", "16", "32", "64"]; var durations = ["32", "24", "16", "12", "8", "6", "4", "3", "2", "1", "0"]; function durationIsDotted(duration) { return $.inArray(duration, ["hd", "qd", "8d", "16d"]) != -1; } function changeDuration(duration, amount) { var out = duration; for (var i = 0; i < durations.length; i++) { if (durations[i] == duration && i + amount < durations.length && i + amount >= 0) { out = durations[i - - amount]; } } return out; } function changeSelectedNoteDuration(amount) { var ints = getMelodyArray(0); var chord = "" + ints[selectedNoteIndex]; var duration = "8"; var baseChord = chord; if (chord.indexOf("(") >= 0 && chord.indexOf(")") > chord.indexOf("(")) { duration = chord.substring(chord.indexOf("(") + 1, chord.indexOf(")")); baseChord = chord.substring(0, chord.indexOf("(")); } var d2 = changeDuration(duration, amount) chord = baseChord + "(" + d2 + ")"; if (d2 == "q") chord = baseChord; ints[selectedNoteIndex] = chord; var new_mel_str = ints.join(" "); $("#contentContainer .gwt-TextBox").val(new_mel_str); _gaq.push(['_trackEvent', "Search parameters", "Visual score editor: duration changed"]); } function transposeChord(chord, amount) { var baseChord = chord; var duration = ""; if (chord.indexOf("(") > 0 && chord.indexOf(")") > chord.indexOf("(")) { duration = chord.substring(chord.indexOf("(") + 1, chord.indexOf(")")); baseChord = chord.substring(0, chord.indexOf("(")); } else { duration = chord.substring(chord.indexOf("(") + 1, chord.indexOf(")")); } if (baseChord.indexOf("_") != -1) { var chordNotes = baseChord.split("_"); chordNotes[0] = chordNotes[0] - - amount; return chordNotes.join("_") + (duration != "" ? "(" + duration + ")" : ""); } else { return baseChord - - amount + (duration != "" ? "(" + duration + ")" : ""); } } function changeMelodyNote(noteIndex, amount) { var ints = getMelodyArray(0); ints[noteIndex] = transposeChord(ints[noteIndex], amount); if (ints.length > noteIndex + 1) { ints[noteIndex + 1] = transposeChord(ints[noteIndex + 1], -amount); } else { playedMelody[playedMelody.length - 1] -= -amount; } var new_mel_str = ints.join(" "); $("#contentContainer .gwt-TextBox").val(new_mel_str); _gaq.push(['_trackEvent', "Search parameters", "Visual score editor: note transposed"]); } function removeNote(noteIndex) { var ints = getMelodyArray(0); if (ints.length <= 1) return; if (noteIndex == ints.length - 1) { selectedNoteIndex = selectedNoteIndex - 1; tickIndex -= notes[noteIndex].ticks; } var chord = ints.splice(noteIndex, 1) + ""; var chordBase = chord.split("(")[0]; var diff = 0; $.each(chordBase.split("_"), function() { diff += 0 - - this; }); if (noteIndex < ints.length) { if (noteIndex > 0) { ints[noteIndex] = transposeChord(ints[noteIndex], diff); } else { ints[0] = transposeChord(ints[0], diff); } } var new_mel_str = ints.join(" "); $("#contentContainer .gwt-TextBox").val(new_mel_str); _gaq.push(['_trackEvent', "Search parameters", "Visual score editor: note deleted"]);} function scoreOnClick(e) { if (getMelodyArray(1) != null) { return; } _gaq.push(['_trackEvent', "Search parameters", "Visual score clicked"]); if (!isScoreEditMode) { _gaq.push(['_trackEvent', "Search parameters", "Visual score edit mode activated"]); } isScoreEditMode = true; $('[id!="canvas_div"]').bind("focus", scoreOnBlur); $(window).unbind("keydown", onScoreKeyPress); $(window).keydown(onScoreKeyPress); var canvas = $("#canvas_div > canvas"); canvasOffset = canvas.offset(); // if notes exist enable canvas click event if (notes.length > 0) { // mouse event handler code from: http://diveintohtml5.org/canvas.html var x, y; if (e.pageX != undefined && e.pageY != undefined) { x = e.pageX; y = e.pageY; } else { x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; } x -= canvasOffset.left; y -= canvasOffset.top; selectedNoteIndex = findNote(x); renderInput(); highlightNote(selectedNoteIndex); } else { var new_mel_str = "57"; playedMelody.push(57); $("#contentContainer .gwt-TextBox").val(new_mel_str); selectedNoteIndex = 0; renderInput(); highlightNote(selectedNoteIndex); } } function findNote(xcord) { var canvas = $("#canvas_div > canvas"); if (formatter.tContexts.map[tickIndex] == undefined) { tickIndex -= notes[notes.length-1].ticks; } var dif = canvas[0].width; // set tickIndex for note var noteIndex; var i = 0 for (var note in formatter.tContexts.map){ // skip bar notes in note array if (formatter.tContexts.map[note].maxTicks == 0) { continue; } var d2 = noteOffsetLeft + formatter.tContexts.map[note].x + formatter.tContexts.map[note].width - xcord; var da = Math.abs(d2); if (da < dif) { dif = da; tickIndex = note; } i++; } // if user clicks for a new note (anything to the right of the last existing note) /* if ((noteOffsetLeft + formatter.tContexts.map[tickIndex].x + formatter.tContexts.map[tickIndex].width + 30 - xcord) < 0) { tickIndex = 0; for (var i=0; i <= notes.length-1; i++) { tickIndex += notes[i].ticks; } noteIndex = notes.length; } */ // set noteIndex for 'notes' array based on tickIndex 'map' object var i = 0; for (var note in formatter.tContexts.map) { if (tickIndex == note) { noteIndex = i; break; } i++; } return noteIndex; } function highlightNote(noteIndex) { ctx.fillStyle = "rgba(200,0,0,0.4)"; // if notes exist if (notes.length > 0) { // when adding a new note vs. editing an existing note draw the cursor for next new note //(the tickIndex will be undefined in map object for a new note) if (formatter.tContexts.map[tickIndex] == undefined) { var tempIndex = notes[selectedNoteIndex].ticks; var t = formatter.tContexts.map[tempIndex]; ctx.fillRect(noteOffsetLeft + t.x + 60, 10, 16.5, cursorHeight); } else { var t; var i = 0; for (var note in formatter.tContexts.map) { if (i == noteIndex) { t = formatter.tContexts.map[note]; tickIndex = note; break; } i++; } ctx.fillRect(noteOffsetLeft + t.x - 10, 10, t.width*0 + 22 + t.padding*2, cursorHeight); } } else { ctx.fillRect(noteOffsetLeft, 10, 16, cursorHeight); } ctx.fillStyle = "#000"; }