ref: 2cf6fcbce159ce595a388d078dd940c08a8b010f
dir: /src/ft2_trim.c/
// for finding memory leaks in debug mode with Visual Studio #if defined _DEBUG && defined _MSC_VER #include <crtdbg.h> #endif #include <stdint.h> #include <stdbool.h> #include <stdio.h> #include <math.h> #include "ft2_header.h" #include "ft2_sample_ed.h" #include "ft2_gui.h" #include "ft2_scopes.h" #include "ft2_pattern_ed.h" #include "ft2_replayer.h" #include "ft2_audio.h" #include "ft2_mouse.h" #include "ft2_structs.h" // this is truly a mess, but it works... static char byteFormatBuffer[64], tmpInstrName[1 + MAX_INST][22 + 1], tmpInstName[MAX_INST][22 + 1]; static bool removePatt, removeInst, removeSamp, removeChans, removeSmpDataAfterLoop, convSmpsTo8Bit; static uint8_t instrUsed[MAX_INST], instrOrder[MAX_INST], pattUsed[MAX_PATTERNS], pattOrder[MAX_PATTERNS]; static int16_t oldPattLens[MAX_PATTERNS], tmpPattLens[MAX_PATTERNS]; static int64_t xmSize64 = -1, xmAfterTrimSize64 = -1, spaceSaved64 = -1; static tonTyp *oldPatts[MAX_PATTERNS], *tmpPatt[MAX_PATTERNS]; static instrTyp *tmpInstr[1 + MAX_INST], *tmpInst[MAX_INST]; // tmpInstr[x] = copy of instr[x] for "after trim" size calculation static SDL_Thread *trimThread; void pbTrimCalc(void); static void freeTmpInstruments(void) { for (int32_t i = 0; i <= MAX_INST; i++) { if (tmpInstr[i] != NULL) { // don't free samples, as the pointers are shared with main instruments... free(tmpInstr[i]); tmpInstr[i] = NULL; } } } static bool setTmpInstruments(void) { freeTmpInstruments(); for (int32_t i = 0; i <= MAX_INST; i++) { if (instr[i] != NULL) { tmpInstr[i] = (instrTyp *)malloc(sizeof (instrTyp)); if (tmpInstr[i] == NULL) { freeTmpInstruments(); return false; } *tmpInstr[i] = *instr[i]; } } return true; } static void remapInstrInSong(uint8_t src, uint8_t dst, int32_t ap) { int32_t readLen; tonTyp *pattPtr, *note; for (int32_t i = 0; i < ap; i++) { pattPtr = patt[i]; if (pattPtr == NULL) continue; readLen = pattLens[i] * MAX_VOICES; for (int32_t j = 0; j < readLen; j++) { note = &pattPtr[j]; if (note->instr == src) note->instr = dst; } } } static int16_t getUsedTempSamples(uint16_t nr) { int16_t i, j; instrTyp *ins; if (tmpInstr[nr] == NULL) return 0; ins = tmpInstr[nr]; i = 16 - 1; while (i >= 0 && ins->samp[i].pek == NULL && ins->samp[i].name[0] == '\0') i--; /* Yes, 'i' can be -1 here, and will be set to at least 0 ** because of ins->ta values. Possibly an FT2 bug... **/ for (j = 0; j < 96; j++) { if (ins->ta[j] > i) i = ins->ta[j]; } return i+1; } static int64_t getTempInsAndSmpSize(void) { int16_t a, j, ai; int64_t currSize64; instrTyp *ins; ai = MAX_INST; while (ai > 0 && getUsedTempSamples(ai) == 0 && tmpInstrName[ai][0] == '\0') ai--; currSize64 = 0; // count instrument and sample data size in song for (int16_t i = 1; i <= ai; i++) { if (tmpInstr[i] == NULL) j = 0; else j = i; a = getUsedTempSamples(i); if (a > 0) currSize64 += INSTR_HEADER_SIZE + (a * sizeof (sampleHeaderTyp)); else currSize64 += 22+11; ins = tmpInstr[j]; for (int16_t k = 0; k < a; k++) { if (ins->samp[k].pek != NULL) currSize64 += ins->samp[k].len; } } return currSize64; } static void wipeInstrUnused(bool testWipeSize, int16_t *ai, int32_t ap, int32_t antChn) { uint8_t newInst; int16_t newNumInsts, instToDel, pattLen; int32_t numInsts, i, j, k; tonTyp *pattPtr; numInsts = *ai; // calculate what instruments are used memset(instrUsed, 0, numInsts); for (i = 0; i < ap; i++) { if (testWipeSize) { pattPtr = tmpPatt[i]; pattLen = tmpPattLens[i]; } else { pattPtr = patt[i]; pattLen = pattLens[i]; } if (pattPtr == NULL) continue; for (j = 0; j < pattLen; j++) { for (k = 0; k < antChn; k++) { newInst = pattPtr[(j * MAX_VOICES) + k].instr; if (newInst > 0 && newInst <= MAX_INST) instrUsed[newInst-1] = true; } } } instToDel = 0; newInst = 0; newNumInsts = 0; memset(instrOrder, 0, numInsts); for (i = 0; i < numInsts; i++) { if (instrUsed[i]) { newNumInsts++; instrOrder[i] = newInst++; } else { instToDel++; } } if (instToDel == 0) return; if (testWipeSize) { for (i = 0; i < numInsts; i++) { if (!instrUsed[i] && tmpInstr[1+i] != NULL) { free(tmpInstr[1+i]); tmpInstr[1+i] = NULL; } } // relocate instruments memcpy(tmpInstName, &tmpInstrName[1], MAX_INST * sizeof (song.instrName[0])); memcpy(tmpInst, &tmpInstr[1], MAX_INST * sizeof (instr[0])); memset(&tmpInstr[1], 0, numInsts * sizeof (tmpInstr[0])); memset(&tmpInstrName[1], 0, numInsts * sizeof (tmpInstrName[0])); for (i = 0; i < numInsts; i++) { if (instrUsed[i]) { newInst = instrOrder[i]; memcpy(&tmpInstr[1+newInst], &tmpInst[i], sizeof (tmpInst[0])); strcpy(tmpInstrName[1+newInst], tmpInstName[i]); } } *ai = newNumInsts; return; } // clear unused instruments for (i = 0; i < numInsts; i++) { if (!instrUsed[i]) freeInstr(1 + i); } // relocate instruments memcpy(tmpInstName, &song.instrName[1], MAX_INST * sizeof (song.instrName[0])); memcpy(tmpInst, &instr[1], MAX_INST * sizeof (instr[0])); memset(&instr[1], 0, numInsts * sizeof (instr[0])); memset(song.instrName[1], 0, numInsts * sizeof (song.instrName[0])); for (i = 0; i < numInsts; i++) { if (instrUsed[i]) { newInst = instrOrder[i]; remapInstrInSong(1 + (uint8_t)i, 1 + newInst, ap); memcpy(&instr[1+newInst], &tmpInst[i], sizeof (instr[0])); strcpy(song.instrName[1+newInst], tmpInstName[i]); } } *ai = newNumInsts; setTmpInstruments(); } static void wipePattsUnused(bool testWipeSize, int16_t *ap) { uint8_t newPatt; int16_t usedPatts, newUsedPatts, i, *pLens; tonTyp **p; usedPatts = *ap; memset(pattUsed, 0, usedPatts); newUsedPatts = 0; for (i = 0; i < song.len; i++) { newPatt = song.songTab[i]; if (newPatt < usedPatts && !pattUsed[newPatt]) { pattUsed[newPatt] = true; newUsedPatts++; } } if (newUsedPatts == 0 || newUsedPatts == usedPatts) return; // nothing to do! newPatt = 0; memset(pattOrder, 0, usedPatts); for (i = 0; i < usedPatts; i++) { if (pattUsed[i]) pattOrder[i] = newPatt++; } if (testWipeSize) { p = tmpPatt; pLens = tmpPattLens; } else { p = patt; pLens = pattLens; } memcpy(oldPatts, p, usedPatts * sizeof (tonTyp *)); memcpy(oldPattLens, pLens, usedPatts * sizeof (int16_t)); memset(p, 0, usedPatts * sizeof (tonTyp *)); memset(pLens, 0, usedPatts * sizeof (int16_t)); // relocate patterns for (i = 0; i < usedPatts; i++) { p[i] = NULL; if (!pattUsed[i]) { if (!testWipeSize && oldPatts[i] != NULL) { free(oldPatts[i]); oldPatts[i] = NULL; } } else { newPatt = pattOrder[i]; p[newPatt] = oldPatts[i]; pLens[newPatt] = oldPattLens[i]; } } if (!testWipeSize) { for (i = 0; i < MAX_PATTERNS; i++) { if (patt[i] == NULL) pattLens[i] = 64; } // reorder order list (and clear unused entries) for (i = 0; i < 256; i++) { if (i < song.len) song.songTab[i] = pattOrder[song.songTab[i]]; else song.songTab[i] = 0; } } *ap = newUsedPatts; } static void wipeSamplesUnused(bool testWipeSize, int16_t ai) { uint8_t newSamp, smpUsed[16], smpOrder[16]; int16_t i, j, k, l; instrTyp *ins; sampleTyp *s, tempSamples[16]; for (i = 1; i <= ai; i++) { if (!testWipeSize) { if (instr[i] == NULL) l = 0; else l = i; ins = instr[l]; l = getUsedSamples(i); } else { if (tmpInstr[i] == NULL) l = 0; else l = i; ins = tmpInstr[l]; l = getUsedTempSamples(i); } memset(smpUsed, 0, l); if (l > 0) { for (j = 0; j < l; j++) { s = &ins->samp[j]; // check if sample is referenced in instrument for (k = 0; k < 96; k++) { if (ins->ta[k] == j) { smpUsed[j] = true; break; // sample is used } } if (k == 96) { // sample is unused if (s->origPek != NULL && !testWipeSize) free(s->origPek); memset(s, 0, sizeof (sampleTyp)); s->origPek = NULL; s->pek = NULL; } } // create re-order list newSamp = 0; memset(smpOrder, 0, l); for (j = 0; j < l; j++) { if (smpUsed[j]) smpOrder[j] = newSamp++; } // re-order samples memcpy(tempSamples, ins->samp, l * sizeof (sampleTyp)); memset(ins->samp, 0, l * sizeof (sampleTyp)); for (j = 0; j < l; j++) { if (smpUsed[j]) ins->samp[smpOrder[j]] = tempSamples[j]; } // re-order note->sample list for (j = 0; j < 96; j++) { newSamp = ins->ta[j]; if (smpUsed[newSamp]) ins->ta[j] = smpOrder[newSamp]; else ins->ta[j] = 0; } } } } static void wipeSmpDataAfterLoop(bool testWipeSize, int16_t ai) { int8_t *newPtr; int16_t l; instrTyp *ins; sampleTyp *s; for (int16_t i = 1; i <= ai; i++) { if (!testWipeSize) { if (instr[i] == NULL) l = 0; else l = i; ins = instr[l]; l = getUsedSamples(i); } else { if (tmpInstr[i] == NULL) l = 0; else l = i; ins = tmpInstr[l]; l = getUsedTempSamples(i); } for (int16_t j = 0; j < l; j++) { s = &ins->samp[j]; if (s->origPek != NULL && s->typ & 3 && s->len > 0 && s->len > s->repS+s->repL) { if (!testWipeSize) restoreSample(s); s->len = s->repS + s->repL; if (!testWipeSize) { if (s->len <= 0) { s->len = 0; free(s->origPek); s->origPek = NULL; s->pek = NULL; } else { newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN); if (newPtr != NULL) { s->origPek = newPtr; s->pek = s->origPek + SMP_DAT_OFFSET; } } } if (!testWipeSize) fixSample(s); } } } } static void convertSamplesTo8bit(bool testWipeSize, int16_t ai) { int8_t *dst8, smp8, *newPtr; int16_t *src16, k; int32_t newLen; instrTyp *ins; sampleTyp *s; for (int16_t i = 1; i <= ai; i++) { if (!testWipeSize) { if (instr[i] == NULL) k = 0; else k = i; ins = instr[k]; k = getUsedSamples(i); } else { if (tmpInstr[i] == NULL) k = 0; else k = i; ins = tmpInstr[k]; k = getUsedTempSamples(i); } for (int16_t j = 0; j < k; j++) { s = &ins->samp[j]; if (s->origPek != NULL && (s->typ & 16) && s->len > 0) { if (testWipeSize) { s->typ &= ~16; s->len >>= 1; s->repL >>= 1; s->repS >>= 1; } else { restoreSample(s); assert(s->pek != NULL); src16 = (int16_t *)s->pek; dst8 = s->pek; newLen = s->len >> 1; for (int32_t a = 0; a < newLen; a++) { smp8 = src16[a] >> 8; dst8[a] = smp8; } s->repL >>= 1; s->repS >>= 1; s->len >>= 1; s->typ &= ~16; newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN); if (newPtr != NULL) { s->origPek = newPtr; s->pek = s->origPek + SMP_DAT_OFFSET; } fixSample(s); } } } } } static uint16_t getPackedPattSize(tonTyp *pattern, int32_t numRows, int32_t antChn) { uint8_t bytes[sizeof (tonTyp)], packBits, *writePtr, *firstBytePtr, *pattPtr; uint16_t totalPackLen; totalPackLen = 0; pattPtr = (uint8_t *)pattern; writePtr = pattPtr; for (int32_t row = 0; row < numRows; row++) { for (int32_t chn = 0; chn < antChn; chn++) { bytes[0] = *pattPtr++; bytes[1] = *pattPtr++; bytes[2] = *pattPtr++; bytes[3] = *pattPtr++; bytes[4] = *pattPtr++; firstBytePtr = writePtr++; packBits = 0; if (bytes[0] > 0) { packBits |= 1; writePtr++; } // note if (bytes[1] > 0) { packBits |= 2; writePtr++; } // instrument if (bytes[2] > 0) { packBits |= 4; writePtr++; } // volume column if (bytes[3] > 0) { packBits |= 8; writePtr++; } // effect if (packBits == 15) // first four bits set? { // no packing needed, write pattern data as is totalPackLen += 5; writePtr += 5; continue; } if (bytes[4] > 0) writePtr++; // effect parameter totalPackLen += (uint16_t)(writePtr - firstBytePtr); // bytes writen } // skip unused channels pattPtr += sizeof (tonTyp) * (MAX_VOICES - antChn); } return totalPackLen; } static bool tmpPatternEmpty(uint16_t nr, int32_t antChn) { uint8_t *scanPtr; int32_t pattLen, scanLen; if (tmpPatt[nr] == NULL) return true; scanPtr = (uint8_t *)tmpPatt[nr]; scanLen = antChn * sizeof (tonTyp); pattLen = tmpPattLens[nr]; for (int32_t i = 0; i < pattLen; i++) { for (int32_t j = 0; j < scanLen; j++) { if (scanPtr[j] != 0) return false; } scanPtr += TRACK_WIDTH; } return true; } static int64_t calculateXMSize(void) { int16_t i, j, ap, ai, a; int64_t currSize64; instrTyp *ins; // count header size in song currSize64 = sizeof (songHeaderTyp); // count number of patterns that would be saved ap = MAX_PATTERNS; do { if (patternEmpty(ap - 1)) ap--; else break; } while (ap > 0); // count number of instruments ai = 128; while (ai > 0 && getUsedSamples(ai) == 0 && song.instrName[ai][0] == '\0') ai--; // count packed pattern data size in song for (i = 0; i < ap; i++) { currSize64 += sizeof (patternHeaderTyp); if (!patternEmpty(i)) currSize64 += getPackedPattSize(patt[i], pattLens[i], song.antChn); } // count instrument and sample data size in song for (i = 1; i <= ai; i++) { if (instr[i] == NULL) j = 0; else j = i; a = getUsedSamples(i); if (a > 0) currSize64 += INSTR_HEADER_SIZE + (a * sizeof (sampleHeaderTyp)); else currSize64 += 22+11; ins = instr[j]; for (int16_t k = 0; k < a; k++) { if (ins->samp[k].pek != NULL) currSize64 += ins->samp[k].len; } } return currSize64; } static int64_t calculateTrimSize(void) { int16_t ap, i, j, k, ai, highestChan, pattLen; int32_t antChn, pattDataLen, newPattDataLen; int64_t bytes64, oldInstrSize64, newInstrSize64; tonTyp *note, *pattPtr; antChn = song.antChn; pattDataLen = 0; newPattDataLen = 0; bytes64 = 0; oldInstrSize64 = 0; // copy over temp data memcpy(tmpPatt, patt, sizeof (tmpPatt)); memcpy(tmpPattLens, pattLens, sizeof (tmpPattLens)); memcpy(tmpInstrName, song.instrName, sizeof (tmpInstrName)); if (!setTmpInstruments()) { okBox(0, "System message", "Not enough memory!"); return 0; } // get current size of all instruments and their samples if (removeInst || removeSamp || removeSmpDataAfterLoop || convSmpsTo8Bit) oldInstrSize64 = getTempInsAndSmpSize(); // count number of patterns that would be saved ap = MAX_PATTERNS; do { if (tmpPatternEmpty(ap - 1, antChn)) ap--; else break; } while (ap > 0); // count number of instruments that would be saved ai = MAX_INST; while (ai > 0 && getUsedTempSamples(ai) == 0 && tmpInstrName[ai][0] == '\0') ai--; // calculate "remove unused samples" size if (removeSamp) wipeSamplesUnused(true, ai); // calculate "remove sample data after loop" size if (removeSmpDataAfterLoop) wipeSmpDataAfterLoop(true, ai); // calculate "convert samples to 8-bit" size if (convSmpsTo8Bit) convertSamplesTo8bit(true, ai); // get old pattern data length if (removeChans || removePatt) { for (i = 0; i < ap; i++) { pattDataLen += sizeof (patternHeaderTyp); if (!tmpPatternEmpty(i, antChn)) pattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], antChn); } } // calculate "remove unused channels" size if (removeChans) { // get real number of channels highestChan = -1; for (i = 0; i < ap; i++) { pattPtr = tmpPatt[i]; if (pattPtr == NULL) continue; pattLen = tmpPattLens[i]; for (j = 0; j < pattLen; j++) { for (k = 0; k < antChn; k++) { note = &pattPtr[(j * MAX_VOICES) + k]; if (note->eff || note->effTyp || note->instr || note->ton || note->vol) { if (k > highestChan) highestChan = k; } } } } // set new number of channels (and make it an even number) if (highestChan >= 0) { highestChan++; if (highestChan & 1) highestChan++; antChn = (uint8_t)(CLAMP(highestChan, 2, antChn)); } } // calculate "remove unused patterns" size if (removePatt) wipePattsUnused(true, &ap); // calculate new pattern data size if (removeChans || removePatt) { for (i = 0; i < ap; i++) { newPattDataLen += sizeof (patternHeaderTyp); if (!tmpPatternEmpty(i, antChn)) newPattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], antChn); } assert(pattDataLen >= newPattDataLen); if (pattDataLen > newPattDataLen) bytes64 += (pattDataLen - newPattDataLen); } // calculate "remove unused instruments" size if (removeInst) wipeInstrUnused(true, &ai, ap, antChn); // calculat new instruments and samples size if (removeInst || removeSamp || removeSmpDataAfterLoop || convSmpsTo8Bit) { newInstrSize64 = getTempInsAndSmpSize(); assert(oldInstrSize64 >= newInstrSize64); if (oldInstrSize64 > newInstrSize64) bytes64 += (oldInstrSize64 - newInstrSize64); } freeTmpInstruments(); return bytes64; } static int32_t SDLCALL trimThreadFunc(void *ptr) { int16_t ap, ai, i, j, k, pattLen, highestChan; tonTyp *pattPtr, *note; (void)ptr; if (!setTmpInstruments()) { okBoxThreadSafe(0, "System message", "Not enough memory!"); return true; } // audio callback is not running now, so we're safe // count number of patterns ap = MAX_PATTERNS; do { if (patternEmpty(ap - 1)) ap--; else break; } while (ap > 0); // count number of instruments ai = MAX_INST; while (ai > 0 && getUsedSamples(ai) == 0 && song.instrName[ai][0] == '\0') ai--; // remove unused samples if (removeSamp) wipeSamplesUnused(false, ai); // remove sample data after loop if (removeSmpDataAfterLoop) wipeSmpDataAfterLoop(false, ai); // convert samples to 8-bit if (convSmpsTo8Bit) convertSamplesTo8bit(false, ai); // removed unused channels if (removeChans) { // count used channels highestChan = -1; for (i = 0; i < ap; i++) { pattPtr = patt[i]; if (pattPtr == NULL) continue; pattLen = pattLens[i]; for (j = 0; j < pattLen; j++) { for (k = 0; k < song.antChn; k++) { note = &pattPtr[(j * MAX_VOICES) + k]; if (note->eff || note->effTyp || note->instr || note->ton || note->vol) { if (k > highestChan) highestChan = k; } } } } // set new 'channels used' number if (highestChan >= 0) { highestChan++; if (highestChan & 1) highestChan++; song.antChn = (uint8_t)(CLAMP(highestChan, 2, song.antChn)); } // clear potentially unused channel data if (song.antChn < MAX_VOICES) { for (i = 0; i < MAX_PATTERNS; i++) { pattPtr = patt[i]; if (pattPtr == NULL) continue; pattLen = pattLens[i]; for (j = 0; j < pattLen; j++) memset(&pattPtr[(j * MAX_VOICES) + song.antChn], 0, sizeof (tonTyp) * (MAX_VOICES - song.antChn)); } } } // clear unused patterns if (removePatt) wipePattsUnused(false, &ap); // remove unused instruments if (removeInst) wipeInstrUnused(false, &ai, ap, song.antChn); freeTmpInstruments(); editor.trimThreadWasDone = true; return true; } void trimThreadDone(void) { if (removePatt) setPos(song.songPos, song.pattPos, false); if (removeInst) { editor.currVolEnvPoint = 0; editor.currPanEnvPoint = 0; } updateTextBoxPointers(); hideTopScreen(); showTopScreen(true); showBottomScreen(); if (removeChans) { if (ui.patternEditorShown) { if (ui.channelOffset > song.antChn-ui.numChannelsShown) setScrollBarPos(SB_CHAN_SCROLL, song.antChn - ui.numChannelsShown, true); } if (cursor.ch >= ui.channelOffset+ui.numChannelsShown) cursor.ch = ui.channelOffset+ui.numChannelsShown - 1; } checkMarkLimits(); if (removeSamp || convSmpsTo8Bit) updateSampleEditorSample(); pbTrimCalc(); setSongModifiedFlag(); resumeAudio(); setMouseBusy(false); } static char *formatBytes(uint64_t bytes, bool roundUp) { double dBytes; if (bytes == 0) { strcpy(byteFormatBuffer, "0"); return byteFormatBuffer; } bytes %= 1000ULL*1024*1024*999; // wrap around gigabytes in case of overflow if (bytes >= 1024ULL*1024*1024*9) { // gigabytes dBytes = bytes / (1024.0*1024.0*1024.0); if (dBytes < 100) sprintf(byteFormatBuffer, "%.1fGB", dBytes); else sprintf(byteFormatBuffer, "%dGB", roundUp ? (int32_t)ceil(dBytes) : (int32_t)dBytes); } else if (bytes >= 1024*1024*9) { // megabytes dBytes = bytes / (1024.0*1024.0); if (dBytes < 100) sprintf(byteFormatBuffer, "%.1fMB", dBytes); else sprintf(byteFormatBuffer, "%dMB", roundUp ? (int32_t)ceil(dBytes) : (int32_t)dBytes); } else if (bytes >= 1024*9) { // kilobytes dBytes = bytes / 1024.0; if (dBytes < 100) sprintf(byteFormatBuffer, "%.1fkB", dBytes); else sprintf(byteFormatBuffer, "%dkB", roundUp ? (int32_t)ceil(dBytes) : (int32_t)dBytes); } else { // bytes sprintf(byteFormatBuffer, "%d", (int32_t)bytes); } return byteFormatBuffer; } void drawTrimScreen(void) { char sizeBuf[16]; drawFramework(0, 92, 136, 81, FRAMEWORK_TYPE1); drawFramework(136, 92, 155, 81, FRAMEWORK_TYPE1); textOutShadow(4, 95, PAL_FORGRND, PAL_DSKTOP2, "What to remove:"); textOutShadow(19, 109, PAL_FORGRND, PAL_DSKTOP2, "Unused patterns"); textOutShadow(19, 122, PAL_FORGRND, PAL_DSKTOP2, "Unused instruments"); textOutShadow(19, 135, PAL_FORGRND, PAL_DSKTOP2, "Unused samples"); textOutShadow(19, 148, PAL_FORGRND, PAL_DSKTOP2, "Unused channels"); textOutShadow(19, 161, PAL_FORGRND, PAL_DSKTOP2, "Smp. dat. after loop"); textOutShadow(155, 96, PAL_FORGRND, PAL_DSKTOP2, "Conv. samples to 8-bit"); textOutShadow(140, 111, PAL_FORGRND, PAL_DSKTOP2, ".xm size before"); textOutShadow(140, 124, PAL_FORGRND, PAL_DSKTOP2, ".xm size after"); textOutShadow(140, 137, PAL_FORGRND, PAL_DSKTOP2, "Bytes to save"); if (xmSize64 > -1) { sprintf(sizeBuf, "%s", formatBytes(xmSize64, true)); textOut(287 - textWidth(sizeBuf), 111, PAL_FORGRND, sizeBuf); } else { textOut(287 - textWidth("Unknown"), 111, PAL_FORGRND, "Unknown"); } if (xmAfterTrimSize64 > -1) { sprintf(sizeBuf, "%s", formatBytes(xmAfterTrimSize64, true)); textOut(287 - textWidth(sizeBuf), 124, PAL_FORGRND, sizeBuf); } else { textOut(287 - textWidth("Unknown"), 124, PAL_FORGRND, "Unknown"); } if (spaceSaved64 > -1) { sprintf(sizeBuf, "%s", formatBytes(spaceSaved64, false)); textOut(287 - textWidth(sizeBuf), 137, PAL_FORGRND, sizeBuf); } else { textOut(287 - textWidth("Unknown"), 137, PAL_FORGRND, "Unknown"); } showCheckBox(CB_TRIM_PATT); showCheckBox(CB_TRIM_INST); showCheckBox(CB_TRIM_SAMP); showCheckBox(CB_TRIM_CHAN); showCheckBox(CB_TRIM_SMPD); showCheckBox(CB_TRIM_CONV); showPushButton(PB_TRIM_CALC); showPushButton(PB_TRIM_TRIM); } void hideTrimScreen(void) { hideCheckBox(CB_TRIM_PATT); hideCheckBox(CB_TRIM_INST); hideCheckBox(CB_TRIM_SAMP); hideCheckBox(CB_TRIM_CHAN); hideCheckBox(CB_TRIM_SMPD); hideCheckBox(CB_TRIM_CONV); hidePushButton(PB_TRIM_CALC); hidePushButton(PB_TRIM_TRIM); ui.trimScreenShown = false; ui.scopesShown = true; drawScopeFramework(); } void showTrimScreen(void) { if (ui.extended) exitPatternEditorExtended(); hideTopScreen(); showTopScreen(false); ui.trimScreenShown = true; ui.scopesShown = false; drawTrimScreen(); } void toggleTrimScreen(void) { if (ui.trimScreenShown) hideTrimScreen(); else showTrimScreen(); } void setInitialTrimFlags(void) { removePatt = true; removeInst = true; removeSamp = true; removeChans = true; removeSmpDataAfterLoop = true; convSmpsTo8Bit = false; checkBoxes[CB_TRIM_PATT].checked = true; checkBoxes[CB_TRIM_INST].checked = true; checkBoxes[CB_TRIM_SAMP].checked = true; checkBoxes[CB_TRIM_CHAN].checked = true; checkBoxes[CB_TRIM_SMPD].checked = true; checkBoxes[CB_TRIM_CONV].checked = false; } void cbTrimUnusedPatt(void) { removePatt ^= 1; } void cbTrimUnusedInst(void) { removeInst ^= 1; } void cbTrimUnusedSamp(void) { removeSamp ^= 1; } void cbTrimUnusedChans(void) { removeChans ^= 1; } void cbTrimUnusedSmpData(void) { removeSmpDataAfterLoop ^= 1; } void cbTrimSmpsTo8Bit(void) { convSmpsTo8Bit ^= 1; } void pbTrimCalc(void) { xmSize64 = calculateXMSize(); spaceSaved64 = calculateTrimSize(); xmAfterTrimSize64 = xmSize64 - spaceSaved64; if (xmAfterTrimSize64 < 0) xmAfterTrimSize64 = 0; if (ui.trimScreenShown) drawTrimScreen(); } void pbTrimDoTrim(void) { if (!removePatt && !removeInst && !removeSamp && !removeChans && !removeSmpDataAfterLoop && !convSmpsTo8Bit) return; // nothing to trim... if (okBox(2, "System request", "Are you sure you want to trim the song? Making a backup of the song first is recommended.") != 1) return; mouseAnimOn(); pauseAudio(); trimThread = SDL_CreateThread(trimThreadFunc, NULL, NULL); if (trimThread == NULL) { resumeAudio(); mouseAnimOff(); return; } SDL_DetachThread(trimThread); } void resetTrimSizes(void) { xmSize64 = -1; xmAfterTrimSize64 = -1; spaceSaved64 = -1; if (ui.trimScreenShown) drawTrimScreen(); }