Closed felixroos closed 3 years ago
Example usage for voicing multiple chords:
function sequence(chords, range?, dictionary?, voiceLeading?, lastVoicing?) {
return chords.reduce(
({ voicings, lastVoicing }, chord) => {
lastVoicing = Voicing.get(chord, range, dictionary, topNoteDiff, lastVoicing);
voicings.push(lastVoicing);
return { voicings, lastVoicing };
},
{ voicings: [], lastVoicing }
).voicings;
}
const voicings = sequence(['C', 'F', 'G'], ['F3', 'A4'], triads, VoiceLeading.topNoteDiff);
/* [
[ 'C4', 'E4', 'G4' ], // root position
[ 'A3', 'C4', 'F4' ], // first inversion (F4 closest to G4)
[ 'B3', 'D4', 'G4' ] // first inversion (G4 closest to F4)
] */
Maybe, sequence could also be a part of Voicing itself...
Hi @felixroos
I am stoked with your contribution ;-)
I did a quick read, but I think I'll need some time to digest and understand this well.
Anyway, some questions/thoughts arose in this first read:
search
should be namedget
(and probably the opposite :-D). So something to discuss in #224 What lefthand exactly means?
From my understanding, lefthand voicings are piano voicings that can be played with one hand, which is mostly the left one as the right is commonly used to play a melody above. But you can also play them with the right hand and use the left for a bassline. Also, they rarely contain the root note, as the root is often played by a bass instrument (or the left hand if the right hand plays the "lefthand" voicing). Sometimes they are also called rootless voicings, but I think the term "rootless" is a little more open, where lefthand are mostly a relatively small set of voicings that are first taught to jazz piano beginners.
Some voicings, like the triads one, can be relatively easy to generate from the chord itself. I'm wondering if it would be possible to generate voicings instead of having a dictionary. Or more specifically: to have a dictionary of meta-voicings, how to distribute a given chord into a voicing. Some thing we can apply to the chords to generate all possible?/common? combinations.
Yeah, that was exactly my intention here. But this is a rather complicated topic.. Generally, a chord has a sort of hierarchy of note importance. As a rule of thumb the 3 and 7 are essential, while the rest is optional. Also, you can play as many or as little notes as you like. Plus, there are many "rules" to be aware of, like lower interval limits.. After a LOT of experimentation with the topic, I found that using a "combinatorial search" with flexible rules is a pretty good solution to generate any voicing.
I think Voicing.search and Voincing.get are not named consistenly with the rest of the library. I thing search should be namedget (and probably the opposite :-D). So something to discuss in #224
Yes, it's kind of inconsistent. I found it difficult to name the "search" method, as it starts with Voicing.* but the result contains mutiple VoicingS...
I think it's probably to better separate those things in different modules: voicing-dictionary, voicing, voice-leading, voicing-analyze (not sure about organisation or names, just an idea)
That could be done. On the other hand, I think there are not that many voice-leading algorithms out there. Also, I think the aim for voicing-dictionary should'nt be completeness, as this is just impossible...
closing this, as #224 exists
As mentioned here, it would be cool if tonal could handle voicings. I am stoked that you offered me to contribute :)
I have written about most of my ideas here:
For the start, I would just concentrate on generating voicings from dictionaries, as it is way simpler and I am mostly done implementing it.
UPDATE: further changes in https://github.com/tonaljs/tonal/pull/224
I'll make a rough proposal by writing a pseudo doc + some test for methods that could be available:
VoicingDictionary
Maps a chord symbol to a set of voicings (interval string). The Voicings package could provide a set of common voicings. Could this also be a seperate package?!
Maybe this could also be in array format.
Voicing.search
This method returns all possible voicings of the given chord, as defined in the dictionary, inside the given range:
changes:
Voicing.get
This method returns the best voicing for chord after the optional lastVoicing, using voiceLeading. Internally calls Voicing.search to generate the available voicings.
changes:
VoiceLeading
A function that decides which of a set of voicings is picked as a follow up to lastVoicing.
The lib could include some common voice leading strategies:
changes:
VoicingDictionary.lookup
Get possible interval sets for given chord in given dictionary:
Note that it works, even if the chord symbol "M7" is just an alias of the "^7" symbol used in the dictionary.
changes:
Optional: Voicing.analyze
Returns some useful info on the given voicing:
Optional: Voicing.analyzeTransition
Returns some useful info on the given voice transition
Could also use intervals instead of semitones (but semitones are easier to compare)
Optional: Voicing.searchSets
Renders all sets of notes that represent any of the interval sets inside the given range, relative to the root:
changes:
Note.enharmonicEquivalent
For my implementation of Voicing.get to work, I would also need a helper function that returns enharmonic equivalents. As this is a rather general purpose method, I would propose this as part of Note:
edit: this feature is now covered by Note.enharmonic
That's it for a start. What do you think? UPDATE: Of course, I am open to suggestions of any sort!
UPDATE: changed some method names + param orderings (see changes under each section)