diff --git a/src/config.h b/src/config.h
index aaf2476..c5cb7e8 100644
--- a/src/config.h
+++ b/src/config.h
@@ -47,17 +47,17 @@ public:
 #if (RASPPI==4 || RASPPI==5)
 	// Pi 4 and 5 quad core
 	// These are max values, default is to support 8 in total with optional 16 TGs
-	static const unsigned TGsCore1 = 2;		// process 2 TGs on core 1
-	static const unsigned TGsCore23 = 3;		// process 3 TGs on core 2 and 3 each
-	static const unsigned TGsCore1Opt = 2;		// process optional additional 2 TGs on core 1
-	static const unsigned TGsCore23Opt = 3;		// process optional additional 3 TGs on core 2 and 3 each
+	static const unsigned TGsCore1 = 2*4;		// process 2 TGs on core 1
+	static const unsigned TGsCore23 = 3*4;		// process 3 TGs on core 2 and 3 each
+	static const unsigned TGsCore1Opt = 2*4;		// process optional additional 2 TGs on core 1
+	static const unsigned TGsCore23Opt = 3*4;		// process optional additional 3 TGs on core 2 and 3 each
 	static const unsigned MinToneGenerators = TGsCore1 + 2*TGsCore23;
 	static const unsigned AllToneGenerators = TGsCore1 + TGsCore1Opt + 2*TGsCore23 + 2*TGsCore23Opt;
 	static const unsigned DefToneGenerators = MinToneGenerators;
 #else
 	// Pi 2 or 3 quad core
-	static const unsigned TGsCore1 = 2;		// process 2 TGs on core 1
-	static const unsigned TGsCore23 = 3;		// process 3 TGs on core 2 and 3 each
+	static const unsigned TGsCore1 = 2*4;		// process 2 TGs on core 1
+	static const unsigned TGsCore23 = 3*4;		// process 3 TGs on core 2 and 3 each
 	static const unsigned TGsCore1Opt = 0;
 	static const unsigned TGsCore23Opt = 0;
 	static const unsigned MinToneGenerators = TGsCore1 + 2*TGsCore23;
diff --git a/src/minidexed.cpp b/src/minidexed.cpp
index 3f465bb..7b81bca 100644
--- a/src/minidexed.cpp
+++ b/src/minidexed.cpp
@@ -673,6 +673,8 @@ void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
 	assert (nTG < CConfig::AllToneGenerators);
 	if (nTG >= m_nToneGenerators) return;  // Not an active TG
 
+	LOGNOTE("Voice change: logicalTG=%u, program=%u", nTG, nProgram);
+
 	m_nProgram[nTG] = nProgram;
 
 	uint8_t Buffer[156];
@@ -691,6 +693,7 @@ void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
 	unsigned unisonSpread = m_nUnisonSpread[nTG];
 	for (unsigned v = 0; v < unisonVoices; ++v) {
 		unsigned physicalTG = getPhysicalTG(nTG, v, unisonVoices);
+		LOGNOTE("  affected physicalTG=%u (logicalTG=%u, unisonVoice=%u)", physicalTG, nTG, v);
 		if (physicalTG == nTG || physicalTG >= m_nToneGenerators) continue;
 		m_pTG[physicalTG]->loadVoiceParameters(Buffer);
 		setOPMask(0b111111, physicalTG);
@@ -1311,62 +1314,57 @@ int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG)
 	}
 }
 
