Juan Gonzales Juan Gonzales - 1 year ago 169
Javascript Question

Transposing music using javascript or jquery

I have a chord chart application that I wrote and I would like to allow users to transpose the key of the chart using an onClick handler.

My chart looks like this

{C}My name is Blanket, {F}And I can run fast


The chords inside the brackets appear above the letter it preceeds.

I would like to use javascript or jquery in order to do this. How would I go about creating this transpose button? Any help is appreciated. Thank you in advance.

EDIT

So here's what I came up with...

$('.transposeUp').click(function(){
$('.chord').each(function(){
var currentChord = $(this).text(); // gathers the chord being used
if(currentChord == $(this).text()){
var chord = $(this).text().replace("F#", "G")
}
//... the if statements continue though every chord
//but I didn't place them here to save space
});
});


So here is the problem...

I have a slash chord in the mix (G/B), It changes the be on transpose but because it changes the "B" the chord is now (G/C) which is not the same as the "currentChord" so it doesn't change the G when it gets to its respective if condition. Until I have transposed enough where the Chord is eventualy (G/G) then the first "G" starts transposing leaving the last "G" the same. Any ideas? Again your knowledge and help is greatly appreciated. Thanks in advance.

Answer Source

You need to match the chords sequentially so that you can update them one at a time. If you try to match everything at once you'll run into problems like you described, because you keep matching the same chord over and over again.

A good way to accomplish this would be with regular expressions to parse and split the chord. Once you have the matching chord values, use an array of chords to find the next/previous chord to transpose. Here is some sample code I have developed as a demo:

<p><span class="chord">{C}</span>My name is Blanket,</p>
<p><span class="chord">{G / B}</span>And I can run fast</p>

<p>
<input id="transposeDown" type="button" value="Down" /> |
<input id="transposeUp" type="button" value="Up" />
</p>

var match;
var chords =
    ['C','C#','D','Eb','E','F','F#','G','Ab','A','Bb','B','C',
     'Db','D','D#','E','F','Gb','G','G#','A','A#','C'];
var chordRegex = /C#|D#|F#|G#|A#|Db|Eb|Gb|Ab|Bb|C|D|E|F|G|A|B/g;

$('#transposeUp').click(function() {
    $('.chord').each(function() {
        var currentChord = $(this).text();
        var output = "";
        var parts = currentChord.split(chordRegex);
        var index = 0;
        while (match = chordRegex.exec(currentChord))
        {
            var chordIndex = chords.indexOf(match[0]);
            output += parts[index++] + chords[chordIndex+1];
        }
        output += parts[index];
        $(this).text(output);
    });
});

$('#transposeDown').click(function() {
    $('.chord').each(function() {
        var currentChord = $(this).text();
        var output = "";
        var parts = currentChord.split(chordRegex);
        var index = 0;
        while (match = chordRegex.exec(currentChord))
        {
            var chordIndex = chords.indexOf(match[0],1);
            output += parts[index++] + chords[chordIndex-1];
        }
        output += parts[index];
        $(this).text(output);
    });
});

Sample demo: http://jsfiddle.net/4kYQZ/2/

A couple of things to notice:

  1. I took @gregp's idea of having multiple copies of the key list so that I can handle both sharps and flats. The first scale includes the preferred #/b which will be used during transposing. The second scale includes the other formats so that if the music includes them, they will transpose correctly. For instance, since Eb is in the first scale, it will be used instead of D# as you are transposing up and down; however, if there is a D# in the music to begin with, it will correctly move to D/E (with the caveat that if you move back, it will become an Eb again). Feel free to modify the preferred scale - I used my educated judgment based on personal music experience.
  2. The regular expression has to have the sharped/flatted keys first, otherwise a C# would be just as easily matched as a C when that's not correct.
  3. I have C at the beginning and the end of the array so that I can start at any location and move both directions without passing the end of the array. In order for this to work, the transposeDown code has an extra parameter in the call to chords.indexOf to start at position 1 so it matches the last C in the array instead of the first C. Then when it attempts to move to the previous element, it doesn't pass the beginning of the array.
  4. I'm splitting the chord as well using the regular expression, which is redundant, but it makes it super-easy to recompose the final string back together - by interleaving the original string parts with the updated chord keys (don't forget the last piece at the end!).
  5. I'm using elements instead of classes for your buttons.

Hope this helps!


Update 1: Per comment by OP, the use of indexOf on arrays is not supported by pre-ie9. This can be solved by using a helper function that does the same thing:

function arrayIndexOf(arr, match)
{
    for (var i = 0; i < arr.length; i++)
        if (arr[i] == match)
            return i;
    return -1;
}

And this line

var chordIndex = chords.indexOf(match[0]);

would be replaced with:

var chordIndex = arrayIndexOf(chords, match[0]);

See updated sample demo: http://jsfiddle.net/4kYQZ/11/

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download