#ifndef H_SOUND
#define H_SOUND

#define DECODE_ADPCM
#define DECODE_IMA
#define DECODE_VAG
#define DECODE_XA

#define DECODE_OGG

#if !defined(_OS_PSP) && !defined(_OS_WEB) && !defined(_OS_PSV) && !defined(_OS_3DS) && !defined(_OS_XBOX) && !defined(_OS_XB1)
    #define DECODE_MP3
#endif

#include "utils.h"

#ifdef DECODE_MP3
    #include "libs/minimp3/minimp3.h"
#endif

#ifdef DECODE_OGG
    #ifdef USE_LIBVORBIS
        #include <tremor/ivorbisfile.h>
    #else
        #define STB_VORBIS_HEADER_ONLY
        #include "libs/stb_vorbis/stb_vorbis.c"
    #endif
#endif

#define SND_CHANNELS_MAX    128
#define SND_FADEOFF_DIST    (1024.0f * 8.0f)
#define SND_LOWPASS_FREQ    0.2f
#define SND_MAX_VOLUME      20
#define SND_PAN_FACTOR      0.7f
#define SND_FACING_FACTOR   0.3f

namespace Sound {

    static const int8 SPU_POS[] = { 0, 60, 115,  98, 122 };
    static const int8 SPU_NEG[] = { 0,  0, -52, -55, -60 };

    static const int16 SPU_ZIG_ZAG[7][30] = {
        { 0, 0, 0, 0, 0, 0, -0x0002, +0x000A, -0x0022, +0x0041, -0x0054, +0x0034, +0x0009, -0x010A, +0x0400, -0x0A78, +0x234C, +0x6794, -0x1780, +0x0BCD, -0x0623, +0x0350, -0x016D, +0x006B, +0x000A, -0x0010, +0x0011, -0x0008, +0x0003, -0x0001 },
        { 0, 0, 0, 0, -0x0002, 0, +0x0003, -0x0013, +0x003C, -0x004B, +0x00A2, -0x00E3, +0x0132, -0x0043, -0x0267, +0x0C9D, +0x74BB, -0x11B4, +0x09B8, -0x05BF, +0x0372, -0x01A8, +0x00A6, -0x001B, +0x0005, +0x0006, -0x0008, +0x0003, -0x0001, 0 },
        { 0, 0, 0, -0x0001, +0x0003, -0x0002, -0x0005, +0x001F, -0x004A, +0x00B3, -0x0192, +0x02B1, -0x039E, +0x04F8, -0x05A6, +0x7939, -0x05A6, +0x04F8, -0x039E, +0x02B1, -0x0192, +0x00B3, -0x004A, +0x001F, -0x0005, -0x0002, +0x0003, -0x0001, 0, 0 },
        { 0, 0, -0x0001, +0x0003, -0x0008, +0x0006, +0x0005, -0x001B, +0x00A6, -0x01A8, +0x0372, -0x05BF, +0x09B8, -0x11B4, +0x74BB, +0x0C9D, -0x0267, -0x0043, +0x0132, -0x00E3, +0x00A2, -0x004B, +0x003C, -0x0013, +0x0003, 0, -0x0002, 0, 0, 0 },
        { 0, -0x0001, +0x0003, -0x0008, +0x0011, -0x0010, +0x000A, +0x006B, -0x016D, +0x0350, -0x0623, +0x0BCD, -0x1780, +0x6794, +0x234C, -0x0A78, +0x0400, -0x010A, +0x0009, +0x0034, -0x0054, +0x0041, -0x0022, +0x000A, -0x0001, 0, +0x0001, 0, 0, 0 },
        { 0, +0x0002, -0x0008, +0x0010, -0x0023, +0x002B, +0x001A, -0x00EB, +0x027B, -0x0548, +0x0AFA, -0x16FA, +0x53E0, +0x3C07, -0x1249, +0x080E, -0x0347, +0x015B, -0x0044, -0x0017, +0x0046, -0x0023, +0x0011, -0x0005, 0, 0, 0, 0, 0, 0 },
        { 0, -0x0005, +0x0011, -0x0023, +0x0046, -0x0017, -0x0044, +0x015B, -0x0347, +0x080E, -0x1249, +0x3C07, +0x53E0, -0x16FA, +0x0AFA, -0x0548, +0x027B, -0x00EB, +0x001A, +0x002B, -0x0023, +0x0010, -0x0008, +0x0002, 0, 0, 0, 0, 0, 0 },
    };

    struct Frame {
        int16 L, R;
    };

    struct FrameHI {
        int32 L, R;
    };

    struct Stats {
        int mixer;
        int reverb;
        int render[2];
        int ogg;
    } stats;

    namespace Filter {
        #define MAX_FDN       16
        #define MAX_DELAY     954

        #define DSP_SCALE_BIT 8
        #define DSP_SCALE     (1 << DSP_SCALE_BIT)

        static const int16 FDN[MAX_FDN] = { 281, 331, 373, 419, 461, 503, 547, 593, 641, 683, 727, 769, 811, 853, 907, 953 };

        struct Delay {
            int     index;
            int16   out[MAX_DELAY];

            void process(int16 &x, int16 delay) {
                index = (index + 1) % delay;
                int16 y = out[index];
                out[index] = x;
                x = y;
            }
        };

