ref: 5ffa61cd4ef639f057683070f1c85fe5ecaeec0c
dir: /src/ft2_smpfx.c/
// for finding memory leaks in debug mode with Visual Studio #if defined _DEBUG && defined _MSC_VER #include <crtdbg.h> #endif #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <math.h> #include "ft2_header.h" #include "ft2_audio.h" #include "ft2_pattern_ed.h" #include "ft2_gui.h" #include "ft2_sample_ed.h" #include "ft2_structs.h" #include "ft2_replayer.h" #define RESONANCE_RANGE 99 #define RESONANCE_MIN 0.01 /* prevent massive blow-up */ enum { REMOVE_SAMPLE_MARK = 0, KEEP_SAMPLE_MARK = 1 }; static struct { bool filled, keepSampleMark; uint8_t flags, undoInstr, undoSmp; uint32_t length, loopStart, loopLength; int8_t *smpData8; int16_t *smpData16; } sampleUndo; typedef struct { double a1, a2, a3, b1, b2; double inTmp[2], outTmp[2]; } resoFilter_t; enum { FILTER_LOWPASS = 0, FILTER_HIGHPASS = 1 }; static bool normalization; static uint8_t lastFilterType; static int32_t lastLpCutoff = 2000, lastHpCutoff = 200, filterResonance, smpCycles = 1, lastWaveLength = 64, lastAmp = 75; void clearSampleUndo(void) { if (sampleUndo.smpData8 != NULL) { free(sampleUndo.smpData8); sampleUndo.smpData8 = NULL; } if (sampleUndo.smpData16 != NULL) { free(sampleUndo.smpData16); sampleUndo.smpData16 = NULL; } sampleUndo.filled = false; sampleUndo.keepSampleMark = false; } static void fillSampleUndo(bool keepSampleMark) { sampleUndo.filled = false; sample_t *s = getCurSample(); if (s != NULL && s->length > 0) { pauseAudio(); unfixSample(s); clearSampleUndo(); sampleUndo.undoInstr = editor.curInstr; sampleUndo.undoSmp = editor.curSmp; sampleUndo.flags = s->flags; sampleUndo.length = s->length; sampleUndo.loopStart = s->loopStart; sampleUndo.loopLength = s->loopLength; sampleUndo.keepSampleMark = keepSampleMark; if (s->flags & SAMPLE_16BIT) { sampleUndo.smpData16 = (int16_t *)malloc(s->length * sizeof (int16_t)); if (sampleUndo.smpData16 != NULL) { memcpy(sampleUndo.smpData16, s->dataPtr, s->length * sizeof (int16_t)); sampleUndo.filled = true; } } else { sampleUndo.smpData8 = (int8_t *)malloc(s->length * sizeof (int8_t)); if (sampleUndo.smpData8 != NULL) { memcpy(sampleUndo.smpData8, s->dataPtr, s->length * sizeof (int8_t)); sampleUndo.filled = true; } } fixSample(s); resumeAudio(); } } static sample_t *setupNewSample(uint32_t length) { pauseAudio(); if (instr[editor.curInstr] == NULL) allocateInstr(editor.curInstr); if (instr[editor.curInstr] == NULL) goto Error; sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; if (!reallocateSmpData(s, length, true)) goto Error; s->isFixed = false; s->length = length; s->loopLength = s->loopStart = 0; s->flags = SAMPLE_16BIT; resumeAudio(); return s; Error: resumeAudio(); return NULL; } void cbSfxNormalization(void) { normalization ^= 1; } static void drawSampleCycles(void) { const int16_t x = 54; fillRect(x, 352, 7*3, 8, PAL_DESKTOP); char str[16]; sprintf(str, "%03d", smpCycles); textOut(x, 352, PAL_FORGRND, str); } void pbSfxCyclesUp(void) { if (smpCycles < 256) { smpCycles++; drawSampleCycles(); } } void pbSfxCyclesDown(void) { if (smpCycles > 1) { smpCycles--; drawSampleCycles(); } } void pbSfxTriangle(void) { char lengthStr[5+1]; memset(lengthStr, '\0', sizeof (lengthStr)); snprintf(lengthStr, sizeof (lengthStr), "%d", lastWaveLength); if (inputBox(1, "Enter new waveform length:", lengthStr, sizeof (lengthStr)-1) != 1) return; if (lengthStr[0] == '\0') return; lastWaveLength = (int32_t)atoi(lengthStr); if (lastWaveLength <= 1 || lastWaveLength > 65536) { okBox(0, "System message", "Illegal range! Allowed range is 2..65535", NULL); return; } fillSampleUndo(REMOVE_SAMPLE_MARK); int32_t newLength = lastWaveLength * smpCycles; pauseAudio(); sample_t *s = setupNewSample(newLength); if (s == NULL) { resumeAudio(); okBox(0, "System message", "Not enough memory!", NULL); return; } const double delta = 4.0 / lastWaveLength; double phase = 0.0; int16_t *ptr16 = (int16_t *)s->dataPtr; for (int32_t i = 0; i < newLength; i++) { double t = phase; if (t > 3.0) t -= 4.0; else if (t >= 1.0) t = 2.0 - t; *ptr16++ = (int16_t)(t * INT16_MAX); phase = fmod(phase + delta, 4.0); } s->loopLength = newLength; s->flags |= LOOP_FORWARD; fixSample(s); resumeAudio(); updateSampleEditorSample(); } void pbSfxSaw(void) { char lengthStr[5+1]; memset(lengthStr, '\0', sizeof (lengthStr)); snprintf(lengthStr, sizeof (lengthStr), "%d", lastWaveLength); if (inputBox(1, "Enter new waveform length:", lengthStr, sizeof (lengthStr)-1) != 1) return; if (lengthStr[0] == '\0') return; lastWaveLength = (int32_t)atoi(lengthStr); if (lastWaveLength <= 1 || lastWaveLength > 65536) { okBox(0, "System message", "Illegal range! Allowed range is 2..65535", NULL); return; } fillSampleUndo(REMOVE_SAMPLE_MARK); int32_t newLength = lastWaveLength * smpCycles; pauseAudio(); sample_t *s = setupNewSample(newLength); if (s == NULL) { resumeAudio(); okBox(0, "System message", "Not enough memory!", NULL); return; } uint64_t point64 = 0; uint64_t delta64 = ((uint64_t)(INT16_MAX*2) << 32ULL) / lastWaveLength; int16_t *ptr16 = (int16_t *)s->dataPtr; for (int32_t i = 0; i < newLength; i++) { *ptr16++ = (int16_t)(point64 >> 32); point64 += delta64; } s->loopLength = newLength; s->flags |= LOOP_FORWARD; fixSample(s); resumeAudio(); updateSampleEditorSample(); } void pbSfxSine(void) { char lengthStr[5+1]; memset(lengthStr, '\0', sizeof (lengthStr)); snprintf(lengthStr, sizeof (lengthStr), "%d", lastWaveLength); if (inputBox(1, "Enter new waveform length:", lengthStr, sizeof (lengthStr)-1) != 1) return; if (lengthStr[0] == '\0') return; lastWaveLength = (int32_t)atoi(lengthStr); if (lastWaveLength <= 1 || lastWaveLength > 65536) { okBox(0, "System message", "Illegal range! Allowed range is 2..65535", NULL); return; } fillSampleUndo(REMOVE_SAMPLE_MARK); int32_t newLength = lastWaveLength * smpCycles; pauseAudio(); sample_t *s = setupNewSample(newLength); if (s == NULL) { resumeAudio(); okBox(0, "System message", "Not enough memory!", NULL); return; } const double dMul = (2.0 * M_PI) / lastWaveLength; int16_t *ptr16 = (int16_t *)s->dataPtr; for (int32_t i = 0; i < newLength; i++) *ptr16++ = (int16_t)(INT16_MAX * sin(i * dMul)); s->loopLength = newLength; s->flags |= LOOP_FORWARD; fixSample(s); resumeAudio(); updateSampleEditorSample(); } void pbSfxSquare(void) { char lengthStr[5+1]; memset(lengthStr, '\0', sizeof (lengthStr)); snprintf(lengthStr, sizeof (lengthStr), "%d", lastWaveLength); if (inputBox(1, "Enter new waveform length:", lengthStr, sizeof (lengthStr)-1) != 1) return; if (lengthStr[0] == '\0') return; lastWaveLength = (int32_t)atoi(lengthStr); if (lastWaveLength <= 1 || lastWaveLength > 65536) { okBox(0, "System message", "Illegal range! Allowed range is 2..65535", NULL); return; } fillSampleUndo(REMOVE_SAMPLE_MARK); uint32_t newLength = lastWaveLength * smpCycles; pauseAudio(); sample_t *s = setupNewSample(newLength); if (s == NULL) { resumeAudio(); okBox(0, "System message", "Not enough memory!", NULL); return; } const uint32_t halfWaveLength = lastWaveLength / 2; int16_t currValue = INT16_MAX; uint32_t counter = 0; int16_t *ptr16 = (int16_t *)s->dataPtr; for (uint32_t i = 0; i < newLength; i++) { *ptr16++ = currValue; if (++counter >= halfWaveLength) { counter = 0; currValue = -currValue; } } s->loopLength = newLength; s->flags |= LOOP_FORWARD; fixSample(s); resumeAudio(); updateSampleEditorSample(); } void drawFilterResonance(void) { const int16_t x = 172; fillRect(x, 352, 18, 12, PAL_DESKTOP); if (filterResonance <= 0) { textOut(x, 352, PAL_FORGRND, "off"); } else { char str[16]; sprintf(str, "%02d", filterResonance); textOut(x+3, 352, PAL_FORGRND, str); } } void pbSfxResoUp(void) { if (filterResonance < RESONANCE_RANGE) { filterResonance++; drawFilterResonance(); } } void pbSfxResoDown(void) { if (filterResonance > 0) { filterResonance--; drawFilterResonance(); } } #define CUTOFF_EPSILON (1E-4) static void setupResoLpFilter(sample_t *s, resoFilter_t *f, double cutoff, uint32_t resonance, bool absoluteCutoff) { // 12dB/oct resonant low-pass filter if (!absoluteCutoff) { const double sampleFreq = getSampleC4Rate(s); if (cutoff >= sampleFreq/2.0) cutoff = (sampleFreq/2.0) - CUTOFF_EPSILON; cutoff /= sampleFreq; } double r = sqrt(2.0); if (resonance > 0) { r = pow(10.0, (resonance * -24.0) / (RESONANCE_RANGE * 20.0)); if (r < RESONANCE_MIN) r = RESONANCE_MIN; } const double c = 1.0 / tan(PI * cutoff); f->a1 = 1.0 / (1.0 + r * c + c * c); f->a2 = 2.0 * f->a1; f->a3 = f->a1; f->b1 = 2.0 * (1.0 - c*c) * f->a1; f->b2 = (1.0 - r * c + c * c) * f->a1; f->inTmp[0] = f->inTmp[1] = f->outTmp[0] = f->outTmp[1] = 0.0; // clear filter history } static void setupResoHpFilter(sample_t *s, resoFilter_t *f, double cutoff, uint32_t resonance, bool absoluteCutoff) { // 12dB/oct resonant high-pass filter if (!absoluteCutoff) { const double sampleFreq = getSampleC4Rate(s); if (cutoff >= sampleFreq/2.0) cutoff = (sampleFreq/2.0) - CUTOFF_EPSILON; cutoff /= sampleFreq; } double r = sqrt(2.0); if (resonance > 0) { r = pow(10.0, (resonance * -24.0) / (RESONANCE_RANGE * 20.0)); if (r < RESONANCE_MIN) r = RESONANCE_MIN; } const double c = tan(PI * cutoff); f->a1 = 1.0 / (1.0 + r * c + c * c); f->a2 = -2.0 * f->a1; f->a3 = f->a1; f->b1 = 2.0 * (c*c - 1.0) * f->a1; f->b2 = (1.0 - r * c + c * c) * f->a1; f->inTmp[0] = f->inTmp[1] = f->outTmp[0] = f->outTmp[1] = 0.0; // clear filter history } static bool applyResoFilter(sample_t *s, resoFilter_t *f) { int32_t x1, x2; if (smpEd_Rx1 < smpEd_Rx2) { x1 = smpEd_Rx1; x2 = smpEd_Rx2; if (x2 > s->length) x2 = s->length; if (x1 < 0) x1 = 0; if (x2 <= x1) return true; } else { // no mark, operate on whole sample x1 = 0; x2 = s->length; } const int32_t len = x2 - x1; if (!normalization) { pauseAudio(); unfixSample(s); if (s->flags & SAMPLE_16BIT) { int16_t *ptr16 = (int16_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) { double out = (f->a1*ptr16[i]) + (f->a2*f->inTmp[0]) + (f->a3*f->inTmp[1]) - (f->b1*f->outTmp[0]) - (f->b2*f->outTmp[1]); f->inTmp[1] = f->inTmp[0]; f->inTmp[0] = ptr16[i]; f->outTmp[1] = f->outTmp[0]; f->outTmp[0] = out; ptr16[i] = (int16_t)CLAMP(out, INT16_MIN, INT16_MAX); } } else { int8_t *ptr8 = (int8_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) { double out = (f->a1*ptr8[i]) + (f->a2*f->inTmp[0]) + (f->a3*f->inTmp[1]) - (f->b1*f->outTmp[0]) - (f->b2*f->outTmp[1]); f->inTmp[1] = f->inTmp[0]; f->inTmp[0] = ptr8[i]; f->outTmp[1] = f->outTmp[0]; f->outTmp[0] = out; ptr8[i] = (int8_t)CLAMP(out, INT8_MIN, INT8_MAX); } } fixSample(s); resumeAudio(); } else // normalize peak, no clipping { double *dSmp = (double *)malloc(len * sizeof (double)); if (dSmp == NULL) { okBox(0, "System message", "Not enough memory!", NULL); return false; } pauseAudio(); unfixSample(s); if (s->flags & SAMPLE_16BIT) { int16_t *ptr16 = (int16_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) dSmp[i] = (double)ptr16[i]; } else { int8_t *ptr8 = (int8_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) dSmp[i] = (double)ptr8[i]; } double peak = 0.0; for (int32_t i = 0; i < len; i++) { const double out = (f->a1*dSmp[i]) + (f->a2*f->inTmp[0]) + (f->a3*f->inTmp[1]) - (f->b1*f->outTmp[0]) - (f->b2*f->outTmp[1]); f->inTmp[1] = f->inTmp[0]; f->inTmp[0] = dSmp[i]; f->outTmp[1] = f->outTmp[0]; f->outTmp[0] = out; dSmp[i] = out; const double outAbs = fabs(out); if (outAbs > peak) peak = outAbs; } if (s->flags & SAMPLE_16BIT) { const double scale = INT16_MAX / peak; int16_t *ptr16 = (int16_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) ptr16[i] = (int16_t)(dSmp[i] * scale); } else { const double scale = INT8_MAX / peak; int8_t *ptr8 = (int8_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) ptr8[i] = (int8_t)(dSmp[i] * scale); } free(dSmp); fixSample(s); resumeAudio(); } return true; } void pbSfxLowPass(void) { resoFilter_t f; sample_t *s = getCurSample(); if (s == NULL || s->dataPtr == NULL) return; char lengthStr[5+1]; memset(lengthStr, '\0', sizeof (lengthStr)); snprintf(lengthStr, sizeof (lengthStr), "%d", lastLpCutoff); lastFilterType = FILTER_LOWPASS; if (inputBox(6, "Enter low-pass filter cutoff (in Hz):", lengthStr, sizeof (lengthStr)-1) != 1) return; if (lengthStr[0] == '\0') return; lastLpCutoff = (int32_t)atoi(lengthStr); if (lastLpCutoff < 1 || lastLpCutoff > 99999) { okBox(0, "System message", "Illegal range! Allowed range is 1..99999", NULL); return; } setupResoLpFilter(s, &f, lastLpCutoff, filterResonance, false); fillSampleUndo(KEEP_SAMPLE_MARK); applyResoFilter(s, &f); writeSample(true); } void pbSfxHighPass(void) { resoFilter_t f; sample_t *s = getCurSample(); if (s == NULL || s->dataPtr == NULL) return; char lengthStr[5+1]; memset(lengthStr, '\0', sizeof (lengthStr)); snprintf(lengthStr, sizeof (lengthStr), "%d", lastHpCutoff); lastFilterType = FILTER_HIGHPASS; if (inputBox(6, "Enter high-pass filter cutoff (in Hz):", lengthStr, sizeof (lengthStr)-1) != 1) return; if (lengthStr[0] == '\0') return; lastHpCutoff = (int32_t)atoi(lengthStr); if (lastHpCutoff < 1 || lastHpCutoff > 99999) { okBox(0, "System message", "Illegal range! Allowed range is 1..99999", NULL); return; } setupResoHpFilter(s, &f, lastHpCutoff, filterResonance, false); fillSampleUndo(KEEP_SAMPLE_MARK); applyResoFilter(s, &f); writeSample(true); } void sfxPreviewFilter(uint32_t cutoff) { sample_t oldSample; resoFilter_t f; sample_t *s = getCurSample(); if (s == NULL || s->dataPtr == NULL || s->length == 0 || cutoff < 1 || cutoff > 99999) return; int32_t x1, x2; if (smpEd_Rx1 < smpEd_Rx2) { x1 = smpEd_Rx1; x2 = smpEd_Rx2; if (x2 > s->length) x2 = s->length; if (x1 < 0) x1 = 0; if (x2 <= x1) return; } else { // no mark, operate on whole sample x1 = 0; x2 = s->length; } const int32_t len = x2 - x1; pauseAudio(); unfixSample(s); memcpy(&oldSample, s, sizeof (sample_t)); if (lastFilterType == FILTER_LOWPASS) setupResoLpFilter(s, &f, cutoff, filterResonance, false); else setupResoHpFilter(s, &f, cutoff, filterResonance, false); // prepare new sample int8_t *sampleData; if (s->flags & SAMPLE_16BIT) { sampleData = (int8_t *)malloc((len * sizeof (int16_t)) + SAMPLE_PAD_LENGTH); if (sampleData == NULL) goto Error; memcpy(sampleData + SMP_DAT_OFFSET, (int16_t *)s->dataPtr + x1, len * sizeof (int16_t)); } else { sampleData = (int8_t *)malloc((len * sizeof (int8_t)) + SAMPLE_PAD_LENGTH); if (sampleData == NULL) goto Error; memcpy(sampleData + SMP_DAT_OFFSET, (int8_t *)s->dataPtr + x1, len * sizeof (int8_t)); } s->origDataPtr = sampleData; s->length = len; s->dataPtr = s->origDataPtr + SMP_DAT_OFFSET; s->loopStart = s->loopLength = 0; fixSample(s); const int32_t oldX1 = smpEd_Rx1; const int32_t oldX2 = smpEd_Rx2; smpEd_Rx1 = smpEd_Rx2 = 0; applyResoFilter(s, &f); smpEd_Rx1 = oldX1; smpEd_Rx2 = oldX2; // set up preview sample on channel 0 channel_t *ch = &channel[0]; uint8_t note = editor.smpEd_NoteNr; ch->smpNum = editor.curSmp; ch->instrNum = editor.curInstr; ch->copyOfInstrAndNote = (ch->instrNum << 8) | note; ch->efx = 0; ch->smpStartPos = 0; resumeAudio(); triggerNote(note, 0, 0, ch); resetVolumes(ch); triggerInstrument(ch); ch->realVol = ch->outVol = ch->oldVol = 64; updateVolPanAutoVib(ch); while (ch->status & IS_Trigger); // wait for sample to latch in mixer SDL_Delay(1500); // wait 1.5 seconds // we're done, stop voice and free temporary data pauseAudio(); free(sampleData); Error: // set back old sample memcpy(s, &oldSample, sizeof (sample_t)); fixSample(s); resumeAudio(); } void pbSfxSubBass(void) { resoFilter_t f; sample_t *s = getCurSample(); if (s == NULL || s->dataPtr == NULL) return; setupResoHpFilter(s, &f, 0.001, 0, true); fillSampleUndo(KEEP_SAMPLE_MARK); applyResoFilter(s, &f); writeSample(true); } void pbSfxAddBass(void) { resoFilter_t f; sample_t *s = getCurSample(); if (s == NULL || s->dataPtr == NULL) return; int32_t x1, x2; if (smpEd_Rx1 < smpEd_Rx2) { x1 = smpEd_Rx1; x2 = smpEd_Rx2; if (x2 > s->length) x2 = s->length; if (x1 < 0) x1 = 0; if (x2 <= x1) return; } else { // no mark, operate on whole sample x1 = 0; x2 = s->length; } const int32_t len = x2 - x1; setupResoLpFilter(s, &f, 0.015, 0, true); double *dSmp = (double *)malloc(len * sizeof (double)); if (dSmp == NULL) { okBox(0, "System message", "Not enough memory!", NULL); return; } fillSampleUndo(KEEP_SAMPLE_MARK); pauseAudio(); unfixSample(s); if (s->flags & SAMPLE_16BIT) { int16_t *ptr16 = (int16_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) dSmp[i] = (double)ptr16[i]; } else { int8_t *ptr8 = (int8_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) dSmp[i] = (double)ptr8[i]; } if (!normalization) { for (int32_t i = 0; i < len; i++) { double out = (f.a1*dSmp[i]) + (f.a2*f.inTmp[0]) + (f.a3*f.inTmp[1]) - (f.b1*f.outTmp[0]) - (f.b2*f.outTmp[1]); f.inTmp[1] = f.inTmp[0]; f.inTmp[0] = dSmp[i]; f.outTmp[1] = f.outTmp[0]; f.outTmp[0] = out; dSmp[i] = out; } if (s->flags & SAMPLE_16BIT) { int16_t *ptr16 = (int16_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) { double out = ptr16[i] + (dSmp[i] * 0.25); out = CLAMP(out, INT16_MIN, INT16_MAX); ptr16[i] = (int16_t)out; } } else { int8_t *ptr8 = (int8_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) { double out = ptr8[i] + (dSmp[i] * 0.25); out = CLAMP(out, INT8_MIN, INT8_MAX); ptr8[i] = (int8_t)out; } } } else { if (s->flags & SAMPLE_16BIT) { int16_t *ptr16 = (int16_t *)s->dataPtr + x1; double peak = 0.0; for (int32_t i = 0; i < len; i++) { double out = (f.a1*dSmp[i]) + (f.a2*f.inTmp[0]) + (f.a3*f.inTmp[1]) - (f.b1*f.outTmp[0]) - (f.b2*f.outTmp[1]); f.inTmp[1] = f.inTmp[0]; f.inTmp[0] = dSmp[i]; f.outTmp[1] = f.outTmp[0]; f.outTmp[0] = out; dSmp[i] = out; double bass = ptr16[i] + (out * 0.25); const double outAbs = fabs(bass); if (outAbs > peak) peak = outAbs; } double scale = INT16_MAX / peak; for (int32_t i = 0; i < len; i++) ptr16[i] = (int16_t)((ptr16[i] + (dSmp[i] * 0.25)) * scale); } else { int8_t *ptr8 = (int8_t *)s->dataPtr + x1; double peak = 0.0; for (int32_t i = 0; i < len; i++) { double out = (f.a1*dSmp[i]) + (f.a2*f.inTmp[0]) + (f.a3*f.inTmp[1]) - (f.b1*f.outTmp[0]) - (f.b2*f.outTmp[1]); f.inTmp[1] = f.inTmp[0]; f.inTmp[0] = dSmp[i]; f.outTmp[1] = f.outTmp[0]; f.outTmp[0] = out; dSmp[i] = out; double bass = ptr8[i] + (out * 0.25); const double outAbs = fabs(bass); if (outAbs > peak) peak = outAbs; } double scale = INT8_MAX / peak; for (int32_t i = 0; i < len; i++) ptr8[i] = (int8_t)((ptr8[i] + (dSmp[i] * 0.25)) * scale); } } free(dSmp); fixSample(s); resumeAudio(); writeSample(true); } void pbSfxSubTreble(void) { resoFilter_t f; sample_t *s = getCurSample(); if (s == NULL || s->dataPtr == NULL) return; setupResoLpFilter(s, &f, 0.33, 0, true); fillSampleUndo(KEEP_SAMPLE_MARK); applyResoFilter(s, &f); writeSample(true); } void pbSfxAddTreble(void) { resoFilter_t f; sample_t *s = getCurSample(); if (s == NULL || s->dataPtr == NULL) return; int32_t x1, x2; if (smpEd_Rx1 < smpEd_Rx2) { x1 = smpEd_Rx1; x2 = smpEd_Rx2; if (x2 > s->length) x2 = s->length; if (x1 < 0) x1 = 0; if (x2 <= x1) return; } else { // no mark, operate on whole sample x1 = 0; x2 = s->length; } const int32_t len = x2 - x1; setupResoHpFilter(s, &f, 0.27, 0, true); double *dSmp = (double *)malloc(len * sizeof (double)); if (dSmp == NULL) { okBox(0, "System message", "Not enough memory!", NULL); return; } fillSampleUndo(KEEP_SAMPLE_MARK); pauseAudio(); unfixSample(s); if (s->flags & SAMPLE_16BIT) { int16_t *ptr16 = (int16_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) dSmp[i] = (double)ptr16[i]; } else { int8_t *ptr8 = (int8_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) dSmp[i] = (double)ptr8[i]; } if (!normalization) { for (int32_t i = 0; i < len; i++) { double out = (f.a1*dSmp[i]) + (f.a2*f.inTmp[0]) + (f.a3*f.inTmp[1]) - (f.b1*f.outTmp[0]) - (f.b2*f.outTmp[1]); f.inTmp[1] = f.inTmp[0]; f.inTmp[0] = dSmp[i]; f.outTmp[1] = f.outTmp[0]; f.outTmp[0] = out; dSmp[i] = out; } if (s->flags & SAMPLE_16BIT) { int16_t *ptr16 = (int16_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) { double out = ptr16[i] - (dSmp[i] * 0.25); out = CLAMP(out, INT16_MIN, INT16_MAX); ptr16[i] = (int16_t)out; } } else { int8_t *ptr8 = (int8_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) { double out = ptr8[i] - (dSmp[i] * 0.25); out = CLAMP(out, INT8_MIN, INT8_MAX); ptr8[i] = (int8_t)out; } } } else { if (s->flags & SAMPLE_16BIT) { int16_t *ptr16 = (int16_t *)s->dataPtr + x1; double peak = 0.0; for (int32_t i = 0; i < len; i++) { double out = (f.a1*dSmp[i]) + (f.a2*f.inTmp[0]) + (f.a3*f.inTmp[1]) - (f.b1*f.outTmp[0]) - (f.b2*f.outTmp[1]); f.inTmp[1] = f.inTmp[0]; f.inTmp[0] = dSmp[i]; f.outTmp[1] = f.outTmp[0]; f.outTmp[0] = out; dSmp[i] = out; double treble = ptr16[i] - (out * 0.25); const double outAbs = fabs(treble); if (outAbs > peak) peak = outAbs; } double scale = INT16_MAX / peak; for (int32_t i = 0; i < len; i++) ptr16[i] = (int16_t)((ptr16[i] - (dSmp[i] * 0.25)) * scale); } else { int8_t *ptr8 = (int8_t *)s->dataPtr + x1; double peak = 0.0; for (int32_t i = 0; i < len; i++) { double out = (f.a1*dSmp[i]) + (f.a2*f.inTmp[0]) + (f.a3*f.inTmp[1]) - (f.b1*f.outTmp[0]) - (f.b2*f.outTmp[1]); f.inTmp[1] = f.inTmp[0]; f.inTmp[0] = dSmp[i]; f.outTmp[1] = f.outTmp[0]; f.outTmp[0] = out; dSmp[i] = out; double treble = ptr8[i] - (out * 0.25); const double outAbs = fabs(treble); if (outAbs > peak) peak = outAbs; } double scale = INT8_MAX / peak; for (int32_t i = 0; i < len; i++) ptr8[i] = (int8_t)((ptr8[i] - (dSmp[i] * 0.25)) * scale); } } free(dSmp); fixSample(s); resumeAudio(); writeSample(true); } void pbSfxSetAmp(void) { sample_t *s = getCurSample(); if (s == NULL || s->dataPtr == NULL) return; int32_t x1, x2; if (smpEd_Rx1 < smpEd_Rx2) { x1 = smpEd_Rx1; x2 = smpEd_Rx2; if (x2 > s->length) x2 = s->length; if (x1 < 0) x1 = 0; if (x2 <= x1) return; } else { // no mark, operate on whole sample x1 = 0; x2 = s->length; } const int32_t len = x2 - x1; char ampStr[3+1]; memset(ampStr, '\0', sizeof (ampStr)); snprintf(ampStr, sizeof (ampStr), "%d", lastAmp); if (inputBox(1, "Change sample amplitude (in percentage, 0..999):", ampStr, sizeof (ampStr)-1) != 1) return; if (ampStr[0] == '\0') return; lastAmp = (int32_t)atoi(ampStr); fillSampleUndo(KEEP_SAMPLE_MARK); pauseAudio(); unfixSample(s); const int32_t mul = (int32_t)round((1 << 22UL) * (lastAmp / 100.0)); if (s->flags & SAMPLE_16BIT) { int16_t *ptr16 = (int16_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) { int32_t sample = ((int64_t)ptr16[i] * (int32_t)mul) >> 22; sample = CLAMP(sample, INT16_MIN, INT16_MAX); ptr16[i] = (int16_t)sample; } } else { int8_t *ptr8 = (int8_t *)s->dataPtr + x1; for (int32_t i = 0; i < len; i++) { int32_t sample = ((int64_t)ptr8[i] * (int32_t)mul) >> 22; sample = CLAMP(sample, INT8_MIN, INT8_MAX); ptr8[i] = (int8_t)sample; } } fixSample(s); resumeAudio(); writeSample(true); } void pbSfxUndo(void) { if (!sampleUndo.filled || sampleUndo.undoInstr != editor.curInstr || sampleUndo.undoSmp != editor.curSmp) return; sample_t *s = getCurSample(); if (s == NULL || s->dataPtr == NULL) return; pauseAudio(); freeSmpData(s); s->flags = sampleUndo.flags; s->length = sampleUndo.length; s->loopStart = sampleUndo.loopStart; s->loopLength = sampleUndo.loopLength; if (allocateSmpData(s, s->length, !!(s->flags & SAMPLE_16BIT))) { if (s->flags & SAMPLE_16BIT) memcpy(s->dataPtr, sampleUndo.smpData16, s->length * sizeof (int16_t)); else memcpy(s->dataPtr, sampleUndo.smpData8, s->length * sizeof (int8_t)); fixSample(s); resumeAudio(); } else { resumeAudio(); okBox(0, "System message", "Not enough memory!", NULL); } int32_t oldRx1 = smpEd_Rx1; int32_t oldRx2 = smpEd_Rx2; updateSampleEditorSample(); if (sampleUndo.keepSampleMark && oldRx1 < oldRx2) { smpEd_Rx1 = oldRx1; smpEd_Rx2 = oldRx2; writeSample(false); // redraw sample mark only } sampleUndo.keepSampleMark = false; sampleUndo.filled = false; } void hideSampleEffectsScreen(void) { ui.sampleEditorEffectsShown = false; hideCheckBox(CB_SAMPFX_NORMALIZATION); hidePushButton(PB_SAMPFX_CYCLES_UP); hidePushButton(PB_SAMPFX_CYCLES_DOWN); hidePushButton(PB_SAMPFX_TRIANGLE); hidePushButton(PB_SAMPFX_SAW); hidePushButton(PB_SAMPFX_SINE); hidePushButton(PB_SAMPFX_SQUARE); hidePushButton(PB_SAMPFX_RESO_UP); hidePushButton(PB_SAMPFX_RESO_DOWN); hidePushButton(PB_SAMPFX_LOWPASS); hidePushButton(PB_SAMPFX_HIGHPASS); hidePushButton(PB_SAMPFX_SUB_BASS); hidePushButton(PB_SAMPFX_SUB_TREBLE); hidePushButton(PB_SAMPFX_ADD_BASS); hidePushButton(PB_SAMPFX_ADD_TREBLE); hidePushButton(PB_SAMPFX_SET_AMP); hidePushButton(PB_SAMPFX_UNDO); hidePushButton(PB_SAMPFX_XFADE); hidePushButton(PB_SAMPFX_BACK); drawFramework(0, 346, 115, 54, FRAMEWORK_TYPE1); drawFramework(115, 346, 133, 54, FRAMEWORK_TYPE1); drawFramework(248, 346, 49, 54, FRAMEWORK_TYPE1); drawFramework(297, 346, 56, 54, FRAMEWORK_TYPE1); showPushButton(PB_SAMP_PNOTE_UP); showPushButton(PB_SAMP_PNOTE_DOWN); showPushButton(PB_SAMP_STOP); showPushButton(PB_SAMP_PWAVE); showPushButton(PB_SAMP_PRANGE); showPushButton(PB_SAMP_PDISPLAY); showPushButton(PB_SAMP_SHOW_RANGE); showPushButton(PB_SAMP_RANGE_ALL); showPushButton(PB_SAMP_CLR_RANGE); showPushButton(PB_SAMP_ZOOM_OUT); showPushButton(PB_SAMP_SHOW_ALL); showPushButton(PB_SAMP_SAVE_RNG); showPushButton(PB_SAMP_CUT); showPushButton(PB_SAMP_COPY); showPushButton(PB_SAMP_PASTE); showPushButton(PB_SAMP_CROP); showPushButton(PB_SAMP_VOLUME); showPushButton(PB_SAMP_EFFECTS); drawFramework(2, 366, 34, 15, FRAMEWORK_TYPE2); textOutShadow(5, 352, PAL_FORGRND, PAL_DSKTOP2, "Play:"); updateSampleEditor(); } void pbEffects(void) { hidePushButton(PB_SAMP_PNOTE_UP); hidePushButton(PB_SAMP_PNOTE_DOWN); hidePushButton(PB_SAMP_STOP); hidePushButton(PB_SAMP_PWAVE); hidePushButton(PB_SAMP_PRANGE); hidePushButton(PB_SAMP_PDISPLAY); hidePushButton(PB_SAMP_SHOW_RANGE); hidePushButton(PB_SAMP_RANGE_ALL); hidePushButton(PB_SAMP_CLR_RANGE); hidePushButton(PB_SAMP_ZOOM_OUT); hidePushButton(PB_SAMP_SHOW_ALL); hidePushButton(PB_SAMP_SAVE_RNG); hidePushButton(PB_SAMP_CUT); hidePushButton(PB_SAMP_COPY); hidePushButton(PB_SAMP_PASTE); hidePushButton(PB_SAMP_CROP); hidePushButton(PB_SAMP_VOLUME); hidePushButton(PB_SAMP_EFFECTS); drawFramework(0, 346, 116, 54, FRAMEWORK_TYPE1); drawFramework(116, 346, 114, 54, FRAMEWORK_TYPE1); drawFramework(230, 346, 67, 54, FRAMEWORK_TYPE1); drawFramework(297, 346, 56, 54, FRAMEWORK_TYPE1); checkBoxes[CB_SAMPFX_NORMALIZATION].checked = normalization ? true : false; showCheckBox(CB_SAMPFX_NORMALIZATION); showPushButton(PB_SAMPFX_CYCLES_UP); showPushButton(PB_SAMPFX_CYCLES_DOWN); showPushButton(PB_SAMPFX_TRIANGLE); showPushButton(PB_SAMPFX_SAW); showPushButton(PB_SAMPFX_SINE); showPushButton(PB_SAMPFX_SQUARE); showPushButton(PB_SAMPFX_RESO_UP); showPushButton(PB_SAMPFX_RESO_DOWN); showPushButton(PB_SAMPFX_LOWPASS); showPushButton(PB_SAMPFX_HIGHPASS); showPushButton(PB_SAMPFX_SUB_BASS); showPushButton(PB_SAMPFX_SUB_TREBLE); showPushButton(PB_SAMPFX_ADD_BASS); showPushButton(PB_SAMPFX_ADD_TREBLE); showPushButton(PB_SAMPFX_SET_AMP); showPushButton(PB_SAMPFX_UNDO); showPushButton(PB_SAMPFX_XFADE); showPushButton(PB_SAMPFX_BACK); textOutShadow(4, 352, PAL_FORGRND, PAL_DSKTOP2, "Cycles:"); drawSampleCycles(); textOutShadow(121, 352, PAL_FORGRND, PAL_DSKTOP2, "Reson.:"); drawFilterResonance(); textOutShadow(135, 386, PAL_FORGRND, PAL_DSKTOP2, "Normalization"); textOutShadow(235, 352, PAL_FORGRND, PAL_DSKTOP2, "Bass"); textOutShadow(235, 369, PAL_FORGRND, PAL_DSKTOP2, "Treb."); ui.sampleEditorEffectsShown = true; }