예제 #1
0
 static void _v_readstring(ref Ogg.oggpack_buffer o, ref char[] buf, int bytes)
 {
     for (int i = 0; i < bytes; i++)
     {
         buf[i] = (char)Ogg.oggpack_read(ref o, 8);
     }
 }
예제 #2
0
        /* pack side */

        static public int _vorbis_pack_info(ref Ogg.oggpack_buffer opb, ref vorbis_info vi)
        {
            codec_setup_info ci = vi.codec_setup as codec_setup_info;

            if (ci == null)
            {
                return(OV_EFAULT);
            }

            /* preamble */
            Ogg.oggpack_write(ref opb, 0x01, 8);
            _v_writestring(ref opb, new char[] { 'v', 'o', 'r', 'b', 'i', 's' }, 6);

            /* basic information about the stream */
            Ogg.oggpack_write(ref opb, 0x00, 32);
            Ogg.oggpack_write(ref opb, (uint)vi.channels, 8);
            Ogg.oggpack_write(ref opb, (uint)vi.rate, 32);

            Ogg.oggpack_write(ref opb, (uint)vi.bitrate_upper, 32);
            Ogg.oggpack_write(ref opb, (uint)vi.bitrate_nominal, 32);
            Ogg.oggpack_write(ref opb, (uint)vi.bitrate_lower, 32);

            Ogg.oggpack_write(ref opb, (uint)ilog2((uint)ci.blocksizes[0]), 4);
            Ogg.oggpack_write(ref opb, (uint)ilog2((uint)ci.blocksizes[1]), 4);
            Ogg.oggpack_write(ref opb, 1, 1);

            return(0);
        }
예제 #3
0
        /* helpers */

        static void _v_writestring(ref Ogg.oggpack_buffer o, char[] s, int bytes)
        {
            for (int i = 0; i < bytes; i++)
            {
                Ogg.oggpack_write(ref o, s[i], 8);
            }
        }
예제 #4
0
        /* used to track pcm position without actually performing decode. Useful for sequential 'fast forward' */
        static public int vorbis_synthesis_trackonly(ref vorbis_block vb, ref Ogg.ogg_packet op)
        {
            vorbis_dsp_state vd = vb.vd;
            private_state    b  = vd.backend_state as private_state;
            vorbis_info      vi = vd.vi;
            codec_setup_info ci = vi.codec_setup as codec_setup_info;

            Ogg.oggpack_buffer opb = vb.opb;
            int mode;

            /* first things first.  Make sure decode is ready */
            _vorbis_block_ripcord(ref vb);
            Ogg.oggpack_readinit(ref opb, op.packet, op.bytes);

            /* Check the packet type */
            if (Ogg.oggpack_read(ref opb, 1) != 0)
            {
                /* Oops. This is not an audio data packet */
                return(OV_ENOTAUDIO);
            }

            /* read our mode and pre/post windowsize */
            mode = Ogg.oggpack_read(ref opb, b.modebits);

            if (mode == -1)
            {
                return(OV_EBADPACKET);
            }

            vb.mode = mode;

            vb.W = ci.mode_param[mode].blockflag;
            if (vb.W > 0)
            {
                vb.lW = Ogg.oggpack_read(ref opb, 1);
                vb.nW = Ogg.oggpack_read(ref opb, 1);

                if (vb.nW == -1)
                {
                    return(OV_EBADPACKET);
                }
            }
            else
            {
                vb.lW = 0;
                vb.nW = 0;
            }

            /* more setup */
            vb.granulepos = op.granulepos;
            vb.sequence   = op.packetno;
            vb.eofflag    = op.e_o_s;

            /* no pcm */
            vb.pcmend = 0;
            vb.pcm    = null;

            return(0);
        }
