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 FieldData(character)Offset(from end of mp3)
TAG3-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 character1-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

 

转载于:https://www.cnblogs.com/ainijiutian/archive/2010/12/30/1921638.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值