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; }
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; }