예제 #5
0
        static void mapping0_pack(ref vorbis_info vi, vorbis_info_mapping vm, ref Ogg.oggpack_buffer opb)
        {
            int i;
            vorbis_info_mapping0 info = vm as vorbis_info_mapping0;

            /* another 'we meant to do it this way' hack...  up to beta 4, we packed 4 binary zeros here to signify one submapping in use.  We
             * now redefine that to mean four bitflags that indicate use of deeper features; bit0:submappings, bit1:coupling,
             * bit2,3: reserved. This is backward compatable with all actual uses of the beta code. */

            if (info.submaps > 1)
            {
                Ogg.oggpack_write(ref opb, 1, 1);
                Ogg.oggpack_write(ref opb, (uint)(info.submaps - 1), 4);
            }
            else
            {
                Ogg.oggpack_write(ref opb, 0, 1);
            }

            if (info.coupling_steps > 0)
            {
                Ogg.oggpack_write(ref opb, 1, 1);
                Ogg.oggpack_write(ref opb, (uint)(info.coupling_steps - 1), 8);

                for (i = 0; i < info.coupling_steps; i++)
                {
                    Ogg.oggpack_write(ref opb, (uint)info.coupling_mag[i], ilog((uint)vi.channels));
                    Ogg.oggpack_write(ref opb, (uint)info.coupling_ang[i], ilog((uint)vi.channels));
                }
            }
            else
            {
                Ogg.oggpack_write(ref opb, 0, 1);
            }

            Ogg.oggpack_write(ref opb, 0, 2); /* 2,3:reserved */

            /* we don't write the channel submappings if we only have one... */
            if (info.submaps > 1)
            {
                for (i = 0; i < vi.channels; i++)
                {
                    Ogg.oggpack_write(ref opb, (uint)info.chmuxlist[i], 4);
                }
            }

            for (i = 0; i < info.submaps; i++)
            {
                Ogg.oggpack_write(ref opb, 0, 8); /* time submap unused */
                Ogg.oggpack_write(ref opb, (uint)info.floorsubmap[i], 8);
                Ogg.oggpack_write(ref opb, (uint)info.residuesubmap[i], 8);
            }
        }
예제 #6
0
        /* Is this packet a vorbis ID header? */
        static public int vorbis_synthesis_idheader(ref Ogg.ogg_packet op)
        {
            Ogg.oggpack_buffer opb    = new Ogg.oggpack_buffer();
            char[]             buffer = new char[6];

            if (op == null)
            {
                return(0);
            }

            Ogg.oggpack_readinit(ref opb, op.packet, op.bytes);

            if (op.b_o_s == 0)
            {
                return(0); /* Not the initial packet */
            }

            if (Ogg.oggpack_read(ref opb, 8) != 1)
            {
                return(0); /* Not an ID header */
            }

            _v_readstring(ref opb, ref buffer, 6);

            if
            (
                buffer[0] != 'v' ||
                buffer[1] != 'o' ||
                buffer[2] != 'r' ||
                buffer[3] != 'b' ||
                buffer[4] != 'i' ||
                buffer[5] != 's'
            )
            {
                return(0); /* not vorbis */
            }

            return(1);
        }
예제 #7
0
        static public int vorbis_packet_blocksize(ref vorbis_info vi, ref Ogg.ogg_packet op)
        {
            codec_setup_info ci = vi.codec_setup as codec_setup_info;

            Ogg.oggpack_buffer opb = new Ogg.oggpack_buffer();

            int mode;

            Ogg.oggpack_readinit(ref opb, op.packet, op.bytes);

            /* Check the packet type */
            if (Ogg.oggpack_read(ref opb, 1) != 0)
            {
                /* Oops.  This is not an audio data packet */
                return(OV_ENOTAUDIO);
            }

            {
                int modebits = 0;
                int v        = ci.modes;

                while (v > 1)
                {
                    modebits++;
                    v >>= 1;
                }

                /* read our mode and pre/post windowsize */
                mode = Ogg.oggpack_read(ref opb, modebits);
            }

            if (mode == -1)
            {
                return(OV_EBADPACKET);
            }

            return(ci.blocksizes[ci.mode_param[mode].blockflag]);
        }
