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