예제 #1
0
        internal void set_frame_pinfo(LameGlobalFlags gfp, III_psy_ratio[][] ratio)
        {
            var gfc = gfp.internal_flags;

            gfc.masking_lower = 1.0f;
            for (var gr = 0; gr < gfc.mode_gr; gr++)
            {
                for (var ch = 0; ch < gfc.channels_out; ch++)
                {
                    var cod_info     = gfc.l3_side.tt[gr][ch];
                    var scalefac_sav = new int[L3Side.SFBMAX];
                    Array.Copy(cod_info.scalefac, 0, scalefac_sav, 0, scalefac_sav.Length);
                    if (gr == 1)
                    {
                        int sfb;
                        for (sfb = 0; sfb < cod_info.sfb_lmax; sfb++)
                        {
                            if (cod_info.scalefac[sfb] < 0)
                            {
                                cod_info.scalefac[sfb] = gfc.l3_side.tt[0][ch].scalefac[sfb];
                            }
                        }
                    }

                    set_pinfo(gfp, cod_info, ratio[gr][ch], gr, ch);
                    Array.Copy(scalefac_sav, 0, cod_info.scalefac, 0, scalefac_sav.Length);
                }
            }
        }
예제 #2
0
        internal virtual int read_samples_mp3(LameGlobalFlags gfp, FileStream musicin, short[][] mpg123pcm)
        {
            int @out;

            @out = lame_decode_fromfile(musicin, mpg123pcm[0], mpg123pcm[1], parse.mp3input_data);
            if (@out < 0)
            {
                Arrays.Fill(mpg123pcm[0], (short)0);
                Arrays.Fill(mpg123pcm[1], (short)0);
                return(0);
            }

            if (gfp.num_channels != parse.mp3input_data.stereo)
            {
                if (parse.silent < 10)
                {
                    Console.WriteLine("Error: number of channels has changed in %s - not supported\n", type_name);
                }

                @out = -1;
            }

            if (gfp.in_samplerate != parse.mp3input_data.samplerate)
            {
                if (parse.silent < 10)
                {
                    Console.WriteLine("Error: sample frequency has changed in %s - not supported\n", type_name);
                }

                @out = -1;
            }

            return(@out);
        }
예제 #3
0
        } // main_data

        /*
         * compute the number of bits required to flush all mp3 frames currently in
         * the buffer. This should be the same as the reservoir size. Only call this
         * routine between frames - i.e. only after all headers and data have been
         * added to the buffer by format_bitstream().
         *
         * Also compute total_bits_output = size of mp3 buffer (including frame
         * headers which may not have yet been send to the mp3 buffer) + number of
         * bits needed to flush all mp3 frames.
         *
         * total_bytes_output is the size of the mp3 output buffer if
         * lame_encode_flush_nogap() was called right now.
         */

        private int compute_flushbits(LameGlobalFlags gfp, TotalBytes total_bytes_output)
        {
            var gfc = gfp.internal_flags;
            int flushbits, remaining_headers;
            int bitsPerFrame;
            int last_ptr, first_ptr;

            first_ptr = gfc.w_ptr;
            /* first header to add to bitstream */
            last_ptr = gfc.h_ptr - 1;
            /* last header to add to bitstream */
            if (last_ptr == -1)
            {
                last_ptr = LameInternalFlags.MAX_HEADER_BUF - 1;
            }

            /* add this many bits to bitstream so we can flush all headers */
            flushbits = gfc.header[last_ptr].write_timing - totbit;
            total_bytes_output.total = flushbits;

            if (flushbits >= 0)
            {
                /* if flushbits >= 0, some headers have not yet been written */
                /* reduce flushbits by the size of the headers */
                remaining_headers = 1 + last_ptr - first_ptr;
                if (last_ptr < first_ptr)
                {
                    remaining_headers = 1 + last_ptr - first_ptr + LameInternalFlags.MAX_HEADER_BUF;
                }

                flushbits -= remaining_headers * 8 * gfc.sideinfo_len;
            }

            /*
             * finally, add some bits so that the last frame is complete these bits
             * are not necessary to decode the last frame, but some decoders will
             * ignore last frame if these bits are missing
             */
            bitsPerFrame              = getframebits(gfp);
            flushbits                += bitsPerFrame;
            total_bytes_output.total += bitsPerFrame;
            /* round up: */
            if (total_bytes_output.total % 8 != 0)
            {
                total_bytes_output.total = 1 + total_bytes_output.total / 8;
            }
            else
            {
                total_bytes_output.total = total_bytes_output.total / 8;
            }

            total_bytes_output.total += bufByteIdx + 1;

            if (flushbits < 0)
            {
                Console.Error.WriteLine("strange error flushing buffer ... \n");
            }

            return(flushbits);
        }
