示例#1
0
        // TODO : support for CUESHEET block
        public bool Read(BinaryReader source, ReadTagParams readTagParams)
        {
            bool result = false;

            if (readTagParams.ReadTag && null == vorbisTag)
            {
                vorbisTag = new VorbisTag(false, false, false, false);
            }

            initialPaddingOffset = -1;
            initialPaddingSize   = 0;

            byte[] aMetaDataBlockHeader;
            long   position;
            uint   blockLength;
            byte   blockType;
            int    blockIndex;
            bool   isLast;
            bool   paddingFound   = false;
            long   blockEndOffset = -1;

            readHeader(source);

            // Process data if loaded and header valid
            if (header.IsValid())
            {
                int channels = (header.Info[12] >> 1) & 0x7;
                switch (channels)
                {
                case 0b0000: channelsArrangement = MONO; break;

                case 0b0001: channelsArrangement = STEREO; break;

                case 0b0010: channelsArrangement = ISO_3_0_0; break;

                case 0b0011: channelsArrangement = QUAD; break;

                case 0b0100: channelsArrangement = ISO_3_2_0; break;

                case 0b0101: channelsArrangement = ISO_3_2_1; break;

                case 0b0110: channelsArrangement = LRCLFECrLssRss; break;

                case 0b0111: channelsArrangement = LRCLFELrRrLssRss; break;

                case 0b1000: channelsArrangement = JOINT_STEREO_LEFT_SIDE; break;

                case 0b1001: channelsArrangement = JOINT_STEREO_RIGHT_SIDE; break;

                case 0b1010: channelsArrangement = JOINT_STEREO_MID_SIDE; break;

                default: channelsArrangement = UNKNOWN; break;
                }

                sampleRate    = header.Info[10] << 12 | header.Info[11] << 4 | header.Info[12] >> 4;
                bitsPerSample = (byte)(((header.Info[12] & 1) << 4) | (header.Info[13] >> 4) + 1);
                samples       = header.Info[14] << 24 | header.Info[15] << 16 | header.Info[16] << 8 | header.Info[17];

                if (0 == (header.MetaDataBlockHeader[1] & FLAG_LAST_METADATA_BLOCK)) // metadata block exists
                {
                    blockIndex = 0;
                    vorbisTag.Clear();
                    if (readTagParams.PrepareForWriting)
                    {
                        if (null == zones)
                        {
                            zones = new List <Zone>();
                        }
                        else
                        {
                            zones.Clear();
                        }
                        blockEndOffset = source.BaseStream.Position;
                    }

                    do // Read all metadata blocks
                    {
                        aMetaDataBlockHeader = source.ReadBytes(4);
                        isLast = (aMetaDataBlockHeader[0] & FLAG_LAST_METADATA_BLOCK) > 0; // last flag ( first bit == 1 )

                        blockIndex++;
                        blockLength = StreamUtils.DecodeBEUInt24(aMetaDataBlockHeader, 1);

                        blockType = (byte)(aMetaDataBlockHeader[0] & 0x7F); // decode metablock type
                        position  = source.BaseStream.Position;

                        if (blockType == META_VORBIS_COMMENT) // Vorbis metadata
                        {
                            if (readTagParams.PrepareForWriting)
                            {
                                zones.Add(new Zone(blockType + "", position - 4, (int)blockLength + 4, new byte[0], blockType));
                            }
                            vorbisTag.Read(source, readTagParams);
                        }
                        else if ((blockType == META_PADDING) && (!paddingFound))  // Padding block (skip any other padding block)
                        {
                            if (readTagParams.PrepareForWriting)
                            {
                                zones.Add(new Zone(PADDING_ZONE_NAME, position - 4, (int)blockLength + 4, new byte[0], blockType));
                            }
                            initialPaddingSize   = blockLength;
                            initialPaddingOffset = position;
                            paddingFound         = true;
                            source.BaseStream.Seek(blockLength, SeekOrigin.Current);
                        }
                        else if (blockType == META_PICTURE) // Picture (NB: as per FLAC specs, pictures must be embedded at the FLAC level, not in the VorbisComment !)
                        {
                            if (readTagParams.PrepareForWriting)
                            {
                                zones.Add(new Zone(blockType + "", position - 4, (int)blockLength + 4, new byte[0], blockType));
                            }
                            vorbisTag.ReadPicture(source.BaseStream, readTagParams);
                        }
                        else // Unhandled block; needs to be zoned anyway to be able to manage the 'isLast' flag at write-time
                        {
                            if (readTagParams.PrepareForWriting)
                            {
                                zones.Add(new Zone(blockType + "", position - 4, (int)blockLength + 4, new byte[0], blockType));
                            }
                        }


                        if (blockType < 7)
                        {
                            source.BaseStream.Seek(position + blockLength, SeekOrigin.Begin);
                            blockEndOffset = position + blockLength;
                        }
                        else
                        {
                            // Abnormal header : incorrect size and/or misplaced last-metadata-block flag
                            break;
                        }
                    }while (!isLast);

                    if (readTagParams.PrepareForWriting)
                    {
                        bool vorbisTagFound = false;
                        bool pictureFound   = false;

                        foreach (Zone zone in zones)
                        {
                            if (zone.Flag == META_PICTURE)
                            {
                                pictureFound = true;
                            }
                            else if (zone.Flag == META_VORBIS_COMMENT)
                            {
                                vorbisTagFound = true;
                            }
                        }

                        if (!vorbisTagFound)
                        {
                            zones.Add(new Zone(META_VORBIS_COMMENT + "", blockEndOffset, 0, new byte[0], META_VORBIS_COMMENT));
                        }
                        if (!pictureFound)
                        {
                            zones.Add(new Zone(META_PICTURE + "", blockEndOffset, 0, new byte[0], META_PICTURE));
                        }
                        // Padding must be the last block for it to correctly absorb size variations of the other blocks
                        if (!paddingFound && Settings.AddNewPadding)
                        {
                            zones.Add(new Zone(PADDING_ZONE_NAME, blockEndOffset, 0, new byte[0], META_PADDING));
                        }
                    }
                }
            }

            if (isValid())
            {
                audioOffset = source.BaseStream.Position;  // we need that to calculate the bitrate
                result      = true;
            }

            return(result);
        }
示例#2
0
        public Boolean Read(BinaryReader source, ReadTagParams readTagParams)
        {
            var result = false;

            if (readTagParams.ReadTag && null == vorbisTag)
            {
                vorbisTag = new VorbisTag(false, false, false);
            }

            Byte[]  aMetaDataBlockHeader;
            Int64   position;
            UInt32  blockLength;
            Int32   blockType;
            Int32   blockIndex;
            Boolean isLast;
            var     bPaddingFound = false;

            readHeader(source);

            // Process data if loaded and header valid
            if (header.IsValid())
            {
                channels      = (Byte)(((header.Info[12] >> 1) & 0x7) + 1);
                sampleRate    = (header.Info[10] << 12 | header.Info[11] << 4 | header.Info[12] >> 4);
                bitsPerSample = (Byte)(((header.Info[12] & 1) << 4) | (header.Info[13] >> 4) + 1);
                samples       = (header.Info[14] << 24 | header.Info[15] << 16 | header.Info[16] << 8 | header.Info[17]);

                if (0 == (header.MetaDataBlockHeader[1] & 0x80))                   // metadata block exists
                {
                    blockIndex = 0;
                    if (readTagParams.PrepareForWriting)
                    {
                        if (null == zones)
                        {
                            zones = new List <Zone>();
                        }
                        else
                        {
                            zones.Clear();
                        }
                        firstBlockPosition = source.BaseStream.Position;
                    }

                    do // read more metadata blocks if available
                    {
                        aMetaDataBlockHeader = source.ReadBytes(4);
                        isLast = ((aMetaDataBlockHeader[0] & 0x80) > 0); // last flag ( first bit == 1 )

                        blockIndex++;
                        blockLength = StreamUtils.DecodeBEUInt24(aMetaDataBlockHeader, 1);

                        blockType = (aMetaDataBlockHeader[0] & 0x7F);                         // decode metablock type
                        position  = source.BaseStream.Position;

                        if (blockType == META_VORBIS_COMMENT)                           // Vorbis metadata
                        {
                            if (readTagParams.PrepareForWriting)
                            {
                                zones.Add(new Zone(ZONE_VORBISTAG, position - 4, (Int32)blockLength + 4, new Byte[0], (Byte)(isLast ? 1 : 0)));
                            }
                            vorbisTag.Read(source, readTagParams);
                        }
                        else if ((blockType == META_PADDING) && (!bPaddingFound))                            // Padding block
                        {
                            padding       = blockLength;                                                     // if we find more skip & put them in metablock array
                            paddingLast   = ((aMetaDataBlockHeader[0] & 0x80) != 0);
                            paddingIndex  = blockIndex;
                            bPaddingFound = true;
                            source.BaseStream.Seek(padding, SeekOrigin.Current); // advance into file till next block or audio data start
                        }
                        else if (blockType == META_PICTURE)
                        {
                            if (readTagParams.PrepareForWriting)
                            {
                                zones.Add(new Zone(ZONE_PICTURE, position - 4, (Int32)blockLength + 4, new Byte[0], (Byte)(isLast ? 1 : 0)));
                            }
                            vorbisTag.ReadPicture(source.BaseStream, readTagParams);
                        }
                        // TODO : support for CUESHEET block

                        if (blockType < 7)
                        {
                            source.BaseStream.Seek(position + blockLength, SeekOrigin.Begin);
                        }
                        else
                        {
                            // Abnormal header : incorrect size and/or misplaced last-metadata-block flag
                            break;
                        }
                    }while (!isLast);

                    if (readTagParams.PrepareForWriting)
                    {
                        var vorbisTagFound = false;
                        var pictureFound   = false;

                        foreach (var zone in zones)
                        {
                            if (zone.Name.Equals(ZONE_PICTURE))
                            {
                                pictureFound = true;
                            }
                            else if (zone.Name.Equals(ZONE_VORBISTAG))
                            {
                                vorbisTagFound = true;
                            }
                        }

                        if (!vorbisTagFound)
                        {
                            zones.Add(new Zone(ZONE_VORBISTAG, firstBlockPosition, 0, new Byte[0]));
                        }
                        if (!pictureFound)
                        {
                            zones.Add(new Zone(ZONE_PICTURE, firstBlockPosition, 0, new Byte[0]));
                        }
                    }
                }
            }

            if (isValid())
            {
                audioOffset = source.BaseStream.Position;  // we need that to rebuild the file if nedeed
                result      = true;
            }

            return(result);
        }
