1 // for finding memory leaks in debug mode with Visual Studio
2 #if defined _DEBUG && defined _MSC_VER
3 #include <crtdbg.h>
4 #endif
5
6 #include <stdio.h>
7 #include <stdint.h>
8 #include <stdbool.h>
9 #include <string.h>
10 #include <math.h> // modf()
11 #ifdef _WIN32
12 #define WIN32_MEAN_AND_LEAN
13 #include <windows.h>
14 #include <SDL2/SDL_syswm.h>
15 #endif
16 #include <stdint.h>
17 #include <stdbool.h>
18 #ifndef _WIN32
19 #include <unistd.h> // usleep()
20 #endif
21 #include <ctype.h> // tolower()
22 #include "pt2_header.h"
23 #include "pt2_keyboard.h"
24 #include "pt2_mouse.h"
25 #include "pt2_audio.h"
26 #include "pt2_helpers.h"
27 #include "pt2_textout.h"
28 #include "pt2_tables.h"
29 #include "pt2_module_loader.h"
30 #include "pt2_module_saver.h"
31 #include "pt2_sample_loader.h"
32 #include "pt2_sample_saver.h"
33 #include "pt2_pattern_viewer.h"
34 #include "pt2_sampler.h"
35 #include "pt2_diskop.h"
36 #include "pt2_visuals.h"
37 #include "pt2_scopes.h"
38 #include "pt2_edit.h"
39 #include "pt2_pat2smp.h"
40 #include "pt2_mod2wav.h"
41 #include "pt2_config.h"
42 #include "pt2_bmp.h"
43 #include "pt2_sampling.h"
44 #include "pt2_chordmaker.h"
45
46 typedef struct sprite_t
47 {
48 bool visible;
49 int8_t pixelType;
50 int16_t newX, newY, x, y, w, h;
51 uint32_t colorKey, *refreshBuffer;
52 const void *data;
53 } sprite_t;
54
55 static uint32_t vuMetersBg[4 * (10 * 48)];
56 static uint64_t timeNext64, timeNext64Frac;
57
58 sprite_t sprites[SPRITE_NUM]; // globalized
59
60 static const uint16_t cursorPosTable[24] =
61 {
62 30, 54, 62, 70, 78, 86,
63 102, 126, 134, 142, 150, 158,
64 174, 198, 206, 214, 222, 230,
65 246, 270, 278, 286, 294, 302
66 };
67
68 void updateSongInfo1(void);
69 void updateSongInfo2(void);
70 void updateSampler(void);
71 void updatePatternData(void);
72 void updateMOD2WAVDialog(void);
73
blit32(int32_t x,int32_t y,int32_t w,int32_t h,const uint32_t * src)74 void blit32(int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t *src)
75 {
76 const uint32_t *srcPtr = src;
77 uint32_t *dstPtr = &video.frameBuffer[(y * SCREEN_W) + x];
78
79 for (int32_t yy = 0; yy < h; yy++)
80 {
81 memcpy(dstPtr, srcPtr, w * sizeof (int32_t));
82
83 srcPtr += w;
84 dstPtr += SCREEN_W;
85 }
86 }
87
putPixel(int32_t x,int32_t y,const uint32_t pixelColor)88 void putPixel(int32_t x, int32_t y, const uint32_t pixelColor)
89 {
90 video.frameBuffer[(y * SCREEN_W) + x] = pixelColor;
91 }
92
hLine(int32_t x,int32_t y,int32_t w,const uint32_t pixelColor)93 void hLine(int32_t x, int32_t y, int32_t w, const uint32_t pixelColor)
94 {
95 uint32_t *dstPtr = &video.frameBuffer[(y * SCREEN_W) + x];
96 for (int32_t xx = 0; xx < w; xx++)
97 dstPtr[xx] = pixelColor;
98 }
99
vLine(int32_t x,int32_t y,int32_t h,const uint32_t pixelColor)100 void vLine(int32_t x, int32_t y, int32_t h, const uint32_t pixelColor)
101 {
102 uint32_t *dstPtr = &video.frameBuffer[(y * SCREEN_W) + x];
103 for (int32_t yy = 0; yy < h; yy++)
104 {
105 *dstPtr = pixelColor;
106 dstPtr += SCREEN_W;
107 }
108 }
109
drawFramework1(int32_t x,int32_t y,int32_t w,int32_t h)110 void drawFramework1(int32_t x, int32_t y, int32_t w, int32_t h)
111 {
112 hLine(x, y, w-1, video.palette[PAL_BORDER]);
113 vLine(x, y+1, h-2, video.palette[PAL_BORDER]);
114 hLine(x+1, y+h-1, w-1, video.palette[PAL_GENBKG2]);
115 vLine(x+w-1, y+1, h-2, video.palette[PAL_GENBKG2]);
116
117 putPixel(x, y+h-1, video.palette[PAL_GENBKG]);
118 putPixel(x+w-1, y, video.palette[PAL_GENBKG]);
119
120 fillRect(x+1, y+1, w-2, h-2, video.palette[PAL_GENBKG]);
121 }
122
drawFramework2(int32_t x,int32_t y,int32_t w,int32_t h)123 void drawFramework2(int32_t x, int32_t y, int32_t w, int32_t h)
124 {
125 hLine(x, y, w-1, video.palette[PAL_GENBKG2]);
126 vLine(x, y+1, h-2, video.palette[PAL_GENBKG2]);
127 hLine(x+1, y+h-1, w-1, video.palette[PAL_BORDER]);
128 vLine(x+w-1, y+1, h-2, video.palette[PAL_BORDER]);
129
130 putPixel(x, y+h-1, video.palette[PAL_GENBKG]);
131 putPixel(x+w-1, y, video.palette[PAL_GENBKG]);
132
133 fillRect(x+1, y+1, w-2, h-2, video.palette[PAL_BACKGRD]);
134 }
135
fillRect(int32_t x,int32_t y,int32_t w,int32_t h,const uint32_t pixelColor)136 void fillRect(int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t pixelColor)
137 {
138 uint32_t *dstPtr = &video.frameBuffer[(y * SCREEN_W) + x];
139
140 for (int32_t yy = 0; yy < h; yy++)
141 {
142 for (int32_t xx = 0; xx < w; xx++)
143 dstPtr[xx] = pixelColor;
144
145 dstPtr += SCREEN_W;
146 }
147 }
148
drawButton(int32_t x,int32_t y,int32_t w,const char * text)149 void drawButton(int32_t x, int32_t y, int32_t w, const char *text)
150 {
151 if (w < 2)
152 return;
153
154 const int32_t textW = (int32_t)strlen(text) * (FONT_CHAR_W-1);
155 const int32_t textX = x + (((w - 2) - textW) >> 1);
156
157 drawFramework1(x, y, w, 11);
158 textOut2(textX, y+3, text);
159 }
160
drawUpButton(int32_t x,int32_t y)161 void drawUpButton(int32_t x, int32_t y)
162 {
163 drawFramework1(x, y, 11, 11);
164 textOut2(x+1, y+2, ARROW_UP_STR);
165 }
166
drawDownButton(int32_t x,int32_t y)167 void drawDownButton(int32_t x, int32_t y)
168 {
169 drawFramework1(x, y, 11, 11);
170 textOut2(x+1, y+2, ARROW_DOWN_STR);
171 }
172
statusAllRight(void)173 void statusAllRight(void)
174 {
175 setStatusMessage("ALL RIGHT", DO_CARRY);
176 }
177
statusOutOfMemory(void)178 void statusOutOfMemory(void)
179 {
180 displayErrorMsg("OUT OF MEMORY !!!");
181 }
182
statusSampleIsEmpty(void)183 void statusSampleIsEmpty(void)
184 {
185 displayErrorMsg("SAMPLE IS EMPTY");
186 }
187
changeStatusText(const char * text)188 void changeStatusText(const char *text)
189 {
190 fillRect(88, 127, 17*FONT_CHAR_W, FONT_CHAR_H, video.palette[PAL_GENBKG]);
191 textOut(88, 127, text, video.palette[PAL_GENTXT]);
192 }
193
statusNotSampleZero(void)194 void statusNotSampleZero(void)
195 {
196 /* This rather confusing error message actually means that
197 ** you can't load a sample to sample slot #0 (which isn't a
198 ** real sample slot).
199 */
200 displayErrorMsg("NOT SAMPLE 0 !");
201 }
202
setupPerfFreq(void)203 void setupPerfFreq(void)
204 {
205 uint64_t perfFreq64;
206 double dInt, dFrac;
207
208 perfFreq64 = SDL_GetPerformanceFrequency(); assert(perfFreq64 != 0);
209 editor.dPerfFreq = (double)perfFreq64;
210 editor.dPerfFreqMulMicro = 1000000.0 / editor.dPerfFreq;
211
212 // calculate vblank time for performance counters and split into int/frac
213 dFrac = modf(editor.dPerfFreq / VBLANK_HZ, &dInt);
214
215 // integer part
216 editor.vblankTimeLen = (int32_t)dInt;
217
218 // fractional part (scaled to 0..2^32-1)
219 dFrac *= UINT32_MAX+1.0;
220 editor.vblankTimeLenFrac = (uint32_t)dFrac;
221 }
222
setupWaitVBL(void)223 void setupWaitVBL(void)
224 {
225 // set next frame time
226 timeNext64 = SDL_GetPerformanceCounter() + editor.vblankTimeLen;
227 timeNext64Frac = editor.vblankTimeLenFrac;
228 }
229
waitVBL(void)230 void waitVBL(void)
231 {
232 // this routine almost never delays if we have 60Hz vsync, but it's still needed in some occasions
233
234 uint64_t time64 = SDL_GetPerformanceCounter();
235 if (time64 < timeNext64)
236 {
237 time64 = timeNext64 - time64;
238 if (time64 > UINT32_MAX)
239 time64 = UINT32_MAX;
240
241 const uint32_t diff32 = (uint32_t)time64;
242
243 // convert to microseconds and round to integer
244 const int32_t time32 = (int32_t)((diff32 * editor.dPerfFreqMulMicro) + 0.5);
245
246 // delay until we have reached the next frame
247 if (time32 > 0)
248 usleep(time32);
249 }
250
251 // update next tick time
252 timeNext64 += editor.vblankTimeLen;
253 timeNext64Frac += editor.vblankTimeLenFrac;
254 if (timeNext64Frac > 0xFFFFFFFF)
255 {
256 timeNext64Frac &= 0xFFFFFFFF;
257 timeNext64++;
258 }
259 }
260
renderFrame(void)261 void renderFrame(void)
262 {
263 updateMOD2WAVDialog(); // must be first to avoid flickering issues
264
265 updateSongInfo1(); // top left side of screen, when "disk op"/"pos ed" is hidden
266 updateSongInfo2(); // two middle rows of screen, always visible
267 updateEditOp();
268 updatePatternData();
269 updateDiskOp();
270 updateSampler();
271 updatePosEd();
272 updateVisualizer();
273 handleLastGUIObjectDown();
274 drawSamplerLine();
275 writeSampleMonitorWaveform();
276 }
277
resetAllScreens(void)278 void resetAllScreens(void) // prepare GUI for "really quit?" dialog
279 {
280 editor.mixFlag = false;
281 editor.swapChannelFlag = false;
282 ui.clearScreenShown = false;
283
284 ui.changingChordNote = false;
285 ui.changingSmpResample = false;
286 ui.changingSamplingNote = false;
287 ui.changingDrumPadNote = false;
288
289 ui.pat2SmpDialogShown = false;
290 ui.disablePosEd = false;
291 ui.disableVisualizer = false;
292
293 if (ui.samplerScreenShown)
294 {
295 if (ui.samplingBoxShown)
296 {
297 ui.samplingBoxShown = false;
298 removeSamplingBox();
299 }
300
301 ui.samplerVolBoxShown = false;
302 ui.samplerFiltersBoxShown = false;
303 displaySample(); // removes volume/filter box
304 }
305
306 if (ui.editTextFlag)
307 exitGetTextLine(EDIT_TEXT_NO_UPDATE);
308 }
309
removeAskDialog(void)310 void removeAskDialog(void)
311 {
312 if (!ui.askScreenShown && !editor.isWAVRendering)
313 displayMainScreen();
314
315 ui.disablePosEd = false;
316 ui.disableVisualizer = false;
317 }
318
renderAskDialog(void)319 void renderAskDialog(void)
320 {
321 ui.disablePosEd = true;
322 ui.disableVisualizer = true;
323
324 const uint32_t *srcPtr = ui.pat2SmpDialogShown ? pat2SmpDialogBMP : yesNoDialogBMP;
325 blit32(160, 51, 104, 39, srcPtr);
326 }
327
renderBigAskDialog(void)328 void renderBigAskDialog(void)
329 {
330 ui.disablePosEd = true;
331 ui.disableVisualizer = true;
332
333 blit32(120, 44, 200, 55, bigYesNoDialogBMP);
334 }
335
showDownsampleAskDialog(void)336 void showDownsampleAskDialog(void)
337 {
338 ui.askScreenShown = true;
339 ui.askScreenType = ASK_LOAD_DOWNSAMPLE;
340 pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
341 setStatusMessage("PLEASE SELECT", NO_CARRY);
342 renderBigAskDialog();
343
344 textOutTight(133, 49, "THE SAMPLE'S FREQUENCY IS", video.palette[PAL_BACKGRD]);
345 textOutTight(154, 57, "HIGH (ABOVE 22KHZ).", video.palette[PAL_BACKGRD]);
346 textOutTight(133, 65, "DO YOU WANT TO DOWNSAMPLE", video.palette[PAL_BACKGRD]);
347 textOutTight(156, 73, "BEFORE LOADING IT?", video.palette[PAL_BACKGRD]);
348 }
349
fillFromVuMetersBgBuffer(void)350 static void fillFromVuMetersBgBuffer(void)
351 {
352 const uint32_t *srcPtr;
353 uint32_t *dstPtr;
354
355 if (ui.samplerScreenShown || editor.isWAVRendering || editor.isSMPRendering)
356 return;
357
358 srcPtr = vuMetersBg;
359 dstPtr = &video.frameBuffer[(187 * SCREEN_W) + 55];
360
361 for (uint32_t i = 0; i < AMIGA_VOICES; i++)
362 {
363 for (uint32_t y = 0; y < 48; y++)
364 {
365 for (uint32_t x = 0; x < 10; x++)
366 dstPtr[x] = srcPtr[x];
367
368 srcPtr += 10;
369 dstPtr -= SCREEN_W;
370 }
371
372 dstPtr += (SCREEN_W * 48) + 72;
373 }
374 }
375
fillToVuMetersBgBuffer(void)376 void fillToVuMetersBgBuffer(void)
377 {
378 const uint32_t *srcPtr;
379 uint32_t *dstPtr;
380
381 if (ui.samplerScreenShown || editor.isWAVRendering || editor.isSMPRendering)
382 return;
383
384 srcPtr = &video.frameBuffer[(187 * SCREEN_W) + 55];
385 dstPtr = vuMetersBg;
386
387 for (uint32_t i = 0; i < AMIGA_VOICES; i++)
388 {
389 for (uint32_t y = 0; y < 48; y++)
390 {
391 for (uint32_t x = 0; x < 10; x++)
392 dstPtr[x] = srcPtr[x];
393
394 srcPtr -= SCREEN_W;
395 dstPtr += 10;
396 }
397
398 srcPtr += (SCREEN_W * 48) + 72;
399 }
400 }
401
renderVuMeters(void)402 void renderVuMeters(void)
403 {
404 const uint32_t *srcPtr;
405 uint32_t h, *dstPtr;
406
407 if (ui.samplerScreenShown || editor.isWAVRendering || editor.isSMPRendering)
408 return;
409
410 fillToVuMetersBgBuffer();
411
412 dstPtr = &video.frameBuffer[(187 * SCREEN_W) + 55];
413 for (uint32_t i = 0; i < AMIGA_VOICES; i++)
414 {
415 if (config.realVuMeters)
416 h = editor.realVuMeterVolumes[i];
417 else
418 h = editor.vuMeterVolumes[i];
419
420 if (h > 48)
421 h = 48;
422
423 srcPtr = vuMeterBMP;
424 for (uint32_t y = 0; y < h; y++)
425 {
426 for (uint32_t x = 0; x < 10; x++)
427 dstPtr[x] = srcPtr[x];
428
429 srcPtr += 10;
430 dstPtr -= SCREEN_W;
431 }
432
433 dstPtr += (SCREEN_W * h) + 72;
434 }
435 }
436
updateSongInfo1(void)437 void updateSongInfo1(void) // left side of screen, when Disk Op. is hidden
438 {
439 moduleSample_t *currSample;
440
441 if (ui.diskOpScreenShown)
442 return;
443
444 currSample = &song->samples[editor.currSample];
445
446 if (ui.updateSongPos)
447 {
448 ui.updateSongPos = false;
449 printThreeDecimalsBg(72, 3, *editor.currPosDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
450 }
451
452 if (ui.updateSongPattern)
453 {
454 ui.updateSongPattern = false;
455 printTwoDecimalsBg(80, 14, *editor.currPatternDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
456 }
457
458 if (ui.updateSongLength)
459 {
460 ui.updateSongLength = false;
461 if (!editor.isWAVRendering)
462 printThreeDecimalsBg(72, 25, *editor.currLengthDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
463 }
464
465 if (ui.updateCurrSampleFineTune)
466 {
467 ui.updateCurrSampleFineTune = false;
468
469 if (!editor.isWAVRendering)
470 textOutBg(80, 36, ftuneStrTab[currSample->fineTune & 0xF], video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
471 }
472
473 if (ui.updateCurrSampleNum)
474 {
475 ui.updateCurrSampleNum = false;
476 if (!editor.isWAVRendering)
477 {
478 printTwoHexBg(80, 47,
479 editor.sampleZero ? 0 : ((*editor.currSampleDisp) + 1), video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
480 }
481 }
482
483 if (ui.updateCurrSampleVolume)
484 {
485 ui.updateCurrSampleVolume = false;
486 if (!editor.isWAVRendering)
487 printTwoHexBg(80, 58, *currSample->volumeDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
488 }
489
490 if (ui.updateCurrSampleLength)
491 {
492 ui.updateCurrSampleLength = false;
493 if (!editor.isWAVRendering)
494 printFourHexBg(64, 69, *currSample->lengthDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
495 }
496
497 if (ui.updateCurrSampleRepeat)
498 {
499 ui.updateCurrSampleRepeat = false;
500 printFourHexBg(64, 80, *currSample->loopStartDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
501 }
502
503 if (ui.updateCurrSampleReplen)
504 {
505 ui.updateCurrSampleReplen = false;
506 printFourHexBg(64, 91, *currSample->loopLengthDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
507 }
508 }
509
updateSongInfo2(void)510 void updateSongInfo2(void) // two middle rows of screen, always present
511 {
512 char tempChar;
513 int32_t x, i;
514 moduleSample_t *currSample;
515
516 if (ui.updateStatusText)
517 {
518 ui.updateStatusText = false;
519
520 // clear background
521 fillRect(88, 127, 17*FONT_CHAR_W, FONT_CHAR_H, video.palette[PAL_GENBKG]);
522
523 // render status text
524 if (!editor.errorMsgActive && editor.blockMarkFlag && !ui.askScreenShown
525 && !ui.clearScreenShown && !editor.swapChannelFlag)
526 {
527 textOut(88, 127, "MARK BLOCK", video.palette[PAL_GENTXT]);
528 charOut(192, 127, '-', video.palette[PAL_GENTXT]);
529
530 editor.blockToPos = song->currRow;
531 if (editor.blockFromPos >= editor.blockToPos)
532 {
533 printTwoDecimals(176, 127, editor.blockToPos, video.palette[PAL_GENTXT]);
534 printTwoDecimals(200, 127, editor.blockFromPos, video.palette[PAL_GENTXT]);
535 }
536 else
537 {
538 printTwoDecimals(176, 127, editor.blockFromPos, video.palette[PAL_GENTXT]);
539 printTwoDecimals(200, 127, editor.blockToPos, video.palette[PAL_GENTXT]);
540 }
541 }
542 else
543 {
544 textOut(88, 127, ui.statusMessage, video.palette[PAL_GENTXT]);
545 }
546 }
547
548 if (ui.updateSongBPM)
549 {
550 ui.updateSongBPM = false;
551 if (!ui.samplerScreenShown)
552 printThreeDecimalsBg(32, 123, song->currBPM, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
553 }
554
555 if (ui.updateCurrPattText)
556 {
557 ui.updateCurrPattText = false;
558 if (!ui.samplerScreenShown)
559 printTwoDecimalsBg(8, 127, *editor.currEditPatternDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
560 }
561
562 if (ui.updateTrackerFlags)
563 {
564 ui.updateTrackerFlags = false;
565
566 charOutBg(1, 113, ' ', video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
567 charOutBg(8, 113, ' ', video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
568
569 if (editor.autoInsFlag)
570 {
571 charOut(0, 113, 'I', video.palette[PAL_GENTXT]);
572
573 // in Amiga PT, "auto insert" 9 means 0
574 if (editor.autoInsSlot == 9)
575 charOut(8, 113, '0', video.palette[PAL_GENTXT]);
576 else
577 charOut(8, 113, '1' + editor.autoInsSlot, video.palette[PAL_GENTXT]);
578 }
579
580 charOutBg(1, 102, ' ', video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
581 if (editor.metroFlag)
582 charOut(0, 102, 'M', video.palette[PAL_GENTXT]);
583
584 charOutBg(16, 102, ' ', video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
585 if (editor.multiFlag)
586 charOut(16, 102, 'M', video.palette[PAL_GENTXT]);
587
588 charOutBg(24, 102, '0' + editor.editMoveAdd, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
589
590 charOutBg(311, 128, ' ', video.palette[PAL_GENBKG], video.palette[PAL_GENBKG]);
591 if (editor.pNoteFlag == 1)
592 {
593 putPixel(314, 129, video.palette[PAL_GENTXT]);
594 putPixel(315, 129, video.palette[PAL_GENTXT]);
595 }
596 else if (editor.pNoteFlag == 2)
597 {
598 putPixel(314, 128, video.palette[PAL_GENTXT]);
599 putPixel(315, 128, video.palette[PAL_GENTXT]);
600 putPixel(314, 130, video.palette[PAL_GENTXT]);
601 putPixel(315, 130, video.palette[PAL_GENTXT]);
602 }
603 }
604
605 // playback timer
606
607 const uint32_t ms1024 = editor.musicTime64 >> 32; // milliseconds (scaled from 1000 to 1024)
608
609 uint32_t seconds = ms1024 >> 10;
610 if (seconds <= 5999) // below 100 minutes (99:59 is max for the UI)
611 {
612 const uint32_t MI_TimeM = seconds / 60;
613 const uint32_t MI_TimeS = seconds - (MI_TimeM * 60);
614
615 // xx:xx
616 printTwoDecimalsBg(272, 102, MI_TimeM, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
617 printTwoDecimalsBg(296, 102, MI_TimeS, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
618 }
619 else
620 {
621 // 99:59
622 printTwoDecimalsBg(272, 102, 99, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
623 printTwoDecimalsBg(296, 102, 59, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
624 }
625
626 if (ui.updateSongName)
627 {
628 ui.updateSongName = false;
629 for (x = 0; x < 20; x++)
630 {
631 tempChar = song->header.name[x];
632 if (tempChar == '\0')
633 tempChar = '_';
634
635 charOutBg(104 + (x * FONT_CHAR_W), 102, tempChar, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
636 }
637 }
638
639 if (ui.updateCurrSampleName)
640 {
641 ui.updateCurrSampleName = false;
642 currSample = &song->samples[editor.currSample];
643
644 for (x = 0; x < 22; x++)
645 {
646 tempChar = currSample->text[x];
647 if (tempChar == '\0')
648 tempChar = '_';
649
650 charOutBg(104 + (x * FONT_CHAR_W), 113, tempChar, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
651 }
652 }
653
654 if (ui.updateSongSize)
655 {
656 ui.updateSongSize = false;
657
658 // clear background
659 fillRect(264, 123, 6*FONT_CHAR_W, FONT_CHAR_H, video.palette[PAL_GENBKG]);
660
661 // calculate module length
662
663 uint32_t totalSampleDataSize = 0;
664 for (i = 0; i < MOD_SAMPLES; i++)
665 totalSampleDataSize += song->samples[i].length;
666
667 uint32_t totalPatterns = 0;
668 for (i = 0; i < MOD_ORDERS; i++)
669 {
670 if (song->header.order[i] > totalPatterns)
671 totalPatterns = song->header.order[i];
672 }
673
674 uint32_t moduleSize = 2108 + (totalPatterns * 1024) + totalSampleDataSize;
675 if (moduleSize > 999999)
676 {
677 charOut(304, 123, 'K', video.palette[PAL_GENTXT]);
678 printFourDecimals(272, 123, moduleSize / 1000, video.palette[PAL_GENTXT]);
679 }
680 else
681 {
682 printSixDecimals(264, 123, moduleSize, video.palette[PAL_GENTXT]);
683 }
684 }
685
686 if (ui.updateSongTiming)
687 {
688 ui.updateSongTiming = false;
689 textOutBg(288, 130, (editor.timingMode == TEMPO_MODE_CIA) ? "CIA" : "VBL", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
690 }
691 }
692
updateCursorPos(void)693 void updateCursorPos(void)
694 {
695 if (!ui.samplerScreenShown)
696 setSpritePos(SPRITE_PATTERN_CURSOR, cursorPosTable[cursor.pos], 188);
697 }
698
updateSampler(void)699 void updateSampler(void)
700 {
701 int32_t tmpSampleOffset;
702 moduleSample_t *s;
703
704 if (!ui.samplerScreenShown || ui.samplingBoxShown)
705 return;
706
707 assert(editor.currSample >= 0 && editor.currSample <= 30);
708 s = &song->samples[editor.currSample];
709
710 // update 9xx offset
711 if (mouse.y >= 138 && mouse.y <= 201 && mouse.x >= 3 && mouse.x <= 316)
712 {
713 if (!ui.samplerVolBoxShown && !ui.samplerFiltersBoxShown && s->length > 0)
714 {
715 tmpSampleOffset = (scr2SmpPos(mouse.x-3) + (1 << 7)) >> 8; // rounded
716 tmpSampleOffset = 0x900 + CLAMP(tmpSampleOffset, 0x00, 0xFF);
717
718 if (tmpSampleOffset != ui.lastSampleOffset)
719 {
720 ui.lastSampleOffset = (uint16_t)tmpSampleOffset;
721 ui.update9xxPos = true;
722 }
723 }
724 }
725
726 // display 9xx offset
727 if (ui.update9xxPos)
728 {
729 ui.update9xxPos = false;
730 printThreeHexBg(288, 247, ui.lastSampleOffset, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
731 }
732
733 if (ui.updateResampleNote)
734 {
735 ui.updateResampleNote = false;
736
737 // show resample note
738 if (ui.changingSmpResample)
739 {
740 textOutBg(288, 236, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
741 }
742 else
743 {
744 assert(editor.resampleNote < 36);
745 textOutBg(288, 236,
746 config.accidental ? noteNames2[2+editor.resampleNote] : noteNames1[2+editor.resampleNote],
747 video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
748 }
749 }
750
751 if (ui.samplerVolBoxShown)
752 {
753 if (ui.updateVolFromText)
754 {
755 ui.updateVolFromText = false;
756 printThreeDecimalsBg(176, 157, *editor.vol1Disp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
757 }
758
759 if (ui.updateVolToText)
760 {
761 ui.updateVolToText = false;
762 printThreeDecimalsBg(176, 168, *editor.vol2Disp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
763 }
764 }
765 else if (ui.samplerFiltersBoxShown)
766 {
767 if (ui.updateLPText)
768 {
769 ui.updateLPText = false;
770 printFourDecimalsBg(168, 157, *editor.lpCutOffDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
771 }
772
773 if (ui.updateHPText)
774 {
775 ui.updateHPText = false;
776 printFourDecimalsBg(168, 168, *editor.hpCutOffDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
777 }
778
779 if (ui.updateNormFlag)
780 {
781 ui.updateNormFlag = false;
782
783 if (editor.normalizeFiltersFlag)
784 textOutBg(208, 179, "YES", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
785 else
786 textOutBg(208, 179, "NO ", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
787 }
788 }
789 }
790
showVolFromSlider(void)791 void showVolFromSlider(void)
792 {
793 uint32_t *dstPtr, pixel, bgPixel, sliderStart, sliderEnd;
794
795 sliderStart = ((editor.vol1 * 3) + 5) / 10;
796 sliderEnd = sliderStart + 4;
797 pixel = video.palette[PAL_QADSCP];
798 bgPixel = video.palette[PAL_BACKGRD];
799 dstPtr = &video.frameBuffer[(158 * SCREEN_W) + 105];
800
801 for (uint32_t y = 0; y < 3; y++)
802 {
803 for (uint32_t x = 0; x < 65; x++)
804 {
805 if (x >= sliderStart && x <= sliderEnd)
806 dstPtr[x] = pixel;
807 else
808 dstPtr[x] = bgPixel;
809 }
810
811 dstPtr += SCREEN_W;
812 }
813 }
814
showVolToSlider(void)815 void showVolToSlider(void)
816 {
817 uint32_t *dstPtr, pixel, bgPixel, sliderStart, sliderEnd;
818
819 sliderStart = ((editor.vol2 * 3) + 5) / 10;
820 sliderEnd = sliderStart + 4;
821 pixel = video.palette[PAL_QADSCP];
822 bgPixel = video.palette[PAL_BACKGRD];
823 dstPtr = &video.frameBuffer[(169 * SCREEN_W) + 105];
824
825 for (uint32_t y = 0; y < 3; y++)
826 {
827 for (uint32_t x = 0; x < 65; x++)
828 {
829 if (x >= sliderStart && x <= sliderEnd)
830 dstPtr[x] = pixel;
831 else
832 dstPtr[x] = bgPixel;
833 }
834
835 dstPtr += SCREEN_W;
836 }
837 }
838
renderSamplerVolBox(void)839 void renderSamplerVolBox(void)
840 {
841 blit32(72, 154, 136, 33, samplerVolumeBMP);
842
843 ui.updateVolFromText = true;
844 ui.updateVolToText = true;
845 showVolFromSlider();
846 showVolToSlider();
847
848 // hide loop sprites
849 hideSprite(SPRITE_LOOP_PIN_LEFT);
850 hideSprite(SPRITE_LOOP_PIN_RIGHT);
851 }
852
removeSamplerVolBox(void)853 void removeSamplerVolBox(void)
854 {
855 displaySample();
856 }
857
renderSamplerFiltersBox(void)858 void renderSamplerFiltersBox(void)
859 {
860 blit32(65, 154, 186, 33, samplerFiltersBMP);
861
862 textOut(200, 157, "HZ", video.palette[PAL_GENTXT]);
863 textOut(200, 168, "HZ", video.palette[PAL_GENTXT]);
864
865 ui.updateLPText = true;
866 ui.updateHPText = true;
867 ui.updateNormFlag = true;
868
869 // hide loop sprites
870 hideSprite(SPRITE_LOOP_PIN_LEFT);
871 hideSprite(SPRITE_LOOP_PIN_RIGHT);
872 }
873
removeSamplerFiltersBox(void)874 void removeSamplerFiltersBox(void)
875 {
876 displaySample();
877 }
878
updatePosEd(void)879 void updatePosEd(void)
880 {
881 if (!ui.posEdScreenShown || !ui.updatePosEd)
882 return;
883
884 ui.updatePosEd = false;
885
886 if (!ui.disablePosEd)
887 {
888 int32_t posEdPosition = song->currOrder;
889 if (posEdPosition > song->header.numOrders-1)
890 posEdPosition = song->header.numOrders-1;
891
892 // top five
893 for (int32_t y = 0; y < 5; y++)
894 {
895 if (posEdPosition-(5-y) >= 0)
896 {
897 printThreeDecimalsBg(128, 23+(y*6), posEdPosition-(5-y), video.palette[PAL_QADSCP], video.palette[PAL_BACKGRD]);
898 printTwoDecimalsBg(160, 23+(y*6), song->header.order[posEdPosition-(5-y)], video.palette[PAL_QADSCP], video.palette[PAL_BACKGRD]);
899 }
900 else
901 {
902 fillRect(128, 23+(y*6), 22*FONT_CHAR_W, FONT_CHAR_H, video.palette[PAL_BACKGRD]);
903 }
904 }
905
906 // middle
907 printThreeDecimalsBg(128, 53, posEdPosition, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
908 printTwoDecimalsBg(160, 53, *editor.currPosEdPattDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
909
910 // bottom six
911 for (int32_t y = 0; y < 6; y++)
912 {
913 if (posEdPosition+y < song->header.numOrders-1)
914 {
915 printThreeDecimalsBg(128, 59+(y*6), posEdPosition+(y+1), video.palette[PAL_QADSCP], video.palette[PAL_BACKGRD]);
916 printTwoDecimalsBg(160, 59+(y*6), song->header.order[posEdPosition+(y+1)], video.palette[PAL_QADSCP], video.palette[PAL_BACKGRD]);
917 }
918 else
919 {
920 fillRect(128, 59+(y*6), 22*FONT_CHAR_W, FONT_CHAR_H, video.palette[PAL_BACKGRD]);
921 }
922 }
923
924 // kludge to fix bottom part of text edit marker in pos ed
925 if (ui.editTextFlag && ui.editObject == PTB_PE_PATT)
926 renderTextEditMarker();
927 }
928 }
929
renderPosEdScreen(void)930 void renderPosEdScreen(void)
931 {
932 blit32(120, 0, 200, 99, posEdBMP);
933 }
934
renderMuteButtons(void)935 void renderMuteButtons(void)
936 {
937 const uint32_t *srcPtr;
938 uint32_t *dstPtr, srcPitch;
939
940 if (ui.diskOpScreenShown || ui.posEdScreenShown)
941 return;
942
943 dstPtr = &video.frameBuffer[(3 * SCREEN_W) + 310];
944 for (uint32_t i = 0; i < AMIGA_VOICES; i++)
945 {
946 if (editor.muted[i])
947 {
948 srcPtr = &muteButtonsBMP[i * (6 * 7)];
949 srcPitch = 7;
950 }
951 else
952 {
953 srcPtr = &trackerFrameBMP[((3 + (i * 11)) * SCREEN_W) + 310];
954 srcPitch = SCREEN_W;
955 }
956
957 for (uint32_t y = 0; y < 6; y++)
958 {
959 for (uint32_t x = 0; x < 7; x++)
960 dstPtr[x] = srcPtr[x];
961
962 srcPtr += srcPitch;
963 dstPtr += SCREEN_W;
964 }
965
966 dstPtr += SCREEN_W * 5;
967 }
968 }
969
renderClearScreen(void)970 void renderClearScreen(void)
971 {
972 ui.disablePosEd = true;
973 ui.disableVisualizer = true;
974
975 blit32(160, 51, 104, 39, clearDialogBMP);
976 }
977
removeClearScreen(void)978 void removeClearScreen(void)
979 {
980 displayMainScreen();
981
982 ui.disablePosEd = false;
983 ui.disableVisualizer = false;
984 }
985
updateCurrSample(void)986 void updateCurrSample(void)
987 {
988 ui.updateCurrSampleName = true;
989 ui.updateSongSize = true;
990
991 if (!ui.diskOpScreenShown)
992 {
993 ui.updateCurrSampleFineTune = true;
994 ui.updateCurrSampleNum = true;
995 ui.updateCurrSampleVolume = true;
996 ui.updateCurrSampleLength = true;
997 ui.updateCurrSampleRepeat = true;
998 ui.updateCurrSampleReplen = true;
999 }
1000
1001 if (ui.samplerScreenShown)
1002 redrawSample();
1003
1004 updateSamplePos();
1005 recalcChordLength();
1006
1007 sampler.tmpLoopStart = 0;
1008 sampler.tmpLoopLength = 0;
1009 }
1010
updatePatternData(void)1011 void updatePatternData(void)
1012 {
1013 if (ui.updatePatternData)
1014 {
1015 ui.updatePatternData = false;
1016 if (!ui.samplerScreenShown)
1017 redrawPattern();
1018 }
1019 }
1020
removeTextEditMarker(void)1021 void removeTextEditMarker(void)
1022 {
1023 if (!ui.editTextFlag)
1024 return;
1025
1026 if (ui.editObject == PTB_PE_PATT)
1027 {
1028 // position editor text editing
1029 hLine(ui.lineCurX - 4, ui.lineCurY - 1, 7, video.palette[PAL_GENBKG2]);
1030
1031 // no need to clear the second row of pixels
1032
1033 ui.updatePosEd = true;
1034 }
1035 else
1036 {
1037 // all others
1038 fillRect(ui.lineCurX - 4, ui.lineCurY - 1, 7, 2, video.palette[PAL_GENBKG]);
1039 }
1040 }
1041
renderTextEditMarker(void)1042 void renderTextEditMarker(void)
1043 {
1044 if (!ui.editTextFlag)
1045 return;
1046
1047 fillRect(ui.lineCurX - 4, ui.lineCurY - 1, 7, 2, video.palette[PAL_TEXTMARK]);
1048 }
1049
sendMouseButtonUpEvent(uint8_t button)1050 static void sendMouseButtonUpEvent(uint8_t button)
1051 {
1052 SDL_Event event;
1053
1054 memset(&event, 0, sizeof (event));
1055
1056 event.type = SDL_MOUSEBUTTONUP;
1057 event.button.button = button;
1058
1059 SDL_PushEvent(&event);
1060 }
1061
handleLastGUIObjectDown(void)1062 void handleLastGUIObjectDown(void)
1063 {
1064 bool testMouseButtonRelease = false;
1065
1066 if (ui.sampleMarkingPos >= 0)
1067 {
1068 samplerSamplePressed(MOUSE_BUTTON_HELD);
1069 testMouseButtonRelease = true;
1070 }
1071
1072 if (ui.forceSampleDrag)
1073 {
1074 samplerBarPressed(MOUSE_BUTTON_HELD);
1075 testMouseButtonRelease = true;
1076 }
1077
1078 if (ui.forceSampleEdit)
1079 {
1080 samplerEditSample(MOUSE_BUTTON_HELD);
1081 testMouseButtonRelease = true;
1082 }
1083
1084 if (ui.forceVolDrag)
1085 {
1086 volBoxBarPressed(MOUSE_BUTTON_HELD);
1087 testMouseButtonRelease = true;
1088 }
1089
1090 /* Hack to send "mouse button up" events if we released the mouse button(s)
1091 ** outside of the window...
1092 */
1093 if (testMouseButtonRelease)
1094 {
1095 if (mouse.x < 0 || mouse.x >= SCREEN_W || mouse.y < 0 || mouse.y >= SCREEN_H)
1096 {
1097 if (mouse.leftButtonPressed && !(mouse.buttonState & SDL_BUTTON_LMASK))
1098 sendMouseButtonUpEvent(SDL_BUTTON_LEFT);
1099
1100 if (mouse.rightButtonPressed && !(mouse.buttonState & SDL_BUTTON_RMASK))
1101 sendMouseButtonUpEvent(SDL_BUTTON_RIGHT);
1102 }
1103 }
1104 }
1105
updateVisualizer(void)1106 void updateVisualizer(void)
1107 {
1108 if (ui.disableVisualizer || ui.diskOpScreenShown ||
1109 ui.posEdScreenShown || ui.editOpScreenShown ||
1110 ui.aboutScreenShown || ui.askScreenShown ||
1111 editor.isWAVRendering || ui.samplingBoxShown)
1112 {
1113 return;
1114 }
1115
1116 if (ui.visualizerMode == VISUAL_SPECTRUM)
1117 {
1118 // spectrum analyzer
1119
1120 uint32_t *dstPtr = &video.frameBuffer[(59 * SCREEN_W) + 129];
1121 for (uint32_t i = 0; i < SPECTRUM_BAR_NUM; i++)
1122 {
1123 const uint32_t *srcPtr = analyzerColorsRGB24;
1124 uint32_t pixel = video.palette[PAL_GENBKG];
1125
1126 int32_t tmpVol = editor.spectrumVolumes[i];
1127 if (tmpVol > SPECTRUM_BAR_HEIGHT)
1128 tmpVol = SPECTRUM_BAR_HEIGHT;
1129
1130 for (int32_t y = SPECTRUM_BAR_HEIGHT-1; y >= 0; y--)
1131 {
1132 if (y < tmpVol)
1133 pixel = srcPtr[y];
1134
1135 for (uint32_t x = 0; x < SPECTRUM_BAR_WIDTH; x++)
1136 dstPtr[x] = pixel;
1137
1138 dstPtr += SCREEN_W;
1139 }
1140
1141 dstPtr -= (SCREEN_W * SPECTRUM_BAR_HEIGHT) - (SPECTRUM_BAR_WIDTH + 2);
1142 }
1143 }
1144 else
1145 {
1146 drawScopes();
1147 }
1148 }
1149
renderQuadrascopeBg(void)1150 void renderQuadrascopeBg(void)
1151 {
1152 const uint32_t *srcPtr;
1153 uint32_t *dstPtr;
1154
1155 srcPtr = &trackerFrameBMP[(44 * SCREEN_W) + 120];
1156 dstPtr = &video.frameBuffer[(44 * SCREEN_W) + 120];
1157
1158 for (uint32_t y = 0; y < 55; y++)
1159 {
1160 memcpy(dstPtr, srcPtr, 200 * sizeof (int32_t));
1161
1162 srcPtr += SCREEN_W;
1163 dstPtr += SCREEN_W;
1164 }
1165
1166 for (int32_t i = 0; i < AMIGA_VOICES; i++)
1167 scope[i].emptyScopeDrawn = false;
1168 }
1169
renderSpectrumAnalyzerBg(void)1170 void renderSpectrumAnalyzerBg(void)
1171 {
1172 blit32(120, 44, 200, 55, spectrumVisualsBMP);
1173 }
1174
renderAboutScreen(void)1175 void renderAboutScreen(void)
1176 {
1177 char verString[16];
1178 uint32_t verStringX;
1179
1180 if (!ui.aboutScreenShown || ui.diskOpScreenShown || ui.posEdScreenShown || ui.editOpScreenShown)
1181 return;
1182
1183 blit32(120, 44, 200, 55, aboutScreenBMP);
1184
1185 // draw version string
1186
1187 sprintf(verString, "v%s", PROG_VER_STR);
1188 verStringX = 260 + (((63 - ((uint32_t)strlen(verString) * (FONT_CHAR_W - 1))) + 1) / 2);
1189 textOutTight(verStringX, 67, verString, video.palette[PAL_GENBKG2]);
1190 }
1191
renderEditOpMode(void)1192 void renderEditOpMode(void)
1193 {
1194 const uint32_t *srcPtr;
1195
1196 // select what character box to render
1197
1198 switch (ui.editOpScreen)
1199 {
1200 default:
1201 case 0:
1202 srcPtr = &editOpModeCharsBMP[editor.sampleAllFlag ? EDOP_MODE_BMP_A_OFS : EDOP_MODE_BMP_S_OFS];
1203 break;
1204
1205 case 1:
1206 {
1207 if (editor.trackPattFlag == 0) srcPtr = &editOpModeCharsBMP[EDOP_MODE_BMP_T_OFS];
1208 else if (editor.trackPattFlag == 1) srcPtr = &editOpModeCharsBMP[EDOP_MODE_BMP_P_OFS];
1209 else srcPtr = &editOpModeCharsBMP[EDOP_MODE_BMP_S_OFS];
1210 }
1211 break;
1212
1213 case 2:
1214 srcPtr = &editOpModeCharsBMP[editor.halfClipFlag ? EDOP_MODE_BMP_C_OFS : EDOP_MODE_BMP_H_OFS];
1215 break;
1216
1217 case 3:
1218 srcPtr = (editor.newOldFlag == 0) ? &editOpModeCharsBMP[EDOP_MODE_BMP_N_OFS] : &editOpModeCharsBMP[EDOP_MODE_BMP_O_OFS];
1219 break;
1220 }
1221
1222 // render it...
1223 blit32(310, 47, 7, 6, srcPtr);
1224 }
1225
renderEditOpScreen(void)1226 void renderEditOpScreen(void)
1227 {
1228 const uint32_t *srcPtr;
1229
1230 // select which graphics to render
1231 switch (ui.editOpScreen)
1232 {
1233 default:
1234 case 0: srcPtr = editOpScreen1BMP; break;
1235 case 1: srcPtr = editOpScreen2BMP; break;
1236 case 2: srcPtr = editOpScreen3BMP; break;
1237 case 3: srcPtr = editOpScreen4BMP; break;
1238 }
1239
1240 blit32(120, 44, 200, 55, srcPtr);
1241
1242 renderEditOpMode();
1243
1244 // render text and content
1245 if (ui.editOpScreen == 0)
1246 {
1247 textOut(128, 47, " TRACK PATTERN ", video.palette[PAL_GENTXT]);
1248 }
1249 else if (ui.editOpScreen == 1)
1250 {
1251 textOut(128, 47, " RECORD SAMPLES ", video.palette[PAL_GENTXT]);
1252
1253 ui.updateRecordText = true;
1254 ui.updateQuantizeText = true;
1255 ui.updateMetro1Text = true;
1256 ui.updateMetro2Text = true;
1257 ui.updateFromText = true;
1258 ui.updateKeysText = true;
1259 ui.updateToText = true;
1260 }
1261 else if (ui.editOpScreen == 2)
1262 {
1263 textOut(128, 47, " SAMPLE EDITOR ", video.palette[PAL_GENTXT]);
1264 charOut(272, 91, '%', video.palette[PAL_GENTXT]); // for Volume text
1265
1266 ui.updatePosText = true;
1267 ui.updateModText = true;
1268 ui.updateVolText = true;
1269 }
1270 else if (ui.editOpScreen == 3)
1271 {
1272 textOut(128, 47, " SAMPLE CHORD EDITOR ", video.palette[PAL_GENTXT]);
1273
1274 ui.updateLengthText = true;
1275 ui.updateNote1Text = true;
1276 ui.updateNote2Text = true;
1277 ui.updateNote3Text = true;
1278 ui.updateNote4Text = true;
1279 }
1280 }
1281
renderMOD2WAVDialog(void)1282 void renderMOD2WAVDialog(void)
1283 {
1284 blit32(64, 27, 192, 48, mod2wavBMP);
1285 }
1286
updateMOD2WAVDialog(void)1287 void updateMOD2WAVDialog(void)
1288 {
1289 if (!ui.updateMod2WavDialog)
1290 return;
1291
1292 ui.updateMod2WavDialog = false;
1293
1294 if (editor.isWAVRendering)
1295 {
1296 if (ui.mod2WavFinished)
1297 {
1298 ui.mod2WavFinished = false;
1299
1300 resetSong();
1301 pointerSetMode(POINTER_MODE_IDLE, DO_CARRY);
1302
1303 if (editor.abortMod2Wav)
1304 {
1305 displayErrorMsg("MOD2WAV ABORTED !");
1306 }
1307 else
1308 {
1309 displayMsg("MOD RENDERED !");
1310 setMsgPointer();
1311 }
1312
1313 editor.isWAVRendering = false;
1314 modSetTempo(song->currBPM, true); // update BPM with normal audio output rate
1315 displayMainScreen();
1316 }
1317 else
1318 {
1319 if (song->rowsInTotal == 0)
1320 return;
1321
1322 // render progress bar
1323
1324 int32_t percent = (song->rowsCounter * 100) / song->rowsInTotal;
1325 if (percent > 100)
1326 percent = 100;
1327
1328 // foreground (progress)
1329 const int32_t progressBarWidth = ((percent * 180) + 50) / 100;
1330 if (progressBarWidth > 0)
1331 fillRect(70, 42, progressBarWidth, 11, video.palette[PAL_GENBKG2]); // foreground (progress)
1332
1333 // background
1334 int32_t bgWidth = 180 - progressBarWidth;
1335 if (bgWidth > 0)
1336 fillRect(70+progressBarWidth, 42, bgWidth, 11, video.palette[PAL_BORDER]);
1337
1338 // draw percentage text
1339 if (percent > 99)
1340 printThreeDecimals(144, 45, percent, video.palette[PAL_GENTXT]);
1341 else
1342 printTwoDecimals(152, 45, percent, video.palette[PAL_GENTXT]);
1343
1344 charOut(168, 45, '%', video.palette[PAL_GENTXT]);
1345 }
1346 }
1347 }
1348
updateEditOp(void)1349 void updateEditOp(void)
1350 {
1351 if (!ui.editOpScreenShown || ui.posEdScreenShown || ui.diskOpScreenShown)
1352 return;
1353
1354 if (ui.editOpScreen == 1)
1355 {
1356 if (ui.updateRecordText)
1357 {
1358 ui.updateRecordText = false;
1359 textOutBg(176, 58, (editor.recordMode == RECORD_PATT) ? "PATT" : "SONG", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1360 }
1361
1362 if (ui.updateQuantizeText)
1363 {
1364 ui.updateQuantizeText = false;
1365 printTwoDecimalsBg(192, 69, *editor.quantizeValueDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1366 }
1367
1368 if (ui.updateMetro1Text)
1369 {
1370 ui.updateMetro1Text = false;
1371 printTwoDecimalsBg(168, 80, *editor.metroSpeedDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1372 }
1373
1374 if (ui.updateMetro2Text)
1375 {
1376 ui.updateMetro2Text = false;
1377 printTwoDecimalsBg(192, 80, *editor.metroChannelDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1378 }
1379
1380 if (ui.updateFromText)
1381 {
1382 ui.updateFromText = false;
1383 printTwoHexBg(264, 80, *editor.sampleFromDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1384 }
1385
1386 if (ui.updateKeysText)
1387 {
1388 ui.updateKeysText = false;
1389 textOutBg(160, 91, editor.multiFlag ? "MULTI " : "SINGLE", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1390 }
1391
1392 if (ui.updateToText)
1393 {
1394 ui.updateToText = false;
1395 printTwoHexBg(264, 91, *editor.sampleToDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1396 }
1397 }
1398 else if (ui.editOpScreen == 2)
1399 {
1400 if (ui.updateMixText)
1401 {
1402 ui.updateMixText = false;
1403 if (editor.mixFlag)
1404 {
1405 textOutBg(128, 47, editor.mixText, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1406 textOutBg(248, 47, " ", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1407 }
1408 else
1409 {
1410 textOutBg(128, 47, " SAMPLE EDITOR ", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1411 }
1412 }
1413
1414 if (ui.updatePosText)
1415 {
1416 ui.updatePosText = false;
1417 printFourHexBg(248, 58, *editor.samplePosDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1418 }
1419
1420 if (ui.updateModText)
1421 {
1422 ui.updateModText = false;
1423 printThreeDecimalsBg(256, 69,
1424 (editor.modulateSpeed < 0) ? (0 - editor.modulateSpeed) : editor.modulateSpeed,
1425 video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1426
1427 if (editor.modulateSpeed < 0)
1428 charOutBg(248, 69, '-', video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1429 else
1430 charOutBg(248, 69, ' ', video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1431 }
1432
1433 if (ui.updateVolText)
1434 {
1435 ui.updateVolText = false;
1436 printThreeDecimalsBg(248, 91, *editor.sampleVolDisp, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1437 }
1438 }
1439 else if (ui.editOpScreen == 3)
1440 {
1441 if (ui.updateLengthText)
1442 {
1443 ui.updateLengthText = false;
1444
1445 // clear background
1446 textOutBg(168, 91, " ", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1447 charOut(198, 91, ':', video.palette[PAL_GENBKG]);
1448
1449 if (song->samples[editor.currSample].loopLength > 2 || song->samples[editor.currSample].loopStart >= 2)
1450 {
1451 textOut(168, 91, "LOOP", video.palette[PAL_GENTXT]);
1452 }
1453 else
1454 {
1455 printFourHex(168, 91, *editor.chordLengthDisp, video.palette[PAL_GENTXT]); // chord max length
1456 charOut(198, 91, (editor.chordLengthMin) ? '.' : ':', video.palette[PAL_GENTXT]); // min/max flag
1457 }
1458 }
1459
1460 if (ui.updateNote1Text)
1461 {
1462 ui.updateNote1Text = false;
1463 if (editor.note1 > 35)
1464 textOutBg(256, 58, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1465 else
1466 textOutBg(256, 58, config.accidental ? noteNames2[2+editor.note1] : noteNames1[2+editor.note1],
1467 video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1468 }
1469
1470 if (ui.updateNote2Text)
1471 {
1472 ui.updateNote2Text = false;
1473 if (editor.note2 > 35)
1474 textOutBg(256, 69, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1475 else
1476 textOutBg(256, 69, config.accidental ? noteNames2[2+editor.note2] : noteNames1[2+editor.note2],
1477 video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1478 }
1479
1480 if (ui.updateNote3Text)
1481 {
1482 ui.updateNote3Text = false;
1483 if (editor.note3 > 35)
1484 textOutBg(256, 80, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1485 else
1486 textOutBg(256, 80, config.accidental ? noteNames2[2+editor.note3] : noteNames1[2+editor.note3],
1487 video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1488 }
1489
1490 if (ui.updateNote4Text)
1491 {
1492 ui.updateNote4Text = false;
1493 if (editor.note4 > 35)
1494 textOutBg(256, 91, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1495 else
1496 textOutBg(256, 91, config.accidental ? noteNames2[2+editor.note4] : noteNames1[2+editor.note4],
1497 video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
1498 }
1499 }
1500 }
1501
displayMainScreen(void)1502 void displayMainScreen(void)
1503 {
1504 editor.blockMarkFlag = false;
1505
1506 ui.updateSongName = true;
1507 ui.updateSongSize = true;
1508 ui.updateSongTiming = true;
1509 ui.updateTrackerFlags = true;
1510 ui.updateStatusText = true;
1511
1512 ui.updateCurrSampleName = true;
1513
1514 if (!ui.diskOpScreenShown)
1515 {
1516 ui.updateCurrSampleFineTune = true;
1517 ui.updateCurrSampleNum = true;
1518 ui.updateCurrSampleVolume = true;
1519 ui.updateCurrSampleLength = true;
1520 ui.updateCurrSampleRepeat = true;
1521 ui.updateCurrSampleReplen = true;
1522 }
1523
1524 if (ui.samplerScreenShown)
1525 {
1526 if (!ui.diskOpScreenShown)
1527 blit32(0, 0, 320, 121, trackerFrameBMP);
1528 }
1529 else
1530 {
1531 if (!ui.diskOpScreenShown)
1532 blit32(0, 0, 320, 255, trackerFrameBMP);
1533 else
1534 blit32(0, 121, 320, 134, &trackerFrameBMP[121 * SCREEN_W]);
1535
1536 ui.updateSongBPM = true;
1537 ui.updateCurrPattText = true;
1538 ui.updatePatternData = true;
1539 }
1540
1541 if (ui.diskOpScreenShown)
1542 {
1543 renderDiskOpScreen();
1544 }
1545 else
1546 {
1547 ui.updateSongPos = true;
1548 ui.updateSongPattern = true;
1549 ui.updateSongLength = true;
1550
1551 // draw zeroes that will never change (to the left of numbers)
1552
1553 charOut(64, 3, '0', video.palette[PAL_GENTXT]);
1554 textOut(64, 14, "00", video.palette[PAL_GENTXT]);
1555
1556 if (!editor.isWAVRendering)
1557 {
1558 charOut(64, 25, '0', video.palette[PAL_GENTXT]);
1559 textOut(64, 47, "00", video.palette[PAL_GENTXT]);
1560 textOut(64, 58, "00", video.palette[PAL_GENTXT]);
1561 }
1562
1563 if (ui.posEdScreenShown)
1564 {
1565 renderPosEdScreen();
1566 ui.updatePosEd = true;
1567 }
1568 else
1569 {
1570 if (ui.editOpScreenShown)
1571 {
1572 renderEditOpScreen();
1573 }
1574 else
1575 {
1576 if (ui.aboutScreenShown)
1577 {
1578 renderAboutScreen();
1579 }
1580 else
1581 {
1582 if (ui.visualizerMode == VISUAL_QUADRASCOPE) renderQuadrascopeBg();
1583 else if (ui.visualizerMode == VISUAL_SPECTRUM) renderSpectrumAnalyzerBg();
1584 }
1585 }
1586
1587 renderMuteButtons();
1588 }
1589 }
1590 }
1591
restoreStatusAndMousePointer(void)1592 static void restoreStatusAndMousePointer(void)
1593 {
1594 editor.errorMsgActive = false;
1595 editor.errorMsgBlock = false;
1596 editor.errorMsgCounter = 0;
1597 pointerSetPreviousMode();
1598 setPrevStatusMessage();
1599 }
1600
handleAskNo(void)1601 void handleAskNo(void)
1602 {
1603 ui.pat2SmpDialogShown = false;
1604
1605 switch (ui.askScreenType)
1606 {
1607 case ASK_SAVEMOD_OVERWRITE:
1608 {
1609 restoreStatusAndMousePointer();
1610 saveModule(DONT_CHECK_IF_FILE_EXIST, GIVE_NEW_FILENAME);
1611 }
1612 break;
1613
1614 case ASK_SAVESMP_OVERWRITE:
1615 {
1616 restoreStatusAndMousePointer();
1617 saveSample(DONT_CHECK_IF_FILE_EXIST, GIVE_NEW_FILENAME);
1618 }
1619 break;
1620
1621 case ASK_LOAD_DOWNSAMPLE:
1622 {
1623 restoreStatusAndMousePointer();
1624 extLoadWAVOrAIFFSampleCallback(DONT_DOWNSAMPLE);
1625 }
1626 break;
1627
1628 default:
1629 restoreStatusAndMousePointer();
1630 break;
1631 }
1632
1633 removeAskDialog();
1634 }
1635
handleAskYes(void)1636 void handleAskYes(void)
1637 {
1638 char fileName[20 + 4 + 1];
1639 int8_t oldSample;
1640 uint32_t i;
1641 moduleSample_t *s;
1642
1643 switch (ui.askScreenType)
1644 {
1645 case ASK_DISCARD_SONG:
1646 {
1647 restoreStatusAndMousePointer();
1648 diskOpLoadFile2();
1649 }
1650 break;
1651
1652 case ASK_DISCARD_SONG_DRAGNDROP:
1653 {
1654 restoreStatusAndMousePointer();
1655 loadDroppedFile2();
1656 }
1657 break;
1658
1659 case ASK_RESTORE_SAMPLE:
1660 {
1661 restoreStatusAndMousePointer();
1662 redoSampleData(editor.currSample);
1663 }
1664 break;
1665
1666 case ASK_PAT2SMP:
1667 {
1668 restoreStatusAndMousePointer();
1669 doPat2Smp();
1670 }
1671 break;
1672
1673 case ASK_SAVE_ALL_SAMPLES:
1674 {
1675 editor.errorMsgActive = false;
1676 editor.errorMsgBlock = false;
1677 editor.errorMsgCounter = 0;
1678
1679 oldSample = editor.currSample;
1680 for (i = 0; i < MOD_SAMPLES; i++)
1681 {
1682 editor.currSample = (int8_t)i;
1683 if (song->samples[i].length > 2)
1684 saveSample(DONT_CHECK_IF_FILE_EXIST, GIVE_NEW_FILENAME);
1685 }
1686 editor.currSample = oldSample;
1687
1688 displayMsg("SAMPLES SAVED !");
1689 setMsgPointer();
1690 }
1691 break;
1692
1693 case ASK_MAKE_CHORD:
1694 {
1695 restoreStatusAndMousePointer();
1696 mixChordSample();
1697 }
1698 break;
1699
1700 case ASK_BOOST_ALL_SAMPLES:
1701 {
1702 restoreStatusAndMousePointer();
1703
1704 for (i = 0; i < MOD_SAMPLES; i++)
1705 boostSample(i, true);
1706
1707 if (ui.samplerScreenShown)
1708 redrawSample();
1709
1710 updateWindowTitle(MOD_IS_MODIFIED);
1711 }
1712 break;
1713
1714 case ASK_FILTER_ALL_SAMPLES:
1715 {
1716 restoreStatusAndMousePointer();
1717
1718 for (i = 0; i < MOD_SAMPLES; i++)
1719 filterSample(i, true);
1720
1721 if (ui.samplerScreenShown)
1722 redrawSample();
1723
1724 updateWindowTitle(MOD_IS_MODIFIED);
1725 }
1726 break;
1727
1728 case ASK_UPSAMPLE:
1729 {
1730 restoreStatusAndMousePointer();
1731 upSample();
1732 }
1733 break;
1734
1735 case ASK_DOWNSAMPLE:
1736 {
1737 restoreStatusAndMousePointer();
1738 downSample();
1739 }
1740 break;
1741
1742 case ASK_KILL_SAMPLE:
1743 {
1744 restoreStatusAndMousePointer();
1745
1746 if (editor.sampleZero)
1747 {
1748 statusNotSampleZero();
1749 break;
1750 }
1751
1752 turnOffVoices();
1753 s = &song->samples[editor.currSample];
1754
1755 s->fineTune = 0;
1756 s->volume = 0;
1757 s->length = 0;
1758 s->loopStart = 0;
1759 s->loopLength = 2;
1760
1761 memset(s->text, 0, sizeof (s->text));
1762 memset(&song->sampleData[(editor.currSample * MAX_SAMPLE_LEN)], 0, MAX_SAMPLE_LEN);
1763
1764 editor.samplePos = 0;
1765 updateCurrSample();
1766
1767 ui.updateSongSize = true;
1768 updateWindowTitle(MOD_IS_MODIFIED);
1769 }
1770 break;
1771
1772 case ASK_RESAMPLE:
1773 {
1774 restoreStatusAndMousePointer();
1775 samplerResample();
1776 }
1777 break;
1778
1779 case ASK_LOAD_DOWNSAMPLE:
1780 {
1781 // for WAV and AIFF sample loader
1782 restoreStatusAndMousePointer();
1783 extLoadWAVOrAIFFSampleCallback(DO_DOWNSAMPLE);
1784 }
1785 break;
1786
1787 case ASK_MOD2WAV_OVERWRITE:
1788 {
1789 memset(fileName, 0, sizeof (fileName));
1790
1791 if (song->header.name[0] != '\0')
1792 {
1793 for (i = 0; i < 20; i++)
1794 {
1795 fileName[i] = (char)tolower(song->header.name[i]);
1796 if (fileName[i] == '\0') break;
1797 sanitizeFilenameChar(&fileName[i]);
1798 }
1799
1800 strcat(fileName, ".wav");
1801 }
1802 else
1803 {
1804 strcpy(fileName, "untitled.wav");
1805 }
1806
1807 renderToWav(fileName, DONT_CHECK_IF_FILE_EXIST);
1808 }
1809 break;
1810
1811 case ASK_MOD2WAV:
1812 {
1813 memset(fileName, 0, sizeof (fileName));
1814
1815 if (song->header.name[0] != '\0')
1816 {
1817 for (i = 0; i < 20; i++)
1818 {
1819 fileName[i] = (char)tolower(song->header.name[i]);
1820 if (fileName[i] == '\0') break;
1821 sanitizeFilenameChar(&fileName[i]);
1822 }
1823
1824 strcat(fileName, ".wav");
1825 }
1826 else
1827 {
1828 strcpy(fileName, "untitled.wav");
1829 }
1830
1831 renderToWav(fileName, CHECK_IF_FILE_EXIST);
1832 }
1833 break;
1834
1835 case ASK_QUIT:
1836 {
1837 restoreStatusAndMousePointer();
1838 ui.throwExit = true;
1839 }
1840 break;
1841
1842 case ASK_SAVE_SAMPLE:
1843 {
1844 restoreStatusAndMousePointer();
1845 saveSample(CHECK_IF_FILE_EXIST, DONT_GIVE_NEW_FILENAME);
1846 }
1847 break;
1848
1849 case ASK_SAVESMP_OVERWRITE:
1850 {
1851 restoreStatusAndMousePointer();
1852 saveSample(DONT_CHECK_IF_FILE_EXIST, DONT_GIVE_NEW_FILENAME);
1853 }
1854 break;
1855
1856 case ASK_SAVE_MODULE:
1857 {
1858 restoreStatusAndMousePointer();
1859 saveModule(CHECK_IF_FILE_EXIST, DONT_GIVE_NEW_FILENAME);
1860 }
1861 break;
1862
1863 case ASK_SAVEMOD_OVERWRITE:
1864 {
1865 restoreStatusAndMousePointer();
1866 saveModule(DONT_CHECK_IF_FILE_EXIST, DONT_GIVE_NEW_FILENAME);
1867 }
1868 break;
1869
1870 default: break;
1871 }
1872
1873 removeAskDialog();
1874 }
1875
videoClose(void)1876 void videoClose(void)
1877 {
1878 SDL_DestroyTexture(video.texture);
1879 SDL_DestroyRenderer(video.renderer);
1880 SDL_DestroyWindow(video.window);
1881 free(video.frameBufferUnaligned);
1882 }
1883
setupSprites(void)1884 void setupSprites(void)
1885 {
1886 memset(sprites, 0, sizeof (sprites));
1887
1888 sprites[SPRITE_MOUSE_POINTER].data = mousePointerBMP;
1889 sprites[SPRITE_MOUSE_POINTER].pixelType = SPRITE_TYPE_PALETTE;
1890 sprites[SPRITE_MOUSE_POINTER].colorKey = PAL_COLORKEY;
1891 sprites[SPRITE_MOUSE_POINTER].w = 16;
1892 sprites[SPRITE_MOUSE_POINTER].h = 16;
1893 hideSprite(SPRITE_MOUSE_POINTER);
1894
1895 sprites[SPRITE_PATTERN_CURSOR].data = patternCursorBMP;
1896 sprites[SPRITE_PATTERN_CURSOR].pixelType = SPRITE_TYPE_RGB;
1897 sprites[SPRITE_PATTERN_CURSOR].colorKey = video.palette[PAL_COLORKEY];
1898 sprites[SPRITE_PATTERN_CURSOR].w = 11;
1899 sprites[SPRITE_PATTERN_CURSOR].h = 14;
1900 hideSprite(SPRITE_PATTERN_CURSOR);
1901
1902 sprites[SPRITE_LOOP_PIN_LEFT].data = loopPinsBMP;
1903 sprites[SPRITE_LOOP_PIN_LEFT].pixelType = SPRITE_TYPE_RGB;
1904 sprites[SPRITE_LOOP_PIN_LEFT].colorKey = video.palette[PAL_COLORKEY];
1905 sprites[SPRITE_LOOP_PIN_LEFT].w = 4;
1906 sprites[SPRITE_LOOP_PIN_LEFT].h = 64;
1907 hideSprite(SPRITE_LOOP_PIN_LEFT);
1908
1909 sprites[SPRITE_LOOP_PIN_RIGHT].data = &loopPinsBMP[4 * 64];
1910 sprites[SPRITE_LOOP_PIN_RIGHT].pixelType = SPRITE_TYPE_RGB;
1911 sprites[SPRITE_LOOP_PIN_RIGHT].colorKey = video.palette[PAL_COLORKEY];
1912 sprites[SPRITE_LOOP_PIN_RIGHT].w = 4;
1913 sprites[SPRITE_LOOP_PIN_RIGHT].h = 64;
1914 hideSprite(SPRITE_LOOP_PIN_RIGHT);
1915
1916 sprites[SPRITE_SAMPLING_POS_LINE].data = samplingPosBMP;
1917 sprites[SPRITE_SAMPLING_POS_LINE].pixelType = SPRITE_TYPE_RGB;
1918 sprites[SPRITE_SAMPLING_POS_LINE].colorKey = video.palette[PAL_COLORKEY];
1919 sprites[SPRITE_SAMPLING_POS_LINE].w = 1;
1920 sprites[SPRITE_SAMPLING_POS_LINE].h = 64;
1921 hideSprite(SPRITE_SAMPLING_POS_LINE);
1922
1923 // setup refresh buffer (used to clear sprites after each frame)
1924 for (uint32_t i = 0; i < SPRITE_NUM; i++)
1925 sprites[i].refreshBuffer = (uint32_t *)malloc((sprites[i].w * sprites[i].h) * sizeof (int32_t));
1926 }
1927
freeSprites(void)1928 void freeSprites(void)
1929 {
1930 for (int32_t i = 0; i < SPRITE_NUM; i++)
1931 free(sprites[i].refreshBuffer);
1932 }
1933
setSpritePos(int32_t sprite,int32_t x,int32_t y)1934 void setSpritePos(int32_t sprite, int32_t x, int32_t y)
1935 {
1936 sprites[sprite].newX = (int16_t)x;
1937 sprites[sprite].newY = (int16_t)y;
1938 }
1939
hideSprite(int32_t sprite)1940 void hideSprite(int32_t sprite)
1941 {
1942 sprites[sprite].newX = SCREEN_W;
1943 }
1944
eraseSprites(void)1945 void eraseSprites(void)
1946 {
1947 int32_t sx, sy, x, y, sw, sh, srcPitch, dstPitch;
1948 const uint32_t *src32;
1949 uint32_t *dst32;
1950 sprite_t *s;
1951
1952 for (int32_t i = SPRITE_NUM-1; i >= 0; i--) // erasing must be done in reverse order
1953 {
1954 s = &sprites[i];
1955 if (s->x >= SCREEN_W || s->y >= SCREEN_H) // sprite is hidden, don't draw nor fill clear buffer
1956 continue;
1957
1958 assert(s->refreshBuffer != NULL);
1959
1960 sw = s->w;
1961 sh = s->h;
1962 sx = s->x;
1963 sy = s->y;
1964
1965 // if x is negative, adjust variables
1966 if (sx < 0)
1967 {
1968 sw += sx; // subtraction
1969 sx = 0;
1970 }
1971
1972 // if y is negative, adjust variables
1973 if (sy < 0)
1974 {
1975 sh += sy; // subtraction
1976 sy = 0;
1977 }
1978
1979 dst32 = &video.frameBuffer[(sy * SCREEN_W) + sx];
1980 src32 = s->refreshBuffer;
1981
1982 // handle x/y clipping
1983 if (sx+sw >= SCREEN_W) sw = SCREEN_W - sx;
1984 if (sy+sh >= SCREEN_H) sh = SCREEN_H - sy;
1985
1986 srcPitch = s->w - sw;
1987 dstPitch = SCREEN_W - sw;
1988
1989 for (y = 0; y < sh; y++)
1990 {
1991 for (x = 0; x < sw; x++)
1992 *dst32++ = *src32++;
1993
1994 src32 += srcPitch;
1995 dst32 += dstPitch;
1996 }
1997 }
1998
1999 fillFromVuMetersBgBuffer(); // let's put it here even though it's not sprite-based
2000 }
2001
renderSprites(void)2002 void renderSprites(void)
2003 {
2004 const uint8_t *src8;
2005 int32_t sx, sy, x, y, srcPtrBias, sw, sh, srcPitch, dstPitch;
2006 const uint32_t *src32;
2007 uint32_t *dst32, *clr32, colorKey;
2008 sprite_t *s;
2009
2010 renderVuMeters(); // let's put it here even though it's not sprite-based
2011
2012 for (int32_t i = 0; i < SPRITE_NUM; i++)
2013 {
2014 s = &sprites[i];
2015
2016 // set new sprite position
2017 s->x = s->newX;
2018 s->y = s->newY;
2019
2020 if (s->x >= SCREEN_W || s->y >= SCREEN_H) // sprite is hidden, don't draw nor fill clear buffer
2021 continue;
2022
2023 assert(s->data != NULL && s->refreshBuffer != NULL);
2024
2025 sw = s->w;
2026 sh = s->h;
2027 sx = s->x;
2028 sy = s->y;
2029 srcPtrBias = 0;
2030
2031 // if x is negative, adjust variables
2032 if (sx < 0)
2033 {
2034 sw += sx; // subtraction
2035 srcPtrBias += -sx;
2036 sx = 0;
2037 }
2038
2039 // if y is negative, adjust variables
2040 if (sy < 0)
2041 {
2042 sh += sy; // subtraction
2043 srcPtrBias += (-sy * s->w);
2044 sy = 0;
2045 }
2046
2047 dst32 = &video.frameBuffer[(sy * SCREEN_W) + sx];
2048 clr32 = s->refreshBuffer;
2049
2050 // handle x/y clipping
2051 if (sx+sw >= SCREEN_W) sw = SCREEN_W - sx;
2052 if (sy+sh >= SCREEN_H) sh = SCREEN_H - sy;
2053
2054 srcPitch = s->w - sw;
2055 dstPitch = SCREEN_W - sw;
2056
2057 colorKey = sprites[i].colorKey;
2058 if (sprites[i].pixelType == SPRITE_TYPE_RGB)
2059 {
2060 // 24-bit RGB sprite
2061 src32 = ((uint32_t *)sprites[i].data) + srcPtrBias;
2062 for (y = 0; y < sh; y++)
2063 {
2064 for (x = 0; x < sw; x++)
2065 {
2066 *clr32++ = *dst32; // fill clear buffer
2067 if (*src32 != colorKey)
2068 *dst32 = *src32;
2069
2070 dst32++;
2071 src32++;
2072 }
2073
2074 clr32 += srcPitch;
2075 src32 += srcPitch;
2076 dst32 += dstPitch;
2077 }
2078 }
2079 else
2080 {
2081 // 8-bit paletted sprite
2082 src8 = ((uint8_t *)sprites[i].data) + srcPtrBias;
2083 for (y = 0; y < sh; y++)
2084 {
2085 for (x = 0; x < sw; x++)
2086 {
2087 *clr32++ = *dst32; // fill clear buffer
2088 if (*src8 != colorKey)
2089 {
2090 assert(*src8 < PALETTE_NUM);
2091 *dst32 = video.palette[*src8];
2092 }
2093
2094 dst32++;
2095 src8++;
2096 }
2097
2098 clr32 += srcPitch;
2099 src8 += srcPitch;
2100 dst32 += dstPitch;
2101 }
2102 }
2103 }
2104 }
2105
flipFrame(void)2106 void flipFrame(void)
2107 {
2108 const uint32_t windowFlags = SDL_GetWindowFlags(video.window);
2109 bool minimized = (windowFlags & SDL_WINDOW_MINIMIZED) ? true : false;
2110
2111 renderSprites();
2112 SDL_UpdateTexture(video.texture, NULL, video.frameBuffer, SCREEN_W * sizeof (int32_t));
2113
2114 // SDL 2.0.14 bug on Windows (?): This function consumes ever-increasing memory if the program is minimized
2115 if (!minimized)
2116 SDL_RenderClear(video.renderer);
2117
2118 SDL_RenderCopy(video.renderer, video.texture, NULL, NULL);
2119 SDL_RenderPresent(video.renderer);
2120 eraseSprites();
2121
2122 if (!video.vsync60HzPresent)
2123 {
2124 waitVBL(); // we have no VSync, do crude thread sleeping to sync to ~60Hz
2125 }
2126 else
2127 {
2128 /* We have VSync, but it can unexpectedly get inactive in certain scenarios.
2129 ** We have to force thread sleeping (to ~60Hz) if so.
2130 */
2131 #ifdef __APPLE__
2132 // macOS: VSync gets disabled if the window is 100% covered by another window. Let's add a (crude) fix:
2133 if (minimized || !(windowFlags & SDL_WINDOW_INPUT_FOCUS))
2134 waitVBL();
2135 #elif __unix__
2136 // *NIX: VSync gets disabled in fullscreen mode (at least on some distros/systems). Let's add a fix:
2137 if (minimized || video.fullscreen)
2138 waitVBL();
2139 #else
2140 if (minimized)
2141 waitVBL();
2142 #endif
2143 }
2144 }
2145
updateSpectrumAnalyzer(int8_t vol,int16_t period)2146 void updateSpectrumAnalyzer(int8_t vol, int16_t period)
2147 {
2148 const uint8_t maxHeight = SPECTRUM_BAR_HEIGHT + 1; // +1 because of audio latency - allows full height to be seen
2149 int8_t scaledVol;
2150 int32_t scaledNote;
2151
2152 if (ui.visualizerMode != VISUAL_SPECTRUM || vol <= 0)
2153 return;
2154
2155 scaledVol = (vol * 24600L) >> 16; // scaledVol = (vol << 8) / 682
2156
2157 period = CLAMP(period, 113, 856);
2158
2159 scaledNote = 856 - period;
2160 scaledNote *= scaledNote;
2161 scaledNote = ((int64_t)scaledNote * 171162) >> 32; // scaledNote /= 25093
2162
2163 // scaledNote now ranges 0..22, no need to clamp
2164
2165 // increment main spectrum bar
2166 editor.spectrumVolumes[scaledNote] += scaledVol;
2167 if (editor.spectrumVolumes[scaledNote] > maxHeight)
2168 editor.spectrumVolumes[scaledNote] = maxHeight;
2169
2170 // increment left side of spectrum bar with half volume
2171 if (scaledNote > 0)
2172 {
2173 editor.spectrumVolumes[scaledNote-1] += scaledVol >> 1;
2174 if (editor.spectrumVolumes[scaledNote-1] > maxHeight)
2175 editor.spectrumVolumes[scaledNote-1] = maxHeight;
2176 }
2177
2178 // increment right side of spectrum bar with half volume
2179 if (scaledNote < SPECTRUM_BAR_NUM-1)
2180 {
2181 editor.spectrumVolumes[scaledNote+1] += scaledVol >> 1;
2182 if (editor.spectrumVolumes[scaledNote+1] > maxHeight)
2183 editor.spectrumVolumes[scaledNote+1] = maxHeight;
2184 }
2185 }
2186
sinkVisualizerBars(void)2187 void sinkVisualizerBars(void)
2188 {
2189 int32_t i;
2190
2191 // sink stuff @ 49.92Hz (Amiga PAL) rate
2192
2193 static uint64_t counter50Hz;
2194 const uint64_t counter50HzDelta = (uint64_t)(((UINT32_MAX+1.0) * (AMIGA_PAL_VBLANK_HZ / (double)VBLANK_HZ)) + 0.5);
2195
2196 counter50Hz += counter50HzDelta; // 32.32 fixed-point counter
2197 if (counter50Hz > 0xFFFFFFFF)
2198 {
2199 counter50Hz &= 0xFFFFFFFF;
2200
2201 // sink VU-meters
2202 for (i = 0; i < AMIGA_VOICES; i++)
2203 {
2204 if (editor.vuMeterVolumes[i] > 0)
2205 editor.vuMeterVolumes[i]--;
2206 }
2207
2208 // sink "spectrum analyzer" bars
2209 for (i = 0; i < SPECTRUM_BAR_NUM; i++)
2210 {
2211 if (editor.spectrumVolumes[i] > 0)
2212 editor.spectrumVolumes[i]--;
2213 }
2214 }
2215 }
2216
updateRenderSizeVars(void)2217 void updateRenderSizeVars(void)
2218 {
2219 int32_t di;
2220 #ifdef __APPLE__
2221 int32_t actualScreenW, actualScreenH;
2222 double dXUpscale, dYUpscale;
2223 #endif
2224 float fXScale, fYScale;
2225 SDL_DisplayMode dm;
2226
2227 di = SDL_GetWindowDisplayIndex(video.window);
2228 if (di < 0)
2229 di = 0; // return display index 0 (default) on error
2230
2231 SDL_GetDesktopDisplayMode(di, &dm);
2232 video.displayW = dm.w;
2233 video.displayH = dm.h;
2234
2235 if (video.fullscreen)
2236 {
2237 if (config.fullScreenStretch)
2238 {
2239 video.renderW = video.displayW;
2240 video.renderH = video.displayH;
2241 video.renderX = 0;
2242 video.renderY = 0;
2243 }
2244 else
2245 {
2246 SDL_RenderGetScale(video.renderer, &fXScale, &fYScale);
2247
2248 video.renderW = (int32_t)(SCREEN_W * fXScale);
2249 video.renderH = (int32_t)(SCREEN_H * fYScale);
2250
2251 #ifdef __APPLE__
2252 // retina high-DPI hackery (SDL2 is bad at reporting actual rendering sizes on macOS w/ high-DPI)
2253 SDL_GL_GetDrawableSize(video.window, &actualScreenW, &actualScreenH);
2254 SDL_GetDesktopDisplayMode(0, &dm);
2255
2256 dXUpscale = (double)actualScreenW / video.displayW;
2257 dYUpscale = (double)actualScreenH / video.displayH;
2258
2259 // downscale back to correct sizes
2260 if (dXUpscale != 0.0) video.renderW = (int32_t)(video.renderW / dXUpscale);
2261 if (dYUpscale != 0.0) video.renderH = (int32_t)(video.renderH / dYUpscale);
2262 #endif
2263 video.renderX = (video.displayW - video.renderW) >> 1;
2264 video.renderY = (video.displayH - video.renderH) >> 1;
2265 }
2266 }
2267 else
2268 {
2269 SDL_GetWindowSize(video.window, &video.renderW, &video.renderH);
2270
2271 video.renderX = 0;
2272 video.renderY = 0;
2273 }
2274
2275 // for mouse cursor creation
2276 video.xScale = (int32_t)((video.renderW * (1.0 / SCREEN_W)) + 0.5);
2277 video.yScale = (int32_t)((video.renderH * (1.0 / SCREEN_H)) + 0.5);
2278 createMouseCursors();
2279 }
2280
toggleFullScreen(void)2281 void toggleFullScreen(void)
2282 {
2283 SDL_DisplayMode dm;
2284
2285 video.fullscreen ^= 1;
2286 if (video.fullscreen)
2287 {
2288 if (config.fullScreenStretch)
2289 {
2290 SDL_GetDesktopDisplayMode(0, &dm);
2291 SDL_RenderSetLogicalSize(video.renderer, dm.w, dm.h);
2292 }
2293 else
2294 {
2295 SDL_RenderSetLogicalSize(video.renderer, SCREEN_W, SCREEN_H);
2296 }
2297
2298 SDL_SetWindowSize(video.window, SCREEN_W, SCREEN_H);
2299 SDL_SetWindowFullscreen(video.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
2300 SDL_SetWindowGrab(video.window, SDL_TRUE);
2301 }
2302 else
2303 {
2304 SDL_SetWindowFullscreen(video.window, 0);
2305 SDL_RenderSetLogicalSize(video.renderer, SCREEN_W, SCREEN_H);
2306 SDL_SetWindowSize(video.window, SCREEN_W * config.videoScaleFactor, SCREEN_H * config.videoScaleFactor);
2307
2308 // this is not sensible on a multi-monitor setup
2309 //SDL_SetWindowPosition(video.window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
2310
2311 SDL_SetWindowGrab(video.window, SDL_FALSE);
2312 }
2313
2314 updateRenderSizeVars();
2315 updateMouseScaling();
2316
2317 if (video.fullscreen)
2318 {
2319 mouse.setPosX = video.displayW >> 1;
2320 mouse.setPosY = video.displayH >> 1;
2321 }
2322 else
2323 {
2324 mouse.setPosX = video.renderW >> 1;
2325 mouse.setPosY = video.renderH >> 1;
2326 }
2327
2328 mouse.setPosFlag = true;
2329 }
2330
setupVideo(void)2331 bool setupVideo(void)
2332 {
2333 int32_t screenW, screenH;
2334 uint32_t rendererFlags;
2335 SDL_DisplayMode dm;
2336
2337 screenW = SCREEN_W * config.videoScaleFactor;
2338 screenH = SCREEN_H * config.videoScaleFactor;
2339
2340 rendererFlags = 0;
2341
2342 #ifdef _WIN32
2343 #if SDL_PATCHLEVEL >= 4
2344 SDL_SetHint(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "1"); // this is for Windows only
2345 #endif
2346 #endif
2347
2348 #if SDL_PATCHLEVEL >= 5
2349 SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
2350 #endif
2351
2352 video.vsync60HzPresent = false;
2353 if (!config.vsyncOff)
2354 {
2355 SDL_GetDesktopDisplayMode(0, &dm);
2356 if (dm.refresh_rate >= 59 && dm.refresh_rate <= 61)
2357 {
2358 video.vsync60HzPresent = true;
2359 rendererFlags |= SDL_RENDERER_PRESENTVSYNC;
2360 }
2361 }
2362
2363 uint32_t windowFlags = SDL_WINDOW_HIDDEN | SDL_WINDOW_ALLOW_HIGHDPI;
2364
2365 video.window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED,
2366 SDL_WINDOWPOS_CENTERED, screenW, screenH, windowFlags);
2367
2368 if (video.window == NULL)
2369 {
2370 showErrorMsgBox("Couldn't create SDL window:\n%s", SDL_GetError());
2371 return false;
2372 }
2373
2374 video.renderer = SDL_CreateRenderer(video.window, -1, rendererFlags);
2375 if (video.renderer == NULL)
2376 {
2377 if (video.vsync60HzPresent) // try again without vsync flag
2378 {
2379 video.vsync60HzPresent = false;
2380 rendererFlags &= ~SDL_RENDERER_PRESENTVSYNC;
2381 video.renderer = SDL_CreateRenderer(video.window, -1, rendererFlags);
2382 }
2383
2384 if (video.renderer == NULL)
2385 {
2386 showErrorMsgBox("Couldn't create SDL renderer:\n%s\n\n" \
2387 "Is your GPU (+ driver) too old?", SDL_GetError());
2388 return false;
2389 }
2390 }
2391
2392 SDL_RenderSetLogicalSize(video.renderer, SCREEN_W, SCREEN_H);
2393
2394 #if SDL_PATCHLEVEL >= 5
2395 SDL_RenderSetIntegerScale(video.renderer, config.integerScaling ? SDL_TRUE : SDL_FALSE);
2396 #endif
2397
2398 SDL_SetRenderDrawBlendMode(video.renderer, SDL_BLENDMODE_NONE);
2399
2400 if (config.pixelFilter == PIXELFILTER_LINEAR)
2401 SDL_SetHint("SDL_RENDER_SCALE_QUALITY", "linear");
2402 else if (config.pixelFilter == PIXELFILTER_BEST)
2403 SDL_SetHint("SDL_RENDER_SCALE_QUALITY", "best");
2404 else
2405 SDL_SetHint("SDL_RENDER_SCALE_QUALITY", "nearest");
2406
2407 video.texture = SDL_CreateTexture(video.renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_W, SCREEN_H);
2408 if (video.texture == NULL)
2409 {
2410 showErrorMsgBox("Couldn't create %dx%d GPU texture:\n%s\n\n" \
2411 "Is your GPU (+ driver) too old?", SCREEN_W, SCREEN_H, SDL_GetError());
2412 return false;
2413 }
2414
2415 SDL_SetTextureBlendMode(video.texture, SDL_BLENDMODE_NONE);
2416
2417 // frame buffer used by SDL (for texture)
2418 video.frameBufferUnaligned = (uint32_t *)MALLOC_PAD(SCREEN_W * SCREEN_H * sizeof (int32_t), 256);
2419 if (video.frameBufferUnaligned == NULL)
2420 {
2421 showErrorMsgBox("Out of memory!");
2422 return false;
2423 }
2424
2425 // we want an aligned pointer
2426 video.frameBuffer = (uint32_t *)ALIGN_PTR(video.frameBufferUnaligned, 256);
2427
2428 updateRenderSizeVars();
2429 updateMouseScaling();
2430
2431 if (config.hwMouse)
2432 SDL_ShowCursor(SDL_TRUE);
2433 else
2434 SDL_ShowCursor(SDL_FALSE);
2435
2436 // Workaround: SDL_GetGlobalMouseState() doesn't work with KMSDRM/Wayland
2437 video.useDesktopMouseCoords = true;
2438 const char *videoDriver = SDL_GetCurrentVideoDriver();
2439 if (videoDriver != NULL && (strcmp("KMSDRM", videoDriver) == 0 || strcmp("wayland", videoDriver) == 0))
2440 video.useDesktopMouseCoords = false;
2441
2442 SDL_SetRenderDrawColor(video.renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
2443 return true;
2444 }
2445