Subversion Repositories svn.mios32

Rev

Rev 2593 | Rev 2595 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2182 hawkeye 1
// $Id: screen.c 1223 2011-06-23 21:26:52Z hawkeye $
2
 
3
#include <mios32.h>
2593 hawkeye 4
#include "app_lcd.h"
5
#include "loopa_datatypes.h"
2182 hawkeye 6
 
2593 hawkeye 7
#include <stdio.h>
2183 hawkeye 8
#include <stdarg.h>
2184 hawkeye 9
#include <string.h>
2183 hawkeye 10
 
2182 hawkeye 11
#include <app_lcd.h>
2571 hawkeye 12
#include <seq_bpm.h>
2182 hawkeye 13
 
2571 hawkeye 14
#include "tasks.h"
2183 hawkeye 15
#include "gfx_resources.h"
2571 hawkeye 16
#include "loopa.h"
2593 hawkeye 17
#include "voxelspace.h"
2182 hawkeye 18
 
19
 
20
// -------------------------------------------------------------------------------------------
21
// SSD 1322 Screen routines by Hawkeye
22
 
2184 hawkeye 23
// --- globals ---
24
 
2182 hawkeye 25
u8 screen[64][128];             // Screen buffer [y][x]
26
 
2184 hawkeye 27
u8 screenShowLoopaLogo_;
2593 hawkeye 28
u8 screenShowShift_ = 0;
29
u8 screenShowMenu_ = 0;
30
u8 screenShowVoxel_ = 0;
2185 hawkeye 31
u8 screenClipNumberSelected_ = 0;
2571 hawkeye 32
u16 screenClipStepPosition_[TRACKS];
2185 hawkeye 33
u32 screenSongStep_ = 0;
2186 hawkeye 34
char screenFlashMessage_[40];
35
u8 screenFlashMessageFrameCtr_;
2571 hawkeye 36
char sceneChangeNotification_[20] = "";
37
u8 screenNewPagePanelFrameCtr_ = 0;
2183 hawkeye 38
 
2184 hawkeye 39
unsigned char* fontptr_ = (unsigned char*) fontsmall_pixdata;
40
u16 fontchar_bytewidth_ = 3;    // bytes to copy for a line of character pixel data
41
u16 fontchar_height_ = 12;      // lines to copy for a full-height character
42
u16 fontline_bytewidth_ = 95*3; // bytes per font pixdata line (character layout all in one line)
2571 hawkeye 43
u8 fontInverted_ = 0;
2184 hawkeye 44
 
45
 
46
/**
47
 * Set the bold font
48
 *
49
 */
50
void setFontBold()
2183 hawkeye 51
{
2184 hawkeye 52
   fontptr_ = (unsigned char*) fontbold_pixdata;
53
   fontchar_bytewidth_ = 5;
54
   fontchar_height_ = 18;
55
   fontline_bytewidth_ = 95 * 5;
2183 hawkeye 56
}
57
// ----------------------------------------------------------------------------------------
58
 
59
 
2184 hawkeye 60
/**
61
 * Set the normal font
62
 *
63
 */
64
void setFontNormal()
2183 hawkeye 65
{
2184 hawkeye 66
   fontptr_ = (unsigned char*) fontnormal_pixdata;
67
   fontchar_bytewidth_ = 5;
68
   fontchar_height_ = 18;
69
   fontline_bytewidth_ = 95 * 5;
2183 hawkeye 70
}
71
// ----------------------------------------------------------------------------------------
72
 
2184 hawkeye 73
 
74
/**
75
 * Set the small font
76
 *
77
 */
78
void setFontSmall()
79
{
80
   fontptr_ = (unsigned char*) fontsmall_pixdata;
81
   fontchar_bytewidth_ = 3;
82
   fontchar_height_ = 12;
83
   fontline_bytewidth_ = 95 * 3;
84
}
85
// ----------------------------------------------------------------------------------------
86
 
2593 hawkeye 87
/**
88
 * Set the small font
89
 *
90
 */
91
void setFontKeyIcon()
92
{
93
    fontptr_ = (unsigned char*) keyicons_pixdata;
94
    fontchar_bytewidth_ = 18;
95
    fontchar_height_ = 32;
96
    fontline_bytewidth_ = 600;
97
}
98
// ----------------------------------------------------------------------------------------
2184 hawkeye 99
 
2593 hawkeye 100
 
2184 hawkeye 101
/**
2571 hawkeye 102
 * Set font noninverted
103
 * *
104
 */
105
void setFontNonInverted()
106
{
107
   fontInverted_ = 0;
108
}
109
// ----------------------------------------------------------------------------------------
110
 
111
 
112
/**
113
 * Set font inverted
114
 *
115
 */
116
void setFontInverted()
117
{
118
   fontInverted_ = 1;
119
}
120
// ----------------------------------------------------------------------------------------
121
 
122
 
123
/**
2184 hawkeye 124
 * Display the given string at the given pixel coordinates
125
 * output to screen output buffer, the next display() will render it to hardware
126
 * provides clipping support, coordinates can be offscreen/negative for scrolling fx
127
 *
128
 */
2183 hawkeye 129
void printString(int xPixCoord /* even! */, int yPixCoord, const char *str)
130
{
131
   unsigned stringpos = 0;
132
 
133
   while (*str != '\0')
134
   {
135
      int c = *str++;
136
      unsigned x;
137
 
138
      // in-font coordinates
139
      unsigned f_y = 0;
2184 hawkeye 140
      unsigned f_x = (c-32) * fontchar_bytewidth_;
2183 hawkeye 141
 
142
      // screenbuf target coordinates
143
      unsigned s_y = yPixCoord;
2184 hawkeye 144
      unsigned s_x = xPixCoord / 2 + stringpos * fontchar_bytewidth_; // 2 pixels per byte, require even xPixCoord start coordinate
2183 hawkeye 145
 
2184 hawkeye 146
      while (f_y < fontchar_height_)
2183 hawkeye 147
      {
148
         if (s_y >= 0 && s_y < 64) // clip y offscreen
149
         {
150
            unsigned char* sdata = (unsigned char*) screen + s_y * 128 + s_x;
2184 hawkeye 151
            unsigned char* fdata = (unsigned char*) fontptr_ + f_y * fontline_bytewidth_ + f_x;
2183 hawkeye 152
            unsigned c_s_x = s_x;
153
 
2184 hawkeye 154
            for (x = 0; x <fontchar_bytewidth_; x++)
2183 hawkeye 155
            {
156
               if (c_s_x >= 0 && c_s_x < 128)
2571 hawkeye 157
               {
158
                  if (!fontInverted_)
159
                  {
160
                     if (*fdata)
161
                        *sdata = *fdata;  // inner loop: copy 2 pixels, if onscreen
162
                  }
163
                  else
164
                  {
165
                     // "invert" font
166
                     u8 first = *fdata >> 4;
167
                     u8 second = *fdata % 16;
2183 hawkeye 168
 
2571 hawkeye 169
                     first = 15-first;
170
                     second = 15-second;
171
                     *sdata = (first << 4) + second;
172
                  }
173
               }
174
 
2183 hawkeye 175
               c_s_x++;
176
               fdata++;
177
               sdata++;
178
            }
179
         }
180
 
181
         f_y++;
182
         s_y++;
183
      }
184
 
185
      stringpos++;
186
   }
187
}
188
// ----------------------------------------------------------------------------------------
189
 
190
 
2184 hawkeye 191
/**
192
 * Display the given formatted string at the given pixel coordinates
193
 * output to screen output buffer, the next display() will render it to hardware
194
 * provides clipping support, coordinates can be offscreen/negative for scrolling fx
195
 *
196
 */
2183 hawkeye 197
void printFormattedString(int xPixCoord /* even! */, int yPixCoord, const char* format, ...)
198
{
2571 hawkeye 199
   char buffer[64];
2183 hawkeye 200
   va_list args;
201
 
202
   va_start(args, format);
203
   vsprintf((char *)buffer, format, args);
204
   return printString(xPixCoord, yPixCoord, buffer);
205
}
206
// ----------------------------------------------------------------------------------------
207
 
208
 
2571 hawkeye 209
/**
210
 * Display the given formatted string at the given y pixel coordinates, center x
211
 * output to screen output buffer, the next display() will render it to hardware
212
 * provides clipping support, coordinates can be offscreen/negative for scrolling fx
213
 *
214
 */
215
void printCenterFormattedString(int yPixCoord, const char* format, ...)
216
{
217
   char buffer[64];
218
   va_list args;
2184 hawkeye 219
 
2571 hawkeye 220
   va_start(args, format);
221
   vsprintf((char *)buffer, format, args);
222
 
223
   int xPixCoord = 128 - (fontchar_bytewidth_ * strlen(buffer));
224
   return printString(xPixCoord, yPixCoord, buffer);
225
}
226
// ----------------------------------------------------------------------------------------
227
 
228
 
2184 hawkeye 229
/**
230
 * Display a loopa slot time indicator
2185 hawkeye 231
 * Format: [clipPos:clipLength]
2571 hawkeye 232
 *         times are in steps
2184 hawkeye 233
 *
234
 *         the time indicator will be rendered inverted, if this is the selected clip/active clip
235
 *         the display position depends on the slot number, slot #0 is upper left, slot #7 is lower right
236
 *
237
 */
2185 hawkeye 238
void displayClipPosition(u8 clipNumber)
2184 hawkeye 239
{
240
   char buffer[16];
241
 
2571 hawkeye 242
   u16 stepLength = clipSteps_[clipNumber][activeScene_];
2185 hawkeye 243
   u16 stepPos = screenClipStepPosition_[clipNumber];
244
   u8 isSelected = (clipNumber == screenClipNumberSelected_);
2184 hawkeye 245
 
2571 hawkeye 246
   u8 syncedMuteUnmuteInProgress = trackMuteToggleRequested_[clipNumber] && (tickToStep(tick_) % beatLoopSteps_) != 0;
247
 
248
   if (!syncedMuteUnmuteInProgress)
2185 hawkeye 249
   {
250
      if (stepLength == 0)
2571 hawkeye 251
         sprintf((char *)buffer, "       ");
2185 hawkeye 252
      else if (stepLength > 99)
2571 hawkeye 253
         sprintf((char *)buffer, "%03d>%3d", stepPos, stepLength);
2185 hawkeye 254
      else if (stepLength > 9)
2571 hawkeye 255
         sprintf((char *)buffer, " %02d>%2d ", stepPos, stepLength);
256
      else
257
         sprintf((char *)buffer, "  %01d>%1d  ", stepPos, stepLength);
2185 hawkeye 258
   }
259
   else
260
   {
2571 hawkeye 261
      u8 remainSteps = 16 - (tickToStep(tick_) % beatLoopSteps_);
262
 
263
      if (remainSteps > 9)
264
         sprintf((char *)buffer, "  %d   ", remainSteps);
265
      else
266
         sprintf((char *)buffer, "   %d   ", remainSteps);
2185 hawkeye 267
   }
268
 
2571 hawkeye 269
   u8 xPixCoord = clipNumber * 42;
270
   u8 yPixCoord = 56;
2184 hawkeye 271
   u8 fontHeight = 7;
272
   u8 fontByteWidth = 3;
273
   u8 fontLineByteWidth = 16*3;
274
 
275
   char *str = buffer;
276
   u8 stringpos = 0;
277
   while (*str != '\0')
278
   {
279
      int c = *str++;
280
 
281
      if (c == ' ')  // Map string space to font space
282
         c = '/';
283
 
284
      unsigned x;
285
 
286
      // in-font coordinates
287
      unsigned f_y = 0;
288
      unsigned f_x = (c-47) * fontByteWidth;
289
 
290
      // screenbuf target coordinates
291
      unsigned s_y = yPixCoord;
292
      unsigned s_x = xPixCoord / 2 + stringpos * fontByteWidth; // 2 pixels per byte, require even xPixCoord start coordinate
293
 
294
      while (f_y < fontHeight)
295
      {
296
         if (s_y >= 0 && s_y < 64) // clip y offscreen
297
         {
298
            unsigned char* sdata = (unsigned char*) screen + s_y * 128 + s_x;
299
            unsigned char* fdata = (unsigned char*) digitstiny_pixdata + f_y * fontLineByteWidth + f_x;
300
            unsigned c_s_x = s_x;
301
 
302
            for (x = 0; x <fontByteWidth; x++)
303
            {
304
               if (c_s_x >= 0 && c_s_x < 128)
305
               {
306
                  if (!isSelected)
307
                  {
308
                     if (*fdata)
309
                        *sdata = *fdata;  // inner loop: copy 2 pixels, if onscreen
310
                  }
311
                  else
312
                  {
313
                     // "invert" font
314
                     u8 first = *fdata >> 4;
315
                     u8 second = *fdata % 16;
316
 
317
                     first = 15-first;
318
                     second = 15-second;
319
                     *sdata = (first << 4) + second;
320
                  }
321
               }
322
 
323
               c_s_x++;
324
               fdata++;
325
               sdata++;
326
            }
327
         }
328
 
329
         f_y++;
330
         s_y++;
331
      }
332
 
333
      stringpos++;
334
   }
335
}
336
// ----------------------------------------------------------------------------------------
337
 
338
 
339
/**
2593 hawkeye 340
 * If showLogo is true, draw the LoopA Logo (usually during unit startup)
2184 hawkeye 341
 *
342
 */
343
void screenShowLoopaLogo(u8 showLogo)
344
{
345
   screenShowLoopaLogo_ = showLogo;
346
}
347
// ----------------------------------------------------------------------------------------
348
 
349
 
350
/**
2593 hawkeye 351
 * If showShift is true, draw the shift key overlay
352
 *
353
 */
354
void screenShowShift(u8 showShift)
355
{
356
   screenShowShift_ = showShift;
357
}
358
// ----------------------------------------------------------------------------------------
359
 
360
 
361
/**
362
 * @return true, if we are currently showing the shift overlay
363
 *
364
 */
365
u8 screenIsInShift()
366
{
367
   return screenShowShift_;
368
}
369
// ----------------------------------------------------------------------------------------
370
 
371
 
372
/**
373
 * If showMenu is true, draw the menu key overlay
374
 *
375
 */
376
void screenShowMenu(u8 showMenu)
377
{
378
    screenShowMenu_ = showMenu;
379
}
380
// ----------------------------------------------------------------------------------------
381
 
382
 
383
/**
384
 * @return if we are currently showing the menu
385
 *
386
 */
387
u8 screenIsInMenu()
388
{
389
  return screenShowMenu_;
390
}
391
// ----------------------------------------------------------------------------------------
392
 
393
 
394
/**
395
 * If showVoxel is true, draw the voxel screensaver
396
 * TODO: should be able to add titles or gfx on top of underlying screensaver, e.g. for startup
397
 */
398
void screenShowVoxel(u8 showVoxel)
399
{
400
   screenShowVoxel_ = showVoxel;
401
}
402
// ----------------------------------------------------------------------------------------
403
 
404
 
405
/**
2184 hawkeye 406
 * Set the currently selected clip
407
 *
408
 */
409
void screenSetClipSelected(u8 clipNumber)
410
{
2185 hawkeye 411
   screenClipNumberSelected_ = clipNumber;
2184 hawkeye 412
}
413
// ----------------------------------------------------------------------------------------
414
 
415
 
416
/**
2571 hawkeye 417
 * Set the position info of a clip
2184 hawkeye 418
 *
419
 */
2571 hawkeye 420
void screenSetClipPosition(u8 clipNumber, u16 stepPosition)
2184 hawkeye 421
{
2571 hawkeye 422
   // DEBUG_MSG("[screenSetClipPosition] clip: %u stepPosition: %u", clipNumber, stepPosition);
423
   screenClipStepPosition_[clipNumber] = stepPosition;
424
}
425
// ----------------------------------------------------------------------------------------
426
 
427
 
428
/**
429
 * Set the global song step position (e.g. for displaying the recording-clip step)
430
 *
431
 */
432
void screenSetSongStep(u32 stepPosition)
433
{
434
   screenSongStep_ = stepPosition;
435
}
436
// ----------------------------------------------------------------------------------------
437
 
438
 
439
/**
440
 * Flash a short-lived message to the center of the screen
441
 *
442
 */
443
void screenFormattedFlashMessage(const char* format, ...)
444
{
445
   va_list args;
446
 
447
   va_start(args, format);
448
   vsprintf((char *)screenFlashMessage_, format, args);
449
   screenFlashMessageFrameCtr_ = 10;
450
}
451
// ----------------------------------------------------------------------------------------
452
 
453
 
454
/**
455
 * Set scene change notification message (change in ticks)
456
 *
457
 */
458
void screenSetSceneChangeInTicks(u8 ticks)
459
{
460
   if (ticks)
461
      sprintf((char *)sceneChangeNotification_, " [...%d]", ticks);
2185 hawkeye 462
   else
2571 hawkeye 463
      strcpy(sceneChangeNotification_, "");
2184 hawkeye 464
}
465
// ----------------------------------------------------------------------------------------
466
 
467
 
468
/**
2571 hawkeye 469
 * Notify, that a screen page change has occured (flash a page descriptor for a while)
2185 hawkeye 470
 *
471
 */
2571 hawkeye 472
void screenNotifyPageChanged()
2185 hawkeye 473
{
2571 hawkeye 474
   screenNewPagePanelFrameCtr_ = 20;
2185 hawkeye 475
}
476
// ----------------------------------------------------------------------------------------
477
 
478
 
479
/**
2571 hawkeye 480
 * Convert note length to pixel width
481
 * if ticksLength == 0 (still recording),
2184 hawkeye 482
 *
483
 */
2571 hawkeye 484
u16 noteLengthPixels(u32 ticksLength)
2184 hawkeye 485
{
2571 hawkeye 486
   return tickToStep(ticksLength);
2184 hawkeye 487
}
488
// ----------------------------------------------------------------------------------------
489
 
490
 
2185 hawkeye 491
/**
2571 hawkeye 492
 * Display the note data of a clip
2185 hawkeye 493
 *
494
 */
2571 hawkeye 495
void displayClip(u8 clip)
2185 hawkeye 496
{
2571 hawkeye 497
   u16 x;
498
   u8 y;
499
   u16 i;
500
   u16 mult = 128/clipSteps_[clip][activeScene_];  // horizontal multiplier to make clips as wide as the screen
501
 
502
   u16 curStep = ((u32)boundTickToClipSteps(tick_, clip) * mult) / 24;
503
 
504
   // Render vertical 1/4th note indicators
505
   for (i=0; i<clipSteps_[clip][activeScene_] / 4; i++)
506
   {
507
      x = i * 4 * mult;
508
      if (x < 128)
509
         for (y=12; y<52; y++)
510
               screen[y][x] = i % 4 == 0 ? 0x60 : 0x50;
511
   }
512
 
513
   // Render vertical time indicator line (if seq is playing)
514
   if (SEQ_BPM_IsRunning() && curStep < 128)
515
      for (y=0; y<64; y++)
516
         screen[y][curStep] = 0x88;
517
 
518
 
519
   // Render note data
520
   for (i=0; i < clipNotesSize_[clip][activeScene_]; i++)
521
   {
522
      s32 transformedStep = (s32)quantizeTransform(clip, i) * mult;
523
 
524
      if (transformedStep >= 0) // if note starts within (potentially reconfigured) clip length
525
      {
526
         u16 step = transformedStep / 24;
527
 
528
         s16 note = clipNotes_[clip][activeScene_][i].note + clipTranspose_[clip][activeScene_];
529
         note = note < 0 ? 0 : note;
530
         note = note > 127 ? 127 : note;
531
         u8 y = (127 - note) / 2;
532
 
533
         if (y < 64)
534
         {
535
            u16 len = noteLengthPixels(clipNotes_[clip][activeScene_][i].length * mult);
536
 
537
            if (clipNotes_[clip][activeScene_][i].length == 0 && curStep > step)
538
            {
539
               // still recording (also check for boundary wrapping, disabled right now)
540
               len = curStep - step;
541
            }
542
 
543
            for (x = step; x <= step + len; x++)
544
            {
545
               if (clipNotes_[clip][activeScene_][i].velocity > 0)
546
               {
547
                  u8 color;
548
                  if (!trackMute_[clip])
549
                     color = x == step ? 0xFF
550
                                       : 0x99;  // The modulo only works if we are not scrolling and screen width = clip length
551
                  else
2577 hawkeye 552
                     color = x == step ? 0x88
553
                                       : 0x66;  // The modulo only works if we are not scrolling and screen width = clip length
2571 hawkeye 554
 
555
                  screen[y][x % 128] = color;
556
 
557
                  if (page_ == PAGE_NOTES && i == clipActiveNote_[clip][activeScene_])
558
                  {
559
                     // render cursor for selected note
560
                     u8 cursorX = x % 128;
561
                     u8 cursorY = y;
562
 
563
                     if (cursorY > 2 && cursorX < 126)
564
                        screen[cursorY - 2][cursorX + 2] = 0x0F;
565
 
566
                     if (cursorY > 2 && cursorX > 2)
567
                        screen[cursorY - 2][cursorX - 2] = 0xF0;
568
 
569
                     if (cursorY < 62 && cursorX < 126)
570
                        screen[cursorY + 2][cursorX + 2] = 0x0F;
571
 
572
                     if (cursorY < 62 && cursorX > 2)
573
                        screen[cursorY + 2][cursorX - 2] = 0xF0;
574
                  }
575
               }
576
            }
577
         }
578
      }
579
   }
2185 hawkeye 580
}
581
// ----------------------------------------------------------------------------------------
582
 
583
 
584
/**
2571 hawkeye 585
 * Display the normal loopa view (PAGE_TRACK)
2186 hawkeye 586
 *
587
 */