예제 #4
0
        private int id3tag_set_textinfo_latin1(LameGlobalFlags gfp, string id, string text)
        {
            long t_mask   = FRAME_ID('T', (char)0, (char)0, (char)0);
            var  frame_id = toID3v2TagId(id);

            if (frame_id == 0)
            {
                return(-1);
            }

            if ((frame_id & t_mask) == t_mask)
            {
                if (ReferenceEquals(text, null))
                {
                    return(0);
                }

                if (gfp != null)
                {
                    id3v2_add_latin1(gfp, frame_id, null, null, text);
                    return(0);
                }
            }

            return(-255);
        }
예제 #5
0
        public int seek(LameGlobalFlags gfp)
        {
            int samples_read;
            int framesize;
            var num_channels    = gfp.num_channels;
            int samples_to_read = framesize = gfp.framesize;

            if (is_mpeg_file_format(parse.input_format))
            {
                if (first)
                {
                    first            = false;
                    offset           = (int)(musicin.Position - hip.bsize - hip.tail.pos);
                    FirstFrameOffset = offset;
                }

                samples_read = lame_decode_fromfile(musicin, null, null, parse.mp3input_data, true);

                LastHeaderPosition = hip.lastHeader + FirstFrameOffset;

                if (samples_read < 0)
                {
                    return(samples_read);
                }
            }
            else
            {
                // TODO: Not supported
                throw new Exception("PCM Seek not supported");

                samples_read = 0;//ead_samples_pcm(musicin, insamp, num_channels * samples_to_read);
            }

            return(samples_read);
        }
예제 #6
0
        internal void id3tag_v2_only(LameGlobalFlags gfp)
        {
            var gfc = gfp.internal_flags;

            gfc.tag_spec.flags &= ~V1_ONLY_FLAG;
            gfc.tag_spec.flags |= V2_ONLY_FLAG;
        }
예제 #7
0
        private sound_file_format parse_file_header(LameGlobalFlags gfp, Stream sf)
        {
            var type = Read32BitsHighLow(sf);

            count_samples_carefully = false;
            pcm_is_unsigned_8bit    = !parse.in_signed;
            if (type == WAV_ID_RIFF)
            {
                var ret = parse_wave_header(gfp, sf);
                if (ret > 0)
                {
                    count_samples_carefully = true;
                    return(sound_file_format.sf_wave);
                }
            }
            else if (type == IFF_ID_FORM)
            {
                var ret = parse_aiff_header(gfp, sf);
                if (ret > 0)
                {
                    count_samples_carefully = true;
                    return(sound_file_format.sf_aiff);
                }
            }
            else
            {
            }

            return(sound_file_format.sf_unknown);
        }
예제 #8
0
        internal void id3tag_space_v1(LameGlobalFlags gfp)
        {
            var gfc = gfp.internal_flags;

            gfc.tag_spec.flags &= ~V2_ONLY_FLAG;
            gfc.tag_spec.flags |= SPACE_V1_FLAG;
        }
예제 #9
0
        internal void id3tag_add_v2(LameGlobalFlags gfp)
        {
            var gfc = gfp.internal_flags;

            gfc.tag_spec.flags &= ~V1_ONLY_FLAG;
            gfc.tag_spec.flags |= ADD_V2_FLAG;
        }
예제 #10
0
        internal void id3tag_v1_only(LameGlobalFlags gfp)
        {
            var gfc = gfp.internal_flags;

            gfc.tag_spec.flags &= ~(ADD_V2_FLAG | V2_ONLY_FLAG);
            gfc.tag_spec.flags |= V1_ONLY_FLAG;
        }
