Logo Search packages:      
Sourcecode: taglib-sharp version File versions  Download package

File.cs

//
// File.cs: Provides tagging and properties support for Apple's AIFF 
// files.
//
// Author:
//   Helmut Wahrmann
//
// Copyright (C) 2009 Helmut Wahrmann
//
// This library is free software; you can redistribute it and/or modify
// it  under the terms of the GNU Lesser General Public License version
// 2.1 as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
// USA
//

using System;
using TagLib.Id3v2;

00028 namespace TagLib.Aiff
{
      /// <summary>
      ///    This class extends <see cref="TagLib.File" /> to provide
      ///    support for reading and writing tags and properties for files
      ///    using the AIFF file format.
      /// </summary>
      [SupportedMimeType("taglib/aif", "aif")]
      [SupportedMimeType("audio/x-aiff")]
      [SupportedMimeType("audio/aiff")]
      [SupportedMimeType("sound/aiff")]
      [SupportedMimeType("application/x-aiff")]
00040       public class File : TagLib.File
      {
            #region Private Fields

            /// <summary>
            ///    Contains the address of the AIFF header block.
            /// </summary>
00047             private ByteVector header_block = null;

            /// <summary>
            ///  Contains the Id3v2 tag.
            /// </summary>
00052             private Id3v2.Tag tag = null;

            /// <summary>
            ///  Contains the media properties.
            /// </summary>
00057             private Properties properties = null;

            #endregion

            #region Public Static Fields

            /// <summary>
            ///    The identifier used to recognize a AIFF files.
            /// </summary>
            /// <value>
            ///    "FORM"
            /// </value>
00069             public static readonly ReadOnlyByteVector FileIdentifier = "FORM";

            /// <summary>
            ///    The identifier used to recognize a AIFF Common chunk.
            /// </summary>
            /// <value>
            ///    "COMM"
            /// </value>
00077             public static readonly ReadOnlyByteVector CommIdentifier = "COMM";

            /// <summary>
            ///    The identifier used to recognize a AIFF Sound Data Chunk.
            /// </summary>
            /// <value>
            ///    "SSND"
            /// </value>
00085             public static readonly ReadOnlyByteVector SoundIdentifier = "SSND";

            /// <summary>
            ///    The identifier used to recognize a AIFF ID3 chunk.
            /// </summary>
            /// <value>
            ///    "ID3 "
            /// </value>
00093             public static readonly ReadOnlyByteVector ID3Identifier = "ID3 ";

            #endregion

            #region Public Constructors

            /// <summary>
            ///    Constructs and initializes a new instance of <see
            ///    cref="File" /> for a specified path in the local file
            ///    system and specified read style.
            /// </summary>
            /// <param name="path">
            ///    A <see cref="string" /> object containing the path of the
            ///    file to use in the new instance.
            /// </param>
            /// <param name="propertiesStyle">
            ///    A <see cref="ReadStyle" /> value specifying at what level
            ///    of accuracy to read the media properties, or <see
            ///    cref="ReadStyle.None" /> to ignore the properties.
            /// </param>
            /// <exception cref="ArgumentNullException">
            ///    <paramref name="path" /> is <see langword="null" />.
            /// </exception>
00116             public File(string path, ReadStyle propertiesStyle)
                  : this(new File.LocalFileAbstraction(path),
                         propertiesStyle)
            {
            }

            /// <summary>
            ///    Constructs and initializes a new instance of <see
            ///    cref="File" /> for a specified path in the local file
            ///    system with an average read style.
            /// </summary>
            /// <param name="path">
            ///    A <see cref="string" /> object containing the path of the
            ///    file to use in the new instance.
            /// </param>
            /// <exception cref="ArgumentNullException">
            ///    <paramref name="path" /> is <see langword="null" />.
            /// </exception>
00134             public File(string path)
                  : this(path, ReadStyle.Average)
            {
            }

            /// <summary>
            ///    Constructs and initializes a new instance of <see
            ///    cref="File" /> for a specified file abstraction and
            ///    specified read style.
            /// </summary>
            /// <param name="abstraction">
            ///    A <see cref="IFileAbstraction" /> object to use when
            ///    reading from and writing to the file.
            /// </param>
            /// <param name="propertiesStyle">
            ///    A <see cref="ReadStyle" /> value specifying at what level
            ///    of accuracy to read the media properties, or <see
            ///    cref="ReadStyle.None" /> to ignore the properties.
            /// </param>
            /// <exception cref="ArgumentNullException">
            ///    <paramref name="abstraction" /> is <see langword="null"
            ///    />.
            /// </exception>
00157             public File(File.IFileAbstraction abstraction,
                        ReadStyle propertiesStyle)
                  : base(abstraction)
            {
                  Mode = AccessMode.Read;
                  try
                  {
                        uint aiff_size;
                        long tag_start, tag_end;
                        Read(true, propertiesStyle, out aiff_size,
                             out tag_start, out tag_end);
                  }
                  finally
                  {
                        Mode = AccessMode.Closed;
                  }

                  TagTypesOnDisk = TagTypes;

                  GetTag(TagTypes.Id3v2, true);
            }

            /// <summary>
            ///    Constructs and initializes a new instance of <see
            ///    cref="File" /> for a specified file abstraction with an
            ///    average read style.
            /// </summary>
            /// <param name="abstraction">
            ///    A <see cref="IFileAbstraction" /> object to use when
            ///    reading from and writing to the file.
            /// </param>
            /// <exception cref="ArgumentNullException">
            ///    <paramref name="abstraction" /> is <see langword="null"
            ///    />.
            /// </exception>
00192             public File(File.IFileAbstraction abstraction)
                  : this(abstraction, ReadStyle.Average)
            {
            }

            #endregion

            #region Public Properties

            /// <summary>
            ///    Gets a abstract representation of all tags stored in the
            ///    current instance.
            /// </summary>
            /// <value>
            ///    A <see cref="TagLib.Tag" /> object representing all tags
            ///    stored in the current instance.
            /// </value>
            public override Tag Tag
00210             {
                  get { return tag; }
            }

            /// <summary>
            ///    Gets the media properties of the file represented by the
            ///    current instance.
            /// </summary>
            /// <value>
            ///    A <see cref="TagLib.Properties" /> object containing the
            ///    media properties of the file represented by the current
            ///    instance.
            /// </value>
            public override TagLib.Properties Properties
00224             {
                  get { return properties; }
            }

            #endregion

            #region Public Methods

            /// <summary>
            ///    Saves the changes made in the current instance to the
            ///    file it represents.
            /// </summary>
00236             public override void Save()
            {
                  Mode = AccessMode.Write;
                  try
                  {
                        ByteVector data = new ByteVector();

                        // Add the ID3 chunk and ID32 tag to the vector
                        if (tag != null)
                        {
                              ByteVector tag_data = tag.Render();
                              if (tag_data.Count > 10)
                              {
                                    if (tag_data.Count%2 == 1)
                                          tag_data.Add(0);

                                    data.Add("ID3 ");
                                    data.Add(ByteVector.FromUInt(
                                                (uint) tag_data.Count,
                                                true));
                                    data.Add(tag_data);
                              }
                        }

                        // Read the file to determine the current AIFF
                        // size and the area tagging is in.
                        uint aiff_size;
                        long tag_start, tag_end;
                        Read(false, ReadStyle.None, out aiff_size,
                             out tag_start, out tag_end);

                        // If tagging info cannot be found, place it at
                        // the end of the file.
                        if (tag_start < 12 || tag_end < tag_start)
                              tag_start = tag_end = Length;

                        int length = (int) (tag_end - tag_start + 8);

                        // Insert the tagging data.
                        Insert(data, tag_start, length);

                        // If the data size changed update the aiff size.
                        if (data.Count - length != 0 &&
                            tag_start <= aiff_size)
                        {
                              // Depending, if a Tag has been added or removed, 
                              // the length needs to be adjusted
                              if (tag == null)
                              {
                                    length -= 16;
                              }
                              else
                              {
                                    length -= 8;
                              }

                              Insert(ByteVector.FromUInt((uint)
                                                         (aiff_size + data.Count - length),
                                                         true), 4, 4);
                        }
                        // Update the tag types.
                        TagTypesOnDisk = TagTypes;
                  }
                  finally
                  {
                        Mode = AccessMode.Closed;
                  }
            }

            /// <summary>
            ///    Removes a set of tag types from the current instance.
            /// </summary>
            /// <param name="types">
            ///    A bitwise combined <see cref="TagLib.TagTypes" /> value
            ///    containing tag types to be removed from the file.
            /// </param>
            /// <remarks>
            ///    In order to remove all tags from a file, pass <see
            ///    cref="TagTypes.AllTags" /> as <paramref name="types" />.
            /// </remarks>
00316             public override void RemoveTags(TagTypes types)
            {
                  if (types == TagLib.TagTypes.Id3v2 ||
                      types == TagLib.TagTypes.AllTags)
                  {
                        tag = null;
                  }
            }

            /// <summary>
            ///    Gets a tag of a specified type from the current instance,
            ///    optionally creating a new tag if possible.
            /// </summary>
            /// <param name="type">
            ///    A <see cref="TagLib.TagTypes" /> value indicating the
            ///    type of tag to read.
            /// </param>
            /// <param name="create">
            ///    A <see cref="bool" /> value specifying whether or not to
            ///    try and create the tag if one is not found.
            /// </param>
            /// <returns>
            ///    A <see cref="Tag" /> object containing the tag that was
            ///    found in or added to the current instance. If no
            ///    matching tag was found and none was created, <see
            ///    langword="null" /> is returned.
            /// </returns>
00343             public override TagLib.Tag GetTag(TagTypes type, bool create)
            {
                  TagLib.Tag id32_tag = null;

                  switch (type)
                  {
                        case TagTypes.Id3v2:
                              if (tag == null && create)
                              {
                                    tag = new Id3v2.Tag();
                                    tag.Version = 2;
                              }

                              id32_tag = tag;
                              break;
                  }

                  return id32_tag;
            }

            #endregion

            #region Private Methods

            /// <summary>
            ///    Reads the contents of the current instance determining
            ///    the size of the riff data, the area the tagging is in,
            ///    and optionally reading in the tags and media properties.
            /// </summary>
            /// <param name="read_tags">
            ///    If <see langword="true" />, any tags found will be read
            ///    into the current instance.
            /// </param>
            /// <param name="style">
            ///    A <see cref="ReadStyle"/> value specifying how the media
            ///    data is to be read into the current instance.
            /// </param>
            /// <param name="aiff_size">
            ///    A <see cref="uint"/> value reference to be filled with
            ///    the size of the RIFF data as read from the file.
            /// </param>
            /// <param name="tag_start">
            ///    A <see cref="long" /> value reference to be filled with
            ///    the absolute seek position at which the tagging data
            ///    starts.
            /// </param>
            /// <param name="tag_end">
            ///    A <see cref="long" /> value reference to be filled with
            ///    the absolute seek position at which the tagging data
            ///    ends.
            /// </param>
            /// <exception cref="CorruptFileException">
            ///    The file does not begin with <see cref="FileIdentifier"
            ///    />.
            /// </exception>
00398             private void Read(bool read_tags, ReadStyle style,
                              out uint aiff_size, out long tag_start,
                              out long tag_end)
            {
                  Seek(0);
                  if (ReadBlock(4) != FileIdentifier)
                        throw new CorruptFileException(
                              "File does not begin with AIFF identifier");

                  aiff_size = ReadBlock(4).ToUInt(true);
                  tag_start = -1;
                  tag_end = -1;

                  // Get the properties of the file
                  if (header_block == null &&
                      style != ReadStyle.None)
                  {
                        long common_chunk_pos = Find(CommIdentifier, 0);

                        if (common_chunk_pos == -1)
                        {
                              throw new CorruptFileException(
                                    "No Common chunk available in AIFF file.");
                        }

                        Seek(common_chunk_pos);
                        header_block = ReadBlock((int) StreamHeader.Size);

                        StreamHeader header = new StreamHeader(header_block, aiff_size);
                        properties = new Properties(TimeSpan.Zero, header);
                  }

                  // Now we search for the ID3 chunk.
                  // Normally it appears after the Sound data chunk. But as the order of
                  // chunks is free, it might be the case that the ID3 chunk appears before 
                  // the sound data chunk.
                  // So we search first for the Sound data chunk and see, if an ID3 chunk appears before
                  long id3_chunk_pos = -1;
                  long sound_chunk_pos = Find(SoundIdentifier, 0, ID3Identifier);
                  if (sound_chunk_pos == -1)
                  {
                        // The ID3 chunk appears before the Sound chunk
                        id3_chunk_pos = Find(ID3Identifier, 0);
                  }

                  // Now let's look for the Sound chunk again
                  // Since a previous return value of -1 does mean, that the ID3 chunk was found first
                  sound_chunk_pos = Find(SoundIdentifier, 0);
                  if (sound_chunk_pos == -1)
                  {
                        throw new CorruptFileException(
                              "No Sound chunk available in AIFF file.");
                  }

                  // Get the length of the Sound chunk and use this as a start value to look for the ID3 chunk
                  Seek(sound_chunk_pos + 4);
                  ulong sound_chunk_length = ReadBlock(4).ToULong(true);
                  long start_search_pos = (long) sound_chunk_length + sound_chunk_pos + 4;

                  if (id3_chunk_pos == -1)
                  {
                        id3_chunk_pos = Find(ID3Identifier, start_search_pos);
                  }

                  if (id3_chunk_pos > -1)
                  {
                        if (read_tags && tag == null)
                        {
                              tag = new Id3v2.Tag(this,
                                                  id3_chunk_pos + 8);
                        }

                        // Get the length of the tag out of the ID3 chunk
                        Seek(id3_chunk_pos + 4);
                        uint tag_size = ReadBlock(4).ToUInt(true) + 8;

                        tag_start = InvariantStartPosition = id3_chunk_pos;
                        tag_end = InvariantEndPosition = tag_start + tag_size;
                  }
            }

            #endregion
      }
}

Generated by  Doxygen 1.6.0   Back to index