2593 hawkeye 588
void displayPageMute(void)
2186 hawkeye 589
{
2571 hawkeye 590
   setFontSmall();
2186 hawkeye 591
 
2571 hawkeye 592
   if (screenNewPagePanelFrameCtr_ > 0)
593
   {
594
      setFontInverted();
595
      printString(250, 8, "M");
596
      printString(250, 20, "U");
597
      printString(250, 32, "T");
598
      printString(250, 44, "E");
599
      setFontNonInverted();
600
      screenNewPagePanelFrameCtr_--;
601
   }
602
 
603
   if (trackMute_[activeTrack_])
2577 hawkeye 604
      printCenterFormattedString(0, "[Clip %d Scene %c [muted]%s]", activeTrack_ + 1, 'A' + activeScene_, sceneChangeNotification_);
2571 hawkeye 605
   else
2577 hawkeye 606
      printCenterFormattedString(0, "[Clip %d Scene %c%s]", activeTrack_ + 1, 'A' + activeScene_, sceneChangeNotification_);
2571 hawkeye 607
 
608
   u8 clip;
609
   for (clip = 0; clip < TRACKS; clip++)
610
   {
611
      if (clip == activeTrack_ || clipNotesSize_[clip] > 0 || trackMuteToggleRequested_[clip])
612
         displayClipPosition(clip);  // only display clip position indicators, if it is the active clip or it has notes
613
   }
614
 
615
   displayClip(activeTrack_);
2186 hawkeye 616
}
617
// ----------------------------------------------------------------------------------------
618
 
619
 
2571 hawkeye 620
/**
621
 * Display the edit clip page (PAGE_EDIT)
622
 *
623
 */
624
void displayPageEdit(void)
625
{
626
   setFontSmall();
2186 hawkeye 627
 
2571 hawkeye 628
   if (screenNewPagePanelFrameCtr_ > 0)
629
   {
630
      setFontInverted();
631
      printString(250, 8, "E");
632
      printString(250, 20, "D");
633
      printString(250, 32, "I");
634
      printString(250, 44, "T");
635
      setFontNonInverted();
636
      screenNewPagePanelFrameCtr_--;
637
   }
638
 
2577 hawkeye 639
   printCenterFormattedString(0, "Edit Settings [Clip %d Scene %c%s]", activeTrack_ + 1, 'A' + activeScene_, sceneChangeNotification_);
2571 hawkeye 640
 
641
   command_ == COMMAND_CLIPLEN ? setFontInverted() : setFontNonInverted();
642
   if (clipSteps_[activeTrack_][activeScene_] < 100)
643
      printFormattedString(0, 54, "Len %d", clipSteps_[activeTrack_][activeScene_]);
644
   else
645
      printFormattedString(0, 54, "Le %d", clipSteps_[activeTrack_][activeScene_]);
646
 
647
   command_ == COMMAND_QUANTIZE ? setFontInverted() : setFontNonInverted();
648
   switch (clipQuantize_[activeTrack_][activeScene_])
649
   {
650
      case 3: printFormattedString(42, 54, "Q1/128"); break;
651
      case 6: printFormattedString(42, 54, "Qu1/64"); break;
652
      case 12: printFormattedString(42, 54, "Qu1/32"); break;
653
      case 24: printFormattedString(42, 54, "Qu1/16"); break;
654
      case 48: printFormattedString(42, 54, "Qu 1/8"); break;
655
      case 96: printFormattedString(42, 54, "Qu 1/4"); break;
656
      case 192: printFormattedString(42, 54, "Qu 1/2"); break;
657
      case 384: printFormattedString(42, 54, "Qu 1/1"); break;
658
      default: printFormattedString(42, 54, "Qu OFF"); break;
659
   }
660
 
661
   command_ == COMMAND_TRANSPOSE ? setFontInverted() : setFontNonInverted();
662
   printFormattedString(84, 54, "Trn %d", clipTranspose_[activeTrack_][activeScene_]);
663
 
664
   command_ == COMMAND_SCROLL ? setFontInverted() : setFontNonInverted();
665
   printFormattedString(126, 54, "Scr %d", clipScroll_[activeTrack_][activeScene_]);
666
 
667
   command_ == COMMAND_STRETCH ? setFontInverted() : setFontNonInverted();
668
   switch (clipStretch_[activeTrack_][activeScene_])
669
   {
670
      case 1: printFormattedString(168, 54, "Zo 1/16"); break;
671
      case 2: printFormattedString(168, 54, "Zo 1/8"); break;
672
      case 4: printFormattedString(168, 54, "Zo 1/4"); break;
673
      case 8: printFormattedString(168, 54, "Zo 1/2"); break;
674
      case 16: printFormattedString(168, 54, "Zoom 1"); break;
675
      case 32: printFormattedString(168, 54, "Zoom 2"); break;
676
      case 64: printFormattedString(168, 54, "Zoom 4"); break;
677
      case 128: printFormattedString(168, 54, "Zoom 8"); break;
678
   }
679
 
2594 hawkeye 680
   command_ == COMMAND_FREEZE ? setFontInverted() : setFontNonInverted();
2571 hawkeye 681
   printFormattedString(210, 54, "Clear");
682
 
683
   setFontNonInverted();
684
   displayClip(activeTrack_);
685
}
686
// ----------------------------------------------------------------------------------------
687
 
688
 
2186 hawkeye 689
/**
2571 hawkeye 690
 * Numeric note to note string helper
2185 hawkeye 691
 *
692
 */
