ref: eaee288d876e3dd4e2fc0fa7ee9ab17bf8d1eb6c
dir: /src/ft2_sample_ed_features.c/
/* This file contains the routines for the following sample editor functions: ** - Resampler ** - Echo ** - Mix ** - Volume **/ // for finding memory leaks in debug mode with Visual Studio #if defined _DEBUG && defined _MSC_VER #include <crtdbg.h> #endif #include <stdio.h> #include <stdint.h> #include <stdbool.h> #include <math.h> #include "ft2_header.h" #include "ft2_mouse.h" #include "ft2_audio.h" #include "ft2_gui.h" #include "ft2_events.h" #include "ft2_video.h" #include "ft2_inst_ed.h" #include "ft2_sample_ed.h" #include "ft2_keyboard.h" #include "ft2_tables.h" #include "ft2_structs.h" static volatile bool stopThread; static int8_t smpEd_RelReSmp, mix_Balance = 50; static bool echo_AddMemory, exitFlag, outOfMemory; static int16_t vol_StartVol = 100, vol_EndVol = 100, echo_nEcho = 1, echo_VolChange = 30; static int32_t echo_Distance = 0x100; static SDL_Thread *thread; static void pbExit(void) { ui.sysReqShown = false; exitFlag = true; } static void windowOpen(void) { ui.sysReqShown = true; ui.sysReqEnterPressed = false; unstuckLastUsedGUIElement(); SDL_EventState(SDL_DROPFILE, SDL_DISABLE); } static void windowClose(bool rewriteSample) { SDL_EventState(SDL_DROPFILE, SDL_ENABLE); if (exitFlag || rewriteSample) writeSample(true); else updateNewSample(); mouseAnimOff(); } static void sbSetResampleTones(uint32_t pos) { if (smpEd_RelReSmp != (int8_t)(pos - 36)) smpEd_RelReSmp = (int8_t)(pos - 36); } static void pbResampleTonesDown(void) { if (smpEd_RelReSmp > -36) smpEd_RelReSmp--; } static void pbResampleTonesUp(void) { if (smpEd_RelReSmp < 36) smpEd_RelReSmp++; } static int32_t SDLCALL resampleThread(void *ptr) { int8_t *p1, *p2, *src8, *dst8; int16_t *src16, *dst16; uint32_t newLen, mask, resampleLen; uint64_t posFrac64, delta64; double dNewLen, dLenMul; sampleTyp *s; (void)ptr; if (instr[editor.curInstr] == NULL) return true; s = &instr[editor.curInstr]->samp[editor.curSmp]; mask = (s->typ & 16) ? 0xFFFFFFFE : 0xFFFFFFFF; dLenMul = exp2(smpEd_RelReSmp * (1.0 / 12.0)); dNewLen = s->len * dLenMul; if (dNewLen > (double)MAX_SAMPLE_LEN) dNewLen = (double)MAX_SAMPLE_LEN; newLen = (int32_t)dNewLen & mask; p2 = (int8_t *)malloc(newLen + LOOP_FIX_LEN); if (p2 == NULL) { outOfMemory = true; setMouseBusy(false); ui.sysReqShown = false; return true; } int8_t *newPtr = p2 + SMP_DAT_OFFSET; p1 = s->pek; // don't use the potentially clamped newLen value here delta64 = ((int64_t)s->len << 32) / (int64_t)(s->len * dLenMul); // 32.32 fixed point delta posFrac64 = 0; // 32.32 fixed point position.fraction pauseAudio(); restoreSample(s); if (newLen > 0) { if (s->typ & 16) { src16 = (int16_t *)p1; dst16 = (int16_t *)newPtr; resampleLen = newLen >> 1; for (uint32_t i = 0; i < resampleLen; i++) { dst16[i] = src16[posFrac64 >> 32]; posFrac64 += delta64; } } else { src8 = p1; dst8 = newPtr; for (uint32_t i = 0; i < newLen; i++) { dst8[i] = src8[posFrac64 >> 32]; posFrac64 += delta64; } } } free(s->origPek); s->relTon = CLAMP(s->relTon + smpEd_RelReSmp, -48, 71); s->len = newLen & mask; s->origPek = p2; s->pek = s->origPek + SMP_DAT_OFFSET; s->repS = (int32_t)(s->repS * dLenMul); s->repL = (int32_t)(s->repL * dLenMul); s->repS &= mask; s->repL &= mask; if (s->repS >= s->len) s->repS = s->len-1; if (s->repS+s->repL > s->len) s->repL = s->len - s->repS; if (s->typ & 16) { s->len &= 0xFFFFFFFE; s->repS &= 0xFFFFFFFE; s->repL &= 0xFFFFFFFE; } if (s->repL <= 0) s->typ &= ~3; // disable loop fixSample(s); resumeAudio(); setSongModifiedFlag(); setMouseBusy(false); ui.sysReqShown = false; return true; } static void pbDoResampling(void) { mouseAnimOn(); thread = SDL_CreateThread(resampleThread, NULL, NULL); if (thread == NULL) { okBox(0, "System message", "Couldn't create thread!"); return; } SDL_DetachThread(thread); } static void drawResampleBox(void) { char sign; const int16_t x = 209; const int16_t y = 230; const int16_t w = 214; const int16_t h = 54; uint16_t val; uint32_t mask; double dNewLen, dLenMul; sampleTyp *s; // main fill fillRect(x + 1, y + 1, w - 2, h - 2, PAL_BUTTONS); // outer border vLine(x, y, h - 1, PAL_BUTTON1); hLine(x + 1, y, w - 2, PAL_BUTTON1); vLine(x + w - 1, y, h, PAL_BUTTON2); hLine(x, y + h - 1, w - 1, PAL_BUTTON2); // inner border vLine(x + 2, y + 2, h - 5, PAL_BUTTON2); hLine(x + 3, y + 2, w - 6, PAL_BUTTON2); vLine(x + w - 3, y + 2, h - 4, PAL_BUTTON1); hLine(x + 2, y + h - 3, w - 4, PAL_BUTTON1); s = &instr[editor.curInstr]->samp[editor.curSmp]; mask = (s->typ & 16) ? 0xFFFFFFFE : 0xFFFFFFFF; dLenMul = exp2(smpEd_RelReSmp * (1.0 / 12.0)); dNewLen = s->len * dLenMul; if (dNewLen > (double)MAX_SAMPLE_LEN) dNewLen = (double)MAX_SAMPLE_LEN; textOutShadow(215, 236, PAL_FORGRND, PAL_BUTTON2, "Rel. h.tones"); textOutShadow(215, 250, PAL_FORGRND, PAL_BUTTON2, "New sample size"); hexOut(361, 250, PAL_FORGRND, (uint32_t)dNewLen & mask, 8); if (smpEd_RelReSmp == 0) sign = ' '; else if (smpEd_RelReSmp < 0) sign = '-'; else sign = '+'; val = ABS(smpEd_RelReSmp); if (val > 9) { charOut(291, 236, PAL_FORGRND, sign); charOut(298, 236, PAL_FORGRND, '0' + ((val / 10) % 10)); charOut(305, 236, PAL_FORGRND, '0' + (val % 10)); } else { charOut(298, 236, PAL_FORGRND, sign); charOut(305, 236, PAL_FORGRND, '0' + (val % 10)); } } static void setupResampleBoxWidgets(void) { pushButton_t *p; scrollBar_t *s; // "Apply" pushbutton p = &pushButtons[0]; memset(p, 0, sizeof (pushButton_t)); p->caption = "Apply"; p->x = 214; p->y = 264; p->w = 73; p->h = 16; p->callbackFuncOnUp = pbDoResampling; p->visible = true; // "Exit" pushbutton p = &pushButtons[1]; memset(p, 0, sizeof (pushButton_t)); p->caption = "Exit"; p->x = 345; p->y = 264; p->w = 73; p->h = 16; p->callbackFuncOnUp = pbExit; p->visible = true; // scrollbar buttons p = &pushButtons[2]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_LEFT_STRING; p->x = 314; p->y = 234; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbResampleTonesDown; p->visible = true; p = &pushButtons[3]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_RIGHT_STRING; p->x = 395; p->y = 234; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbResampleTonesUp; p->visible = true; // echo num scrollbar s = &scrollBars[0]; memset(s, 0, sizeof (scrollBar_t)); s->x = 337; s->y = 234; s->w = 58; s->h = 13; s->callbackFunc = sbSetResampleTones; s->visible = true; setScrollBarPageLength(0, 1); setScrollBarEnd(0, 36 * 2); } void pbSampleResample(void) { uint16_t i; if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) { return; } setupResampleBoxWidgets(); windowOpen(); outOfMemory = false; exitFlag = false; while (ui.sysReqShown) { readInput(); if (ui.sysReqEnterPressed) pbDoResampling(); setSyncedReplayerVars(); handleRedrawing(); drawResampleBox(); setScrollBarPos(0, smpEd_RelReSmp + 36, false); drawCheckBox(0); for (i = 0; i < 4; i++) drawPushButton(i); drawScrollBar(0); flipFrame(); } for (i = 0; i < 4; i++) hidePushButton(i); hideScrollBar(0); windowClose(false); if (outOfMemory) okBox(0, "System message", "Not enough memory!"); } static void cbEchoAddMemory(void) { echo_AddMemory ^= 1; } static void sbSetEchoNumPos(uint32_t pos) { if (echo_nEcho != (int32_t)pos) echo_nEcho = (int16_t)pos; } static void sbSetEchoDistPos(uint32_t pos) { if (echo_Distance != (int32_t)pos) echo_Distance = (int32_t)pos; } static void sbSetEchoFadeoutPos(uint32_t pos) { if (echo_VolChange != (int32_t)pos) echo_VolChange = (int16_t)pos; } static void pbEchoNumDown(void) { if (echo_nEcho > 0) echo_nEcho--; } static void pbEchoNumUp(void) { if (echo_nEcho < 1024) echo_nEcho++; } static void pbEchoDistDown(void) { if (echo_Distance > 0) echo_Distance--; } static void pbEchoDistUp(void) { if (echo_Distance < 16384) echo_Distance++; } static void pbEchoFadeoutDown(void) { if (echo_VolChange > 0) echo_VolChange--; } static void pbEchoFadeoutUp(void) { if (echo_VolChange < 100) echo_VolChange++; } static int32_t SDLCALL createEchoThread(void *ptr) { int8_t *readPtr, *writePtr, *writePtr8, *newPtr; bool is16Bit; int16_t *readPtr16, *writePtr16; int32_t nEchoes, distance, readLen, writeLen, i, j; int32_t smpOut, volChange, smpMul, echoRead, echoCycle, writeIdx; int64_t tmp64; sampleTyp *s; (void)ptr; s = &instr[editor.curInstr]->samp[editor.curSmp]; readLen = s->len; readPtr = s->pek; is16Bit = (s->typ & 16) ? true : false; distance = echo_Distance * 16; // scale value for faster math and suitable rounding for PCM waveforms (DIV -> arithmetic bitshift right) volChange = (echo_VolChange * 256) / 100; // 0..100 -> 0..256 if (echo_nEcho < 1) { ui.sysReqShown = false; return true; } // calculate real number of echoes j = 32768; i = 0; while (i < echo_nEcho && j > 0) { j = (j * volChange) >> 8; i++; } nEchoes = i + 1; if (nEchoes < 1) { ui.sysReqShown = false; return true; } // set write length (either original length or full echo length) writeLen = readLen; if (echo_AddMemory) { tmp64 = (int64_t)distance * (nEchoes - 1); if (is16Bit) tmp64 <<= 1; tmp64 += writeLen; if (tmp64 > MAX_SAMPLE_LEN) tmp64 = MAX_SAMPLE_LEN; writeLen = (int32_t)tmp64; if (is16Bit) writeLen &= 0xFFFFFFFE; } writePtr = (int8_t *)malloc(writeLen + LOOP_FIX_LEN); if (writePtr == NULL) { outOfMemory = true; setMouseBusy(false); ui.sysReqShown = false; return false; } pauseAudio(); restoreSample(s); writeIdx = 0; if (is16Bit) { readPtr16 = (int16_t *)readPtr; writePtr16 = (int16_t *)&writePtr[SMP_DAT_OFFSET]; writeLen >>= 1; readLen >>= 1; while (writeIdx < writeLen) { smpOut = 0; smpMul = 32768; echoRead = writeIdx; echoCycle = nEchoes; while (!stopThread) { if (echoRead < readLen) smpOut += (readPtr16[echoRead] * smpMul) >> 15; smpMul = (smpMul * volChange) >> 8; echoRead -= distance; echoCycle--; if (echoRead <= 0 || echoCycle <= 0) break; } CLAMP16(smpOut); writePtr16[writeIdx++] = (int16_t)smpOut; } writeLen <<= 1; } else { writePtr8 = writePtr + SMP_DAT_OFFSET; while (writeIdx < writeLen) { smpOut = 0; smpMul = 32768; echoRead = writeIdx; echoCycle = nEchoes; while (!stopThread) { if (echoRead < readLen) smpOut += (readPtr[echoRead] * smpMul) >> (15-8); smpMul = (smpMul * volChange) >> 8; echoRead -= distance; echoCycle--; if (echoRead <= 0 || echoCycle <= 0) break; } CLAMP16(smpOut); writePtr8[writeIdx++] = (int8_t)(smpOut >> 8); } } free(s->origPek); if (stopThread) { writeLen = writeIdx; newPtr = (int8_t *)realloc(writePtr, writeIdx + LOOP_FIX_LEN); if (newPtr != NULL) { s->origPek = newPtr; s->pek = s->origPek + SMP_DAT_OFFSET; } else { if (writePtr != NULL) free(writePtr); s->origPek = s->pek = NULL; writeLen = 0; } editor.updateCurSmp = true; } else { s->origPek = writePtr; s->pek = s->origPek + SMP_DAT_OFFSET; } if (is16Bit) writeLen &= 0xFFFFFFFE; s->len = writeLen; fixSample(s); resumeAudio(); setSongModifiedFlag(); setMouseBusy(false); ui.sysReqShown = false; return true; } static void pbCreateEcho(void) { stopThread = false; mouseAnimOn(); thread = SDL_CreateThread(createEchoThread, NULL, NULL); if (thread == NULL) { okBox(0, "System message", "Couldn't create thread!"); return; } SDL_DetachThread(thread); } static void drawEchoBox(void) { const int16_t x = 171; const int16_t y = 220; const int16_t w = 291; const int16_t h = 66; // main fill fillRect(x + 1, y + 1, w - 2, h - 2, PAL_BUTTONS); // outer border vLine(x, y, h - 1, PAL_BUTTON1); hLine(x + 1, y, w - 2, PAL_BUTTON1); vLine(x + w - 1, y, h, PAL_BUTTON2); hLine(x, y + h - 1, w - 1, PAL_BUTTON2); // inner border vLine(x + 2, y + 2, h - 5, PAL_BUTTON2); hLine(x + 3, y + 2, w - 6, PAL_BUTTON2); vLine(x + w - 3, y + 2, h - 4, PAL_BUTTON1); hLine(x + 2, y + h - 3, w - 4, PAL_BUTTON1); textOutShadow(177, 226, PAL_FORGRND, PAL_BUTTON2, "Number of echoes"); textOutShadow(177, 240, PAL_FORGRND, PAL_BUTTON2, "Echo distance"); textOutShadow(177, 254, PAL_FORGRND, PAL_BUTTON2, "Fade out"); textOutShadow(192, 270, PAL_FORGRND, PAL_BUTTON2, "Add memory to sample"); assert(echo_nEcho <= 1024); charOut(315 + (0 * 7), 226, PAL_FORGRND, '0' + (char)(echo_nEcho / 1000)); charOut(315 + (1 * 7), 226, PAL_FORGRND, '0' + (echo_nEcho / 100) % 10); charOut(315 + (2 * 7), 226, PAL_FORGRND, '0' + (echo_nEcho / 10) % 10); charOut(315 + (3 * 7), 226, PAL_FORGRND, '0' + (echo_nEcho % 10)); assert(echo_Distance <= 0x4000); hexOut(308, 240, PAL_FORGRND, (uint32_t)echo_Distance << 4, 5); assert(echo_VolChange <= 100); textOutFixed(312, 254, PAL_FORGRND, PAL_BUTTONS, dec3StrTab[echo_VolChange]); charOutShadow(313 + (3 * 7), 254, PAL_FORGRND, PAL_BUTTON2, '%'); } static void setupEchoBoxWidgets(void) { checkBox_t *c; pushButton_t *p; scrollBar_t *s; // "Add memory to sample" checkbox c = &checkBoxes[0]; memset(c, 0, sizeof (checkBox_t)); c->x = 176; c->y = 268; c->clickAreaWidth = 146; c->clickAreaHeight = 12; c->callbackFunc = cbEchoAddMemory; c->checked = echo_AddMemory ? CHECKBOX_CHECKED : CHECKBOX_UNCHECKED; c->visible = true; // "Apply" pushbutton p = &pushButtons[0]; memset(p, 0, sizeof (pushButton_t)); p->caption = "Apply"; p->x = 345; p->y = 266; p->w = 56; p->h = 16; p->callbackFuncOnUp = pbCreateEcho; p->visible = true; // "Exit" pushbutton p = &pushButtons[1]; memset(p, 0, sizeof (pushButton_t)); p->caption = "Exit"; p->x = 402; p->y = 266; p->w = 55; p->h = 16; p->callbackFuncOnUp = pbExit; p->visible = true; // scrollbar buttons p = &pushButtons[2]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_LEFT_STRING; p->x = 345; p->y = 224; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbEchoNumDown; p->visible = true; p = &pushButtons[3]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_RIGHT_STRING; p->x = 434; p->y = 224; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbEchoNumUp; p->visible = true; p = &pushButtons[4]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_LEFT_STRING; p->x = 345; p->y = 238; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbEchoDistDown; p->visible = true; p = &pushButtons[5]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_RIGHT_STRING; p->x = 434; p->y = 238; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbEchoDistUp; p->visible = true; p = &pushButtons[6]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_LEFT_STRING; p->x = 345; p->y = 252; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbEchoFadeoutDown; p->visible = true; p = &pushButtons[7]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_RIGHT_STRING; p->x = 434; p->y = 252; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbEchoFadeoutUp; p->visible = true; // echo num scrollbar s = &scrollBars[0]; memset(s, 0, sizeof (scrollBar_t)); s->x = 368; s->y = 224; s->w = 66; s->h = 13; s->callbackFunc = sbSetEchoNumPos; s->visible = true; setScrollBarPageLength(0, 1); setScrollBarEnd(0, 1024); // echo distance scrollbar s = &scrollBars[1]; memset(s, 0, sizeof (scrollBar_t)); s->x = 368; s->y = 238; s->w = 66; s->h = 13; s->callbackFunc = sbSetEchoDistPos; s->visible = true; setScrollBarPageLength(1, 1); setScrollBarEnd(1, 16384); // echo fadeout scrollbar s = &scrollBars[2]; memset(s, 0, sizeof (scrollBar_t)); s->x = 368; s->y = 252; s->w = 66; s->h = 13; s->callbackFunc = sbSetEchoFadeoutPos; s->visible = true; setScrollBarPageLength(2, 1); setScrollBarEnd(2, 100); } void handleEchoToolPanic(void) { stopThread = true; } void pbSampleEcho(void) { uint16_t i; if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) { return; } setupEchoBoxWidgets(); windowOpen(); outOfMemory = false; exitFlag = false; while (ui.sysReqShown) { readInput(); if (ui.sysReqEnterPressed) pbCreateEcho(); setSyncedReplayerVars(); handleRedrawing(); drawEchoBox(); setScrollBarPos(0, echo_nEcho, false); setScrollBarPos(1, echo_Distance, false); setScrollBarPos(2, echo_VolChange, false); drawCheckBox(0); for (i = 0; i < 8; i++) drawPushButton(i); for (i = 0; i < 3; i++) drawScrollBar(i); flipFrame(); } hideCheckBox(0); for (i = 0; i < 8; i++) hidePushButton(i); for (i = 0; i < 3; i++) hideScrollBar(i); windowClose(echo_AddMemory ? false : true); if (outOfMemory) okBox(0, "System message", "Not enough memory!"); } static int32_t SDLCALL mixThread(void *ptr) { int8_t *destPtr, *mixPtr, *p; uint8_t mixTyp, destTyp; int16_t destIns, destSmp, mixIns, mixSmp; int32_t mixMul1, mixMul2, smp32, x1, x2, i, destLen, mixLen, maxLen, dest8Size, max8Size, mix8Size; (void)ptr; destIns = editor.curInstr; destSmp = editor.curSmp; mixIns = editor.srcInstr; mixSmp = editor.srcSmp; if (destIns == mixIns && destSmp == mixSmp) { setMouseBusy(false); ui.sysReqShown = false; return true; } if (instr[mixIns] == NULL) { mixLen = 0; mixPtr = NULL; mixTyp = 0; } else { mixLen = instr[mixIns]->samp[mixSmp].len; mixPtr = instr[mixIns]->samp[mixSmp].pek; mixTyp = instr[mixIns]->samp[mixSmp].typ; if (mixPtr == NULL) { mixLen = 0; mixTyp = 0; } } if (instr[destIns] == NULL) { destLen = 0; destPtr = NULL; destTyp = 0; } else { destLen = instr[destIns]->samp[destSmp].len; destPtr = instr[destIns]->samp[destSmp].pek; destTyp = instr[destIns]->samp[destSmp].typ; if (destPtr == NULL) { destLen = 0; destTyp = 0; } } bool src16Bits = (mixTyp >> 4) & 1; bool dst16Bits = (destTyp >> 4) & 1; mix8Size = src16Bits ? (mixLen >> 1) : mixLen; dest8Size = dst16Bits ? (destLen >> 1) : destLen; max8Size = (dest8Size > mix8Size) ? dest8Size : mix8Size; maxLen = dst16Bits ? (max8Size << 1) : max8Size; if (maxLen <= 0) { setMouseBusy(false); ui.sysReqShown = false; return true; } p = (int8_t *)calloc(maxLen + LOOP_FIX_LEN, sizeof (int8_t)); if (p == NULL) { outOfMemory = true; setMouseBusy(false); ui.sysReqShown = false; return true; } if (instr[destIns] == NULL && !allocateInstr(destIns)) { outOfMemory = true; setMouseBusy(false); ui.sysReqShown = false; return true; } pauseAudio(); restoreSample(&instr[destIns]->samp[destSmp]); // restore source sample if (instr[mixIns] != NULL) restoreSample(&instr[mixIns]->samp[mixSmp]); // scale value for faster math and suitable rounding for PCM waveforms (DIV -> arithmetic bitshift right) mixMul1 = (mix_Balance * 256) / 100; mixMul2 = 256 - mixMul1; int8_t *destPek = p + SMP_DAT_OFFSET; for (i = 0; i < max8Size; i++) { int32_t index16 = i << 1; x1 = (i >= mix8Size) ? 0 : getSampleValue(mixPtr, mixTyp, src16Bits ? index16 : i); x2 = (i >= dest8Size) ? 0 : getSampleValue(destPtr, destTyp, dst16Bits ? index16 : i); if (!src16Bits) x1 <<= 8; if (!dst16Bits) x2 <<= 8; smp32 = ((x1 * mixMul1) >> 8) + ((x2 * mixMul2) >> 8); CLAMP16(smp32); if (!dst16Bits) smp32 >>= 8; putSampleValue(destPek, destTyp, dst16Bits ? index16 : i, (int16_t)smp32); } if (instr[destIns]->samp[destSmp].origPek != NULL) free(instr[destIns]->samp[destSmp].origPek); instr[destIns]->samp[destSmp].origPek = p; instr[destIns]->samp[destSmp].pek = instr[destIns]->samp[destSmp].origPek + SMP_DAT_OFFSET; instr[destIns]->samp[destSmp].len = maxLen; instr[destIns]->samp[destSmp].typ = destTyp; if (dst16Bits) instr[destIns]->samp[destSmp].len &= 0xFFFFFFFE; fixSample(&instr[destIns]->samp[destSmp]); // fix source sample if (instr[mixIns] != NULL) fixSample(&instr[mixIns]->samp[mixSmp]); resumeAudio(); setSongModifiedFlag(); setMouseBusy(false); ui.sysReqShown = false; return true; } static void pbMix(void) { mouseAnimOn(); thread = SDL_CreateThread(mixThread, NULL, NULL); if (thread == NULL) { okBox(0, "System message", "Couldn't create thread!"); return; } SDL_DetachThread(thread); } static void sbSetMixBalancePos(uint32_t pos) { if (mix_Balance != (int8_t)pos) mix_Balance = (int8_t)pos; } static void pbMixBalanceDown(void) { if (mix_Balance > 0) mix_Balance--; } static void pbMixBalanceUp(void) { if (mix_Balance < 100) mix_Balance++; } static void drawMixSampleBox(void) { const int16_t x = 192; const int16_t y = 240; const int16_t w = 248; const int16_t h = 38; // main fill fillRect(x + 1, y + 1, w - 2, h - 2, PAL_BUTTONS); // outer border vLine(x, y, h - 1, PAL_BUTTON1); hLine(x + 1, y, w - 2, PAL_BUTTON1); vLine(x + w - 1, y, h, PAL_BUTTON2); hLine(x, y + h - 1, w - 1, PAL_BUTTON2); // inner border vLine(x + 2, y + 2, h - 5, PAL_BUTTON2); hLine(x + 3, y + 2, w - 6, PAL_BUTTON2); vLine(x + w - 3, y + 2, h - 4, PAL_BUTTON1); hLine(x + 2, y + h - 3, w - 4, PAL_BUTTON1); textOutShadow(198, 246, PAL_FORGRND, PAL_BUTTON2, "Mixing balance"); assert((mix_Balance >= 0) && (mix_Balance <= 100)); textOutFixed(299, 246, PAL_FORGRND, PAL_BUTTONS, dec3StrTab[mix_Balance]); } static void setupMixBoxWidgets(void) { pushButton_t *p; scrollBar_t *s; // "Apply" pushbutton p = &pushButtons[0]; memset(p, 0, sizeof (pushButton_t)); p->caption = "Apply"; p->x = 197; p->y = 258; p->w = 73; p->h = 16; p->callbackFuncOnUp = pbMix; p->visible = true; // "Exit" pushbutton p = &pushButtons[1]; memset(p, 0, sizeof (pushButton_t)); p->caption = "Exit"; p->x = 361; p->y = 258; p->w = 73; p->h = 16; p->callbackFuncOnUp = pbExit; p->visible = true; // scrollbar buttons p = &pushButtons[2]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_LEFT_STRING; p->x = 322; p->y = 244; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbMixBalanceDown; p->visible = true; p = &pushButtons[3]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_RIGHT_STRING; p->x = 411; p->y = 244; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbMixBalanceUp; p->visible = true; // mixing balance scrollbar s = &scrollBars[0]; memset(s, 0, sizeof (scrollBar_t)); s->x = 345; s->y = 244; s->w = 66; s->h = 13; s->callbackFunc = sbSetMixBalancePos; s->visible = true; setScrollBarPageLength(0, 1); setScrollBarEnd(0, 100); } void pbSampleMix(void) { uint16_t i; if (editor.curInstr == 0) return; setupMixBoxWidgets(); windowOpen(); outOfMemory = false; exitFlag = false; while (ui.sysReqShown) { readInput(); if (ui.sysReqEnterPressed) pbMix(); setSyncedReplayerVars(); handleRedrawing(); drawMixSampleBox(); setScrollBarPos(0, mix_Balance, false); for (i = 0; i < 4; i++) drawPushButton(i); drawScrollBar(0); flipFrame(); } for (i = 0; i < 4; i++) hidePushButton(i); hideScrollBar(0); windowClose(false); if (outOfMemory) okBox(0, "System message", "Not enough memory!"); } static void sbSetStartVolPos(uint32_t pos) { int16_t val = (int16_t)(pos - 500); if (val != vol_StartVol) { if (ABS(val) < 10) val = 0; else if (ABS(val - 100) < 10) val = 100; else if (ABS(val - 200) < 10) val = 200; else if (ABS(val - 300) < 10) val = 300; else if (ABS(val - 400) < 10) val = 400; else if (ABS(val + 100) < 10) val = -100; else if (ABS(val + 200) < 10) val = -200; else if (ABS(val + 300) < 10) val = -300; else if (ABS(val + 400) < 10) val = -400; vol_StartVol = val; } } static void sbSetEndVolPos(uint32_t pos) { int16_t val = (int16_t)(pos - 500); if (val != vol_EndVol) { if (ABS(val) < 10) val = 0; else if (ABS(val - 100) < 10) val = 100; else if (ABS(val - 200) < 10) val = 200; else if (ABS(val - 300) < 10) val = 300; else if (ABS(val - 400) < 10) val = 400; else if (ABS(val + 100) < 10) val = -100; else if (ABS(val + 200) < 10) val = -200; else if (ABS(val + 300) < 10) val = -300; else if (ABS(val + 400) < 10) val = -400; vol_EndVol = val; } } static void pbSampStartVolDown(void) { if (vol_StartVol > -500) vol_StartVol--; } static void pbSampStartVolUp(void) { if (vol_StartVol < 500) vol_StartVol++; } static void pbSampEndVolDown(void) { if (vol_EndVol > -500) vol_EndVol--; } static void pbSampEndVolUp(void) { if (vol_EndVol < 500) vol_EndVol++; } static int32_t SDLCALL applyVolumeThread(void *ptr) { #define AMP_BITS 15 int8_t *ptr8; int16_t *ptr16; int32_t amp32, x1, x2, len, i; sampleTyp *s; if (instr[editor.curInstr] == NULL) goto applyVolumeExit; s = &instr[editor.curInstr]->samp[editor.curSmp]; if (smpEd_Rx1 < smpEd_Rx2) { x1 = smpEd_Rx1; x2 = smpEd_Rx2; if (x2 > s->len) x2 = s->len; if (x1 < 0) x1 = 0; if (x2 <= x1) goto applyVolumeExit; if (s->typ & 16) { x1 &= 0xFFFFFFFE; x2 &= 0xFFFFFFFE; } } else { x1 = 0; x2 = s->len; } if (s->typ & 16) { x1 >>= 1; x2 >>= 1; } len = x2 - x1; if (len <= 0) goto applyVolumeExit; const double dVol1 = vol_StartVol * ((1 << AMP_BITS) / 100.0); const double dVol2 = vol_EndVol * ((1 << AMP_BITS) / 100.0); /* 8bitbubsy: Rewritten to use 64-bit deltas instead, to ** prevent having to do a 64-bit mul for every output sample. */ const int64_t delta64 = (int64_t)round(((dVol2 - dVol1) * (UINT32_MAX+1.0)) / len); int64_t pos64 = (int64_t)dVol1 << 32; pauseAudio(); restoreSample(s); if (s->typ & 16) { ptr16 = (int16_t *)s->pek; for (i = x1; i < x2; i++) { amp32 = (int32_t)(pos64 >> 32); pos64 += delta64; amp32 = (ptr16[i] * amp32) >> AMP_BITS; CLAMP16(amp32); ptr16[i] = (int16_t)amp32; } } else { ptr8 = s->pek; for (i = x1; i < x2; i++) { amp32 = (int32_t)(pos64 >> 32); pos64 += delta64; amp32 = (ptr8[i] * amp32) >> AMP_BITS; CLAMP8(amp32); ptr8[i] = (int8_t)amp32; } } fixSample(s); resumeAudio(); setSongModifiedFlag(); applyVolumeExit: setMouseBusy(false); ui.sysReqShown = false; (void)ptr; return true; } static void pbApplyVolume(void) { if (vol_StartVol == 100 && vol_EndVol == 100) { ui.sysReqShown = false; return; // no volume change to be done } mouseAnimOn(); thread = SDL_CreateThread(applyVolumeThread, NULL, NULL); if (thread == NULL) { okBox(0, "System message", "Couldn't create thread!"); return; } SDL_DetachThread(thread); } static int32_t SDLCALL getMaxScaleThread(void *ptr) { int8_t *ptr8; int16_t *ptr16; int32_t vol, absSmp, x1, x2, len, i, maxAmp; sampleTyp *s; (void)ptr; if (instr[editor.curInstr] == NULL) goto getScaleExit; s = &instr[editor.curInstr]->samp[editor.curSmp]; if (smpEd_Rx1 < smpEd_Rx2) { x1 = smpEd_Rx1; x2 = smpEd_Rx2; if (x2 > s->len) x2 = s->len; if (x1 < 0) x1 = 0; if (x2 <= x1) goto getScaleExit; if (s->typ & 16) { x1 &= 0xFFFFFFFE; x2 &= 0xFFFFFFFE; } } else { // no sample marking, operate on the whole sample x1 = 0; x2 = s->len; } len = x2 - x1; if (s->typ & 16) len /= 2; if (len <= 0) { vol_StartVol = 0; vol_EndVol = 0; goto getScaleExit; } restoreSample(s); maxAmp = 0; if (s->typ & 16) { ptr16 = (int16_t *)&s->pek[x1]; for (i = 0; i < len; i++) { absSmp = ABS(ptr16[i]); if (absSmp > maxAmp) maxAmp = absSmp; } } else { ptr8 = &s->pek[x1]; for (i = 0; i < len; i++) { absSmp = ABS(ptr8[i]); if (absSmp > maxAmp) maxAmp = absSmp; } maxAmp <<= 8; } fixSample(s); if (maxAmp <= 0) { vol_StartVol = 0; vol_EndVol = 0; } else { vol = (100 * 32768) / maxAmp; if (vol > 500) vol = 500; vol_StartVol = (int16_t)vol; vol_EndVol = (int16_t)vol; } getScaleExit: setMouseBusy(false); return true; } static void pbGetMaxScale(void) { mouseAnimOn(); thread = SDL_CreateThread(getMaxScaleThread, NULL, NULL); if (thread == NULL) { okBox(0, "System message", "Couldn't create thread!"); return; } SDL_DetachThread(thread); } static void drawSampleVolumeBox(void) { char sign; const int16_t x = 166; const int16_t y = 230; const int16_t w = 301; const int16_t h = 52; uint16_t val; // main fill fillRect(x + 1, y + 1, w - 2, h - 2, PAL_BUTTONS); // outer border vLine(x, y, h - 1, PAL_BUTTON1); hLine(x + 1, y, w - 2, PAL_BUTTON1); vLine(x + w - 1, y, h, PAL_BUTTON2); hLine(x, y + h - 1, w - 1, PAL_BUTTON2); // inner border vLine(x + 2, y + 2, h - 5, PAL_BUTTON2); hLine(x + 3, y + 2, w - 6, PAL_BUTTON2); vLine(x + w - 3, y + 2, h - 4, PAL_BUTTON1); hLine(x + 2, y + h - 3, w - 4, PAL_BUTTON1); textOutShadow(172, 236, PAL_FORGRND, PAL_BUTTON2, "Start volume"); textOutShadow(172, 250, PAL_FORGRND, PAL_BUTTON2, "End volume"); charOutShadow(282, 236, PAL_FORGRND, PAL_BUTTON2, '%'); charOutShadow(282, 250, PAL_FORGRND, PAL_BUTTON2, '%'); if (vol_StartVol == 0) sign = ' '; else if (vol_StartVol < 0) sign = '-'; else sign = '+'; val = ABS(vol_StartVol); if (val > 99) { charOut(253, 236, PAL_FORGRND, sign); charOut(260, 236, PAL_FORGRND, '0' + (char)(val / 100)); charOut(267, 236, PAL_FORGRND, '0' + ((val / 10) % 10)); charOut(274, 236, PAL_FORGRND, '0' + (val % 10)); } else if (val > 9) { charOut(260, 236, PAL_FORGRND, sign); charOut(267, 236, PAL_FORGRND, '0' + (char)(val / 10)); charOut(274, 236, PAL_FORGRND, '0' + (val % 10)); } else { charOut(267, 236, PAL_FORGRND, sign); charOut(274, 236, PAL_FORGRND, '0' + (char)val); } if (vol_EndVol == 0) sign = ' '; else if (vol_EndVol < 0) sign = '-'; else sign = '+'; val = ABS(vol_EndVol); if (val > 99) { charOut(253, 250, PAL_FORGRND, sign); charOut(260, 250, PAL_FORGRND, '0' + (char)(val / 100)); charOut(267, 250, PAL_FORGRND, '0' + ((val / 10) % 10)); charOut(274, 250, PAL_FORGRND, '0' + (val % 10)); } else if (val > 9) { charOut(260, 250, PAL_FORGRND, sign); charOut(267, 250, PAL_FORGRND, '0' + (char)(val / 10)); charOut(274, 250, PAL_FORGRND, '0' + (val % 10)); } else { charOut(267, 250, PAL_FORGRND, sign); charOut(274, 250, PAL_FORGRND, '0' + (char)val); } } static void setupVolumeBoxWidgets(void) { pushButton_t *p; scrollBar_t *s; // "Apply" pushbutton p = &pushButtons[0]; memset(p, 0, sizeof (pushButton_t)); p->caption = "Apply"; p->x = 171; p->y = 262; p->w = 73; p->h = 16; p->callbackFuncOnUp = pbApplyVolume; p->visible = true; // "Get maximum scale" pushbutton p = &pushButtons[1]; memset(p, 0, sizeof (pushButton_t)); p->caption = "Get maximum scale"; p->x = 245; p->y = 262; p->w = 143; p->h = 16; p->callbackFuncOnUp = pbGetMaxScale; p->visible = true; // "Exit" pushbutton p = &pushButtons[2]; memset(p, 0, sizeof (pushButton_t)); p->caption = "Exit"; p->x = 389; p->y = 262; p->w = 73; p->h = 16; p->callbackFuncOnUp = pbExit; p->visible = true; // scrollbar buttons p = &pushButtons[3]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_LEFT_STRING; p->x = 292; p->y = 234; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbSampStartVolDown; p->visible = true; p = &pushButtons[4]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_RIGHT_STRING; p->x = 439; p->y = 234; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbSampStartVolUp; p->visible = true; p = &pushButtons[5]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_LEFT_STRING; p->x = 292; p->y = 248; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbSampEndVolDown; p->visible = true; p = &pushButtons[6]; memset(p, 0, sizeof (pushButton_t)); p->caption = ARROW_RIGHT_STRING; p->x = 439; p->y = 248; p->w = 23; p->h = 13; p->preDelay = 1; p->delayFrames = 3; p->callbackFuncOnDown = pbSampEndVolUp; p->visible = true; // volume start scrollbar s = &scrollBars[0]; memset(s, 0, sizeof (scrollBar_t)); s->x = 315; s->y = 234; s->w = 124; s->h = 13; s->callbackFunc = sbSetStartVolPos; s->visible = true; setScrollBarPageLength(0, 1); setScrollBarEnd(0, 500 * 2); setScrollBarPos(0, 500, false); // volume end scrollbar s = &scrollBars[1]; memset(s, 0, sizeof (scrollBar_t)); s->x = 315; s->y = 248; s->w = 124; s->h = 13; s->callbackFunc = sbSetEndVolPos; s->visible = true; setScrollBarPageLength(1, 1); setScrollBarEnd(1, 500 * 2); setScrollBarPos(1, 500, false); } void pbSampleVolume(void) { uint16_t i; if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) { return; } setupVolumeBoxWidgets(); windowOpen(); exitFlag = false; while (ui.sysReqShown) { readInput(); if (ui.sysReqEnterPressed) { pbApplyVolume(); keyb.ignoreCurrKeyUp = true; // don't handle key up event for this key release } setSyncedReplayerVars(); handleRedrawing(); // this is needed for the "Get maximum scale" button if (ui.setMouseIdle) mouseAnimOff(); drawSampleVolumeBox(); setScrollBarPos(0, 500 + vol_StartVol, false); setScrollBarPos(1, 500 + vol_EndVol, false); for (i = 0; i < 7; i++) drawPushButton(i); for (i = 0; i < 2; i++) drawScrollBar(i); flipFrame(); } for (i = 0; i < 7; i++) hidePushButton(i); for (i = 0; i < 2; i++) hideScrollBar(i); windowClose(true); }