예제 #8
0
        static public int vorbis_synthesis(ref vorbis_block vb, ref Ogg.ogg_packet op)
        {
            vorbis_dsp_state vd = (vb != null) ? vb.vd : null;
            private_state    b  = (vd != null) ? (vd.backend_state as private_state) : null;
            vorbis_info      vi = (vd != null) ? vd.vi : null;
            codec_setup_info ci = (vi != null) ? (vi.codec_setup as codec_setup_info) : null;

            Ogg.oggpack_buffer opb = (vb != null) ? vb.opb : null;

            int type, mode, i;

            if (vd == null || b == null || vi == null || ci == null || opb == null)
            {
                return(OV_EBADHEADER);
            }

            /* first things first.  Make sure decode is ready */
            _vorbis_block_ripcord(ref vb);
            Ogg.oggpack_readinit(ref opb, op.packet, op.bytes);

            /* Check the packet type */
            if (Ogg.oggpack_read(ref opb, 1) != 0)
            {
                /* Oops.  This is not an audio data packet */
                return(OV_ENOTAUDIO);
            }

            /* read our mode and pre/post windowsize */
            mode = Ogg.oggpack_read(ref opb, b.modebits);

            if (mode == -1)
            {
                return(OV_EBADPACKET);
            }

            vb.mode = mode;

            if (ci.mode_param[mode] == null)
            {
                return(OV_EBADPACKET);
            }

            vb.W = ci.mode_param[mode].blockflag;

            if (vb.W > 0)
            {
                /* this doesn't get mapped through mode selection as it's used only for window selection */
                vb.lW = Ogg.oggpack_read(ref opb, 1);
                vb.nW = Ogg.oggpack_read(ref opb, 1);

                if (vb.nW == -1)
                {
                    return(OV_EBADPACKET);
                }
            }
            else
            {
                vb.lW = 0;
                vb.nW = 0;
            }

            /* more setup */
            vb.granulepos = op.granulepos;
            vb.sequence   = op.packetno;
            vb.eofflag    = op.e_o_s;

            /* alloc pcm passback storage */
            vb.pcmend = ci.blocksizes[vb.W];
            vb.pcm    = (float **)_vorbis_block_alloc(ref vb, sizeof(float *) * vi.channels);

            for (i = 0; i < vi.channels; i++)
            {
                vb.pcm[i] = (float *)_vorbis_block_alloc(ref vb, sizeof(float) * vb.pcmend);
            }

            /* unpack_header enforces range checking */
            type = ci.map_type[ci.mode_param[mode].mapping];

            return(_mapping_P[type].inverse(ref vb, ci.map_param[ci.mode_param[mode].mapping]));
        }
예제 #9
0
        /* The Vorbis header is in three packets; the initial small packet in the first page that identifies basic parameters, a second packet
         * with bitstream comments and a third packet that holds the codebook. */

        static public int vorbis_synthesis_headerin(ref vorbis_info vi, ref vorbis_comment vc, ref Ogg.ogg_packet op)
        {
            Ogg.oggpack_buffer opb = new Ogg.oggpack_buffer();
            Ogg.oggpack_readinit(ref opb, op.packet, op.bytes);

            /* Which of the three types of header is this? */
            /* Also verify header-ness, vorbis */
            {
                char[] buffer   = new char[6];
                int    packtype = Ogg.oggpack_read(ref opb, 8);

                _v_readstring(ref opb, ref buffer, 6);

                if
                (
                    buffer[0] != 'v' ||
                    buffer[1] != 'o' ||
                    buffer[2] != 'r' ||
                    buffer[3] != 'b' ||
                    buffer[4] != 'i' ||
                    buffer[5] != 's'
                )
                {
                    /* not a vorbis header */
                    return(OV_ENOTVORBIS);
                }

                switch (packtype)
                {
                case 0x01:     /* least significant *bit* is read first */
                {
                    if (op.b_o_s == 0)
                    {
                        /* Not the initial packet */
                        return(OV_EBADHEADER);
                    }

                    if (vi.rate != 0)
                    {
                        /* previously initialized info header */
                        return(OV_EBADHEADER);
                    }
                }
                    return(_vorbis_unpack_info(ref vi, ref opb));

                case 0x03:     /* least significant *bit* is read first */
                {
                    if (vi.rate == 0)
                    {
                        /* um... we didn't get the initial header */
                        return(OV_EBADHEADER);
                    }
                }
                    return(_vorbis_unpack_comment(ref vc, ref opb));

                case 0x05:     /* least significant *bit* is read first */
                {
                    if (vi.rate == 0 || vc.vendor == null)
                    {
                        /* um... we didn;t get the initial header or comments yet */
                        return(OV_EBADHEADER);
                    }
                }
                    return(_vorbis_unpack_books(ref vi, ref opb));

                default:
                {
                    /* Not a valid vorbis header type */
                    return(OV_EBADHEADER);
                }
                }
            }
        }
