zoukankan      html  css  js  c++  java
  • ID3v1信息结构(MP3文件)的读取、修改(C#)

    背景

    前几天了解到MP3文件的ID3v1信息和ID3v2信息结构,其中ID3v1信息存储的内容比较简单,有歌曲名、艺术家、专辑、发行年、备注、曲目编号、流派。其版本有1.0和1.1,其中1.0没有曲目编号。

    正文

    ID3v1信息存储在MP3文件的尾部,一共128字节,可有可无。以下是其信息排列:

    ID3v1.1(1.0中无曲目编号,所以其备注包括Null character和Track,共30字节)

    Tag Field Data(character) Offset(from end of mp3)
    TAG 3 -128 to -126
    Song title(歌曲名) 30 -125 to -96
    Artist(艺术家) 30 -95 to -66
    Album(专辑) 30 -65 to -36
    Year(发行年) 4 -35 to -32
    Comment(备注) 28 -31 to -4
    Null character 1 -3
    Track(曲目编号) 1 -2
    Genre(流派) 1 -1

     

    说明

    Year:以字符串形式存在,获取时无需将其从byte[4]转换成int。

    Null character:此为保留位,1.1中始终为(byte)0,所以通过其判断Comment的大小和ID3v1的版本。

    Track: int类型,其值为 0 - 255,毕竟1byte只能存这么多。

    Genre: int类型,其对应类型详见附录。

    知道了以上信息就可以开始编码了。

    public enum ID3v1TagVersion
    {
    ID3v10,
    ID3v11
    }

    ID3v1

    image

    private ID3v1(){ }

    public ID3v1(string path)
    {
    MP3Path
    = path;
    ReadPath(MP3Path);
    }

    私有字段和共属性
    #region Private Fields
    private ID3v1TagVersion _tagVersion;
    private string _title; //30 characters
    private string _artist; //30 characters
    private string _album; //30 characters
    private string _year; // 4 characters
    private string _comment; //28 characters, sometimes it's 30 characters when the next byte is not be 0 and this tag has not track information.
    private string _reserved; // 1 byte, if it's 0 that means the next byte should contain which track on the CD this music comes from.
    private int _track; // 1 byte, sometimes not exist if the reserved byte is not 0.
    private int _genre = 12; // 1 byte

    private string MP3Path; //mp3 file path
    #endregion

    #region Property
    public ID3v1TagVersion TagVersion
    {
    get { return _tagVersion; }
    set { _tagVersion = value; if (value == ID3v1TagVersion.ID3v11) { this.Comment = this._comment; } }
    }

    public string Title
    {
    get { return _title; }
    set { _title = GetString(value, 30); }
    }

    public string Artist
    {
    get { return _artist; }
    set { _artist = GetString(value, 30); }
    }

    public string Album
    {
    get { return _album; }
    set { _album = GetString(value, 30); }
    }

    public string Year
    {
    get { return _year; }
    set { _year = GetString(value, 4); }
    }

    public string Comment
    {
    get { return _comment; }
    set { _comment = GetString(value, this._tagVersion == ID3v1TagVersion.ID3v10 ? 30 : 28); }
    }

    private string Reserved
    {
    get { return _reserved; }
    set { _reserved = value; }
    }

    public int Track
    {
    get { return _track; }
    set {
    if (value >= 0 && value <= 0xff)
    {
    _track
    = value;
    if (this._tagVersion == ID3v1TagVersion.ID3v10)
    {
    this.TagVersion = ID3v1TagVersion.ID3v11;
    }
    }
    }
    }

    public int Genre
    {
    get { return _genre; }
    set { _genre = value; }
    }
    #endregion

    读取ID3v1信息
    private void ReadPath(string path)
    {
    using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
    {
    this.ReadStream(stream);
    }
    }

    private void ReadStream(Stream stream)
    {
    if (stream.Length >= 128)
    {
    Encoding encode
    = Encoding.Default;
    byte[] tag = new byte[128];

    stream.Seek(
    -128L, SeekOrigin.End);
    stream.Read(tag,
    0, 128);

    if ("TAG" == encode.GetString(tag, 0, 3))
    {
    this._title = encode.GetString(tag, 3, 30);
    this._artist = encode.GetString(tag, 33, 30);
    this._album = encode.GetString(tag, 63, 30);
    this._year = encode.GetString(tag, 93, 4);
    if (tag[125] == 0)
    {
    this._tagVersion = ID3v1TagVersion.ID3v11;
    this._comment = encode.GetString(tag, 97, 28);
    this._track = tag[126];
    }
    else
    {
    this._tagVersion = ID3v1TagVersion.ID3v10;
    this._comment = encode.GetString(tag, 97, 30);
    this._track = 0;
    }
    this._genre = (int)tag[127];
    }
    }
    }

    保存ID3v1信息
    private void Save(string path)
    {
    using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
    {
    Save(stream);
    }
    }

    private void Save(Stream stream)
    {
    byte[] header = SafeGetBytes("TAG");
    byte[] title = SafeGetBytes(this._title);
    byte[] artist = SafeGetBytes(this._artist);
    byte[] album = SafeGetBytes(this._album);
    byte[] year = SafeGetBytes(this._year);
    byte[] comment = SafeGetBytes(this._comment);

    stream.Seek((
    long)-GetTagSize(stream), SeekOrigin.End);
    stream.Write(header,
    0, 3);
    WriteBytesPadded(stream, title,
    30);
    WriteBytesPadded(stream, artist,
    30);
    WriteBytesPadded(stream, album,
    30);
    WriteBytesPadded(stream, year,
    4);
    if (this._tagVersion == ID3v1TagVersion.ID3v11)
    {
    WriteBytesPadded(stream, comment,
    28);
    stream.WriteByte(
    0);
    stream.WriteByte((
    byte)this._track);
    }
    else
    {
    WriteBytesPadded(stream, comment,
    30);
    }
    stream.WriteByte((
    byte)this._genre);
    }

    私有函数
    private static string GetString(string value, int maxLength)
    {
    if (value == null)
    {
    return null;
    }
    value
    = value.Trim();
    return value.Length > maxLength ? value.Substring(0, maxLength) : value;
    }

    private static int GetTagSize(string path)
    {
    using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
    {
    return GetTagSize(stream);
    }
    return 0;
    }

    private static int GetTagSize(Stream stream)
    {
    if (stream.Length >= 128L)
    {
    byte[] header = new byte[3];
    stream.Seek(
    -128L, SeekOrigin.End);
    stream.Read(header,
    0, 3);
    if (Encoding.Default.GetString(header) == "TAG")
    {
    return 128;
    }
    }
    return 0;
    }

    private static byte[] SafeGetBytes(string value)
    {
    if (value == null)
    {
    return new byte[0];
    }
    return Encoding.Default.GetBytes(value);
    }

    private static void WriteBytesPadded(Stream stream, byte[] buffer, int length)
    {
    int index = 0;
    while ((index < length && index < buffer.Length) && buffer[index] != 0)
    {
    stream.WriteByte(buffer[index]);
    index
    ++;
    }
    while (index < length)
    {
    stream.WriteByte(
    0);
    index
    ++;
    }
    }

    结语

    有了这些就可以实现MP3的ID3v1信息的读取和修改了。

    附录

    流派信息有两部分,摘自ID3.ORG。若想知道其他流派信息,请自行搜索。

    1. ID3v1定义的(流派前的数字是编号)

    ID3v1定义
    0. Blues
    1. Classic Rock
    2. Country
    3. Dance
    4. Disco
    5. Funk
    6. Grunge
    7. Hip-Hop
    8. Jazz
    9. Metal
    10. New Age
    11. Oldies
    12. Other
    13. Pop
    14. R&B
    15. Rap
    16. Reggae
    17. Rock
    18. Techno
    19. Industrial
    20. Alternative
    21. Ska
    22. Death Metal
    23. Pranks
    24. Soundtrack
    25. Euro-Techno
    26. Ambient
    27. Trip-Hop
    28. Vocal
    29. Jazz+Funk
    30. Fusion
    31. Trance
    32. Classical
    33. Instrumental
    34. Acid
    35. House
    36. Game
    37. Sound Clip
    38. Gospel
    39. Noise
    40. AlternRock
    41. Bass
    42. Soul
    43. Punk
    44. Space
    45. Meditative
    46. Instrumental Pop
    47. Instrumental Rock
    48. Ethnic
    49. Gothic
    50. Darkwave
    51. Techno-Industrial
    52. Electronic
    53. Pop-Folk
    54. Eurodance
    55. Dream
    56. Southern Rock
    57. Comedy
    58. Cult
    59. Gangsta
    60. Top 40
    61. Christian Rap
    62. Pop/Funk
    63. Jungle
    64. Native American
    65. Cabaret
    66. New Wave
    67. Psychadelic
    68. Rave
    69. Showtunes
    70. Trailer
    71. Lo-Fi
    72. Tribal
    73. Acid Punk
    74. Acid Jazz
    75. Polka
    76. Retro
    77. Musical
    78. Rock & Roll
    79. Hard Rock

    2. Winamp扩展的

    Winamp扩展
    80. Folk
    81. Folk-Rock
    82. National Folk
    83. Swing
    84. Fast Fusion
    85. Bebob
    86. Latin
    87. Revival
    88. Celtic
    89. Bluegrass
    90. Avantgarde
    91. Gothic Rock
    92. Progressive Rock
    93. Psychedelic Rock
    94. Symphonic Rock
    95. Slow Rock
    96. Big Band
    97. Chorus
    98. Easy Listening
    99. Acoustic
    100. Humour
    101. Speech
    102. Chanson
    103. Opera
    104. Chamber Music
    105. Sonata
    106. Symphony
    107. Booty Bass
    108. Primus
    109. Porn Groove
    110. Satire
    111. Slow Jam
    112. Club
    113. Tango
    114. Samba
    115. Folklore
    116. Ballad
    117. Power Ballad
    118. Rhythmic Soul
    119. Freestyle
    120. Duet
    121. Punk Rock
    122. Drum Solo
    123. A capella
    124. Euro-House
    125. Dance Hall

  • 相关阅读:
    js let
    go 语言
    第二十七篇、使用MVVM布局页面
    第二十六篇、因为自定item(nav)而使系统右滑返回手势失效的解决方法
    第四篇、点赞的粒子动画
    第二十五篇、抽屉效果的核心代码
    第二十四篇、iOS 10版本适配
    第二十三篇、使用NSURLSession时需要注意一个内存泄漏问题
    第十四篇、Ajax与Json
    第十三篇、jQuery Mobile
  • 原文地址:https://www.cnblogs.com/ainijiutian/p/1921638.html
Copyright © 2011-2022 走看看