From 2b1c7c4562e80e3374b1cb60df66e350657013e6 Mon Sep 17 00:00:00 2001
From: Rene Stange <rsta2@o2online.de>
Date: Mon, 14 Mar 2022 10:19:44 +0100
Subject: [PATCH] Add volume control to MIDI CC and UI

---
 src/mididevice.cpp    |  5 +++
 src/minidexed.cpp     | 13 +++++++
 src/minidexed.h       |  1 +
 src/userinterface.cpp | 85 ++++++++++++++++++++++++++++++++++++++++---
 src/userinterface.h   | 11 ++++++
 5 files changed, 109 insertions(+), 6 deletions(-)

diff --git a/src/mididevice.cpp b/src/mididevice.cpp
index 8b0d570..4f92e61 100644
--- a/src/mididevice.cpp
+++ b/src/mididevice.cpp
@@ -31,6 +31,7 @@
 #define MIDI_AFTERTOUCH		0b1010			// TODO
 #define MIDI_CONTROL_CHANGE	0b1011
 	#define MIDI_CC_BANK_SELECT_MSB		0	// TODO
+	#define MIDI_CC_VOLUME			7
 	#define MIDI_CC_BANK_SELECT_LSB		32
 #define MIDI_PROGRAM_CHANGE	0b1100
 #define MIDI_PITCH_BEND		0b1110
@@ -130,6 +131,10 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
 
 		switch (pMessage[1])
 		{
+		case MIDI_CC_VOLUME:
+			m_pSynthesizer->SetVolume (pMessage[2]);
+			break;
+
 		case MIDI_CC_BANK_SELECT_LSB:
 			m_pSynthesizer->BankSelectLSB (pMessage[2]);
 			break;
diff --git a/src/minidexed.cpp b/src/minidexed.cpp
index 1edeee9..4217c35 100644
--- a/src/minidexed.cpp
+++ b/src/minidexed.cpp
@@ -98,6 +98,7 @@ bool CMiniDexed::Initialize (void)
 
 	activate ();
 
+	SetVolume (100);
 	ProgramChange (0);
 	setTranspose (24);
 
@@ -199,6 +200,18 @@ void CMiniDexed::ProgramChange (unsigned nProgram)
 	m_UI.ProgramChanged (nProgram);
 }
 
+void CMiniDexed::SetVolume (unsigned nVolume)
+{
+	if (nVolume > 127)
+	{
+		return;
+	}
+
+	setGain (nVolume / 127.0);
+
+	m_UI.VolumeChanged (nVolume);
+}
+
 void CMiniDexed::ProcessSound (void)
 {
 	assert (m_pSoundDevice);
diff --git a/src/minidexed.h b/src/minidexed.h
index 29c9214..17ca610 100644
--- a/src/minidexed.h
+++ b/src/minidexed.h
@@ -57,6 +57,7 @@ public:
 
 	void BankSelectLSB (unsigned nBankLSB);
 	void ProgramChange (unsigned nProgram);
+	void SetVolume (unsigned nVolume);
 
 private:
 	void ProcessSound (void);
diff --git a/src/userinterface.cpp b/src/userinterface.cpp
index 6cc5b90..6f573e2 100644
--- a/src/userinterface.cpp
+++ b/src/userinterface.cpp
@@ -38,7 +38,8 @@ CUserInterface::CUserInterface (CMiniDexed *pMiniDexed, CGPIOManager *pGPIOManag
 	m_pRotaryEncoder (0),
 	m_UIMode (UIModeVoiceSelect),
 	m_nBank (0),
-	m_nProgram (0)
+	m_nProgram (0),
+	m_nVolume (0)
 {
 }
 
@@ -73,7 +74,7 @@ bool CUserInterface::Initialize (void)
 		m_pLCDBuffered = new CWriteBufferDevice (m_pLCD);
 		assert (m_pLCDBuffered);
 
-		LCDWrite ("\x1B[?25l");		// cursor off
+		LCDWrite ("\x1B[?25l\x1B""d+");		// cursor off, autopage mode
 
 		LOGDBG ("LCD initialized");
 	}
@@ -121,9 +122,9 @@ void CUserInterface::BankSelected (unsigned nBankLSB)
 	if (m_UIMode == UIModeBankSelect)
 	{
 		CString String;
-		String.Format ("\n\r%-12uBANK%s", nBankLSB+1, BankName.c_str ());
+		String.Format ("%u", nBankLSB+1);
 
-		LCDWrite (String);
+		DisplayWrite (String, "BANK", BankName.c_str ());
 	}
 }
 