예제 #10
0
        /* all of the real encoding details are here. The modes, books, everything */
        static public int _vorbis_unpack_books(ref vorbis_info vi, ref Ogg.oggpack_buffer opb)
        {
            codec_setup_info ci = vi.codec_setup as codec_setup_info;

            if (ci == null)
            {
                return(OV_EFAULT);
            }

            /* code books */
            ci.books = Ogg.oggpack_read(ref opb, 8) + 1;

            if (ci.books <= 0)
            {
                goto err_out;
            }

            for (int i = 0; i < ci.books; i++)
            {
                ci.book_param[i] = vorbis_staticbook_unpack(ref opb);

                if (ci.book_param[i] == null)
                {
                    goto err_out;
                }
            }

            /* time backend settings; hooks are unused */
            {
                int times = Ogg.oggpack_read(ref opb, 6) + 1;

                if (times <= 0)
                {
                    goto err_out;
                }

                for (int i = 0; i < times; i++)
                {
                    int test = Ogg.oggpack_read(ref opb, 16);

                    if (test < 0 || test >= VI_TIMEB)
                    {
                        goto err_out;
                    }
                }
            }

            /* floor backend settings */
            ci.floors = Ogg.oggpack_read(ref opb, 6) + 1;

            if (ci.floors <= 0)
            {
                goto err_out;
            }

            for (int i = 0; i < ci.floors; i++)
            {
                ci.floor_type[i] = Ogg.oggpack_read(ref opb, 16);

                if (ci.floor_type[i] < 0 || ci.floor_type[i] >= VI_FLOORB)
                {
                    goto err_out;
                }

                ci.floor_param[i] = _floor_P[ci.floor_type[i]].unpack(ref vi, ref opb);

                if (ci.floor_param[i] == null)
                {
                    goto err_out;
                }
            }

            /* residue backend settings */
            ci.residues = Ogg.oggpack_read(ref opb, 6) + 1;

            if (ci.residues <= 0)
            {
                goto err_out;
            }

            for (int i = 0; i < ci.residues; i++)
            {
                ci.residue_type[i] = Ogg.oggpack_read(ref opb, 16);

                if (ci.residue_type[i] < 0 || ci.residue_type[i] >= VI_RESB)
                {
                    goto err_out;
                }

                ci.residue_param[i] = _residue_P[ci.residue_type[i]].unpack(ref vi, ref opb);

                if (ci.residue_param == null)
                {
                    goto err_out;
                }
            }

            /* map backed settings */
            ci.maps = Ogg.oggpack_read(ref opb, 6) + 1;

            if (ci.maps <= 0)
            {
                goto err_out;
            }

            for (int i = 0; i < ci.maps; i++)
            {
                ci.map_type[i] = Ogg.oggpack_read(ref opb, 16);

                if (ci.map_type[i] < 0 || ci.map_type[i] >= VI_MAPB)
                {
                    goto err_out;
                }

                ci.map_param[i] = _mapping_P[ci.map_type[i]].unpack(ref vi, ref opb);

                if (ci.map_param[i] == null)
                {
                    goto err_out;
                }
            }

            /* mode settings */
            ci.modes = Ogg.oggpack_read(ref opb, 6) + 1;

            if (ci.modes <= 0)
            {
                goto err_out;
            }

            for (int i = 0; i < ci.modes; i++)
            {
                ci.mode_param[i]               = new vorbis_info_mode();
                ci.mode_param[i].blockflag     = Ogg.oggpack_read(ref opb, 1);
                ci.mode_param[i].windowtype    = Ogg.oggpack_read(ref opb, 16);
                ci.mode_param[i].transformtype = Ogg.oggpack_read(ref opb, 16);
                ci.mode_param[i].mapping       = Ogg.oggpack_read(ref opb, 8);

                if (ci.mode_param[i].windowtype >= VI_WINDOWB)
                {
                    goto err_out;
                }

                if (ci.mode_param[i].transformtype >= VI_WINDOWB)
                {
                    goto err_out;
                }

                if (ci.mode_param[i].mapping >= ci.maps)
                {
                    goto err_out;
                }

                if (ci.mode_param[i].mapping < 0)
                {
                    goto err_out;
                }
            }

            if (Ogg.oggpack_read(ref opb, 1) != 1)
            {
                goto err_out;
            }

            return(0);

err_out:
            vorbis_info_clear(ref vi);
            return(OV_EBADHEADER);
        }