        struct Absorption {
            int16 out;

            void process(int16 &x, int32 gain, int32 damping) {
                x = out = (out * damping + ((x * gain * (DSP_SCALE - damping)) >> DSP_SCALE_BIT)) >> DSP_SCALE_BIT;
            }
        };

        struct LowPass {
            float buffer[2][4];

            LowPass() {
                memset(buffer, 0, sizeof(buffer));
            }

            inline void process(int32 &x, float *out, float freq) {
                out[0] += freq * (float(x) - out[0]);
                out[1] += freq * (out[0] - out[1]);
                out[2] += freq * (out[1] - out[2]);
                out[3] += freq * (out[2] - out[3]);
                x = int32(out[3]);
            }

            void process(FrameHI *frames, int count, float freq) {
                for (int i = 0; i < count; i++) {
                    process(frames[i].L, buffer[0], freq);
                    process(frames[i].R, buffer[1], freq);
                }
            }
        };

        struct Reverberation {
            Delay       df[MAX_FDN];
            Absorption  af[MAX_FDN];

            int32       output[MAX_FDN];
            int16       buffer[MAX_FDN];
            int16       panCoeff[MAX_FDN][2];
            int32       absCoeff[MAX_FDN][2];   // absorption gain & damping

            Reverberation() {
                for (int i = 0; i < MAX_FDN; i++) {
                    panCoeff[i][0] = (i % 2) ? -1 : 1;
                }

                for (int i = 0; i < MAX_FDN; i += 2) {
                    if ((i / 2) % 2)
                        panCoeff[i][1] = panCoeff[i + 1][1] = -1;
                    else
                        panCoeff[i][1] = panCoeff[i + 1][1] =  1;
                }

                clear();
            }

            void clear() {
                memset(output, 0, sizeof(output));
                memset(df, 0, sizeof(df));
                memset(af, 0, sizeof(af));

                setRoomSize(vec3(1.0f));
            }

            void setRoomSize(const vec3 &size) {
                float S = (size.x * size.z + size.x * size.y + size.z * size.y) * 2;
                float V = size.x * size.y * size.z;
                float f = 0.1f; // absorption factor
                float rt60 = 0.161f * (V / (S * f));
                float k = -10.0f / (44100.0f * rt60);

                for (int i = 0; i < MAX_FDN; i++) {
                    float v = powf(10.0f, FDN[i] * k);
                    absCoeff[i][0] = int32(v * DSP_SCALE);
                    absCoeff[i][1] = int32((1.0f - (2.0f / (1.0f + powf(v, 1.0f - 1.0f / 0.15f)))) * DSP_SCALE);
                }
            };

            void process(FrameHI *frames, int count) {
                PROFILE_CPU_TIMING(stats.reverb);

                for (int i = 0; i < count; i++) {
                    FrameHI &frame = frames[i];
                    int32 in  = (frame.L + frame.R) / 2;
                    int32 out = 0;
                    int32 L   = 0;
                    int32 R   = 0;

                // apply delay & absorption filters
                    for (int j = 0; j < MAX_FDN; j++) {
                        int16 k = clamp(in + output[j], -0x7FFF, 0x7FFF);
                        df[j].process(k, FDN[j]);
                        af[j].process(k, absCoeff[j][0], absCoeff[j][1]);
                        buffer[j] = k;
                        out += k;
                    }
                    out = out * 2 / MAX_FDN;

                // apply pan
                    int16 buf = buffer[MAX_FDN - 1];
                    for (int j = 0; j < MAX_FDN; j++) {
                        output[j] = max(0, out - buf);
                        buf = buffer[j];
                        L += buf ^ panCoeff[j][0];
                        R += buf ^ panCoeff[j][1];
                    }

                    frame.L += L / MAX_FDN;
                    frame.R += R / MAX_FDN;
                }
            }
        };

        #undef MAX_FDN
        #undef MAX_DELAY
    };

    struct Decoder {
        Stream  *stream;
        int     channels, freq, offset;
        Frame   prevFrame;

        Decoder(Stream *stream, int channels, int freq) : stream(stream), channels(channels), freq(freq), offset(stream ? stream->pos : 0) {
            memset(&prevFrame, 0, sizeof(prevFrame));
        }

        virtual ~Decoder() { delete stream; }
        virtual int decode(Frame *frames, int count) { return 0; }
        virtual void replay() { stream->seek(offset - stream->pos); }