-void CMiniDexed::SetVoiceParameter (uint8_t uchOffset, uint8_t uchValue, unsigned nOP, unsigned nTG)
-{
-	assert (nTG < CConfig::AllToneGenerators);
-	if (nTG >= m_nToneGenerators) return;  // Not an active TG
-
-	assert (m_pTG[nTG]);
-	assert (nOP <= 6);
-
-	if (nOP < 6)
-	{
-		nOP = 5 - nOP;		// OPs are in reverse order
-
-		if (uchOffset == DEXED_OP_ENABLE)
-		{
-			if (uchValue)
-			{
-				setOPMask(m_uchOPMask[nTG] | 1 << nOP, nTG);
-			}
-			else
-			{
-				setOPMask(m_uchOPMask[nTG] & ~(1 << nOP), nTG);
-			}
-
-
-			return;
-		}		
-	}
-
-	uchOffset += nOP * 21;
-	assert (uchOffset < 156);
-
-	m_pTG[nTG]->setVoiceDataElement (uchOffset, uchValue);
-}
-
-uint8_t CMiniDexed::GetVoiceParameter (uint8_t uchOffset, unsigned nOP, unsigned nTG)
-{
-	assert (nTG < CConfig::AllToneGenerators);
-	if (nTG >= m_nToneGenerators) return 0;  // Not an active TG
-
-	assert (m_pTG[nTG]);
-	assert (nOP <= 6);
-
-	if (nOP < 6)
-	{
-		nOP = 5 - nOP;		// OPs are in reverse order
-
-		if (uchOffset == DEXED_OP_ENABLE)
-		{
-			return !!(m_uchOPMask[nTG] & (1 << nOP));
-		}
-	}
-
-	uchOffset += nOP * 21;
-	assert (uchOffset < 156);
-
-	return m_pTG[nTG]->getVoiceDataElement (uchOffset);
+void CMiniDexed::SetVoiceParameter(uint8_t uchOffset, uint8_t uchValue, unsigned nOP, unsigned nTG)
+{
+    assert(nTG < CConfig::AllToneGenerators);
+    if (nTG >= m_nToneGenerators) return;  // Not an active TG
+
+    unsigned unisonVoices = m_nUnisonVoices[nTG];
+    if (unisonVoices < 1) unisonVoices = 1;
+    LOGNOTE("SetVoiceParameter: logicalTG=%u, unisonVoices=%u", nTG, unisonVoices);
+    for (unsigned v = 0; v < unisonVoices; ++v) {
+        unsigned physicalTG = getPhysicalTG(nTG, v, unisonVoices);
+        LOGNOTE("  -> physicalTG=%u (logicalTG=%u, unisonVoice=%u)", physicalTG, nTG, v);
+        if (physicalTG >= m_nToneGenerators) break;
+        assert(m_pTG[physicalTG]);
+        unsigned op = nOP;
+        if (op < 6) {
+            op = 5 - op; // OPs are in reverse order
+            if (uchOffset == DEXED_OP_ENABLE) {
+                if (uchValue) {
+                    setOPMask(m_uchOPMask[physicalTG] | 1 << op, physicalTG);
+                } else {
+                    setOPMask(m_uchOPMask[physicalTG] & ~(1 << op), physicalTG);
+                }
+                continue;
+            }
+        }
+        uint8_t offset = uchOffset + op * 21;
+        assert(offset < 156);
+        m_pTG[physicalTG]->setVoiceDataElement(offset, uchValue);
+    }
+}
+
+uint8_t CMiniDexed::GetVoiceParameter(uint8_t uchOffset, unsigned nOP, unsigned nTG)
+{
+    assert(nTG < CConfig::AllToneGenerators);
+    if (nTG >= m_nToneGenerators) return 0;  // Not an active TG
+
+    unsigned unisonVoices = m_nUnisonVoices[nTG];
+    if (unisonVoices < 1) unisonVoices = 1;
+    unsigned physicalTG = getPhysicalTG(nTG, 0, unisonVoices); // Use first physical TG for this logical TG
+    LOGNOTE("GetVoiceParameter: logicalTG=%u, unisonVoices=%u, physicalTG=%u", nTG, unisonVoices, physicalTG);
+    assert(m_pTG[physicalTG]);
+    unsigned op = nOP;
+    if (op < 6) {
+        op = 5 - op; // OPs are in reverse order
+        if (uchOffset == DEXED_OP_ENABLE) {
+            return !!(m_uchOPMask[physicalTG] & (1 << op));
+        }
+    }
+    uint8_t offset = uchOffset + op * 21;
+    assert(offset < 156);
+    return m_pTG[physicalTG]->getVoiceDataElement(offset);
 }
 
 std::string CMiniDexed::GetVoiceName (unsigned nTG)