ft2_sample_ed.c (85491B)
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 <math.h> 10 #ifndef _WIN32 11 #include <unistd.h> // chdir() in UNICHAR_CHDIR() 12 #endif 13 #if defined _WIN32 || defined __amd64__ || (defined __i386__ && defined __SSE2__) 14 #include <emmintrin.h> 15 #endif 16 #include "ft2_header.h" 17 #include "ft2_config.h" 18 #include "ft2_audio.h" 19 #include "ft2_pattern_ed.h" 20 #include "ft2_gui.h" 21 #include "scopes/ft2_scopes.h" 22 #include "ft2_video.h" 23 #include "ft2_inst_ed.h" 24 #include "ft2_sample_ed.h" 25 #include "ft2_sample_saver.h" 26 #include "ft2_mouse.h" 27 #include "ft2_diskop.h" 28 #include "ft2_keyboard.h" 29 #include "ft2_structs.h" 30 #include "ft2_random.h" 31 #include "ft2_replayer.h" 32 #include "ft2_smpfx.h" 33 #include "mixer/ft2_windowed_sinc.h" // SINC_TAPS, SINC_NEGATIVE_TAPS 34 35 static const char sharpNote1Char[12] = { 'C', 'C', 'D', 'D', 'E', 'F', 'F', 'G', 'G', 'A', 'A', 'B' }; 36 static const char sharpNote2Char[12] = { '-', '#', '-', '#', '-', '-', '#', '-', '#', '-', '#', '-' }; 37 static const char flatNote1Char[12] = { 'C', 'D', 'D', 'E', 'E', 'F', 'G', 'G', 'A', 'A', 'B', 'B' }; 38 static const char flatNote2Char[12] = { '-', 'b', '-', 'b', '-', '-', 'b', '-', 'b', '-', 'b', '-' }; 39 40 static char smpEd_SysReqText[64]; 41 static int8_t *smpCopyBuff; 42 static bool updateLoopsOnMouseUp, writeSampleFlag, smpCopyDidCopyWholeSample; 43 static int32_t smpEd_OldSmpPosLine = -1; 44 static int32_t smpEd_ViewSize, smpEd_ScrPos, smpCopySize, smpCopyBits; 45 static int32_t old_Rx1, old_Rx2, old_ViewSize, old_SmpScrPos; 46 static int32_t lastMouseX, lastMouseY, lastDrawX, lastDrawY, mouseXOffs, curSmpLoopStart, curSmpLoopLength; 47 static double dScrPosScaled, dPos2ScrMul, dScr2SmpPosMul; 48 static sample_t smpCopySample; 49 static SDL_Thread *thread; 50 51 // globals 52 int32_t smpEd_Rx1 = 0, smpEd_Rx2 = 0; 53 54 // allocs sample with proper alignment and padding for branchless resampling interpolation 55 bool allocateSmpData(sample_t *s, int32_t length, bool sample16Bit) 56 { 57 if (sample16Bit) 58 length <<= 1; 59 60 s->origDataPtr = (int8_t *)malloc(length + SAMPLE_PAD_LENGTH); 61 if (s->origDataPtr == NULL) 62 { 63 s->dataPtr = NULL; 64 return false; 65 } 66 67 s->dataPtr = s->origDataPtr + SMP_DAT_OFFSET; 68 return true; 69 } 70 71 bool allocateSmpDataPtr(smpPtr_t *sp, int32_t length, bool sample16Bit) 72 { 73 if (sample16Bit) 74 length <<= 1; 75 76 int8_t *newPtr = (int8_t *)malloc(length + SAMPLE_PAD_LENGTH); 77 if (newPtr == NULL) 78 return false; 79 80 sp->origPtr = newPtr; 81 82 sp->ptr = sp->origPtr + SMP_DAT_OFFSET; 83 return true; 84 } 85 86 // reallocs sample with proper alignment and padding for branchless resampling interpolation 87 bool reallocateSmpData(sample_t *s, int32_t length, bool sample16Bit) 88 { 89 if (s->origDataPtr == NULL) 90 return allocateSmpData(s, length, sample16Bit); 91 92 if (sample16Bit) 93 length <<= 1; 94 95 int8_t *newPtr = (int8_t *)realloc(s->origDataPtr, length + SAMPLE_PAD_LENGTH); 96 if (newPtr == NULL) 97 return false; 98 99 s->origDataPtr = newPtr; 100 s->dataPtr = s->origDataPtr + SMP_DAT_OFFSET; 101 102 return true; 103 } 104 105 // reallocs sample with proper alignment and padding for branchless resampling interpolation 106 bool reallocateSmpDataPtr(smpPtr_t *sp, int32_t length, bool sample16Bit) 107 { 108 if (sp->origPtr == NULL) 109 return allocateSmpDataPtr(sp, length, sample16Bit); 110 111 if (sample16Bit) 112 length <<= 1; 113 114 int8_t *newPtr = (int8_t *)realloc(sp->origPtr, length + SAMPLE_PAD_LENGTH); 115 if (newPtr == NULL) 116 return false; 117 118 sp->origPtr = newPtr; 119 sp->ptr = sp->origPtr + SMP_DAT_OFFSET; 120 121 return true; 122 } 123 124 void setSmpDataPtr(sample_t *s, smpPtr_t *sp) 125 { 126 s->origDataPtr = sp->origPtr; 127 s->dataPtr = sp->ptr; 128 } 129 130 void freeSmpDataPtr(smpPtr_t *sp) 131 { 132 if (sp->origPtr != NULL) 133 { 134 free(sp->origPtr); 135 sp->origPtr = NULL; 136 } 137 138 sp->ptr = NULL; 139 } 140 141 void freeSmpData(sample_t *s) 142 { 143 if (s->origDataPtr != NULL) 144 { 145 free(s->origDataPtr); 146 s->origDataPtr = NULL; 147 } 148 149 s->dataPtr = NULL; 150 s->isFixed = false; 151 } 152 153 bool cloneSample(sample_t *src, sample_t *dst) 154 { 155 smpPtr_t sp; 156 157 freeSmpData(dst); 158 159 if (src == NULL) 160 { 161 memset(dst, 0, sizeof (sample_t)); 162 } 163 else 164 { 165 memcpy(dst, src, sizeof (sample_t)); 166 167 // zero out stuff that wasn't supposed to be cloned 168 dst->origDataPtr = dst->dataPtr = NULL; 169 dst->isFixed = false; 170 dst->fixedPos = 0; 171 172 // if source sample isn't empty, allocate room and copy it over (and fix it) 173 if (src->length > 0 && src->dataPtr != NULL) 174 { 175 bool sample16Bit = !!(src->flags & SAMPLE_16BIT); 176 if (!allocateSmpDataPtr(&sp, src->length, sample16Bit)) 177 { 178 dst->length = 0; 179 return false; 180 } 181 182 setSmpDataPtr(dst, &sp); 183 memcpy(dst->dataPtr, src->dataPtr, src->length << sample16Bit); 184 fixSample(dst); 185 } 186 } 187 188 return true; 189 } 190 191 sample_t *getCurSample(void) 192 { 193 if (editor.curInstr == 0 || instr[editor.curInstr] == NULL) 194 return NULL; 195 196 return &instr[editor.curInstr]->smp[editor.curSmp]; 197 } 198 199 void sanitizeSample(sample_t *s) 200 { 201 if (s == NULL) 202 return; 203 204 // if a sample has both forward loop and pingpong loop set, it means pingpong loop (FT2 mixer behavior) 205 if (GET_LOOPTYPE(s->flags) == (LOOP_FWD | LOOP_BIDI)) 206 s->flags &= ~LOOP_FWD; // remove forward loop flag 207 208 if (s->volume > 64) 209 s->volume = 64; 210 211 s->relativeNote = CLAMP(s->relativeNote, -48, 71); 212 s->length = CLAMP(s->length, 0, MAX_SAMPLE_LEN); 213 214 if (s->loopStart < 0 || s->loopLength <= 0 || s->loopStart+s->loopLength > s->length) 215 { 216 s->loopStart = 0; 217 s->loopLength = 0; 218 DISABLE_LOOP(s->flags); 219 } 220 } 221 222 static int32_t myMod(int32_t a, int32_t b) // works on negative numbers! 223 { 224 int32_t c = a % b; 225 return (c < 0) ? (c + b) : c; 226 } 227 228 // modifies samples before index 0, and after loop/end (for branchless mixer interpolation (kinda)) 229 void fixSample(sample_t *s) 230 { 231 int32_t pos; 232 bool backwards; 233 234 assert(s != NULL); 235 if (s->dataPtr == NULL || s->length <= 0) 236 { 237 s->isFixed = false; 238 s->fixedPos = 0; 239 return; // empty sample 240 } 241 242 const bool sample16Bit = !!(s->flags & SAMPLE_16BIT); 243 int16_t *ptr16 = (int16_t *)s->dataPtr; 244 uint8_t loopType = GET_LOOPTYPE(s->flags); 245 int32_t length = s->length; 246 int32_t loopStart = s->loopStart; 247 int32_t loopLength = s->loopLength; 248 int32_t loopEnd = s->loopStart + s->loopLength; 249 250 // treat loop as disabled if loopLen == 0 (FT2 does this) 251 if (loopType != 0 && loopLength <= 0) 252 { 253 loopType = 0; 254 loopStart = loopLength = loopEnd = 0; 255 } 256 257 /* All negative taps should be equal to the first sample point when at sampling 258 ** position #0 (on sample trigger), until an eventual loop cycle, where we do 259 ** a special left edge case with replaced tap data. 260 ** The sample pointer is offset and has allocated data before it, so this is 261 ** safe. 262 */ 263 if (sample16Bit) 264 { 265 for (int32_t i = 0; i < MAX_LEFT_TAPS; i++) 266 ptr16[i-MAX_LEFT_TAPS] = ptr16[0]; 267 } 268 else 269 { 270 for (int32_t i = 0; i < MAX_LEFT_TAPS; i++) 271 s->dataPtr[i-MAX_LEFT_TAPS] = s->dataPtr[0]; 272 } 273 274 if (loopType == LOOP_OFF) // no loop 275 { 276 if (sample16Bit) 277 { 278 for (int32_t i = 0; i < MAX_RIGHT_TAPS; i++) 279 ptr16[length+i] = ptr16[length-1]; 280 } 281 else 282 { 283 for (int32_t i = 0; i < MAX_RIGHT_TAPS; i++) 284 s->dataPtr[length+i] = s->dataPtr[length-1]; 285 } 286 287 s->fixedPos = 0; // this value is not used for non-looping samples, set to zero 288 s->isFixed = false; // no fixed samples inside actual sample data 289 return; 290 } 291 292 s->fixedPos = loopEnd; 293 s->isFixed = true; 294 295 if (loopType == LOOP_FWD) // forward loop 296 { 297 if (sample16Bit) 298 { 299 // left edge (we need MAX_TAPS amount of taps starting from the center tap) 300 for (int32_t i = -MAX_LEFT_TAPS; i < MAX_TAPS; i++) 301 { 302 pos = loopStart + myMod(i, loopLength); 303 s->leftEdgeTapSamples16[MAX_LEFT_TAPS+i] = ptr16[pos]; 304 } 305 306 // right edge (change actual sample data since data after loop is never used) 307 pos = loopStart; 308 for (int32_t i = 0; i < MAX_RIGHT_TAPS; i++) 309 { 310 s->fixedSmp[i] = ptr16[loopEnd+i]; 311 ptr16[loopEnd+i] = ptr16[pos]; 312 313 if (++pos >= loopEnd) 314 pos -= loopLength; 315 } 316 } 317 else // 8-bit 318 { 319 // left edge (we need MAX_TAPS amount of taps starting from the center tap) 320 for (int32_t i = -MAX_LEFT_TAPS; i < MAX_TAPS; i++) 321 { 322 pos = loopStart + myMod(i, loopLength); 323 s->leftEdgeTapSamples8[MAX_LEFT_TAPS+i] = s->dataPtr[pos]; 324 } 325 326 // right edge (change actual sample data since data after loop is never used) 327 pos = loopStart; 328 for (int32_t i = 0; i < MAX_RIGHT_TAPS; i++) 329 { 330 s->fixedSmp[i] = s->dataPtr[loopEnd+i]; 331 s->dataPtr[loopEnd+i] = s->dataPtr[pos]; 332 333 if (++pos >= loopEnd) 334 pos -= loopLength; 335 } 336 } 337 } 338 else // pingpong loop 339 { 340 if (sample16Bit) 341 { 342 // left edge (positive taps, we need MAX_TAPS amount of taps starting from the center tap) 343 pos = loopStart; 344 backwards = false; 345 for (int32_t i = 0; i < MAX_TAPS; i++) 346 { 347 if (backwards) 348 { 349 if (pos < loopStart) 350 { 351 pos = loopStart; 352 backwards = false; 353 } 354 } 355 else if (pos >= loopEnd) // forwards 356 { 357 pos = loopEnd-1; 358 backwards = true; 359 } 360 361 s->leftEdgeTapSamples16[MAX_LEFT_TAPS+i] = ptr16[pos]; 362 363 if (backwards) 364 pos--; 365 else 366 pos++; 367 } 368 369 // left edge (negative taps) 370 for (int32_t i = 0; i < MAX_LEFT_TAPS; i++) 371 s->leftEdgeTapSamples16[(MAX_LEFT_TAPS-1)-i] = s->leftEdgeTapSamples16[MAX_LEFT_TAPS+1+i]; 372 373 // right edge (change actual sample data since data after loop is never used) 374 pos = loopEnd-1; 375 backwards = true; 376 for (int32_t i = 0; i < MAX_RIGHT_TAPS; i++) 377 { 378 if (backwards) 379 { 380 if (pos < loopStart) 381 { 382 pos = loopStart; 383 backwards = false; 384 } 385 } 386 else if (pos >= loopEnd) // forwards 387 { 388 pos = loopEnd-1; 389 backwards = true; 390 } 391 392 s->fixedSmp[i] = ptr16[loopEnd+i]; 393 ptr16[loopEnd+i] = ptr16[pos]; 394 395 if (backwards) 396 pos--; 397 else 398 pos++; 399 } 400 } 401 else // 8-bit 402 { 403 // left edge (positive taps, we need MAX_TAPS amount of taps starting from the center tap) 404 pos = loopStart; 405 backwards = false; 406 for (int32_t i = 0; i < MAX_TAPS; i++) 407 { 408 if (backwards) 409 { 410 if (pos < loopStart) 411 { 412 pos = loopStart; 413 backwards = false; 414 } 415 } 416 else if (pos >= loopEnd) // forwards 417 { 418 pos = loopEnd-1; 419 backwards = true; 420 } 421 422 s->leftEdgeTapSamples8[MAX_LEFT_TAPS+i] = s->dataPtr[pos]; 423 424 if (backwards) 425 pos--; 426 else 427 pos++; 428 } 429 430 // left edge (negative taps) 431 for (int32_t i = 0; i < MAX_LEFT_TAPS; i++) 432 s->leftEdgeTapSamples8[(MAX_LEFT_TAPS-1)-i] = s->leftEdgeTapSamples8[MAX_LEFT_TAPS+1+i]; 433 434 // right edge (change actual sample data since data after loop is never used) 435 pos = loopEnd-1; 436 backwards = true; 437 for (int32_t i = 0; i < MAX_RIGHT_TAPS; i++) 438 { 439 if (backwards) 440 { 441 if (pos < loopStart) 442 { 443 pos = loopStart; 444 backwards = false; 445 } 446 } 447 else if (pos >= loopEnd) // forwards 448 { 449 pos = loopEnd-1; 450 backwards = true; 451 } 452 453 s->fixedSmp[i] = s->dataPtr[loopEnd+i]; 454 s->dataPtr[loopEnd+i] = s->dataPtr[pos]; 455 456 if (backwards) 457 pos--; 458 else 459 pos++; 460 } 461 } 462 } 463 } 464 465 // restores interpolation tap samples after loop/end 466 void unfixSample(sample_t *s) 467 { 468 assert(s != NULL); 469 if (s->dataPtr == NULL || !s->isFixed) 470 return; // empty sample or not fixed (f.ex. no loop) 471 472 if (s->flags & SAMPLE_16BIT) 473 { 474 int16_t *ptr16 = (int16_t *)s->dataPtr + s->fixedPos; 475 for (int32_t i = 0; i < MAX_RIGHT_TAPS; i++) 476 ptr16[i] = s->fixedSmp[i]; 477 } 478 else // 8-bit 479 { 480 int8_t *ptr8 = s->dataPtr + s->fixedPos; 481 for (int32_t i = 0; i < MAX_RIGHT_TAPS; i++) 482 ptr8[i] = (int8_t)s->fixedSmp[i]; 483 } 484 485 s->isFixed = false; 486 } 487 488 double getSampleValue(int8_t *smpData, int32_t position, bool sample16Bit) 489 { 490 if (smpData == NULL) 491 return 0; 492 493 if (sample16Bit) 494 { 495 position <<= 1; 496 return *((int16_t *)&smpData[position]); 497 } 498 else 499 { 500 return smpData[position]; 501 } 502 } 503 504 void putSampleValue(int8_t *smpData, int32_t position, double dSample, bool sample16Bit) 505 { 506 DROUND(dSample); 507 int32_t sample = (int32_t)dSample; 508 509 if (sample16Bit) 510 { 511 CLAMP16(sample); 512 *((int16_t *)&smpData[position<<1]) = (int16_t)sample; 513 } 514 else 515 { 516 CLAMP8(sample); 517 smpData[position] = (int8_t)sample; 518 } 519 } 520 521 void clearCopyBuffer(void) 522 { 523 if (smpCopyBuff != NULL) 524 { 525 free(smpCopyBuff); 526 smpCopyBuff = NULL; 527 } 528 529 smpCopySize = 0; 530 smpCopyBits = 8; 531 smpCopyDidCopyWholeSample = false; 532 } 533 534 int32_t getSampleMiddleCRate(sample_t *s) 535 { 536 return (int32_t)(getSampleC4Rate(s) + 0.5); // rounded 537 } 538 539 int32_t getSampleRangeStart(void) 540 { 541 return smpEd_Rx1; 542 } 543 544 int32_t getSampleRangeEnd(void) 545 { 546 return smpEd_Rx2; 547 } 548 549 int32_t getSampleRangeLength(void) 550 { 551 return smpEd_Rx2 - smpEd_Rx1; 552 } 553 554 // for smpPos2Scr() / scr2SmpPos() 555 static void updateViewSize(void) 556 { 557 if (smpEd_ViewSize == 0) 558 dPos2ScrMul = 1.0; 559 else 560 dPos2ScrMul = (double)SAMPLE_AREA_WIDTH / smpEd_ViewSize; 561 562 dScr2SmpPosMul = smpEd_ViewSize * (1.0 / SAMPLE_AREA_WIDTH); 563 } 564 565 static void updateScrPos(void) 566 { 567 dScrPosScaled = floor(smpEd_ScrPos * dPos2ScrMul); 568 } 569 570 // sample pos -> screen x pos (if outside of visible area, will return <0 or >=SCREEN_W) 571 static int32_t smpPos2Scr(int32_t pos) 572 { 573 if (smpEd_ViewSize <= 0) 574 return -1; 575 576 sample_t *s = getCurSample(); 577 if (s == NULL) 578 return -1; 579 580 if (pos > s->length) 581 pos = s->length; 582 583 double dPos = (pos * dPos2ScrMul) + 0.5; // pre-rounding bias is needed here 584 dPos -= dScrPosScaled; 585 586 // this is important, or else the result can mess up in some cases 587 dPos = CLAMP(dPos, INT32_MIN, INT32_MAX); 588 pos = (int32_t)dPos; 589 590 return pos; 591 } 592 593 // screen x pos -> sample pos 594 static int32_t scr2SmpPos(int32_t x) 595 { 596 if (smpEd_ViewSize <= 0) 597 return 0; 598 599 sample_t *s = getCurSample(); 600 if (s == NULL) 601 return 0; 602 603 if (x < 0) 604 x = 0; 605 606 double dPos = (dScrPosScaled + x) * dScr2SmpPosMul; 607 608 x = (int32_t)dPos; 609 if (x > s->length) 610 x = s->length; 611 612 return x; 613 } 614 615 static void hideLoopPinSprites(void) 616 { 617 hideSprite(SPRITE_LEFT_LOOP_PIN); 618 hideSprite(SPRITE_RIGHT_LOOP_PIN); 619 } 620 621 static void fixLoopGadgets(void) 622 { 623 if (!ui.sampleEditorShown) 624 { 625 hideLoopPinSprites(); 626 return; 627 } 628 629 sample_t *s = getCurSample(); 630 631 bool showLoopPins = true; 632 if (s == NULL || s->dataPtr == NULL || s->length <= 0 || GET_LOOPTYPE(s->flags) == LOOP_OFF) 633 showLoopPins = false; 634 635 // draw Repeat/Replen. numbers 636 hexOutBg(536, 375, PAL_FORGRND, PAL_DESKTOP, curSmpLoopStart, 8); 637 hexOutBg(536, 387, PAL_FORGRND, PAL_DESKTOP, curSmpLoopLength, 8); 638 639 if (!showLoopPins) 640 { 641 hideLoopPinSprites(); 642 } 643 else 644 { 645 // draw sample loop points 646 647 const int32_t loopStart = smpPos2Scr(curSmpLoopStart); 648 const int32_t loopEnd = smpPos2Scr(curSmpLoopStart+curSmpLoopLength); 649 650 // do -8 test because part of the loop sprite sticks out on the left/right 651 652 if (loopStart >= -8 && loopStart <= SAMPLE_AREA_WIDTH+8) 653 setSpritePos(SPRITE_LEFT_LOOP_PIN, (int16_t)(loopStart - 8), 174); 654 else 655 hideSprite(SPRITE_LEFT_LOOP_PIN); 656 657 if (loopEnd >= -8) 658 { 659 if (loopEnd <= SAMPLE_AREA_WIDTH+8) 660 setSpritePos(SPRITE_RIGHT_LOOP_PIN, (int16_t)(loopEnd - 8), 174); 661 else 662 hideSprite(SPRITE_RIGHT_LOOP_PIN); 663 } 664 else 665 { 666 hideSprite(SPRITE_RIGHT_LOOP_PIN); 667 } 668 } 669 } 670 671 static void fixSampleScrollbar(void) 672 { 673 sample_t *s = getCurSample(); 674 if (s == NULL) 675 { 676 setScrollBarPageLength(SB_SAMP_SCROLL, 0); 677 setScrollBarEnd(SB_SAMP_SCROLL, 0); 678 setScrollBarPos(SB_SAMP_SCROLL, 0, false); 679 return; 680 } 681 682 setScrollBarPageLength(SB_SAMP_SCROLL, smpEd_ViewSize); 683 setScrollBarEnd(SB_SAMP_SCROLL, instr[editor.curInstr]->smp[editor.curSmp].length); 684 setScrollBarPos(SB_SAMP_SCROLL, smpEd_ScrPos, false); 685 } 686 687 static bool getCopyBuffer(int32_t size, bool sample16Bit) 688 { 689 if (smpCopyBuff != NULL) 690 free(smpCopyBuff); 691 692 if (size > MAX_SAMPLE_LEN) 693 size = MAX_SAMPLE_LEN; 694 695 smpCopyBuff = (int8_t *)malloc(size << sample16Bit); 696 if (smpCopyBuff == NULL) 697 { 698 smpCopySize = 0; 699 return false; 700 } 701 702 smpCopySize = size; 703 return true; 704 } 705 706 static int32_t SDLCALL copySampleThread(void *ptr) 707 { 708 pauseAudio(); 709 710 sample_t *src; 711 if (instr[editor.srcInstr] == NULL) 712 src = NULL; 713 else 714 src = &instr[editor.srcInstr]->smp[editor.srcSmp]; 715 716 if (instr[editor.curInstr] == NULL && !allocateInstr(editor.curInstr)) 717 goto error; 718 719 sample_t *dst = &instr[editor.curInstr]->smp[editor.curSmp]; 720 if (!cloneSample(src, dst)) 721 goto error; 722 723 resumeAudio(); 724 725 editor.updateCurSmp = true; 726 setSongModifiedFlag(); 727 setMouseBusy(false); 728 729 return true; 730 731 error: 732 resumeAudio(); 733 okBoxThreadSafe(0, "System message", "Not enough memory!", NULL); 734 return true; 735 736 (void)ptr; 737 } 738 739 void copySmp(void) // copy sample from srcInstr->srcSmp to curInstr->curSmp 740 { 741 if (editor.curInstr == 0 || (editor.curInstr == editor.srcInstr && editor.curSmp == editor.srcSmp)) 742 return; 743 744 mouseAnimOn(); 745 thread = SDL_CreateThread(copySampleThread, NULL, NULL); 746 if (thread == NULL) 747 { 748 okBox(0, "System message", "Couldn't create thread!", NULL); 749 return; 750 } 751 752 SDL_DetachThread(thread); 753 } 754 755 void xchgSmp(void) // dstSmp <-> srcSmp 756 { 757 if (editor.curInstr == 0 || 758 (editor.curInstr == editor.srcInstr && editor.curSmp == editor.srcSmp) || 759 instr[editor.curInstr] == NULL) 760 { 761 return; 762 } 763 764 sample_t *src = &instr[editor.curInstr]->smp[editor.srcSmp]; 765 sample_t *dst = &instr[editor.curInstr]->smp[editor.curSmp]; 766 767 lockMixerCallback(); 768 const sample_t dstTmp = *dst; 769 *dst = *src; 770 *src = dstTmp; 771 unlockMixerCallback(); 772 773 updateNewSample(); 774 setSongModifiedFlag(); 775 } 776 777 static void writeRange(void) 778 { 779 // very first sample (rx1=0,rx2=0) is the "no range" special case 780 if (!ui.sampleEditorShown || smpEd_ViewSize == 0 || (smpEd_Rx1 == 0 && smpEd_Rx2 == 0)) 781 return; 782 783 // test if range is outside of view (passed it by scrolling) 784 int32_t start = smpPos2Scr(smpEd_Rx1); 785 if (start >= SAMPLE_AREA_WIDTH) 786 return; 787 788 // test if range is outside of view (passed it by scrolling) 789 int32_t end = smpPos2Scr(smpEd_Rx2); 790 if (end < 0) 791 return; 792 793 start = CLAMP(start, 0, SAMPLE_AREA_WIDTH-1); 794 end = CLAMP(end, 0, SAMPLE_AREA_WIDTH-1); 795 796 int32_t rangeLen = (end + 1) - start; 797 assert(start+rangeLen <= SCREEN_W); 798 799 uint32_t *ptr32 = &video.frameBuffer[(174 * SCREEN_W) + start]; 800 for (int32_t y = 0; y < SAMPLE_AREA_HEIGHT; y++) 801 { 802 for (int32_t x = 0; x < rangeLen; x++) 803 ptr32[x] = video.palette[(ptr32[x] >> 24) ^ 2]; // ">> 24" to get palette, XOR 2 to switch between mark/normal palette 804 805 ptr32 += SCREEN_W; 806 } 807 } 808 809 static int32_t getScaledSample(sample_t *s, int32_t index) // for sample data viewer 810 { 811 int32_t tmp32, sample; 812 813 const int32_t loopEnd = s->loopStart + s->loopLength; 814 815 if (s->dataPtr == NULL || index < 0 || index >= s->length) 816 return 0; 817 818 if (s->flags & SAMPLE_16BIT) 819 { 820 int16_t *ptr16 = (int16_t *)s->dataPtr; 821 822 // don't read fixed mixer interpolation samples, read the prestine ones instead 823 if (index >= s->fixedPos && index < s->fixedPos+MAX_RIGHT_TAPS && s->length > loopEnd && s->isFixed) 824 tmp32 = s->fixedSmp[index-s->fixedPos]; 825 else 826 tmp32 = ptr16[index]; 827 828 sample = (int8_t)((tmp32 * SAMPLE_AREA_HEIGHT) >> 16); 829 } 830 else // 8-bit 831 { 832 if (index >= s->fixedPos && index < s->fixedPos+MAX_RIGHT_TAPS && s->length > loopEnd && s->isFixed) 833 tmp32 = s->fixedSmp[index-s->fixedPos]; 834 else 835 tmp32 = s->dataPtr[index]; 836 837 sample = (int8_t)((tmp32 * SAMPLE_AREA_HEIGHT) >> 8); 838 } 839 840 return SAMPLE_AREA_Y_CENTER-sample; 841 } 842 843 void sampleLine(int32_t x1, int32_t x2, int32_t y1, int32_t y2) 844 { 845 int32_t d; 846 const int32_t dx = x2 - x1; 847 const int32_t ax = ABS(dx) * 2; 848 const int32_t sx = SGN(dx); 849 const int32_t dy = y2 - y1; 850 const int32_t ay = ABS(dy) * 2; 851 const int32_t sy = SGN(dy); 852 int32_t x = x1; 853 int32_t y = y1; 854 const uint32_t pal1 = video.palette[PAL_DESKTOP]; 855 const uint32_t pal2 = video.palette[PAL_FORGRND]; 856 const uint32_t pixVal = video.palette[PAL_PATTEXT]; 857 const int32_t pitch = sy * SCREEN_W; 858 uint32_t *dst32 = &video.frameBuffer[(y * SCREEN_W) + x]; 859 860 // draw line 861 if (ax > ay) 862 { 863 d = ay - (ax >> 1); 864 865 while (true) 866 { 867 // invert certain colors 868 if (*dst32 != pal2) 869 { 870 if (*dst32 == pal1) 871 *dst32 = pal2; 872 else 873 *dst32 = pixVal; 874 } 875 876 if (x == x2) 877 break; 878 879 if (d >= 0) 880 { 881 d -= ax; 882 dst32 += pitch; 883 } 884 885 x += sx; 886 d += ay; 887 dst32 += sx; 888 } 889 } 890 else 891 { 892 d = ax - (ay >> 1); 893 894 while (true) 895 { 896 // invert certain colors 897 if (*dst32 != pal2) 898 { 899 if (*dst32 == pal1) 900 *dst32 = pal2; 901 else 902 *dst32 = pixVal; 903 } 904 905 if (y == y2) 906 break; 907 908 if (d >= 0) 909 { 910 d -= ay; 911 dst32 += sx; 912 } 913 914 y += sy; 915 d += ax; 916 dst32 += pitch; 917 } 918 } 919 } 920 921 static void getMinMax16(const void *p, uint32_t scanLen, int16_t *min16, int16_t *max16) 922 { 923 #if defined _WIN32 || defined __amd64__ || (defined __i386__ && defined __SSE2__) 924 if (cpu.hasSSE2) 925 { 926 /* Taken with permission from the OpenMPT project (and slightly modified). 927 ** 928 ** SSE2 implementation for min/max finder, packs 8*int16 in a 128-bit XMM register. 929 ** scanLen = How many samples to process 930 */ 931 const int16_t *p16; 932 uint32_t scanLen8; 933 const __m128i *v; 934 __m128i minVal, maxVal, minVal2, maxVal2, curVals; 935 936 // Put minimum / maximum in 8 packed int16 values 937 minVal = _mm_set1_epi16(32767); 938 maxVal = _mm_set1_epi16(-32768); 939 940 scanLen8 = scanLen / 8; 941 if (scanLen8 > 0) 942 { 943 v = (__m128i *)p; 944 p = (const __m128i *)p + scanLen8; 945 946 while (scanLen8--) 947 { 948 curVals = _mm_loadu_si128(v++); 949 minVal = _mm_min_epi16(minVal, curVals); 950 maxVal = _mm_max_epi16(maxVal, curVals); 951 } 952 953 /* Now we have 8 minima and maxima each. 954 ** Move the upper 4 values to the lower half and compute the minima/maxima of that. */ 955 minVal2 = _mm_unpackhi_epi64(minVal, minVal); 956 maxVal2 = _mm_unpackhi_epi64(maxVal, maxVal); 957 minVal = _mm_min_epi16(minVal, minVal2); 958 maxVal = _mm_max_epi16(maxVal, maxVal2); 959 960 /* Now we have 4 minima and maxima each. 961 ** Move the upper 2 values to the lower half and compute the minima/maxima of that. */ 962 minVal2 = _mm_shuffle_epi32(minVal, _MM_SHUFFLE(1, 1, 1, 1)); 963 maxVal2 = _mm_shuffle_epi32(maxVal, _MM_SHUFFLE(1, 1, 1, 1)); 964 minVal = _mm_min_epi16(minVal, minVal2); 965 maxVal = _mm_max_epi16(maxVal, maxVal2); 966 967 // Compute the minima/maxima of the both remaining values 968 minVal2 = _mm_shufflelo_epi16(minVal, _MM_SHUFFLE(1, 1, 1, 1)); 969 maxVal2 = _mm_shufflelo_epi16(maxVal, _MM_SHUFFLE(1, 1, 1, 1)); 970 minVal = _mm_min_epi16(minVal, minVal2); 971 maxVal = _mm_max_epi16(maxVal, maxVal2); 972 } 973 974 p16 = (const int16_t *)p; 975 while (scanLen-- & 7) 976 { 977 curVals = _mm_set1_epi16(*p16++); 978 minVal = _mm_min_epi16(minVal, curVals); 979 maxVal = _mm_max_epi16(maxVal, curVals); 980 } 981 982 *min16 = (int16_t)_mm_cvtsi128_si32(minVal); 983 *max16 = (int16_t)_mm_cvtsi128_si32(maxVal); 984 } 985 else 986 #endif 987 { 988 // non-SSE version (really slow for big samples while zoomed out) 989 int16_t minVal = 32767; 990 int16_t maxVal = -32768; 991 992 const int16_t *ptr16 = (const int16_t *)p; 993 for (uint32_t i = 0; i < scanLen; i++) 994 { 995 const int16_t smp16 = ptr16[i]; 996 if (smp16 < minVal) minVal = smp16; 997 if (smp16 > maxVal) maxVal = smp16; 998 } 999 1000 *min16 = minVal; 1001 *max16 = maxVal; 1002 } 1003 } 1004 1005 static void getMinMax8(const void *p, uint32_t scanLen, int8_t *min8, int8_t *max8) 1006 { 1007 #if defined _WIN32 || defined __amd64__ || (defined __i386__ && defined __SSE2__) 1008 if (cpu.hasSSE2) 1009 { 1010 /* Taken with permission from the OpenMPT project (and slightly modified). 1011 ** 1012 ** SSE2 implementation for min/max finder, packs 16*int8 in a 128-bit XMM register. 1013 ** scanLen = How many samples to process 1014 */ 1015 const int8_t *p8; 1016 uint32_t scanLen16; 1017 const __m128i *v; 1018 __m128i xorVal, minVal, maxVal, minVal2, maxVal2, curVals; 1019 1020 // Put minimum / maximum in 8 packed int16 values (-1 and 0 because unsigned) 1021 minVal = _mm_set1_epi8(-1); 1022 maxVal = _mm_set1_epi8(0); 1023 1024 // For signed <-> unsigned conversion (_mm_min_epi8/_mm_max_epi8 is SSE4) 1025 xorVal = _mm_set1_epi8(0x80); 1026 1027 scanLen16 = scanLen / 16; 1028 if (scanLen16 > 0) 1029 { 1030 v = (__m128i *)p; 1031 p = (const __m128i *)p + scanLen16; 1032 1033 while (scanLen16--) 1034 { 1035 curVals = _mm_loadu_si128(v++); 1036 curVals = _mm_xor_si128(curVals, xorVal); 1037 minVal = _mm_min_epu8(minVal, curVals); 1038 maxVal = _mm_max_epu8(maxVal, curVals); 1039 } 1040 1041 /* Now we have 16 minima and maxima each. 1042 ** Move the upper 8 values to the lower half and compute the minima/maxima of that. */ 1043 minVal2 = _mm_unpackhi_epi64(minVal, minVal); 1044 maxVal2 = _mm_unpackhi_epi64(maxVal, maxVal); 1045 minVal = _mm_min_epu8(minVal, minVal2); 1046 maxVal = _mm_max_epu8(maxVal, maxVal2); 1047 1048 /* Now we have 8 minima and maxima each. 1049 ** Move the upper 4 values to the lower half and compute the minima/maxima of that. */ 1050 minVal2 = _mm_shuffle_epi32(minVal, _MM_SHUFFLE(1, 1, 1, 1)); 1051 maxVal2 = _mm_shuffle_epi32(maxVal, _MM_SHUFFLE(1, 1, 1, 1)); 1052 minVal = _mm_min_epu8(minVal, minVal2); 1053 maxVal = _mm_max_epu8(maxVal, maxVal2); 1054 1055 /* Now we have 4 minima and maxima each. 1056 ** Move the upper 2 values to the lower half and compute the minima/maxima of that. */ 1057 minVal2 = _mm_srai_epi32(minVal, 16); 1058 maxVal2 = _mm_srai_epi32(maxVal, 16); 1059 minVal = _mm_min_epu8(minVal, minVal2); 1060 maxVal = _mm_max_epu8(maxVal, maxVal2); 1061 1062 // Compute the minima/maxima of the both remaining values 1063 minVal2 = _mm_srai_epi16(minVal, 8); 1064 maxVal2 = _mm_srai_epi16(maxVal, 8); 1065 minVal = _mm_min_epu8(minVal, minVal2); 1066 maxVal = _mm_max_epu8(maxVal, maxVal2); 1067 } 1068 1069 p8 = (const int8_t *)p; 1070 while (scanLen-- & 15) 1071 { 1072 curVals = _mm_set1_epi8(*p8++ ^ 0x80); 1073 minVal = _mm_min_epu8(minVal, curVals); 1074 maxVal = _mm_max_epu8(maxVal, curVals); 1075 } 1076 1077 *min8 = (int8_t)(_mm_cvtsi128_si32(minVal) ^ 0x80); 1078 *max8 = (int8_t)(_mm_cvtsi128_si32(maxVal) ^ 0x80); 1079 } 1080 else 1081 #endif 1082 { 1083 // non-SSE version (really slow for big samples while zoomed out) 1084 int8_t minVal = 127; 1085 int8_t maxVal = -128; 1086 1087 const int8_t *ptr8 = (const int8_t *)p; 1088 for (uint32_t i = 0; i < scanLen; i++) 1089 { 1090 const int8_t smp8 = ptr8[i]; 1091 if (smp8 < minVal) minVal = smp8; 1092 if (smp8 > maxVal) maxVal = smp8; 1093 } 1094 1095 *min8 = minVal; 1096 *max8 = maxVal; 1097 } 1098 } 1099 1100 // for scanning sample data peak where loopEnd+MAX_RIGHT_TAPS is within scan range (fixed interpolation tap samples) 1101 static void getSpecialMinMax16(sample_t *s, int32_t index, int32_t scanEnd, int16_t *min16, int16_t *max16) 1102 { 1103 int16_t minVal2, maxVal2; 1104 1105 const int16_t *ptr16 = (const int16_t *)s->dataPtr; 1106 1107 int16_t minVal = 32767; 1108 int16_t maxVal = -32768; 1109 1110 // read samples before fixed samples (if needed) 1111 if (index < s->fixedPos) 1112 { 1113 getMinMax16(&ptr16[index], s->fixedPos-index, &minVal, &maxVal); 1114 index = s->fixedPos; 1115 } 1116 1117 // read fixed samples (we are guaranteed to be within the fixed samples here) 1118 const int32_t tapIndex = index-s->fixedPos; 1119 const int32_t scanLength = MAX_RIGHT_TAPS-tapIndex; 1120 1121 int32_t tmpScanEnd = index+scanLength; 1122 if (tmpScanEnd > scanEnd) 1123 tmpScanEnd = scanEnd; 1124 1125 const int16_t *smpReadPtr = s->fixedSmp + tapIndex; 1126 for (; index < tmpScanEnd; index++) 1127 { 1128 const int16_t smp16 = *smpReadPtr++; 1129 if (smp16 < minVal) minVal = smp16; 1130 if (smp16 > maxVal) maxVal = smp16; 1131 } 1132 1133 // read samples after fixed samples (if needed) 1134 if (index < scanEnd) 1135 { 1136 getMinMax16(&ptr16[index], scanEnd-index, &minVal2, &maxVal2); 1137 if (minVal2 < minVal) minVal = minVal2; 1138 if (maxVal2 > maxVal) maxVal = maxVal2; 1139 } 1140 1141 *min16 = minVal; 1142 *max16 = maxVal; 1143 } 1144 1145 // for scanning sample data peak where loopEnd+MAX_RIGHT_TAPS is within scan range (fixed interpolation tap samples) 1146 static void getSpecialMinMax8(sample_t *s, int32_t index, int32_t scanEnd, int8_t *min8, int8_t *max8) 1147 { 1148 int8_t minVal2, maxVal2; 1149 1150 const int8_t *ptr8 = (const int8_t *)s->dataPtr; 1151 1152 int8_t minVal = 127; 1153 int8_t maxVal = -128; 1154 1155 // read samples before fixed samples (if needed) 1156 if (index < s->fixedPos) 1157 { 1158 getMinMax8(&ptr8[index], s->fixedPos-index, &minVal, &maxVal); 1159 index = s->fixedPos; 1160 } 1161 1162 // read fixed samples (we are guaranteed to be within the fixed samples here) 1163 const int32_t tapIndex = index-s->fixedPos; 1164 const int32_t scanLength = MAX_RIGHT_TAPS-tapIndex; 1165 1166 int32_t tmpScanEnd = index+scanLength; 1167 if (tmpScanEnd > scanEnd) 1168 tmpScanEnd = scanEnd; 1169 1170 const int16_t *smpReadPtr = (const int16_t *)s->fixedSmp + tapIndex; 1171 for (; index < tmpScanEnd; index++) 1172 { 1173 const int8_t smp8 = (int8_t)(*smpReadPtr++); 1174 if (smp8 < minVal) minVal = smp8; 1175 if (smp8 > maxVal) maxVal = smp8; 1176 } 1177 1178 // read samples after fixed samples (if needed) 1179 if (index < scanEnd) 1180 { 1181 getMinMax8(&ptr8[index], scanEnd-index, &minVal2, &maxVal2); 1182 if (minVal2 < minVal) minVal = minVal2; 1183 if (maxVal2 > maxVal) maxVal = maxVal2; 1184 } 1185 1186 *min8 = minVal; 1187 *max8 = maxVal; 1188 } 1189 1190 static void getSampleDataPeak(sample_t *s, int32_t index, int32_t length, int16_t *outMin, int16_t *outMax) 1191 { 1192 int8_t min8, max8; 1193 int16_t min16, max16; 1194 1195 if (length == 0 || s->dataPtr == NULL || s->length <= 0) 1196 { 1197 *outMin = SAMPLE_AREA_Y_CENTER; 1198 *outMax = SAMPLE_AREA_Y_CENTER; 1199 return; 1200 } 1201 1202 if (s->isFixed && s->length > s->loopLength+s->loopStart) 1203 { 1204 const int32_t scanEnd = index + length; 1205 1206 /* If the scan area is including the fixed samples (for branchless mixer interpolation), 1207 ** do a special procedure to scan the original non-touched samples when needed. 1208 */ 1209 const bool insideRange = index >= s->fixedPos && index < s->fixedPos+MAX_RIGHT_TAPS; 1210 if (insideRange || (index < s->fixedPos && scanEnd >= s->fixedPos)) 1211 { 1212 if (s->flags & SAMPLE_16BIT) 1213 { 1214 getSpecialMinMax16(s, index, scanEnd, &min16, &max16); 1215 *outMin = SAMPLE_AREA_Y_CENTER - ((min16 * SAMPLE_AREA_HEIGHT) >> 16); 1216 *outMax = SAMPLE_AREA_Y_CENTER - ((max16 * SAMPLE_AREA_HEIGHT) >> 16); 1217 } 1218 else // 8-bit 1219 { 1220 getSpecialMinMax8(s, index, scanEnd, &min8, &max8); 1221 *outMin = SAMPLE_AREA_Y_CENTER - ((min8 * SAMPLE_AREA_HEIGHT) >> 8); 1222 *outMax = SAMPLE_AREA_Y_CENTER - ((max8 * SAMPLE_AREA_HEIGHT) >> 8); 1223 } 1224 1225 return; 1226 } 1227 } 1228 1229 if (s->flags & SAMPLE_16BIT) 1230 { 1231 const int16_t *smpPtr16 = (int16_t *)s->dataPtr; 1232 getMinMax16(&smpPtr16[index], length, &min16, &max16); 1233 *outMin = SAMPLE_AREA_Y_CENTER - ((min16 * SAMPLE_AREA_HEIGHT) >> 16); 1234 *outMax = SAMPLE_AREA_Y_CENTER - ((max16 * SAMPLE_AREA_HEIGHT) >> 16); 1235 } 1236 else // 8-bit 1237 { 1238 getMinMax8(&s->dataPtr[index], length, &min8, &max8); 1239 *outMin = SAMPLE_AREA_Y_CENTER - ((min8 * SAMPLE_AREA_HEIGHT) >> 8); 1240 *outMax = SAMPLE_AREA_Y_CENTER - ((max8 * SAMPLE_AREA_HEIGHT) >> 8); 1241 } 1242 } 1243 1244 static void writeWaveform(void) 1245 { 1246 // clear sample data area 1247 memset(&video.frameBuffer[174 * SCREEN_W], 0, SAMPLE_AREA_WIDTH * SAMPLE_AREA_HEIGHT * sizeof (int32_t)); 1248 1249 // draw center line 1250 hLine(0, SAMPLE_AREA_Y_CENTER, SAMPLE_AREA_WIDTH, PAL_DESKTOP); 1251 1252 if (instr[editor.curInstr] == NULL || smpEd_ViewSize == 0) 1253 return; 1254 1255 sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; 1256 if (s->dataPtr == NULL || s->length <= 0) 1257 return; 1258 1259 if (smpEd_ViewSize <= SAMPLE_AREA_WIDTH) // zoomed in (or 1:1) 1260 { 1261 for (int32_t x = 0; x <= SAMPLE_AREA_WIDTH; x++) 1262 { 1263 int32_t currSmpPos = scr2SmpPos(x+0); 1264 int32_t nextSmpPos = scr2SmpPos(x+1); 1265 1266 if (currSmpPos >= s->length) currSmpPos = s->length-1; 1267 if (nextSmpPos >= s->length) nextSmpPos = s->length-1; 1268 1269 int32_t x1 = smpPos2Scr(currSmpPos); 1270 int32_t x2 = smpPos2Scr(nextSmpPos); 1271 int32_t y1 = getScaledSample(s, currSmpPos); 1272 int32_t y2 = getScaledSample(s, nextSmpPos); 1273 1274 x1 = CLAMP(x1, 0, SAMPLE_AREA_WIDTH-1); 1275 x2 = CLAMP(x2, 0, SAMPLE_AREA_WIDTH-1); 1276 1277 // kludge: sometimes the last point wouldn't reach the end of the sample window 1278 if (x == SAMPLE_AREA_WIDTH) 1279 x2 = SAMPLE_AREA_WIDTH-1; 1280 1281 sampleLine(x1, x2, y1, y2); 1282 } 1283 } 1284 else // zoomed out 1285 { 1286 const int32_t firstSamplePoint = getScaledSample(s, scr2SmpPos(0)); 1287 1288 int32_t oldMin = firstSamplePoint; 1289 int32_t oldMax = firstSamplePoint; 1290 1291 for (int16_t x = 0; x < SAMPLE_AREA_WIDTH; x++) 1292 { 1293 int32_t smpIdx = scr2SmpPos(x+0); 1294 int32_t smpNum = scr2SmpPos(x+1) - smpIdx; 1295 1296 // prevent look-up overflow (yes, this can happen near the end of the sample) 1297 if (smpIdx+smpNum > s->length) 1298 smpNum = s->length - smpIdx; 1299 1300 if (smpNum > 0) 1301 { 1302 int16_t min, max; 1303 getSampleDataPeak(s, smpIdx, smpNum, &min, &max); 1304 1305 if (x != 0) 1306 { 1307 if (min > oldMax) sampleLine(x-1, x, oldMax, min); 1308 if (max < oldMin) sampleLine(x-1, x, oldMin, max); 1309 } 1310 1311 sampleLine(x, x, max, min); 1312 1313 oldMin = min; 1314 oldMax = max; 1315 } 1316 } 1317 } 1318 } 1319 1320 void writeSample(bool forceSmpRedraw) 1321 { 1322 int32_t tmpRx1, tmpRx2; 1323 sample_t *s; 1324 1325 // update sample loop points for visuals 1326 1327 if (instr[editor.curInstr] == NULL) 1328 s = &instr[0]->smp[0]; 1329 else 1330 s = &instr[editor.curInstr]->smp[editor.curSmp]; 1331 1332 curSmpLoopStart = s->loopStart; 1333 curSmpLoopLength = s->loopLength; 1334 1335 // exchange range variables if x1 is after x2 1336 if (smpEd_Rx1 > smpEd_Rx2) 1337 { 1338 tmpRx2 = smpEd_Rx2; 1339 smpEd_Rx2 = smpEd_Rx1; 1340 smpEd_Rx1 = tmpRx2; 1341 } 1342 1343 // clamp range 1344 smpEd_Rx1 = CLAMP(smpEd_Rx1, 0, s->length); 1345 smpEd_Rx2 = CLAMP(smpEd_Rx2, 0, s->length); 1346 1347 // sanitize sample scroll position 1348 if (smpEd_ScrPos+smpEd_ViewSize > s->length) 1349 { 1350 smpEd_ScrPos = s->length - smpEd_ViewSize; 1351 updateScrPos(); 1352 } 1353 1354 if (smpEd_ScrPos < 0) 1355 { 1356 smpEd_ScrPos = 0; 1357 updateScrPos(); 1358 1359 if (smpEd_ViewSize > s->length) 1360 { 1361 smpEd_ViewSize = s->length; 1362 updateViewSize(); 1363 } 1364 } 1365 1366 // handle updating 1367 if (ui.sampleEditorShown) 1368 { 1369 // check if we need to redraw sample data 1370 if (forceSmpRedraw || (old_SmpScrPos != smpEd_ScrPos || old_ViewSize != smpEd_ViewSize)) 1371 { 1372 if (ui.sampleEditorShown) 1373 writeWaveform(); 1374 1375 old_SmpScrPos = smpEd_ScrPos; 1376 old_ViewSize = smpEd_ViewSize; 1377 1378 if (ui.sampleEditorShown) 1379 writeRange(); // range was overwritten, draw it again 1380 1381 smpEd_OldSmpPosLine = -1; 1382 1383 old_Rx1 = smpEd_Rx1; 1384 old_Rx2 = smpEd_Rx2; 1385 } 1386 1387 // check if we need to write new range 1388 if (old_Rx1 != smpEd_Rx1 || old_Rx2 != smpEd_Rx2) 1389 { 1390 tmpRx1 = smpEd_Rx1; 1391 tmpRx2 = smpEd_Rx2; 1392 1393 // remove old range 1394 smpEd_Rx1 = old_Rx1; 1395 smpEd_Rx2 = old_Rx2; 1396 1397 if (ui.sampleEditorShown) 1398 writeRange(); 1399 1400 // write new range 1401 smpEd_Rx1 = tmpRx1; 1402 smpEd_Rx2 = tmpRx2; 1403 1404 if (ui.sampleEditorShown) 1405 writeRange(); 1406 1407 old_Rx1 = smpEd_Rx1; 1408 old_Rx2 = smpEd_Rx2; 1409 } 1410 1411 fixLoopGadgets(); 1412 } 1413 1414 if (ui.sampleEditorShown) 1415 fixSampleScrollbar(); 1416 1417 updateSampleEditor(); 1418 } 1419 1420 static void setSampleRange(int32_t start, int32_t end) 1421 { 1422 if (instr[editor.curInstr] == NULL) 1423 { 1424 smpEd_Rx1 = 0; 1425 smpEd_Rx2 = 0; 1426 return; 1427 } 1428 1429 if (start < 0) 1430 start = 0; 1431 1432 if (end < 0) 1433 end = 0; 1434 1435 smpEd_Rx1 = scr2SmpPos(start); 1436 smpEd_Rx2 = scr2SmpPos(end); 1437 } 1438 1439 void updateSampleEditorSample(void) 1440 { 1441 smpEd_Rx1 = smpEd_Rx2 = 0; 1442 1443 smpEd_ScrPos = 0; 1444 updateScrPos(); 1445 1446 if (instr[editor.curInstr] == NULL) 1447 smpEd_ViewSize = 0; 1448 else 1449 smpEd_ViewSize = instr[editor.curInstr]->smp[editor.curSmp].length; 1450 1451 updateViewSize(); 1452 1453 writeSample(true); 1454 } 1455 1456 void updateSampleEditor(void) 1457 { 1458 char noteChar1, noteChar2; 1459 uint8_t flags; 1460 int32_t sampleLength; 1461 1462 if (!ui.sampleEditorShown) 1463 return; 1464 1465 if (instr[editor.curInstr] == NULL) 1466 { 1467 flags = 0; 1468 sampleLength = 0; 1469 } 1470 else 1471 { 1472 flags = instr[editor.curInstr]->smp[editor.curSmp].flags; 1473 sampleLength = instr[editor.curInstr]->smp[editor.curSmp].length; 1474 } 1475 1476 // sample bit depth radio buttons 1477 uncheckRadioButtonGroup(RB_GROUP_SAMPLE_DEPTH); 1478 if (flags & SAMPLE_16BIT) 1479 radioButtons[RB_SAMPLE_16BIT].state = RADIOBUTTON_CHECKED; 1480 else 1481 radioButtons[RB_SAMPLE_8BIT].state = RADIOBUTTON_CHECKED; 1482 showRadioButtonGroup(RB_GROUP_SAMPLE_DEPTH); 1483 1484 // sample loop radio buttons 1485 uncheckRadioButtonGroup(RB_GROUP_SAMPLE_LOOP); 1486 1487 uint8_t loopType = GET_LOOPTYPE(flags); 1488 if (loopType == LOOP_OFF) 1489 radioButtons[RB_SAMPLE_NO_LOOP].state = RADIOBUTTON_CHECKED; 1490 else if (loopType == LOOP_FWD) 1491 radioButtons[RB_SAMPLE_FORWARD_LOOP].state = RADIOBUTTON_CHECKED; 1492 else 1493 radioButtons[RB_SAMPLE_PINGPONG_LOOP].state = RADIOBUTTON_CHECKED; 1494 1495 showRadioButtonGroup(RB_GROUP_SAMPLE_LOOP); 1496 1497 if (!ui.sampleEditorEffectsShown) 1498 { 1499 // draw sample play note 1500 1501 const uint32_t noteNr = editor.smpEd_NoteNr - 1; 1502 1503 const uint32_t note = noteNr % 12; 1504 const uint32_t octave = noteNr / 12; 1505 1506 if (config.ptnAcc == 0) 1507 { 1508 noteChar1 = sharpNote1Char[note]; 1509 noteChar2 = sharpNote2Char[note]; 1510 } 1511 else 1512 { 1513 noteChar1 = flatNote1Char[note]; 1514 noteChar2 = flatNote2Char[note]; 1515 } 1516 1517 charOutBg(7, 369, PAL_FORGRND, PAL_BCKGRND, noteChar1); 1518 charOutBg(15, 369, PAL_FORGRND, PAL_BCKGRND, noteChar2); 1519 charOutBg(23, 369, PAL_FORGRND, PAL_BCKGRND, (char)('0' + octave)); 1520 } 1521 1522 // draw sample display/length 1523 1524 hexOutBg(536, 350, PAL_FORGRND, PAL_DESKTOP, smpEd_ViewSize, 8); 1525 hexOutBg(536, 362, PAL_FORGRND, PAL_DESKTOP, sampleLength, 8); 1526 } 1527 1528 void sampPlayNoteUp(void) 1529 { 1530 if (editor.smpEd_NoteNr < 96) 1531 { 1532 editor.smpEd_NoteNr++; 1533 updateSampleEditor(); 1534 } 1535 } 1536 1537 void sampPlayNoteDown(void) 1538 { 1539 if (editor.smpEd_NoteNr > 1) 1540 { 1541 editor.smpEd_NoteNr--; 1542 updateSampleEditor(); 1543 } 1544 } 1545 1546 void scrollSampleDataLeft(void) 1547 { 1548 int32_t sampleLen; 1549 1550 if (instr[editor.curInstr] == NULL) 1551 sampleLen = 0; 1552 else 1553 sampleLen = instr[editor.curInstr]->smp[editor.curSmp].length; 1554 1555 if (smpEd_ViewSize == 0 || smpEd_ViewSize == sampleLen) 1556 return; 1557 1558 int32_t scrollAmount = (uint32_t)smpEd_ViewSize / 32; 1559 if (scrollAmount < 1) 1560 scrollAmount = 1; 1561 1562 smpEd_ScrPos -= scrollAmount; 1563 if (smpEd_ScrPos < 0) 1564 smpEd_ScrPos = 0; 1565 1566 updateScrPos(); 1567 } 1568 1569 void scrollSampleDataRight(void) 1570 { 1571 int32_t sampleLen; 1572 1573 if (instr[editor.curInstr] == NULL) 1574 sampleLen = 0; 1575 else 1576 sampleLen = instr[editor.curInstr]->smp[editor.curSmp].length; 1577 1578 if (smpEd_ViewSize == 0 || smpEd_ViewSize == sampleLen) 1579 return; 1580 1581 int32_t scrollAmount = (uint32_t)smpEd_ViewSize / 32; 1582 if (scrollAmount < 1) 1583 scrollAmount = 1; 1584 1585 smpEd_ScrPos += scrollAmount; 1586 if (smpEd_ScrPos+smpEd_ViewSize > sampleLen) 1587 smpEd_ScrPos = sampleLen - smpEd_ViewSize; 1588 1589 updateScrPos(); 1590 } 1591 1592 void scrollSampleData(uint32_t pos) 1593 { 1594 int32_t sampleLen; 1595 1596 if (instr[editor.curInstr] == NULL) 1597 sampleLen = 0; 1598 else 1599 sampleLen = instr[editor.curInstr]->smp[editor.curSmp].length; 1600 1601 if (smpEd_ViewSize == 0 || smpEd_ViewSize == sampleLen) 1602 return; 1603 1604 smpEd_ScrPos = pos; 1605 updateScrPos(); 1606 } 1607 1608 void sampPlayWave(void) 1609 { 1610 playSample(cursor.ch, editor.curInstr, editor.curSmp, editor.smpEd_NoteNr, 0, 0); 1611 } 1612 1613 void sampPlayDisplay(void) 1614 { 1615 playRange(cursor.ch, editor.curInstr, editor.curSmp, editor.smpEd_NoteNr, 0, 0, smpEd_ScrPos, smpEd_ViewSize); 1616 } 1617 1618 void sampPlayRange(void) 1619 { 1620 playRange(cursor.ch, editor.curInstr, editor.curSmp, editor.smpEd_NoteNr, 0, 0, smpEd_Rx1, smpEd_Rx2 - smpEd_Rx1); 1621 } 1622 1623 void showRange(void) 1624 { 1625 if (editor.curInstr == 0 || instr[editor.curInstr] == NULL) 1626 return; 1627 1628 sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; 1629 if (s->dataPtr == NULL) 1630 return; 1631 1632 if (smpEd_Rx1 < smpEd_Rx2) 1633 { 1634 smpEd_ViewSize = smpEd_Rx2 - smpEd_Rx1; 1635 if (smpEd_ViewSize < 2) 1636 smpEd_ViewSize = 2; 1637 1638 updateViewSize(); 1639 1640 smpEd_ScrPos = smpEd_Rx1; 1641 updateScrPos(); 1642 } 1643 else 1644 { 1645 okBox(0, "System message", "Cannot show empty range!", NULL); 1646 } 1647 } 1648 1649 void rangeAll(void) 1650 { 1651 if (editor.curInstr == 0 || 1652 instr[editor.curInstr] == NULL || 1653 instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) 1654 { 1655 return; 1656 } 1657 1658 smpEd_Rx1 = smpEd_ScrPos; 1659 smpEd_Rx2 = smpEd_ScrPos + smpEd_ViewSize; 1660 } 1661 1662 static void zoomSampleDataIn(int32_t step, int32_t x) 1663 { 1664 if (editor.curInstr == 0 || 1665 instr[editor.curInstr] == NULL || 1666 instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) 1667 { 1668 return; 1669 } 1670 1671 sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; 1672 1673 if (old_ViewSize <= 2) 1674 return; 1675 1676 if (step < 1) 1677 step = 1; 1678 1679 smpEd_ViewSize = old_ViewSize - (step * 2); 1680 if (smpEd_ViewSize < 2) 1681 smpEd_ViewSize = 2; 1682 1683 updateViewSize(); 1684 1685 int32_t tmp32 = (x - (SAMPLE_AREA_WIDTH / 2)) * step; 1686 tmp32 += SAMPLE_AREA_WIDTH/4; // rounding bias 1687 tmp32 /= SAMPLE_AREA_WIDTH/2; 1688 1689 step += tmp32; 1690 1691 int64_t newScrPos64 = old_SmpScrPos + step; 1692 if (newScrPos64+smpEd_ViewSize > s->length) 1693 newScrPos64 = s->length - smpEd_ViewSize; 1694 1695 smpEd_ScrPos = (uint32_t)newScrPos64; 1696 updateScrPos(); 1697 } 1698 1699 static void zoomSampleDataOut(int32_t step, int32_t x) 1700 { 1701 if (editor.curInstr == 0 || 1702 instr[editor.curInstr] == NULL || 1703 instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) 1704 { 1705 return; 1706 } 1707 1708 sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; 1709 if (old_ViewSize == s->length) 1710 return; 1711 1712 if (step < 1) 1713 step = 1; 1714 1715 int64_t newViewSize64 = (int64_t)old_ViewSize + (step * 2); 1716 if (newViewSize64 > s->length) 1717 { 1718 smpEd_ViewSize = s->length; 1719 smpEd_ScrPos = 0; 1720 } 1721 else 1722 { 1723 int32_t tmp32 = (x - (SAMPLE_AREA_WIDTH / 2)) * step; 1724 tmp32 += SAMPLE_AREA_WIDTH/4; // rounding bias 1725 tmp32 /= SAMPLE_AREA_WIDTH/2; 1726 1727 step += tmp32; 1728 1729 smpEd_ViewSize = newViewSize64 & 0xFFFFFFFF; 1730 1731 smpEd_ScrPos = old_SmpScrPos - step; 1732 if (smpEd_ScrPos < 0) 1733 smpEd_ScrPos = 0; 1734 1735 if (smpEd_ScrPos+smpEd_ViewSize > s->length) 1736 smpEd_ScrPos = s->length - smpEd_ViewSize; 1737 } 1738 1739 updateViewSize(); 1740 updateScrPos(); 1741 } 1742 1743 void mouseZoomSampleDataIn(void) 1744 { 1745 if (editor.curInstr == 0 || 1746 instr[editor.curInstr] == NULL || 1747 instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) 1748 { 1749 return; 1750 } 1751 1752 zoomSampleDataIn((old_ViewSize+5) / 10, mouse.x); 1753 } 1754 1755 void mouseZoomSampleDataOut(void) 1756 { 1757 if (editor.curInstr == 0 || 1758 instr[editor.curInstr] == NULL || 1759 instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) 1760 { 1761 return; 1762 } 1763 1764 zoomSampleDataOut((old_ViewSize+5) / 10, mouse.x); 1765 } 1766 1767 void zoomOut(void) 1768 { 1769 if (editor.curInstr == 0 || 1770 instr[editor.curInstr] == NULL || 1771 instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) 1772 { 1773 return; 1774 } 1775 1776 sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; 1777 if (old_ViewSize == s->length) 1778 return; 1779 1780 int32_t tmp32 = old_ViewSize; 1781 tmp32++; // rounding bias 1782 tmp32 >>= 1; 1783 1784 smpEd_ScrPos = old_SmpScrPos - tmp32; 1785 if (smpEd_ScrPos < 0) 1786 smpEd_ScrPos = 0; 1787 1788 smpEd_ViewSize = old_ViewSize * 2; 1789 if (smpEd_ViewSize < old_ViewSize) 1790 { 1791 smpEd_ViewSize = s->length; 1792 smpEd_ScrPos = 0; 1793 } 1794 else if (smpEd_ViewSize+smpEd_ScrPos > s->length) 1795 { 1796 smpEd_ViewSize = s->length - smpEd_ScrPos; 1797 } 1798 1799 updateViewSize(); 1800 updateScrPos(); 1801 } 1802 1803 void showAll(void) 1804 { 1805 if (editor.curInstr == 0 || 1806 instr[editor.curInstr] == NULL || 1807 instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) 1808 { 1809 return; 1810 } 1811 1812 smpEd_ScrPos = 0; 1813 updateScrPos(); 1814 1815 smpEd_ViewSize = instr[editor.curInstr]->smp[editor.curSmp].length; 1816 updateViewSize(); 1817 } 1818 1819 void saveRange(void) 1820 { 1821 if (editor.curInstr == 0 || 1822 instr[editor.curInstr] == NULL || 1823 instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) 1824 { 1825 return; 1826 } 1827 1828 if (smpEd_Rx1 == smpEd_Rx2) 1829 { 1830 okBox(0, "System message", "No range specified!", NULL); 1831 return; 1832 } 1833 1834 smpEd_SysReqText[0] = '\0'; 1835 if (inputBox(1, "Enter filename:", smpEd_SysReqText, sizeof (smpEd_SysReqText) - 1) != 1) 1836 return; 1837 1838 if (smpEd_SysReqText[0] == '\0') 1839 { 1840 okBox(0, "System message", "Filename can't be empty!", NULL); 1841 return; 1842 } 1843 1844 if (smpEd_SysReqText[0] == '.') 1845 { 1846 okBox(0, "System message", "The very first character in the filename can't be '.' (dot)!", NULL); 1847 return; 1848 } 1849 1850 if (strpbrk(smpEd_SysReqText, "\\/:*?\"<>|") != NULL) 1851 { 1852 okBox(0, "System message", "The filename can't contain the following characters: \\ / : * ? \" < > |", NULL); 1853 return; 1854 } 1855 1856 switch (editor.sampleSaveMode) 1857 { 1858 case SMP_SAVE_MODE_RAW: changeFilenameExt(smpEd_SysReqText, ".raw", sizeof (smpEd_SysReqText) - 1); break; 1859 case SMP_SAVE_MODE_IFF: changeFilenameExt(smpEd_SysReqText, ".iff", sizeof (smpEd_SysReqText) - 1); break; 1860 default: case SMP_SAVE_MODE_WAV: changeFilenameExt(smpEd_SysReqText, ".wav", sizeof (smpEd_SysReqText) - 1); break; 1861 } 1862 1863 UNICHAR *filenameU = cp850ToUnichar(smpEd_SysReqText); 1864 if (filenameU == NULL) 1865 { 1866 okBox(0, "System message", "Not enough memory!", NULL); 1867 return; 1868 } 1869 1870 if (fileExistsAnsi(smpEd_SysReqText)) 1871 { 1872 char buf[256]; 1873 createFileOverwriteText(smpEd_SysReqText, buf); 1874 if (okBox(2, "System request", buf, NULL) != 1) 1875 return; 1876 } 1877 1878 saveSample(filenameU, SAVE_RANGE); 1879 free(filenameU); 1880 } 1881 1882 static bool cutRange(bool cropMode, int32_t r1, int32_t r2) 1883 { 1884 sample_t *s = getCurSample(); 1885 if (s == NULL) 1886 return false; 1887 1888 bool sample16Bit = !!(s->flags & SAMPLE_16BIT); 1889 1890 if (!cropMode) 1891 { 1892 if (editor.curInstr == 0 || s->dataPtr == NULL || s->length == 0) 1893 return false; 1894 1895 pauseAudio(); 1896 unfixSample(s); 1897 1898 if (config.smpCutToBuffer) 1899 { 1900 if (!getCopyBuffer(r2-r1, sample16Bit)) 1901 { 1902 fixSample(s); 1903 resumeAudio(); 1904 1905 okBoxThreadSafe(0, "System message", "Not enough memory!", NULL); 1906 return false; 1907 } 1908 1909 memcpy(smpCopyBuff, &s->dataPtr[r1 << sample16Bit], (r2-r1) << sample16Bit); 1910 smpCopyBits = sample16Bit ? 16 : 8; 1911 } 1912 } 1913 1914 memmove(&s->dataPtr[r1 << sample16Bit], &s->dataPtr[r2 << sample16Bit], (s->length-r2) << sample16Bit); 1915 1916 int32_t length = s->length - r2+r1; 1917 if (length > 0) 1918 { 1919 if (!reallocateSmpData(s, length, sample16Bit)) 1920 { 1921 freeSample(editor.curInstr, editor.curSmp); 1922 editor.updateCurSmp = true; 1923 1924 if (!cropMode) 1925 resumeAudio(); 1926 1927 okBoxThreadSafe(0, "System message", "Not enough memory!", NULL); 1928 return false; 1929 } 1930 1931 s->length = length; 1932 1933 int32_t loopEnd = s->loopStart + s->loopLength; 1934 if (s->loopStart > r1) 1935 { 1936 s->loopStart -= r2-r1; 1937 if (s->loopStart < r1) 1938 s->loopStart = r1; 1939 } 1940 1941 if (loopEnd > r1) 1942 { 1943 loopEnd -= r2-r1; 1944 if (loopEnd < r1) 1945 loopEnd = r1; 1946 } 1947 1948 s->loopLength = loopEnd - s->loopStart; 1949 if (s->loopLength < 0) 1950 s->loopLength = 0; 1951 1952 if (s->loopStart+s->loopLength > length) 1953 s->loopLength = length - s->loopStart; 1954 1955 if (s->loopLength <= 0) 1956 { 1957 s->loopStart = 0; 1958 DISABLE_LOOP(s->flags); 1959 } 1960 1961 if (!cropMode) 1962 fixSample(s); 1963 } 1964 else 1965 { 1966 freeSample(editor.curInstr, editor.curSmp); 1967 editor.updateCurSmp = true; 1968 } 1969 1970 if (!cropMode) 1971 { 1972 resumeAudio(); 1973 setSongModifiedFlag(); 1974 1975 setMouseBusy(false); 1976 1977 smpEd_Rx2 = r1; 1978 writeSampleFlag = true; 1979 } 1980 1981 return true; 1982 } 1983 1984 static int32_t SDLCALL sampCutThread(void *ptr) 1985 { 1986 if (!cutRange(false, smpEd_Rx1, smpEd_Rx2)) 1987 okBoxThreadSafe(0, "System message", "Not enough memory! (Disable \"cut to buffer\")", NULL); 1988 else 1989 writeSampleFlag = true; 1990 1991 return true; 1992 1993 (void)ptr; 1994 } 1995 1996 void sampCut(void) 1997 { 1998 sample_t *s = getCurSample(); 1999 if (s == NULL || s->dataPtr == NULL || s->length <= 0 || smpEd_Rx2 == 0 || smpEd_Rx2 < smpEd_Rx1) 2000 return; 2001 2002 mouseAnimOn(); 2003 thread = SDL_CreateThread(sampCutThread, NULL, NULL); 2004 if (thread == NULL) 2005 { 2006 okBox(0, "System message", "Couldn't create thread!", NULL); 2007 return; 2008 } 2009 2010 SDL_DetachThread(thread); 2011 } 2012 2013 static int32_t SDLCALL sampCopyThread(void *ptr) 2014 { 2015 sample_t *s = getCurSample(); 2016 2017 bool sample16Bit = !!(s->flags & SAMPLE_16BIT); 2018 2019 if (!getCopyBuffer(smpEd_Rx2- smpEd_Rx1, sample16Bit)) 2020 { 2021 okBoxThreadSafe(0, "System message", "Not enough memory!", NULL); 2022 return true; 2023 } 2024 2025 unfixSample(s); 2026 memcpy(smpCopyBuff, &s->dataPtr[smpEd_Rx1 << sample16Bit], (smpEd_Rx2-smpEd_Rx1) << sample16Bit); 2027 fixSample(s); 2028 2029 setMouseBusy(false); 2030 2031 // copy sample information (in case we paste over an empty sample) 2032 if (smpEd_Rx1 == 0 && smpEd_Rx2 == s->length) 2033 { 2034 smpCopySample = *s; 2035 smpCopyDidCopyWholeSample = true; 2036 } 2037 2038 smpCopyBits = sample16Bit? 16 : 8; 2039 return true; 2040 2041 (void)ptr; 2042 } 2043 2044 void sampCopy(void) 2045 { 2046 sample_t *s = getCurSample(); 2047 if (s == NULL || s->dataPtr == NULL || s->length <= 0 || smpEd_Rx2 == 0 || smpEd_Rx2 < smpEd_Rx1) 2048 return; 2049 2050 mouseAnimOn(); 2051 thread = SDL_CreateThread(sampCopyThread, NULL, NULL); 2052 if (thread == NULL) 2053 { 2054 okBox(0, "System message", "Couldn't create thread!", NULL); 2055 return; 2056 } 2057 2058 SDL_DetachThread(thread); 2059 } 2060 2061 static void pasteOverwrite(sample_t *s) 2062 { 2063 bool sample16Bit = (smpCopyBits == 16); 2064 2065 if (!reallocateSmpData(s, smpCopySize, sample16Bit)) 2066 { 2067 okBoxThreadSafe(0, "System message", "Not enough memory!", NULL); 2068 return; 2069 } 2070 2071 pauseAudio(); 2072 2073 memcpy(s->dataPtr, smpCopyBuff, smpCopySize << sample16Bit); 2074 2075 if (smpCopyDidCopyWholeSample) 2076 { 2077 sample_t *src = &smpCopySample; 2078 memcpy(s->name, src->name, 23); 2079 s->length = src->length; 2080 s->loopStart = src->loopStart; 2081 s->loopLength = src->loopLength; 2082 s->volume = src->volume; 2083 s->panning = src->panning; 2084 s->finetune = src->finetune; 2085 s->relativeNote = src->relativeNote; 2086 s->flags = src->flags; 2087 } 2088 else 2089 { 2090 s->name[0] = '\0'; 2091 s->length = smpCopySize; 2092 s->loopStart = 0; 2093 s->loopLength = 0; 2094 s->volume = 64; 2095 s->panning = 128; 2096 s->finetune = 0; 2097 s->relativeNote = 0; 2098 s->flags = (smpCopyBits == 16) ? SAMPLE_16BIT : 0; 2099 } 2100 2101 s->isFixed = false; 2102 2103 fixSample(s); 2104 resumeAudio(); 2105 2106 editor.updateCurSmp = true; 2107 setSongModifiedFlag(); 2108 setMouseBusy(false); 2109 } 2110 2111 static void pasteCopiedData(int8_t *dataPtr, int32_t offset, int32_t length, bool sample16Bit) 2112 { 2113 if (sample16Bit) // destination sample is 16-bits 2114 { 2115 if (smpCopyBits == 16) 2116 { 2117 // src/dst bits are equal, do direct copy 2118 memcpy(&dataPtr[offset<<1], smpCopyBuff, length * sizeof (int16_t)); 2119 } 2120 else 2121 { 2122 // convert copied data to 16-bit then paste 2123 int16_t *ptr16 = (int16_t *)dataPtr + offset; 2124 for (int32_t i = 0; i < length; i++) 2125 ptr16[i] = smpCopyBuff[i] << 8; 2126 } 2127 } 2128 else // destination sample is 8-bits 2129 { 2130 if (smpCopyBits == 8) 2131 { 2132 // src/dst bits are equal, do direct copy 2133 memcpy(&dataPtr[offset], smpCopyBuff, length * sizeof (int8_t)); 2134 } 2135 else 2136 { 2137 // convert copied data to 8-bit then paste 2138 int8_t *ptr8 = (int8_t *)&dataPtr[offset]; 2139 int16_t *ptr16 = (int16_t *)smpCopyBuff; 2140 2141 for (int32_t i = 0; i < length; i++) 2142 ptr8[i] = ptr16[i] >> 8; 2143 } 2144 } 2145 } 2146 2147 static int32_t SDLCALL sampPasteThread(void *ptr) 2148 { 2149 smpPtr_t sp; 2150 2151 if (instr[editor.curInstr] == NULL && !allocateInstr(editor.curInstr)) 2152 { 2153 okBoxThreadSafe(0, "System message", "Not enough memory!", NULL); 2154 return true; 2155 } 2156 2157 sample_t *s = getCurSample(); 2158 if (smpEd_Rx2 == 0 || s == NULL || s->dataPtr == NULL) 2159 { 2160 pasteOverwrite(s); 2161 return true; 2162 } 2163 2164 bool sample16Bit = !!(s->flags & SAMPLE_16BIT); 2165 2166 if (s->length+smpCopySize > MAX_SAMPLE_LEN) 2167 { 2168 okBoxThreadSafe(0, "System message", "Not enough room in sample!", NULL); 2169 return true; 2170 } 2171 2172 int32_t newLength = s->length + smpCopySize - (smpEd_Rx2 - smpEd_Rx1); 2173 if (newLength <= 0) 2174 return true; 2175 2176 if (newLength > MAX_SAMPLE_LEN) 2177 { 2178 okBoxThreadSafe(0, "System message", "Not enough room in sample!", NULL); 2179 return true; 2180 } 2181 2182 if (!allocateSmpDataPtr(&sp, newLength, sample16Bit)) 2183 { 2184 okBoxThreadSafe(0, "System message", "Not enough memory!", NULL); 2185 return true; 2186 } 2187 2188 pauseAudio(); 2189 unfixSample(s); 2190 2191 // paste left part of original sample 2192 if (smpEd_Rx1 > 0) 2193 memcpy(sp.ptr, s->dataPtr, smpEd_Rx1 << sample16Bit); 2194 2195 // paste copied data 2196 pasteCopiedData(sp.ptr, smpEd_Rx1, smpCopySize, sample16Bit); 2197 2198 // paste right part of original sample 2199 if (smpEd_Rx2 < s->length) 2200 memmove(&sp.ptr[(smpEd_Rx1+smpCopySize) << sample16Bit], &s->dataPtr[smpEd_Rx2 << sample16Bit], (s->length-smpEd_Rx2) << sample16Bit); 2201 2202 freeSmpData(s); 2203 setSmpDataPtr(s, &sp); 2204 2205 // adjust loop points if necessary 2206 if (smpEd_Rx2-smpEd_Rx1 != smpCopySize) 2207 { 2208 int32_t loopAdjust = smpCopySize - (smpEd_Rx1 - smpEd_Rx2); 2209 2210 if (s->loopStart > smpEd_Rx2) 2211 { 2212 s->loopStart += loopAdjust; 2213 s->loopLength -= loopAdjust; 2214 } 2215 2216 if (s->loopStart+s->loopLength > smpEd_Rx2) 2217 s->loopLength += loopAdjust; 2218 2219 if (s->loopStart > newLength) 2220 { 2221 s->loopStart = 0; 2222 s->loopLength = 0; 2223 } 2224 2225 if (s->loopStart+s->loopLength > newLength) 2226 s->loopLength = newLength - s->loopStart; 2227 } 2228 2229 s->length = newLength; 2230 2231 fixSample(s); 2232 resumeAudio(); 2233 2234 setSongModifiedFlag(); 2235 setMouseBusy(false); 2236 2237 // set new range 2238 smpEd_Rx2 = smpEd_Rx1 + smpCopySize; 2239 2240 writeSampleFlag = true; 2241 return true; 2242 2243 (void)ptr; 2244 } 2245 2246 void sampPaste(void) 2247 { 2248 if (editor.curInstr == 0 || smpEd_Rx2 < smpEd_Rx1 || smpCopyBuff == NULL || smpCopySize == 0) 2249 return; 2250 2251 if (smpEd_Rx2 == 0) // no sample data marked, overwrite sample with copy buffer 2252 { 2253 sample_t *s = getCurSample(); 2254 if (s != NULL && s->dataPtr != NULL && s->length > 0) 2255 { 2256 if (okBox(2, "System request", "The current sample is not empty. Do you really want to overwrite it?", NULL) != 1) 2257 return; 2258 } 2259 } 2260 2261 mouseAnimOn(); 2262 thread = SDL_CreateThread(sampPasteThread, NULL, NULL); 2263 if (thread == NULL) 2264 { 2265 okBox(0, "System message", "Couldn't create thread!", NULL); 2266 return; 2267 } 2268 2269 SDL_DetachThread(thread); 2270 } 2271 2272 static int32_t SDLCALL sampCropThread(void *ptr) 2273 { 2274 sample_t *s = getCurSample(); 2275 2276 int32_t r1 = smpEd_Rx1; 2277 int32_t r2 = smpEd_Rx2; 2278 2279 pauseAudio(); 2280 unfixSample(s); 2281 2282 if (!cutRange(true, 0, r1) || !cutRange(true, r2-r1, s->length)) 2283 { 2284 fixSample(s); 2285 resumeAudio(); 2286 return true; 2287 } 2288 2289 fixSample(s); 2290 resumeAudio(); 2291 2292 r1 = 0; 2293 r2 = s->length; 2294 2295 setSongModifiedFlag(); 2296 setMouseBusy(false); 2297 2298 smpEd_Rx1 = r1; 2299 smpEd_Rx2 = r2; 2300 2301 writeSampleFlag = true; 2302 return true; 2303 2304 (void)ptr; 2305 } 2306 2307 void sampCrop(void) 2308 { 2309 sample_t *s = getCurSample(); 2310 if (s == NULL || s->dataPtr == NULL || s->length <= 0 || smpEd_Rx1 == smpEd_Rx2) 2311 return; 2312 2313 if (smpEd_Rx1 == 0 && smpEd_Rx2 == s->length) 2314 return; // nothing to crop (the whole sample is marked) 2315 2316 mouseAnimOn(); 2317 thread = SDL_CreateThread(sampCropThread, NULL, NULL); 2318 if (thread == NULL) 2319 { 2320 okBox(0, "System message", "Couldn't create thread!", NULL); 2321 return; 2322 } 2323 2324 SDL_DetachThread(thread); 2325 } 2326 2327 void sampXFade(void) 2328 { 2329 int32_t y1, y2, d1, d2, d3; 2330 2331 sample_t *s = getCurSample(); 2332 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 2333 return; 2334 2335 // check if the sample has the loop flag enabled 2336 if (GET_LOOPTYPE(s->flags) == LOOP_OFF) 2337 { 2338 okBox(0, "System message", "X-Fade can only be used on a loop-enabled sample!", NULL); 2339 return; 2340 } 2341 2342 // check if we selected a range 2343 if (smpEd_Rx2 == 0) 2344 { 2345 okBox(0, "System message", "No range selected! Make a small range that includes loop start or loop end.", NULL); 2346 return; 2347 } 2348 2349 // check if we selected a valid range length 2350 if (smpEd_Rx2-smpEd_Rx1 <= 2) 2351 { 2352 okBox(0, "System message", "Invalid range!", NULL); 2353 return; 2354 } 2355 2356 int32_t x1 = smpEd_Rx1; 2357 int32_t x2 = smpEd_Rx2; 2358 2359 bool sample16Bit = !!(s->flags & SAMPLE_16BIT); 2360 2361 if (GET_LOOPTYPE(s->flags) == LOOP_BIDI) 2362 { 2363 y1 = s->loopStart; 2364 if (x1 <= y1) // first loop point 2365 { 2366 if (x2 <= y1 || x2 >= s->loopStart+s->loopLength) 2367 { 2368 okBox(0, "System message", "Error: No loop point found inside marked data.", NULL); 2369 return; 2370 } 2371 2372 d1 = y1 - x1; 2373 if (x2-y1 > d1) 2374 d1 = x2 - y1; 2375 2376 d2 = y1 - x1; 2377 d3 = x2 - y1; 2378 2379 if (d1 < 1 || d2 < 1 || d3 < 1) 2380 { 2381 okBox(0, "System message", "Invalid range! Try to mark more data.", NULL); 2382 return; 2383 } 2384 2385 if (y1-d1 < 0 || y1+d1 >= s->length) 2386 { 2387 okBox(0, "System message", "Not enough sample data outside loop!", NULL); 2388 return; 2389 } 2390 2391 const double dD2Mul = 1.0 / d2; 2392 const double dD3Mul = 1.0 / d3; 2393 2394 pauseAudio(); 2395 unfixSample(s); 2396 2397 for (int32_t i = 0; i < d1; i++) 2398 { 2399 const int32_t aIdx = y1-i-1; 2400 const int32_t bIdx = y1+i; 2401 const double dI = i; 2402 2403 const double dA = getSampleValue(s->dataPtr, aIdx, sample16Bit); 2404 const double dB = getSampleValue(s->dataPtr, bIdx, sample16Bit); 2405 2406 if (i < d2) 2407 { 2408 const double dS1 = 1.0 - (dI * dD2Mul); 2409 const double dS2 = 2.0 - dS1; 2410 double dSample = (dA * dS2 + dB * dS1) / (dS1 + dS2); 2411 putSampleValue(s->dataPtr, aIdx, dSample, sample16Bit); 2412 } 2413 2414 if (i < d3) 2415 { 2416 const double dS1 = 1.0 - (dI * dD3Mul); 2417 const double dS2 = 2.0 - dS1; 2418 double dSample = (dB * dS2 + dA * dS1) / (dS1 + dS2); 2419 putSampleValue(s->dataPtr, bIdx, dSample, sample16Bit); 2420 } 2421 } 2422 2423 fixSample(s); 2424 resumeAudio(); 2425 } 2426 else // last loop point 2427 { 2428 y1 += s->loopLength; 2429 if (x1 >= y1 || x2 <= y1 || x2 >= s->length) 2430 { 2431 okBox(0, "System message", "Error: No loop point found inside marked data.", NULL); 2432 return; 2433 } 2434 2435 d1 = y1 - x1; 2436 if (x2-y1 > d1) 2437 d1 = x2 - y1; 2438 2439 d2 = y1 - x1; 2440 d3 = x2 - y1; 2441 2442 if (d1 < 1 || d2 < 1 || d3 < 1) 2443 { 2444 okBox(0, "System message", "Invalid range! Try to mark more data.", NULL); 2445 return; 2446 } 2447 2448 if (y1-d1 < 0 || y1+d1 >= s->length) 2449 { 2450 okBox(0, "System message", "Not enough sample data outside loop!", NULL); 2451 return; 2452 } 2453 2454 const double dD2Mul = 1.0 / d2; 2455 const double dD3Mul = 1.0 / d3; 2456 2457 pauseAudio(); 2458 unfixSample(s); 2459 2460 for (int32_t i = 0; i < d1; i++) 2461 { 2462 const int32_t aIdx = y1-i-1; 2463 const int32_t bIdx = y1+i; 2464 const double dI = i; 2465 2466 const double dA = getSampleValue(s->dataPtr, aIdx, sample16Bit); 2467 const double dB = getSampleValue(s->dataPtr, bIdx, sample16Bit); 2468 2469 if (i < d2) 2470 { 2471 const double dS1 = 1.0 - (dI * dD2Mul); 2472 const double dS2 = 2.0 - dS1; 2473 double dSample = (dA * dS2 + dB * dS1) / (dS1 + dS2); 2474 putSampleValue(s->dataPtr, aIdx, dSample, sample16Bit); 2475 } 2476 2477 if (i < d3) 2478 { 2479 const double dS1 = 1.0 - (dI * dD3Mul); 2480 const double dS2 = 2.0 - dS1; 2481 double dSample = (dB * dS2 + dA * dS1) / (dS1 + dS2); 2482 putSampleValue(s->dataPtr, bIdx, dSample, sample16Bit); 2483 } 2484 } 2485 2486 fixSample(s); 2487 resumeAudio(); 2488 } 2489 } 2490 else // forward loop 2491 { 2492 if (x1 > s->loopStart) 2493 { 2494 x1 -= s->loopLength; 2495 x2 -= s->loopLength; 2496 } 2497 2498 if (x1 < 0 || x2 <= x1 || x2 >= s->length) 2499 { 2500 okBox(0, "System message", "Invalid range!", NULL); 2501 return; 2502 } 2503 2504 const int32_t length = x2 - x1; 2505 2506 int32_t x = (length + 1) >> 1; 2507 y1 = s->loopStart - x; 2508 y2 = s->loopStart+s->loopLength - x; 2509 2510 if (y1 < 0 || y2+length >= s->length) 2511 { 2512 okBox(0, "System message", "Not enough sample data outside loop!", NULL); 2513 return; 2514 } 2515 2516 d1 = length; 2517 d2 = s->loopStart - y1; 2518 d3 = length - d2; 2519 2520 if (y1+length <= s->loopStart || d1 == 0 || d3 == 0 || d1 > s->loopLength) 2521 { 2522 okBox(0, "System message", "Invalid range!", NULL); 2523 return; 2524 } 2525 2526 const double dR = (s->loopStart - x) / (double)length; 2527 const double dD1 = d1; 2528 const double dD1Mul = 1.0 / d1; 2529 const double dD2Mul = 1.0 / d2; 2530 const double dD3Mul = 1.0 / d3; 2531 2532 pauseAudio(); 2533 unfixSample(s); 2534 2535 for (int32_t i = 0; i < length; i++) 2536 { 2537 const int32_t aIdx = y1+i; 2538 const int32_t bIdx = y2+i; 2539 const double dI = i; 2540 2541 const double dA = getSampleValue(s->dataPtr, aIdx, sample16Bit); 2542 const double dB = getSampleValue(s->dataPtr, bIdx, sample16Bit); 2543 const double dS2 = dI * dD1Mul; 2544 const double dS1 = 1.0 - dS2; 2545 2546 double dC, dD; 2547 if (y1+i < s->loopStart) 2548 { 2549 const double dS3 = 1.0 - (1.0 - dR) * dI * dD2Mul; 2550 const double dS4 = dR * dI * dD2Mul; 2551 2552 dC = (dA * dS3 + dB * dS4) / (dS3 + dS4); 2553 dD = (dA * dS2 + dB * dS1) / (dS1 + dS2); 2554 } 2555 else 2556 { 2557 const double dS3 = 1.0 - (1.0 - dR) * (dD1 - dI) * dD3Mul; 2558 const double dS4 = dR * (dD1 - dI) * dD3Mul; 2559 2560 dC = (dA * dS2 + dB * dS1) / (dS1 + dS2); 2561 dD = (dA * dS4 + dB * dS3) / (dS3 + dS4); 2562 } 2563 2564 putSampleValue(s->dataPtr, aIdx, dC, sample16Bit); 2565 putSampleValue(s->dataPtr, bIdx, dD, sample16Bit); 2566 } 2567 2568 fixSample(s); 2569 resumeAudio(); 2570 } 2571 2572 writeSample(true); 2573 setSongModifiedFlag(); 2574 } 2575 2576 void rbSampleNoLoop(void) 2577 { 2578 sample_t *s = getCurSample(); 2579 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 2580 return; 2581 2582 lockMixerCallback(); 2583 unfixSample(s); 2584 2585 DISABLE_LOOP(s->flags); 2586 2587 fixSample(s); 2588 unlockMixerCallback(); 2589 2590 updateSampleEditor(); 2591 writeSample(true); 2592 setSongModifiedFlag(); 2593 } 2594 2595 void rbSampleForwardLoop(void) 2596 { 2597 sample_t *s = getCurSample(); 2598 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 2599 return; 2600 2601 lockMixerCallback(); 2602 unfixSample(s); 2603 2604 DISABLE_LOOP(s->flags); 2605 s->flags |= LOOP_FWD; 2606 2607 if (s->loopStart+s->loopLength == 0) 2608 { 2609 s->loopStart = 0; 2610 s->loopLength = s->length; 2611 } 2612 2613 fixSample(s); 2614 unlockMixerCallback(); 2615 2616 updateSampleEditor(); 2617 writeSample(true); 2618 setSongModifiedFlag(); 2619 } 2620 2621 void rbSamplePingpongLoop(void) 2622 { 2623 sample_t *s = getCurSample(); 2624 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 2625 return; 2626 2627 lockMixerCallback(); 2628 unfixSample(s); 2629 2630 DISABLE_LOOP(s->flags); 2631 s->flags |= LOOP_BIDI; 2632 2633 if (s->loopStart+s->loopLength == 0) 2634 { 2635 s->loopStart = 0; 2636 s->loopLength = s->length; 2637 } 2638 2639 fixSample(s); 2640 unlockMixerCallback(); 2641 2642 updateSampleEditor(); 2643 writeSample(true); 2644 setSongModifiedFlag(); 2645 } 2646 2647 static int32_t SDLCALL convSmp8Bit(void *ptr) 2648 { 2649 sample_t *s = getCurSample(); 2650 assert(s->dataPtr != NULL); 2651 2652 pauseAudio(); 2653 unfixSample(s); 2654 2655 const int16_t *src16 = (const int16_t *)s->dataPtr; 2656 for (int32_t i = 0; i < s->length; i++) 2657 s->dataPtr[i] = src16[i] >> 8; 2658 2659 reallocateSmpData(s, s->length, false); 2660 2661 s->flags &= ~SAMPLE_16BIT; // remove 16-bit flag 2662 2663 fixSample(s); 2664 resumeAudio(); 2665 2666 setSongModifiedFlag(); 2667 setMouseBusy(false); 2668 2669 editor.updateCurSmp = true; 2670 return true; 2671 2672 (void)ptr; 2673 } 2674 2675 void rbSample8bit(void) 2676 { 2677 sample_t *s = getCurSample(); 2678 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 2679 return; 2680 2681 if (okBox(2, "System request", "Pre-convert sample data?", NULL) == 1) 2682 { 2683 mouseAnimOn(); 2684 thread = SDL_CreateThread(convSmp8Bit, NULL, NULL); 2685 if (thread == NULL) 2686 { 2687 okBox(0, "System message", "Couldn't create thread!", NULL); 2688 return; 2689 } 2690 2691 SDL_DetachThread(thread); 2692 return; 2693 } 2694 else 2695 { 2696 lockMixerCallback(); 2697 unfixSample(s); 2698 2699 s->flags &= ~SAMPLE_16BIT; // remove 16-bit flag 2700 s->length <<= 1; 2701 // no need to call reallocateSmpData, number of bytes allocated is the same 2702 2703 fixSample(s); 2704 unlockMixerCallback(); 2705 2706 updateSampleEditorSample(); 2707 updateSampleEditor(); 2708 setSongModifiedFlag(); 2709 } 2710 } 2711 2712 static int32_t SDLCALL convSmp16Bit(void *ptr) 2713 { 2714 sample_t *s = getCurSample(); 2715 2716 pauseAudio(); 2717 unfixSample(s); 2718 2719 if (!reallocateSmpData(s, s->length, true)) 2720 { 2721 okBoxThreadSafe(0, "System message", "Not enough memory!", NULL); 2722 return true; 2723 } 2724 2725 int16_t *dst16 = (int16_t *)s->dataPtr; 2726 for (int32_t i = s->length-1; i >= 0; i--) 2727 dst16[i] = s->dataPtr[i] << 8; 2728 2729 s->flags |= SAMPLE_16BIT; 2730 2731 fixSample(s); 2732 resumeAudio(); 2733 2734 setSongModifiedFlag(); 2735 setMouseBusy(false); 2736 2737 editor.updateCurSmp = true; 2738 return true; 2739 2740 (void)ptr; 2741 } 2742 2743 void rbSample16bit(void) 2744 { 2745 sample_t *s = getCurSample(); 2746 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 2747 return; 2748 2749 if (okBox(2, "System request", "Pre-convert sample data?", NULL) == 1) 2750 { 2751 mouseAnimOn(); 2752 thread = SDL_CreateThread(convSmp16Bit, NULL, NULL); 2753 if (thread == NULL) 2754 { 2755 okBox(0, "System message", "Couldn't create thread!", NULL); 2756 return; 2757 } 2758 2759 SDL_DetachThread(thread); 2760 return; 2761 } 2762 else 2763 { 2764 lockMixerCallback(); 2765 unfixSample(s); 2766 2767 s->flags |= SAMPLE_16BIT; 2768 s->length >>= 1; 2769 // no need to call reallocateSmpData, number of bytes allocated is the same 2770 2771 fixSample(s); 2772 unlockMixerCallback(); 2773 2774 updateSampleEditorSample(); 2775 updateSampleEditor(); 2776 setSongModifiedFlag(); 2777 } 2778 } 2779 2780 void clearSample(void) 2781 { 2782 sample_t *s = getCurSample(); 2783 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 2784 return; 2785 2786 if (okBox(1, "System request", "Clear sample?", NULL) != 1) 2787 return; 2788 2789 freeSample(editor.curInstr, editor.curSmp); 2790 updateNewSample(); 2791 setSongModifiedFlag(); 2792 } 2793 2794 void sampMinimize(void) 2795 { 2796 sample_t *s = getCurSample(); 2797 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 2798 return; 2799 2800 const bool hasLoop = GET_LOOPTYPE(s->flags) != LOOP_OFF; 2801 if (!hasLoop) 2802 { 2803 okBox(0, "System message", "Only a looped sample can be minimized!", NULL); 2804 return; 2805 } 2806 2807 if (s->loopStart+s->loopLength >= s->length) 2808 { 2809 okBox(0, "System message", "This sample is already minimized.", NULL); 2810 return; 2811 } 2812 2813 if (okBox(1, "System request", "Minimize sample?", NULL) != 1) 2814 return; 2815 2816 lockMixerCallback(); 2817 2818 s->length = s->loopStart + s->loopLength; 2819 2820 bool sample16Bit = !!(s->flags & SAMPLE_16BIT); 2821 reallocateSmpData(s, s->length, sample16Bit); 2822 // note: we don't need to make a call to fixSample() 2823 2824 unlockMixerCallback(); 2825 2826 updateSampleEditorSample(); 2827 updateSampleEditor(); 2828 setSongModifiedFlag(); 2829 } 2830 2831 void sampRepeatUp(void) 2832 { 2833 sample_t *s = getCurSample(); 2834 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 2835 return; 2836 2837 int32_t loopStart = curSmpLoopStart; 2838 int32_t loopLength = curSmpLoopLength; 2839 2840 if (loopStart < s->length-2) 2841 loopStart++; 2842 2843 if (loopStart+loopLength > s->length) 2844 loopLength = s->length - loopStart; 2845 2846 curSmpLoopStart = loopStart; 2847 curSmpLoopLength = loopLength; 2848 2849 fixLoopGadgets(); 2850 updateLoopsOnMouseUp = true; 2851 } 2852 2853 void sampRepeatDown(void) 2854 { 2855 sample_t *s = getCurSample(); 2856 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 2857 return; 2858 2859 int32_t loopStart = curSmpLoopStart - 1; 2860 if (loopStart < 0) 2861 loopStart = 0; 2862 2863 curSmpLoopStart = loopStart; 2864 2865 fixLoopGadgets(); 2866 updateLoopsOnMouseUp = true; 2867 } 2868 2869 void sampReplenUp(void) 2870 { 2871 sample_t *s = getCurSample(); 2872 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 2873 return; 2874 2875 int32_t loopLength = curSmpLoopLength + 1; 2876 if (curSmpLoopStart+loopLength > s->length) 2877 loopLength = s->length - curSmpLoopStart; 2878 2879 curSmpLoopLength = loopLength; 2880 2881 fixLoopGadgets(); 2882 updateLoopsOnMouseUp = true; 2883 } 2884 2885 void sampReplenDown(void) 2886 { 2887 int32_t loopLength; 2888 2889 sample_t *s = getCurSample(); 2890 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 2891 return; 2892 2893 loopLength = curSmpLoopLength - 1; 2894 if (loopLength < 0) 2895 loopLength = 0; 2896 2897 curSmpLoopLength = loopLength; 2898 2899 fixLoopGadgets(); 2900 updateLoopsOnMouseUp = true; 2901 } 2902 2903 void hideSampleEditor(void) 2904 { 2905 hideSampleEffectsScreen(); 2906 2907 hidePushButton(PB_SAMP_SCROLL_LEFT); 2908 hidePushButton(PB_SAMP_SCROLL_RIGHT); 2909 hidePushButton(PB_SAMP_PNOTE_UP); 2910 hidePushButton(PB_SAMP_PNOTE_DOWN); 2911 hidePushButton(PB_SAMP_STOP); 2912 hidePushButton(PB_SAMP_PWAVE); 2913 hidePushButton(PB_SAMP_PRANGE); 2914 hidePushButton(PB_SAMP_PDISPLAY); 2915 hidePushButton(PB_SAMP_SHOW_RANGE); 2916 hidePushButton(PB_SAMP_RANGE_ALL); 2917 hidePushButton(PB_SAMP_CLR_RANGE); 2918 hidePushButton(PB_SAMP_ZOOM_OUT); 2919 hidePushButton(PB_SAMP_SHOW_ALL); 2920 hidePushButton(PB_SAMP_SAVE_RNG); 2921 hidePushButton(PB_SAMP_CUT); 2922 hidePushButton(PB_SAMP_COPY); 2923 hidePushButton(PB_SAMP_PASTE); 2924 hidePushButton(PB_SAMP_CROP); 2925 hidePushButton(PB_SAMP_VOLUME); 2926 hidePushButton(PB_SAMP_EFFECTS); 2927 hidePushButton(PB_SAMP_EXIT); 2928 hidePushButton(PB_SAMP_CLEAR); 2929 hidePushButton(PB_SAMP_MIN); 2930 hidePushButton(PB_SAMP_REPEAT_UP); 2931 hidePushButton(PB_SAMP_REPEAT_DOWN); 2932 hidePushButton(PB_SAMP_REPLEN_UP); 2933 hidePushButton(PB_SAMP_REPLEN_DOWN); 2934 2935 hideRadioButtonGroup(RB_GROUP_SAMPLE_LOOP); 2936 hideRadioButtonGroup(RB_GROUP_SAMPLE_DEPTH); 2937 2938 hideScrollBar(SB_SAMP_SCROLL); 2939 2940 ui.sampleEditorShown = false; 2941 2942 hideSprite(SPRITE_LEFT_LOOP_PIN); 2943 hideSprite(SPRITE_RIGHT_LOOP_PIN); 2944 } 2945 2946 void exitSampleEditor(void) 2947 { 2948 hideSampleEditor(); 2949 2950 if (ui.sampleEditorExtShown) 2951 hideSampleEditorExt(); 2952 2953 showPatternEditor(); 2954 } 2955 2956 void showSampleEditor(void) 2957 { 2958 if (ui.extendedPatternEditor) 2959 exitPatternEditorExtended(); 2960 2961 hideInstEditor(); 2962 hidePatternEditor(); 2963 ui.sampleEditorShown = true; 2964 2965 drawFramework(0, 329, 632, 17, FRAMEWORK_TYPE1); 2966 drawFramework(0, 346, 115, 54, FRAMEWORK_TYPE1); 2967 drawFramework(115, 346, 133, 54, FRAMEWORK_TYPE1); 2968 drawFramework(248, 346, 49, 54, FRAMEWORK_TYPE1); 2969 drawFramework(297, 346, 56, 54, FRAMEWORK_TYPE1); 2970 drawFramework(353, 346, 74, 54, FRAMEWORK_TYPE1); 2971 drawFramework(427, 346, 205, 54, FRAMEWORK_TYPE1); 2972 drawFramework(2, 366, 34, 15, FRAMEWORK_TYPE2); 2973 2974 textOutShadow(5, 352, PAL_FORGRND, PAL_DSKTOP2, "Play:"); 2975 textOutShadow(371, 352, PAL_FORGRND, PAL_DSKTOP2, "No loop"); 2976 textOutShadow(371, 369, PAL_FORGRND, PAL_DSKTOP2, "Forward"); 2977 textOutShadow(371, 386, PAL_FORGRND, PAL_DSKTOP2, "Pingpong"); 2978 textOutShadow(446, 369, PAL_FORGRND, PAL_DSKTOP2, "8-bit"); 2979 textOutShadow(445, 384, PAL_FORGRND, PAL_DSKTOP2, "16-bit"); 2980 textOutShadow(488, 350, PAL_FORGRND, PAL_DSKTOP2, "Display"); 2981 textOutShadow(488, 362, PAL_FORGRND, PAL_DSKTOP2, "Length"); 2982 textOutShadow(488, 375, PAL_FORGRND, PAL_DSKTOP2, "Repeat"); 2983 textOutShadow(488, 387, PAL_FORGRND, PAL_DSKTOP2, "Replen."); 2984 2985 showPushButton(PB_SAMP_SCROLL_LEFT); 2986 showPushButton(PB_SAMP_SCROLL_RIGHT); 2987 showPushButton(PB_SAMP_PNOTE_UP); 2988 showPushButton(PB_SAMP_PNOTE_DOWN); 2989 showPushButton(PB_SAMP_STOP); 2990 showPushButton(PB_SAMP_PWAVE); 2991 showPushButton(PB_SAMP_PRANGE); 2992 showPushButton(PB_SAMP_PDISPLAY); 2993 showPushButton(PB_SAMP_SHOW_RANGE); 2994 showPushButton(PB_SAMP_RANGE_ALL); 2995 showPushButton(PB_SAMP_CLR_RANGE); 2996 showPushButton(PB_SAMP_ZOOM_OUT); 2997 showPushButton(PB_SAMP_SHOW_ALL); 2998 showPushButton(PB_SAMP_SAVE_RNG); 2999 showPushButton(PB_SAMP_CUT); 3000 showPushButton(PB_SAMP_COPY); 3001 showPushButton(PB_SAMP_PASTE); 3002 showPushButton(PB_SAMP_CROP); 3003 showPushButton(PB_SAMP_VOLUME); 3004 showPushButton(PB_SAMP_EFFECTS); 3005 showPushButton(PB_SAMP_EXIT); 3006 showPushButton(PB_SAMP_CLEAR); 3007 showPushButton(PB_SAMP_MIN); 3008 showPushButton(PB_SAMP_REPEAT_UP); 3009 showPushButton(PB_SAMP_REPEAT_DOWN); 3010 showPushButton(PB_SAMP_REPLEN_UP); 3011 showPushButton(PB_SAMP_REPLEN_DOWN); 3012 3013 showRadioButtonGroup(RB_GROUP_SAMPLE_LOOP); 3014 showRadioButtonGroup(RB_GROUP_SAMPLE_DEPTH); 3015 3016 showScrollBar(SB_SAMP_SCROLL); 3017 3018 // clear two lines in the sample data view that are never written to when the sampler is open 3019 hLine(0, 173, SAMPLE_AREA_WIDTH, PAL_BCKGRND); 3020 hLine(0, 328, SAMPLE_AREA_WIDTH, PAL_BCKGRND); 3021 3022 updateSampleEditor(); 3023 writeSample(true); 3024 3025 if (ui.sampleEditorEffectsShown) 3026 pbEffects(); 3027 } 3028 3029 void toggleSampleEditor(void) 3030 { 3031 hideInstEditor(); 3032 3033 if (ui.sampleEditorShown) 3034 { 3035 exitSampleEditor(); 3036 } 3037 else 3038 { 3039 hidePatternEditor(); 3040 showSampleEditor(); 3041 } 3042 } 3043 3044 static void invertSamplePosLine(int32_t x) 3045 { 3046 if (x < 0 || x >= SCREEN_W) 3047 return; 3048 3049 uint32_t *ptr32 = &video.frameBuffer[(174 * SCREEN_W) + x]; 3050 for (int32_t y = 0; y < SAMPLE_AREA_HEIGHT; y++, ptr32 += SCREEN_W) 3051 *ptr32 = video.palette[(*ptr32 >> 24) ^ 1]; // ">> 24" to get palette, XOR 1 to switch between normal/inverted mode 3052 } 3053 3054 static void writeSamplePosLine(void) 3055 { 3056 uint8_t ins, smp; 3057 3058 assert(editor.curSmpChannel < MAX_CHANNELS); 3059 lastChInstr_t *c = &lastChInstr[editor.curSmpChannel]; 3060 3061 if (c->instrNum == 130) // "Play Wave/Range/Display" in Smp. Ed. 3062 { 3063 ins = editor.curPlayInstr; 3064 smp = editor.curPlaySmp; 3065 } 3066 else 3067 { 3068 ins = c->instrNum; 3069 smp = c->smpNum; 3070 } 3071 3072 if (editor.curInstr == ins && editor.curSmp == smp) 3073 { 3074 const int32_t smpPos = getSamplePositionFromScopes(editor.curSmpChannel); 3075 if (smpPos != -1) 3076 { 3077 // convert sample position to screen position 3078 const int32_t scrPos = smpPos2Scr(smpPos); 3079 if (scrPos != -1) 3080 { 3081 if (scrPos != smpEd_OldSmpPosLine) 3082 { 3083 invertSamplePosLine(smpEd_OldSmpPosLine); // remove old line 3084 invertSamplePosLine(scrPos); // write new line 3085 } 3086 3087 smpEd_OldSmpPosLine = scrPos; 3088 return; 3089 } 3090 } 3091 } 3092 3093 if (smpEd_OldSmpPosLine != -1) 3094 invertSamplePosLine(smpEd_OldSmpPosLine); 3095 3096 smpEd_OldSmpPosLine = -1; 3097 } 3098 3099 void handleSamplerRedrawing(void) 3100 { 3101 // update sample editor 3102 3103 if (!ui.sampleEditorShown || editor.samplingAudioFlag) 3104 return; 3105 3106 if (writeSampleFlag) 3107 { 3108 writeSampleFlag = false; 3109 writeSample(true); 3110 } 3111 else if (smpEd_Rx1 != old_Rx1 || smpEd_Rx2 != old_Rx2 || smpEd_ScrPos != old_SmpScrPos || smpEd_ViewSize != old_ViewSize) 3112 { 3113 writeSample(false); 3114 } 3115 3116 writeSamplePosLine(); 3117 } 3118 3119 static void setLeftLoopPinPos(int32_t x) 3120 { 3121 sample_t *s = getCurSample(); 3122 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 3123 return; 3124 3125 int32_t newPos = scr2SmpPos(x) - curSmpLoopStart; 3126 int32_t loopStart = curSmpLoopStart + newPos; 3127 int32_t loopLength = curSmpLoopLength - newPos; 3128 3129 if (loopStart < 0) 3130 { 3131 loopLength += loopStart; 3132 loopStart = 0; 3133 } 3134 3135 if (loopLength < 0) 3136 { 3137 loopLength = 0; 3138 loopStart = curSmpLoopStart + curSmpLoopLength; 3139 } 3140 3141 curSmpLoopStart = loopStart; 3142 curSmpLoopLength = loopLength; 3143 3144 fixLoopGadgets(); 3145 updateLoopsOnMouseUp = true; 3146 } 3147 3148 static void setRightLoopPinPos(int32_t x) 3149 { 3150 sample_t *s = getCurSample(); 3151 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 3152 return; 3153 3154 int32_t loopLength = scr2SmpPos(x) - curSmpLoopStart; 3155 if (loopLength < 0) 3156 loopLength = 0; 3157 3158 if (loopLength+curSmpLoopStart > s->length) 3159 loopLength = s->length - curSmpLoopStart; 3160 3161 if (loopLength < 0) 3162 loopLength = 0; 3163 3164 curSmpLoopLength = loopLength; 3165 3166 fixLoopGadgets(); 3167 updateLoopsOnMouseUp = true; 3168 } 3169 3170 static int32_t mouseYToSampleY(int32_t my) 3171 { 3172 my -= 174; // 0..SAMPLE_AREA_HEIGHT-1 3173 3174 const double dTmp = my * (256.0 / SAMPLE_AREA_HEIGHT); 3175 const int32_t tmp32 = (const int32_t)(dTmp + 0.5); // rounded 3176 3177 return 255 - CLAMP(tmp32, 0, 255); 3178 } 3179 3180 static void editSampleData(bool mouseButtonHeld) 3181 { 3182 int8_t *ptr8; 3183 int16_t *ptr16; 3184 int32_t tmp32, p, vl, tvl, r, rl, rvl, start, end; 3185 3186 sample_t *s = getCurSample(); 3187 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 3188 return; 3189 3190 int32_t mx = mouse.x; 3191 if (mx > SCREEN_W) 3192 mx = SCREEN_W; 3193 3194 int32_t my = mouse.y; 3195 3196 if (!mouseButtonHeld) 3197 { 3198 pauseAudio(); 3199 unfixSample(s); 3200 editor.editSampleFlag = true; 3201 3202 lastDrawX = scr2SmpPos(mx); 3203 3204 lastDrawY = mouseYToSampleY(my); 3205 3206 lastMouseX = mx; 3207 lastMouseY = my; 3208 } 3209 else if (mx == lastMouseX && my == lastMouseY) 3210 { 3211 return; // don't continue if we didn't move the mouse 3212 } 3213 3214 if (mx != lastMouseX) 3215 p = scr2SmpPos(mx); 3216 else 3217 p = lastDrawX; 3218 3219 if (!keyb.leftShiftPressed && my != lastMouseY) 3220 vl = mouseYToSampleY(my); 3221 else 3222 vl = lastDrawY; 3223 3224 lastMouseX = mx; 3225 lastMouseY = my; 3226 3227 r = p; 3228 rvl = vl; 3229 3230 // swap x/y if needed 3231 if (p > lastDrawX) 3232 { 3233 // swap x 3234 tmp32 = p; 3235 p = lastDrawX; 3236 lastDrawX = tmp32; 3237 3238 // swap y 3239 tmp32 = lastDrawY; 3240 lastDrawY = vl; 3241 vl = tmp32; 3242 } 3243 3244 if (s->flags & SAMPLE_16BIT) 3245 { 3246 ptr16 = (int16_t *)s->dataPtr; 3247 3248 start = p; 3249 if (start < 0) 3250 start = 0; 3251 3252 end = lastDrawX+1; 3253 if (end > s->length) 3254 end = s->length; 3255 3256 if (p == lastDrawX) 3257 { 3258 const int16_t smpVal = (int16_t)((vl << 8) ^ 0x8000); 3259 for (rl = start; rl < end; rl++) 3260 ptr16[rl] = smpVal; 3261 } 3262 else 3263 { 3264 int32_t y = lastDrawY - vl; 3265 int32_t x = lastDrawX - p; 3266 3267 if (x != 0) 3268 { 3269 double dMul = 1.0 / x; 3270 int32_t i = 0; 3271 3272 for (rl = start; rl < end; rl++) 3273 { 3274 tvl = y * i; 3275 tvl = (int32_t)(tvl * dMul); // tvl /= x 3276 tvl += vl; 3277 tvl <<= 8; 3278 tvl ^= 0x8000; 3279 3280 ptr16[rl] = (int16_t)tvl; 3281 i++; 3282 } 3283 } 3284 } 3285 } 3286 else // 8-bit 3287 { 3288 ptr8 = s->dataPtr; 3289 3290 start = p; 3291 if (start < 0) 3292 start = 0; 3293 3294 end = lastDrawX+1; 3295 if (end > s->length) 3296 end = s->length; 3297 3298 if (p == lastDrawX) 3299 { 3300 const int8_t smpVal = (int8_t)(vl ^ 0x80); 3301 for (rl = start; rl < end; rl++) 3302 ptr8[rl] = smpVal; 3303 } 3304 else 3305 { 3306 int32_t y = lastDrawY - vl; 3307 int32_t x = lastDrawX - p; 3308 3309 if (x != 0) 3310 { 3311 double dMul = 1.0 / x; 3312 int32_t i = 0; 3313 3314 for (rl = start; rl < end; rl++) 3315 { 3316 tvl = y * i; 3317 tvl = (int32_t)(tvl * dMul); // tvl /= x 3318 tvl += vl; 3319 tvl ^= 0x80; 3320 3321 ptr8[rl] = (int8_t)tvl; 3322 i++; 3323 } 3324 } 3325 } 3326 } 3327 3328 lastDrawY = rvl; 3329 lastDrawX = r; 3330 3331 writeSample(true); 3332 } 3333 3334 void handleSampleDataMouseDown(bool mouseButtonHeld) 3335 { 3336 if (editor.curInstr == 0) 3337 return; 3338 3339 int32_t mx = CLAMP(mouse.x, 0, SCREEN_W+8); // allow some pixels outside of the screen 3340 int32_t my = CLAMP(mouse.y, 0, SCREEN_H-1); 3341 3342 if (!mouseButtonHeld) 3343 { 3344 ui.rightLoopPinMoving = false; 3345 ui.leftLoopPinMoving = false; 3346 ui.sampleDataOrLoopDrag = -1; 3347 3348 mouseXOffs = 0; 3349 lastMouseX = mx; 3350 lastMouseY = my; 3351 3352 mouse.lastUsedObjectType = OBJECT_SMPDATA; 3353 3354 if (mouse.leftButtonPressed) 3355 { 3356 // move loop pins 3357 if (my < 183) 3358 { 3359 const int32_t leftLoopPinPos = getSpritePosX(SPRITE_LEFT_LOOP_PIN); 3360 if (mx >= leftLoopPinPos && mx <= leftLoopPinPos+16) 3361 { 3362 mouseXOffs = (leftLoopPinPos + 8) - mx; 3363 3364 ui.sampleDataOrLoopDrag = true; 3365 3366 setLeftLoopPinState(true); 3367 lastMouseX = mx; 3368 3369 ui.leftLoopPinMoving = true; 3370 return; 3371 } 3372 } 3373 else if (my > 318) 3374 { 3375 const int32_t rightLoopPinPos = getSpritePosX(SPRITE_RIGHT_LOOP_PIN); 3376 if (mx >= rightLoopPinPos && mx <= rightLoopPinPos+16) 3377 { 3378 mouseXOffs = (rightLoopPinPos + 8) - mx; 3379 3380 ui.sampleDataOrLoopDrag = true; 3381 3382 setRightLoopPinState(true); 3383 lastMouseX = mx; 3384 3385 ui.rightLoopPinMoving = true; 3386 return; 3387 } 3388 } 3389 3390 // mark data 3391 lastMouseX = mx; 3392 ui.sampleDataOrLoopDrag = mx; 3393 3394 setSampleRange(mx, mx); 3395 } 3396 else if (mouse.rightButtonPressed) 3397 { 3398 // edit data 3399 ui.sampleDataOrLoopDrag = true; 3400 editSampleData(false); 3401 } 3402 3403 return; 3404 } 3405 3406 if (mouse.rightButtonPressed) 3407 { 3408 editSampleData(true); 3409 return; 3410 } 3411 3412 if (mx != lastMouseX) 3413 { 3414 if (mouse.leftButtonPressed) 3415 { 3416 if (ui.leftLoopPinMoving) 3417 { 3418 lastMouseX = mx; 3419 setLeftLoopPinPos(mouseXOffs + mx); 3420 } 3421 else if (ui.rightLoopPinMoving) 3422 { 3423 lastMouseX = mx; 3424 setRightLoopPinPos(mouseXOffs + mx); 3425 } 3426 else if (ui.sampleDataOrLoopDrag >= 0) 3427 { 3428 // mark data 3429 3430 lastMouseX = mx; 3431 3432 /* Edge-case hack for fullscreen sample marking where the width 3433 ** of the image fills the whole screen (or close). 3434 */ 3435 if (video.fullscreen && video.renderW >= video.displayW-5 && mx == SCREEN_W-1) 3436 mx = SCREEN_W; 3437 3438 if (mx > ui.sampleDataOrLoopDrag) 3439 setSampleRange(ui.sampleDataOrLoopDrag, mx); 3440 else if (mx == ui.sampleDataOrLoopDrag) 3441 setSampleRange(ui.sampleDataOrLoopDrag, ui.sampleDataOrLoopDrag); 3442 else if (mx < ui.sampleDataOrLoopDrag) 3443 setSampleRange(mx, ui.sampleDataOrLoopDrag); 3444 } 3445 } 3446 } 3447 } 3448 3449 // SAMPLE EDITOR EXTENSION 3450 3451 void handleSampleEditorExtRedrawing(void) 3452 { 3453 hexOutBg(35, 96, PAL_FORGRND, PAL_DESKTOP, smpEd_Rx1, 8); 3454 hexOutBg(99, 96, PAL_FORGRND, PAL_DESKTOP, smpEd_Rx2, 8); 3455 hexOutBg(99, 110, PAL_FORGRND, PAL_DESKTOP, smpEd_Rx2 - smpEd_Rx1, 8); 3456 hexOutBg(99, 124, PAL_FORGRND, PAL_DESKTOP, smpCopySize, 8); 3457 hexOutBg(226, 96, PAL_FORGRND, PAL_DESKTOP, editor.srcInstr, 2); 3458 hexOutBg(274, 96, PAL_FORGRND, PAL_DESKTOP, editor.srcSmp, 2); 3459 hexOutBg(226, 109, PAL_FORGRND, PAL_DESKTOP, editor.curInstr, 2); 3460 hexOutBg(274, 109, PAL_FORGRND, PAL_DESKTOP, editor.curSmp, 2); 3461 } 3462 3463 void drawSampleEditorExt(void) 3464 { 3465 drawFramework(0, 92, 158, 44, FRAMEWORK_TYPE1); 3466 drawFramework(0, 136, 158, 37, FRAMEWORK_TYPE1); 3467 drawFramework(158, 92, 133, 81, FRAMEWORK_TYPE1); 3468 3469 textOutShadow( 4, 96, PAL_FORGRND, PAL_DSKTOP2, "Rng.:"); 3470 charOutShadow(91, 95, PAL_FORGRND, PAL_DSKTOP2, '-'); 3471 textOutShadow( 4, 110, PAL_FORGRND, PAL_DSKTOP2, "Range size"); 3472 textOutShadow( 4, 124, PAL_FORGRND, PAL_DSKTOP2, "Copy buf. size"); 3473 3474 textOutShadow(162, 96, PAL_FORGRND, PAL_DSKTOP2, "Src.instr."); 3475 textOutShadow(245, 96, PAL_FORGRND, PAL_DSKTOP2, "smp."); 3476 textOutShadow(162, 109, PAL_FORGRND, PAL_DSKTOP2, "Dest.instr."); 3477 textOutShadow(245, 109, PAL_FORGRND, PAL_DSKTOP2, "smp."); 3478 3479 showPushButton(PB_SAMP_EXT_CLEAR_COPYBUF); 3480 showPushButton(PB_SAMP_EXT_CONV); 3481 showPushButton(PB_SAMP_EXT_ECHO); 3482 showPushButton(PB_SAMP_EXT_BACKWARDS); 3483 showPushButton(PB_SAMP_EXT_CONV_W); 3484 showPushButton(PB_SAMP_EXT_MORPH); 3485 showPushButton(PB_SAMP_EXT_COPY_INS); 3486 showPushButton(PB_SAMP_EXT_COPY_SMP); 3487 showPushButton(PB_SAMP_EXT_XCHG_INS); 3488 showPushButton(PB_SAMP_EXT_XCHG_SMP); 3489 showPushButton(PB_SAMP_EXT_RESAMPLE); 3490 showPushButton(PB_SAMP_EXT_MIX_SAMPLE); 3491 } 3492 3493 void showSampleEditorExt(void) 3494 { 3495 hideTopScreen(); 3496 showTopScreen(false); 3497 3498 if (ui.extendedPatternEditor) 3499 exitPatternEditorExtended(); 3500 3501 if (!ui.sampleEditorShown) 3502 showSampleEditor(); 3503 3504 ui.sampleEditorExtShown = true; 3505 ui.scopesShown = false; 3506 drawSampleEditorExt(); 3507 } 3508 3509 void hideSampleEditorExt(void) 3510 { 3511 ui.sampleEditorExtShown = false; 3512 3513 hidePushButton(PB_SAMP_EXT_CLEAR_COPYBUF); 3514 hidePushButton(PB_SAMP_EXT_CONV); 3515 hidePushButton(PB_SAMP_EXT_ECHO); 3516 hidePushButton(PB_SAMP_EXT_BACKWARDS); 3517 hidePushButton(PB_SAMP_EXT_CONV_W); 3518 hidePushButton(PB_SAMP_EXT_MORPH); 3519 hidePushButton(PB_SAMP_EXT_COPY_INS); 3520 hidePushButton(PB_SAMP_EXT_COPY_SMP); 3521 hidePushButton(PB_SAMP_EXT_XCHG_INS); 3522 hidePushButton(PB_SAMP_EXT_XCHG_SMP); 3523 hidePushButton(PB_SAMP_EXT_RESAMPLE); 3524 hidePushButton(PB_SAMP_EXT_MIX_SAMPLE); 3525 3526 ui.scopesShown = true; 3527 drawScopeFramework(); 3528 } 3529 3530 void toggleSampleEditorExt(void) 3531 { 3532 if (ui.sampleEditorExtShown) 3533 hideSampleEditorExt(); 3534 else 3535 showSampleEditorExt(); 3536 } 3537 3538 static int32_t SDLCALL sampleBackwardsThread(void *ptr) 3539 { 3540 int8_t tmp8, *ptrStart, *ptrEnd; 3541 int16_t tmp16, *ptrStart16, *ptrEnd16; 3542 3543 const bool sampleDataMarked = (smpEd_Rx1 != smpEd_Rx2); 3544 sample_t *s = getCurSample(); 3545 const bool sample16Bit = !!(s->flags & SAMPLE_16BIT); 3546 3547 if (sample16Bit) 3548 { 3549 if (!sampleDataMarked) 3550 { 3551 ptrStart16 = (int16_t *)s->dataPtr; 3552 ptrEnd16 = (int16_t *)s->dataPtr + (s->length-1); 3553 } 3554 else 3555 { 3556 ptrStart16 = (int16_t *)s->dataPtr + smpEd_Rx1; 3557 ptrEnd16 = (int16_t *)s->dataPtr + (smpEd_Rx2-1); 3558 } 3559 3560 pauseAudio(); 3561 unfixSample(s); 3562 3563 while (ptrStart16 < ptrEnd16) 3564 { 3565 tmp16 = *ptrStart16; 3566 *ptrStart16++ = *ptrEnd16; 3567 *ptrEnd16-- = tmp16; 3568 } 3569 3570 fixSample(s); 3571 resumeAudio(); 3572 } 3573 else 3574 { 3575 if (!sampleDataMarked) 3576 { 3577 ptrStart = s->dataPtr; 3578 ptrEnd = &s->dataPtr[s->length-1]; 3579 } 3580 else 3581 { 3582 ptrStart = &s->dataPtr[smpEd_Rx1]; 3583 ptrEnd = &s->dataPtr[smpEd_Rx2-1]; 3584 } 3585 3586 pauseAudio(); 3587 unfixSample(s); 3588 3589 while (ptrStart < ptrEnd) 3590 { 3591 tmp8 = *ptrStart; 3592 *ptrStart++ = *ptrEnd; 3593 *ptrEnd-- = tmp8; 3594 } 3595 3596 fixSample(s); 3597 resumeAudio(); 3598 } 3599 3600 setSongModifiedFlag(); 3601 setMouseBusy(false); 3602 3603 writeSampleFlag = true; 3604 return true; 3605 3606 (void)ptr; 3607 } 3608 3609 void sampleBackwards(void) 3610 { 3611 sample_t *s = getCurSample(); 3612 if (s == NULL || s->dataPtr == NULL || s->length < 2) 3613 return; 3614 3615 mouseAnimOn(); 3616 thread = SDL_CreateThread(sampleBackwardsThread, NULL, NULL); 3617 if (thread == NULL) 3618 { 3619 okBox(0, "System message", "Couldn't create thread!", NULL); 3620 return; 3621 } 3622 3623 SDL_DetachThread(thread); 3624 } 3625 3626 static int32_t SDLCALL sampleChangeSignThread(void *ptr) 3627 { 3628 sample_t *s = getCurSample(); 3629 3630 pauseAudio(); 3631 unfixSample(s); 3632 3633 if (s->flags & SAMPLE_16BIT) 3634 { 3635 int16_t *ptr16 = (int16_t *)s->dataPtr; 3636 for (int32_t i = 0; i < s->length; i++) 3637 ptr16[i] ^= 0x8000; 3638 } 3639 else 3640 { 3641 int8_t *ptr8 = s->dataPtr; 3642 for (int32_t i = 0; i < s->length; i++) 3643 ptr8[i] ^= 0x80; 3644 } 3645 3646 fixSample(s); 3647 resumeAudio(); 3648 3649 setSongModifiedFlag(); 3650 setMouseBusy(false); 3651 3652 writeSampleFlag = true; 3653 return true; 3654 3655 (void)ptr; 3656 } 3657 3658 void sampleChangeSign(void) 3659 { 3660 sample_t *s = getCurSample(); 3661 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 3662 return; 3663 3664 mouseAnimOn(); 3665 thread = SDL_CreateThread(sampleChangeSignThread, NULL, NULL); 3666 if (thread == NULL) 3667 { 3668 okBox(0, "System message", "Couldn't create thread!", NULL); 3669 return; 3670 } 3671 3672 SDL_DetachThread(thread); 3673 } 3674 3675 static int32_t SDLCALL sampleByteSwapThread(void *ptr) 3676 { 3677 sample_t *s = getCurSample(); 3678 3679 pauseAudio(); 3680 unfixSample(s); 3681 3682 int32_t length = s->length; 3683 if (!(s->flags & SAMPLE_16BIT)) 3684 length >>= 1; 3685 3686 int8_t *ptr8 = s->dataPtr; 3687 for (int32_t i = 0; i < length; i++, ptr8 += 2) 3688 { 3689 const int8_t tmp = ptr8[0]; 3690 ptr8[0] = ptr8[1]; 3691 ptr8[1] = tmp; 3692 } 3693 3694 fixSample(s); 3695 resumeAudio(); 3696 3697 setSongModifiedFlag(); 3698 setMouseBusy(false); 3699 3700 writeSampleFlag = true; 3701 return true; 3702 3703 (void)ptr; 3704 } 3705 3706 void sampleByteSwap(void) 3707 { 3708 sample_t *s = getCurSample(); 3709 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 3710 return; 3711 3712 if (!(s->flags & SAMPLE_16BIT)) 3713 { 3714 if (okBox(2, "System request", "Byte swapping rarely makes sense on an 8-bit sample. Continue?", NULL) != 1) 3715 return; 3716 } 3717 3718 mouseAnimOn(); 3719 thread = SDL_CreateThread(sampleByteSwapThread, NULL, NULL); 3720 if (thread == NULL) 3721 { 3722 okBox(0, "System message", "Couldn't create thread!", NULL); 3723 return; 3724 } 3725 3726 SDL_DetachThread(thread); 3727 } 3728 3729 static int32_t SDLCALL fixDCThread(void *ptr) 3730 { 3731 int8_t *ptr8; 3732 int16_t *ptr16; 3733 int32_t length; 3734 3735 const bool sampleDataMarked = (smpEd_Rx1 != smpEd_Rx2); 3736 sample_t *s = getCurSample(); 3737 3738 if (s->flags & SAMPLE_16BIT) 3739 { 3740 if (!sampleDataMarked) 3741 { 3742 ptr16 = (int16_t *)s->dataPtr; 3743 length = s->length; 3744 } 3745 else 3746 { 3747 ptr16 = (int16_t *)&s->dataPtr + smpEd_Rx1; 3748 length = smpEd_Rx2 - smpEd_Rx1; 3749 } 3750 3751 if (length < 0 || length > s->length) 3752 { 3753 setMouseBusy(false); 3754 return true; 3755 } 3756 3757 pauseAudio(); 3758 unfixSample(s); 3759 3760 int64_t averageDC = 0; 3761 for (int32_t i = 0; i < length; i++) 3762 averageDC += ptr16[i]; 3763 averageDC = (averageDC + (length>>1)) / length; // rounded 3764 3765 const int32_t smpSub = (int32_t)averageDC; 3766 for (int32_t i = 0; i < length; i++) 3767 { 3768 int32_t smp32 = ptr16[i] - smpSub; 3769 CLAMP16(smp32); 3770 ptr16[i] = (int16_t)smp32; 3771 } 3772 3773 fixSample(s); 3774 resumeAudio(); 3775 } 3776 else // 8-bit 3777 { 3778 if (!sampleDataMarked) 3779 { 3780 ptr8 = s->dataPtr; 3781 length = s->length; 3782 } 3783 else 3784 { 3785 ptr8 = &s->dataPtr[smpEd_Rx1]; 3786 length = smpEd_Rx2 - smpEd_Rx1; 3787 } 3788 3789 if (length < 0 || length > s->length) 3790 { 3791 setMouseBusy(false); 3792 return true; 3793 } 3794 3795 pauseAudio(); 3796 unfixSample(s); 3797 3798 int64_t averageDC = 0; 3799 for (int32_t i = 0; i < length; i++) 3800 averageDC += ptr8[i]; 3801 averageDC = (averageDC + (length>>1)) / length; // rounded 3802 3803 const int32_t smpSub = (int32_t)averageDC; 3804 for (int32_t i = 0; i < length; i++) 3805 { 3806 int32_t smp32 = ptr8[i] - smpSub; 3807 CLAMP8(smp32); 3808 ptr8[i] = (int8_t)smp32; 3809 } 3810 3811 fixSample(s); 3812 resumeAudio(); 3813 } 3814 3815 setSongModifiedFlag(); 3816 setMouseBusy(false); 3817 3818 writeSampleFlag = true; 3819 return true; 3820 3821 (void)ptr; 3822 } 3823 3824 void fixDC(void) 3825 { 3826 sample_t *s = getCurSample(); 3827 if (s == NULL || s->dataPtr == NULL || s->length <= 0) 3828 return; 3829 3830 mouseAnimOn(); 3831 thread = SDL_CreateThread(fixDCThread, NULL, NULL); 3832 if (thread == NULL) 3833 { 3834 okBox(0, "System message", "Couldn't create thread!", NULL); 3835 return; 3836 } 3837 3838 SDL_DetachThread(thread); 3839 } 3840 3841 void smpEdStop(void) 3842 { 3843 // safely kills all voices 3844 lockMixerCallback(); 3845 unlockMixerCallback(); 3846 } 3847 3848 void testSmpEdMouseUp(void) // used for setting new loop points 3849 { 3850 if (updateLoopsOnMouseUp) 3851 { 3852 updateLoopsOnMouseUp = false; 3853 3854 sample_t *s = getCurSample(); 3855 if (s == NULL) 3856 return; 3857 3858 if (s->loopStart != curSmpLoopStart || s->loopLength != curSmpLoopLength) 3859 { 3860 lockMixerCallback(); 3861 unfixSample(s); 3862 s->loopStart = curSmpLoopStart; 3863 s->loopLength = curSmpLoopLength; 3864 fixSample(s); 3865 unlockMixerCallback(); 3866 3867 setSongModifiedFlag(); 3868 writeSample(true); 3869 } 3870 } 3871 }