@@ -145,12 +146,68 @@ void CUserInterface::ProgramChanged (unsigned nProgram)
 	if (m_UIMode == UIModeVoiceSelect)
 	{
 		CString String;
-		String.Format ("\n\r%-11uVOICE%s", nProgram, ProgramName);
+		String.Format ("%u", nProgram);
 
-		LCDWrite (String);
+		DisplayWrite (String, "VOICE", ProgramName);
 	}
 }
 
+void CUserInterface::VolumeChanged (unsigned nVolume)
+{
+	assert (nVolume < 128);
+	m_nVolume = nVolume;
+
+	if (m_UIMode == UIModeVolume)
+	{
+		char VolumeBar[CConfig::LCDColumns+1];
+		memset (VolumeBar, 0xFF, sizeof VolumeBar);	// 0xFF is the block character
+		VolumeBar[nVolume * CConfig::LCDColumns / 127] = '\0';
+
+		DisplayWrite ("", "VOLUME", VolumeBar);
+	}
+}
+
+void CUserInterface::DisplayWrite (const char *pInstance, const char *pMenu,
+				   const char *pParam, const char *pValue)
+{
+	assert (pInstance);
+	assert (pMenu);
+	assert (pParam);
+
+	CString Msg ("\x1B[H");		// cursor home
+
+	// first line
+	Msg.Append (pInstance);
+
+	size_t nLen = strlen (pInstance) + strlen (pMenu);
+	if (nLen < CConfig::LCDColumns)
+	{
+		for (unsigned i = CConfig::LCDColumns-nLen; i > 0; i--)
+		{
+			Msg.Append (" ");
+		}
+	}
+
+	Msg.Append (pMenu);
+
+	// second line
+	CString ParamValue (pParam);
+	if (pValue)
+	{
+		ParamValue.Append ("=");
+		ParamValue.Append (pValue);
+	}
+
+	Msg.Append (ParamValue);
+
+	if (ParamValue.GetLength () < CConfig::LCDColumns)
+	{
+		Msg.Append ("\x1B[K");		// clear end of line
+	}
+
+	LCDWrite (Msg);
+}
+
 void CUserInterface::LCDWrite (const char *pString)
 {
 	if (m_pLCDBuffered)
@@ -210,6 +267,22 @@ void CUserInterface::EncoderEventHandler (CKY040::TEvent Event)
 		}
 		break;
 
+	case UIModeVolume: {
+		const int Increment = 128 / CConfig::LCDColumns;
+
+		int nVolume = m_nVolume + nStep*Increment;
+		if (nVolume < 0)
+		{
+			nVolume = 0;
+		}
+		else if (nVolume > 127)
+		{
+			nVolume = 127;
+		}
+
+		m_pMiniDexed->SetVolume (nVolume);
+		} break;
+
 	default:
 		break;
 	}
diff --git a/src/userinterface.h b/src/userinterface.h
index 46a0cff..6e34d99 100644
--- a/src/userinterface.h
+++ b/src/userinterface.h
@@ -40,8 +40,17 @@ public:
 
 	void BankSelected (unsigned nBankLSB);		// 0 .. 127
 	void ProgramChanged (unsigned nProgram);	// 0 .. 127
+	void VolumeChanged (unsigned nVolume);		// 0 .. 127
 
 private:
+	// Print to display in this format:
+	// +----------------+
+	// |INSTANCE    MENU|
+	// |PARAM[=VALUE]   |
+	// +----------------+
+	void DisplayWrite (const char *pInstance, const char *pMenu,
+			   const char *pParam, const char *pValue = nullptr);
+
 	void LCDWrite (const char *pString);		// Print to optional HD44780 display
 
 	void EncoderEventHandler (CKY040::TEvent Event);
@@ -53,6 +62,7 @@ private:
 		UIModeStart,
 		UIModeVoiceSelect = UIModeStart,
 		UIModeBankSelect,
+		UIModeVolume,
 		UIModeUnknown
 	};
 
@@ -70,6 +80,7 @@ private:
 
 	unsigned m_nBank;
 	unsigned m_nProgram;
+	unsigned m_nVolume;
 };
 
 #endif