        int resample(Sound::Frame *frames, Sound::Frame &frame) {
            if (freq == 44100) {
                frames[0] = frame;
                return 1;
            }

            int dL = int(frame.L) - int(prevFrame.L);
            int dR = int(frame.R) - int(prevFrame.R);
            switch (freq) {
                case 11025 :
                    if (channels == 2) {
                        frames[0].L = prevFrame.L + dL / 4;                   // 0.25 L
                        frames[0].R = prevFrame.R + dR / 4;                   // 0.25 R
                        frames[1].L = prevFrame.L + dL / 2;                   // 0.50 L
                        frames[1].R = prevFrame.R + dR / 2;                   // 0.50 R
                        frames[2].L = prevFrame.L + dL * 3 / 4;               // 0.75 L
                        frames[2].R = prevFrame.R + dR * 3 / 4;               // 0.75 R
                    } else {
                        frames[0].L = frames[0].R = prevFrame.L + dL / 4;     // 0.25 LR
                        frames[1].L = frames[1].R = prevFrame.L + dL / 2;     // 0.50 LR
                        frames[2].L = frames[2].R = prevFrame.L + dL * 3 / 4; // 0.75 LR
                    }
                    frames[3] = prevFrame = frame;                            // 1.00 LR
                    return 4;
                case 22050 :
                    if (channels == 2) {
                        frames[0].L = prevFrame.L + dL / 2;                   // 0.50 L
                        frames[0].R = prevFrame.R + dR / 2;                   // 0.50 R
                    } else
                        frames[0].L = frames[0].R = prevFrame.L + dL / 2;     // 0.50 LR
                    frames[1] = prevFrame = frame;                            // 1.00 LR
                    return 2;
                default    : // impossible
                    ASSERT(false);
                    int k = 44100 / freq;
                    for (int i = 0; i < k; i++) frames[i] = frame; // no lerp
                    return k;
            }        
        }
    };

    struct PCM : Decoder {
        int size, bits;

        PCM(Stream *stream, int channels, int freq, int size, int bits) : Decoder(stream, channels, freq), size(size), bits(bits) {}

        virtual int decode(Frame *frames, int count) {
            if (stream->pos - offset >= size) return 0;

            // ! in the original game series only 11025 and 22050 Hz single channel samples were used ! //

            Frame frame;
            if (bits == 16) {
                int16 value;
                if (channels == 2) {
                    frame.L = stream->read(value);
                    frame.R = stream->read(value);
                } else
                    frame.L = frame.R = stream->read(value);
            } else if (bits == 8 || bits == -8) {

                if (bits > 0) {
                    uint8 value;
                    if (channels == 2) {
                        frame.L = stream->read(value) * 257 - 32768;
                        frame.R = stream->read(value) * 257 - 32768;
                    } else
                        frame.L = frame.R = stream->read(value) * 257 - 32768;
                } else {
                    int8 value;
                    if (channels == 2) {
                        frame.L = (stream->read(value) + 128) * 257 - 32768;
                        frame.R = (stream->read(value) + 128) * 257 - 32768;
                    } else
                        frame.L = frame.R = (stream->read(value) + 128) * 257 - 32768;
                }

            } else {
                ASSERT(false);
                return 0;
            }

            return resample(frames, frame);
        }
    };

#ifdef DECODE_ADPCM
    struct ADPCM : Decoder { // https://wiki.multimedia.cx/?title=Microsoft_ADPCM
        int size, block;

        ADPCM(Stream *stream, int channels, int freq, int size, int block) : Decoder(stream, channels, freq), size(size), block(block) {}

        struct Channel {
            int16 c1, c2;
            int16 delta;
            int16 sample1;
            int16 sample2;

            int predicate(uint8 nibble) {
                static const int table[] = { 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 };
    
                int8 ns = nibble;
                if (ns & 8) ns -= 16;

                int sample = (sample1 * c1 + sample2 * c2) / 256 + ns * delta;
                sample  = clamp(sample, -32768, 32767);
                sample2 = sample1;
                sample1 = sample;
                delta   = max(table[nibble] * delta / 256, 16);
                return sample;
            }
        } channel[2];

        virtual int decode(Frame *frames, int count) {
            static const int coeff1[] = { 256, 512, 0, 192, 240, 460, 392 };
            static const int coeff2[] = { 0, -256, 0, 64, 0, -208, -232 };
            
            int seek = stream->pos - offset;
            if (seek >= size) return 0;

            if (seek % block == 0) {
                for (int i = 0; i < channels; i++) {
                    uint8 index;
                    stream->read(index);
                    channel[i].c1 = coeff1[index];
                    channel[i].c2 = coeff2[index];
                }
                for (int i = 0; i < channels; i++) stream->read(channel[i].delta);
                for (int i = 0; i < channels; i++) stream->read(channel[i].sample1);
                for (int i = 0; i < channels; i++) stream->read(channel[i].sample2);

                if (channels == 1) {
                    if (freq == 22050) {
                        ASSERT(count >= 4);
                        frames[0].L = frames[0].R =
                        frames[1].L = frames[1].R = channel[0].sample2;
                        frames[2].L = frames[2].R =
                        frames[3].L = frames[3].R = channel[0].sample1;
                        return 4;
                    } else {
                        ASSERT(count >= 2);
                        frames[0].L = frames[0].R = channel[0].sample2;
                        frames[1].L = frames[1].R = channel[0].sample1;
                        return 2;
                    }
                } else {
                    ASSERT(freq == 44100);
                    ASSERT(count >= 2);
                    frames[0].L = channel[0].sample2;
                    frames[0].R = channel[1].sample2;
                    frames[1].L = channel[0].sample1;
                    frames[1].R = channel[1].sample1;
                    return 2;
                }
            } else {
                uint8 value;
                stream->read(value);
                uint8 n1 = value >> 4, n2 = value & 0xF;

                if (channels == 1) {
                    if (freq == 22050) {
                        ASSERT(count >= 4);
                        frames[0].L = frames[0].R =
                        frames[1].L = frames[1].R = channel[0].predicate(n1);
                        frames[2].L = frames[2].R =
                        frames[3].L = frames[3].R = channel[0].predicate(n2);
                        return 4;
                    } else {
                        ASSERT(count >= 2);
                        frames[0].L = frames[0].R = channel[0].predicate(n1);
                        frames[1].L = frames[1].R = channel[0].predicate(n2);
                        return 2;
                    }
                } else {
                    ASSERT(freq == 44100);
                    frames[0].L = channel[0].predicate(n1);
                    frames[0].R = channel[1].predicate(n2);
                    return 1;
                }
            }
        }
    };
#endif

#ifdef DECODE_IMA
    struct IMA : Decoder { // https://wiki.multimedia.cx/?title=Microsoft_ADPCM
        struct State {
            int amp, idx;
        } state[2];

