ft2-clone

Fasttracker 2 clone
Log | Files | Refs | README | LICENSE

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 }