Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transposing chords by a number of semitones #83

Closed
namuol opened this issue Jul 12, 2015 · 6 comments
Closed

Transposing chords by a number of semitones #83

namuol opened this issue Jul 12, 2015 · 6 comments

Comments

@namuol
Copy link

namuol commented Jul 12, 2015

I'm creating a simple ukulele composition tool and need a way to transpose chords by a numerical offset in semitones (this is common in lots of tablature sites/apps).

In order to transpose a Chord, you must pass an Interval object to the .transpose or .interval method, but I don't know how to create such an Interval that correctly represents the operation I'm trying to achieve.

I'm not very familiar with music theory, but I determined that I can achieve this with a lookup table of interval names that achieve what I want:

var INTERVAL_NAMES = [
  'P1',
  'm2',
  'M2',
  'm3',
  'M3',
  'P4',
  'A4',
  'P5',
  'm6',
  'M6',
  'm7',
  'M7',
  'P8',
];

// Handles modulo correctly for negative numbers:
function mod(n, m) {
  return ((n % m) + m) % m;
}

function intervalFromSemitoneOffset (n) {
  return teoria.interval(INTERVAL_NAMES[mod(n, 12)]);
}

This seems to work, but feels sloppy. What would you suggest? Personally, I'd love it if .transpose/.interval could simply take an integer value, but I'm probably misunderstanding something about how intervals work.

@saebekassebil
Copy link
Owner

Hi Louis, thanks for writing!

Well, you don't "misunderstand" how intervals work - it's just that they're ambiguous in music theory. You could argue that you can't hear any difference, but there are several intervals that represent the same amount of semitones (in western equal temperament). E.g. A "major" third and a "diminished fourth" "contains" the same amount of semitones, but are theoretically different.

Anyways. I'd probably do it in a different way, since it seems that you don't care whether you get a "C#" or a "Db". What about transposing the root by key number?

var root = teoria.note('E');
var chordE7 = teoria.chord(root, '7');
var chordF7 = teoria.note.fromKey(root.key() + 1).chord(chordE.symbol)
var chordG7 = teoria.note.fromKey(chordF.root.key() + 2).chord(chordF.symbol)

// general function
function transposeChord(chord, semitones) {
  return teoria.note.fromKey(chord.root.key() + semitones).chord(chord.symbol)
}

Here I'm just reusing the Chord.symbol property, and creating a new root by key number.
It might not be ideal, and I'm open to suggestions on how to do it better.

@namuol
Copy link
Author

namuol commented Jul 12, 2015

Much better! However, I'm having issues with slashed chords. Should the expected behavior be such that the bass note remains the same when a slashed chord is transposed?

For instance:

var transposed_DF = transposeChord(teoria.chord('D/F'), 1);

console.log(transposed_DF.name); // "Eb/F"
console.log(transposed_DF.symbol); // "/F"

Shouldn't the transposed chord actually be Eb/F#?

Note: using .interval has the same problem, because the .symbol property is not considered in transposition methods. The slashed chord problem might be worth opening a separate issue for...

@saebekassebil
Copy link
Owner

Yes it should - an oversight :)
Let's fix that soon.

@namuol namuol closed this as completed Jul 12, 2015
@namuol namuol reopened this Jul 12, 2015
@namuol
Copy link
Author

namuol commented Jul 12, 2015

(Whoops, didn't mean to close the issue there)

I'd be happy to write some tests for these edge-cases if that helps!

@saebekassebil
Copy link
Owner

Yes please do! It might need some rethinking on how we handle "slash chords".

My initial thought is if the voicing doesn't have the root in voiced lowest, then display as a "/ chord", and don't let the slash be part of the .symbol?

@saebekassebil
Copy link
Owner

Closing this instead of #92

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants