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 <stdint.h>
7 #include <stdbool.h>
8 #include <stdio.h>
9 #include <math.h>
10 #include "ft2_header.h"
11 #include "ft2_sample_ed.h"
12 #include "ft2_gui.h"
13 #include "scopes/ft2_scopes.h"
14 #include "ft2_pattern_ed.h"
15 #include "ft2_replayer.h"
16 #include "ft2_audio.h"
17 #include "ft2_mouse.h"
18 #include "ft2_structs.h"
19
20 // this is truly a mess, but it works...
21
22 static char byteFormatBuffer[64], tmpInstrName[1 + MAX_INST][22 + 1], tmpInstName[MAX_INST][22 + 1];
23 static bool removePatt, removeInst, removeSamp, removeChans, removeSmpDataAfterLoop, convSmpsTo8Bit;
24 static uint8_t instrUsed[MAX_INST], instrOrder[MAX_INST], pattUsed[MAX_PATTERNS], pattOrder[MAX_PATTERNS];
25 static int16_t oldPattLens[MAX_PATTERNS], tmpPattLens[MAX_PATTERNS];
26 static int64_t xmSize64 = -1, xmAfterTrimSize64 = -1, spaceSaved64 = -1;
27 static note_t *oldPatts[MAX_PATTERNS], *tmpPatt[MAX_PATTERNS];
28 static instr_t *tmpInstr[1 + MAX_INST], *tmpInst[MAX_INST]; // tmpInstr[x] = copy of instr[x] for "after trim" size calculation
29 static SDL_Thread *trimThread;
30
31 void pbTrimCalc(void);
32
freeTmpInstruments(void)33 static void freeTmpInstruments(void)
34 {
35 for (int32_t i = 0; i <= MAX_INST; i++)
36 {
37 if (tmpInstr[i] != NULL)
38 {
39 // don't free samples, as the pointers are shared with main instruments...
40
41 free(tmpInstr[i]);
42 tmpInstr[i] = NULL;
43 }
44 }
45 }
46
setTmpInstruments(void)47 static bool setTmpInstruments(void)
48 {
49 freeTmpInstruments();
50
51 for (int32_t i = 0; i <= MAX_INST; i++)
52 {
53 if (instr[i] != NULL)
54 {
55 tmpInstr[i] = (instr_t *)malloc(sizeof (instr_t));
56 if (tmpInstr[i] == NULL)
57 {
58 freeTmpInstruments();
59 return false;
60 }
61
62 *tmpInstr[i] = *instr[i];
63 }
64 }
65
66 return true;
67 }
68
remapInstrInSong(uint8_t src,uint8_t dst,int32_t ap)69 static void remapInstrInSong(uint8_t src, uint8_t dst, int32_t ap)
70 {
71 for (int32_t i = 0; i < ap; i++)
72 {
73 note_t *pattPtr = pattern[i];
74 if (pattPtr == NULL)
75 continue;
76
77 const int32_t readLen = patternNumRows[i] * MAX_CHANNELS;
78
79 note_t *p = pattPtr;
80 for (int32_t j = 0; j < readLen; j++, p++)
81 {
82 if (p->instr == src)
83 p->instr = dst;
84 }
85 }
86 }
87
getUsedTempSamples(uint16_t insNum)88 static int16_t getUsedTempSamples(uint16_t insNum)
89 {
90 if (tmpInstr[insNum] == NULL)
91 return 0;
92
93 instr_t *ins = tmpInstr[insNum];
94
95 int16_t i = 16 - 1;
96 while (i >= 0 && ins->smp[i].dataPtr == NULL && ins->smp[i].name[0] == '\0')
97 i--;
98
99 /* Yes, 'i' can be -1 here, and will be set to at least 0
100 ** because of ins->ta values. Possibly an FT2 bug...
101 **/
102 for (int16_t j = 0; j < 96; j++)
103 {
104 if (ins->note2SampleLUT[j] > i)
105 i = ins->note2SampleLUT[j];
106 }
107
108 return i+1;
109 }
110
getTempInsAndSmpSize(void)111 static int64_t getTempInsAndSmpSize(void)
112 {
113 int16_t j;
114
115 int16_t ai = MAX_INST;
116 while (ai > 0 && getUsedTempSamples(ai) == 0 && tmpInstrName[ai][0] == '\0')
117 ai--;
118
119 int64_t currSize64 = 0;
120
121 // count instrument and sample data size in song
122 for (int16_t i = 1; i <= ai; i++)
123 {
124 if (tmpInstr[i] == NULL)
125 j = 0;
126 else
127 j = i;
128
129 const int16_t a = getUsedTempSamples(i);
130 if (a > 0)
131 currSize64 += INSTR_HEADER_SIZE + (a * sizeof (xmSmpHdr_t));
132 else
133 currSize64 += 22+11;
134
135 instr_t *ins = tmpInstr[j];
136 for (int16_t k = 0; k < a; k++)
137 {
138 sample_t *s = &ins->smp[k];
139 if (s->dataPtr != NULL && s->length > 0)
140 {
141 if (s->flags & SAMPLE_16BIT)
142 currSize64 += s->length << 1;
143 else
144 currSize64 += s->length;
145 }
146 }
147 }
148
149 return currSize64;
150 }
151
wipeInstrUnused(bool testWipeSize,int16_t * ai,int32_t ap,int32_t antChn)152 static void wipeInstrUnused(bool testWipeSize, int16_t *ai, int32_t ap, int32_t antChn)
153 {
154 int16_t numRows;
155 int32_t i, j, k;
156 note_t *p;
157
158 int32_t numInsts = *ai;
159
160 // calculate what instruments are used
161 memset(instrUsed, 0, numInsts);
162 for (i = 0; i < ap; i++)
163 {
164 if (testWipeSize)
165 {
166 p = tmpPatt[i];
167 numRows = tmpPattLens[i];
168 }
169 else
170 {
171 p = pattern[i];
172 numRows = patternNumRows[i];
173 }
174
175 if (p == NULL)
176 continue;
177
178 for (j = 0; j < numRows; j++)
179 {
180 for (k = 0; k < antChn; k++)
181 {
182 uint8_t ins = p[(j * MAX_CHANNELS) + k].instr;
183 if (ins > 0 && ins <= MAX_INST)
184 instrUsed[ins-1] = true;
185 }
186 }
187 }
188
189 int16_t instToDel = 0;
190 uint8_t newInst = 0;
191 int16_t newNumInsts = 0;
192
193 memset(instrOrder, 0, numInsts);
194 for (i = 0; i < numInsts; i++)
195 {
196 if (instrUsed[i])
197 {
198 newNumInsts++;
199 instrOrder[i] = newInst++;
200 }
201 else
202 {
203 instToDel++;
204 }
205 }
206
207 if (instToDel == 0)
208 return;
209
210 if (testWipeSize)
211 {
212 for (i = 0; i < numInsts; i++)
213 {
214 if (!instrUsed[i] && tmpInstr[1+i] != NULL)
215 {
216 free(tmpInstr[1+i]);
217 tmpInstr[1+i] = NULL;
218 }
219 }
220
221 // relocate instruments
222
223 memcpy(tmpInstName, &tmpInstrName[1], MAX_INST * sizeof (song.instrName[0]));
224 memcpy(tmpInst, &tmpInstr[1], MAX_INST * sizeof (instr[0]));
225
226 memset(&tmpInstr[1], 0, numInsts * sizeof (tmpInstr[0]));
227 memset(&tmpInstrName[1], 0, numInsts * sizeof (tmpInstrName[0]));
228
229 for (i = 0; i < numInsts; i++)
230 {
231 if (instrUsed[i])
232 {
233 newInst = instrOrder[i];
234
235 memcpy(&tmpInstr[1+newInst], &tmpInst[i], sizeof (tmpInst[0]));
236 strcpy(tmpInstrName[1+newInst], tmpInstName[i]);
237 }
238 }
239
240 *ai = newNumInsts;
241 return;
242 }
243
244 // clear unused instruments
245 for (i = 0; i < numInsts; i++)
246 {
247 if (!instrUsed[i])
248 freeInstr(1 + i);
249 }
250
251 // relocate instruments
252
253 memcpy(tmpInstName, &song.instrName[1], MAX_INST * sizeof (song.instrName[0]));
254 memcpy(tmpInst, &instr[1], MAX_INST * sizeof (instr[0]));
255
256 memset(&instr[1], 0, numInsts * sizeof (instr[0]));
257 memset(song.instrName[1], 0, numInsts * sizeof (song.instrName[0])); // XXX: Is this safe?
258
259 for (i = 0; i < numInsts; i++)
260 {
261 if (instrUsed[i])
262 {
263 newInst = instrOrder[i];
264 remapInstrInSong(1 + (uint8_t)i, 1 + newInst, ap);
265
266 memcpy(&instr[1+newInst], &tmpInst[i], sizeof (instr[0]));
267 strcpy(song.instrName[1+newInst], tmpInstName[i]);
268 }
269 }
270
271 *ai = newNumInsts;
272
273 setTmpInstruments();
274 }
275
wipePattsUnused(bool testWipeSize,int16_t * ap)276 static void wipePattsUnused(bool testWipeSize, int16_t *ap)
277 {
278 uint8_t newPatt;
279 int16_t i, *pLens;
280 note_t **p;
281
282 int16_t usedPatts = *ap;
283 memset(pattUsed, 0, usedPatts);
284
285 int16_t newUsedPatts = 0;
286 for (i = 0; i < song.songLength; i++)
287 {
288 newPatt = song.orders[i];
289 if (newPatt < usedPatts && !pattUsed[newPatt])
290 {
291 pattUsed[newPatt] = true;
292 newUsedPatts++;
293 }
294 }
295
296 if (newUsedPatts == 0 || newUsedPatts == usedPatts)
297 return; // nothing to do!
298
299 newPatt = 0;
300 memset(pattOrder, 0, usedPatts);
301 for (i = 0; i < usedPatts; i++)
302 {
303 if (pattUsed[i])
304 pattOrder[i] = newPatt++;
305 }
306
307 if (testWipeSize)
308 {
309 p = tmpPatt;
310 pLens = tmpPattLens;
311 }
312 else
313 {
314 p = pattern;
315 pLens = patternNumRows;
316 }
317
318 memcpy(oldPatts, p, usedPatts * sizeof (note_t *));
319 memcpy(oldPattLens, pLens, usedPatts * sizeof (int16_t));
320 memset(p, 0, usedPatts * sizeof (note_t *));
321 memset(pLens, 0, usedPatts * sizeof (int16_t));
322
323 // relocate patterns
324 for (i = 0; i < usedPatts; i++)
325 {
326 p[i] = NULL;
327
328 if (!pattUsed[i])
329 {
330 if (!testWipeSize && oldPatts[i] != NULL)
331 {
332 free(oldPatts[i]);
333 oldPatts[i] = NULL;
334 }
335 }
336 else
337 {
338 newPatt = pattOrder[i];
339 p[newPatt] = oldPatts[i];
340 pLens[newPatt] = oldPattLens[i];
341 }
342 }
343
344 if (!testWipeSize)
345 {
346 for (i = 0; i < MAX_PATTERNS; i++)
347 {
348 if (pattern[i] == NULL)
349 patternNumRows[i] = 64;
350 }
351
352 // reorder order list (and clear unused entries)
353 for (i = 0; i < 256; i++)
354 {
355 if (i < song.songLength)
356 song.orders[i] = pattOrder[song.orders[i]];
357 else
358 song.orders[i] = 0;
359 }
360 }
361
362 *ap = newUsedPatts;
363 }
364
wipeSamplesUnused(bool testWipeSize,int16_t ai)365 static void wipeSamplesUnused(bool testWipeSize, int16_t ai)
366 {
367 uint8_t smpUsed[16], smpOrder[16];
368 int16_t j, k, l;
369 instr_t *ins;
370 sample_t tempSamples[16];
371
372 for (int16_t i = 1; i <= ai; i++)
373 {
374 if (!testWipeSize)
375 {
376 if (instr[i] == NULL)
377 l = 0;
378 else
379 l = i;
380
381 ins = instr[l];
382 l = getUsedSamples(i);
383 }
384 else
385 {
386 if (tmpInstr[i] == NULL)
387 l = 0;
388 else
389 l = i;
390
391 ins = tmpInstr[l];
392 l = getUsedTempSamples(i);
393 }
394
395 memset(smpUsed, 0, l);
396 if (l > 0)
397 {
398 sample_t *s = ins->smp;
399 for (j = 0; j < l; j++, s++)
400 {
401 // check if sample is referenced in instrument
402 for (k = 0; k < 96; k++)
403 {
404 if (ins->note2SampleLUT[k] == j)
405 {
406 smpUsed[j] = true;
407 break; // sample is used
408 }
409 }
410
411 if (k == 96)
412 {
413 // sample is unused
414
415 if (s->dataPtr != NULL && !testWipeSize)
416 freeSmpData(s);
417
418 memset(s, 0, sizeof (sample_t));
419 }
420 }
421
422 // create re-order list
423 uint8_t newSamp = 0;
424 memset(smpOrder, 0, l);
425 for (j = 0; j < l; j++)
426 {
427 if (smpUsed[j])
428 smpOrder[j] = newSamp++;
429 }
430
431 // re-order samples
432
433 memcpy(tempSamples, ins->smp, l * sizeof (sample_t));
434 memset(ins->smp, 0, l * sizeof (sample_t));
435
436 for (j = 0; j < l; j++)
437 {
438 if (smpUsed[j])
439 ins->smp[smpOrder[j]] = tempSamples[j];
440 }
441
442 // re-order note->sample list
443 for (j = 0; j < 96; j++)
444 {
445 newSamp = ins->note2SampleLUT[j];
446 if (smpUsed[newSamp])
447 ins->note2SampleLUT[j] = smpOrder[newSamp];
448 else
449 ins->note2SampleLUT[j] = 0;
450 }
451 }
452 }
453 }
454
wipeSmpDataAfterLoop(bool testWipeSize,int16_t ai)455 static void wipeSmpDataAfterLoop(bool testWipeSize, int16_t ai)
456 {
457 int16_t l;
458 instr_t *ins;
459
460 for (int16_t i = 1; i <= ai; i++)
461 {
462 if (!testWipeSize)
463 {
464 if (instr[i] == NULL)
465 l = 0;
466 else
467 l = i;
468
469 ins = instr[l];
470 l = getUsedSamples(i);
471 }
472 else
473 {
474 if (tmpInstr[i] == NULL)
475 l = 0;
476 else
477 l = i;
478
479 ins = tmpInstr[l];
480 l = getUsedTempSamples(i);
481 }
482
483 sample_t *s = ins->smp;
484 for (int16_t j = 0; j < l; j++, s++)
485 {
486 if (s->dataPtr != NULL && GET_LOOPTYPE(s->flags) != LOOP_OFF && s->length > 0 && s->length > s->loopStart+s->loopLength)
487 {
488 if (!testWipeSize)
489 unfixSample(s);
490
491 s->length = s->loopStart + s->loopLength;
492 if (!testWipeSize)
493 {
494 if (s->length <= 0)
495 {
496 s->length = 0;
497 freeSmpData(s);
498 }
499 else
500 {
501 reallocateSmpData(s, s->length, !!(s->flags & SAMPLE_16BIT));
502 }
503 }
504
505 if (!testWipeSize)
506 fixSample(s);
507 }
508 }
509 }
510 }
511
convertSamplesTo8bit(bool testWipeSize,int16_t ai)512 static void convertSamplesTo8bit(bool testWipeSize, int16_t ai)
513 {
514 int16_t k;
515 instr_t *ins;
516
517 for (int16_t i = 1; i <= ai; i++)
518 {
519 if (!testWipeSize)
520 {
521 if (instr[i] == NULL)
522 k = 0;
523 else
524 k = i;
525
526 ins = instr[k];
527 k = getUsedSamples(i);
528 }
529 else
530 {
531 if (tmpInstr[i] == NULL)
532 k = 0;
533 else
534 k = i;
535
536 ins = tmpInstr[k];
537 k = getUsedTempSamples(i);
538 }
539
540 sample_t *s = ins->smp;
541 for (int16_t j = 0; j < k; j++, s++)
542 {
543 if (s->dataPtr != NULL && s->length > 0 && (s->flags & SAMPLE_16BIT))
544 {
545 if (testWipeSize)
546 {
547 s->flags &= ~SAMPLE_16BIT;
548 }
549 else
550 {
551 unfixSample(s);
552
553 const int16_t *src16 = (const int16_t *)s->dataPtr;
554 int8_t *dst8 = s->dataPtr;
555
556 for (int32_t a = 0; a < s->length; a++)
557 dst8[a] = src16[a] >> 8;
558
559 s->flags &= ~SAMPLE_16BIT;
560
561 reallocateSmpData(s, s->length, true);
562 fixSample(s);
563 }
564 }
565 }
566 }
567 }
568
getPackedPattSize(note_t * p,int32_t numRows,int32_t antChn)569 static uint16_t getPackedPattSize(note_t *p, int32_t numRows, int32_t antChn)
570 {
571 uint8_t bytes[sizeof (note_t)];
572
573 uint16_t totalPackLen = 0;
574 uint8_t *pattPtr = (uint8_t *)p;
575 uint8_t *writePtr = pattPtr;
576
577 for (int32_t row = 0; row < numRows; row++)
578 {
579 for (int32_t chn = 0; chn < antChn; chn++)
580 {
581 bytes[0] = *pattPtr++;
582 bytes[1] = *pattPtr++;
583 bytes[2] = *pattPtr++;
584 bytes[3] = *pattPtr++;
585 bytes[4] = *pattPtr++;
586
587 uint8_t *firstBytePtr = writePtr++;
588
589 uint8_t packBits = 0;
590 if (bytes[0] > 0) { packBits |= 1; writePtr++; } // note
591 if (bytes[1] > 0) { packBits |= 2; writePtr++; } // instrument
592 if (bytes[2] > 0) { packBits |= 4; writePtr++; } // volume column
593 if (bytes[3] > 0) { packBits |= 8; writePtr++; } // effect
594
595 if (packBits == 15) // first four bits set?
596 {
597 // no packing needed, write pattern data as is
598 totalPackLen += 5;
599 writePtr += 5;
600 continue;
601 }
602
603 if (bytes[4] > 0) writePtr++; // effect parameter
604
605 totalPackLen += (uint16_t)(writePtr - firstBytePtr); // bytes writen
606 }
607
608 // skip unused channels
609 pattPtr += sizeof (note_t) * (MAX_CHANNELS - antChn);
610 }
611
612 return totalPackLen;
613 }
614
tmpPatternEmpty(uint16_t pattNum,int32_t numChannels)615 static bool tmpPatternEmpty(uint16_t pattNum, int32_t numChannels)
616 {
617 if (tmpPatt[pattNum] == NULL)
618 return true;
619
620 uint8_t *scanPtr = (uint8_t *)tmpPatt[pattNum];
621 int32_t scanLen = numChannels * sizeof (note_t);
622 int32_t numRows = tmpPattLens[pattNum];
623
624 for (int32_t i = 0; i < numRows; i++, scanPtr += TRACK_WIDTH)
625 {
626 for (int32_t j = 0; j < scanLen; j++)
627 {
628 if (scanPtr[j] != 0)
629 return false;
630 }
631 }
632
633 return true;
634 }
635
calculateXMSize(void)636 static int64_t calculateXMSize(void)
637 {
638 // count header size in song
639 int64_t currSize64 = sizeof (xmHdr_t);
640
641 // count number of patterns that would be saved
642 int16_t ap = MAX_PATTERNS;
643 do
644 {
645 if (patternEmpty(ap - 1))
646 ap--;
647 else
648 break;
649 }
650 while (ap > 0);
651
652 // count number of instruments
653 int16_t ai = 128;
654 while (ai > 0 && getUsedSamples(ai) == 0 && song.instrName[ai][0] == '\0')
655 ai--;
656
657 // count packed pattern data size in song
658 for (int16_t i = 0; i < ap; i++)
659 {
660 currSize64 += sizeof (xmPatHdr_t);
661 if (!patternEmpty(i))
662 currSize64 += getPackedPattSize(pattern[i], patternNumRows[i], song.numChannels);
663 }
664
665 // count instrument and sample data size in song
666 for (int16_t i = 1; i <= ai; i++)
667 {
668 int16_t j;
669 if (instr[i] == NULL)
670 j = 0;
671 else
672 j = i;
673
674 const int16_t a = getUsedSamples(i);
675 if (a > 0)
676 currSize64 += INSTR_HEADER_SIZE + (a * sizeof (xmSmpHdr_t));
677 else
678 currSize64 += 22+11;
679
680 instr_t *ins = instr[j];
681 for (int16_t k = 0; k < a; k++)
682 {
683 sample_t* s = &ins->smp[k];
684 if (s->dataPtr != NULL && s->length > 0)
685 {
686 if (s->flags & SAMPLE_16BIT)
687 currSize64 += s->length << 1;
688 else
689 currSize64 += s->length;
690 }
691 }
692 }
693
694 return currSize64;
695 }
696
calculateTrimSize(void)697 static int64_t calculateTrimSize(void)
698 {
699 int16_t i, j, k;
700
701 int32_t numChannels = song.numChannels;
702 int32_t pattDataLen = 0;
703 int32_t newPattDataLen = 0;
704 int64_t bytes64 = 0;
705 int64_t oldInstrSize64 = 0;
706
707 // copy over temp data
708 memcpy(tmpPatt, pattern, sizeof (tmpPatt));
709 memcpy(tmpPattLens, patternNumRows, sizeof (tmpPattLens));
710 memcpy(tmpInstrName, song.instrName, sizeof (tmpInstrName));
711
712 if (!setTmpInstruments())
713 {
714 okBox(0, "System message", "Not enough memory!");
715 return 0;
716 }
717
718 // get current size of all instruments and their samples
719 if (removeInst || removeSamp || removeSmpDataAfterLoop || convSmpsTo8Bit)
720 oldInstrSize64 = getTempInsAndSmpSize();
721
722 // count number of patterns that would be saved
723 int16_t ap = MAX_PATTERNS;
724 do
725 {
726 if (tmpPatternEmpty(ap - 1, numChannels))
727 ap--;
728 else
729 break;
730 }
731 while (ap > 0);
732
733 // count number of instruments that would be saved
734 int16_t ai = MAX_INST;
735 while (ai > 0 && getUsedTempSamples(ai) == 0 && tmpInstrName[ai][0] == '\0')
736 ai--;
737
738 // calculate "remove unused samples" size
739 if (removeSamp) wipeSamplesUnused(true, ai);
740
741 // calculate "remove sample data after loop" size
742 if (removeSmpDataAfterLoop) wipeSmpDataAfterLoop(true, ai);
743
744 // calculate "convert samples to 8-bit" size
745 if (convSmpsTo8Bit) convertSamplesTo8bit(true, ai);
746
747 // get old pattern data length
748 if (removeChans || removePatt)
749 {
750 for (i = 0; i < ap; i++)
751 {
752 pattDataLen += sizeof (xmPatHdr_t);
753 if (!tmpPatternEmpty(i, numChannels))
754 pattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], numChannels);
755 }
756 }
757
758 // calculate "remove unused channels" size
759 if (removeChans)
760 {
761 // get real number of channels
762 int16_t highestChan = -1;
763 for (i = 0; i < ap; i++)
764 {
765 note_t *pattPtr = tmpPatt[i];
766 if (pattPtr == NULL)
767 continue;
768
769 const int16_t numRows = tmpPattLens[i];
770 for (j = 0; j < numRows; j++)
771 {
772 for (k = 0; k < numChannels; k++)
773 {
774 note_t *p = &pattPtr[(j * MAX_CHANNELS) + k];
775 if (p->note > 0 || p->instr > 0 || p->vol > 0 || p->efx > 0 || p->efxData > 0)
776 {
777 if (k > highestChan)
778 highestChan = k;
779 }
780 }
781 }
782 }
783
784 // set new number of channels (and make it an even number)
785 if (highestChan >= 0)
786 {
787 highestChan++;
788 if (highestChan & 1)
789 highestChan++;
790
791 numChannels = (uint8_t)(CLAMP(highestChan, 2, numChannels));
792 }
793 }
794
795 // calculate "remove unused patterns" size
796 if (removePatt) wipePattsUnused(true, &ap);
797
798 // calculate new pattern data size
799 if (removeChans || removePatt)
800 {
801 for (i = 0; i < ap; i++)
802 {
803 newPattDataLen += sizeof (xmPatHdr_t);
804 if (!tmpPatternEmpty(i, numChannels))
805 newPattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], numChannels);
806 }
807
808 assert(pattDataLen >= newPattDataLen);
809
810 if (pattDataLen > newPattDataLen)
811 bytes64 += (pattDataLen - newPattDataLen);
812 }
813
814 // calculate "remove unused instruments" size
815 if (removeInst) wipeInstrUnused(true, &ai, ap, numChannels);
816
817 // calculate new instruments and samples size
818 if (removeInst || removeSamp || removeSmpDataAfterLoop || convSmpsTo8Bit)
819 {
820 int64_t newInstrSize64 = getTempInsAndSmpSize();
821
822 assert(oldInstrSize64 >= newInstrSize64);
823 if (oldInstrSize64 > newInstrSize64)
824 bytes64 += (oldInstrSize64 - newInstrSize64);
825 }
826
827 freeTmpInstruments();
828 return bytes64;
829 }
830
trimThreadFunc(void * ptr)831 static int32_t SDLCALL trimThreadFunc(void *ptr)
832 {
833 int16_t i, j, k;
834
835 if (!setTmpInstruments())
836 {
837 okBoxThreadSafe(0, "System message", "Not enough memory!");
838 return true;
839 }
840
841 // audio callback is not running now, so we're safe
842
843 // count number of patterns
844 int16_t ap = MAX_PATTERNS;
845 do
846 {
847 if (patternEmpty(ap - 1))
848 ap--;
849 else
850 break;
851 }
852 while (ap > 0);
853
854 // count number of instruments
855 int16_t ai = MAX_INST;
856 while (ai > 0 && getUsedSamples(ai) == 0 && song.instrName[ai][0] == '\0')
857 ai--;
858
859 // remove unused samples
860 if (removeSamp)
861 wipeSamplesUnused(false, ai);
862
863 // remove sample data after loop
864 if (removeSmpDataAfterLoop)
865 wipeSmpDataAfterLoop(false, ai);
866
867 // convert samples to 8-bit
868 if (convSmpsTo8Bit)
869 convertSamplesTo8bit(false, ai);
870
871 // removed unused channels
872 if (removeChans)
873 {
874 // count used channels
875 int16_t highestChan = -1;
876 for (i = 0; i < ap; i++)
877 {
878 note_t *pattPtr = pattern[i];
879 if (pattPtr == NULL)
880 continue;
881
882 const int16_t numRows = patternNumRows[i];
883 for (j = 0; j < numRows; j++)
884 {
885 for (k = 0; k < song.numChannels; k++)
886 {
887 note_t *p = &pattPtr[(j * MAX_CHANNELS) + k];
888 if (p->note > 0 || p->vol > 0 || p->instr > 0 || p->efx > 0 || p->efxData > 0)
889 {
890 if (k > highestChan)
891 highestChan = k;
892 }
893 }
894 }
895 }
896
897 // set new 'channels used' number
898 if (highestChan >= 0)
899 {
900 highestChan++;
901 if (highestChan & 1)
902 highestChan++;
903
904 song.numChannels = (uint8_t)(CLAMP(highestChan, 2, song.numChannels));
905 }
906
907 // clear potentially unused channel data
908 if (song.numChannels < MAX_CHANNELS)
909 {
910 for (i = 0; i < MAX_PATTERNS; i++)
911 {
912 note_t *p = pattern[i];
913 if (p == NULL)
914 continue;
915
916 const int16_t numRows = patternNumRows[i];
917 for (j = 0; j < numRows; j++)
918 memset(&p[(j * MAX_CHANNELS) + song.numChannels], 0, sizeof (note_t) * (MAX_CHANNELS - song.numChannels));
919 }
920 }
921 }
922
923 // clear unused patterns
924 if (removePatt)
925 wipePattsUnused(false, &ap);
926
927 // remove unused instruments
928 if (removeInst)
929 wipeInstrUnused(false, &ai, ap, song.numChannels);
930
931 freeTmpInstruments();
932 editor.trimThreadWasDone = true;
933
934 return true;
935 (void)ptr;
936 }
937
trimThreadDone(void)938 void trimThreadDone(void)
939 {
940 if (removePatt)
941 setPos(song.songPos, song.row, false);
942
943 if (removeInst)
944 {
945 editor.currVolEnvPoint = 0;
946 editor.currPanEnvPoint = 0;
947 }
948
949 updateTextBoxPointers();
950
951 hideTopScreen();
952 showTopScreen(true);
953 showBottomScreen();
954
955 if (removeChans)
956 {
957 if (ui.patternEditorShown)
958 {
959 if (ui.channelOffset > song.numChannels-ui.numChannelsShown)
960 setScrollBarPos(SB_CHAN_SCROLL, song.numChannels - ui.numChannelsShown, true);
961 }
962
963 if (cursor.ch >= ui.channelOffset+ui.numChannelsShown)
964 cursor.ch = ui.channelOffset+ui.numChannelsShown - 1;
965 }
966
967 checkMarkLimits();
968
969 if (removeSamp || convSmpsTo8Bit)
970 updateSampleEditorSample();
971
972 pbTrimCalc();
973 setSongModifiedFlag();
974 resumeAudio();
975 setMouseBusy(false);
976 }
977
formatBytes(uint64_t bytes,bool roundUp)978 static char *formatBytes(uint64_t bytes, bool roundUp)
979 {
980 double dBytes;
981
982 if (bytes == 0)
983 {
984 strcpy(byteFormatBuffer, "0");
985 return byteFormatBuffer;
986 }
987
988 bytes %= 1000ULL*1024*1024*999; // wrap around gigabytes in case of overflow
989 if (bytes >= 1024ULL*1024*1024*9)
990 {
991 // gigabytes
992 dBytes = bytes / (1024.0*1024.0*1024.0);
993 if (dBytes < 100)
994 sprintf(byteFormatBuffer, "%.1fGB", dBytes);
995 else
996 sprintf(byteFormatBuffer, "%dGB", roundUp ? (int32_t)ceil(dBytes) : (int32_t)dBytes);
997 }
998 else if (bytes >= 1024*1024*9)
999 {
1000 // megabytes
1001 dBytes = bytes / (1024.0*1024.0);
1002 if (dBytes < 100)
1003 sprintf(byteFormatBuffer, "%.1fMB", dBytes);
1004 else
1005 sprintf(byteFormatBuffer, "%dMB", roundUp ? (int32_t)ceil(dBytes) : (int32_t)dBytes);
1006 }
1007 else if (bytes >= 1024*9)
1008 {
1009 // kilobytes
1010 dBytes = bytes / 1024.0;
1011 if (dBytes < 100)
1012 sprintf(byteFormatBuffer, "%.1fkB", dBytes);
1013 else
1014 sprintf(byteFormatBuffer, "%dkB", roundUp ? (int32_t)ceil(dBytes) : (int32_t)dBytes);
1015 }
1016 else
1017 {
1018 // bytes
1019 sprintf(byteFormatBuffer, "%d", (int32_t)bytes);
1020 }
1021
1022 return byteFormatBuffer;
1023 }
1024
drawTrimScreen(void)1025 void drawTrimScreen(void)
1026 {
1027 char sizeBuf[16];
1028
1029 drawFramework(0, 92, 136, 81, FRAMEWORK_TYPE1);
1030 drawFramework(136, 92, 155, 81, FRAMEWORK_TYPE1);
1031
1032 textOutShadow(4, 95, PAL_FORGRND, PAL_DSKTOP2, "What to remove:");
1033 textOutShadow(19, 109, PAL_FORGRND, PAL_DSKTOP2, "Unused patterns");
1034 textOutShadow(19, 122, PAL_FORGRND, PAL_DSKTOP2, "Unused instruments");
1035 textOutShadow(19, 135, PAL_FORGRND, PAL_DSKTOP2, "Unused samples");
1036 textOutShadow(19, 148, PAL_FORGRND, PAL_DSKTOP2, "Unused channels");
1037 textOutShadow(19, 161, PAL_FORGRND, PAL_DSKTOP2, "Smp. dat. after loop");
1038
1039 textOutShadow(155, 96, PAL_FORGRND, PAL_DSKTOP2, "Conv. samples to 8-bit");
1040 textOutShadow(140, 111, PAL_FORGRND, PAL_DSKTOP2, ".xm size before");
1041 textOutShadow(140, 124, PAL_FORGRND, PAL_DSKTOP2, ".xm size after");
1042 textOutShadow(140, 137, PAL_FORGRND, PAL_DSKTOP2, "Bytes to save");
1043
1044 if (xmSize64 > -1)
1045 {
1046 sprintf(sizeBuf, "%s", formatBytes(xmSize64, true));
1047 textOut(287 - textWidth(sizeBuf), 111, PAL_FORGRND, sizeBuf);
1048 }
1049 else
1050 {
1051 textOut(287 - textWidth("Unknown"), 111, PAL_FORGRND, "Unknown");
1052 }
1053
1054 if (xmAfterTrimSize64 > -1)
1055 {
1056 sprintf(sizeBuf, "%s", formatBytes(xmAfterTrimSize64, true));
1057 textOut(287 - textWidth(sizeBuf), 124, PAL_FORGRND, sizeBuf);
1058 }
1059 else
1060 {
1061 textOut(287 - textWidth("Unknown"), 124, PAL_FORGRND, "Unknown");
1062 }
1063
1064 if (spaceSaved64 > -1)
1065 {
1066 sprintf(sizeBuf, "%s", formatBytes(spaceSaved64, false));
1067 textOut(287 - textWidth(sizeBuf), 137, PAL_FORGRND, sizeBuf);
1068 }
1069 else
1070 {
1071 textOut(287 - textWidth("Unknown"), 137, PAL_FORGRND, "Unknown");
1072 }
1073
1074 showCheckBox(CB_TRIM_PATT);
1075 showCheckBox(CB_TRIM_INST);
1076 showCheckBox(CB_TRIM_SAMP);
1077 showCheckBox(CB_TRIM_CHAN);
1078 showCheckBox(CB_TRIM_SMPD);
1079 showCheckBox(CB_TRIM_CONV);
1080 showPushButton(PB_TRIM_CALC);
1081 showPushButton(PB_TRIM_TRIM);
1082 }
1083
hideTrimScreen(void)1084 void hideTrimScreen(void)
1085 {
1086 hideCheckBox(CB_TRIM_PATT);
1087 hideCheckBox(CB_TRIM_INST);
1088 hideCheckBox(CB_TRIM_SAMP);
1089 hideCheckBox(CB_TRIM_CHAN);
1090 hideCheckBox(CB_TRIM_SMPD);
1091 hideCheckBox(CB_TRIM_CONV);
1092 hidePushButton(PB_TRIM_CALC);
1093 hidePushButton(PB_TRIM_TRIM);
1094
1095 ui.trimScreenShown = false;
1096 ui.scopesShown = true;
1097 drawScopeFramework();
1098 }
1099
showTrimScreen(void)1100 void showTrimScreen(void)
1101 {
1102 if (ui.extended)
1103 exitPatternEditorExtended();
1104
1105 hideTopScreen();
1106 showTopScreen(false);
1107
1108 ui.trimScreenShown = true;
1109 ui.scopesShown = false;
1110
1111 drawTrimScreen();
1112 }
1113
toggleTrimScreen(void)1114 void toggleTrimScreen(void)
1115 {
1116 if (ui.trimScreenShown)
1117 hideTrimScreen();
1118 else
1119 showTrimScreen();
1120 }
1121
setInitialTrimFlags(void)1122 void setInitialTrimFlags(void)
1123 {
1124 removePatt = true;
1125 removeInst = true;
1126 removeSamp = true;
1127 removeChans = true;
1128 removeSmpDataAfterLoop = true;
1129 convSmpsTo8Bit = false;
1130
1131 checkBoxes[CB_TRIM_PATT].checked = true;
1132 checkBoxes[CB_TRIM_INST].checked = true;
1133 checkBoxes[CB_TRIM_SAMP].checked = true;
1134 checkBoxes[CB_TRIM_CHAN].checked = true;
1135 checkBoxes[CB_TRIM_SMPD].checked = true;
1136 checkBoxes[CB_TRIM_CONV].checked = false;
1137 }
1138
cbTrimUnusedPatt(void)1139 void cbTrimUnusedPatt(void)
1140 {
1141 removePatt ^= 1;
1142 }
1143
cbTrimUnusedInst(void)1144 void cbTrimUnusedInst(void)
1145 {
1146 removeInst ^= 1;
1147 }
1148
cbTrimUnusedSamp(void)1149 void cbTrimUnusedSamp(void)
1150 {
1151 removeSamp ^= 1;
1152 }
1153
cbTrimUnusedChans(void)1154 void cbTrimUnusedChans(void)
1155 {
1156 removeChans ^= 1;
1157 }
1158
cbTrimUnusedSmpData(void)1159 void cbTrimUnusedSmpData(void)
1160 {
1161 removeSmpDataAfterLoop ^= 1;
1162 }
1163
cbTrimSmpsTo8Bit(void)1164 void cbTrimSmpsTo8Bit(void)
1165 {
1166 convSmpsTo8Bit ^= 1;
1167 }
1168
pbTrimCalc(void)1169 void pbTrimCalc(void)
1170 {
1171 xmSize64 = calculateXMSize();
1172 spaceSaved64 = calculateTrimSize();
1173
1174 xmAfterTrimSize64 = xmSize64 - spaceSaved64;
1175 if (xmAfterTrimSize64 < 0)
1176 xmAfterTrimSize64 = 0;
1177
1178 if (ui.trimScreenShown)
1179 drawTrimScreen();
1180 }
1181
pbTrimDoTrim(void)1182 void pbTrimDoTrim(void)
1183 {
1184 if (!removePatt && !removeInst && !removeSamp && !removeChans && !removeSmpDataAfterLoop && !convSmpsTo8Bit)
1185 return; // nothing to trim...
1186
1187 if (okBox(2, "System request", "Are you sure you want to trim the song? Making a backup of the song first is recommended.") != 1)
1188 return;
1189
1190 mouseAnimOn();
1191 pauseAudio();
1192
1193 trimThread = SDL_CreateThread(trimThreadFunc, NULL, NULL);
1194 if (trimThread == NULL)
1195 {
1196 resumeAudio();
1197 mouseAnimOff();
1198 return;
1199 }
1200
1201 SDL_DetachThread(trimThread);
1202 }
1203
resetTrimSizes(void)1204 void resetTrimSizes(void)
1205 {
1206 xmSize64 = -1;
1207 xmAfterTrimSize64 = -1;
1208 spaceSaved64 = -1;
1209
1210 if (ui.trimScreenShown)
1211 drawTrimScreen();
1212 }
1213