예제 #11
0
        private void id3v2AddAudioDuration(LameGlobalFlags gfp)
        {
            if (gfp.num_samples != -1)
            {
                string buffer;
                double max_ulong = int.MaxValue;
                double ms        = gfp.num_samples;
                long   playlength_ms;
                ms *= 1000;
                ms /= gfp.in_samplerate;
                if (ms > int.MaxValue)
                {
                    playlength_ms = (long)max_ulong;
                }
                else if (ms < 0)
                {
                    playlength_ms = 0;
                }
                else
                {
                    playlength_ms = (long)ms;
                }

                buffer = string.Format("{0:D}", playlength_ms);
                copyV1ToV2(gfp, ID_PLAYLENGTH, buffer);
            }
        }
예제 #12
0
        internal void id3tag_set_year(LameGlobalFlags gfp, string year)
        {
            var gfc = gfp.internal_flags;

            if (!ReferenceEquals(year, null) && year.Length != 0)
            {
                var num = Convert.ToInt32(year);
                if (num < 0)
                {
                    num = 0;
                }

                if (num > 9999)
                {
                    num = 9999;
                }

                if (num != 0)
                {
                    gfc.tag_spec.year   = num;
                    gfc.tag_spec.flags |= CHANGED_FLAG;
                }

                copyV1ToV2(gfp, ID_YEAR, year);
            }
        }
예제 #13
0
        internal int id3tag_write_v2(LameGlobalFlags gfp)
        {
            var gfc = gfp.internal_flags;

            if ((gfc.tag_spec.flags & CHANGED_FLAG) != 0 && 0 == (gfc.tag_spec.flags & V1_ONLY_FLAG))
            {
                sbyte[] tag = null;
                int     tag_size, n;
                n        = lame_get_id3v2_tag(gfp, null, 0);
                tag      = new sbyte[n];
                tag_size = lame_get_id3v2_tag(gfp, tag, n);
                if (tag_size > n)
                {
                    return(-1);
                }
                for (var i = 0; i < tag_size; ++i)
                {
                    bits.add_dummy_byte(gfp, tag[i] & 0xff, 1);
                }

                return(tag_size);
            }

            return(0);
        }
예제 #14
0
        internal int id3tag_set_fieldvalue(LameGlobalFlags gfp, string fieldvalue)
        {
            var gfc = gfp.internal_flags;

            if (!ReferenceEquals(fieldvalue, null) && fieldvalue.Length != 0)
            {
                var frame_id = toID3v2TagId(fieldvalue);
                if (fieldvalue.Length < 5 || fieldvalue[4] != '=')
                {
                    return(-1);
                }

                if (frame_id != 0)
                {
                    if (id3tag_set_textinfo_latin1(gfp, fieldvalue, fieldvalue.Substring(5)) != 0)
                    {
                        gfc.tag_spec.values.Add(fieldvalue);
                        gfc.tag_spec.num_values++;
                    }
                }

                gfc.tag_spec.flags |= CHANGED_FLAG;
            }

            id3tag_add_v2(gfp);
            return(0);
        }
예제 #15
0
        private void copyV1ToV2(LameGlobalFlags gfp, int frame_id, string s)
        {
            var gfc   = gfp.internal_flags;
            var flags = gfc.tag_spec.flags;

            id3v2_add_latin1(gfp, frame_id, null, null, s);
            gfc.tag_spec.flags = flags;
        }
예제 #16
0
        private float ATHmdct(LameGlobalFlags gfp, float f)
        {
            var ath = psy.ATHformula(f, gfp);

            ath -= NSATHSCALE;
            ath  = (float)Math.Pow(10.0, ath / 10.0 + gfp.ATHlower);
            return(ath);
        }
