Initial commit

This commit is contained in:
Thastertyn committed 2024-06-07 00:47:07 +02:00
commit 262601edb8
2025 files changed
+218754

No files matched your search

+275
View File
@@ -0,0 +1,275 @@
//
// File.cs: Provides tagging and properties support for Monkey's Audio APE
// files.
//
// Author:
// Helmut Wahrmann
//
// Copyright (C) 2007 Helmut Wahrmann
// Copyright (C) 2007 Brian Nickel
//
// 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;
namespace TagLib.Ape
{
/// <summary>
/// This class extends <see cref="TagLib.NonContainer.File" /> to
/// provide tagging and properties support for Monkey's Audio APE
/// files.
/// </summary>
/// <remarks>
/// A <see cref="TagLib.Ape.Tag" /> will be added automatically to
/// any file that doesn't contain one. This change does not effect
/// the physical file until <see cref="TagLib.File.Save" /> is called and can be
/// reversed using the following method:
/// <code>file.RemoveTags (file.TagTypes &amp; ~file.TagTypesOnDisk);</code>
/// </remarks>
[SupportedMimeType ("taglib/ape", "ape")]
[SupportedMimeType ("audio/x-ape")]
[SupportedMimeType ("audio/ape")]
[SupportedMimeType ("application/x-ape")]
public class File : TagLib.NonContainer.File
{
#region Private Fields
/// <summary>
/// Contains the block with the audio header.
/// </summary>
ByteVector header_block;
#endregion
#region 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>
public File (string path, ReadStyle propertiesStyle)
: base (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>
public File (string path)
: base (path)
{
}
/// <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="TagLib.File.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>
public File (IFileAbstraction abstraction, ReadStyle propertiesStyle)
: base (abstraction, propertiesStyle)
{
}
/// <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="TagLib.File.IFileAbstraction" /> object to use when
/// reading from and writing to the file.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="abstraction" /> is <see langword="null"
/// />.
/// </exception>
public File (IFileAbstraction abstraction)
: base (abstraction)
{
}
#endregion
#region Public Methods
/// <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>
/// <remarks>
/// If a <see cref="TagLib.Id3v2.Tag" /> is added to the
/// current instance, it will be placed at the start of the
/// file. On the other hand, <see cref="TagLib.Id3v1.Tag" />
/// <see cref="TagLib.Ape.Tag" /> will be added to the end of
/// the file. All other tag types will be ignored.
/// </remarks>
public override TagLib.Tag GetTag (TagTypes type, bool create)
{
TagLib.Tag t = (Tag as TagLib.NonContainer.Tag)
.GetTag (type);
if (t != null || !create)
return t;
switch (type) {
case TagTypes.Id3v1:
return EndTag.AddTag (type, Tag);
case TagTypes.Id3v2:
return StartTag.AddTag (type, Tag);
case TagTypes.Ape:
return EndTag.AddTag (type, Tag);
default:
return null;
}
}
#endregion
#region Protected Methods
/// <summary>
/// Reads format specific information at the start of the
/// file.
/// </summary>
/// <param name="start">
/// A <see cref="long" /> value containing the seek position
/// at which the tags end and the media data begins.
/// </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>
protected override void ReadStart (long start, ReadStyle propertiesStyle)
{
if (header_block != null &&
(propertiesStyle & ReadStyle.Average) == 0)
return;
Seek (start);
header_block = ReadBlock ((int)StreamHeader.Size);
}
/// <summary>
/// Reads format specific information at the end of the
/// file.
/// </summary>
/// <param name="end">
/// A <see cref="long" /> value containing the seek position
/// at which the media data ends and the tags begin.
/// </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>
protected override void ReadEnd (long end, ReadStyle propertiesStyle)
{
// Make sure we have an APE tag.
GetTag (TagTypes.Ape, true);
}
/// <summary>
/// Reads the audio properties from the file represented by
/// the current instance.
/// </summary>
/// <param name="start">
/// A <see cref="long" /> value containing the seek position
/// at which the tags end and the media data begins.
/// </param>
/// <param name="end">
/// A <see cref="long" /> value containing the seek position
/// at which the media data ends and the tags begin.
/// </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>
/// <returns>
/// A <see cref="TagLib.Properties" /> object describing the
/// media properties of the file represented by the current
/// instance.
/// </returns>
protected override Properties ReadProperties (long start, long end, ReadStyle propertiesStyle)
{
StreamHeader header = new StreamHeader (header_block,
end - start);
return new Properties (TimeSpan.Zero, header);
}
#endregion
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6bd919f3cebce9e6b94eb2887772bf8d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+392
View File
@@ -0,0 +1,392 @@
//
// Footer.cs: Provides a representation of an APEv2 tag footer which can be read
// from and written to disk.
//
// Author:
// Brian Nickel (brian.nickel@gmail.com)
//
// Original Source:
// apefooter.cpp from TagLib
//
// Copyright (C) 2005-2007 Brian Nickel
// Copyright (C) 2004 Allan Sandfeld Jensen (Original Implementation)
// copyright (C) 2002, 2003 Scott Wheeler (Original Implementation)
//
// 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;
namespace TagLib.Ape
{
#region Enums
/// <summary>
/// Indicates the flags applied to a <see cref="Footer" /> object.
/// </summary>
[Flags]
public enum FooterFlags : uint
{
/// <summary>
/// The tag lacks a footer object.
/// </summary>
FooterAbsent = 0x40000000,
/// <summary>
/// The footer is actually a header.
/// </summary>
IsHeader = 0x20000000,
/// <summary>
/// The tag contains a header.
/// </summary>
HeaderPresent = 0x80000000
}
#endregion
/// <summary>
/// This structure provides a representation of an APEv2 tag footer
/// which can be read from and written to disk.
/// </summary>
public struct Footer : IEquatable<Footer>
{
#region Private Properties
/// <summary>
/// Contains the APE tag version.
/// </summary>
readonly uint version;
#endregion
#region Public Static Fields
/// <summary>
/// Specifies the size of an APEv2 footer.
/// </summary>
public const uint Size = 32;
/// <summary>
/// Specifies the identifier used find an APEv2 footer in a
/// file.
/// </summary>
/// <value>
/// "<c>APETAGEX</c>"
/// </value>
public static readonly ReadOnlyByteVector FileIdentifier = "APETAGEX";
#endregion
#region Constructors
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="Footer" /> by reading it from raw footer data.
/// </summary>
/// <param name="data">
/// A <see cref="ByteVector" /> object containing the raw
/// data to build the new instance from.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="data" /> is <see langword="null" />.
/// </exception>
/// <exception cref="CorruptFileException">
/// <paramref name="data" /> is smaller than <see
/// cref="Size" /> or does not begin with <see
/// cref="FileIdentifier" />.
/// </exception>
public Footer (ByteVector data)
{
if (data == null)
throw new ArgumentNullException (nameof (data));
if (data.Count < Size)
throw new CorruptFileException ("Provided data is smaller than object size.");
if (!data.StartsWith (FileIdentifier))
throw new CorruptFileException ("Provided data does not start with File Identifier");
version = data.Mid (8, 4).ToUInt (false);
TagSize = data.Mid (12, 4).ToUInt (false);
ItemCount = data.Mid (16, 4).ToUInt (false);
Flags = (FooterFlags)data.Mid (20, 4).ToUInt (false);
}
#endregion
#region Public Properties
/// <summary>
/// Gets the version of APE tag described by the current
/// instance.
/// </summary>
/// <value>
/// A <see cref="uint" /> value containing the version of the
/// APE tag described by the current instance.
/// </value>
public uint Version {
get { return version == 0 ? 2000 : version; }
}
/// <summary>
/// Gets and sets the flags that apply to the current
/// instance.
/// </summary>
/// <value>
/// A bitwise combined <see cref="FooterFlags" /> value
/// containing the flags that apply to the current instance.
/// </value>
public FooterFlags Flags { get; set; }
/// <summary>
/// Gets and sets the number of items in the tag represented
/// by the current instance.
/// </summary>
/// <value>
/// A <see cref="uint" /> value containing the number of
/// items in the tag represented by the current instance.
/// </value>
public uint ItemCount { get; set; }
/// <summary>
/// Gets the size of the tag represented by the current
/// instance, including the footer but excluding the header
/// if applicable.
/// </summary>
/// <value>
/// A <see cref="uint" /> value containing the size of the
/// tag represented by the current instance.
/// </value>
public uint TagSize { get; set; }
/// <summary>
/// Gets the complete size of the tag represented by the
/// current instance, including the header and footer.
/// </summary>
/// <value>
/// A <see cref="uint" /> value containing the size of the
/// tag represented by the current instance.
/// </value>
public uint CompleteTagSize {
get {
return TagSize + ((Flags &
FooterFlags.HeaderPresent) != 0 ?
Size : 0);
}
}
#endregion
#region Public Methods
/// <summary>
/// Renders the current instance as an APE tag footer.
/// </summary>
/// <returns>
/// A <see cref="ByteVector" /> object containing the
/// rendered version of the current instance.
/// </returns>
public ByteVector RenderFooter ()
{
return Render (false);
}
/// <summary>
/// Renders the current instance as an APE tag header.
/// </summary>
/// <returns>
/// A <see cref="ByteVector" /> object containing the
/// rendered version of the current instance or an empty
/// <see cref="ByteVector" /> object if <see cref="Flags" />
/// does not include <see cref="FooterFlags.HeaderPresent"
/// />.
/// </returns>
public ByteVector RenderHeader ()
{
return (Flags & FooterFlags.HeaderPresent) != 0 ? Render (true) : new ByteVector ();
}
#endregion
#region Private Methods
/// <summary>
/// Renders the current instance as either an APE tag header
/// or footer.
/// </summary>
/// <param name="isHeader">
/// A <see cref="bool" /> value indicating whether or not the
/// current instance is to be rendered as a header.
/// </param>
/// <returns>
/// A <see cref="ByteVector" /> object containing the
/// rendered version of the current instance.
/// </returns>
ByteVector Render (bool isHeader)
{
var v = new ByteVector {
// add the file identifier -- "APETAGEX"
FileIdentifier,
// add the version number -- we always render a 2.000
// tag regardless of what the tag originally was.
ByteVector.FromUInt (2000, false),
// add the tag size
ByteVector.FromUInt (TagSize, false),
// add the item count
ByteVector.FromUInt (ItemCount, false)
};
// render and add the flags
uint flags = 0;
if ((Flags & FooterFlags.HeaderPresent) != 0)
flags |= (uint)FooterFlags.HeaderPresent;
// footer is always present
if (isHeader)
flags |= (uint)FooterFlags.IsHeader;
else
flags &= (uint)~FooterFlags.IsHeader;
v.Add (ByteVector.FromUInt (flags, false));
// add the reserved 64bit
v.Add (ByteVector.FromULong (0));
return v;
}
#endregion
#region IEquatable
/// <summary>
/// Generates a hash code for the current instance.
/// </summary>
/// <returns>
/// A <see cref="int" /> value containing the hash code for
/// the current instance.
/// </returns>
public override int GetHashCode ()
{
unchecked {
return (int)((uint)Flags ^ TagSize ^ ItemCount ^ version);
}
}
/// <summary>
/// Checks whether or not the current instance is equal to
/// another object.
/// </summary>
/// <param name="other">
/// A <see cref="object" /> to compare to the current
/// instance.
/// </param>
/// <returns>
/// A <see cref="bool" /> value indicating whether or not the
/// current instance is equal to <paramref name="other" />.
/// </returns>
/// <seealso cref="M:System.IEquatable`1.Equals" />
public override bool Equals (object other)
{
if (!(other is Footer))
return false;
return Equals ((Footer)other);
}
/// <summary>
/// Checks whether or not the current instance is equal to
/// another instance of <see cref="Footer" />.
/// </summary>
/// <param name="other">
/// A <see cref="Footer" /> object to compare to the current
/// instance.
/// </param>
/// <returns>
/// A <see cref="bool" /> value indicating whether or not the
/// current instance is equal to <paramref name="other" />.
/// </returns>
/// <seealso cref="M:System.IEquatable`1.Equals" />
public bool Equals (Footer other)
{
return Flags == other.Flags &&
TagSize == other.TagSize &&
ItemCount == other.ItemCount &&
version == other.version;
}
/// <summary>
/// Gets whether or not two instances of <see cref="Footer"
/// /> are equal to eachother.
/// </summary>
/// <param name="first">
/// The first <see cref="Footer" /> object to compare.
/// </param>
/// <param name="second">
/// The second <see cref="Footer" /> object to compare.
/// </param>
/// <returns>
/// <see langword="true" /> if <paramref name="first" /> is
/// equal to <paramref name="second" />. Otherwise, <see
/// langword="false" />.
/// </returns>
public static bool operator == (Footer first, Footer second)
{
return first.Equals (second);
}
/// <summary>
/// Gets whether or not two instances of <see cref="Footer"
/// /> are unequal to eachother.
/// </summary>
/// <param name="first">
/// The first <see cref="Footer" /> object to compare.
/// </param>
/// <param name="second">
/// The second <see cref="Footer" /> object to compare.
/// </param>
/// <returns>
/// <see langword="true" /> if <paramref name="first" /> is
/// unequal to <paramref name="second" />. Otherwise, <see
/// langword="false" />.
/// </returns>
public static bool operator != (Footer first, Footer second)
{
return !first.Equals (second);
}
#endregion
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3fd643b62da0d63f89725b4266c74752
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+500
View File
@@ -0,0 +1,500 @@
//
// Item.cs: Provides a representation of an APEv2 tag item which can be read
// from and written to disk.
//
// Author:
// Brian Nickel (brian.nickel@gmail.com)
//
// Original Source:
// apeitem.cpp from TagLib
//
// Copyright (C) 2005-2007 Brian Nickel
// Copyright (C) 2004 by Allan Sandfeld Jensen (Original Implementation)
//
// 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;
namespace TagLib.Ape
{
/// <summary>
/// Indicates the type of data stored in a <see cref="Item" />
/// object.
/// </summary>
public enum ItemType
{
/// <summary>
/// The item contains Unicode text.
/// </summary>
Text = 0,
/// <summary>
/// The item contains binary data.
/// </summary>
Binary = 1,
/// <summary>
/// The item contains a locator (file path/URL) for external
/// information.
/// </summary>
Locator = 2
}
/// <summary>
/// This class provides a representation of an APEv2 tag item which
/// can be read from and written to disk.
/// </summary>
public class Item : ICloneable
{
#region Private Fields
/// <summary>
/// Contains the item value.
/// </summary>
ReadOnlyByteVector data;
/// <summary>
/// Contains the item text.
/// </summary>
string[] text;
#endregion
#region Constructors
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="Item" /> by reading in a raw APEv2 item.
/// </summary>
/// <param name="data">
/// A <see cref="ByteVector" /> object containing the item to
/// read.
/// </param>
/// <param name="offset">
/// A <see cref="int" /> value specifying the offset in
/// <paramref name="data" /> at which the item data begins.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="data" /> is <see langword="null" />.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="offset" /> is less than zero.
/// </exception>
/// <exception cref="CorruptFileException">
/// A complete item could not be read.
/// </exception>
public Item (ByteVector data, int offset)
{
if (data == null)
throw new ArgumentNullException (nameof (data));
Parse (data, offset);
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="Item" /> with a specified key and value.
/// </summary>
/// <param name="key">
/// A <see cref="string" /> object containing the key to use
/// for the current instance.
/// </param>
/// <param name="value">
/// A <see cref="string" /> object containing the value to
/// store in the new instance.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="key" /> or <paramref name="value" /> is
/// <see langword="null" />.
/// </exception>
public Item (string key, string value)
{
if (key == null)
throw new ArgumentNullException (nameof (key));
if (value == null)
throw new ArgumentNullException (nameof (value));
Key = key;
text = new [] { value };
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="Item" /> with a specified key and collection of
/// values.
/// </summary>
/// <param name="key">
/// A <see cref="string" /> object containing the key to use
/// for the current instance.
/// </param>
/// <param name="value">
/// A <see cref="T:string[]" /> containing the values to store
/// in the new instance.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="key" /> or <paramref name="value" /> is
/// <see langword="null" />.
/// </exception>
public Item (string key, params string[] value)
{
if (key == null)
throw new ArgumentNullException (nameof (key));
if (value == null)
throw new ArgumentNullException (nameof (value));
Key = key;
text = (string[])value.Clone ();
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="Item" /> with a specified key and collection of
/// values.
/// </summary>
/// <param name="key">
/// A <see cref="string" /> object containing the key to use
/// for the current instance.
/// </param>
/// <param name="value">
/// A <see cref="StringCollection" /> object containing the
/// values to store in the new instance.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="key" /> or <paramref name="value" /> is
/// <see langword="null" />.
/// </exception>
/// <seealso cref="Item(string,string[])" />
[Obsolete ("Use Item(string,string[])")]
public Item (string key, StringCollection value)
{
if (key == null)
throw new ArgumentNullException (nameof (key));
if (value == null)
throw new ArgumentNullException (nameof (value));
Key = key;
text = value.ToArray ();
}
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="Item" /> with a specified key and raw data.
/// </summary>
/// <param name="key">
/// A <see cref="string" /> object containing the key to use
/// for the current instance.
/// </param>
/// <param name="value">
/// A <see cref="StringCollection" /> object containing the
/// values to store in the new instance.
/// </param>
/// <remarks>
/// This constructor automatically marks the new instance as
/// <see cref="ItemType.Binary" />.
/// </remarks>
/// <exception cref="ArgumentNullException">
/// <paramref name="key" /> or <paramref name="value" /> is
/// <see langword="null" />.
/// </exception>
/// <seealso cref="Item(string,string[])" />
public Item (string key, ByteVector value)
{
Key = key;
Type = ItemType.Binary;
data = value as ReadOnlyByteVector;
if (data == null)
data = new ReadOnlyByteVector (value);
}
Item (Item item)
{
Type = item.Type;
Key = item.Key;
if (item.data != null)
data = new ReadOnlyByteVector (item.data);
if (item.text != null)
text = (string[])item.text.Clone ();
ReadOnly = item.ReadOnly;
Size = item.Size;
}
#endregion
#region Public Properties
/// <summary>
/// Gets the key used to identify the current instance.
/// </summary>
/// <value>
/// A <see cref="string" /> object containing the key used to
/// identify the current instance.
/// </value>
/// <remarks>
/// This value is used for specifying the contents of the
/// item in a common and consistant fashion. For example,
/// <c>"TITLE"</c> specifies that the item contains the title
/// of the track.
/// </remarks>
public string Key { get; private set; }
/// <summary>
/// Gets the binary value stored in the current instance.
/// </summary>
/// <value>
/// A <see cref="ByteVector" /> object containing the binary
/// value stored in the current instance, or <see
/// langword="null" /> if the item contains text.
/// </value>
public ByteVector Value {
get { return (Type == ItemType.Binary) ? data : null; }
}
/// <summary>
/// Gets the size of the current instance as it last appeared
/// on disk.
/// </summary>
/// <value>
/// A <see cref="int" /> value containing the size of the
/// current instance as it last appeared on disk.
/// </value>
public int Size { get; private set; }
/// <summary>
/// Gets and sets the type of value contained in the
/// current instance.
/// </summary>
/// <value>
/// A <see cref="ItemType" /> value indicating the type of
/// value contained in the current instance.
/// </value>
public ItemType Type { get; set; } = ItemType.Text;
/// <summary>
/// Gets and sets whether or not the current instance is
/// flagged as read-only on disk.
/// </summary>
/// <value>
/// A <see cref="bool" /> value indicating whether or not the
/// current instance is flagged as read-only on disk.
/// </value>
public bool ReadOnly { get; set; }
/// <summary>
/// Gets whether or not the current instance is empty.
/// </summary>
/// <value>
/// A <see cref="bool" /> value indicating whether or not the
/// current instance contains no value.
/// </value>
public bool IsEmpty {
get {
if (Type != ItemType.Binary)
return text == null || text.Length == 0;
else
return data == null || data.IsEmpty;
}
}
#endregion
#region Public Methods
/// <summary>
/// Gets the contents of the current instance as a <see
/// cref="string" />.
/// </summary>
/// <returns>
/// <para>A <see cref="string" /> object containing the text
/// stored in the current instance, or <see langword="null"
/// /> if the item is empty of contains binary data.</para>
/// <para>If the current instance contains multiple string
/// values, they will be returned as a comma separated
/// value.</para>
/// </returns>
public override string ToString ()
{
if (Type == ItemType.Binary || text == null)
return null;
return string.Join (", ", text);
}
/// <summary>
/// Gets the contents of the current instance as a <see
/// cref="string" /> array.
/// </summary>
/// <returns>
/// A <see cref="T:string[]" /> containing the text stored in
/// the current instance, or an empty array if the item
/// contains binary data.
/// </returns>
public string[] ToStringArray ()
{
if (Type == ItemType.Binary || text == null)
return new string[0];
return text;
}
/// <summary>
/// Renders the current instance as an APEv2 item.
/// </summary>
/// <returns>
/// A <see cref="ByteVector" /> object containing the
/// rendered version of the current instance.
/// </returns>
public ByteVector Render ()
{
uint flags = (uint)((ReadOnly) ? 1 : 0) |
((uint)Type << 1);
if (IsEmpty)
return new ByteVector ();
ByteVector result = null;
if (Type == ItemType.Binary) {
if (text == null && data != null)
result = data;
}
if (result == null && text != null) {
result = new ByteVector ();
for (int i = 0; i < text.Length; i++) {
if (i != 0)
result.Add (0);
result.Add (ByteVector.FromString (text[i], StringType.UTF8));
}
}
// If no data is stored, don't write the item.
if (result == null || result.Count == 0)
return new ByteVector ();
var output = new ByteVector {
ByteVector.FromUInt ((uint)result.Count,
false),
ByteVector.FromUInt (flags, false),
ByteVector.FromString (Key, StringType.UTF8),
0,
result
};
Size = output.Count;
return output;
}
#endregion
#region Protected Methods
/// <summary>
/// Populates the current instance by reading in a raw APEv2
/// item.
/// </summary>
/// <param name="data">
/// A <see cref="ByteVector" /> object containing the item to
/// read.
/// </param>
/// <param name="offset">
/// A <see cref="int" /> value specifying the offset in
/// <paramref name="data" /> at which the item data begins.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="data" /> is <see langword="null" />.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="offset" /> is less than zero.
/// </exception>
/// <exception cref="CorruptFileException">
/// A complete item could not be read.
/// </exception>
protected void Parse (ByteVector data, int offset)
{
if (data == null)
throw new ArgumentNullException (nameof (data));
if (offset < 0)
throw new ArgumentOutOfRangeException (nameof (offset));
// 11 bytes is the minimum size for an APE item
if (data.Count < offset + 11)
throw new CorruptFileException ("Not enough data for APE Item");
uint value_length = data.Mid (offset, 4).ToUInt (false);
uint flags = data.Mid (offset + 4, 4).ToUInt (false);
ReadOnly = (flags & 1) == 1;
Type = (ItemType)((flags >> 1) & 3);
int pos = data.Find (ByteVector.TextDelimiter (StringType.UTF8), offset + 8);
Key = data.ToString (StringType.UTF8, offset + 8, pos - offset - 8);
if (value_length > data.Count - pos - 1)
throw new CorruptFileException ("Invalid data length.");
Size = pos + 1 + (int)value_length - offset;
if (Type == ItemType.Binary)
this.data = new ReadOnlyByteVector (data.Mid (pos + 1, (int)value_length));
else
text = data.Mid (pos + 1, (int)value_length).ToStrings (StringType.UTF8, 0);
}
#endregion
#region ICloneable
/// <summary>
/// Creates a deep copy of the current instance.
/// </summary>
/// <returns>
/// A new <see cref="Item"/> object identical to the current
/// instance.
/// </returns>
public Item Clone ()
{
return new Item (this);
}
object ICloneable.Clone ()
{
return Clone ();
}
#endregion
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec6d7edea3a67467d8e074bff4ac65d7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+359
View File
@@ -0,0 +1,359 @@
//
// StreamHeader.cs: Provides support for reading Monkey's Audio APE stream
// properties.
//
// Author:
// Helmut Wahrmann
//
// Copyright (C) 2007 Helmut Wahrmann
// Copyright (C) 2007 Brian Nickel
//
// 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 System.Globalization;
namespace TagLib.Ape
{
/// <summary>
/// Indicates the compression level used when encoding a Monkey's
/// Audio APE file.
/// </summary>
public enum CompressionLevel
{
/// <summary>
/// The audio is not compressed.
/// </summary>
None = 0,
/// <summary>
/// The audio is mildly compressed.
/// </summary>
Fast = 1000,
/// <summary>
/// The audio is compressed at a normal level.
/// </summary>
Normal = 2000,
/// <summary>
/// The audio is highly compressed.
/// </summary>
High = 3000,
/// <summary>
/// The audio is extremely highly compressed.
/// </summary>
ExtraHigh = 4000,
/// <summary>
/// The audio is compressed to an insane level.
/// </summary>
Insane
}
/// <summary>
/// This struct implements <see cref="IAudioCodec" /> to provide
/// support for reading Monkey's Audio APE stream properties.
/// </summary>
public struct StreamHeader : IAudioCodec, ILosslessAudioCodec
{
#region Private Fields
/// <summary>
/// Contains the APE version.
/// </summary>
/// <remarks>
/// This value is stored in bytes (4,5) of the file and is
/// 1000 times the actual version number, so 3810 indicates
/// version 3.81.
/// </remarks>
readonly ushort version;
/*
/// <summary>
/// Contains the format flags.
/// </summary>
/// <remarks>
/// This value is stored in bytes (53,54).
/// </remarks>
private ushort format_flags;
*/
/// <summary>
/// Contains the number of audio blocks in one frame.
/// </summary>
/// <remarks>
/// This value is stored in bytes (55-58).
/// </remarks>
readonly uint blocks_per_frame;
/// <summary>
/// Contains the number of audio blocks in the final frame.
/// </summary>
/// <remarks>
/// This value is stored in bytes (59-62).
/// </remarks>
readonly uint final_frame_blocks;
/// <summary>
/// Contains the total number of frames.
/// </summary>
/// <remarks>
/// This value is stored in bytes (63-66).
/// </remarks>
readonly uint total_frames;
/// <summary>
/// Contains the number of bits per sample.
/// </summary>
/// <remarks>
/// This value is stored in bytes (67,68) and is typically
/// 16.
/// </remarks>
readonly ushort bits_per_sample;
/// <summary>
/// Contains the number of channels.
/// </summary>
/// <remarks>
/// This value is stored in bytes (69,70) and is typically
/// 1 or 2.
/// </remarks>
readonly ushort channels;
/// <summary>
/// Contains the sample rate.
/// </summary>
/// <remarks>
/// This value is stored in bytes (71-74) and is typically
/// 44100.
/// </remarks>
readonly uint sample_rate;
/// <summary>
/// Contains the length of the audio stream.
/// </summary>
/// <remarks>
/// This value is provided by the constructor.
/// </remarks>
readonly long stream_length;
#endregion
#region Public Static Fields
/// <summary>
/// The size of a Monkey Audio header.
/// </summary>
public const uint Size = 76;
/// <summary>
/// The identifier used to recognize a WavPack file.
/// </summary>
/// <value>
/// "MAC "
/// </value>
public static readonly ReadOnlyByteVector FileIdentifier = "MAC ";
#endregion
#region Constructors
/// <summary>
/// Constructs and initializes a new instance of <see
/// cref="StreamHeader" /> for a specified header block and
/// stream length.
/// </summary>
/// <param name="data">
/// A <see cref="ByteVector" /> object containing the stream
/// header data.
/// </param>
/// <param name="streamLength">
/// A <see cref="long" /> value containing the length of the
/// Monkey Audio stream in bytes.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="data" /> is <see langword="null" />.
/// </exception>
/// <exception cref="CorruptFileException">
/// <paramref name="data" /> does not begin with <see
/// cref="FileIdentifier" /> or is less than <see cref="Size"
/// /> bytes long.
/// </exception>
public StreamHeader (ByteVector data, long streamLength)
{
if (data == null)
throw new ArgumentNullException (nameof (data));
if (!data.StartsWith (FileIdentifier))
throw new CorruptFileException (
"Data does not begin with identifier.");
if (data.Count < Size)
throw new CorruptFileException (
"Insufficient data in stream header");
stream_length = streamLength;
version = data.Mid (4, 2).ToUShort (false);
Compression = (CompressionLevel)data.Mid (52, 2)
.ToUShort (false);
// format_flags = data.Mid(54, 2).ToUShort(false);
blocks_per_frame = data.Mid (56, 4).ToUInt (false);
final_frame_blocks = data.Mid (60, 4).ToUInt (false);
total_frames = data.Mid (64, 4).ToUInt (false);
bits_per_sample = data.Mid (68, 2).ToUShort (false);
channels = data.Mid (70, 2).ToUShort (false);
sample_rate = data.Mid (72, 4).ToUInt (false);
}
#endregion
#region Public Properties
/// <summary>
/// Gets the duration of the media represented by the current
/// instance.
/// </summary>
/// <value>
/// A <see cref="TimeSpan" /> containing the duration of the
/// media represented by the current instance.
/// </value>
public TimeSpan Duration {
get {
if (sample_rate <= 0 || total_frames <= 0)
return TimeSpan.Zero;
return TimeSpan.FromSeconds (
((total_frames - 1) *
blocks_per_frame + final_frame_blocks) /
(double)sample_rate);
}
}
/// <summary>
/// Gets the types of media represented by the current
/// instance.
/// </summary>
/// <value>
/// Always <see cref="MediaTypes.Audio" />.
/// </value>
public MediaTypes MediaTypes {
get { return MediaTypes.Audio; }
}
/// <summary>
/// Gets a text description of the media represented by the
/// current instance.
/// </summary>
/// <value>
/// A <see cref="string" /> object containing a description
/// of the media represented by the current instance.
/// </value>
public string Description {
get {
return string.Format (
CultureInfo.InvariantCulture, "Monkey's Audio APE Version {0:0.000}", Version);
}
}
/// <summary>
/// Gets the bitrate of the audio represented by the current
/// instance.
/// </summary>
/// <value>
/// A <see cref="int" /> value containing a bitrate of the
/// audio represented by the current instance.
/// </value>
public int AudioBitrate {
get {
TimeSpan d = Duration;
if (d <= TimeSpan.Zero)
return 0;
return (int)((stream_length * 8L) / d.TotalSeconds) / 1000;
}
}
/// <summary>
/// Gets the sample rate of the audio represented by the
/// current instance.
/// </summary>
/// <value>
/// A <see cref="int" /> value containing the sample rate of
/// the audio represented by the current instance.
/// </value>
public int AudioSampleRate {
get { return (int)sample_rate; }
}
/// <summary>
/// Gets the number of channels in the audio represented by
/// the current instance.
/// </summary>
/// <value>
/// A <see cref="int" /> value containing the number of
/// channels in the audio represented by the current
/// instance.
/// </value>
public int AudioChannels {
get { return channels; }
}
/// <summary>
/// Gets the APE version of the audio represented by the
/// current instance.
/// </summary>
/// <value>
/// A <see cref="double" /> value containing the APE version
/// of the audio represented by the current instance.
/// </value>
public double Version {
get { return version / (double)1000; }
}
/// <summary>
/// Gets the number of bits per sample in the audio
/// represented by the current instance.
/// </summary>
/// <value>
/// A <see cref="int" /> value containing the number of bits
/// per sample in the audio represented by the current
/// instance.
/// </value>
public int BitsPerSample {
get { return bits_per_sample; }
}
/// <summary>
/// Gets the level of compression used when encoding the
/// audio represented by the current instance.
/// </summary>
/// <value>
/// A <see cref="CompressionLevel" /> value indicating the
/// level of compression used when encoding the audio
/// represented by the current instance.
/// </value>
public CompressionLevel Compression { get; private set; }
#endregion
}␍}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d8234dea7db8f83ed86cb702f3474bbe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large. Load diff
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e937bf4e5ed85bb70b1660583e8eb5dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: