Ejemplo n.º 1
0
        protected override bool read(BinaryReader source, MetaDataIO.ReadTagParams readTagParams)
        {
            bool result = false;
            long position;

            resetData();
            source.BaseStream.Seek(0, SeekOrigin.Begin);

            if (AIFF_CONTAINER_ID.Equals(Utils.Latin1Encoding.GetString(source.ReadBytes(4))))
            {
                // Container chunk size
                long containerChunkPos  = source.BaseStream.Position;
                int  containerChunkSize = StreamUtils.DecodeBEInt32(source.ReadBytes(4));

                if (containerChunkPos + containerChunkSize + 4 != source.BaseStream.Length)
                {
                    LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Header size is incoherent with file size");
                }

                // Form type
                format = Utils.Latin1Encoding.GetString(source.ReadBytes(4));

                if (format.Equals(FORMTYPE_AIFF) || format.Equals(FORMTYPE_AIFC))
                {
                    isValid = true;

                    StringBuilder commentStr         = new StringBuilder("");
                    long          soundChunkPosition = 0;
                    long          soundChunkSize     = 0; // Header size included
                    bool          nameFound          = false;
                    bool          authorFound        = false;
                    bool          copyrightFound     = false;
                    bool          commentsFound      = false;
                    long          limit = Math.Min(containerChunkPos + containerChunkSize + 4, source.BaseStream.Length);

                    int annotationIndex = 0;
                    int commentIndex    = 0;

                    while (source.BaseStream.Position < limit)
                    {
                        ChunkHeader header = seekNextChunkHeader(source, limit);

                        position = source.BaseStream.Position;

                        if (header.ID.Equals(CHUNKTYPE_COMMON))
                        {
                            short channels = StreamUtils.DecodeBEInt16(source.ReadBytes(2));
                            switch (channels)
                            {
                            case 1: channelsArrangement = MONO; break;

                            case 2: channelsArrangement = STEREO; break;

                            case 3: channelsArrangement = ISO_3_0_0; break;

                            case 4: channelsArrangement = ISO_2_2_0; break;     // Specs actually allow both 2/2.0 and LRCS

                            case 6: channelsArrangement = LRLcRcCS; break;

                            default: channelsArrangement = UNKNOWN; break;
                            }

                            numSampleFrames = StreamUtils.DecodeBEUInt32(source.ReadBytes(4));
                            sampleSize      = (uint)StreamUtils.DecodeBEInt16(source.ReadBytes(2)); // This sample size is for uncompressed data only
                            byte[] byteArray = source.ReadBytes(10);
                            Array.Reverse(byteArray);
                            double aSampleRate = StreamUtils.ExtendedToDouble(byteArray);

                            if (format.Equals(FORMTYPE_AIFC))
                            {
                                compression = Utils.Latin1Encoding.GetString(source.ReadBytes(4));
                            }
                            else // AIFF <=> no compression
                            {
                                compression = COMPRESSION_NONE;
                            }

                            if (aSampleRate > 0)
                            {
                                sampleRate = (int)Math.Round(aSampleRate);
                                duration   = (double)numSampleFrames * 1000.0 / sampleRate;

                                if (!compression.Equals(COMPRESSION_NONE)) // Sample size is specific to selected compression method
                                {
                                    if (compression.ToLower().Equals("fl32"))
                                    {
                                        sampleSize = 32;
                                    }
                                    else if (compression.ToLower().Equals("fl64"))
                                    {
                                        sampleSize = 64;
                                    }
                                    else if (compression.ToLower().Equals("alaw"))
                                    {
                                        sampleSize = 8;
                                    }
                                    else if (compression.ToLower().Equals("ulaw"))
                                    {
                                        sampleSize = 8;
                                    }
                                }
                                if (duration > 0)
                                {
                                    bitrate = sampleSize * numSampleFrames * channelsArrangement.NbChannels / duration;
                                }
                            }
                        }
                        else if (header.ID.Equals(CHUNKTYPE_SOUND))
                        {
                            soundChunkPosition = source.BaseStream.Position - 8;
                            soundChunkSize     = header.Size + 8;
                        }
                        else if (header.ID.Equals(CHUNKTYPE_NAME) || header.ID.Equals(CHUNKTYPE_AUTHOR) || header.ID.Equals(CHUNKTYPE_COPYRIGHT))
                        {
                            structureHelper.AddZone(source.BaseStream.Position - 8, header.Size + 8, header.ID);
                            structureHelper.AddSize(containerChunkPos, containerChunkSize, header.ID);

                            tagExists = true;
                            if (header.ID.Equals(CHUNKTYPE_NAME))
                            {
                                nameFound = true;
                            }
                            if (header.ID.Equals(CHUNKTYPE_AUTHOR))
                            {
                                authorFound = true;
                            }
                            if (header.ID.Equals(CHUNKTYPE_COPYRIGHT))
                            {
                                copyrightFound = true;
                            }

                            SetMetaField(header.ID, Utils.Latin1Encoding.GetString(source.ReadBytes(header.Size)), readTagParams.ReadAllMetaFrames);
                        }
                        else if (header.ID.Equals(CHUNKTYPE_ANNOTATION))
                        {
                            annotationIndex++;
                            structureHelper.AddZone(source.BaseStream.Position - 8, header.Size + 8, header.ID + annotationIndex);
                            structureHelper.AddSize(containerChunkPos, containerChunkSize, header.ID + annotationIndex);

                            if (commentStr.Length > 0)
                            {
                                commentStr.Append(Settings.InternalValueSeparator);
                            }
                            commentStr.Append(Utils.Latin1Encoding.GetString(source.ReadBytes(header.Size)));
                            tagExists = true;
                        }
                        else if (header.ID.Equals(CHUNKTYPE_COMMENTS))
                        {
                            commentIndex++;
                            structureHelper.AddZone(source.BaseStream.Position - 8, header.Size + 8, header.ID + commentIndex);
                            structureHelper.AddSize(containerChunkPos, containerChunkSize, header.ID + commentIndex);

                            tagExists     = true;
                            commentsFound = true;

                            ushort numComs = StreamUtils.DecodeBEUInt16(source.ReadBytes(2));

                            for (int i = 0; i < numComs; i++)
                            {
                                CommentData cmtData = new CommentData();
                                cmtData.Timestamp = StreamUtils.DecodeBEUInt32(source.ReadBytes(4));
                                cmtData.MarkerId  = StreamUtils.DecodeBEInt16(source.ReadBytes(2));

                                // Comments length
                                ushort        comLength = StreamUtils.DecodeBEUInt16(source.ReadBytes(2));
                                MetaFieldInfo comment   = new MetaFieldInfo(getImplementedTagType(), header.ID + commentIndex);
                                comment.Value        = Utils.Latin1Encoding.GetString(source.ReadBytes(comLength));
                                comment.SpecificData = cmtData;
                                tagData.AdditionalFields.Add(comment);

                                // Only read general purpose comments, not those linked to a marker
                                if (0 == cmtData.MarkerId)
                                {
                                    if (commentStr.Length > 0)
                                    {
                                        commentStr.Append(Settings.InternalValueSeparator);
                                    }
                                    commentStr.Append(comment.Value);
                                }
                            }
                        }
                        else if (header.ID.Equals(CHUNKTYPE_ID3TAG))
                        {
                            id3v2Offset = source.BaseStream.Position;

                            // Zone is already added by Id3v2.Read
                            id3v2StructureHelper.AddZone(id3v2Offset - 8, header.Size + 8, CHUNKTYPE_ID3TAG);
                            id3v2StructureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_ID3TAG);
                        }

                        source.BaseStream.Position = position + header.Size;

                        if (header.ID.Equals(CHUNKTYPE_SOUND) && header.Size % 2 > 0)
                        {
                            source.BaseStream.Position += 1;                                                           // Sound chunk size must be even
                        }
                    }

                    tagData.IntegrateValue(TagData.TAG_FIELD_COMMENT, commentStr.ToString().Replace("\0", " ").Trim());

                    if (-1 == id3v2Offset)
                    {
                        id3v2Offset = 0; // Switch status to "tried to read, but nothing found"

                        if (readTagParams.PrepareForWriting)
                        {
                            id3v2StructureHelper.AddZone(soundChunkPosition + soundChunkSize, 0, CHUNKTYPE_ID3TAG);
                            id3v2StructureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_ID3TAG);
                        }
                    }

                    // Add zone placeholders for future tag writing
                    if (readTagParams.PrepareForWriting)
                    {
                        if (!nameFound)
                        {
                            structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_NAME);
                            structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_NAME);
                        }
                        if (!authorFound)
                        {
                            structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_AUTHOR);
                            structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_AUTHOR);
                        }
                        if (!copyrightFound)
                        {
                            structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_COPYRIGHT);
                            structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_COPYRIGHT);
                        }
                        if (!commentsFound)
                        {
                            structureHelper.AddZone(soundChunkPosition, 0, CHUNKTYPE_COMMENTS);
                            structureHelper.AddSize(containerChunkPos, containerChunkSize, CHUNKTYPE_COMMENTS);
                        }
                    }

                    result = true;
                }
            }

            return(result);
        }