/// <summary>
        /// Writes a buffer of PCM audio samples to the encoder and packetizer. Runs Opus encoding and potentially outputs one or more pages to the underlying Ogg stream.
        /// You can write any non-zero number of samples that you want here; there are no restrictions on length or packet boundaries
        /// </summary>
        /// <param name="data">The audio samples to write. If stereo, this will be interleaved</param>
        /// <param name="offset">The offset to use when reading data</param>
        /// <param name="count">The amount of PCM data to write</param>
        public void WriteSamples(short[] data, int offset, int count)
        {
            if (_finalized)
            {
                throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!");
            }

            if ((data.Length - offset) < count)
            {
                // Check that caller isn't lying about its buffer sizes
                throw new ArgumentOutOfRangeException("The given audio buffer claims to have " + count + " samples, but it actually only has " + (data.Length - offset));
            }

            // Try and fill the opus frame
            // input cursor in RAW SAMPLES
            int inputCursor = 0;
            // output cursor in RAW SAMPLES
            int amountToWrite = Math.Min(_opusFrame.Length - _opusFrameIndex, count - inputCursor);

            while (amountToWrite > 0)
            {
                // Do we need to resample?
                if (_inputSampleRate != _encoderSampleRate)
                {
                    // Resample the input
                    // divide by channel count because these numbers are in samples-per-channel
                    int in_len  = (count - inputCursor) / _inputChannels;
                    int out_len = amountToWrite / _inputChannels;
                    _resampler.ProcessInterleaved(data, offset + inputCursor, ref in_len, _opusFrame, _opusFrameIndex, ref out_len);
                    inputCursor     += in_len * _inputChannels;
                    _opusFrameIndex += out_len * _inputChannels;
                }
                else
                {
                    // If no resampling, we can do a straight memcpy instead
                    Array.Copy(data, offset + inputCursor, _opusFrame, _opusFrameIndex, amountToWrite);
                    _opusFrameIndex += amountToWrite;
                    inputCursor     += amountToWrite;
                }


                if (_opusFrameIndex == _opusFrame.Length)
                {
                    int maxBlockSize = 0x1000;
                    if (_outputStream.Position < 0x1000)
                    {
                        maxBlockSize = 0x1000 - 0x200;
                    }

                    int pageFree           = maxBlockSize - _payloadIndex - _headerIndex;
                    int maxPayloadThisPage = (pageFree / 256) * 255 + (pageFree % 256) - 1;
                    int reconstructed      = (maxPayloadThisPage / 255) + 1 + maxPayloadThisPage;

                    /* when due to segment sizes we would end up with a 1 byte gap, make sure that the next run will have at least 64 byte.
                     * reason why this could happen is that "adding one byte" would require one segment more and thus occupies two byte more.
                     * if this would happen, just reduce the calculated free space such that there is room for another segment.
                     */
                    if (pageFree != reconstructed && maxPayloadThisPage > 64)
                    {
                        maxPayloadThisPage -= 64;
                    }
                    int lacingSpace = (255 - _lacingTableCount) * 255;
                    int maxOpusData = Math.Min(lacingSpace, maxPayloadThisPage);
                    int packetSize  = _encoder.Encode(_opusFrame, 0, _opusFrameSamples, _currentPayload, _payloadIndex, maxOpusData);

                    maxOpusData -= packetSize;

                    /* pad to full remaining size if we would leave just a few bytes behind. picked a random number that seemed enough for opus */
                    if (maxOpusData > 0 && maxOpusData < 43)
                    {
                        if (OpusRepacketizer.PadPacket(_currentPayload, _payloadIndex, packetSize, packetSize + maxOpusData) != Enums.OpusError.OPUS_OK)
                        {
                            Console.WriteLine("[ERROR] Padding failed at offset 0x" + _outputStream.Position.ToString("X8"));
                            throw new PaddingException("Failed to pad packet");
                        }
                        else
                        {
                            packetSize += maxOpusData;
                        }
                    }

                    _payloadIndex += packetSize;

                    // Opus granules are measured in 48Khz samples.
                    // Since the framesize is fixed (20ms) and the sample rate doesn't change, this is basically a constant value
                    _granulePosition += FRAME_SIZE_MS * 48;

                    // And update the lacing values in the header
                    int segmentLength = packetSize;
                    while (segmentLength >= 255)
                    {
                        segmentLength -= 255;
                        _currentHeader[_headerIndex++] = 0xFF;
                        _lacingTableCount++;
                    }

                    _currentHeader[_headerIndex++] = (byte)segmentLength;
                    _lacingTableCount++;

                    int remain = (maxBlockSize - _payloadIndex - _headerIndex);
                    /* check if there would be enough space for lacing and payload each */
                    if (_lacingTableCount >= 248 || remain < 16)
                    {
                        if (remain > 0)
                        {
                            Console.WriteLine("[ERROR] Ogg Page with just a few bytes remaining. Weird bit rate chosen?");
                            Console.WriteLine("[ERROR]   Offset 0x" + _outputStream.Position.ToString("X8"));
                            throw new Exception("Failed to finalize page");
                        }

                        FinalizePage();
                    }

                    _opusFrameIndex = 0;
                }

                amountToWrite = Math.Min(_opusFrame.Length - _opusFrameIndex, count - inputCursor);
            }
        }
        /// <summary>
        /// Writes a buffer of PCM audio samples to the encoder and packetizer. Runs Opus encoding and potentially outputs one or more pages to the underlying Ogg stream.
        /// You can write any non-zero number of samples that you want here; there are no restrictions on length or packet boundaries
        /// </summary>
        /// <param name="data">The audio samples to write. If stereo, this will be interleaved</param>
        /// <param name="offset">The offset to use when reading data</param>
        /// <param name="count">The amount of PCM data to write</param>
        public void WriteSamples(short[] data, int offset, int count)
        {
            if (_finalized)
            {
                throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!");
            }

            if ((data.Length - offset) < count)
            {
                // Check that caller isn't lying about its buffer sizes
                throw new ArgumentOutOfRangeException("The given audio buffer claims to have " + count + " samples, but it actually only has " + (data.Length - offset));
            }

            // Try and fill the opus frame
            // input cursor in RAW SAMPLES
            int inputCursor = 0;
            // output cursor in RAW SAMPLES
            int amountToWrite = Math.Min(_opusFrame.Length - _opusFrameIndex, count - inputCursor);

            while (amountToWrite > 0)
            {
                // Do we need to resample?
                if (_inputSampleRate != _encoderSampleRate)
                {
                    // Resample the input
                    // divide by channel count because these numbers are in samples-per-channel
                    int in_len  = (count - inputCursor) / _inputChannels;
                    int out_len = amountToWrite / _inputChannels;
                    _resampler.ProcessInterleaved(data, offset + inputCursor, ref in_len, _opusFrame, _opusFrameIndex, ref out_len);
                    inputCursor     += in_len * _inputChannels;
                    _opusFrameIndex += out_len * _inputChannels;
                }
                else
                {
                    // If no resampling, we can do a straight memcpy instead
                    Array.Copy(data, offset + inputCursor, _opusFrame, _opusFrameIndex, amountToWrite);
                    _opusFrameIndex += amountToWrite;
                    inputCursor     += amountToWrite;
                }

                if (_opusFrameIndex == _opusFrame.Length)
                {
                    // Frame is finished. Encode it
                    int packetSize = _encoder.Encode(_opusFrame, 0, _opusFrameSamples, _currentPayload, _payloadIndex, _currentPayload.Length - _payloadIndex);
                    _payloadIndex += packetSize;

                    // Opus granules are measured in 48Khz samples.
                    // Since the framesize is fixed (20ms) and the sample rate doesn't change, this is basically a constant value
                    _granulePosition += FRAME_SIZE_MS * 48;

                    // And update the lacing values in the header
                    int segmentLength = packetSize;
                    while (segmentLength >= 255)
                    {
                        segmentLength -= 255;
                        _currentHeader[_headerIndex++] = 0xFF;
                        _lacingTableCount++;
                    }
                    _currentHeader[_headerIndex++] = (byte)segmentLength;
                    _lacingTableCount++;

                    // And finalize the page if we need
                    // 284 is a magic number meaning "our page is almost full so just finalize it"
                    // A more proper implementation would have the packets span to the next page, but meh
                    if (_lacingTableCount > 248)
                    {
                        FinalizePage();
                    }

                    _opusFrameIndex = 0;
                }

                amountToWrite = Math.Min(_opusFrame.Length - _opusFrameIndex, count - inputCursor);
            }
        }