        int freq;

        IMA(Stream *stream, int channels, int freq) : Decoder(stream, channels, freq) { 
            memset(state, 0, sizeof(state)); 
        }

        int16 getSample(uint8 n, State &state) {
            static int indexLUT[] = {
                -1, -1, -1, -1, 2, 4, 6, 8,
            };

            static int stepLUT[] = {
                7,     8,     9,     10,    11,    12,    13,    14,
                16,    17,    19,    21,    23,    25,    28,    31,
                34,    37,    41,    45,    50,    55,    60,    66,
                73,    80,    88,    97,    107,   118,   130,   143,
                157,   173,   190,   209,   230,   253,   279,   307,
                337,   371,   408,   449,   494,   544,   598,   658,
                724,   796,   876,   963,   1060,  1166,  1282,  1411,
                1552,  1707,  1878,  2066,  2272,  2499,  2749,  3024,
                3327,  3660,  4026,  4428,  4871,  5358,  5894,  6484,
                7132,  7845,  8630,  9493,  10442, 11487, 12635, 13899,
                15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
                32767
            };

            int step = stepLUT[state.idx];
            int idx  = n & 7;

            state.idx = clamp(state.idx + indexLUT[idx], 0, 88);

            int diff = (2 * idx + 1) * step >> 3;

            if (n & 8) {
                state.amp -= diff;
                if (state.amp < -32768)
                    state.amp = -32768;
            } else {
                state.amp += diff;
                if (state.amp > 32767)
                    state.amp = 32767;
            }

            return state.amp;
        }

        virtual int decode(Frame *frames, int count) {
            uint8 n;
            stream->read(n);

            int a = getSample(n >> 4,   state[0]);
            int b = getSample(n & 0x0F, state[1 % channels]);

            Frame frame;
            if (channels == 2) {
                frame.L = a;
                frame.R = b;
                return resample(frames, frame);
            } else {
                frame.L = frame.R = a;
                int i = resample(frames, frame);
                frame.L = frame.R = b;
                return i + resample(frames + i, frame);
            }
        }
    };
#endif

#ifdef DECODE_VAG
    struct VAG : Decoder {
        uint8 pred, shift, flags;
        int s1, s2;
        Frame buffer[28 * 4];
        int bufferSize;

        VAG(Stream *stream) : Decoder(stream, 1, 11025), s1(0), s2(0), bufferSize(0) {}

        void predicate(short value) {
            int s = (s1 * SPU_POS[pred] + s2 * SPU_NEG[pred]) >> 6;
            s = clamp((value >> shift) + s, -32768, 32767);
            s2 = s1;
            s1 = s;
        }

        void resample(Frame *frames, short value) {
            predicate(value);
            frames[0].L = frames[0].R = s2 + (s1 - s2) / 4;     // 0.25
            frames[1].L = frames[1].R = s2 + (s1 - s2) / 2;     // 0.50
            frames[2].L = frames[2].R = s2 + (s1 - s2) * 3 / 4; // 0.75
            frames[3].L = frames[3].R = s1;                     // 1.00
        }

        int processBlock() {
            if (stream->pos >= stream->size)
                return 0;
            stream->read(pred);
            stream->read(flags);
            shift = pred & 0x0F;
            pred >>= 4;

            int i = 0;
            while (i < 14) {
                uint8 d;
                stream->read(d);
                resample(&buffer[i * 8 + 0], (d & 0x0F) << 12);
                resample(&buffer[i * 8 + 4], (d & 0xF0) <<  8);
                i++;
            }
            return i * 8;
        }

        virtual int decode(Frame *frames, int count) {
            int res = 0;

            while (res < count) {
                if (!bufferSize && !(bufferSize = processBlock())) // if no data in buffer - process next block
                    break;

                int length = min(bufferSize, count - res);
                memcpy(&frames[res], buffer, length * sizeof(Frame));
                res += length;

                if (bufferSize -= length) { // if data remained in buffer, move it to the beginning
                    memcpy(buffer, &buffer[sizeof(buffer) / sizeof(Frame) - bufferSize], bufferSize * sizeof(Frame));
                    break;
                }
            }

            return res;
        }

        virtual void replay() {
            stream->setPos(0);
            s1 = s2 = 0;
        }
    };
#endif

#ifdef DECODE_XA
    // http://problemkaputt.de/psx-spx.htm#cdromxaaudioadpcmcompression
    struct XA : Decoder {
        uint8 pred, shift, flags;
        int s1, s2;