예제 #17
0
        private Stream OpenSndFile(LameGlobalFlags gfp, Stream mp3Stream, Enc enc)
        {
            gfp.num_samples = -1;
            musicin         = mp3Stream;

            if (is_mpeg_file_format(parse.input_format))
            {
                if (-1 == lame_decode_initfile(musicin, parse.mp3input_data, enc))
                {
                    throw new Exception(string.Format("Error reading headers in mp3 input file {0}.", mp3Stream));
                }

                gfp.num_channels  = parse.mp3input_data.stereo;
                gfp.in_samplerate = parse.mp3input_data.samplerate;
                gfp.num_samples   = parse.mp3input_data.nsamp;
            }
            else if (parse.input_format == sound_file_format.sf_ogg)
            {
                throw new Exception("sorry, vorbis support in LAME is deprecated.");
            }
            else if (parse.input_format == sound_file_format.sf_raw)
            {
                pcmswapbytes = parse.swapbytes;
            }
            else
            {
                parse.input_format = parse_file_header(gfp, musicin);
            }

            if (parse.input_format == sound_file_format.sf_unknown)
            {
                throw new Exception("Unknown sound format!");
            }

            if (gfp.num_samples == -1)
            {
                double flen = musicin.Length;
                if (flen >= 0)
                {
                    if (is_mpeg_file_format(parse.input_format))
                    {
                        if (parse.mp3input_data.bitrate > 0)
                        {
                            var totalseconds    = flen * 8.0 / (1000.0 * parse.mp3input_data.bitrate);
                            var tmp_num_samples = (int)(totalseconds * gfp.in_samplerate);
                            gfp.num_samples           = tmp_num_samples;
                            parse.mp3input_data.nsamp = tmp_num_samples;
                        }
                    }
                    else
                    {
                        gfp.num_samples = (int)(flen / (2 * gfp.num_channels));
                    }
                }
            }

            return(musicin);
        }
예제 #18
0
        internal void id3tag_init(LameGlobalFlags gfp)
        {
            var gfc = gfp.internal_flags;

            gfc.tag_spec              = new ID3TagSpec();
            gfc.tag_spec.genre_id3v1  = GENRE_NUM_UNKNOWN;
            gfc.tag_spec.padding_size = 128;
            id3v2AddLameVersion(gfp);
        }
예제 #19
0
        internal void id3tag_set_pad(LameGlobalFlags gfp, int n)
        {
            var gfc = gfp.internal_flags;

            gfc.tag_spec.flags       &= ~V1_ONLY_FLAG;
            gfc.tag_spec.flags       |= PAD_V2_FLAG;
            gfc.tag_spec.flags       |= ADD_V2_FLAG;
            gfc.tag_spec.padding_size = n;
        }
예제 #20
0
        private bool balance_noise(LameGlobalFlags gfp, GrInfo cod_info, float[] distort, float[] xrpow, bool bRefine)
        {
            var gfc = gfp.internal_flags;

            amp_scalefac_bands(gfp, cod_info, distort, xrpow, bRefine);
            var status = loop_break(cod_info);

            if (status)
            {
                return(false);
            }

            if (gfc.mode_gr == 2)
            {
                status = tk.scale_bitcount(cod_info);
            }
            else
            {
                status = tk.scale_bitcount_lsf(gfc, cod_info);
            }

            if (!status)
            {
                return(true);
            }

            if (gfc.noise_shaping > 1)
            {
                Arrays.Fill(gfc.pseudohalf, 0);
                if (0 == cod_info.scalefac_scale)
                {
                    inc_scalefac_scale(cod_info, xrpow);
                    status = false;
                }
                else
                {
                    if (cod_info.block_type == Encoder.SHORT_TYPE && gfc.subblock_gain > 0)
                    {
                        status = inc_subblock_gain(gfc, cod_info, xrpow) || loop_break(cod_info);
                    }
                }
            }

            if (!status)
            {
                if (gfc.mode_gr == 2)
                {
                    status = tk.scale_bitcount(cod_info);
                }
                else
                {
                    status = tk.scale_bitcount_lsf(gfc, cod_info);
                }
            }

            return(!status);
        }
예제 #21
0
 internal void init_infile(LameGlobalFlags gfp, Stream mp3Stream, Enc enc)
 {
     count_samples_carefully = false;
     num_samples_read        = 0;
     pcmbitwidth             = parse.in_bitwidth;
     pcmswapbytes            = parse.swapbytes;
     pcm_is_unsigned_8bit    = !parse.in_signed;
     musicin = OpenSndFile(gfp, mp3Stream, enc);
 }
예제 #22
0
        /// <summary>
        ///     Add VBR entry, used to fill the VBR TOC entries.
        /// </summary>
        /// <param name="gfp">
        ///     global flags
        /// </param>
        internal void addVbrFrame(LameGlobalFlags gfp)
        {
            var gfc = gfp.internal_flags;

            var kbps = Tables.bitrate_table[gfp.version][gfc.bitrate_index];

            Debug.Assert(gfc.VBR_seek_table.bag != null);
            addVbr(gfc.VBR_seek_table, kbps);
        }