2571 hawkeye 693
static void stringNote(char *label, u8 note)
694
{
695
   const char noteTab[12][3] = { "C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-" };
696
 
697
   // print "---" if note number is 0
698
   if (note == 0)
699
      sprintf(label, "---  ");
700
   else
701
   {
702
      u8 octave = note / 12;
703
      note %= 12;
704
 
705
      // print semitone and octave (-2): up to 4 chars
706
      sprintf(label, "%s%d  ",
707
              noteTab[note],
708
              (int)octave-2);
709
   }
710
}
711
// ----------------------------------------------------------------------------------------
712
 
713
 
714
/**
715
 * Display the note editor (PAGE_NOTES)
716
 *
717
 */
718
void displayPageNotes(void)
719
{
720
   setFontSmall();
721
 
722
   if (screenNewPagePanelFrameCtr_ > 0)
723
   {
724
      setFontInverted();
725
      printString(250, 8, "N");
726
      printString(250, 20, "O");
727
      printString(250, 32, "T");
728
      printString(250, 44, "E");
729
      setFontNonInverted();
730
      screenNewPagePanelFrameCtr_--;
731
   }
732
 
2577 hawkeye 733
   printCenterFormattedString(0, "Note Editor [Clip %d Scene %c%s]", activeTrack_ + 1, 'A' + activeScene_, sceneChangeNotification_);
2571 hawkeye 734
 
735
   if (clipNotesSize_[activeTrack_][activeScene_] > 0)
736
   {
737
 
738
      u16 activeNote = clipActiveNote_[activeTrack_][activeScene_];
739
 
740
      if (activeNote >= clipNotesSize_[activeTrack_][activeScene_]) // necessary e.g. for clip change
741
         activeNote = 0;
742
 
743
      u16 pos = (clipNotes_[activeTrack_][activeScene_][activeNote].tick) / 24;
744
      u16 length = clipNotes_[activeTrack_][activeScene_][activeNote].length;
745
      u8 note = clipNotes_[activeTrack_][activeScene_][activeNote].note;
746
      u8 velocity = clipNotes_[activeTrack_][activeScene_][activeNote].velocity;
747
 
748
      command_ == COMMAND_POSITION ? setFontInverted() : setFontNonInverted();
749
      if (pos < 100)
750
         printFormattedString(0, 54, "Pos %d", pos);
751
      else
752
         printFormattedString(0, 54, "Po %d", pos);
753
 
754
      command_ == COMMAND_NOTE ? setFontInverted() : setFontNonInverted();
755
 
756
      char noteStr[8];
757
      stringNote(noteStr, note);
758
      printFormattedString(42, 54, "%s", noteStr);
759
 
760
      command_ == COMMAND_VELOCITY ? setFontInverted() : setFontNonInverted();
761
      printFormattedString(84, 54, "Vel %d", velocity);
762
 
763
      command_ == COMMAND_LENGTH ? setFontInverted() : setFontNonInverted();
764
      printFormattedString(126, 54, "Len %d", length);
765
 
2594 hawkeye 766
      command_ == COMMAND_FREEZE ? setFontInverted() : setFontNonInverted();
2571 hawkeye 767
      printFormattedString(210, 54, "Delete");
768
 
769
      setFontNonInverted();
770
   }
771
   else
772
   {
773
      printCenterFormattedString(54, "No notes recorded, yet");
774
   }
775
   displayClip(activeTrack_);
776
}
777
// ----------------------------------------------------------------------------------------
778
 
779
 
780
/**
781
 * Display the track midi settings page (PAGE_MIDI)
782
 *
783
 */
784
void displayPageMidi(void)
785
{
786
   setFontSmall();
787
 
788
   if (screenNewPagePanelFrameCtr_ > 0)
789
   {
790
      setFontInverted();
791
      printString(250, 8, "M");
792
      printString(250, 20, "I");
793
      printString(250, 32, "D");
794
      printString(250, 44, "I");
795
      setFontNonInverted();
796
      screenNewPagePanelFrameCtr_--;
797
   }
798
 
799
   printCenterFormattedString(0, "MIDI Parameters [Track %d]", activeTrack_ + 1);
800
 
801
   command_ == COMMAND_PORT ? setFontInverted() : setFontNonInverted();
802
   switch (trackMidiPort_[activeTrack_])
803
   {
804
      case 0x20: printFormattedString(0, 54, " OUT1 "); break;
805
      case 0x21: printFormattedString(0, 54, " OUT2 "); break;
806
      case 0x22: printFormattedString(0, 54, " OUT3 "); break;
807
      case 0x23: printFormattedString(0, 54, " OUT4 "); break;
808
   }
809
 
810
   command_ == COMMAND_CHANNEL ? setFontInverted() : setFontNonInverted();
811
   printFormattedString(42, 54, " Chn %d ", trackMidiChannel_[activeTrack_] + 1);
812
 
813
   setFontNonInverted();
814
   displayClip(activeTrack_);
815
}
816
// ----------------------------------------------------------------------------------------
817
 
818
 
819
/**
820
 * Display the main menu (PAGE_DISK)
821
 *
822
 */
823
void displayPageDisk(void)
824
{
825
   setFontSmall();
826
 
827
   if (screenNewPagePanelFrameCtr_ > 0)
828
   {
829
      setFontInverted();
830
      printString(250, 8, "D");
831
      printString(250, 20, "I");
832
      printString(250, 32, "S");
833
      printString(250, 44, "K");
834
      setFontNonInverted();
835
      screenNewPagePanelFrameCtr_--;
836
   }
837
 
2577 hawkeye 838
   printCenterFormattedString(0, "Disk Operations");
2571 hawkeye 839
 
840
   command_ == COMMAND_SAVE ? setFontInverted() : setFontNonInverted();
841
   printFormattedString(0, 54, "Save");
842
 
843
   command_ == COMMAND_LOAD ? setFontInverted() : setFontNonInverted();
844
   printFormattedString(42, 54, "Load");
845
 
846
   command_ == COMMAND_NEW ? setFontInverted() : setFontNonInverted();
847
   printFormattedString(84, 54, "New");
848
 
849
 
850
   setFontNonInverted();
851
   setFontBold();
852
   printFormattedString(82, 16, "Session %d", sessionNumber_);
853
 
854
   setFontNormal();
855
   if (sessionExistsOnDisk_)
856
      printFormattedString(82, 32, "(on disk)", sessionNumber_);
857
   else
858
      printFormattedString(106, 32, "(new)", sessionNumber_);
859
 
860
   setFontSmall();
861
}
862
// ----------------------------------------------------------------------------------------
863
 