示例#3
0
        private bool getInfo(BufferedBinaryReader source, FileInfo info, ReadTagParams readTagParams)
        {
            // Get info from file
            bool result        = false;
            bool isValidHeader = false;

            // Check for ID3v2 (NB : this case should not even exist since OGG has its own native tagging system, and is not deemed compatible with ID3v2 according to the ID3 FAQ)
            source.Seek(sizeInfo.ID3v2Size, SeekOrigin.Begin);

            // Read global file header
            info.IdentificationHeader.ReadFromStream(source);

            if (info.IdentificationHeader.IsValid())
            {
                source.Seek(sizeInfo.ID3v2Size + info.IdentificationHeader.Segments + 27, SeekOrigin.Begin); // 27 being the size from 'ID' to 'Segments'

                // Read Vorbis or Opus stream info
                long position = source.Position;

                String headerStart = Utils.Latin1Encoding.GetString(source.ReadBytes(3));
                source.Seek(position, SeekOrigin.Begin);
                if (VORBIS_HEADER_ID.StartsWith(headerStart))
                {
                    contents = CONTENTS_VORBIS;
                    info.VorbisParameters.ID = Utils.Latin1Encoding.GetString(source.ReadBytes(7));
                    isValidHeader            = VORBIS_HEADER_ID.Equals(info.VorbisParameters.ID);

                    info.VorbisParameters.BitstreamVersion = source.ReadBytes(4);
                    info.VorbisParameters.ChannelMode      = source.ReadByte();
                    info.VorbisParameters.SampleRate       = source.ReadInt32();
                    info.VorbisParameters.BitRateMaximal   = source.ReadInt32();
                    info.VorbisParameters.BitRateNominal   = source.ReadInt32();
                    info.VorbisParameters.BitRateMinimal   = source.ReadInt32();
                    info.VorbisParameters.BlockSize        = source.ReadByte();
                    info.VorbisParameters.StopFlag         = source.ReadByte();
                }
                else if (OPUS_HEADER_ID.StartsWith(headerStart))
                {
                    contents = CONTENTS_OPUS;
                    info.OpusParameters.ID = Utils.Latin1Encoding.GetString(source.ReadBytes(8));
                    isValidHeader          = OPUS_HEADER_ID.Equals(info.OpusParameters.ID);

                    info.OpusParameters.Version            = source.ReadByte();
                    info.OpusParameters.OutputChannelCount = source.ReadByte();
                    info.OpusParameters.PreSkip            = source.ReadUInt16();
                    //info.OpusParameters.InputSampleRate = source.ReadUInt32();
                    info.OpusParameters.InputSampleRate = 48000; // Actual sample rate is hardware-dependent. Let's assume for now that the hardware ATL runs on supports 48KHz
                    source.Seek(4, SeekOrigin.Current);
                    info.OpusParameters.OutputGain = source.ReadInt16();

                    info.OpusParameters.ChannelMappingFamily = source.ReadByte();

                    if (info.OpusParameters.ChannelMappingFamily > 0)
                    {
                        info.OpusParameters.StreamCount        = source.ReadByte();
                        info.OpusParameters.CoupledStreamCount = source.ReadByte();

                        info.OpusParameters.ChannelMapping = new byte[info.OpusParameters.OutputChannelCount];
                        for (int i = 0; i < info.OpusParameters.OutputChannelCount; i++)
                        {
                            info.OpusParameters.ChannelMapping[i] = source.ReadByte();
                        }
                    }
                }

                if (isValidHeader)
                {
                    info.CommentHeaderStart = source.Position;
                    IList <long> pagePos = new List <long>();

                    // Reads all related Vorbis pages that describe Comment and Setup headers
                    // and concatenate their content into a single, continuous data stream
                    bool loop  = true;
                    bool first = true;
                    using (MemoryStream s = new MemoryStream())
                    {
                        // Reconstruct the whole Comment header from OGG pages to a MemoryStream
                        while (loop)
                        {
                            info.SetupHeaderEnd              = source.Position; // When the loop stops, cursor is starting to read a brand new page located after Comment _and_ Setup headers
                            info.CommentHeader.ID            = Utils.Latin1Encoding.GetString(source.ReadBytes(4));
                            info.CommentHeader.StreamVersion = source.ReadByte();
                            info.CommentHeader.TypeFlag      = source.ReadByte();
                            // 0 marks a new page
                            if (0 == info.CommentHeader.TypeFlag)
                            {
                                loop = first;
                            }
                            if (loop)
                            {
                                info.CommentHeader.AbsolutePosition = source.ReadUInt64();
                                info.CommentHeader.Serial           = source.ReadInt32();
                                info.CommentHeader.PageNumber       = source.ReadInt32();
                                info.CommentHeader.Checksum         = source.ReadUInt32();
                                info.CommentHeader.Segments         = source.ReadByte();
                                info.CommentHeader.LacingValues     = source.ReadBytes(info.CommentHeader.Segments);
                                s.Write(source.ReadBytes(info.CommentHeader.GetPageLength()), 0, info.CommentHeader.GetPageLength());
                                pagePos.Add(info.SetupHeaderEnd);
                            }
                            first = false;
                        }

                        if (readTagParams.PrepareForWriting) // Metrics to prepare writing
                        {
                            if (pagePos.Count > 1)
                            {
                                source.Position = pagePos[pagePos.Count - 2];
                            }
                            else
                            {
                                source.Position = pagePos[0];
                            }

                            // Determine the boundaries of 3rd header (Setup header) by searching from last-but-one page
                            if (StreamUtils.FindSequence(source, Utils.Latin1Encoding.GetBytes(VORBIS_SETUP_ID)))
                            {
                                info.SetupHeaderStart = source.Position - VORBIS_SETUP_ID.Length;
                                info.CommentHeaderEnd = info.SetupHeaderStart;

                                if (pagePos.Count > 1)
                                {
                                    int firstSetupPage = -1;
                                    for (int i = 1; i < pagePos.Count; i++)
                                    {
                                        if (info.CommentHeaderEnd < pagePos[i])
                                        {
                                            info.CommentHeaderSpanPages = i - 1;
                                            firstSetupPage = i - 1;
                                        }
                                        if (info.SetupHeaderEnd < pagePos[i])
                                        {
                                            info.SetupHeaderSpanPages = i - firstSetupPage;
                                        }
                                    }
                                    /// Not found yet => comment header takes up all pages, and setup header is on the end of the last page
                                    if (-1 == firstSetupPage)
                                    {
                                        info.CommentHeaderSpanPages = pagePos.Count;
                                        info.SetupHeaderSpanPages   = 1;
                                    }
                                }
                                else
                                {
                                    info.CommentHeaderSpanPages = 1;
                                    info.SetupHeaderSpanPages   = 1;
                                }
                            }
                        }

                        // Get total number of samples
                        info.Samples = getSamples(source);

                        // Read metadata from the reconstructed Comment header inside the memoryStream
                        if (readTagParams.ReadTag)
                        {
                            BinaryReader msr = new BinaryReader(s);
                            s.Seek(0, SeekOrigin.Begin);

                            string tagId;
                            bool   isValidTagHeader = false;
                            if (contents.Equals(CONTENTS_VORBIS))
                            {
                                tagId            = Utils.Latin1Encoding.GetString(msr.ReadBytes(7));
                                isValidTagHeader = (VORBIS_TAG_ID.Equals(tagId));
                            }
                            else if (contents.Equals(CONTENTS_OPUS))
                            {
                                tagId            = Utils.Latin1Encoding.GetString(msr.ReadBytes(8));
                                isValidTagHeader = (OPUS_TAG_ID.Equals(tagId));
                            }

                            if (isValidTagHeader)
                            {
                                vorbisTag.Clear();
                                vorbisTag.Read(msr, readTagParams);
                            }
                        }
                    } // using MemoryStream

                    result = true;
                }
            }

            return(result);
        }
