gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

mqGui.cpp (10183B)


      1 #include "mqGui.h"
      2 
      3 #include <iostream>
      4 
      5 #include "mqLib/microq.h"
      6 #include "mqLib/mqhardware.h"
      7 
      8 namespace mqConsoleLib
      9 {
     10 	constexpr int g_deviceW = 140;
     11 	constexpr int g_deviceH = 16;
     12 
     13 	Gui::Gui(mqLib::MicroQ& _mq)
     14 		: m_mq(_mq)
     15 		, m_win(g_deviceW, g_deviceH + 6)
     16 	{
     17 	}
     18 
     19 	void Gui::render()
     20 	{
     21 		handleTerminalSize();
     22 
     23 		m_win.fill_fg(1,1,g_deviceW, 1, Term::fg::bright_yellow);
     24 		m_win.fill_fg(1,g_deviceH,g_deviceW, g_deviceH, Term::fg::bright_yellow);
     25 
     26 		for(int i=2; i<g_deviceH; ++i)
     27 		{
     28 			m_win.fill_fg(1,i,1,i, Term::fg::bright_yellow);
     29 			m_win.fill_fg(g_deviceW,i,g_deviceW,i, Term::fg::bright_yellow);
     30 		}
     31 
     32 		m_win.print_rect(1,1,g_deviceW, g_deviceH);
     33 
     34 		m_win.fill_fg(3, g_deviceH>>1, 9, g_deviceH>>1, Term::fg::bright_yellow);
     35 		m_win.print_str(3, g_deviceH >> 1, "microQ");
     36 		m_win.fill_fg((g_deviceW>>1) - 3, g_deviceH - 1, (g_deviceW>>1) + 7 , g_deviceH - 1, Term::fg::bright_blue);
     37 		m_win.print_str((g_deviceW>>1) - 3, g_deviceH - 1, "waldorf");
     38 
     39 		constexpr int lcdX = 10;
     40 
     41 		renderAboveLCD(lcdX, 3);
     42 		renderLCD(lcdX,6);
     43 		renderBelowLCD(lcdX, 12);
     44 
     45 		renderAlphaAndPlay(lcdX + 28, 7);
     46 
     47 		renderMultimodePeek(lcdX + 23, g_deviceH - 2);
     48 		renderVerticalLedsAndButtons(lcdX + 46, 3);
     49 		renderCursorBlock(lcdX + 59, 6);
     50 
     51 		constexpr int rightBase = 81;
     52 		renderMatrixLEDs(rightBase, 3);
     53 		renderMatrixText(rightBase + 18, 3);
     54 		renderRightEncoders(rightBase + 20, g_deviceH - 4);
     55 
     56 		renderButton(mqLib::Buttons::ButtonType::Power, g_deviceW - 6, g_deviceH - 2);
     57 		renderLED(m_mq.getLedState(mqLib::Leds::Led::Power), g_deviceW - 4, g_deviceH - 3);
     58 		renderLabel(g_deviceW - 2, g_deviceH - 1, "Power", true);
     59 
     60 		renderHelp(2, g_deviceH + 2);
     61 		renderDebug(2, g_deviceH + 5);
     62 
     63 		std::cout << m_win.render(1,1,true) << std::flush;
     64 	}
     65 
     66 	void Gui::renderAboveLCD(int xStart, int yStart)
     67 	{
     68 		constexpr int stepX = 5;
     69 		constexpr int stepY = 2;
     70 		int x = xStart + 3;
     71 		renderLED(mqLib::Leds::Led::Inst1, x, yStart);	x += stepX;
     72 		renderLED(mqLib::Leds::Led::Inst2, x, yStart);	x += stepX;
     73 		renderLED(mqLib::Leds::Led::Inst3, x, yStart);	x += stepX;
     74 		renderLED(mqLib::Leds::Led::Inst4, x, yStart);
     75 
     76 		x = xStart + 3;
     77 		auto y = yStart + stepY;
     78 		renderButton(mqLib::Buttons::ButtonType::Inst1, x, y);	x += stepX;
     79 		renderButton(mqLib::Buttons::ButtonType::Inst2, x, y);	x += stepX;
     80 		renderButton(mqLib::Buttons::ButtonType::Inst3, x, y);	x += stepX;
     81 		renderButton(mqLib::Buttons::ButtonType::Inst4, x, y);
     82 
     83 		x = xStart + 4;
     84 		y = yStart + 1;
     85 		renderLabel(x, y, "1", false);	x += stepX;
     86 		renderLabel(x, y, "2", false);	x += stepX;
     87 		renderLabel(x, y, "3", false);	x += stepX;
     88 		renderLabel(x, y, "4", false);
     89 	}
     90 
     91 	void Gui::renderLCD(int x, int y)
     92 	{
     93 		m_win.print_rect(x+1, y+1, x+22, y+4,true);
     94 
     95 		m_win.fill_bg(x+2, y+2, x+21, y+3, Term::bg::green);
     96 		m_win.fill_fg(x+2, y+2, x+21, y+3, Term::fg::black);
     97 
     98 		std::array<char, 40> text{};
     99 		m_mq.readLCD(text);
    100 
    101 		int cx = x + 2;
    102 		int cy = y + 2;
    103 
    104 		for(size_t i=0; i<text.size(); ++i)
    105 		{
    106 			// https://en.wikipedia.org/wiki/List_of_Unicode_characters
    107 
    108 			char32_t c = static_cast<uint8_t>(text[i]);
    109 			switch (c)
    110 			{
    111 			case 0:		c = 0x24EA;		break;
    112 			case 1:		c = 0x2460;		break;
    113 			case 2:		c = 0x2461;		break;
    114 			case 3:		c = 0x2462;		break;
    115 			case 4:		c = 0x2463;		break;
    116 			case 5:		c = 0x2464;		break;
    117 			case 6:		c = 0x2465;		break;
    118 			case 7:		c = 0x2466;		break;
    119 			case 0xdf:	c = 0x2598;		break;	// Quadrant upper left
    120 			case 0xff:	c = 0x2588;		break;	// Full block
    121 			default:
    122 				if(c < 32 || c >= 0x0010FFFF)
    123 					c = '?';
    124 			}
    125 			m_win.set_char(cx, cy, c);
    126 			++cx;
    127 			if(i == 19)
    128 			{
    129 				cx -= 20;
    130 				++cy;
    131 			}
    132 		}
    133 	}
    134 
    135 	void Gui::renderBelowLCD(int x, int y)
    136 	{
    137 		renderEncoder(mqLib::Buttons::Encoders::LcdLeft, x + 4, y);
    138 		renderEncoder(mqLib::Buttons::Encoders::LcdRight, x + 14, y);
    139 	}
    140 
    141 	void Gui::renderAlphaAndPlay(int x, int y)
    142 	{
    143 		renderEncoder(mqLib::Buttons::Encoders::Master, x, y);
    144 		renderLED(mqLib::Leds::Led::Play, x + 8, y + 2);
    145 		renderButton(mqLib::Buttons::ButtonType::Play, x + 6, y + 3);
    146 		renderLabel(x+5, y+4, "Play", false);
    147 	}
    148 
    149 	void Gui::renderMultimodePeek(int x, int y)
    150 	{
    151 		const auto xStart = x;
    152 		renderLED(mqLib::Leds::Led::Multimode, x, y);
    153 		x += 4;
    154 		renderButton(mqLib::Buttons::ButtonType::Multimode, x, y);
    155 		x += 6;
    156 		renderLED(mqLib::Leds::Led::Peek, x, y);
    157 		x += 4;
    158 		renderButton(mqLib::Buttons::ButtonType::Peek, x, y);
    159 		renderLabel(xStart - 2, y+1, "Multimode   Peek", false);
    160 	}
    161 
    162 	void Gui::renderCursorBlock(int x, int y)
    163 	{
    164 		renderButton(mqLib::Buttons::ButtonType::Left, x, y + 2);
    165 		renderButton(mqLib::Buttons::ButtonType::Right, x + 6, y + 2);
    166 		renderButton(mqLib::Buttons::ButtonType::Up, x + 3, y);
    167 		renderButton(mqLib::Buttons::ButtonType::Down, x + 3, y + 4);
    168 	}
    169 
    170 	void Gui::renderVerticalLedsAndButtons(int xStart, int y)
    171 	{
    172 		constexpr int spreadX = 4;
    173 		constexpr int stepY = 2;
    174 
    175 		int x = xStart;
    176 		renderLED(mqLib::Leds::Led::Global, x, y);	x += spreadX;
    177 		renderButton(mqLib::Buttons::ButtonType::Global, x, y);
    178 		x = xStart;
    179 		renderLabel(x + 3, y + 1, "Global", true);
    180 		renderLabel(x + 4, y + 1, "Util", false);
    181 		y += stepY;
    182 		renderLED(mqLib::Leds::Led::Multi, x, y);	x += spreadX;
    183 		renderButton(mqLib::Buttons::ButtonType::Multi, x, y);
    184 		x = xStart;
    185 		renderLabel(x + 3, y + 1, "Multi", true);
    186 		renderLabel(x + 4, y + 1, "Compare", false);
    187 		y += stepY;
    188 		renderLED(mqLib::Leds::Led::Edit, x, y);	x += spreadX;
    189 		renderButton(mqLib::Buttons::ButtonType::Edit, x, y);
    190 		x = xStart;
    191 		renderLabel(x + 3, y + 1, "Edit", true);
    192 		renderLabel(x + 4, y + 1, "Recall", false);
    193 		y += stepY;
    194 		renderLED(mqLib::Leds::Led::Sound, x, y);	x += spreadX;
    195 		renderButton(mqLib::Buttons::ButtonType::Sound, x, y);
    196 		x = xStart;
    197 		renderLabel(x + 3, y + 1, "Sound", true);
    198 		renderLabel(x + 4, y + 1, "Store", false);
    199 		y += stepY;
    200 		renderLED(mqLib::Leds::Led::Shift, x, y);	x += spreadX;
    201 		renderButton(mqLib::Buttons::ButtonType::Shift, x, y);
    202 		x = xStart;
    203 		renderLabel(x + 1, y + 1, "Shift", false);
    204 	}
    205 
    206 	void Gui::renderMatrixLEDs(int xStart, int yStart)
    207 	{
    208 		constexpr int stepX = 4;
    209 		constexpr int stepY = 1;
    210 
    211 		int x = xStart;
    212 		int y = yStart;
    213 
    214 		renderLED(mqLib::Leds::Led::Osc1, x, y);	x += stepX;
    215 		renderLED(mqLib::Leds::Led::Osc2, x, y);	x += stepX;
    216 		renderLED(mqLib::Leds::Led::Osc3, x, y);
    217 		x = xStart;	y += stepY;
    218 		renderLED(mqLib::Leds::Led::MixerRouting, x, y);
    219 		x = xStart;	y += stepY;
    220 		renderLED(mqLib::Leds::Led::Filters1, x, y);	x += stepX;
    221 		renderLED(mqLib::Leds::Led::Filters2, x, y);
    222 		x = xStart;	y += stepY;
    223 		renderLED(mqLib::Leds::Led::AmpFx, x, y);
    224 		x = xStart;	y += stepY;
    225 		renderLED(mqLib::Leds::Led::Env1, x, y);	x += stepX;
    226 		renderLED(mqLib::Leds::Led::Env2, x, y);	x += stepX;
    227 		renderLED(mqLib::Leds::Led::Env3, x, y);	x += stepX;
    228 		renderLED(mqLib::Leds::Led::Env4, x, y);
    229 		x = xStart;	y += stepY;
    230 		renderLED(mqLib::Leds::Led::LFOs, x, y);
    231 		x = xStart;	y += stepY;
    232 		renderLED(mqLib::Leds::Led::ModMatrix, x, y);
    233 	}
    234 
    235 	void Gui::renderMatrixText(int x, int y)
    236 	{
    237 		m_win.fill_fg(x,y, x + 36, y + 7, Term::fg::gray);
    238 
    239 		m_win.print_str(x, y, "Octave    Detune Shape     PWM      ");	++y;
    240 		m_win.print_str(x, y, "Osc1      Osc2   Osc3      Filter   ");	++y;
    241 		m_win.print_str(x, y, "Cutoff    Res    Env       Type     ");	++y;
    242 		m_win.print_str(x, y, "Volume    FX Mix ArpMode   Tempo    ");	++y;
    243 		m_win.print_str(x, y, "Attack    Decay  Sustain   Release  ");	++y;
    244 		m_win.print_str(x, y, "LFO1Speed Shapee LFO2Speed LFO3Speed");	++y;
    245 		m_win.print_str(x, y, "Select    Source Amount    Dest     ");
    246 	}
    247 
    248 	void Gui::renderRightEncoders(int x, int y)
    249 	{
    250 		constexpr int xStep = 8;
    251 
    252 		renderEncoder(mqLib::Buttons::Encoders::Matrix1, x, y);	x += xStep;
    253 		renderEncoder(mqLib::Buttons::Encoders::Matrix2, x, y);	x += xStep;
    254 		renderEncoder(mqLib::Buttons::Encoders::Matrix3, x, y);	x += xStep;
    255 		renderEncoder(mqLib::Buttons::Encoders::Matrix4, x, y);
    256 	}
    257 
    258 	void Gui::renderHelp(int x, int y)
    259 	{
    260 		renderLabel(x, y  , "Buttons: 1-4=Inst | Arrows=Cursor | g=Global | m=Multi | e=Edit | s=Sound | S=Shift | M=Multimode | p=Play | P=Peek | q=Power");
    261 		renderLabel(x, y+1, "Encoders: F1/F2 & F3/F4=LCD Left/Right | 5/6=Alpha Dial | F5-F12=Matrix");
    262 		renderLabel(x, y+2, "MIDI: 7=Note ON | 8=Note OFF | 9=Modwheel Max | 0=Modwheel Min");
    263 		renderLabel(x, y+3, "Escape: Open Settings to select MIDI In/Out & Audio Out");
    264 	}
    265 
    266 	void Gui::renderDebug(int x, int y)
    267 	{
    268 	//	renderLabel(g_deviceW - 1, y, std::string("    DSP ") + m_hw.getDspThread().getMipsString(), true, Term::fg::red);
    269 	}
    270 
    271 	void Gui::renderLED(mqLib::Leds::Led _led, int x, int y)
    272 	{
    273 		renderLED(m_mq.getLedState(_led), x, y);
    274 	}
    275 
    276 	void Gui::renderLED(const bool on, int x, int y)
    277 	{
    278 		if(on)
    279 		{
    280 			m_win.fill_fg(x, y, x+1, y, Term::fg::bright_white);
    281 			m_win.fill_fg(x+1, y, x+1, y, Term::fg::bright_yellow);
    282 			m_win.fill_fg(x+2, y, x+1, y, Term::fg::bright_white);
    283 			m_win.print_str(x, y, "(*)");
    284 		}
    285 		else
    286 		{
    287 			m_win.fill_fg(x, y, x+1, y, Term::fg::white);
    288 			m_win.fill_fg(x+1, y, x+1, y, Term::fg::white);
    289 			m_win.fill_fg(x+2, y, x+1, y, Term::fg::white);
    290 			m_win.print_str(x, y, "(-)");
    291 		}
    292 	}
    293 
    294 	void Gui::renderButton(mqLib::Buttons::ButtonType _button, int x, int y)
    295 	{
    296 		const auto pressed = m_mq.getButton(_button);
    297 
    298 		if(pressed)
    299 		{
    300 			m_win.print_str(x,y, "[#]");
    301 		}
    302 		else
    303 		{
    304 			m_win.print_str(x,y, "[ ]");
    305 		}
    306 	}
    307 
    308 	void Gui::renderEncoder(mqLib::Buttons::Encoders _encoder, int x, int y)
    309 	{
    310 		const auto state = m_mq.getEncoder(_encoder);
    311 		constexpr char32_t overline = 0x0000203E;
    312 
    313 		const char c0 = state & 2 ? '*' : '.';
    314 		const char c1 = state & 1 ? '*' : '.';
    315 
    316 		if(_encoder == mqLib::Buttons::Encoders::Master)
    317 		{
    318 			m_win.fill_fg(x, y, x + 5, y + 3, Term::fg::bright_red);
    319 		}
    320 		m_win.print_str(x, y,    " /  \\");
    321 	    m_win.print_str(x, y+1,  std::string("| ") + c0 + c1 + " |");
    322 		m_win.print_str(x, y+2, " \\__/");
    323 
    324 		m_win.set_char(x+2, y, overline);
    325 		m_win.set_char(x+3, y, overline);
    326 	}
    327 
    328 	void Gui::renderLabel(int x, int y, const std::string& _text, bool _rightAlign, Term::fg _color/* = Term::fg::gray*/)
    329 	{
    330 		const int len = static_cast<int>(_text.size());
    331 		if(_rightAlign)
    332 			x -= len;
    333 		m_win.fill_fg(x, y, x + len - 1, y, _color);
    334 		m_win.print_str(x,y,_text);
    335 	}
    336 }