// Helpers for the effects in this folder (effects/README.md). An effect's code
// runs wherever it is loaded, so, like a tape's, it keeps no data of its own:
// no tables, globals or statics, and no division or remainder (each calls code
// outside the effect). Its memory is the state it is given: up to 16384 words,
// all zero when the effect is chosen.
//
// The sound arrives as Q31 samples (full scale 2^31). Effects work on it at the
// working level, Q27: full scale is `full` (2^27), with 24 dB of room above it
// for sums and peaks and 27 bits below it, so quiet sound and fading tails stay
// clean. level() takes a sample to it; gain() and times() multiply at it (one
// instruction each on the OP-XY); mix() or blend() put the effect back with the
// untouched sound, which keeps every one of its bits. Controls (knob(), sine())
// stay Q15, 0 to 32767. Every control's middle (where it starts) should sound good.
#pragma once
#include <opxy/types.hpp>
#include <opxy/q31.hpp>

namespace fx {
using opxy::i32;
using opxy::u32;

inline i32 clamp(i32 v, i32 lo, i32 hi) { return v < lo ? lo : v > hi ? hi : v; }
// A control, 0 to 32767.
inline i32 knob(const i32* p, unsigned i) { return p[i] < 0 ? 0 : p[i] >> 16; }

// ---- The working level ----
const i32 full = 1 << 27;                       // full scale
inline i32 level(i32 sample) { return sample >> 4; }
// A level back to a sample, held within full scale.
inline i32 sample(i32 v) { return clamp(v, -full, full - 1) * 16; }
// v times k, a Q15 fraction (-32768 to 32767 for -1 to just under 1).
inline i32 gain(i32 v, i32 k) { return opxy::q31::multiply(v, k * 65536); }
// Two levels multiplied, as a level: full times full is full. Each within 4 x full.
inline i32 times(i32 a, i32 b) { return opxy::q31::multiply(a * 4, b * 4); }
// Dry (a sample) plus an amount (a level). Up to six tenths of full scale it's
// exact; above, it bends softly toward full scale (reached at 1.2 times it), so
// an effect added to a loud mix never clips hard (which crackles).
inline i32 add(i32 dry, i32 amount) {
    amount = clamp(amount, -2 * full, 2 * full);
    const i32 s = level(dry) + amount;
    const i32 size = s < 0 ? -s : s;
    const i32 knee = 80530637;                                             // 0.6 full
    if (size <= knee) return dry + amount * 16;
    const i32 v = gain(clamp(size - knee, 0, knee) * 5, 10923);            // 5/3 of it: 0 .. full over the curve's range
    const i32 bend = (3 * v - times(times(v, v), v)) >> 1;                 // 1.5 v - 0.5 v^3: 0 .. full
    const i32 y = clamp(knee + gain(bend, 13107), 0, full - 1);            // 0.6 .. 1 full
    return (s < 0 ? -y : y) * 16;
}
// The effect (a level) added to the sound, as from a send: Mix 0 leaves the sound
// exactly as it was; the middle, the sound full and the effect at half under it;
// the top, the effect alone.
inline i32 mix(i32 dry, i32 wet, i32 m) {
    if (!m) return dry;
    wet = clamp(wet, -2 * full, 2 * full);
    if (m <= 16384) return add(dry, gain(wet, m));
    return add(0, gain(level(dry), (32767 - m) * 2) + gain(wet, m));
}
// The sound turned into the effect: Mix 0 as it was, the middle half and half.
inline i32 blend(i32 dry, i32 wet, i32 m) {
    if (!m) return dry;
    return add(dry, gain(clamp(wet, -2 * full, 2 * full) - level(dry), m));
}
// Width, 0 to 32767: mono, as it came (the middle), twice as wide.
inline void widen(i32& l, i32& r, i32 width) {
    const i32 middle = (l >> 1) + (r >> 1), side = gain(l - middle, width) * 2;
    l = middle + side; r = middle - side;
}
// Soft clipping of a level: unchanged when quiet, rounding off to full scale at 1.5x.
inline i32 soft(i32 x) {
    const i32 u = gain(clamp(x, -3 * (full >> 1), 3 * (full >> 1)), 21845);
    return clamp((3 * u - times(times(u, u), u)) >> 1, -full, full);
}
// A one-pole low-pass step: k from 0 (stays put) to 32767 (follows at once).
inline i32 follow(i32 state, i32 x, i32 k) { return state + gain(x - state, k); }

// ---- Keeping sound in memory ----
// Two levels kept in one word, 16 bits each as a small floating-point number
// (a sign, 4 bits of exponent, 12 bits of precision): within 1/4000 of the
// sound at any loudness, so echoes and reverb tails fade cleanly, up to twice
// full scale.
inline u32 shrink(i32 v) {
    const u32 sign = v < 0 ? 0x8000u : 0u;
    u32 a = static_cast<u32>(v < 0 ? -clamp(v, -268369920, 0) : clamp(v, 0, 268369920));
    if (a < 8192) return sign | ((a + 2) >> 2);
    u32 x = a >> 13, s = 2;
    if (x >= 256) { x >>= 8; s += 8; }
    if (x >= 16) { x >>= 4; s += 4; }
    if (x >= 4) { x >>= 2; s += 2; }
    if (x >= 2) { s += 1; }
    return sign | (((s - 1) << 11) + ((a + (1u << (s - 1))) >> s) - 2048);
}
inline i32 grow(u32 c) {
    const u32 e = (c >> 11) & 15, m = c & 2047;
    const i32 a = static_cast<i32>(e ? (m + 2048) << (e + 1) : m << 2);
    return c & 0x8000u ? -a : a;
}
inline u32 store(i32 l, i32 r) { return shrink(l) | shrink(r) << 16; }
inline i32 stored_left(u32 w) { return grow(w & 0xffffu); }
inline i32 stored_right(u32 w) { return grow(w >> 16); }

// ---- Half rate ----
// An effect can work at half rate (22 kHz) to double how much sound its memory
// holds. down() takes a block's 64 samples to 32 levels and up() brings 32 back
// to 64, both through a steep filter (everything above 14 kHz 58 dB down, flat to
// 8 kHz), so nothing folds back as harsh tones the music doesn't have. Each side
// needs its own `down_words` and `up_words` of state.
const u32 down_words = 22 + 64 + 32, up_words = 11 + 32;
// The 32 levels, kept in the line's last 32 words.
inline i32* down(u32* line, const i32* in) {
    i32* x = reinterpret_cast<i32*>(line);
    for (u32 j = 0; j < 22; ++j) x[j] = x[64 + j];
    for (u32 j = 0; j < 64; ++j) x[22 + j] = level(in[j]);
    i32* y = x + 86;
    for (u32 m = 0; m < 32; ++m) {
        const i32* c = x + 12 + 2 * m;
        y[m] = (c[0] >> 1) + opxy::q31::multiply(c[-1] + c[1], 673188842) + opxy::q31::multiply(c[-3] + c[3], -198238255)
             + opxy::q31::multiply(c[-5] + c[5], 91991296) + opxy::q31::multiply(c[-7] + c[7], -43519699)
             + opxy::q31::multiply(c[-9] + c[9], 18300321) + opxy::q31::multiply(c[-11] + c[11], -6100729);
    }
    return y;
}
// Where to put the 32 half-rate levels for up().
inline i32* up_in(u32* line) { return reinterpret_cast<i32*>(line) + 11; }
// Full-rate level i (0 to 63) of the block, once up_in() is filled.
inline i32 up(const u32* line, u32 i) {
    const i32* z = reinterpret_cast<const i32*>(line) + 11 + (i >> 1);
    if (i & 1) return z[-5];
    return opxy::q31::multiply(z[-6] + z[-5], 1346377684) + opxy::q31::multiply(z[-7] + z[-4], -396476511)
         + opxy::q31::multiply(z[-8] + z[-3], 183982592) + opxy::q31::multiply(z[-9] + z[-2], -87039398)
         + opxy::q31::multiply(z[-10] + z[-1], 36600643) + opxy::q31::multiply(z[-11] + z[0], -12201459);
}
// After the block: keep what the next one needs.
inline void up_next(u32* line) { for (u32 j = 0; j < 11; ++j) line[j] = line[32 + j]; }

// ---- Controls ----
// A sine of a 32-bit phase, Q15 (a parabola, refined once).
inline i32 sine(u32 phase) {
    const i32 x = static_cast<i32>(phase) >> 16;               // -32768 .. 32767: half a turn each way
    const i32 ax = x < 0 ? -x : x;
    const i32 y = (x * (32768 - ax)) >> 13;                    // 4x(1-|x|), Q15
    const i32 ay = y < 0 ? -y : y;
    return clamp(y + ((((y * ay) >> 15) - y) * 7373 >> 15), -32767, 32767);   // + 0.225 (y|y| - y)
}
// 2 to the power e (Q12, -4 to +4 octaves), as a Q12 multiple: 4096 is 1.
inline u32 exp2_q12(i32 e) {
    const i32 whole = e >> 12;
    const u32 f = static_cast<u32>(e & 4095);
    const u32 base = 4096 + ((f * (2712 + ((1384 * f) >> 12))) >> 12);
    return whole >= 0 ? base << whole : base >> -whole;
}
// 2 to the power e (Q12, 0 to 31 octaves) as a whole number: 2^(e / 4096).
inline u32 exp2_whole(i32 e) {
    if (e <= 0) return 1;
    const u32 whole = static_cast<u32>(e >> 12);
    const u32 base = fx::exp2_q12(e & 4095);          // 4096 .. 8191
    return whole >= 12 ? base << (whole - 12) : base >> (12 - whole);
}
// log2 of x (above 0) in Q12: 4096 a doubling (log2(1) 0, log2(32768) 61440).
inline i32 log2_q12(u32 x) {
    if (!x) return -4096 * 16;
    i32 e = 0;
    if (x >= 1u << 16) { x >>= 16; e += 16; }
    if (x >= 1u << 8) { x >>= 8; e += 8; }
    if (x >= 1u << 4) { x >>= 4; e += 4; }
    if (x >= 1u << 2) { x >>= 2; e += 2; }
    if (x >= 1u << 1) { e += 1; }
    return e << 12;
}
// The same, with the fraction: x's top bits after its highest one (within 0.006).
inline i32 log2_fine(u32 x) {
    if (!x) return -4096 * 16;
    const i32 whole = log2_q12(x) >> 12;
    const u32 m = whole >= 12 ? (x >> (whole - 12)) & 4095 : (x << (12 - whole)) & 4095;   // the fraction, Q12
    const i32 f = static_cast<i32>(m);
    return (whole << 12) + f + ((((f * (4096 - f)) >> 12) * 1420) >> 12);                    // + 0.3466 f (1 - f)
}

// ---- The song's time (version 5 effects, "version": 5 in effect.json) ----
// hd_render gets a fifth argument, the song's time, the same as FxTime in
// <opxy/hadden_tapes.hpp> (SDK patch 0022):
//   extern "C" void hd_render(u32* state, i32* left, i32* right, const i32* parameters, const fx::Time* time);
// and may name its knobs' values (on the screen's side; never touch the state):
//   extern "C" void hd_show(u32 knob, const i32* parameters, const fx::Time* time, char* text);
// hd_show names knob `knob`'s value in up to 8 characters ("350 ms", "1/8"), with
// all the knobs as hd_render gets them, since one may change what another means.
struct Time {
    u32 bpm_x10;          // tempo in tenths of BPM (1200 when there is none)
    u32 playing;          // 1 while the song plays
    u32 ticks96;          // the song's position, 96 a beat
    u32 beat_samples;     // samples in a beat (22050 at 120 BPM)
    u32 beat_phase;       // where in the beat the block starts, a whole turn 2^32
    u32 beat_step;        // how far beat_phase moves each sample
    u32 where;            // 0 the whole mix, 1 a send track
    u32 beats;            // whole beats into the song
};
// Note lengths, short to long, for a knob that picks one: 1/32, 1/16 triplet,
// 1/16, 1/8 triplet, dotted 1/16, 1/8, 1/4 triplet, dotted 1/8, 1/4, dotted 1/4,
// 1/2, a bar. In 96ths of a beat.
const u32 note_count = 12;
inline u32 note_ticks(u32 index) {
    switch (index) {
    case 0: return 12;  case 1: return 16;  case 2: return 24;  case 3: return 32;
    case 4: return 36;  case 5: return 48;  case 6: return 64;  case 7: return 72;
    case 8: return 96;  case 9: return 144; case 10: return 192; default: return 384;
    }
}
// The note length a knob (Q15) points at.
inline u32 note_of(i32 q15) { const u32 i = static_cast<u32>(q15 * static_cast<i32>(note_count)) >> 15; return i < note_count ? i : note_count - 1; }
// A note length in samples at the song's tempo (no division: 2731/262144 is 1/96 to 0.02%).
inline u32 note_samples(const Time* t, u32 index) { return ((t->beat_samples * 2731u) >> 10) * note_ticks(index) >> 8; }
// Its name, such as "1/8." or "1/4T", into text (at least 6 characters).
inline void note_name(u32 index, char* text) {
    u32 n = 0;
    const u32 ticks = note_ticks(index);
    if (ticks == 384) { text[0] = 'b'; text[1] = 'a'; text[2] = 'r'; text[3] = 0; return; }
    text[n++] = '1'; text[n++] = '/';
    const u32 base = ticks == 16 || ticks == 12 ? 32 : ticks == 24 || ticks == 32 || ticks == 36 ? 16
                   : ticks == 48 || ticks == 64 || ticks == 72 ? 8 : ticks == 96 || ticks == 144 ? 4 : 2;
    const u32 shown = ticks == 16 ? 16 : ticks == 32 ? 8 : ticks == 64 ? 4 : base;   // a triplet names the note it shares a beat with
    if (shown >= 10) { text[n++] = static_cast<char>('0' + (shown >= 30 ? 3 : 1)); text[n++] = static_cast<char>('0' + (shown >= 30 ? shown - 30 : shown - 10)); }
    else text[n++] = static_cast<char>('0' + shown);
    if (ticks == 16 || ticks == 32 || ticks == 64) text[n++] = 'T';
    if (ticks == 36 || ticks == 72 || ticks == 144) text[n++] = '.';
    text[n] = 0;
}
// A whole number into text at `at` (no division: each digit by subtraction);
// returns where the text goes on. Add letters after it one at a time, as
// text[n++] = 'm': a string in quotes would be data, which an effect can't hold.
inline u32 number(u32 v, char* text, u32 at = 0) {
    bool started = false;
    for (u32 p = 1000000000u; p; ) {
        u32 d = 0;
        while (v >= p) { v -= p; ++d; }
        if (d || started || p == 1) { if (at < 8) text[at++] = static_cast<char>('0' + d); started = true; }
        p = p == 1 ? 0 : p == 10 ? 1 : p == 100 ? 10 : p == 1000 ? 100 : p == 10000 ? 1000 : p == 100000 ? 10000
          : p == 1000000 ? 100000 : p == 10000000 ? 1000000 : p == 100000000 ? 10000000 : 100000000;
    }
    text[at < 8 ? at : 8] = 0;
    return at;
}
// Milliseconds as "350 ms".
inline void ms_text(u32 ms, char* text) {
    u32 n = number(ms, text);
    if (n < 6) { text[n++] = ' '; text[n++] = 'm'; text[n++] = 's'; }
    text[n] = 0;
}
// A percent as "42%".
inline void percent_text(u32 p, char* text) { u32 n = number(p, text); text[n++] = '%'; text[n] = 0; }
// A knob (0 to 2147483647) as a percent, 0 to 100, without dividing.
inline u32 percent(i32 value) { return static_cast<u32>(((value < 0 ? 0 : value) >> 16) * 100 + 16384) >> 15; }
// Samples to milliseconds (about: 1/44.1 is 1486/65536).
inline u32 milliseconds(u32 samples) { return (samples * 1486u) >> 16; }
// A wave in time with the song: one turn every note length (note_ticks(index)),
// its phase at sample i of the block, 2^32 a turn, from the song's start.
inline u32 synced_phase(const Time* t, u32 index, u32 i) {
    const u32 ticks = note_ticks(index);
    // 96 / ticks turns a beat, Q16 (the triplets and dotted notes rounded).
    const u32 per_beat = ticks == 12 ? 524288u : ticks == 16 ? 393216u : ticks == 24 ? 262144u : ticks == 32 ? 196608u
                       : ticks == 36 ? 174763u : ticks == 48 ? 131072u : ticks == 64 ? 98304u : ticks == 72 ? 87381u
                       : ticks == 96 ? 65536u : ticks == 144 ? 43691u : ticks == 192 ? 32768u : 16384u;
    return ((t->beats * per_beat) << 16) + ((t->beat_phase + t->beat_step * i) >> 16) * per_beat;
}
}