        Frame buffer[18 * 112];
        int   pos;

        struct Group {
            uint8 params[16];
            uint8 data[112];
        } groups[18];

        Frame  prevFrames[2];

        Frame  lerpFrames[32];
        uint32 lerpPos;

        XA(Stream *stream) : Decoder(stream, 1, 11025), s1(0), s2(0), pos(COUNT(buffer)), lerpPos(0) {
            memset(prevFrames, 0, sizeof(prevFrames));
            memset(lerpFrames, 0, sizeof(lerpFrames));
        }

        void decode28(Group &group, int block, int channel) {
            int16 *dst   = channel ? &buffer[pos].R  : &buffer[pos].L;
            int16 &old   = channel ? prevFrames[0].R : prevFrames[0].L; 
            int16 &older = channel ? prevFrames[1].R : prevFrames[1].L; 

            int shift  = 12 - (group.params[4 + block * 2 + channel] & 0x0F);
            int filter =      (group.params[4 + block * 2 + channel] & 0x30) >> 4;

            int f0 = SPU_POS[filter];
            int f1 = SPU_NEG[filter];

            for (int i = 0; i < 28; i++) {
                int t = (group.data[block + i * 4] >> (channel * 4)) & 0x0F;
                if (t & 8) 
                    t -= 16;
                int s = (t << shift) + ((old * f0 + older * f1 + 32) / 64);
                s = clamp(s, -32768, 32767);
                older  = old;
                old    = s;
                dst[0] = s;
                dst += 2; // skip second channel
            }
        }

        void processBlock() {
            if (stream->pos >= stream->size)
                return;

            stream->raw(groups, sizeof(groups));

            pos = 0;

            for (int i = 0; i < COUNT(groups); i++)
                for (int j = 0; j < 4; j++) {
                    decode28(groups[i], j, 0);
                    decode28(groups[i], j, 1);
                    pos += 28;
                }

            pos = 0;
        }

        void ZigZagOut(Frame &frame, uint8 p, const int16 *LUT) {
            FrameHI sum;
            sum.L = sum.R = 0;

            for (uint8 i = 1; i < 30; i++) {
                Frame &f = lerpFrames[uint8(p - i) & 0x1F];
                sum.L += f.L * LUT[i];
                sum.R += f.R * LUT[i];
            }

            frame.L = clamp(sum.L >> 15, -32767, 32767);
            frame.R = clamp(sum.R >> 15, -32767, 32767);
        }

        virtual int decode(Frame *frames, int count) {
            if (pos >= COUNT(buffer))
                processBlock();

            ASSERT((int(COUNT(buffer)) - pos) % 6 == 0)
            ASSERT(count % 7 == 0)

            count = min(count, (int(COUNT(buffer)) - pos) / 6 * 7);

            int i = 0;
            while (i < count) {
                ASSERT(pos < COUNT(buffer));
                lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
                lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
                lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
                lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
                lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
                lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
                ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[0]);
                ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[1]);
                ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[2]);
                ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[3]);
                ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[4]);
                ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[5]);
                ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[6]);
            }

            ASSERT(i == count);

            return count;
        }

        virtual void replay() {
            stream->setPos(0);
            s1 = s2 = 0;
        }
    };
#endif

#ifdef DECODE_MP3
    struct MP3 : Decoder {
        mp3_decoder_t   mp3;
        char    *buffer;
        int     size, pos;        

        MP3(Stream *stream, int channels) : Decoder(stream, channels, 0), size(stream->size), pos(0) {
            mp3 = mp3_create();
            buffer = new char[size]; // TODO: file streaming
            stream->raw(buffer, size);
        }

        virtual ~MP3() {
            delete[] buffer;
            mp3_done(mp3);
        }

        virtual int decode(Frame *frames, int count) {
            mp3_info_t info;
            int i = 0;
            char *ptr = (char*)frames;
            while (ptr < (char*)&frames[count]) {
                int res = mp3_decode(mp3, buffer + pos, size - pos, (short*)ptr, &info);
                if (res) {
                    pos += res;
                    ptr += info.audio_bytes;
                    i   += info.audio_bytes;
                } else
                    break;
            }
            return i;
        }

        virtual void replay() {
            mp3_done(mp3);
            mp3 = mp3_create();
            pos = 0;
        }
    };
#endif

#ifdef DECODE_OGG

#ifdef USE_LIBVORBIS
    struct OGG : Decoder {
        OggVorbis_File   vf;
        FILE            *memFile;
        uint8           *data;

        OGG(Stream *stream, int channels) : Decoder(stream, channels, 0) {
            char buf[255];
            strcpy(buf, contentDir);
            strcat(buf, stream->name);

            data = new uint8[stream->size];
            stream->raw(data, stream->size);

            memFile = fmemopen(data, stream->size, "rb");
            int err = ov_open(memFile, &vf, NULL, 0);
            ASSERT(err >= 0);
            vorbis_info *info = ov_info(&vf, -1);
            this->channels = info->channels;
            this->freq     = info->rate;
        }

        virtual ~OGG() {
            ov_clear(&vf);
            fclose(memFile);
            delete[] data;
        }

        virtual int decode(Frame *frames, int count) {
            PROFILE_CPU_TIMING(stats.ogg);
            int i = 0;
            int bytes = count * sizeof(Frame);
            while (i < bytes) {
                int bitstream;
                int res = ov_read(&vf, (char*)frames + i, bytes - i, &bitstream);
                if (!res) break;
                i += res;
            }
            return i / sizeof(Frame);
        }

        virtual void replay() {
            fseek(memFile, 0, SEEK_SET);
            ov_open(memFile, &vf, NULL, 0);
        }
    };
