Esempio n. 1
0
        PacketDecodeInfo UnpackPacket(DataPacket packet)
        {
            // make sure we're on an audio packet
            if (packet.ReadBit())
            {
                // we really can't do anything... count the bits as waste
                return null;
            }

            var pdi = new PacketDecodeInfo();

            // get mode and prev/next flags
            var modeBits = _modeFieldBits;
            try
            {
                pdi.Mode = Modes[(int)packet.ReadBits(_modeFieldBits)];
                if (pdi.Mode.BlockFlag)
                {
                    pdi.PrevFlag = packet.ReadBit();
                    pdi.NextFlag = packet.ReadBit();
                    modeBits += 2;
                }
            }
            catch (EndOfStreamException)
            {
                return null;
            }

            try
            {
                var startBits = packet.BitsRead;

                // read the noise floor data (but don't decode yet)
                pdi.FloorData = ACache.Get<VorbisFloor.PacketData>(_channels);
                var noExecuteChannel = ACache.Get<bool>(_channels);
                for (int i = 0; i < _channels; i++)
                {
                    pdi.FloorData[i] = pdi.Mode.Mapping.ChannelSubmap[i].Floor.UnpackPacket(packet, pdi.Mode.BlockSize);
                    noExecuteChannel[i] = !pdi.FloorData[i].ExecuteChannel;
                }

                // make sure we handle no-energy channels correctly given the couplings...
                foreach (var step in pdi.Mode.Mapping.CouplingSteps)
                {
                    if (pdi.FloorData[step.Angle].ExecuteChannel || pdi.FloorData[step.Magnitude].ExecuteChannel)
                    {
                        pdi.FloorData[step.Angle].ForceEnergy = true;
                        pdi.FloorData[step.Magnitude].ForceEnergy = true;
                    }
                }

                var floorBits = packet.BitsRead - startBits;
                startBits = packet.BitsRead;

                pdi.Residue = ACache.Get<float>(_channels, pdi.Mode.BlockSize);
                foreach (var subMap in pdi.Mode.Mapping.Submaps)
                {
                    for (int j = 0; j < _channels; j++)
                    {
                        if (pdi.Mode.Mapping.ChannelSubmap[j] != subMap)
                        {
                            pdi.FloorData[j].ForceNoEnergy = true;
                        }
                    }

                    var rTemp = subMap.Residue.Decode(packet, noExecuteChannel, _channels, pdi.Mode.BlockSize);
                    for (int c = 0; c < _channels; c++)
                    {
                        var r = pdi.Residue[c];
                        var rt = rTemp[c];
                        for (int i = 0; i < pdi.Mode.BlockSize; i++)
                        {
                            r[i] += rt[i];
                        }
                    }
                    ACache.Return(ref rTemp);
                }
                ACache.Return(ref noExecuteChannel);

                _glueBits += 1;
                _modeBits += modeBits;
                _floorBits += floorBits;
                _resBits += packet.BitsRead - startBits;
                _wasteBits += 8 * packet.Length - packet.BitsRead;

                _packetCount += 1;
            }
            catch (EndOfStreamException)
            {
                ResetDecoder();
                pdi = null;
            }
            catch (InvalidDataException)
            {
                pdi = null;
            }

            return pdi;
        }
Esempio n. 2
0
        int DecodePacket(PacketDecodeInfo pdi)
        {
            var sizeW = pdi.Mode.BlockSize;

            // inverse coupling
            var steps = pdi.Mode.Mapping.CouplingSteps;
            for (int i = steps.Length - 1; i >= 0; i--)
            {
                var magnitude = pdi.Residue[steps[i].Magnitude];
                var angle = pdi.Residue[steps[i].Angle];

                // we only have to do the first half; MDCT ignores the last half
                for (int j = 0; j < sizeW / 2; j++)
                {
                    float newM, newA;

                    if (magnitude[j] > 0)
                    {
                        if (angle[j] > 0)
                        {
                            newM = magnitude[j];
                            newA = magnitude[j] - angle[j];
                        }
                        else
                        {
                            newA = magnitude[j];
                            newM = magnitude[j] + angle[j];
                        }
                    }
                    else
                    {
                        if (angle[j] > 0)
                        {
                            newM = magnitude[j];
                            newA = magnitude[j] + angle[j];
                        }
                        else
                        {
                            newA = magnitude[j];
                            newM = magnitude[j] - angle[j];
                        }
                    }

                    magnitude[j] = newM;
                    angle[j] = newA;
                }
            }

            // apply floor / dot product / MDCT (only run if we have sound energy in that channel)
            var pcm = ACache.Get<float[]>(_channels);
            for (int c = 0; c < _channels; c++)
            {
                var floorData = pdi.FloorData[c];
                var res = pdi.Residue[c];
                if (floorData.ExecuteChannel)
                {
                    pdi.Mode.Mapping.ChannelSubmap[c].Floor.Apply(floorData, res);
                    pcm[c] = Mdct.Reverse(res);
                }
                else
                {
                    // Mdct.Reverse does an in-place transform, then returns the input buffer... mimic that
                    pcm[c] = res;
                }
            }

            // window
            var window = pdi.Mode.GetWindow(pdi.PrevFlag, pdi.NextFlag);
            // this is applied as part of the lapping operation

            // now lap the data into the buffer...

            // var sizeW = pdi.Mode.BlockSize
            var right = sizeW;
            var center = right >> 1;
            var left = 0;
            var begin = -center;
            var end = center;

            if (pdi.Mode.BlockFlag)
            {
                // if the flag is true, it's a long block
                // if the flag is false, it's a short block
                if (!pdi.PrevFlag)
                {
                    // previous block was short
                    left = Block1Size / 4 - Block0Size / 4;  // where to start in pcm[][]
                    center = left + Block0Size / 2;     // adjust the center so we're correctly clearing the buffer...
                    begin = Block0Size / -2 - left;     // where to start in _outputBuffer[,]
                }

                if (!pdi.NextFlag)
                {
                    // next block is short
                    right -= sizeW / 4 - Block0Size / 4;
                    end = sizeW / 4 + Block0Size / 4;
                }
            }
            // short blocks don't need any adjustments

            var lastLength = _outputBuffer.Length / _channels;
            for (var c = 0; c < _channels; c++)
            {
                var pcmChan = pcm[c];
                int i = left, idx = lastLength + begin;
                for (; i < center; i++)
                {
                    // add the new windowed value to the appropriate buffer index.  clamp to range -1 to 1 and set _clipped appropriately
                    _outputBuffer[c, idx + i] = Utils.ClipValue(_outputBuffer[c, idx + i] + pcmChan[i] * window[i], ref _clipped);
                }
                for (; i < right; i++)
                {
                    _outputBuffer[c, idx + i] = pcmChan[i] * window[i];
                }
            }

            var newPrepLen = _outputBuffer.Length / _channels - end;
            var samplesDecoded = newPrepLen - _preparedLength;
            _preparedLength = newPrepLen;

            return samplesDecoded;
        }