tonaljs / tonal

A music theory library for Javascript
https://tonaljs.github.io/tonal/docs
3.81k stars 219 forks source link

Wrong chroma / normalized chroma #284

Closed ohmycode closed 2 years ago

ohmycode commented 2 years ago

Hi @danigb, I stumbled across the following issue:

let scale = Scale.get('D major');  
console.log(scale);

returns:

aliases: ['ionian']
chroma: "101011010101"
empty: false 
intervals: (7) ['1P', '2M', '3M', '4P', '5P', '6M', '7M']
name: "D major"
normalized: "101010110101"
notes: (7) ['D', 'E', 'F#', 'G', 'A', 'B', 'C#']
setNum: 2773
tonic: "D"
type: "major"

I think neither the chroma nor the normalized chroma is correct.

whereas

console.log(Pcset.chroma(['D', 'E', 'F#', 'G', 'A', 'B', 'C#']));

returns 011010110101 which works as expected.

I tried to find the bug, but couldn't find it.

Any Idea?

danigb commented 2 years ago

Hello!

Yes, there are an inconsistency between how chrome is used. Basically the "chroma" for scales are always "interval based", and that means that ALWAYS the first interval will be 1P and therefor the leftmost number of the chroma will be 1.

I know this is confusing. That's a consequence of using string for chroma instead of more specific data structure. I'll try to find a solution or mitigation.

ohmycode commented 2 years ago

Ah! now I understand! I thought that the normalised representation could be based on the intervals (or each scale shifted to C as root note). But I guess it depends on the use case to see what makes more sense. Anyway, thank you for the explanation!

PS: I think it's a really cool idea to use a bitmask as datastructure for scale notes.

micahscopes commented 2 years ago

Hey, I just came across this too and am wrapping my head around it. Am I understanding correctly?

In my projects I've been referring to the bitset left-aligned to the root as the "shape". Maybe it'd make sense to have four properties:

This way scale and chord info objects could carry info about both.

ohmycode commented 2 years ago

Hi @micahscopes, the chroma for scales (and I guess chords as well) are based on intervals, not on the chroma/pitch-class. An interval set for any scale always starts on 1P, therefore they don't have to be rotated to have a "1" at the first position.

So for example the C-Pentatonic: intervals: ["1P", "2M", "3M", "5P", "6M"], chroma: "101010010100"

micahscopes commented 2 years ago

@ohmycode I think we are getting at the same thing? I don't mean that under the hood there is a literal rotation of the chroma somewhere in the code (maybe there is, maybe not)... just that starting with the pitchclass-chroma of a given scale and rotating so that the tonic is at the left-most bit would give the same result as building off of the intervals directly.

ohmycode commented 2 years ago

@micahscopes yeah, you're right, that should result in the same pattern. And that is my initial point: that means, all 12 scales of the same scale type will all have the same chroma, which makes me wonder what normalized could be used for, apart of generating a setNum integer.

ohmycode commented 2 years ago

@danigb maybe adding a scaleType chroma (based on the intervals) in addition to a scale chroma (based on the actual pitch classes) would be a solution.

danigb commented 2 years ago

makes me wonder what normalized could be used for @ohmycode Basically is a way to detect modes (aka: rotations). For example, major and dorian shares the same "shape" but rotated: they have the same normalized chroma.

scaleChroma (or pitch class chroma) vs scaleTypeChroma (or intervals chroma) maybe are good starting point... 🤔

@micahscopes If I understand correctly, chroma and shape are the same as pitch class chroma and interval chroma, right? The normalized chroma is a different thing: is an arbitrary way to select only one chroma from all posible rotations.