#else // stb_vorbis
    struct OGG : Decoder {
        stb_vorbis       *ogg;
        stb_vorbis_alloc alloc;
        uint8            *data;

        OGG(Stream *stream, int channels) : Decoder(stream, channels, 0) {
            char buf[255];
            strcpy(buf, contentDir);
            strcat(buf, stream->name);

            data = new uint8[stream->size];
            stream->raw(data, stream->size);

            alloc.alloc_buffer_length_in_bytes = 256 * 1024;
            alloc.alloc_buffer = new char[alloc.alloc_buffer_length_in_bytes];
            ogg = stb_vorbis_open_memory(data, stream->size, NULL, &alloc);
            ASSERT(ogg);
            stb_vorbis_info info = stb_vorbis_get_info(ogg);
            this->channels = info.channels;
            this->freq     = info.sample_rate;
        }

        virtual ~OGG() {
            stb_vorbis_close(ogg);
            delete[] alloc.alloc_buffer;
            delete[] data;
        }

        virtual int decode(Frame *frames, int count) {
            PROFILE_CPU_TIMING(stats.ogg);
            int i = 0;
            while (i < count) {
                int res = stb_vorbis_get_samples_short_interleaved(ogg, channels, (short*)frames + i, (count - i) * 2);
                if (!res) break;
                i += res;
            }
            return i;
        }

        virtual void replay() {
            stb_vorbis_seek_start(ogg);
        }
    };
#endif

