# Parsimonious Voice Leading (again): attempts to provide an algorithm

## Recommended Posts

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

##### Share on other sites

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)```

##### Share on other sites

• 1 year later...

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))

(chord-closest-path (list (car chords)) chords))

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

##### Share on other sites

• 3 weeks later...

Julio, the CHORD-CLOSEST-PATH function is doing exacly that:

`(chord-closest-path '(eb4g4b4) '(b3eb5g3 cs6e7gs3 b4f5g6 f7e5c2 d4f7e4 gs7e2a8))`
##### Share on other sites

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

##### Share on other sites

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

##### Share on other sites

what about (b-a, g-f, e-e, c-c) sum 4

##### Share on other sites

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

##### Share on other sites

I always try to think of optional rules but I could add strict one as well

##### Share on other sites

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  !!

##### Share on other sites

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...

##### Share on other sites

`'(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

##### Share on other sites

Hi Janusz,

These look correct to me! I haven’t done the math, but I don’t see any voice leading issues.

Thanks!

Avner

##### Share on other sites

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

##### Share on other sites

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)```

##### Share on other sites

Should I change the name to CLOSEST-PATH, VOICE-LEADING or leave as it is

##### Share on other sites

Maybe closest-path and keep chord-closest-path as the non strict version?

either way - thank you so much!!!

##### Share on other sites

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

##### Share on other sites

Done !!!

All the best, Janusz !

Julio

##### Share on other sites

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

##### Share on other sites

```(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é

##### Share on other sites

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.

##### Share on other sites

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 !

## Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

×   Pasted as rich text.   Paste as plain text instead

Only 75 emoji are allowed.

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×

• Lessons