I am also posting my current solution:
(The player must be revised, but the way to write a score will remain as it is. Honestly, I realised my method to write a score is more time-consuming than the method using Panola.)
(
fork{
var bwv816_4, player;
bwv816_4 = [
[ // bar 0
[ // voice 1, 2, 3: all voices have the same rhythms.
[[83,74,55],65,4], //entry: [aChord, velocity, length.reciprocal]
[[79,71,67],62,4]
]
],
[ // bar 1
[ // voice 1
[74,62,4],
[76,65,8], [78,64,8],
[79,65,4],
[76,62,4]
],
[ // voice 2
[69,62,2],
[71,65,4],
[67,62,4]
],
[ // voice 3
[66,62,4],
[54,62,4],
[52,65,4],
[64,62,4]
]
],
[ // bar 2
[ // voice 1, 2
[[71,66],62,2],
[[76,67],65,4],
[[72,69],62,4]
],
[ // voice 3
[62,62,4],
[50,62,4],
[48,65,4],
[60,62,4]
]
],
[ // bar 3
[ // voice 1 (occasionally 2)
[[69,66],62,4],
[71,64,8], [72,62,8],
[74,64,8], [71,62,8],
[72,64,8], [69,62,8]
],
[ // voice 3 (occasionally 2)
[62,62,4],
[57,62,4],
[[54,69],65,4],
[50,62,4]
]
],
[ // bar 4
[ // voice 1 (occasionally 2)
[72,62,8], [71,62,8],
[69,64,8], [67,62,8],
[[83,74],65,4],
[[79,71],62,4]
],
[ // voice 3 (occasionally 2)
[[67,55],65,4],
[50,62,4],
[43,62,8], [50,62,8],
[52,64,8], [54,62,8]
]
],
[ // bar 5
[ // voice 1, 1
[[76,73],62,4],
[[76,73],62,8], [[78,62],62,8],
[[79,76],62,4],
[[76,73],62,4]
],
[ // voice 3
[55,62,8], [57,62,8],
[55,64,8], [54,62,8],
[52,62,8], [50,62,8],
[52,64,8], [54,62,8]
]
],
[ // bar 6
[ // voice 1
[73,62,4],
[74,65,8], [76,64,8],
[78,65,8], [74,62,8],
[79,65,8], [76,62,8]
],
[ // voice 2
[69,64,0.8]
],
[ // voice 3
[55,65,8], [57,62,8],
[59,65,8], [61,62,8],
[62,62,4],
[61,62,4],
]
],
[ // bar 7
[ // voice 1
[78,65,8], [74,62,8],
[81,65,4],
[79,65,8], [78,62,8],
[76,65,8], [78,62,8]
],
[ // voice 2
[\reat,64,4],
[74,64,2],
[73,64,4],
],
[ // voice 3
[62,65,4],
[54,62,8], [55,62,8],
[57,62,4],
[45,62,4],
]
],
[ // bar 8
[ // voice 1, 2, 3: all voices have the same rhythms.
[[74,50],65,2], //entry: [aChord, velocity, length.reciprocal]
];
]
];
player = {
|music, synth, tempo=60, nthBar=0, vol=1|
var timeFacror, entries, voiceItems, maxItemVoice, lastVoice;
timeFacror = 60 / tempo * 4;
entries = music[nthBar];
voiceItems = entries.size.collect{
|nth|
entries[nth].size.collect{
|ith|
entries[nth][ith][2].reciprocal
}
};
maxItemVoice = voiceItems.size.collect{
|nth|
var items, range;
items = voiceItems[nth];
range = (0..items.size - 2);
(items.size == 1).if{ 0 }{ items[range].sum }
};
lastVoice = maxItemVoice.maxIndex;
("Bar"+nthBar++"\nThe voice which will be played lastly:" + lastVoice).postln;
{
|vce|
fork{
(
"\t\tVoice"+vce+"numItems:" + entries[vce].size +
"\n\t\t\t\t\t\t "++entries[vce]
).postln;
entries[vce].do{
|entry,idxEntry|
var aChord, entryPlayer, len, untilNext, rAmp, rRls;
aChord = entry[0].asArray;
entryPlayer = Array.newClear(10);
len = entry[2];
untilNext = len.reciprocal;
rAmp = -0.1.rand;
rRls = rrand(0.01, 0.03);
aChord.size.do{
|i|
var aNote, amp;
aNote = aChord[i];
amp = entry[1]/127 * (2**(-3 - (i - 1.0.rand) + rAmp));
(instrument: \default, midinote: aNote, amp: amp * vol, dur: untilNext, len:1).play;
(
[
"voice:" + vce,
"MIDI note:" + aNote,
"rhythm:" + untilNext.asString.padRight(8, "0"),
"dbFS:" + amp.ampdb.round(0.01).asString.padRight(6, "0")+"dB"
]
).postln;
rrand(256, 384).reciprocal.wait;
};
( timeFacror * untilNext + (128.reciprocal.rand2) - rRls).wait;
aChord.size.do{
|i|
entryPlayer[i].release
};
rRls.wait;
if (
(nthBar != (music.size - 1))
&&
vce == lastVoice
&&
(idxEntry == (entries[vce].size - 1))
)
{
nthBar = nthBar+1;
player.(music, synth, tempo, nthBar, vol);
}
}
};
}!entries.size;
};
// play music
player.((bwv816_4!2).flatten(1), \default, 150, 0, 1)
}
)