JulioHerrlein Posted December 30, 2017 Share Posted December 30, 2017 Parsimonious Voice Leading (again): attempts to provide an algorithm In a previous post I deleted, I was trying to find a good way to ensure the minimal parsimonious Voice-Leading (VL) between a sequence of chords. In this post I will try to explain my second attempt. Let's take a look at a sequence of chords, spreaded out almost in a random way, with no VL (setf chordstovl2 '(b3eb5g3 cs6e7gs3 b4f5g6 f7e5c2 d4f7e4 gs7e2a8) Let's specify and evaluate a variable for the number of voices used: (setf voices 3) Let's try to think the best way to connect this pitches with minimal movement. I will apply the following expression to ensure a better and less ambiguous result when applying the CHORD-CLOSEST-PATH function. Evaluate voices before (setf voices 3) Then (setf chordized (mclist (chordize-list (gen-divide voices (setf vlfinal (integer-to-pitch (modus (interval-to-pitch (replace-map '((-11 1) (-10 2) (-9 3) (-8 4) (-7 5) (7 -5) (8 -4) (9 -3)(10 -2)(11 -1)) (integer-to-interval (modus (pitch-to-midi (setf chordmelo (pitch-melodize chordstovl2)))))) :start (car chordmelo))))))))) Please note the use of the replace-map function ensuring that no movement will be greater than a tritone away. This means that a movement like "C to G" (7 semitones) will be convertet in a G to C (5 semitones). This ensure a modulo 12 (octave constraint) reduction of all the material and also a constraint in terms of the size of the movements that will not exceed 6 semitones. Finally, I will apply the CHORD-CLOSEST-PATH function (chord-closest-path (car chordized) (chordize-list (gen-divide voices vlfinal))) Hope it help some VL efforts. Best, Julio Herrlein Quote Link to comment Share on other sites More sharing options...
JulioHerrlein Posted December 30, 2017 Author Share Posted December 30, 2017 Janusz suggested a different way for the expression: (setf voices 3) (setf chordstovl2 '(b3eb5g3 cs6e7gs3 b4f5g6 f7e5c2 d4f7e4 gs7e2a8)) (setf chordmelo (pitch-melodize chordstovl2)) (setf intervals (integer-to-interval (modus (pitch-to-midi chordmelo)))) (setf map (replace-map '((-11 1) (-10 2) (-9 3) (-8 4) (-7 5) (7 -5) (8 -4) (9 -3) (10 -2) (11 -1)) intervals)) (setf vlfinal (integer-to-pitch (modus (interval-to-pitch map :start (car chordmelo))))) (setf chordized (mclist (chordize-list (gen-divide voices vlfinal)))) (chord-closest-path (car chordized) chordized) Quote Link to comment Share on other sites More sharing options...
Avner Dorman Posted March 29, 2019 Share Posted March 29, 2019 Dear Julio, Thanks for sharing these codes. So I'm trying to understand if the following approach could work - it seems to work for your example (but not for every progression). (setf chordstovl2 '(b3eb5g3 cs6e7gs3 b4f5g6 f7e5c2 d4f7e4 gs7e2a8)) (defun voicelead (chords) (chord-closest-path (list (car chords)) chords)) (voicelead (ambitus '(c4 c5) chordstovl2)) but (voicelead (harmonic-progression '(0 3 6 2) '(c major) :size 4 :step 2 :relative nil :variant 'r)) can generate one of two options. The second one is obviously better since it uses a common tone. Even with ambitus-chord I'm not able to make it work consistently. If you have any thoughts I'd be happy to hear them. All the best, Avner Quote Link to comment Share on other sites More sharing options...
opmo Posted April 19, 2019 Share Posted April 19, 2019 Julio, the CHORD-CLOSEST-PATH function is doing exacly that: (chord-closest-path '(eb4g4b4) '(b3eb5g3 cs6e7gs3 b4f5g6 f7e5c2 d4f7e4 gs7e2a8)) Quote Link to comment Share on other sites More sharing options...
Avner Dorman Posted April 19, 2019 Share Posted April 19, 2019 Hi Janusz, If I try this: (chord-closest-path '(b4g4e4c4) '(b4g4e4c4 c5a4e4f4 b4a4f4d4 b4g4e4d4)) It produces either or My question is why does it produce two variants and how can one make sure it always chooses the shortest path? To me it seems like the second option is the correct one since the total half steps between all the voices is 6 (b-c, g-f, c-a) and in the first the total half steps between the voices is 8. Thanks! Avner p.s. - if anyone's curious - Dmitri Tymoczko wrote a software that does minimal voice leading for music 21 in Python. It's available here - http://dmitri.mycpanel.princeton.edu/voiceleading_utilities.py Quote Link to comment Share on other sites More sharing options...
JulioHerrlein Posted April 21, 2019 Author Share Posted April 21, 2019 Dear Friends, Avner, I noticed exactly the same behaviour. I feel a contradiction in having two results for something called "the closest path", because "the closest path", in my interpretation, means only one solution: the one in which the sum of the semitones of all voice-leading moves are the less. It's logically impossible to have "the closest path" with two solutions. You cannot have the cake and eat it too... If the function alternate between two solutions, maybe could be the case that two solutions would sum the same amount of semitonal moves, but the example above do not seems to be that case. After I realized it, I tried to find a way to assure a more accurate expression to represent that one "closest" solution. Janusz, what do you think ? All the best !! I love Opusmodus !! Thank you ! Best, Julio opmo 1 Quote Link to comment Share on other sites More sharing options...
opmo Posted April 21, 2019 Share Posted April 21, 2019 what about (b-a, g-f, e-e, c-c) sum 4 Quote Link to comment Share on other sites More sharing options...
Avner Dorman Posted April 21, 2019 Share Posted April 21, 2019 Yes - obviously you're right - that one is the best. So is there a way to implement that? I actually appreciate that chord-closest-path gives different options on different runs (it's quite creative ) but I would also like to have the option of a truly parsimonious voice leading function. Julio's solution seems to work, but I can't quite manage to make it into a function....and I don't know enough Python (and maybe not enough lisp) to easily convert Tymoczko's code to lisp. By the way - I love Opusmodus as well! Thanks Avner opmo 1 Quote Link to comment Share on other sites More sharing options...
opmo Posted April 21, 2019 Share Posted April 21, 2019 I always try to think of optional rules but I could add strict one as well JulioHerrlein, Stephane Boussuge and lviklund 3 Quote Link to comment Share on other sites More sharing options...
JulioHerrlein Posted April 21, 2019 Author Share Posted April 21, 2019 On 4/19/2019 at 3:27 PM, Avner Dorman said: Hi Janusz, In the example below (first two chords) b-c (+1) g-a (+2) e-f (+1) c-e(+3) total: 7 semitones move In the example below (first two chords) b-c (+1) g-f (-2) e-e (0) c-a (-3) total: 6 semitones move - this is smoother, more parsimounious (considering only the first two chords). I think voice-leading is one of the hardest things !! Quote Link to comment Share on other sites More sharing options...
opmo Posted April 21, 2019 Share Posted April 21, 2019 It is not hard if we look for strict result e.i. smallest total without thinking about parallel movement etc... Here is the result of our example (strict): (chord-closest-path '(b4g4e4c4) '(b4g4e4c4 c5a4e4f4 b4a4f4d4 b4g4e4d4)) c-c = 0 e-e = 0 g-f = 2 b-a = 2 total 4 etc... JulioHerrlein and Stephane Boussuge 2 Quote Link to comment Share on other sites More sharing options...
opmo Posted April 22, 2019 Share Posted April 22, 2019 Please check: '(f3d4e5 e3a3gs5) (chord-closest-path '(f3d4e5) '(f3d4e5 e3a3gs5)) => (f3e5d4 e5gs3a3) '(b3eb5g3 cs6e7gs3 b4f5g6 f7e5c2 d4f7e4 gs7e2a8) (chord-closest-path '(b3eb5g3) '(b3eb5g3 cs6e7gs3 b4f5g6 f7e5c2 d4f7e4 gs7e2a8)) => (g3b3eb5 gs3cs4e5 b3g3f5 c4f3e5 d4e5f3 e5gs3a3) '(b4g4e4c4 c5a4e4f4 b4a4f4d4 b4g4e4d4) (chord-closest-path '(b4g4e4c4) '(b4g4e4c4 c5a4e4f4 b4a4f4d4 b4g4e4d4)) => (c4b4e4g4 e4f4c4a4 b3f4d4a4 d4b3e4g4) yours, Janusz JulioHerrlein 1 Quote Link to comment Share on other sites More sharing options...
Avner Dorman Posted April 22, 2019 Share Posted April 22, 2019 Hi Janusz, These look correct to me! I haven’t done the math, but I don’t see any voice leading issues. Thanks! Avner Quote Link to comment Share on other sites More sharing options...
JulioHerrlein Posted April 22, 2019 Author Share Posted April 22, 2019 Dear Janusz, In my version of Opusmodus, I get this: But maybe it's important to have all compressed within one octave, like this: (g3b3eb4 gs3cs4e4 b3g3f4 c4f3e4 d4e4f3 e4gs3a3) What do you think? Best, Julio Quote Link to comment Share on other sites More sharing options...
opmo Posted April 22, 2019 Share Posted April 22, 2019 What you see above is a new revised function. Examples: Now the start is optional. (chord-closest-path '(f3d4e5 e3a3gs5)) => (f3e5d4 e5gs3a3) (chord-closest-path '(b3eb5g3 cs6e7gs3 b4f5g6 f7e5c2 d4f7e4 gs7e2a8)) => (g3b3eb5 gs3cs4e5 b3g3f5 c4f3e5 d4e5f3 e5gs3a3) (chord-closest-path '(b3eb5g3 cs6e7gs3 b4f5g6 f7e5c2 d4f7e4 gs7e2a8) :start '(c3eb4f5)) => (g5eb4b2 gs5cs3e4 b2g5f4 c3f4e5 d3e5f4 e5gs4a2) (chord-closest-path '(b4g4e4c4 c5a4e4f4 b4a4f4d4 b4g4e4d4)) => (c4b4e4g4 e4f4c4a4 b3f4d4a4 d4b3e4g4) (chord-closest-path '(b4g4e4c4 c5a4e4f4 b4a4f4d4 b4g4e4d4) :start '(c3eb4f5d6)) => (c3e6g4b5 e6f4c3a5 b2f6d4a5 d4b2e6g5) JulioHerrlein 1 Quote Link to comment Share on other sites More sharing options...
opmo Posted April 22, 2019 Share Posted April 22, 2019 Should I change the name to CLOSEST-PATH, VOICE-LEADING or leave as it is JulioHerrlein 1 Quote Link to comment Share on other sites More sharing options...
Avner Dorman Posted April 22, 2019 Share Posted April 22, 2019 Maybe closest-path and keep chord-closest-path as the non strict version? either way - thank you so much!!! Quote Link to comment Share on other sites More sharing options...
JulioHerrlein Posted April 22, 2019 Author Share Posted April 22, 2019 Yes, it also occurred to me to change the name. If this is really an sucessful algorythm for the closest-path, I would suggest to change the old function to another name. Thank you, Janusz ! Best, Julio Quote Link to comment Share on other sites More sharing options...
opmo Posted April 27, 2019 Share Posted April 27, 2019 JulioHerrlein 1 Quote Link to comment Share on other sites More sharing options...
JulioHerrlein Posted April 27, 2019 Author Share Posted April 27, 2019 Done !!! All the best, Janusz ! Julio Quote Link to comment Share on other sites More sharing options...
JulioHerrlein Posted April 28, 2019 Author Share Posted April 28, 2019 Dear Janusz, When I evaluate this expression: (closest-path '(b3eb5g3 cs6e7gs3 b4f5g6 f7e5c2 d4f7e4 gs7e2a8)) I get this: I was expecting this: Maybe a good idea would be something like a octave compress option. Best, Julio Quote Link to comment Share on other sites More sharing options...
AM Posted April 28, 2019 Share Posted April 28, 2019 (ambitus-octaves 'c3 2 (closest-path '(b3eb5g3 cs6e7gs3 b4f5g6 f7e5c2 d4f7e4 gs7e2a8))) => (g3b3eb4 gs3cs4e4 b3g3f4 c4f3e4 d4e4f3 e4gs3a3) or you could put this two FUNCTIONS into ONE => (closest-path*... just take all the values/arguments from the basic version and add the octave-thing? greetings andré JulioHerrlein 1 Quote Link to comment Share on other sites More sharing options...
opmo Posted April 28, 2019 Share Posted April 28, 2019 What you expect it is not a closes path. As you can see the first chord in the sequence is b3eb5g3. There are two option you can use to get the result you are looking for: 1. start chord (closest-path '(b3eb5g3 cs6e7gs3 b4f5g6 f7e5c2 d4f7e4 gs7e2a8) :start 'b4eb4g4) 2. ambitus-chord values (closest-path '(b3eb5g3 cs6e7gs3 b4f5g6 f7e5c2 d4f7e4 gs7e2a8) :ambitus-chord 11) @AM example is another good solution. JulioHerrlein 1 Quote Link to comment Share on other sites More sharing options...
JulioHerrlein Posted April 28, 2019 Author Share Posted April 28, 2019 Dear André, It's all solved with :start option ! You can decide a "model" voicing for the voice-leading ! Very cool ! Best, Julio Thank you, Janusz ! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.