// // userinterface.cpp // // MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi // Copyright (C) 2022 The MiniDexed Team // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include "userinterface.h" #include "minidexed.h" #include #include #include #include #include LOGMODULE ("ui"); CUserInterface::CUserInterface (CMiniDexed *pMiniDexed, CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster, CConfig *pConfig) : m_pMiniDexed (pMiniDexed), m_pGPIOManager (pGPIOManager), m_pI2CMaster (pI2CMaster), m_pConfig (pConfig), m_pLCD (0), m_pLCDBuffered (0), m_pUIButtons (0), m_pRotaryEncoder (0), m_bSwitchPressed (false), m_Menu (this, pMiniDexed) { } CUserInterface::~CUserInterface (void) { delete m_pRotaryEncoder; delete m_pUIButtons; delete m_pLCDBuffered; delete m_pLCD; } bool CUserInterface::Initialize (void) { assert (m_pConfig); if (m_pConfig->GetLCDEnabled ()) { unsigned i2caddr = m_pConfig->GetLCDI2CAddress (); unsigned ssd1306addr = m_pConfig->GetSSD1306LCDI2CAddress (); if (ssd1306addr != 0) { m_pSSD1306 = new CSSD1306Device (m_pConfig->GetSSD1306LCDWidth (), m_pConfig->GetSSD1306LCDHeight (), m_pI2CMaster, ssd1306addr, m_pConfig->GetSSD1306LCDRotate (), m_pConfig->GetSSD1306LCDMirror ()); LOGDBG ("LCD: SSD1306"); if (!m_pSSD1306->Initialize ()) { return false; } m_pLCD = m_pSSD1306; } else if (i2caddr == 0) { m_pHD44780 = new CHD44780Device (m_pConfig->GetLCDColumns (), m_pConfig->GetLCDRows (), m_pConfig->GetLCDPinData4 (), m_pConfig->GetLCDPinData5 (), m_pConfig->GetLCDPinData6 (), m_pConfig->GetLCDPinData7 (), m_pConfig->GetLCDPinEnable (), m_pConfig->GetLCDPinRegisterSelect (), m_pConfig->GetLCDPinReadWrite ()); LOGDBG ("LCD: HD44780"); if (!m_pHD44780->Initialize ()) { return false; } m_pLCD = m_pHD44780; } else { m_pHD44780 = new CHD44780Device (m_pI2CMaster, i2caddr, m_pConfig->GetLCDColumns (), m_pConfig->GetLCDRows ()); LOGDBG ("LCD: HD44780 I2C"); if (!m_pHD44780->Initialize ()) { return false; } m_pLCD = m_pHD44780; } assert (m_pLCD); m_pLCDBuffered = new CWriteBufferDevice (m_pLCD); assert (m_pLCDBuffered); LCDWrite ("\x1B[?25l\x1B""d+"); // cursor off, autopage mode LCDWrite ("MiniDexed\nLoading..."); m_pLCDBuffered->Update (); LOGDBG ("LCD initialized"); } m_pUIButtons = new CUIButtons ( m_pConfig->GetButtonPinPrev (), m_pConfig->GetButtonActionPrev (), m_pConfig->GetButtonPinNext (), m_pConfig->GetButtonActionNext (), m_pConfig->GetButtonPinBack (), m_pConfig->GetButtonActionBack (), m_pConfig->GetButtonPinSelect (), m_pConfig->GetButtonActionSelect (), m_pConfig->GetButtonPinHome (), m_pConfig->GetButtonActionHome (), m_pConfig->GetButtonPinPgmUp (), m_pConfig->GetButtonActionPgmUp (), m_pConfig->GetButtonPinPgmDown (), m_pConfig->GetButtonActionPgmDown (), m_pConfig->GetButtonPinTGUp (), m_pConfig->GetButtonActionTGUp (), m_pConfig->GetButtonPinTGDown (), m_pConfig->GetButtonActionTGDown (), m_pConfig->GetDoubleClickTimeout (), m_pConfig->GetLongPressTimeout (), m_pConfig->GetMIDIButtonNotes (), m_pConfig->GetMIDIButtonPrev (), m_pConfig->GetMIDIButtonNext (), m_pConfig->GetMIDIButtonBack (), m_pConfig->GetMIDIButtonSelect (), m_pConfig->GetMIDIButtonHome (), m_pConfig->GetMIDIButtonPgmUp (), m_pConfig->GetMIDIButtonPgmDown (), m_pConfig->GetMIDIButtonTGUp (), m_pConfig->GetMIDIButtonTGDown () ); assert (m_pUIButtons); if (!m_pUIButtons->Initialize ()) { return false; } m_pUIButtons->RegisterEventHandler (UIButtonsEventStub, this); UISetMIDIButtonChannel (m_pConfig->GetMIDIButtonCh ()); LOGDBG ("Button User Interface initialized"); if (m_pConfig->GetEncoderEnabled ()) { m_pRotaryEncoder = new CKY040 (m_pConfig->GetEncoderPinClock (), m_pConfig->GetEncoderPinData (), m_pConfig->GetButtonPinShortcut (), m_pGPIOManager); assert (m_pRotaryEncoder); if (!m_pRotaryEncoder->Initialize ()) { return false; } m_pRotaryEncoder->RegisterEventHandler (EncoderEventStub, this); LOGDBG ("Rotary encoder initialized"); } m_Menu.EventHandler (CUIMenu::MenuEventUpdate); return true; } void CUserInterface::Process (void) { if (m_pLCDBuffered) { m_pLCDBuffered->Update (); } if (m_pUIButtons) { m_pUIButtons->Update(); } } void CUserInterface::ParameterChanged (void) { m_Menu.EventHandler (CUIMenu::MenuEventUpdate); } void CUserInterface::DisplayWrite (const char *pMenu, const char *pParam, const char *pValue, bool bArrowDown, bool bArrowUp) { assert (pMenu); assert (pParam); assert (pValue); CString Msg ("\x1B[H\E[?25l"); // cursor home and off // first line Msg.Append (pParam); size_t nLen = strlen (pParam) + strlen (pMenu); if (nLen < m_pConfig->GetLCDColumns ()) { for (unsigned i = m_pConfig->GetLCDColumns ()-nLen; i > 0; i--) { Msg.Append (" "); } } Msg.Append (pMenu); // second line CString Value (" "); if (bArrowDown) { Value = "\x7F"; // arrow left character } Value.Append (pValue); if (bArrowUp) { if (Value.GetLength () < m_pConfig->GetLCDColumns ()-1) { for (unsigned i = m_pConfig->GetLCDColumns ()-Value.GetLength ()-1; i > 0; i--) { Value.Append (" "); } } Value.Append ("\x7E"); // arrow right character } Msg.Append (Value); if (Value.GetLength () < m_pConfig->GetLCDColumns ()) { Msg.Append ("\x1B[K"); // clear end of line } LCDWrite (Msg); } void CUserInterface::LCDWrite (const char *pString) { if (m_pLCDBuffered) { m_pLCDBuffered->Write (pString, strlen (pString)); } } void CUserInterface::EncoderEventHandler (CKY040::TEvent Event) { switch (Event) { case CKY040::EventSwitchDown: m_bSwitchPressed = true; break; case CKY040::EventSwitchUp: m_bSwitchPressed = false; break; case CKY040::EventClockwise: if (m_bSwitchPressed) { // We must reset the encoder switch button to prevent events from being // triggered after the encoder is rotated m_pUIButtons->ResetButton(m_pConfig->GetButtonPinShortcut()); m_Menu.EventHandler(CUIMenu::MenuEventPressAndStepUp); } else { m_Menu.EventHandler(CUIMenu::MenuEventStepUp); } break; case CKY040::EventCounterclockwise: if (m_bSwitchPressed) { m_pUIButtons->ResetButton(m_pConfig->GetButtonPinShortcut()); m_Menu.EventHandler(CUIMenu::MenuEventPressAndStepDown); } else { m_Menu.EventHandler(CUIMenu::MenuEventStepDown); } break; case CKY040::EventSwitchHold: if (m_pRotaryEncoder->GetHoldSeconds () >= 120) { delete m_pLCD; // reset LCD reboot (); } break; default: break; } } void CUserInterface::EncoderEventStub (CKY040::TEvent Event, void *pParam) { CUserInterface *pThis = static_cast (pParam); assert (pThis != 0); pThis->EncoderEventHandler (Event); } void CUserInterface::UIButtonsEventHandler (CUIButton::BtnEvent Event) { switch (Event) { case CUIButton::BtnEventPrev: m_Menu.EventHandler (CUIMenu::MenuEventStepDown); break; case CUIButton::BtnEventNext: m_Menu.EventHandler (CUIMenu::MenuEventStepUp); break; case CUIButton::BtnEventBack: m_Menu.EventHandler (CUIMenu::MenuEventBack); break; case CUIButton::BtnEventSelect: m_Menu.EventHandler (CUIMenu::MenuEventSelect); break; case CUIButton::BtnEventHome: m_Menu.EventHandler (CUIMenu::MenuEventHome); break; case CUIButton::BtnEventPgmUp: m_Menu.EventHandler (CUIMenu::MenuEventPgmUp); break; case CUIButton::BtnEventPgmDown: m_Menu.EventHandler (CUIMenu::MenuEventPgmDown); break; case CUIButton::BtnEventTGUp: m_Menu.EventHandler (CUIMenu::MenuEventTGUp); break; case CUIButton::BtnEventTGDown: m_Menu.EventHandler (CUIMenu::MenuEventTGDown); break; default: break; } } void CUserInterface::UIButtonsEventStub (CUIButton::BtnEvent Event, void *pParam) { CUserInterface *pThis = static_cast (pParam); assert (pThis != 0); pThis->UIButtonsEventHandler (Event); } void CUserInterface::UIMIDICmdHandler (unsigned nMidiCh, unsigned nMidiCmd, unsigned nMidiData1, unsigned nMidiData2) { if (m_nMIDIButtonCh == CMIDIDevice::Disabled) { // MIDI buttons are not enabled return; } if ((m_nMIDIButtonCh != nMidiCh) && (m_nMIDIButtonCh != CMIDIDevice::OmniMode)) { // Message not on the MIDI Button channel and MIDI buttons not in OMNI mode return; } if (m_pUIButtons) { m_pUIButtons->BtnMIDICmdHandler (nMidiCmd, nMidiData1, nMidiData2); } } void CUserInterface::UISetMIDIButtonChannel (unsigned uCh) { // Mirrors the logic in Performance Config for handling MIDI channel configuration if (uCh == 0) { m_nMIDIButtonCh = CMIDIDevice::Disabled; } else if (uCh < CMIDIDevice::Channels) { m_nMIDIButtonCh = uCh - 1; } else { m_nMIDIButtonCh = CMIDIDevice::OmniMode; } }