예제 #11
0
        static int _vorbis_unpack_comment(ref vorbis_comment vc, ref Ogg.oggpack_buffer opb)
        {
            int i;
            int vendorlen = Ogg.oggpack_read(ref opb, 32);

            if (vendorlen < 0)
            {
                goto err_out;
            }

            if (vendorlen > opb.storage - 8)
            {
                goto err_out;
            }

            vc.vendor = new char[vendorlen + 1];
            _v_readstring(ref opb, ref vc.vendor, vendorlen);

            i = Ogg.oggpack_read(ref opb, 32);

            if (i < 0)
            {
                goto err_out;
            }

            if (i > ((opb.storage - Ogg.oggpack_bytes(ref opb) >> 2)))
            {
                goto err_out;
            }

            vc.comments        = i;
            vc.user_comments   = new char[vc.comments + 1][];
            vc.comment_lengths = new int[vc.comments + 1];

            for (i = 0; i < vc.comments; i++)
            {
                int len = Ogg.oggpack_read(ref opb, 32);

                if (len < 0)
                {
                    goto err_out;
                }

                if (len > opb.storage - Ogg.oggpack_bytes(ref opb))
                {
                    goto err_out;
                }

                vc.comment_lengths[i] = len;
                vc.user_comments[i]   = new char[len + 1];

                _v_readstring(ref opb, ref vc.user_comments[i], len);
            }

            /* EOP check */
            if (Ogg.oggpack_read(ref opb, 1) != 1)
            {
                goto err_out;
            }

            return(0);

err_out:
            vorbis_comment_clear(ref vc);
            return(OV_EBADHEADER);
        }
예제 #12
0
        /* Header packing/unpacking */

        static public int _vorbis_unpack_info(ref vorbis_info vi, ref Ogg.oggpack_buffer opb)
        {
            codec_setup_info ci = vi.codec_setup as codec_setup_info;

            if (ci == null)
            {
                return(OV_EFAULT);
            }

            vi.version = Ogg.oggpack_read(ref opb, 32);

            if (vi.version != 0)
            {
                return(OV_EVERSION);
            }

            vi.channels = Ogg.oggpack_read(ref opb, 8);
            vi.rate     = Ogg.oggpack_read(ref opb, 32);

            vi.bitrate_upper   = Ogg.oggpack_read(ref opb, 32);
            vi.bitrate_nominal = Ogg.oggpack_read(ref opb, 32);
            vi.bitrate_lower   = Ogg.oggpack_read(ref opb, 32);

            ci.blocksizes[0] = 1 << Ogg.oggpack_read(ref opb, 4);
            ci.blocksizes[1] = 1 << Ogg.oggpack_read(ref opb, 4);

            if (vi.rate < 1)
            {
                goto err_out;
            }

            if (vi.channels < 1)
            {
                goto err_out;
            }

            if (ci.blocksizes[0] < 64)
            {
                goto err_out;
            }

            if (ci.blocksizes[1] < ci.blocksizes[0])
            {
                goto err_out;
            }

            if (ci.blocksizes[1] > 8192)
            {
                goto err_out;
            }

            if (Ogg.oggpack_read(ref opb, 1) != 1)
            {
                goto err_out; /* EOP check */
            }

            return(0);

err_out:
            vorbis_info_clear(ref vi);
            return(OV_EBADHEADER);
        }
예제 #13
0
        /* also responsible for range checking */
        static vorbis_info_mapping mapping0_unpack(ref vorbis_info vi, ref Ogg.oggpack_buffer opb)
        {
            int i, b;

            vorbis_info_mapping  _info = new vorbis_info_mapping0();
            vorbis_info_mapping0 info  = _info as vorbis_info_mapping0;
            codec_setup_info     ci    = vi.codec_setup as codec_setup_info;

            b = Ogg.oggpack_read(ref opb, 1);

            if (b < 0)
            {
                goto err_out;
            }

            if (b != 0)
            {
                info.submaps = Ogg.oggpack_read(ref opb, 4) + 1;

                if (info.submaps <= 0)
                {
                    goto err_out;
                }
            }
            else
            {
                info.submaps = 1;
            }

            b = Ogg.oggpack_read(ref opb, 1);

            if (b < 0)
            {
                goto err_out;
            }

            if (b != 0)
            {
                info.coupling_steps = Ogg.oggpack_read(ref opb, 8) + 1;

                if (info.coupling_steps <= 0)
                {
                    goto err_out;
                }

                for (i = 0; i < info.coupling_steps; i++)
                {
                    int testM = info.coupling_mag[i] = Ogg.oggpack_read(ref opb, ilog((uint)vi.channels));
                    int testA = info.coupling_ang[i] = Ogg.oggpack_read(ref opb, ilog((uint)vi.channels));

                    if (testM < 0 || testA < 0 || testM == testA || testM >= vi.channels || testA >= vi.channels)
                    {
                        goto err_out;
                    }
                }
            }

            if (Ogg.oggpack_read(ref opb, 2) != 0) /* 2,3:reserved */
            {
                goto err_out;
            }

            if (info.submaps > 1)
            {
                for (i = 0; i < vi.channels; i++)
                {
                    info.chmuxlist[i] = Ogg.oggpack_read(ref opb, 4);

                    if (info.chmuxlist[i] >= info.submaps || info.chmuxlist[i] < 0)
                    {
                        goto err_out;
                    }
                }
            }

            for (i = 0; i < info.submaps; i++)
            {
                Ogg.oggpack_read(ref opb, 8); /* time submap unused */
                info.floorsubmap[i] = Ogg.oggpack_read(ref opb, 8);

                if (info.floorsubmap[i] >= ci.floors || info.floorsubmap[i] < 0)
                {
                    goto err_out;
                }

                info.residuesubmap[i] = Ogg.oggpack_read(ref opb, 8);

                if (info.residuesubmap[i] >= ci.residues || info.residuesubmap[i] < 0)
                {
                    goto err_out;
                }
            }

            return(info);

err_out:
            mapping0_free_info(ref _info);
            return(null);
        }