#endif // DECODE_OGG

    Core::Mutex lock;

    struct Listener {
        mat4 matrix;
        bool underwater;
    } listener[2];

    int listenersCount;

    Listener& getListener(const vec3 &pos) {
        if (listenersCount == 1 || (listener[0].matrix.getPos() - pos).length2() < (listener[1].matrix.getPos() - pos).length2())
            return listener[0];
        return listener[1];
    }

    enum Flags {
        LOOP            = 1,
        PAN             = 2,
        UNIQUE          = 4,
        REPLAY          = 8,
        MUSIC           = 16,
        FLIPPED         = 32,
        UNFLIPPED       = 64,
    };

    bool flipped;

    struct Sample {
        const vec3 *uniquePtr;
        Decoder *decoder;
        vec3    pos;
        float   volume;
        float   volumeTarget;
        float   volumeDelta;
        float   pitch;
        int     flags;
        int     id;
        bool    isPlaying;
        bool    isPaused;
        bool    stopAfterFade;

        Sample(Decoder *decoder, float volume, float pitch, int flags, int id) : uniquePtr(NULL), decoder(decoder), volume(volume), volumeTarget(volume), volumeDelta(0.0f), pitch(pitch), flags(flags), id(id) {
            isPlaying = decoder != NULL;
            isPaused  = false;
        }

        Sample(Stream *stream, const vec3 *pos, float volume, float pitch, int flags, int id) : uniquePtr(pos), decoder(NULL), volume(volume), volumeTarget(volume), volumeDelta(0.0f), pitch(pitch), flags(flags), id(id) {
            this->pos = pos ? *pos : vec3(0.0f);
            
            uint32 fourcc;
            stream->read(fourcc);
            if (fourcc == FOURCC("RIFF")) { // wav

                struct {
                    uint16  format;
                    uint16  channels;
                    uint32  samplesPerSec;
                    uint32  bytesPerSec;
                    uint16  block;
                    uint16  sampleBits;
                } waveFmt;

                stream->seek(8);
                while (stream->pos < stream->size) {
                    uint32 type, size;
                    stream->read(type);
                    stream->read(size);
                    if (type == FOURCC("fmt ")) {
                        stream->raw(&waveFmt, sizeof(waveFmt));
                        stream->seek(size - sizeof(waveFmt));
                    } else if (type == FOURCC("data")) {
                        if (waveFmt.format == 1) decoder = new PCM(stream, waveFmt.channels, waveFmt.samplesPerSec, size, waveFmt.sampleBits);
                        #ifdef DECODE_ADPCM
                        if (waveFmt.format == 2) decoder = new ADPCM(stream, waveFmt.channels, waveFmt.samplesPerSec, size, waveFmt.block);
                        #endif
                        break;
                    } else
                        stream->seek(size);
                }
            } 
            else if (fourcc == FOURCC("OggS")) { // ogg
                stream->seek(-4);
                #ifdef DECODE_OGG
                    decoder = new OGG(stream, 2);
                #endif 
            }
            else if (fourcc == FOURCC("ID3\3")) { // mp3
                #ifdef DECODE_MP3
                    decoder = new MP3(stream, 2);
                #endif
            }
            else if (fourcc == FOURCC("SEGA")) { // Sega Saturn PCM mono signed 8-bit 11025 Hz
                decoder = new PCM(stream, 1, 11025, stream->size, -8);
            }
            else { // vag
                stream->setPos(0);
                #ifdef DECODE_VAG
                    decoder = new VAG(stream);
                #endif
            }

            if (!decoder)
                delete stream;

            isPlaying = decoder != NULL;
            isPaused  = false;
        }

        ~Sample() {
            delete decoder;
        }

        void setVolume(float value, float time) {
            if (value < 0.0f) {
                stopAfterFade = true;
                value = 0.0f;
            } else
                stopAfterFade = false;

            volumeTarget = value;
            volumeDelta  = volumeTarget - volume;
            if (time > 0.0f)
                volumeDelta /= 44100.0f * time;
        }

        vec2 getPan() {
            if (!(flags & PAN))
                return vec2(1.0f);
            mat4  m = Sound::getListener(pos).matrix;
            vec3  v = pos - m.offset().xyz();
            vec3  n = v.normal();

            float dist   = max(0.0f, 1.0f - (v.length() / SND_FADEOFF_DIST));
            float pan    = m.right().xyz().dot(n);
            float facing = (0.5f - m.dir().xyz().dot(n) * 0.5f) * SND_FACING_FACTOR + (1.0f - SND_FACING_FACTOR);

            vec2  value(min(1.0f, 1.0f - pan),
                        min(1.0f, 1.0f + pan));

            return (value * SND_PAN_FACTOR + (1.0f - SND_PAN_FACTOR)) * facing * dist;
        }

        bool render(Frame *frames, int count) {
            if (!isPlaying) return false;

            if (isPaused) {
                memset(frames, 0, sizeof(Frame) * count);
                return true;
            }

        // decode
            int i = 0;
            while (i < count) {
                int res = decoder->decode(&frames[i], count - i);
                if (res == 0) {
                    if (!(flags & LOOP)) {
                        isPlaying = false;
                        break;
                    } else
                        decoder->replay();
                }
                i += res;
            }
        // apply volume
            #define VOL_CONV(x) (1.0f - sqrtf(1.0f - x * x));

            float m = ((flags & MUSIC) ? Core::settings.audio.music : Core::settings.audio.sound) / float(SND_MAX_VOLUME);
            float v = volume * m;
            vec2 pan = getPan();
            vec2 vol = pan * VOL_CONV(v);
            for (int j = 0; j < i; j++) {
                if (volumeDelta != 0.0f) { // increase / decrease channel volume
                    volume += volumeDelta;
                    if ((volumeDelta < 0.0f && volume < volumeTarget) ||
                        (volumeDelta > 0.0f && volume > volumeTarget)) {
                        volume = volumeTarget;
                        volumeDelta = 0.0f;
                        if (stopAfterFade)
                            isPlaying = false;
                    }
                    v   = volume * m;
                    vol = pan * VOL_CONV(v);
                }
                frames[j].L = int(frames[j].L * vol.x);
                frames[j].R = int(frames[j].R * vol.y);
            }
            #undef VOL_CONV

            return true;
        }

        void stop() {
            isPlaying = false;
        }

        void replay() {
            decoder->replay();
        }

        void pause() {
            isPaused = true;
        }

        void resume() {
            isPaused = false;
        }
    } *channels[SND_CHANNELS_MAX];
    int channelsCount;

    typedef void (Callback)(Sample *channel);
    Callback *callback;

    FrameHI *result;
    Frame   *buffer;

    // TODO: per listener
    Filter::Reverberation reverb;
    Filter::LowPass       lowPass;

    void init() {
        flipped = false;
        channelsCount = 0;
        callback = NULL;
        buffer = NULL;
        result = NULL;
    #ifdef DECODE_MP3
        mp3_decode_init();
    #endif
    }

    void deinit() {
        for (int i = 0; i < channelsCount; i++)
            delete channels[i];
    #ifdef DECODE_MP3
        mp3_decode_free();
    #endif
        delete[] buffer;
        delete[] result;
    }

    void renderChannels(FrameHI *result, int count, bool music) {
        PROFILE_CPU_TIMING(stats.render[music]);

        int bufSize = count + count / 2 + 4;
        if (!buffer) buffer = new Frame[bufSize]; // + 50% for pitch

        for (int i = 0; i < channelsCount; i++) {
            if (music != ((channels[i]->flags & MUSIC) != 0))
                continue;
            
            if (channels[i]->flags & (FLIPPED | UNFLIPPED)) {
                if (!(channels[i]->flags & (flipped ? FLIPPED : UNFLIPPED)))
                    continue;

                vec3 d = channels[i]->pos - getListener(channels[i]->pos).matrix.getPos();
                if (fabsf(d.x) > SND_FADEOFF_DIST || fabsf(d.y) > SND_FADEOFF_DIST || fabsf(d.z) > SND_FADEOFF_DIST)
                    continue;
            }

            if ((channels[i]->flags & LOOP) && channels[i]->volume < EPS && channels[i]->volumeTarget < EPS)
                continue;

            memset(buffer, 0, sizeof(Frame) * bufSize);
            channels[i]->render(buffer, (int(count * channels[i]->pitch) + 3) / 4 * 4);

            if (channels[i]->pitch == 1.0f) { // no pitch
                for (int j = 0; j < count; j++) {
                    result[j].L += buffer[j].L;
                    result[j].R += buffer[j].R;
                }
            } else { // has pitch (interpolate values for smooth wave)
                float t = 0.0f;
                for (int j = 0; j < count; j++, t += channels[i]->pitch) {
                    int idxA = int(t);
                    int idxB = (j == (count - 1)) ? idxA : (idxA + 1);
                    int st = int((t - idxA) * DSP_SCALE);
                    Frame &a = buffer[idxA];
                    Frame &b = buffer[idxB];

                    result[j].L += a.L + ((b.L - a.L) * st >> DSP_SCALE_BIT);
                    result[j].R += a.R + ((b.R - a.R) * st >> DSP_SCALE_BIT);
                }
            }
        }
    }

    void convFrames(FrameHI *from, Frame *to, int count) {
        for (int i = 0; i < count; i++) {
            to[i].L = clamp(from[i].L, -32767, 32767);
            to[i].R = clamp(from[i].R, -32767, 32767);
        }
    }

    void fill(Frame *frames, int count) {
        OS_LOCK(lock);
        PROFILE_CPU_TIMING(stats.mixer);

        if (!channelsCount) {
            if (result && (Core::settings.audio.music != 0 || Core::settings.audio.sound != 0)) {
                memset(result, 0, sizeof(FrameHI) * count);
                if (Core::settings.audio.reverb)
                    reverb.process(result, count);
                convFrames(result, frames, count);
            } else
                memset(frames, 0, sizeof(frames[0]) * count);
            return;
        }

        if (!result) result = new FrameHI[count];
        memset(result, 0, sizeof(FrameHI) * count);

        if (Core::settings.audio.sound != 0) {
            renderChannels(result, count, false);

            if (Core::settings.audio.reverb) {
                if (listener[0].underwater) {
                    lowPass.process(result, count, SND_LOWPASS_FREQ);
                }
                reverb.process(result, count);
            }
        }

        if (Core::settings.audio.music != 0) {
            renderChannels(result, count, true);
        }

        convFrames(result, frames, count);

        for (int i = 0; i < channelsCount; i++) 
            if (!channels[i]->isPlaying) {
                if (callback) callback(channels[i]);
                delete channels[i];
                channels[i] = channels[--channelsCount];
                i--;
            }
    }

    Stream *openCDAudioWAD(const char *name, int index = -1) {
        if (!Stream::existsContent(name))
            return NULL;

        Stream *stream = new Stream(name);
        if (stream->size) {
            struct Item {
                char name[260];
                int  size;
                int  offset;
            } entity;

            stream->seek(sizeof(entity) * index);
            stream->raw(&entity, sizeof(entity));
            stream->setPos(entity.offset);
            return stream;
        }
        delete stream;
        return NULL;
    }

    Stream *openCDAudioMP3(const char *dat, const char *name, int index = -1) {
        if (!Stream::existsContent(dat) || !Stream::existsContent(name))
            return NULL;
        Stream *stream = new Stream(name);
        return stream;
    }

    Sample* getChannel(int id, const vec3 *pos) {
        for (int i = 0; i < channelsCount; i++)
            if (channels[i]->id == id && channels[i]->uniquePtr == pos)
                return channels[i];
        return NULL;
    }

    Sample* play(Stream *stream, const vec3 *pos = NULL, float volume = 1.0f, float pitch = 0.0f, int flags = 0, int id = - 1) {
        OS_LOCK(lock);

        ASSERT(pitch >= 0.0f);
        if (!stream) return NULL;
        if (volume > 0.001f) {
            if (pos && !(flags & (FLIPPED | UNFLIPPED | MUSIC)) && (flags & PAN)) {
                vec3 listenerPos = getListener(*pos).matrix.getPos();
                vec3 d = *pos - listenerPos;

                if (fabsf(d.x) > SND_FADEOFF_DIST || fabsf(d.y) > SND_FADEOFF_DIST || fabsf(d.z) > SND_FADEOFF_DIST) {
                    delete stream;
                    return NULL;
                }
            }

            if (flags & (UNIQUE | REPLAY)) {
                Sample *ch = getChannel(id, pos);

                if (ch) {
                    if (pos)
                        ch->pos = *pos;
                    ch->pitch = pitch;

                    if (flags & REPLAY)
                        ch->replay();

                    delete stream;
                    return ch;
                }
            }

            if (channelsCount < SND_CHANNELS_MAX)
                return channels[channelsCount++] = new Sample(stream, pos, volume, pitch, flags, id);

            LOG("! no free channels\n");
        }
        delete stream;
        return NULL;
    }

    Sample* play(Decoder *decoder) {
        OS_LOCK(lock);

        if (channelsCount < SND_CHANNELS_MAX)
            return channels[channelsCount++] = new Sample(decoder, 1.0f, 1.0f, MUSIC, -1);
        return NULL;
    }

    void stop(int id = -1) {
        OS_LOCK(lock);

        for (int i = 0; i < channelsCount; i++)
            if (id == -1 || channels[i]->id == id)
                channels[i]->stop();
    }

    void stopAll() {
        OS_LOCK(lock);
        reverb.clear();

        for (int i = 0; i < channelsCount; i++)
            delete channels[i];
        channelsCount = 0;
    }
}

#endif
