saebekassebil / teoria

Javascript taught Music Theory
http://saebekassebil.github.io/teoria
MIT License
1.32k stars 114 forks source link

Inversions #82

Open mulhoon opened 9 years ago

mulhoon commented 9 years ago

What is the easiest way of creating a chord inversion? i.e moving the root to the top Am (A, C, E) -> (C, E, A) Would it be useful to have a chord function for creating these?

Awesome library!

Thanks

saebekassebil commented 9 years ago

Hey Nic, and thank you very much!

There's no built-in support for inverting chords, because it should be pretty easy. Basically you just have to shift around the intervals you get from chord#voicing().

However, I just found out this is unnecessarily complex, because what you get back from chord#voicing() is Intervals, while, what you set is strings (Intervals in simpleFormat).

I'll have a look at how I can simplify that. Thanks for reporting!

mulhoon commented 9 years ago

Thanks so much! At first I was thinking something like .inversion(1) .inversion(2) would be cool Would that be too complex? or better to write as an external function?

Nic

saebekassebil commented 9 years ago

I actually find it a bit problematic that the chord objects are mutable. I think it might be better if they were immutable(?).

I think inversion functions are cool, but let me just think about whether they belong on teoria, or in a separate module.

What about:

var teoria = require('teoria');
var rotate = require('rotate-array');

var Am1 = teoria.chord('Am');
var Am2 = Am1.voicing(rotate(Am1.voicing(), 1));
var Am3 = Am2.voicing(rotate(Am2.voicing(), 1));
mulhoon commented 9 years ago

That would work fine for me if you'd rather it not be in teoria. Do the voicings need changing to make this work?

mulhoon commented 9 years ago

I also had a thought that the new vars Am2 and Am3 would still have the chord name of 'Am' Would it be useful for example that, .name displays 'Am (1st inv.)' or maybe a readable flag that tells whether it has been inverted? Just a thought.

Thanks again, Nic

fenomas commented 8 years ago

Just want to chime in that some kind of support for inversions would be really useful. I gather we're meant to do it ourselves, but for idiots like me the whole point of using teoria is that I'm not confident I understand how to do it correctly.

FWIW, right now I'm doing something like:

var vs = chord.voicing()
var vnames = vs.map(function(int){ return int.toString() })
vnames[0] = 'P8'
chord.voicing(vnames)

But I've no idea if that would be correct for all chords, etc.

saebekassebil commented 8 years ago

Hey Andy, thanks for chiming in! Eh, you're totally right we need abstractions for "not-expert" users. Let's look for a good way to do this. What kind of methods would be helpful to you?

Maybe?

chord.inversion() // Returns the current inversion (1, 2, 3, etc)
chord.invert() // Inverts the chord one more time (1 -> 2, 2 -> 3, 3 -> 1)
chord.invert(n) // Sets the inversion number directly

Can you think of something more please let me know :)

fenomas commented 8 years ago

Thank you for the great library Jakob!

Regarding your questions, the APIs you listed are basically what I'd hoped for, but you should take that with a grain of salt because I'm too much of a beginner to know if they make sense or not! (As a side note, would it make sense to also have invert(0) return the chord to the original voicing?)

As an alternate view, I'd originally supposed that inversions would be a matter of voicing, rather than the chord's notes, so I'd started writing myself a function like:

var c = teoria.chord('C');                 // c4, e4, g4
c.transposeVoicingByOctave([ 1, 0, -1 ]);  // c5, e4, g3

I gather that would let one construct any inversion one wanted. But looking at teoria's implementation I see that notes are derived from voicing, not the reverse, so perhaps fiddling with the voicing doesn't make sense. (In fact, if chords are best thought of as immutable, I'm not sure I should be calling that method at all. It essentially lets you overwrite the chord's notes, right?)

BHSPitMonkey commented 8 years ago

Just some thoughts on this:

If you don't like the idea of the Chord data model containing this kind of "state", then perhaps inversions could be computed on-the-fly:

var c = teoria.chord('C');
c.notes(); // Note objects: [c4 e4 g4]
c.notes(1); // Note objects: [e4 g4 c5]

This approach seems like it would undermine the purpose of Chord.bass(), however. If you could set the inversion of an existing Chord, then that function has an opportunity to make sense (though it would just return the first element of the array returned from notes()).

Personally, I'd like to be able to initialize a chord like "Am", then be able to independently modify its inversion and bass octave, and then finally retrieve an array of Note objects (with appropriate ordering and octaves), but I'm willing to work around whatever API design you prefer in the end - it's your project!