예제 #23
0
        internal int id3tag_set_comment(LameGlobalFlags gfp, string lang, string desc, string text, int textPos)
        {
            if (gfp != null)
            {
                id3v2_add_latin1(gfp, ID_COMMENT, lang, desc, text);
                return(0);
            }

            return(-255);
        }
예제 #24
0
        internal bool id3tag_set_albumart(LameGlobalFlags gfp, byte[] image, int size)
        {
            var mimetype = MimeType.MIMETYPE_NONE;
            var data     = image;
            var gfc      = gfp.internal_flags;

            if (Lame.LAME_MAXALBUMART < size)
            {
                return(false);
            }

            if (2 < size && data[0] == unchecked ((sbyte)0xFF) && data[1] == unchecked ((sbyte)0xD8))
            {
                mimetype = MimeType.MIMETYPE_JPEG;
            }
            else if (4 < size && data[0] == unchecked ((sbyte)0x89) &&
                     Encoding.GetEncoding(ASCII).GetString(data, 1, 3).StartsWith("PNG", StringComparison.Ordinal))
            {
                mimetype = MimeType.MIMETYPE_PNG;
            }
            else if (4 < size && Encoding.GetEncoding(ASCII).GetString(data, 1, 3).StartsWith(
                         "GIF8",
                         StringComparison.Ordinal))
            {
                mimetype = MimeType.MIMETYPE_GIF;
            }
            else
            {
                return(false);
            }

            if (gfc.tag_spec.albumart != null)
            {
                gfc.tag_spec.albumart          = null;
                gfc.tag_spec.albumart_size     = 0;
                gfc.tag_spec.albumart_mimetype = MimeType.MIMETYPE_NONE;
            }

            if (size < 1)
            {
                return(true);
            }

            gfc.tag_spec.albumart = new sbyte[size];
            if (gfc.tag_spec.albumart != null)
            {
                Array.Copy(image, 0, gfc.tag_spec.albumart, 0, size);
                gfc.tag_spec.albumart_size     = size;
                gfc.tag_spec.albumart_mimetype = mimetype;
                gfc.tag_spec.flags            |= CHANGED_FLAG;
                id3tag_add_v2(gfp);
            }

            return(true);
        }
예제 #25
0
        private void lame_encode_frame_init(LameGlobalFlags gfp, float[][] inbuf)
        {
            var gfc = gfp.internal_flags;

            int ch, gr;

            if (gfc.lame_encode_frame_init == 0)
            {
                /* prime the MDCT/polyphase filterbank with a short block */
                int i, j;
                var primebuff0 = new float[286 + 1152 + 576];
                var primebuff1 = new float[286 + 1152 + 576];
                gfc.lame_encode_frame_init = 1;
                for (i = 0, j = 0; i < 286 + 576 * (1 + gfc.mode_gr); ++i)
                {
                    if (i < 576 * gfc.mode_gr)
                    {
                        primebuff0[i] = 0;
                        if (gfc.channels_out == 2)
                        {
                            primebuff1[i] = 0;
                        }
                    }
                    else
                    {
                        primebuff0[i] = inbuf[0][j];
                        if (gfc.channels_out == 2)
                        {
                            primebuff1[i] = inbuf[1][j];
                        }

                        ++j;
                    }
                }

                /* polyphase filtering / mdct */
                for (gr = 0; gr < gfc.mode_gr; gr++)
                {
                    for (ch = 0; ch < gfc.channels_out; ch++)
                    {
                        gfc.l3_side.tt[gr][ch].block_type = SHORT_TYPE;
                    }
                }

                newMDCT.mdct_sub48(gfc, primebuff0, primebuff1);

                /* check FFT will not use a negative starting offset */
                Debug.Assert(576 >= FFTOFFSET);
                /* check if we have enough data for FFT */
                Debug.Assert(gfc.mf_size >= BLKSIZE + gfp.framesize - FFTOFFSET);
                /* check if we have enough data for polyphase filterbank */
                Debug.Assert(gfc.mf_size >= 512 + gfp.framesize - 32);
            }
        }
예제 #26
0
        internal void id3tag_set_title(LameGlobalFlags gfp, string title)
        {
            var gfc = gfp.internal_flags;

            if (!ReferenceEquals(title, null) && title.Length != 0)
            {
                gfc.tag_spec.title  = title;
                gfc.tag_spec.flags |= CHANGED_FLAG;
                copyV1ToV2(gfp, ID_TITLE, title);
            }
        }