示例#4
0
        public bool Read(BinaryReader source, ReadTagParams readTagParams)
        {
            bool result = false;

            if (readTagParams.ReadTag && null == vorbisTag)
            {
                vorbisTag = new VorbisTag(false, false, false);
            }

            byte[] aMetaDataBlockHeader;
            long   position;
            uint   blockLength;
            int    blockType;
            int    blockIndex;
            bool   isLast;
            bool   bPaddingFound = false;

            readHeader(source);

            // Process data if loaded and header valid
            if (header.IsValid())
            {
                int channels = (header.Info[12] >> 1) & 0x7;
                switch (channels)
                {
                case 0b0000: channelsArrangement = MONO; break;

                case 0b0001: channelsArrangement = STEREO; break;

                case 0b0010: channelsArrangement = ISO_3_0_0; break;

                case 0b0011: channelsArrangement = QUAD; break;

                case 0b0100: channelsArrangement = ISO_3_2_0; break;

                case 0b0101: channelsArrangement = ISO_3_2_1; break;

                case 0b0110: channelsArrangement = LRCLFECrLssRss; break;

                case 0b0111: channelsArrangement = LRCLFELrRrLssRss; break;

                case 0b1000: channelsArrangement = JOINT_STEREO_LEFT_SIDE; break;

                case 0b1001: channelsArrangement = JOINT_STEREO_RIGHT_SIDE; break;

                case 0b1010: channelsArrangement = JOINT_STEREO_MID_SIDE; break;

                default: channelsArrangement = UNKNOWN; break;
                }

                sampleRate    = (header.Info[10] << 12 | header.Info[11] << 4 | header.Info[12] >> 4);
                bitsPerSample = (byte)(((header.Info[12] & 1) << 4) | (header.Info[13] >> 4) + 1);
                samples       = (header.Info[14] << 24 | header.Info[15] << 16 | header.Info[16] << 8 | header.Info[17]);

                if (0 == (header.MetaDataBlockHeader[1] & 0x80)) // metadata block exists
                {
                    blockIndex = 0;
                    vorbisTag.Clear();
                    if (readTagParams.PrepareForWriting)
                    {
                        if (null == zones)
                        {
                            zones = new List <Zone>();
                        }
                        else
                        {
                            zones.Clear();
                        }
                        firstBlockPosition = source.BaseStream.Position;
                    }

                    do // read more metadata blocks if available
                    {
                        aMetaDataBlockHeader = source.ReadBytes(4);
                        isLast = ((aMetaDataBlockHeader[0] & 0x80) > 0); // last flag ( first bit == 1 )

                        blockIndex++;
                        blockLength = StreamUtils.DecodeBEUInt24(aMetaDataBlockHeader, 1);

                        blockType = (aMetaDataBlockHeader[0] & 0x7F); // decode metablock type
                        position  = source.BaseStream.Position;

                        if (blockType == META_VORBIS_COMMENT) // Vorbis metadata
                        {
                            if (readTagParams.PrepareForWriting)
                            {
                                zones.Add(new Zone(ZONE_VORBISTAG, position - 4, (int)blockLength + 4, new byte[0], (byte)(isLast ? 1 : 0)));
                            }
                            vorbisTag.Read(source, readTagParams);
                        }
                        else if ((blockType == META_PADDING) && (!bPaddingFound))  // Padding block
                        {
                            padding       = blockLength;                           // if we find more skip & put them in metablock array
                            paddingLast   = ((aMetaDataBlockHeader[0] & 0x80) != 0);
                            paddingIndex  = blockIndex;
                            bPaddingFound = true;
                            source.BaseStream.Seek(padding, SeekOrigin.Current); // advance into file till next block or audio data start
                        }
                        else if (blockType == META_PICTURE)
                        {
                            if (readTagParams.PrepareForWriting)
                            {
                                zones.Add(new Zone(ZONE_PICTURE, position - 4, (int)blockLength + 4, new byte[0], (byte)(isLast ? 1 : 0)));
                            }
                            vorbisTag.ReadPicture(source.BaseStream, readTagParams);
                        }
                        // TODO : support for CUESHEET block

                        if (blockType < 7)
                        {
                            source.BaseStream.Seek(position + blockLength, SeekOrigin.Begin);
                        }
                        else
                        {
                            // Abnormal header : incorrect size and/or misplaced last-metadata-block flag
                            break;
                        }
                    }while (!isLast);

                    if (readTagParams.PrepareForWriting)
                    {
                        bool vorbisTagFound = false;
                        bool pictureFound   = false;

                        foreach (Zone zone in zones)
                        {
                            if (zone.Name.Equals(ZONE_PICTURE))
                            {
                                pictureFound = true;
                            }
                            else if (zone.Name.Equals(ZONE_VORBISTAG))
                            {
                                vorbisTagFound = true;
                            }
                        }

                        if (!vorbisTagFound)
                        {
                            zones.Add(new Zone(ZONE_VORBISTAG, firstBlockPosition, 0, new byte[0]));
                        }
                        if (!pictureFound)
                        {
                            zones.Add(new Zone(ZONE_PICTURE, firstBlockPosition, 0, new byte[0]));
                        }
                    }
                }
            }

            if (isValid())
            {
                audioOffset = source.BaseStream.Position;  // we need that to rebuild the file if nedeed
                result      = true;
            }

            return(result);
        }