1 /* This file contains the routines for the following sample editor functions:
2 ** - Resampler
3 ** - Echo
4 ** - Mix
5 ** - Volume
6 **/
7
8 // for finding memory leaks in debug mode with Visual Studio
9 #if defined _DEBUG && defined _MSC_VER
10 #include <crtdbg.h>
11 #endif
12
13 #include <stdio.h>
14 #include <stdint.h>
15 #include <stdbool.h>
16 #include <math.h>
17 #include "ft2_header.h"
18 #include "ft2_mouse.h"
19 #include "ft2_audio.h"
20 #include "ft2_gui.h"
21 #include "ft2_events.h"
22 #include "ft2_video.h"
23 #include "ft2_inst_ed.h"
24 #include "ft2_sample_ed.h"
25 #include "ft2_keyboard.h"
26 #include "ft2_tables.h"
27 #include "ft2_structs.h"
28
29 static volatile bool stopThread;
30
31 static int8_t smpEd_RelReSmp, mix_Balance = 50;
32 static bool echo_AddMemory, exitFlag, outOfMemory;
33 static int16_t echo_nEcho = 1, echo_VolChange = 30;
34 static int32_t echo_Distance = 0x100;
35 static double dVol_StartVol = 100.0, dVol_EndVol = 100.0;
36 static SDL_Thread *thread;
37
pbExit(void)38 static void pbExit(void)
39 {
40 ui.sysReqShown = false;
41 exitFlag = true;
42 }
43
windowOpen(void)44 static void windowOpen(void)
45 {
46 ui.sysReqShown = true;
47 ui.sysReqEnterPressed = false;
48
49 unstuckLastUsedGUIElement();
50 SDL_EventState(SDL_DROPFILE, SDL_DISABLE);
51 }
52
windowClose(bool rewriteSample)53 static void windowClose(bool rewriteSample)
54 {
55 SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
56
57 if (exitFlag || rewriteSample)
58 writeSample(true);
59 else
60 updateNewSample();
61
62 mouseAnimOff();
63 }
64
sbSetResampleTones(uint32_t pos)65 static void sbSetResampleTones(uint32_t pos)
66 {
67 if (smpEd_RelReSmp != (int8_t)(pos - 36))
68 smpEd_RelReSmp = (int8_t)(pos - 36);
69 }
70
pbResampleTonesDown(void)71 static void pbResampleTonesDown(void)
72 {
73 if (smpEd_RelReSmp > -36)
74 smpEd_RelReSmp--;
75 }
76
pbResampleTonesUp(void)77 static void pbResampleTonesUp(void)
78 {
79 if (smpEd_RelReSmp < 36)
80 smpEd_RelReSmp++;
81 }
82
resampleThread(void * ptr)83 static int32_t SDLCALL resampleThread(void *ptr)
84 {
85 smpPtr_t sp;
86
87 if (instr[editor.curInstr] == NULL)
88 return true;
89
90 sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
91 bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
92
93 const double dRatio = exp2((int32_t)smpEd_RelReSmp / 12.0);
94
95 double dNewLen = s->length * dRatio;
96 if (dNewLen > (double)MAX_SAMPLE_LEN)
97 dNewLen = (double)MAX_SAMPLE_LEN;
98
99 const uint32_t newLen = (int32_t)floor(dNewLen);
100 if (!allocateSmpDataPtr(&sp, newLen, sample16Bit))
101 {
102 outOfMemory = true;
103 setMouseBusy(false);
104 ui.sysReqShown = false;
105 return true;
106 }
107
108 int8_t *dst = sp.ptr;
109 int8_t *src = s->dataPtr;
110
111 // 32.32 fixed-point logic
112 const uint64_t delta64 = (const uint64_t)round((UINT32_MAX+1.0) / dRatio);
113 uint64_t posFrac64 = 0;
114
115 pauseAudio();
116 unfixSample(s);
117
118 /* Nearest-neighbor resampling (no interpolation).
119 **
120 ** Could benefit from windowed-sinc interpolation,
121 ** but it seems like some people prefer no resampling
122 ** interpolation (like FT2).
123 */
124
125 if (newLen > 0)
126 {
127 if (sample16Bit)
128 {
129 const int16_t *src16 = (const int16_t *)src;
130 int16_t *dst16 = (int16_t *)dst;
131
132 for (uint32_t i = 0; i < newLen; i++)
133 {
134 const uint32_t position = posFrac64 >> 32;
135 dst16[i] = src16[position];
136 posFrac64 += delta64;
137 }
138 }
139 else // 8-bit
140 {
141 const int8_t *src8 = src;
142 int8_t *dst8 = dst;
143
144 for (uint32_t i = 0; i < newLen; i++)
145 {
146 const uint32_t position = posFrac64 >> 32;
147 dst8[i] = src8[position];
148 posFrac64 += delta64;
149 }
150 }
151 }
152
153 freeSmpData(s);
154 setSmpDataPtr(s, &sp);
155
156 s->relativeNote += smpEd_RelReSmp;
157 s->length = newLen;
158 s->loopStart = (int32_t)(s->loopStart * dRatio);
159 s->loopLength = (int32_t)(s->loopLength * dRatio);
160
161 sanitizeSample(s);
162
163 fixSample(s);
164 resumeAudio();
165
166 setSongModifiedFlag();
167 setMouseBusy(false);
168
169 ui.sysReqShown = false;
170 return true;
171
172 (void)ptr;
173 }
174
pbDoResampling(void)175 static void pbDoResampling(void)
176 {
177 mouseAnimOn();
178 thread = SDL_CreateThread(resampleThread, NULL, NULL);
179 if (thread == NULL)
180 {
181 okBox(0, "System message", "Couldn't create thread!");
182 return;
183 }
184
185 SDL_DetachThread(thread);
186 }
187
drawResampleBox(void)188 static void drawResampleBox(void)
189 {
190 char sign;
191 const int16_t x = 209;
192 const int16_t y = 230;
193 const int16_t w = 214;
194 const int16_t h = 54;
195
196 // main fill
197 fillRect(x + 1, y + 1, w - 2, h - 2, PAL_BUTTONS);
198
199 // outer border
200 vLine(x, y, h - 1, PAL_BUTTON1);
201 hLine(x + 1, y, w - 2, PAL_BUTTON1);
202 vLine(x + w - 1, y, h, PAL_BUTTON2);
203 hLine(x, y + h - 1, w - 1, PAL_BUTTON2);
204
205 // inner border
206 vLine(x + 2, y + 2, h - 5, PAL_BUTTON2);
207 hLine(x + 3, y + 2, w - 6, PAL_BUTTON2);
208 vLine(x + w - 3, y + 2, h - 4, PAL_BUTTON1);
209 hLine(x + 2, y + h - 3, w - 4, PAL_BUTTON1);
210
211 sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
212
213 double dLenMul = exp2(smpEd_RelReSmp * (1.0 / 12.0));
214
215 double dNewLen = s->length * dLenMul;
216 if (dNewLen > (double)MAX_SAMPLE_LEN)
217 dNewLen = (double)MAX_SAMPLE_LEN;
218
219 textOutShadow(215, 236, PAL_FORGRND, PAL_BUTTON2, "Rel. h.tones");
220 textOutShadow(215, 250, PAL_FORGRND, PAL_BUTTON2, "New sample size");
221 hexOut(361, 250, PAL_FORGRND, (int32_t)dNewLen, 8);
222
223 if (smpEd_RelReSmp == 0) sign = ' ';
224 else if (smpEd_RelReSmp < 0) sign = '-';
225 else sign = '+';
226
227 uint16_t val = ABS(smpEd_RelReSmp);
228 if (val > 9)
229 {
230 charOut(291, 236, PAL_FORGRND, sign);
231 charOut(298, 236, PAL_FORGRND, '0' + ((val / 10) % 10));
232 charOut(305, 236, PAL_FORGRND, '0' + (val % 10));
233 }
234 else
235 {
236 charOut(298, 236, PAL_FORGRND, sign);
237 charOut(305, 236, PAL_FORGRND, '0' + (val % 10));
238 }
239 }
240
setupResampleBoxWidgets(void)241 static void setupResampleBoxWidgets(void)
242 {
243 pushButton_t *p;
244 scrollBar_t *s;
245
246 // "Apply" pushbutton
247 p = &pushButtons[0];
248 memset(p, 0, sizeof (pushButton_t));
249 p->caption = "Apply";
250 p->x = 214;
251 p->y = 264;
252 p->w = 73;
253 p->h = 16;
254 p->callbackFuncOnUp = pbDoResampling;
255 p->visible = true;
256
257 // "Exit" pushbutton
258 p = &pushButtons[1];
259 memset(p, 0, sizeof (pushButton_t));
260 p->caption = "Exit";
261 p->x = 345;
262 p->y = 264;
263 p->w = 73;
264 p->h = 16;
265 p->callbackFuncOnUp = pbExit;
266 p->visible = true;
267
268 // scrollbar buttons
269
270 p = &pushButtons[2];
271 memset(p, 0, sizeof (pushButton_t));
272 p->caption = ARROW_LEFT_STRING;
273 p->x = 314;
274 p->y = 234;
275 p->w = 23;
276 p->h = 13;
277 p->preDelay = 1;
278 p->delayFrames = 3;
279 p->callbackFuncOnDown = pbResampleTonesDown;
280 p->visible = true;
281
282 p = &pushButtons[3];
283 memset(p, 0, sizeof (pushButton_t));
284 p->caption = ARROW_RIGHT_STRING;
285 p->x = 395;
286 p->y = 234;
287 p->w = 23;
288 p->h = 13;
289 p->preDelay = 1;
290 p->delayFrames = 3;
291 p->callbackFuncOnDown = pbResampleTonesUp;
292 p->visible = true;
293
294 // echo num scrollbar
295 s = &scrollBars[0];
296 memset(s, 0, sizeof (scrollBar_t));
297 s->x = 337;
298 s->y = 234;
299 s->w = 58;
300 s->h = 13;
301 s->callbackFunc = sbSetResampleTones;
302 s->visible = true;
303 setScrollBarPageLength(0, 1);
304 setScrollBarEnd(0, 36 * 2);
305 }
306
pbSampleResample(void)307 void pbSampleResample(void)
308 {
309 uint16_t i;
310
311 if (editor.curInstr == 0 ||
312 instr[editor.curInstr] == NULL ||
313 instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
314 {
315 return;
316 }
317
318 setupResampleBoxWidgets();
319 windowOpen();
320
321 outOfMemory = false;
322
323 exitFlag = false;
324 while (ui.sysReqShown)
325 {
326 readInput();
327 if (ui.sysReqEnterPressed)
328 pbDoResampling();
329
330 setSyncedReplayerVars();
331 handleRedrawing();
332
333 drawResampleBox();
334 setScrollBarPos(0, smpEd_RelReSmp + 36, false);
335 drawCheckBox(0);
336 for (i = 0; i < 4; i++) drawPushButton(i);
337 drawScrollBar(0);
338
339 flipFrame();
340 }
341
342 for (i = 0; i < 4; i++) hidePushButton(i);
343 hideScrollBar(0);
344
345 windowClose(false);
346
347 if (outOfMemory)
348 okBox(0, "System message", "Not enough memory!");
349 }
350
cbEchoAddMemory(void)351 static void cbEchoAddMemory(void)
352 {
353 echo_AddMemory ^= 1;
354 }
355
sbSetEchoNumPos(uint32_t pos)356 static void sbSetEchoNumPos(uint32_t pos)
357 {
358 if (echo_nEcho != (int32_t)pos)
359 echo_nEcho = (int16_t)pos;
360 }
361
sbSetEchoDistPos(uint32_t pos)362 static void sbSetEchoDistPos(uint32_t pos)
363 {
364 if (echo_Distance != (int32_t)pos)
365 echo_Distance = (int32_t)pos;
366 }
367
sbSetEchoFadeoutPos(uint32_t pos)368 static void sbSetEchoFadeoutPos(uint32_t pos)
369 {
370 if (echo_VolChange != (int32_t)pos)
371 echo_VolChange = (int16_t)pos;
372 }
373
pbEchoNumDown(void)374 static void pbEchoNumDown(void)
375 {
376 if (echo_nEcho > 0)
377 echo_nEcho--;
378 }
379
pbEchoNumUp(void)380 static void pbEchoNumUp(void)
381 {
382 if (echo_nEcho < 64)
383 echo_nEcho++;
384 }
385
pbEchoDistDown(void)386 static void pbEchoDistDown(void)
387 {
388 if (echo_Distance > 0)
389 echo_Distance--;
390 }
391
pbEchoDistUp(void)392 static void pbEchoDistUp(void)
393 {
394 if (echo_Distance < 16384)
395 echo_Distance++;
396 }
397
pbEchoFadeoutDown(void)398 static void pbEchoFadeoutDown(void)
399 {
400 if (echo_VolChange > 0)
401 echo_VolChange--;
402 }
403
pbEchoFadeoutUp(void)404 static void pbEchoFadeoutUp(void)
405 {
406 if (echo_VolChange < 100)
407 echo_VolChange++;
408 }
409
createEchoThread(void * ptr)410 static int32_t SDLCALL createEchoThread(void *ptr)
411 {
412 smpPtr_t sp;
413
414 if (echo_nEcho < 1)
415 {
416 ui.sysReqShown = false;
417 return true;
418 }
419
420 sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
421
422 int32_t readLen = s->length;
423 int8_t *readPtr = s->dataPtr;
424 bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
425 int32_t distance = echo_Distance * 16;
426 double dVolChange = echo_VolChange / 100.0;
427
428 // calculate real number of echoes
429 double dSmp = sample16Bit ? 32768.0 : 128.0;
430 int32_t k = 0;
431 while (k++ < echo_nEcho && dSmp >= 1.0)
432 dSmp *= dVolChange;
433 int32_t nEchoes = k + 1;
434
435 if (nEchoes < 1)
436 {
437 ui.sysReqShown = false;
438 return true;
439 }
440
441 // set write length (either original length or full echo length)
442 int32_t writeLen = readLen;
443 if (echo_AddMemory)
444 {
445 int64_t tmp64 = (int64_t)distance * (nEchoes - 1);
446
447 tmp64 += writeLen;
448 if (tmp64 > MAX_SAMPLE_LEN)
449 tmp64 = MAX_SAMPLE_LEN;
450
451 writeLen = (uint32_t)tmp64;
452 }
453
454 if (!allocateSmpDataPtr(&sp, writeLen, sample16Bit))
455 {
456 outOfMemory = true;
457 setMouseBusy(false);
458 ui.sysReqShown = false;
459 return false;
460 }
461
462 pauseAudio();
463 unfixSample(s);
464
465 int32_t writeIdx = 0;
466
467 if (sample16Bit)
468 {
469 const int16_t *readPtr16 = (const int16_t *)readPtr;
470 int16_t *writePtr16 = (int16_t *)sp.ptr;
471
472 while (writeIdx < writeLen)
473 {
474 double dSmpOut = 0.0;
475 double dSmpMul = 1.0;
476
477 int32_t echoRead = writeIdx;
478 int32_t echoCycle = nEchoes;
479
480 while (!stopThread)
481 {
482 if (echoRead < readLen)
483 dSmpOut += (int32_t)readPtr16[echoRead] * dSmpMul;
484
485 dSmpMul *= dVolChange;
486
487 echoRead -= distance;
488 if (echoRead <= 0 || --echoCycle <= 0)
489 break;
490 }
491
492 DROUND(dSmpOut);
493
494 int32_t smp32 = (int32_t)dSmpOut;
495 CLAMP16(smp32);
496 writePtr16[writeIdx++] = (int16_t)smp32;
497 }
498 }
499 else // 8-bit
500 {
501 int8_t *writePtr8 = sp.ptr;
502 while (writeIdx < writeLen)
503 {
504 double dSmpOut = 0.0;
505 double dSmpMul = 1.0;
506
507 int32_t echoRead = writeIdx;
508 int32_t echoCycle = nEchoes;
509
510 while (!stopThread)
511 {
512 if (echoRead < readLen)
513 dSmpOut += (int32_t)readPtr[echoRead] * dSmpMul;
514
515 dSmpMul *= dVolChange;
516
517 echoRead -= distance;
518 if (echoRead <= 0 || --echoCycle <= 0)
519 break;
520 }
521
522 DROUND(dSmpOut);
523
524 int32_t smp32 = (int32_t)dSmpOut;
525 CLAMP8(smp32);
526 writePtr8[writeIdx++] = (int8_t)smp32;
527 }
528 }
529
530 freeSmpData(s);
531 setSmpDataPtr(s, &sp);
532
533 if (stopThread) // we stopped before echo was done, realloc length
534 {
535 writeLen = writeIdx;
536 reallocateSmpData(s, writeLen, sample16Bit);
537 editor.updateCurSmp = true;
538 }
539
540 s->length = writeLen;
541
542 fixSample(s);
543 resumeAudio();
544
545 setSongModifiedFlag();
546 setMouseBusy(false);
547
548 ui.sysReqShown = false;
549 return true;
550
551 (void)ptr;
552 }
553
pbCreateEcho(void)554 static void pbCreateEcho(void)
555 {
556 stopThread = false;
557
558 mouseAnimOn();
559 thread = SDL_CreateThread(createEchoThread, NULL, NULL);
560 if (thread == NULL)
561 {
562 okBox(0, "System message", "Couldn't create thread!");
563 return;
564 }
565
566 SDL_DetachThread(thread);
567 }
568
drawEchoBox(void)569 static void drawEchoBox(void)
570 {
571 const int16_t x = 171;
572 const int16_t y = 220;
573 const int16_t w = 291;
574 const int16_t h = 66;
575
576 // main fill
577 fillRect(x + 1, y + 1, w - 2, h - 2, PAL_BUTTONS);
578
579 // outer border
580 vLine(x, y, h - 1, PAL_BUTTON1);
581 hLine(x + 1, y, w - 2, PAL_BUTTON1);
582 vLine(x + w - 1, y, h, PAL_BUTTON2);
583 hLine(x, y + h - 1, w - 1, PAL_BUTTON2);
584
585 // inner border
586 vLine(x + 2, y + 2, h - 5, PAL_BUTTON2);
587 hLine(x + 3, y + 2, w - 6, PAL_BUTTON2);
588 vLine(x + w - 3, y + 2, h - 4, PAL_BUTTON1);
589 hLine(x + 2, y + h - 3, w - 4, PAL_BUTTON1);
590
591 textOutShadow(177, 226, PAL_FORGRND, PAL_BUTTON2, "Number of echoes");
592 textOutShadow(177, 240, PAL_FORGRND, PAL_BUTTON2, "Echo distance");
593 textOutShadow(177, 254, PAL_FORGRND, PAL_BUTTON2, "Fade out");
594 textOutShadow(192, 270, PAL_FORGRND, PAL_BUTTON2, "Add memory to sample");
595
596 assert(echo_nEcho <= 64);
597 charOut(315 + (2 * 7), 226, PAL_FORGRND, '0' + (char)(echo_nEcho / 10));
598 charOut(315 + (3 * 7), 226, PAL_FORGRND, '0' + (echo_nEcho % 10));
599
600 assert(echo_Distance <= 0x4000);
601 hexOut(308, 240, PAL_FORGRND, echo_Distance << 4, 5);
602
603 assert(echo_VolChange <= 100);
604 textOutFixed(312, 254, PAL_FORGRND, PAL_BUTTONS, dec3StrTab[echo_VolChange]);
605
606 charOutShadow(313 + (3 * 7), 254, PAL_FORGRND, PAL_BUTTON2, '%');
607 }
608
setupEchoBoxWidgets(void)609 static void setupEchoBoxWidgets(void)
610 {
611 checkBox_t *c;
612 pushButton_t *p;
613 scrollBar_t *s;
614
615 // "Add memory to sample" checkbox
616 c = &checkBoxes[0];
617 memset(c, 0, sizeof (checkBox_t));
618 c->x = 176;
619 c->y = 268;
620 c->clickAreaWidth = 146;
621 c->clickAreaHeight = 12;
622 c->callbackFunc = cbEchoAddMemory;
623 c->checked = echo_AddMemory ? CHECKBOX_CHECKED : CHECKBOX_UNCHECKED;
624 c->visible = true;
625
626 // "Apply" pushbutton
627 p = &pushButtons[0];
628 memset(p, 0, sizeof (pushButton_t));
629 p->caption = "Apply";
630 p->x = 345;
631 p->y = 266;
632 p->w = 56;
633 p->h = 16;
634 p->callbackFuncOnUp = pbCreateEcho;
635 p->visible = true;
636
637 // "Exit" pushbutton
638 p = &pushButtons[1];
639 memset(p, 0, sizeof (pushButton_t));
640 p->caption = "Exit";
641 p->x = 402;
642 p->y = 266;
643 p->w = 55;
644 p->h = 16;
645 p->callbackFuncOnUp = pbExit;
646 p->visible = true;
647
648 // scrollbar buttons
649
650 p = &pushButtons[2];
651 memset(p, 0, sizeof (pushButton_t));
652 p->caption = ARROW_LEFT_STRING;
653 p->x = 345;
654 p->y = 224;
655 p->w = 23;
656 p->h = 13;
657 p->preDelay = 1;
658 p->delayFrames = 3;
659 p->callbackFuncOnDown = pbEchoNumDown;
660 p->visible = true;
661
662 p = &pushButtons[3];
663 memset(p, 0, sizeof (pushButton_t));
664 p->caption = ARROW_RIGHT_STRING;
665 p->x = 434;
666 p->y = 224;
667 p->w = 23;
668 p->h = 13;
669 p->preDelay = 1;
670 p->delayFrames = 3;
671 p->callbackFuncOnDown = pbEchoNumUp;
672 p->visible = true;
673
674 p = &pushButtons[4];
675 memset(p, 0, sizeof (pushButton_t));
676 p->caption = ARROW_LEFT_STRING;
677 p->x = 345;
678 p->y = 238;
679 p->w = 23;
680 p->h = 13;
681 p->preDelay = 1;
682 p->delayFrames = 3;
683 p->callbackFuncOnDown = pbEchoDistDown;
684 p->visible = true;
685
686 p = &pushButtons[5];
687 memset(p, 0, sizeof (pushButton_t));
688 p->caption = ARROW_RIGHT_STRING;
689 p->x = 434;
690 p->y = 238;
691 p->w = 23;
692 p->h = 13;
693 p->preDelay = 1;
694 p->delayFrames = 3;
695 p->callbackFuncOnDown = pbEchoDistUp;
696 p->visible = true;
697
698 p = &pushButtons[6];
699 memset(p, 0, sizeof (pushButton_t));
700 p->caption = ARROW_LEFT_STRING;
701 p->x = 345;
702 p->y = 252;
703 p->w = 23;
704 p->h = 13;
705 p->preDelay = 1;
706 p->delayFrames = 3;
707 p->callbackFuncOnDown = pbEchoFadeoutDown;
708 p->visible = true;
709
710 p = &pushButtons[7];
711 memset(p, 0, sizeof (pushButton_t));
712 p->caption = ARROW_RIGHT_STRING;
713 p->x = 434;
714 p->y = 252;
715 p->w = 23;
716 p->h = 13;
717 p->preDelay = 1;
718 p->delayFrames = 3;
719 p->callbackFuncOnDown = pbEchoFadeoutUp;
720 p->visible = true;
721
722 // echo num scrollbar
723 s = &scrollBars[0];
724 memset(s, 0, sizeof (scrollBar_t));
725 s->x = 368;
726 s->y = 224;
727 s->w = 66;
728 s->h = 13;
729 s->callbackFunc = sbSetEchoNumPos;
730 s->visible = true;
731 setScrollBarPageLength(0, 1);
732 setScrollBarEnd(0, 64);
733
734 // echo distance scrollbar
735 s = &scrollBars[1];
736 memset(s, 0, sizeof (scrollBar_t));
737 s->x = 368;
738 s->y = 238;
739 s->w = 66;
740 s->h = 13;
741 s->callbackFunc = sbSetEchoDistPos;
742 s->visible = true;
743 setScrollBarPageLength(1, 1);
744 setScrollBarEnd(1, 16384);
745
746 // echo fadeout scrollbar
747 s = &scrollBars[2];
748 memset(s, 0, sizeof (scrollBar_t));
749 s->x = 368;
750 s->y = 252;
751 s->w = 66;
752 s->h = 13;
753 s->callbackFunc = sbSetEchoFadeoutPos;
754 s->visible = true;
755 setScrollBarPageLength(2, 1);
756 setScrollBarEnd(2, 100);
757 }
758
handleEchoToolPanic(void)759 void handleEchoToolPanic(void)
760 {
761 stopThread = true;
762 }
763
pbSampleEcho(void)764 void pbSampleEcho(void)
765 {
766 if (editor.curInstr == 0 ||
767 instr[editor.curInstr] == NULL ||
768 instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
769 {
770 return;
771 }
772
773 setupEchoBoxWidgets();
774 windowOpen();
775
776 outOfMemory = false;
777
778 exitFlag = false;
779 while (ui.sysReqShown)
780 {
781 readInput();
782 if (ui.sysReqEnterPressed)
783 pbCreateEcho();
784
785 setSyncedReplayerVars();
786 handleRedrawing();
787
788 drawEchoBox();
789 setScrollBarPos(0, echo_nEcho, false);
790 setScrollBarPos(1, echo_Distance, false);
791 setScrollBarPos(2, echo_VolChange, false);
792 drawCheckBox(0);
793 for (uint16_t i = 0; i < 8; i++) drawPushButton(i);
794 for (uint16_t i = 0; i < 3; i++) drawScrollBar(i);
795
796 flipFrame();
797 }
798
799 hideCheckBox(0);
800 for (uint16_t i = 0; i < 8; i++) hidePushButton(i);
801 for (uint16_t i = 0; i < 3; i++) hideScrollBar(i);
802
803 windowClose(echo_AddMemory ? false : true);
804
805 if (outOfMemory)
806 okBox(0, "System message", "Not enough memory!");
807 }
808
mixThread(void * ptr)809 static int32_t SDLCALL mixThread(void *ptr)
810 {
811 smpPtr_t sp;
812
813 int8_t *dstPtr, *mixPtr;
814 uint8_t mixFlags, dstFlags;
815 int32_t dstLen, mixLen;
816
817 int16_t dstIns = editor.curInstr;
818 int16_t dstSmp = editor.curSmp;
819 int16_t mixIns = editor.srcInstr;
820 int16_t mixSmp = editor.srcSmp;
821
822 sample_t *s = &instr[dstIns]->smp[dstSmp];
823 sample_t *sSrc = &instr[mixIns]->smp[mixSmp];
824
825 if (dstIns == mixIns && dstSmp == mixSmp)
826 {
827 setMouseBusy(false);
828 ui.sysReqShown = false;
829 return true;
830 }
831
832 if (instr[mixIns] == NULL)
833 {
834 mixLen = 0;
835 mixPtr = NULL;
836 mixFlags = 0;
837 }
838 else
839 {
840 mixLen = sSrc->length;
841 mixPtr = sSrc->dataPtr;
842 mixFlags = sSrc->flags;
843
844 if (mixPtr == NULL)
845 {
846 mixLen = 0;
847 mixFlags = 0;
848 }
849 }
850
851 if (instr[dstIns] == NULL)
852 {
853 dstLen = 0;
854 dstPtr = NULL;
855 dstFlags = 0;
856 }
857 else
858 {
859 dstLen = s->length;
860 dstPtr = s->dataPtr;
861 dstFlags = s->flags;
862
863 if (dstPtr == NULL)
864 {
865 dstLen = 0;
866 dstFlags = 0;
867 }
868 }
869
870 bool src16Bits = !!(mixFlags & SAMPLE_16BIT);
871 bool dst16Bits = !!(dstFlags & SAMPLE_16BIT);
872
873 int32_t maxLen = (dstLen > mixLen) ? dstLen : mixLen;
874 if (maxLen == 0)
875 {
876 setMouseBusy(false);
877 ui.sysReqShown = false;
878 return true;
879 }
880
881 if (!allocateSmpDataPtr(&sp, maxLen, dst16Bits))
882 {
883 outOfMemory = true;
884 setMouseBusy(false);
885 ui.sysReqShown = false;
886 return true;
887 }
888 memset(sp.ptr, 0, maxLen);
889
890 if (instr[dstIns] == NULL && !allocateInstr(dstIns))
891 {
892 outOfMemory = true;
893 setMouseBusy(false);
894 ui.sysReqShown = false;
895 return true;
896 }
897
898 pauseAudio();
899 unfixSample(s);
900
901 // unfix source sample
902 if (instr[mixIns] != NULL)
903 unfixSample(sSrc);
904
905 const double dAmp1 = mix_Balance / 100.0;
906 const double dAmp2 = 1.0 - dAmp1;
907 const double dSmp1ScaleMul = src16Bits ? (1.0 / 32768.0) : (1.0 / 128.0);
908 const double dSmp2ScaleMul = dst16Bits ? (1.0 / 32768.0) : (1.0 / 128.0);
909 const double dNormalizeMul = dst16Bits ? 32768.0 : 128.0;
910
911 for (int32_t i = 0; i < maxLen; i++)
912 {
913 double dSmp1 = (i >= mixLen) ? 0.0 : (getSampleValue(mixPtr, i, src16Bits) * dSmp1ScaleMul); // -1.0 .. 0.999inf
914 double dSmp2 = (i >= dstLen) ? 0.0 : (getSampleValue(dstPtr, i, dst16Bits) * dSmp2ScaleMul); // -1.0 .. 0.999inf
915
916 const double dSmp = ((dSmp1 * dAmp1) + (dSmp2 * dAmp2)) * dNormalizeMul;
917 putSampleValue(sp.ptr, i, dSmp, dst16Bits);
918 }
919
920 freeSmpData(s);
921 setSmpDataPtr(s, &sp);
922
923 s->length = maxLen;
924 s->flags = dstFlags;
925
926 fixSample(s);
927
928 // re-fix source sample again
929 if (instr[mixIns] != NULL)
930 fixSample(sSrc);
931
932 resumeAudio();
933
934 setSongModifiedFlag();
935 setMouseBusy(false);
936
937 ui.sysReqShown = false;
938 return true;
939
940 (void)ptr;
941 }
942
pbMix(void)943 static void pbMix(void)
944 {
945 mouseAnimOn();
946 thread = SDL_CreateThread(mixThread, NULL, NULL);
947 if (thread == NULL)
948 {
949 okBox(0, "System message", "Couldn't create thread!");
950 return;
951 }
952
953 SDL_DetachThread(thread);
954 }
955
sbSetMixBalancePos(uint32_t pos)956 static void sbSetMixBalancePos(uint32_t pos)
957 {
958 if (mix_Balance != (int8_t)pos)
959 mix_Balance = (int8_t)pos;
960 }
961
pbMixBalanceDown(void)962 static void pbMixBalanceDown(void)
963 {
964 if (mix_Balance > 0)
965 mix_Balance--;
966 }
967
pbMixBalanceUp(void)968 static void pbMixBalanceUp(void)
969 {
970 if (mix_Balance < 100)
971 mix_Balance++;
972 }
973
drawMixSampleBox(void)974 static void drawMixSampleBox(void)
975 {
976 const int16_t x = 192;
977 const int16_t y = 240;
978 const int16_t w = 248;
979 const int16_t h = 38;
980
981 // main fill
982 fillRect(x + 1, y + 1, w - 2, h - 2, PAL_BUTTONS);
983
984 // outer border
985 vLine(x, y, h - 1, PAL_BUTTON1);
986 hLine(x + 1, y, w - 2, PAL_BUTTON1);
987 vLine(x + w - 1, y, h, PAL_BUTTON2);
988 hLine(x, y + h - 1, w - 1, PAL_BUTTON2);
989
990 // inner border
991 vLine(x + 2, y + 2, h - 5, PAL_BUTTON2);
992 hLine(x + 3, y + 2, w - 6, PAL_BUTTON2);
993 vLine(x + w - 3, y + 2, h - 4, PAL_BUTTON1);
994 hLine(x + 2, y + h - 3, w - 4, PAL_BUTTON1);
995
996 textOutShadow(198, 246, PAL_FORGRND, PAL_BUTTON2, "Mixing balance");
997
998 assert((mix_Balance >= 0) && (mix_Balance <= 100));
999 textOutFixed(299, 246, PAL_FORGRND, PAL_BUTTONS, dec3StrTab[mix_Balance]);
1000 }
1001
setupMixBoxWidgets(void)1002 static void setupMixBoxWidgets(void)
1003 {
1004 pushButton_t *p;
1005 scrollBar_t *s;
1006
1007 // "Apply" pushbutton
1008 p = &pushButtons[0];
1009 memset(p, 0, sizeof (pushButton_t));
1010 p->caption = "Apply";
1011 p->x = 197;
1012 p->y = 258;
1013 p->w = 73;
1014 p->h = 16;
1015 p->callbackFuncOnUp = pbMix;
1016 p->visible = true;
1017
1018 // "Exit" pushbutton
1019 p = &pushButtons[1];
1020 memset(p, 0, sizeof (pushButton_t));
1021 p->caption = "Exit";
1022 p->x = 361;
1023 p->y = 258;
1024 p->w = 73;
1025 p->h = 16;
1026 p->callbackFuncOnUp = pbExit;
1027 p->visible = true;
1028
1029 // scrollbar buttons
1030
1031 p = &pushButtons[2];
1032 memset(p, 0, sizeof (pushButton_t));
1033 p->caption = ARROW_LEFT_STRING;
1034 p->x = 322;
1035 p->y = 244;
1036 p->w = 23;
1037 p->h = 13;
1038 p->preDelay = 1;
1039 p->delayFrames = 3;
1040 p->callbackFuncOnDown = pbMixBalanceDown;
1041 p->visible = true;
1042
1043 p = &pushButtons[3];
1044 memset(p, 0, sizeof (pushButton_t));
1045 p->caption = ARROW_RIGHT_STRING;
1046 p->x = 411;
1047 p->y = 244;
1048 p->w = 23;
1049 p->h = 13;
1050 p->preDelay = 1;
1051 p->delayFrames = 3;
1052 p->callbackFuncOnDown = pbMixBalanceUp;
1053 p->visible = true;
1054
1055 // mixing balance scrollbar
1056 s = &scrollBars[0];
1057 memset(s, 0, sizeof (scrollBar_t));
1058 s->x = 345;
1059 s->y = 244;
1060 s->w = 66;
1061 s->h = 13;
1062 s->callbackFunc = sbSetMixBalancePos;
1063 s->visible = true;
1064 setScrollBarPageLength(0, 1);
1065 setScrollBarEnd(0, 100);
1066 }
1067
pbSampleMix(void)1068 void pbSampleMix(void)
1069 {
1070 uint16_t i;
1071
1072 if (editor.curInstr == 0)
1073 return;
1074
1075 setupMixBoxWidgets();
1076 windowOpen();
1077
1078 outOfMemory = false;
1079
1080 exitFlag = false;
1081 while (ui.sysReqShown)
1082 {
1083 readInput();
1084 if (ui.sysReqEnterPressed)
1085 pbMix();
1086
1087 setSyncedReplayerVars();
1088 handleRedrawing();
1089
1090 drawMixSampleBox();
1091 setScrollBarPos(0, mix_Balance, false);
1092 for (i = 0; i < 4; i++) drawPushButton(i);
1093 drawScrollBar(0);
1094
1095 flipFrame();
1096 }
1097
1098 for (i = 0; i < 4; i++) hidePushButton(i);
1099 hideScrollBar(0);
1100
1101 windowClose(false);
1102
1103 if (outOfMemory)
1104 okBox(0, "System message", "Not enough memory!");
1105 }
1106
sbSetStartVolPos(uint32_t pos)1107 static void sbSetStartVolPos(uint32_t pos)
1108 {
1109 int32_t val = (int32_t)(pos - 200);
1110 if (val != (int32_t)dVol_StartVol)
1111 {
1112 if (ABS(val) < 10) val = 0;
1113 else if (ABS(val - 100) < 10) val = 100;
1114 else if (ABS(val + 100) < 10) val = -100;
1115
1116 dVol_StartVol = (double)val;
1117 }
1118 }
1119
sbSetEndVolPos(uint32_t pos)1120 static void sbSetEndVolPos(uint32_t pos)
1121 {
1122 int32_t val = (int32_t)(pos - 200);
1123 if (val != (int32_t)dVol_EndVol)
1124 {
1125 if (ABS(val) < 10) val = 0;
1126 else if (ABS(val - 100) < 10) val = 100;
1127 else if (ABS(val + 100) < 10) val = -100;
1128
1129 dVol_EndVol = val;
1130 }
1131 }
1132
pbSampStartVolDown(void)1133 static void pbSampStartVolDown(void)
1134 {
1135 if (dVol_StartVol > -200.0)
1136 dVol_StartVol -= 1.0;
1137
1138 dVol_StartVol = floor(dVol_StartVol);
1139 }
1140
pbSampStartVolUp(void)1141 static void pbSampStartVolUp(void)
1142 {
1143 if (dVol_StartVol < 200.0)
1144 dVol_StartVol += 1.0;
1145
1146 dVol_StartVol = floor(dVol_StartVol);
1147 }
1148
pbSampEndVolDown(void)1149 static void pbSampEndVolDown(void)
1150 {
1151 if (dVol_EndVol > -200.0)
1152 dVol_EndVol -= 1.0;
1153
1154 dVol_EndVol = floor(dVol_EndVol);
1155 }
1156
pbSampEndVolUp(void)1157 static void pbSampEndVolUp(void)
1158 {
1159 if (dVol_EndVol < 200.0)
1160 dVol_EndVol += 1.0;
1161
1162 dVol_EndVol = floor(dVol_EndVol);
1163 }
1164
applyVolumeThread(void * ptr)1165 static int32_t SDLCALL applyVolumeThread(void *ptr)
1166 {
1167 int32_t x1, x2;
1168
1169 if (instr[editor.curInstr] == NULL)
1170 goto applyVolumeExit;
1171
1172 sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
1173
1174 if (smpEd_Rx1 < smpEd_Rx2)
1175 {
1176 x1 = smpEd_Rx1;
1177 x2 = smpEd_Rx2;
1178
1179 if (x2 > s->length)
1180 x2 = s->length;
1181
1182 if (x1 < 0)
1183 x1 = 0;
1184
1185 if (x2 <= x1)
1186 goto applyVolumeExit;
1187 }
1188 else
1189 {
1190 // no mark, operate on whole sample
1191 x1 = 0;
1192 x2 = s->length;
1193 }
1194
1195 const int32_t len = x2 - x1;
1196 if (len <= 0)
1197 goto applyVolumeExit;
1198
1199 bool mustInterpolate = (dVol_StartVol != dVol_EndVol);
1200 const double dVol = dVol_StartVol / 100.0;
1201 const double dPosMul = ((dVol_EndVol / 100.0) - dVol) / len;
1202
1203 pauseAudio();
1204 unfixSample(s);
1205 if (s->flags & SAMPLE_16BIT)
1206 {
1207 int16_t *ptr16 = (int16_t *)s->dataPtr + x1;
1208 if (mustInterpolate)
1209 {
1210 for (int32_t i = 0; i < len; i++)
1211 {
1212 double dSmp = (int32_t)ptr16[i] * (dVol + (i * dPosMul)); // linear interpolation
1213 DROUND(dSmp);
1214
1215 int32_t smp32 = (int32_t)dSmp;
1216 CLAMP16(smp32);
1217 ptr16[i] = (int16_t)smp32;
1218 }
1219
1220 }
1221 else // no interpolation needed
1222 {
1223 for (int32_t i = 0; i < len; i++)
1224 {
1225 double dSmp = (int32_t)ptr16[i] * dVol;
1226 DROUND(dSmp);
1227
1228 int32_t smp32 = (int32_t)dSmp;
1229 CLAMP16(smp32);
1230 ptr16[i] = (int16_t)smp32;
1231 }
1232 }
1233 }
1234 else // 8-bit sample
1235 {
1236 int8_t *ptr8 = s->dataPtr + x1;
1237 if (mustInterpolate)
1238 {
1239 for (int32_t i = 0; i < len; i++)
1240 {
1241 double dSmp = (int32_t)ptr8[i] * (dVol + (i * dPosMul)); // linear interpolation
1242 DROUND(dSmp);
1243
1244 int32_t smp32 = (int32_t)dSmp;
1245 CLAMP8(smp32);
1246 ptr8[i] = (int8_t)smp32;
1247 }
1248 }
1249 else // no interpolation needed
1250 {
1251 for (int32_t i = 0; i < len; i++)
1252 {
1253 double dSmp = (int32_t)ptr8[i] * dVol;
1254 DROUND(dSmp);
1255
1256 int32_t smp32 = (int32_t)dSmp;
1257 CLAMP8(smp32);
1258 ptr8[i] = (int8_t)smp32;
1259 }
1260 }
1261 }
1262 fixSample(s);
1263 resumeAudio();
1264
1265 setSongModifiedFlag();
1266
1267 applyVolumeExit:
1268 setMouseBusy(false);
1269 ui.sysReqShown = false;
1270
1271 return true;
1272
1273 (void)ptr;
1274 }
1275
pbApplyVolume(void)1276 static void pbApplyVolume(void)
1277 {
1278 if (dVol_StartVol == 100.0 && dVol_EndVol == 100.0)
1279 {
1280 ui.sysReqShown = false;
1281 return; // no volume change to be done
1282 }
1283
1284 mouseAnimOn();
1285 thread = SDL_CreateThread(applyVolumeThread, NULL, NULL);
1286 if (thread == NULL)
1287 {
1288 okBox(0, "System message", "Couldn't create thread!");
1289 return;
1290 }
1291
1292 SDL_DetachThread(thread);
1293 }
1294
getMaxScaleThread(void * ptr)1295 static int32_t SDLCALL getMaxScaleThread(void *ptr)
1296 {
1297 int32_t x1, x2;
1298
1299 if (instr[editor.curInstr] == NULL)
1300 goto getScaleExit;
1301
1302 sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
1303
1304 if (smpEd_Rx1 < smpEd_Rx2)
1305 {
1306 x1 = smpEd_Rx1;
1307 x2 = smpEd_Rx2;
1308
1309 if (x2 > s->length)
1310 x2 = s->length;
1311
1312 if (x1 < 0)
1313 x1 = 0;
1314
1315 if (x2 <= x1)
1316 goto getScaleExit;
1317 }
1318 else
1319 {
1320 // no sample marking, operate on the whole sample
1321 x1 = 0;
1322 x2 = s->length;
1323 }
1324
1325 uint32_t len = x2 - x1;
1326 if (len <= 0)
1327 {
1328 dVol_StartVol = dVol_EndVol = 100.0;
1329 goto getScaleExit;
1330 }
1331
1332 double dVolChange = 100.0;
1333
1334 /* If sample is looped and the loopEnd point is inside the marked range,
1335 ** we need to unfix the fixed interpolation sample before scanning,
1336 ** and fix it again after we're done.
1337 */
1338 bool hasLoop = GET_LOOPTYPE(s->flags) != LOOP_OFF;
1339 const int32_t loopEnd = s->loopStart + s->loopLength;
1340 bool fixedSampleInRange = hasLoop && (x1 <= loopEnd) && (x2 >= loopEnd);
1341
1342 if (fixedSampleInRange)
1343 unfixSample(s);
1344
1345 int32_t maxAmp = 0;
1346 if (s->flags & SAMPLE_16BIT)
1347 {
1348 const int16_t *ptr16 = (const int16_t *)s->dataPtr + x1;
1349 for (uint32_t i = 0; i < len; i++)
1350 {
1351 const int32_t absSmp = ABS(ptr16[i]);
1352 if (absSmp > maxAmp)
1353 maxAmp = absSmp;
1354 }
1355
1356 if (maxAmp > 0)
1357 dVolChange = (32767.0 / maxAmp) * 100.0;
1358 }
1359 else // 8-bit
1360 {
1361 const int8_t *ptr8 = (const int8_t *)&s->dataPtr[x1];
1362 for (uint32_t i = 0; i < len; i++)
1363 {
1364 const int32_t absSmp = ABS(ptr8[i]);
1365 if (absSmp > maxAmp)
1366 maxAmp = absSmp;
1367 }
1368
1369 if (maxAmp > 0)
1370 dVolChange = (127.0 / maxAmp) * 100.0;
1371 }
1372
1373 if (fixedSampleInRange)
1374 fixSample(s);
1375
1376 if (dVolChange < 100.0) // yes, this can happen...
1377 dVolChange = 100.0;
1378
1379 dVol_StartVol = dVol_EndVol = dVolChange;
1380
1381 getScaleExit:
1382 setMouseBusy(false);
1383 return true;
1384
1385 (void)ptr;
1386 }
1387
pbGetMaxScale(void)1388 static void pbGetMaxScale(void)
1389 {
1390 mouseAnimOn();
1391 thread = SDL_CreateThread(getMaxScaleThread, NULL, NULL);
1392 if (thread == NULL)
1393 {
1394 okBox(0, "System message", "Couldn't create thread!");
1395 return;
1396 }
1397
1398 SDL_DetachThread(thread);
1399 }
1400
drawSampleVolumeBox(void)1401 static void drawSampleVolumeBox(void)
1402 {
1403 char sign;
1404 const int16_t x = 166;
1405 const int16_t y = 230;
1406 const int16_t w = 301;
1407 const int16_t h = 52;
1408 uint32_t val;
1409
1410 // main fill
1411 fillRect(x + 1, y + 1, w - 2, h - 2, PAL_BUTTONS);
1412
1413 // outer border
1414 vLine(x, y, h - 1, PAL_BUTTON1);
1415 hLine(x + 1, y, w - 2, PAL_BUTTON1);
1416 vLine(x + w - 1, y, h, PAL_BUTTON2);
1417 hLine(x, y + h - 1, w - 1, PAL_BUTTON2);
1418
1419 // inner border
1420 vLine(x + 2, y + 2, h - 5, PAL_BUTTON2);
1421 hLine(x + 3, y + 2, w - 6, PAL_BUTTON2);
1422 vLine(x + w - 3, y + 2, h - 4, PAL_BUTTON1);
1423 hLine(x + 2, y + h - 3, w - 4, PAL_BUTTON1);
1424
1425 textOutShadow(172, 236, PAL_FORGRND, PAL_BUTTON2, "Start volume");
1426 textOutShadow(172, 250, PAL_FORGRND, PAL_BUTTON2, "End volume");
1427 charOutShadow(282, 236, PAL_FORGRND, PAL_BUTTON2, '%');
1428 charOutShadow(282, 250, PAL_FORGRND, PAL_BUTTON2, '%');
1429
1430 const int32_t startVol = (int32_t)dVol_StartVol;
1431 const int32_t endVol = (int32_t)dVol_EndVol;
1432
1433 if (startVol > 200)
1434 {
1435 charOut(253, 236, PAL_FORGRND, '>');
1436 charOut(260, 236, PAL_FORGRND, '2');
1437 charOut(267, 236, PAL_FORGRND, '0');
1438 charOut(274, 236, PAL_FORGRND, '0');
1439 }
1440 else
1441 {
1442 if (startVol == 0) sign = ' ';
1443 else if (startVol < 0) sign = '-';
1444 else sign = '+';
1445
1446 val = ABS(startVol);
1447 if (val > 99)
1448 {
1449 charOut(253, 236, PAL_FORGRND, sign);
1450 charOut(260, 236, PAL_FORGRND, '0' + (char)(val / 100));
1451 charOut(267, 236, PAL_FORGRND, '0' + ((val / 10) % 10));
1452 charOut(274, 236, PAL_FORGRND, '0' + (val % 10));
1453 }
1454 else if (val > 9)
1455 {
1456 charOut(260, 236, PAL_FORGRND, sign);
1457 charOut(267, 236, PAL_FORGRND, '0' + (char)(val / 10));
1458 charOut(274, 236, PAL_FORGRND, '0' + (val % 10));
1459 }
1460 else
1461 {
1462 charOut(267, 236, PAL_FORGRND, sign);
1463 charOut(274, 236, PAL_FORGRND, '0' + (char)val);
1464 }
1465 }
1466
1467 if (endVol > 200)
1468 {
1469 charOut(253, 250, PAL_FORGRND, '>');
1470 charOut(260, 250, PAL_FORGRND, '2');
1471 charOut(267, 250, PAL_FORGRND, '0');
1472 charOut(274, 250, PAL_FORGRND, '0');
1473 }
1474 else
1475 {
1476 if (endVol == 0) sign = ' ';
1477 else if (endVol < 0) sign = '-';
1478 else sign = '+';
1479
1480 val = ABS(endVol);
1481 if (val > 99)
1482 {
1483 charOut(253, 250, PAL_FORGRND, sign);
1484 charOut(260, 250, PAL_FORGRND, '0' + (char)(val / 100));
1485 charOut(267, 250, PAL_FORGRND, '0' + ((val / 10) % 10));
1486 charOut(274, 250, PAL_FORGRND, '0' + (val % 10));
1487 }
1488 else if (val > 9)
1489 {
1490 charOut(260, 250, PAL_FORGRND, sign);
1491 charOut(267, 250, PAL_FORGRND, '0' + (char)(val / 10));
1492 charOut(274, 250, PAL_FORGRND, '0' + (val % 10));
1493 }
1494 else
1495 {
1496 charOut(267, 250, PAL_FORGRND, sign);
1497 charOut(274, 250, PAL_FORGRND, '0' + (char)val);
1498 }
1499 }
1500 }
1501
setupVolumeBoxWidgets(void)1502 static void setupVolumeBoxWidgets(void)
1503 {
1504 pushButton_t *p;
1505 scrollBar_t *s;
1506
1507 // "Apply" pushbutton
1508 p = &pushButtons[0];
1509 memset(p, 0, sizeof (pushButton_t));
1510 p->caption = "Apply";
1511 p->x = 171;
1512 p->y = 262;
1513 p->w = 73;
1514 p->h = 16;
1515 p->callbackFuncOnUp = pbApplyVolume;
1516 p->visible = true;
1517
1518 // "Get maximum scale" pushbutton
1519 p = &pushButtons[1];
1520 memset(p, 0, sizeof (pushButton_t));
1521 p->caption = "Get maximum scale";
1522 p->x = 245;
1523 p->y = 262;
1524 p->w = 143;
1525 p->h = 16;
1526 p->callbackFuncOnUp = pbGetMaxScale;
1527 p->visible = true;
1528
1529 // "Exit" pushbutton
1530 p = &pushButtons[2];
1531 memset(p, 0, sizeof (pushButton_t));
1532 p->caption = "Exit";
1533 p->x = 389;
1534 p->y = 262;
1535 p->w = 73;
1536 p->h = 16;
1537 p->callbackFuncOnUp = pbExit;
1538 p->visible = true;
1539
1540 // scrollbar buttons
1541
1542 p = &pushButtons[3];
1543 memset(p, 0, sizeof (pushButton_t));
1544 p->caption = ARROW_LEFT_STRING;
1545 p->x = 292;
1546 p->y = 234;
1547 p->w = 23;
1548 p->h = 13;
1549 p->preDelay = 1;
1550 p->delayFrames = 3;
1551 p->callbackFuncOnDown = pbSampStartVolDown;
1552 p->visible = true;
1553
1554 p = &pushButtons[4];
1555 memset(p, 0, sizeof (pushButton_t));
1556 p->caption = ARROW_RIGHT_STRING;
1557 p->x = 439;
1558 p->y = 234;
1559 p->w = 23;
1560 p->h = 13;
1561 p->preDelay = 1;
1562 p->delayFrames = 3;
1563 p->callbackFuncOnDown = pbSampStartVolUp;
1564 p->visible = true;
1565
1566 p = &pushButtons[5];
1567 memset(p, 0, sizeof (pushButton_t));
1568 p->caption = ARROW_LEFT_STRING;
1569 p->x = 292;
1570 p->y = 248;
1571 p->w = 23;
1572 p->h = 13;
1573 p->preDelay = 1;
1574 p->delayFrames = 3;
1575 p->callbackFuncOnDown = pbSampEndVolDown;
1576 p->visible = true;
1577
1578 p = &pushButtons[6];
1579 memset(p, 0, sizeof (pushButton_t));
1580 p->caption = ARROW_RIGHT_STRING;
1581 p->x = 439;
1582 p->y = 248;
1583 p->w = 23;
1584 p->h = 13;
1585 p->preDelay = 1;
1586 p->delayFrames = 3;
1587 p->callbackFuncOnDown = pbSampEndVolUp;
1588 p->visible = true;
1589
1590 // volume start scrollbar
1591 s = &scrollBars[0];
1592 memset(s, 0, sizeof (scrollBar_t));
1593 s->x = 315;
1594 s->y = 234;
1595 s->w = 124;
1596 s->h = 13;
1597 s->callbackFunc = sbSetStartVolPos;
1598 s->visible = true;
1599 setScrollBarPageLength(0, 1);
1600 setScrollBarEnd(0, 200 * 2);
1601 setScrollBarPos(0, 200, false);
1602
1603 // volume end scrollbar
1604 s = &scrollBars[1];
1605 memset(s, 0, sizeof (scrollBar_t));
1606 s->x = 315;
1607 s->y = 248;
1608 s->w = 124;
1609 s->h = 13;
1610 s->callbackFunc = sbSetEndVolPos;
1611 s->visible = true;
1612 setScrollBarPageLength(1, 1);
1613 setScrollBarEnd(1, 200 * 2);
1614 setScrollBarPos(1, 200, false);
1615 }
1616
pbSampleVolume(void)1617 void pbSampleVolume(void)
1618 {
1619 uint16_t i;
1620
1621 if (editor.curInstr == 0 ||
1622 instr[editor.curInstr] == NULL ||
1623 instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
1624 {
1625 return;
1626 }
1627
1628 setupVolumeBoxWidgets();
1629 windowOpen();
1630
1631 exitFlag = false;
1632 while (ui.sysReqShown)
1633 {
1634 readInput();
1635 if (ui.sysReqEnterPressed)
1636 {
1637 pbApplyVolume();
1638 keyb.ignoreCurrKeyUp = true; // don't handle key up event for this key release
1639 }
1640
1641 setSyncedReplayerVars();
1642 handleRedrawing();
1643
1644 // this is needed for the "Get maximum scale" button
1645 if (ui.setMouseIdle) mouseAnimOff();
1646
1647 drawSampleVolumeBox();
1648
1649 const int32_t startVol = (int32_t)dVol_StartVol;
1650 const int32_t endVol = (int32_t)dVol_EndVol;
1651
1652 setScrollBarPos(0, 200 + startVol, false);
1653 setScrollBarPos(1, 200 + endVol, false);
1654 for (i = 0; i < 7; i++) drawPushButton(i);
1655 for (i = 0; i < 2; i++) drawScrollBar(i);
1656
1657 flipFrame();
1658 }
1659
1660 for (i = 0; i < 7; i++) hidePushButton(i);
1661 for (i = 0; i < 2; i++) hideScrollBar(i);
1662
1663 windowClose(true);
1664 }
1665