예제 #14
0
        static int mapping0_forward(ref vorbis_block vb)
        {
            vorbis_dsp_state vd = vb.vd;
            vorbis_info      vi = vd.vi;

            codec_setup_info      ci  = vi.codec_setup as codec_setup_info;
            private_state         b   = vb.vd.backend_state as private_state;
            vorbis_block_internal vbi = vb._internal as vorbis_block_internal;

            int n = vb.pcmend;
            int i, j, k;

            int *   nonzero     = stackalloc int[vi.channels];
            float **gmdct       = (float **)_vorbis_block_alloc(ref vb, vi.channels * sizeof(float *));
            int **  iwork       = (int **)_vorbis_block_alloc(ref vb, vi.channels * sizeof(int *));
            int *** floor_posts = (int ***)_vorbis_block_alloc(ref vb, vi.channels * sizeof(int **));

            float  global_ampmax = vbi.ampmax;
            float *local_ampmax  = stackalloc float[vi.channels];

            int blocktype  = vbi.blocktype;
            int modenumber = vb.W;

            vorbis_info_mapping0 info     = ci.map_param[modenumber] as vorbis_info_mapping0;
            vorbis_look_psy      psy_look = b.psy[blocktype + (vb.W != 0 ? 2 : 0)];

            vb.mode = modenumber;

            for (i = 0; i < vi.channels; i++)
            {
                float scale = 4.0f / n;
                float scale_dB;

                float *pcm    = vb.pcm[i];
                float *logfft = pcm;

                iwork[i] = (int *)_vorbis_block_alloc(ref vb, (n / 2) * sizeof(int));
                gmdct[i] = (float *)_vorbis_block_alloc(ref vb, (n / 2) * sizeof(float));

                /* + .345 is a hack; the original todB estimation used on IEEE 754 compliant machines had a bug that
                 * returned dB values about a third of a decibel too high.  The bug was harmless because tunings
                 * implicitly took that into account.  However, fixing the bug in the estimator requires changing all the tunings as well.
                 * For now, it's easier to sync things back up here, and recalibrate the tunings in the next major model upgrade. */

                scale_dB = todB(scale) + 0.345f;

                /* window the PCM data */
                _vorbis_apply_window(pcm, ref b.window, ref ci.blocksizes, vb.lW, vb.W, vb.nW);

                /* transform the PCM data */
                /* only MDCT right now.... */
                mdct_forward(b.transform[vb.W][0] as mdct_lookup, pcm, gmdct[i]);

                /* FFT yields more accurate tonal estimation (not phase sensitive) */
                drft_forward(ref b.fft_look[vb.W], pcm);

                /* + .345 is a hack; the original todB estimation used on IEEE 754 compliant machines had a bug that
                 * returned dB values about a third of a decibel too high.  The bug was harmless because tunings
                 * implicitly took that into account.  However, fixing the bug in the estimator requires changing all the tunings as well.
                 * For now, it's easier to sync things back up here, and recalibrate the tunings in the next major model upgrade. */

                logfft[0]       = scale_dB + todB(*pcm) + 0.345f;
                local_ampmax[i] = logfft[0];

                for (j = 1; j < n - 1; j += 2)
                {
                    float temp = pcm[j] * pcm[j] + pcm[j + 1] * pcm[j + 1];

                    /* + .345 is a hack; the original todB estimation used on IEEE 754 compliant machines had a bug that
                     * returned dB values about a third of a decibel too high.  The bug was harmless because tunings
                     * implicitly took that into account.  However, fixing the bug in the estimator requires changing all the tunings as well.
                     * For now, it's easier to sync things back up here, and recalibrate the tunings in the next major model upgrade. */

                    temp = logfft[(j + 1) >> 1] = scale_dB + 0.5f * todB(temp) + 0.345f;

                    if (temp > local_ampmax[i])
                    {
                        local_ampmax[i] = temp;
                    }
                }

                if (local_ampmax[i] > 0.0f)
                {
                    local_ampmax[i] = 0.0f;
                }

                if (local_ampmax[i] > global_ampmax)
                {
                    global_ampmax = local_ampmax[i];
                }
            }

            {
                float *noise = (float *)_vorbis_block_alloc(ref vb, n / 2 * sizeof(float));
                float *tone  = (float *)_vorbis_block_alloc(ref vb, n / 2 * sizeof(float));

                for (i = 0; i < vi.channels; i++)
                {
                    /* the encoder setup assumes that all the modes used by any
                     * specific bitrate tweaking use the same floor */

                    int submap = info.chmuxlist[i];

                    /* the following makes things clearer to *me* anyway */

                    float *mdct   = gmdct[i];
                    float *logfft = vb.pcm[i];

                    float *logmdct = logfft + n / 2;
                    float *logmask = logfft;

                    vb.mode = modenumber;

                    floor_posts[i] = (int **)_vorbis_block_alloc(ref vb, PACKETBLOBS * sizeof(int *));
                    ZeroMemory(floor_posts[i], sizeof(int *) * PACKETBLOBS);

                    for (j = 0; j < n / 2; j++)
                    {
                        /* + .345 is a hack; the original todB estimation used on IEEE 754 compliant machines had a bug that
                         * returned dB values about a third of a decibel too high.  The bug was harmless because tunings
                         * implicitly took that into account.  However, fixing the bug in the estimator requires changing all the tunings as well.
                         * For now, it's easier to sync things back up here, and recalibrate the tunings in the next major model upgrade. */

                        logmdct[j] = todB(mdct[j]) + 0.345f;

                        /* first step; noise masking.  Not only does 'noise masking' give us curves from which we can decide how much resolution
                         * to give noise parts of the spectrum, it also implicitly hands us a tonality estimate (the larger the value in the
                         * 'noise_depth' vector, the more tonal that area is) */

                        _vp_noisemask(ref psy_look, logmdct, noise); /* noise does not have by-frequency offset bias applied yet */

                        /* second step: 'all the other crap'; all the stuff that isn't computed/fit for bitrate management goes in the second psy
                         * vector.  This includes tone masking, peak limiting and ATH */

                        _vp_tonemask(ref psy_look, logfft, tone, global_ampmax, local_ampmax[i]);

                        /* third step; we offset the noise vectors, overlay tone masking.  We then do a floor1-specific line fit.  If we're
                         * performing bitrate management, the line fit is performed multiple times for up/down tweakage on demand. */

                        _vp_offset_and_mix(ref psy_look, noise, tone, 1, logmask, mdct, logmdct);

                        /* this algorithm is hardwired to floor 1 for now; abort out if  we're *not* floor1.  This won't happen unless someone has
                         * broken the encode setup lib.  Guard it anyway. */

                        if (ci.floor_type[info.floorsubmap[submap]] != 1)
                        {
                            return(-1);
                        }

                        floor_posts[i][PACKETBLOBS / 2] = floor1_fit(ref vb, b.flr[info.floorsubmap[submap]] as vorbis_look_floor1, logmdct, logmask);

                        /* are we managing bitrate?  If so, perform two more fits for later rate tweaking (fits represent hi/lo) */
                        if (vorbis_bitrate_managed(ref vb) != 0 && floor_posts[i][PACKETBLOBS / 2] != null)
                        {
                            /* higher rate by way of lower noise curve */
                            _vp_offset_and_mix(ref psy_look, noise, tone, 2, logmask, mdct, logmdct);

                            floor_posts[i][PACKETBLOBS - 1] = floor1_fit(ref vb, b.flr[info.floorsubmap[submap]] as vorbis_look_floor1, logmdct, logmask);

                            /* lower rate by way of higher noise curve */
                            _vp_offset_and_mix(ref psy_look, noise, tone, 0, logmask, mdct, logmdct);

                            floor_posts[i][0] = floor1_fit(ref vb, b.flr[info.floorsubmap[submap]] as vorbis_look_floor1, logmdct, logmask);

                            /* we also interpolate a range of intermediate curves for
                             * intermediate rates */
                            for (k = 1; k < PACKETBLOBS / 2; k++)
                            {
                                floor_posts[i][k] = floor1_interpolate_fit(ref vb, b.flr[info.floorsubmap[submap]] as vorbis_look_floor1, floor_posts[i][0], floor_posts[i][PACKETBLOBS / 2], k * 65536 / (PACKETBLOBS / 2));
                            }

                            for (k = PACKETBLOBS / 2 + 1; k < PACKETBLOBS - 1; k++)
                            {
                                floor_posts[i][k] = floor1_interpolate_fit(ref vb, b.flr[info.floorsubmap[submap]] as vorbis_look_floor1, floor_posts[i][PACKETBLOBS / 2], floor_posts[i][PACKETBLOBS - 1], (k - PACKETBLOBS / 2) * 65536 / (PACKETBLOBS / 2));
                            }
                        }
                    }
                }

                vbi.ampmax = global_ampmax;

                /*
                 * the next phases are performed once for vbr-only and PACKETBLOB
                 * times for bitrate managed modes.
                 *
                 * 1) encode actual mode being used
                 * 2) encode the floor for each channel, compute coded mask curve/res
                 * 3) normalize and couple.
                 * 4) encode residue
                 * 5) save packet bytes to the packetblob vector
                 */

                /* iterate over the many masking curve fits we've created */

                {
                    int **couple_bundle = stackalloc int *[vi.channels];
                    int * zerobundle    = stackalloc int[vi.channels];

                    for (k = (vorbis_bitrate_managed(ref vb) != 0 ? 0 : PACKETBLOBS / 2); k <= (vorbis_bitrate_managed(ref vb) != 0 ? PACKETBLOBS - 1 : PACKETBLOBS / 2); k++)
                    {
                        Ogg.oggpack_buffer opb = vbi.packetblob[k];

                        /* start out our new packet blob with packet type and mode */
                        /* Encode the packet type */
                        Ogg.oggpack_write(ref opb, 0, 1);

                        /* Encode the modenumber */
                        /* Encode frame mode, pre,post windowsize, then dispatch */
                        Ogg.oggpack_write(ref opb, (uint)modenumber, b.modebits);

                        if (vb.W != 0)
                        {
                            Ogg.oggpack_write(ref opb, (uint)vb.lW, 1);
                            Ogg.oggpack_write(ref opb, (uint)vb.nW, 1);
                        }

                        /* encode floor, compute masking curve, sep out residue */
                        for (i = 0; i < vi.channels; i++)
                        {
                            int  submap   = info.chmuxlist[i];
                            int *ilogmask = iwork[i];

                            nonzero[i] = floor1_encode(ref opb, ref vb, b.flr[info.floorsubmap[submap]] as vorbis_look_floor1, floor_posts[i][k], ilogmask);
                        }

                        /* our iteration is now based on masking curve, not prequant and coupling.  Only one prequant/coupling step */

                        /* quantize/couple */
                        /* incomplete implementation that assumes the tree is all depth one, or no tree at all */
                        _vp_couple_quantize_normalize(k, ci.psy_g_param, ref psy_look, info, gmdct, iwork, nonzero, ci.psy_g_param.sliding_lowpass[vb.W, k], vi.channels);

                        /* classify and encode by submap */
                        for (i = 0; i < info.submaps; i++)
                        {
                            int   ch_in_bundle = 0;
                            int **classifications;
                            int   resnum = info.residuesubmap[i];

                            for (j = 0; j < vi.channels; j++)
                            {
                                if (info.chmuxlist[j] == i)
                                {
                                    zerobundle[ch_in_bundle] = 0;

                                    if (nonzero[j] != 0)
                                    {
                                        zerobundle[ch_in_bundle] = 1;
                                    }

                                    couple_bundle[ch_in_bundle++] = iwork[j];
                                }
                            }

                            classifications = _residue_P[ci.residue_type[resnum]]._class(ref vb, b.residue[resnum], couple_bundle, zerobundle, ch_in_bundle);
                            ch_in_bundle    = 0;

                            for (j = 0; j < vi.channels; j++)
                            {
                                if (info.chmuxlist[j] == i)
                                {
                                    couple_bundle[ch_in_bundle++] = iwork[j];
                                }
                            }

                            _residue_P[ci.residue_type[resnum]].forward(ref opb, ref vb, b.residue[resnum], couple_bundle, zerobundle, ch_in_bundle, classifications, i);
                        }

                        /* ok, done encoding.  Next protopacket. */
                    }
                }

                return(0);
            }
        }