864
 
865
/**
866
 * Display the bpm settings page (PAGE_BPM)
867
 *
868
 */
869
void displayPageBpm(void)
870
{
871
   setFontSmall();
2576 hawkeye 872
 
2571 hawkeye 873
   if (screenNewPagePanelFrameCtr_ > 0)
874
   {
875
      setFontInverted();
876
      printString(250, 14, "B");
877
      printString(250, 26, "P");
878
      printString(250, 38, "M");
879
      setFontNonInverted();
880
      screenNewPagePanelFrameCtr_--;
881
   }
882
 
883
   printCenterFormattedString(0, "Tempo Settings", activeTrack_ + 1, activeScene_ + 1);
884
 
885
   command_ == COMMAND_BPM ? setFontInverted() : setFontNonInverted();
886
   u16 bpm = SEQ_BPM_IsMaster() ? bpm_ : SEQ_BPM_Get();
887
   printFormattedString(0, 54, "%d BPM", bpm);
888
 
889
   setFontNonInverted();
890
   displayClip(activeTrack_);
891
}
892
// ----------------------------------------------------------------------------------------
893
 
894
 
895
/**
896
 * Display the current screen buffer (once per frame, called in app.c scheduler)
897
 *
898
 */
2182 hawkeye 899
void display(void)
900
{
901
   u8 i, j;
902
 
2184 hawkeye 903
   if (screenShowLoopaLogo_)
904
   {
2593 hawkeye 905
      // Startup/initial session loading: Render the LoopA Logo
2571 hawkeye 906
 
907
      setFontBold();  // width per letter: 10px (for center calculation)
2593 hawkeye 908
      printFormattedString(78, 2, "LoopA V2.03");
2571 hawkeye 909
 
910
      setFontSmall(); // width per letter: 6px
911
      printFormattedString(28, 20, "(C) Hawkeye, latigid on, TK. 2018");
912
      printFormattedString(52, 32, "MIDIbox hardware platform");
913
 
914
      setFontBold(); // width per letter: 10px;
915
      printFormattedString(52, 44, "www.midiphy.com");
2184 hawkeye 916
   }
2593 hawkeye 917
   else if (screenIsInMenu())
918
   {
919
      voxelFrame();
920
      setFontKeyIcon();
921
      // setFontDarkGreyAsBlack(); // TODO
922
 
923
      int iconId;
924
 
2594 hawkeye 925
      iconId = (page_ == PAGE_MIDIMONITOR) ? 32 + KEYICON_MIDIMONITOR_INVERTED : 32 + KEYICON_MIDIMONITOR;
2593 hawkeye 926
      printFormattedString(1 * 36 + 18, 0, "%c", iconId);
927
 
2594 hawkeye 928
      iconId = (page_ == PAGE_TEMPO) ? 32 + KEYICON_TEMPO_INVERTED : 32 + KEYICON_TEMPO;
2593 hawkeye 929
      printFormattedString(2 * 36 + 18, 0, "%c", iconId);
930
 
2594 hawkeye 931
      iconId = (page_ == PAGE_MUTE) ? 32 + KEYICON_MUTE_INVERTED : 32 + KEYICON_MUTE;
2593 hawkeye 932
      printFormattedString(3 * 36 + 18, 0, "%c", iconId);
933
 
934
      iconId = (page_ == PAGE_NOTES) ? 32 + KEYICON_NOTES_INVERTED : 32 + KEYICON_NOTES;
935
      printFormattedString(4 * 36 + 18, 0, "%c", iconId);
936
 
2594 hawkeye 937
      iconId = (page_ == PAGE_SETUP) ? 32 + KEYICON_SETUP_INVERTED : 32 + KEYICON_SETUP;
2593 hawkeye 938
      printFormattedString(0 * 36, 32, "%c", iconId);
939
 
2594 hawkeye 940
      iconId = (page_ == PAGE_ROUTER) ? 32 + KEYICON_ROUTER_INVERTED : 32 + KEYICON_ROUTER;
2593 hawkeye 941
      printFormattedString(1 * 36, 32, "%c", iconId);
942
 
2594 hawkeye 943
      iconId = (page_ == PAGE_DISK) ? 32 + KEYICON_DISK_INVERTED : 32 + KEYICON_DISK;
2593 hawkeye 944
      printFormattedString(2 * 36, 32, "%c", iconId);
945
 
946
      iconId = 32 + KEYICON_MENU_INVERTED;
947
      printFormattedString(3 * 36, 32, "%c", iconId);
948
 
2594 hawkeye 949
      iconId = (page_ == PAGE_CLIP) ? 32 + KEYICON_CLIP_INVERTED : 32 + KEYICON_CLIP;
2593 hawkeye 950
      printFormattedString(4 * 36, 32, "%c", iconId);
951
 
2594 hawkeye 952
      iconId = (page_ == PAGE_FX) ? 32 + KEYICON_FX_INVERTED : 32 + KEYICON_FX;
2593 hawkeye 953
      printFormattedString(5 * 36, 32, "%c", iconId);
954
 
2594 hawkeye 955
      iconId = (page_ == PAGE_TRACK) ? 32 + KEYICON_TRACK_INVERTED : 32 + KEYICON_TRACK;
2593 hawkeye 956
      printFormattedString(6 * 36, 32, "%c", iconId);
957
 
958
      setFontBold();
959
   }
960
   else if (screenShowVoxel_)
961
   {
962
      voxelFrame();
963
   }
2184 hawkeye 964
   else
965
   {
2571 hawkeye 966
      // Display page content...
967
      switch (page_)
968
      {
2593 hawkeye 969
         case PAGE_MUTE:
970
            displayPageMute();
2571 hawkeye 971
            break;
2183 hawkeye 972
 
2593 hawkeye 973
         case PAGE_CLIP:
2571 hawkeye 974
            displayPageEdit();
975
            break;
2184 hawkeye 976
 
2571 hawkeye 977
         case PAGE_NOTES:
978
            displayPageNotes();
979
            break;
980
 
2593 hawkeye 981
         case PAGE_TRACK:
2571 hawkeye 982
            displayPageMidi();
983
            break;
984
 
985
         case PAGE_DISK:
986
            displayPageDisk();
987
            break;
988
 
2593 hawkeye 989
         case PAGE_TEMPO:
2571 hawkeye 990
            displayPageBpm();
991
            break;
992
      }
2184 hawkeye 993
   }
994
 
2571 hawkeye 995
   // Display flash notification
2186 hawkeye 996
   if (screenFlashMessageFrameCtr_)
997
   {
998
      setFontNormal();
999
      u8 len = strlen(screenFlashMessage_);
1000
      u8 xpos = 128 - len * 5;
1001
      u16 displacement = 10 - screenFlashMessageFrameCtr_;
1002
      displacement = (displacement * displacement) / 3;
1003
      printFormattedString(xpos, 26 - displacement, screenFlashMessage_);
1004
 
1005
      screenFlashMessageFrameCtr_--;
1006
   }
1007
 
2571 hawkeye 1008
   u8 flash = 0;
1009
   // no flashing for now :)
1010
   //if (oledBeatFlashState_ > 0)
1011
   //   flash = oledBeatFlashState_ == 1 ? 0x44 : 0x66;
1012
 
1013
   // Push screen buffer to screen
2182 hawkeye 1014
   for (j = 0; j < 64; j++)
1015
   {
1016
      APP_LCD_Cmd(0x15);
1017
      APP_LCD_Data(0+0x1c);
1018
 
1019
      APP_LCD_Cmd(0x75);
1020
      APP_LCD_Data(j);
1021
 
1022
      APP_LCD_Cmd(0x5c);
1023
 
2571 hawkeye 1024
      u8 bgcol = 0;
2182 hawkeye 1025
      for (i = 0; i < 128; i++)
1026
      {
2571 hawkeye 1027
         // first two pixels...
1028
         u8 out = screen[j][i];
1029
         if (flash && out == 0)
1030
            APP_LCD_Data(flash); // normally raise dark level slightly, but more intensively after 16 16th notes during flash
1031
         else
1032
            APP_LCD_Data(out);
1033
 
1034
         screen[j][i] = bgcol; // clear written pixels
1035
 
1036
         // next two pixels
2182 hawkeye 1037
         i++;
2571 hawkeye 1038
         out = screen[j][i];
1039
 
1040
         if (flash && out == 0)
1041
            APP_LCD_Data(flash); // normally raise dark level slightly, but more intensively after 16 16th notes during flash
1042
         else
1043
            APP_LCD_Data(out);
1044
 
1045
         screen[j][i] = bgcol; // clear written pixels
2182 hawkeye 1046
      }
1047
   }
1048
 
2571 hawkeye 1049
   if (flash)
1050
      oledBeatFlashState_ = 0;
2182 hawkeye 1051
}
1052
// ----------------------------------------------------------------------------------------
1053
 