예제 #27
0
        internal void id3tag_set_artist(LameGlobalFlags gfp, string artist)
        {
            var gfc = gfp.internal_flags;

            if (!ReferenceEquals(artist, null) && artist.Length != 0)
            {
                gfc.tag_spec.artist = artist;
                gfc.tag_spec.flags |= CHANGED_FLAG;
                copyV1ToV2(gfp, ID_ARTIST, artist);
            }
        }
예제 #28
0
        internal void id3tag_set_album(LameGlobalFlags gfp, string album)
        {
            var gfc = gfp.internal_flags;

            if (!ReferenceEquals(album, null) && album.Length != 0)
            {
                gfc.tag_spec.album  = album;
                gfc.tag_spec.flags |= CHANGED_FLAG;
                copyV1ToV2(gfp, ID_ALBUM, album);
            }
        }
예제 #29
0
        internal int lame_get_id3v1_tag(LameGlobalFlags gfp, sbyte[] buffer, int size)
        {
            var tag_size = 128;
            LameInternalFlags gfc;

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

            if (size < tag_size)
            {
                return(tag_size);
            }

            gfc = gfp.internal_flags;
            if (gfc == null)
            {
                return(0);
            }

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

            if ((gfc.tag_spec.flags & CHANGED_FLAG) != 0 && 0 == (gfc.tag_spec.flags & V2_ONLY_FLAG))
            {
                var    p   = 0;
                var    pad = (gfc.tag_spec.flags & SPACE_V1_FLAG) != 0 ? ' ' : 0;
                string year;
                buffer[p++] = (sbyte)'T';
                buffer[p++] = (sbyte)'A';
                buffer[p++] = (sbyte)'G';
                p           = set_text_field(buffer, p, gfc.tag_spec.title, 30, pad);
                p           = set_text_field(buffer, p, gfc.tag_spec.artist, 30, pad);
                p           = set_text_field(buffer, p, gfc.tag_spec.album, 30, pad);
                year        = string.Format("{0:D}", Convert.ToInt32(gfc.tag_spec.year));
                p           = set_text_field(buffer, p, gfc.tag_spec.year != 0 ? year : null, 4, pad);
                p           = set_text_field(buffer, p, gfc.tag_spec.comment, gfc.tag_spec.track_id3v1 != 0 ? 28 : 30, pad);
                if (gfc.tag_spec.track_id3v1 != 0)
                {
                    buffer[p++] = 0;
                    buffer[p++] = (sbyte)gfc.tag_spec.track_id3v1;
                }

                buffer[p++] = (sbyte)gfc.tag_spec.genre_id3v1;
                return(tag_size);
            }

            return(0);
        }
예제 #30
0
        /// <summary>
        ///     Some combinations of bitrate, Fs, and stereo make it impossible to stuff
        ///     out a frame using just main_data, due to the limited number of bits to
        ///     indicate main_data_length. In these situations, we put stuffing bits into
        ///     the ancillary data...
        /// </summary>
        private void drain_into_ancillary(LameGlobalFlags gfp, int remainingBits)
        {
            var gfc = gfp.internal_flags;
            int i;

            Debug.Assert(remainingBits >= 0);

            if (remainingBits >= 8)
            {
                putbits2(gfc, 0x4c, 8);
                remainingBits -= 8;
            }

            if (remainingBits >= 8)
            {
                putbits2(gfc, 0x41, 8);
                remainingBits -= 8;
            }

            if (remainingBits >= 8)
            {
                putbits2(gfc, 0x4d, 8);
                remainingBits -= 8;
            }

            if (remainingBits >= 8)
            {
                putbits2(gfc, 0x45, 8);
                remainingBits -= 8;
            }

            if (remainingBits >= 32)
            {
                var version = ver.LameShortVersion;
                if (remainingBits >= 32)
                {
                    for (i = 0; i < version.Length && remainingBits >= 8; ++i)
                    {
                        remainingBits -= 8;
                        putbits2(gfc, version[i], 8);
                    }
                }
            }

            for (; remainingBits >= 1; remainingBits -= 1)
            {
                putbits2(gfc, gfc.ancillary_flag, 1);
                gfc.ancillary_flag ^= !gfp.disable_reservoir ? 1 : 0;
            }

            Debug.Assert(remainingBits == 0);
        }