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