1054
 
2185 hawkeye 1055
/**
1056
 * Render test screen, one half is "full on" for flicker tests
1057
 * one half contains a color gradient pattern
1058
 *
1059
 */
2182 hawkeye 1060
void testScreen()
1061
{
1062
  u16 x = 0;
1063
  u16 y = 0;
1064
 
1065
  for (y = 0; y < 64; y++)
1066
  {
1067
     APP_LCD_Cmd(0x15);
1068
     APP_LCD_Data(0x1c);
1069
 
1070
     APP_LCD_Cmd(0x75);
1071
     APP_LCD_Data(y);
1072
 
1073
     APP_LCD_Cmd(0x5c);
1074
 
1075
     for (x = 0; x < 64; x++)
1076
     {
1077
        if (x < 32)
1078
        {  // half screen pattern
1079
 
2593 hawkeye 1080
           if ((x | 4) == 0 || (y | 4) == 0)
2182 hawkeye 1081
           {
1082
              APP_LCD_Data(y & 0x0f);
1083
              APP_LCD_Data(0);
1084
           }
1085
           else
1086
           {
1087
              APP_LCD_Data(0x00);
1088
              APP_LCD_Data(0x00);
1089
           }
1090
        }
1091
        else // half screen "white"
1092
        {
1093
           APP_LCD_Data(0xff);
1094
           APP_LCD_Data(0xff);
1095
        }
1096
     }
1097
  }
1098
 
1099
  while(1);
1100
}
1101
// -------------------------------------------------------------------------------------------