ft2_sample_ed_features.c (33587B)
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 38 static void pbExit(void) 39 { 40 ui.sysReqShown = false; 41 exitFlag = true; 42 } 43 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 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 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 71 static void pbResampleTonesDown(void) 72 { 73 if (smpEd_RelReSmp > -36) 74 smpEd_RelReSmp--; 75 } 76 77 static void pbResampleTonesUp(void) 78 { 79 if (smpEd_RelReSmp < 36) 80 smpEd_RelReSmp++; 81 } 82 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 = pow(2.0, (int32_t)smpEd_RelReSmp * (1.0 / 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 /* Fast nearest-neighbor resampling. 119 ** 120 ** Could benefit from windowed-sinc interpolation, 121 ** but it seems like some people prefer it the way it is. 122 */ 123 124 if (newLen > 0) 125 { 126 if (sample16Bit) 127 { 128 const int16_t *src16 = (const int16_t *)src; 129 int16_t *dst16 = (int16_t *)dst; 130 131 for (uint32_t i = 0; i < newLen; i++) 132 { 133 dst16[i] = src16[posFrac64 >> 32]; 134 posFrac64 += delta64; 135 } 136 } 137 else // 8-bit 138 { 139 const int8_t *src8 = src; 140 int8_t *dst8 = dst; 141 142 for (uint32_t i = 0; i < newLen; i++) 143 { 144 dst8[i] = src8[posFrac64 >> 32]; 145 posFrac64 += delta64; 146 } 147 } 148 } 149 150 freeSmpData(s); 151 setSmpDataPtr(s, &sp); 152 153 s->relativeNote += smpEd_RelReSmp; 154 s->length = newLen; 155 s->loopStart = (int32_t)(s->loopStart * dRatio); 156 s->loopLength = (int32_t)(s->loopLength * dRatio); 157 158 sanitizeSample(s); 159 160 fixSample(s); 161 resumeAudio(); 162 163 setSongModifiedFlag(); 164 setMouseBusy(false); 165 166 ui.sysReqShown = false; 167 return true; 168 169 (void)ptr; 170 } 171 172 static void pbDoResampling(void) 173 { 174 mouseAnimOn(); 175 thread = SDL_CreateThread(resampleThread, NULL, NULL); 176 if (thread == NULL) 177 { 178 okBox(0, "System message", "Couldn't create thread!", NULL); 179 return; 180 } 181 182 SDL_DetachThread(thread); 183 } 184 185 static void drawResampleBox(void) 186 { 187 char sign; 188 const int16_t x = 209; 189 const int16_t y = 230; 190 const int16_t w = 214; 191 const int16_t h = 54; 192 193 // main fill 194 fillRect(x + 1, y + 1, w - 2, h - 2, PAL_BUTTONS); 195 196 // outer border 197 vLine(x, y, h - 1, PAL_BUTTON1); 198 hLine(x + 1, y, w - 2, PAL_BUTTON1); 199 vLine(x + w - 1, y, h, PAL_BUTTON2); 200 hLine(x, y + h - 1, w - 1, PAL_BUTTON2); 201 202 // inner border 203 vLine(x + 2, y + 2, h - 5, PAL_BUTTON2); 204 hLine(x + 3, y + 2, w - 6, PAL_BUTTON2); 205 vLine(x + w - 3, y + 2, h - 4, PAL_BUTTON1); 206 hLine(x + 2, y + h - 3, w - 4, PAL_BUTTON1); 207 208 sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; 209 210 double dLenMul = pow(2.0, smpEd_RelReSmp * (1.0 / 12.0)); 211 212 double dNewLen = s->length * dLenMul; 213 if (dNewLen > (double)MAX_SAMPLE_LEN) 214 dNewLen = (double)MAX_SAMPLE_LEN; 215 216 textOutShadow(215, 236, PAL_FORGRND, PAL_BUTTON2, "Rel. h.tones"); 217 textOutShadow(215, 250, PAL_FORGRND, PAL_BUTTON2, "New sample size"); 218 hexOut(361, 250, PAL_FORGRND, (int32_t)dNewLen, 8); 219 220 if (smpEd_RelReSmp == 0) sign = ' '; 221 else if (smpEd_RelReSmp < 0) sign = '-'; 222 else sign = '+'; 223 224 uint16_t val = ABS(smpEd_RelReSmp); 225 if (val > 9) 226 { 227 charOut(291, 236, PAL_FORGRND, sign); 228 charOut(298, 236, PAL_FORGRND, '0' + ((val / 10) % 10)); 229 charOut(305, 236, PAL_FORGRND, '0' + (val % 10)); 230 } 231 else 232 { 233 charOut(298, 236, PAL_FORGRND, sign); 234 charOut(305, 236, PAL_FORGRND, '0' + (val % 10)); 235 } 236 } 237 238 static void setupResampleBoxWidgets(void) 239 { 240 pushButton_t *p; 241 scrollBar_t *s; 242 243 // "Apply" pushbutton 244 p = &pushButtons[0]; 245 memset(p, 0, sizeof (pushButton_t)); 246 p->caption = "Apply"; 247 p->x = 214; 248 p->y = 264; 249 p->w = 73; 250 p->h = 16; 251 p->callbackFuncOnUp = pbDoResampling; 252 p->visible = true; 253 254 // "Exit" pushbutton 255 p = &pushButtons[1]; 256 memset(p, 0, sizeof (pushButton_t)); 257 p->caption = "Exit"; 258 p->x = 345; 259 p->y = 264; 260 p->w = 73; 261 p->h = 16; 262 p->callbackFuncOnUp = pbExit; 263 p->visible = true; 264 265 // scrollbar buttons 266 267 p = &pushButtons[2]; 268 memset(p, 0, sizeof (pushButton_t)); 269 p->caption = ARROW_LEFT_STRING; 270 p->x = 314; 271 p->y = 234; 272 p->w = 23; 273 p->h = 13; 274 p->preDelay = 1; 275 p->delayFrames = 3; 276 p->callbackFuncOnDown = pbResampleTonesDown; 277 p->visible = true; 278 279 p = &pushButtons[3]; 280 memset(p, 0, sizeof (pushButton_t)); 281 p->caption = ARROW_RIGHT_STRING; 282 p->x = 395; 283 p->y = 234; 284 p->w = 23; 285 p->h = 13; 286 p->preDelay = 1; 287 p->delayFrames = 3; 288 p->callbackFuncOnDown = pbResampleTonesUp; 289 p->visible = true; 290 291 // echo num scrollbar 292 s = &scrollBars[0]; 293 memset(s, 0, sizeof (scrollBar_t)); 294 s->x = 337; 295 s->y = 234; 296 s->w = 58; 297 s->h = 13; 298 s->callbackFunc = sbSetResampleTones; 299 s->visible = true; 300 setScrollBarPageLength(0, 1); 301 setScrollBarEnd(0, 36 * 2); 302 } 303 304 void pbSampleResample(void) 305 { 306 uint16_t i; 307 308 if (editor.curInstr == 0 || 309 instr[editor.curInstr] == NULL || 310 instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) 311 { 312 return; 313 } 314 315 setupResampleBoxWidgets(); 316 windowOpen(); 317 318 outOfMemory = false; 319 320 exitFlag = false; 321 while (ui.sysReqShown) 322 { 323 readInput(); 324 if (ui.sysReqEnterPressed) 325 pbDoResampling(); 326 327 setSyncedReplayerVars(); 328 handleRedrawing(); 329 330 drawResampleBox(); 331 setScrollBarPos(0, smpEd_RelReSmp + 36, false); 332 drawCheckBox(0); 333 for (i = 0; i < 4; i++) drawPushButton(i); 334 drawScrollBar(0); 335 336 flipFrame(); 337 } 338 339 for (i = 0; i < 4; i++) hidePushButton(i); 340 hideScrollBar(0); 341 342 windowClose(false); 343 344 if (outOfMemory) 345 okBox(0, "System message", "Not enough memory!", NULL); 346 } 347 348 static void cbEchoAddMemory(void) 349 { 350 echo_AddMemory ^= 1; 351 } 352 353 static void sbSetEchoNumPos(uint32_t pos) 354 { 355 if (echo_nEcho != (int32_t)pos) 356 echo_nEcho = (int16_t)pos; 357 } 358 359 static void sbSetEchoDistPos(uint32_t pos) 360 { 361 if (echo_Distance != (int32_t)pos) 362 echo_Distance = (int32_t)pos; 363 } 364 365 static void sbSetEchoFadeoutPos(uint32_t pos) 366 { 367 if (echo_VolChange != (int32_t)pos) 368 echo_VolChange = (int16_t)pos; 369 } 370 371 static void pbEchoNumDown(void) 372 { 373 if (echo_nEcho > 0) 374 echo_nEcho--; 375 } 376 377 static void pbEchoNumUp(void) 378 { 379 if (echo_nEcho < 64) 380 echo_nEcho++; 381 } 382 383 static void pbEchoDistDown(void) 384 { 385 if (echo_Distance > 0) 386 echo_Distance--; 387 } 388 389 static void pbEchoDistUp(void) 390 { 391 if (echo_Distance < 16384) 392 echo_Distance++; 393 } 394 395 static void pbEchoFadeoutDown(void) 396 { 397 if (echo_VolChange > 0) 398 echo_VolChange--; 399 } 400 401 static void pbEchoFadeoutUp(void) 402 { 403 if (echo_VolChange < 100) 404 echo_VolChange++; 405 } 406 407 static int32_t SDLCALL createEchoThread(void *ptr) 408 { 409 smpPtr_t sp; 410 411 if (echo_nEcho < 1) 412 { 413 ui.sysReqShown = false; 414 return true; 415 } 416 417 sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; 418 419 int32_t readLen = s->length; 420 int8_t *readPtr = s->dataPtr; 421 bool sample16Bit = !!(s->flags & SAMPLE_16BIT); 422 int32_t distance = echo_Distance * 16; 423 double dVolChange = echo_VolChange / 100.0; 424 425 // calculate real number of echoes 426 double dSmp = sample16Bit ? 32768.0 : 128.0; 427 int32_t k = 0; 428 while (k < echo_nEcho && dSmp >= 1.0) 429 { 430 dSmp *= dVolChange; 431 k++; 432 } 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 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!", NULL); 563 return; 564 } 565 566 SDL_DetachThread(thread); 567 } 568 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 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 759 void handleEchoToolPanic(void) 760 { 761 stopThread = true; 762 } 763 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!", NULL); 807 } 808 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 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!", NULL); 950 return; 951 } 952 953 SDL_DetachThread(thread); 954 } 955 956 static void sbSetMixBalancePos(uint32_t pos) 957 { 958 if (mix_Balance != (int8_t)pos) 959 mix_Balance = (int8_t)pos; 960 } 961 962 static void pbMixBalanceDown(void) 963 { 964 if (mix_Balance > 0) 965 mix_Balance--; 966 } 967 968 static void pbMixBalanceUp(void) 969 { 970 if (mix_Balance < 100) 971 mix_Balance++; 972 } 973 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 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 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!", NULL); 1105 } 1106 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 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 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 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 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 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 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 const bool mustInterpolate = (dVol_StartVol != dVol_EndVol); 1200 const double dVolDelta = ((dVol_EndVol - dVol_StartVol) / 100.0) / len; 1201 double dVol = dVol_StartVol / 100.0; 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 int32_t smp32 = (int32_t)((int32_t)ptr16[i] * dVol); 1213 CLAMP16(smp32); 1214 ptr16[i] = (int16_t)smp32; 1215 1216 dVol += dVolDelta; 1217 } 1218 } 1219 else // no interpolation needed 1220 { 1221 for (int32_t i = 0; i < len; i++) 1222 { 1223 int32_t smp32 = (int32_t)((int32_t)ptr16[i] * dVol); 1224 CLAMP16(smp32); 1225 ptr16[i] = (int16_t)smp32; 1226 } 1227 } 1228 } 1229 else // 8-bit sample 1230 { 1231 int8_t *ptr8 = s->dataPtr + x1; 1232 if (mustInterpolate) 1233 { 1234 for (int32_t i = 0; i < len; i++) 1235 { 1236 int32_t smp32 = (int32_t)((int32_t)ptr8[i] * dVol); 1237 CLAMP8(smp32); 1238 ptr8[i] = (int8_t)smp32; 1239 1240 dVol += dVolDelta; 1241 } 1242 } 1243 else // no interpolation needed 1244 { 1245 for (int32_t i = 0; i < len; i++) 1246 { 1247 int32_t smp32 = (int32_t)((int32_t)ptr8[i] * dVol); 1248 CLAMP8(smp32); 1249 ptr8[i] = (int8_t)smp32; 1250 } 1251 } 1252 } 1253 fixSample(s); 1254 resumeAudio(); 1255 1256 setSongModifiedFlag(); 1257 1258 applyVolumeExit: 1259 setMouseBusy(false); 1260 ui.sysReqShown = false; 1261 1262 return true; 1263 1264 (void)ptr; 1265 } 1266 1267 static void pbApplyVolume(void) 1268 { 1269 if (dVol_StartVol == 100.0 && dVol_EndVol == 100.0) 1270 { 1271 ui.sysReqShown = false; 1272 return; // no volume change to be done 1273 } 1274 1275 mouseAnimOn(); 1276 thread = SDL_CreateThread(applyVolumeThread, NULL, NULL); 1277 if (thread == NULL) 1278 { 1279 okBox(0, "System message", "Couldn't create thread!", NULL); 1280 return; 1281 } 1282 1283 SDL_DetachThread(thread); 1284 } 1285 1286 static int32_t SDLCALL getMaxScaleThread(void *ptr) 1287 { 1288 int32_t x1, x2; 1289 1290 if (instr[editor.curInstr] == NULL) 1291 goto getScaleExit; 1292 1293 sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; 1294 1295 if (smpEd_Rx1 < smpEd_Rx2) 1296 { 1297 x1 = smpEd_Rx1; 1298 x2 = smpEd_Rx2; 1299 1300 if (x2 > s->length) 1301 x2 = s->length; 1302 1303 if (x1 < 0) 1304 x1 = 0; 1305 1306 if (x2 <= x1) 1307 goto getScaleExit; 1308 } 1309 else 1310 { 1311 // no sample marking, operate on the whole sample 1312 x1 = 0; 1313 x2 = s->length; 1314 } 1315 1316 uint32_t len = x2 - x1; 1317 if (len <= 0) 1318 { 1319 dVol_StartVol = dVol_EndVol = 100.0; 1320 goto getScaleExit; 1321 } 1322 1323 double dVolChange = 100.0; 1324 1325 /* If sample is looped and the loopEnd point is inside the marked range, 1326 ** we need to unfix the fixed interpolation sample before scanning, 1327 ** and fix it again after we're done. 1328 */ 1329 bool hasLoop = GET_LOOPTYPE(s->flags) != LOOP_OFF; 1330 const int32_t loopEnd = s->loopStart + s->loopLength; 1331 bool fixedSampleInRange = hasLoop && (x1 <= loopEnd) && (x2 >= loopEnd); 1332 1333 if (fixedSampleInRange) 1334 unfixSample(s); 1335 1336 int32_t maxAmp = 0; 1337 if (s->flags & SAMPLE_16BIT) 1338 { 1339 const int16_t *ptr16 = (const int16_t *)s->dataPtr + x1; 1340 for (uint32_t i = 0; i < len; i++) 1341 { 1342 const int32_t absSmp = ABS(ptr16[i]); 1343 if (absSmp > maxAmp) 1344 maxAmp = absSmp; 1345 } 1346 1347 if (maxAmp > 0) 1348 dVolChange = (32767.0 / maxAmp) * 100.0; 1349 } 1350 else // 8-bit 1351 { 1352 const int8_t *ptr8 = (const int8_t *)&s->dataPtr[x1]; 1353 for (uint32_t i = 0; i < len; i++) 1354 { 1355 const int32_t absSmp = ABS(ptr8[i]); 1356 if (absSmp > maxAmp) 1357 maxAmp = absSmp; 1358 } 1359 1360 if (maxAmp > 0) 1361 dVolChange = (127.0 / maxAmp) * 100.0; 1362 } 1363 1364 if (fixedSampleInRange) 1365 fixSample(s); 1366 1367 if (dVolChange < 100.0) // yes, this can happen... 1368 dVolChange = 100.0; 1369 1370 dVol_StartVol = dVol_EndVol = dVolChange; 1371 1372 getScaleExit: 1373 setMouseBusy(false); 1374 return true; 1375 1376 (void)ptr; 1377 } 1378 1379 static void pbGetMaxScale(void) 1380 { 1381 mouseAnimOn(); 1382 thread = SDL_CreateThread(getMaxScaleThread, NULL, NULL); 1383 if (thread == NULL) 1384 { 1385 okBox(0, "System message", "Couldn't create thread!", NULL); 1386 return; 1387 } 1388 1389 SDL_DetachThread(thread); 1390 } 1391 1392 static void drawSampleVolumeBox(void) 1393 { 1394 char sign; 1395 const int16_t x = 166; 1396 const int16_t y = 230; 1397 const int16_t w = 301; 1398 const int16_t h = 52; 1399 uint32_t val; 1400 1401 // main fill 1402 fillRect(x + 1, y + 1, w - 2, h - 2, PAL_BUTTONS); 1403 1404 // outer border 1405 vLine(x, y, h - 1, PAL_BUTTON1); 1406 hLine(x + 1, y, w - 2, PAL_BUTTON1); 1407 vLine(x + w - 1, y, h, PAL_BUTTON2); 1408 hLine(x, y + h - 1, w - 1, PAL_BUTTON2); 1409 1410 // inner border 1411 vLine(x + 2, y + 2, h - 5, PAL_BUTTON2); 1412 hLine(x + 3, y + 2, w - 6, PAL_BUTTON2); 1413 vLine(x + w - 3, y + 2, h - 4, PAL_BUTTON1); 1414 hLine(x + 2, y + h - 3, w - 4, PAL_BUTTON1); 1415 1416 textOutShadow(172, 236, PAL_FORGRND, PAL_BUTTON2, "Start volume"); 1417 textOutShadow(172, 250, PAL_FORGRND, PAL_BUTTON2, "End volume"); 1418 charOutShadow(282, 236, PAL_FORGRND, PAL_BUTTON2, '%'); 1419 charOutShadow(282, 250, PAL_FORGRND, PAL_BUTTON2, '%'); 1420 1421 const int32_t startVol = (int32_t)dVol_StartVol; 1422 const int32_t endVol = (int32_t)dVol_EndVol; 1423 1424 if (startVol > 200) 1425 { 1426 charOut(253, 236, PAL_FORGRND, '>'); 1427 charOut(260, 236, PAL_FORGRND, '2'); 1428 charOut(267, 236, PAL_FORGRND, '0'); 1429 charOut(274, 236, PAL_FORGRND, '0'); 1430 } 1431 else 1432 { 1433 if (startVol == 0) sign = ' '; 1434 else if (startVol < 0) sign = '-'; 1435 else sign = '+'; 1436 1437 val = ABS(startVol); 1438 if (val > 99) 1439 { 1440 charOut(253, 236, PAL_FORGRND, sign); 1441 charOut(260, 236, PAL_FORGRND, '0' + (char)(val / 100)); 1442 charOut(267, 236, PAL_FORGRND, '0' + ((val / 10) % 10)); 1443 charOut(274, 236, PAL_FORGRND, '0' + (val % 10)); 1444 } 1445 else if (val > 9) 1446 { 1447 charOut(260, 236, PAL_FORGRND, sign); 1448 charOut(267, 236, PAL_FORGRND, '0' + (char)(val / 10)); 1449 charOut(274, 236, PAL_FORGRND, '0' + (val % 10)); 1450 } 1451 else 1452 { 1453 charOut(267, 236, PAL_FORGRND, sign); 1454 charOut(274, 236, PAL_FORGRND, '0' + (char)val); 1455 } 1456 } 1457 1458 if (endVol > 200) 1459 { 1460 charOut(253, 250, PAL_FORGRND, '>'); 1461 charOut(260, 250, PAL_FORGRND, '2'); 1462 charOut(267, 250, PAL_FORGRND, '0'); 1463 charOut(274, 250, PAL_FORGRND, '0'); 1464 } 1465 else 1466 { 1467 if (endVol == 0) sign = ' '; 1468 else if (endVol < 0) sign = '-'; 1469 else sign = '+'; 1470 1471 val = ABS(endVol); 1472 if (val > 99) 1473 { 1474 charOut(253, 250, PAL_FORGRND, sign); 1475 charOut(260, 250, PAL_FORGRND, '0' + (char)(val / 100)); 1476 charOut(267, 250, PAL_FORGRND, '0' + ((val / 10) % 10)); 1477 charOut(274, 250, PAL_FORGRND, '0' + (val % 10)); 1478 } 1479 else if (val > 9) 1480 { 1481 charOut(260, 250, PAL_FORGRND, sign); 1482 charOut(267, 250, PAL_FORGRND, '0' + (char)(val / 10)); 1483 charOut(274, 250, PAL_FORGRND, '0' + (val % 10)); 1484 } 1485 else 1486 { 1487 charOut(267, 250, PAL_FORGRND, sign); 1488 charOut(274, 250, PAL_FORGRND, '0' + (char)val); 1489 } 1490 } 1491 } 1492 1493 static void setupVolumeBoxWidgets(void) 1494 { 1495 pushButton_t *p; 1496 scrollBar_t *s; 1497 1498 // "Apply" pushbutton 1499 p = &pushButtons[0]; 1500 memset(p, 0, sizeof (pushButton_t)); 1501 p->caption = "Apply"; 1502 p->x = 171; 1503 p->y = 262; 1504 p->w = 73; 1505 p->h = 16; 1506 p->callbackFuncOnUp = pbApplyVolume; 1507 p->visible = true; 1508 1509 // "Get maximum scale" pushbutton 1510 p = &pushButtons[1]; 1511 memset(p, 0, sizeof (pushButton_t)); 1512 p->caption = "Get maximum scale"; 1513 p->x = 245; 1514 p->y = 262; 1515 p->w = 143; 1516 p->h = 16; 1517 p->callbackFuncOnUp = pbGetMaxScale; 1518 p->visible = true; 1519 1520 // "Exit" pushbutton 1521 p = &pushButtons[2]; 1522 memset(p, 0, sizeof (pushButton_t)); 1523 p->caption = "Exit"; 1524 p->x = 389; 1525 p->y = 262; 1526 p->w = 73; 1527 p->h = 16; 1528 p->callbackFuncOnUp = pbExit; 1529 p->visible = true; 1530 1531 // scrollbar buttons 1532 1533 p = &pushButtons[3]; 1534 memset(p, 0, sizeof (pushButton_t)); 1535 p->caption = ARROW_LEFT_STRING; 1536 p->x = 292; 1537 p->y = 234; 1538 p->w = 23; 1539 p->h = 13; 1540 p->preDelay = 1; 1541 p->delayFrames = 3; 1542 p->callbackFuncOnDown = pbSampStartVolDown; 1543 p->visible = true; 1544 1545 p = &pushButtons[4]; 1546 memset(p, 0, sizeof (pushButton_t)); 1547 p->caption = ARROW_RIGHT_STRING; 1548 p->x = 439; 1549 p->y = 234; 1550 p->w = 23; 1551 p->h = 13; 1552 p->preDelay = 1; 1553 p->delayFrames = 3; 1554 p->callbackFuncOnDown = pbSampStartVolUp; 1555 p->visible = true; 1556 1557 p = &pushButtons[5]; 1558 memset(p, 0, sizeof (pushButton_t)); 1559 p->caption = ARROW_LEFT_STRING; 1560 p->x = 292; 1561 p->y = 248; 1562 p->w = 23; 1563 p->h = 13; 1564 p->preDelay = 1; 1565 p->delayFrames = 3; 1566 p->callbackFuncOnDown = pbSampEndVolDown; 1567 p->visible = true; 1568 1569 p = &pushButtons[6]; 1570 memset(p, 0, sizeof (pushButton_t)); 1571 p->caption = ARROW_RIGHT_STRING; 1572 p->x = 439; 1573 p->y = 248; 1574 p->w = 23; 1575 p->h = 13; 1576 p->preDelay = 1; 1577 p->delayFrames = 3; 1578 p->callbackFuncOnDown = pbSampEndVolUp; 1579 p->visible = true; 1580 1581 // volume start scrollbar 1582 s = &scrollBars[0]; 1583 memset(s, 0, sizeof (scrollBar_t)); 1584 s->x = 315; 1585 s->y = 234; 1586 s->w = 124; 1587 s->h = 13; 1588 s->callbackFunc = sbSetStartVolPos; 1589 s->visible = true; 1590 setScrollBarPageLength(0, 1); 1591 setScrollBarEnd(0, 200 * 2); 1592 setScrollBarPos(0, 200, false); 1593 1594 // volume end scrollbar 1595 s = &scrollBars[1]; 1596 memset(s, 0, sizeof (scrollBar_t)); 1597 s->x = 315; 1598 s->y = 248; 1599 s->w = 124; 1600 s->h = 13; 1601 s->callbackFunc = sbSetEndVolPos; 1602 s->visible = true; 1603 setScrollBarPageLength(1, 1); 1604 setScrollBarEnd(1, 200 * 2); 1605 setScrollBarPos(1, 200, false); 1606 } 1607 1608 void pbSampleVolume(void) 1609 { 1610 uint16_t i; 1611 1612 if (editor.curInstr == 0 || 1613 instr[editor.curInstr] == NULL || 1614 instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) 1615 { 1616 return; 1617 } 1618 1619 setupVolumeBoxWidgets(); 1620 windowOpen(); 1621 1622 exitFlag = false; 1623 while (ui.sysReqShown) 1624 { 1625 readInput(); 1626 if (ui.sysReqEnterPressed) 1627 { 1628 pbApplyVolume(); 1629 keyb.ignoreCurrKeyUp = true; // don't handle key up event for this key release 1630 } 1631 1632 setSyncedReplayerVars(); 1633 handleRedrawing(); 1634 1635 // this is needed for the "Get maximum scale" button 1636 if (ui.setMouseIdle) mouseAnimOff(); 1637 1638 drawSampleVolumeBox(); 1639 1640 const int32_t startVol = (int32_t)dVol_StartVol; 1641 const int32_t endVol = (int32_t)dVol_EndVol; 1642 1643 setScrollBarPos(0, 200 + startVol, false); 1644 setScrollBarPos(1, 200 + endVol, false); 1645 for (i = 0; i < 7; i++) drawPushButton(i); 1646 for (i = 0; i < 2; i++) drawScrollBar(i); 1647 1648 flipFrame(); 1649 } 1650 1651 for (i = 0; i < 7; i++) hidePushButton(i); 1652 for (i = 0; i < 2; i++) hideScrollBar(i); 1653 1654 windowClose(true); 1655 }