Skip to content

Commit b2d30b8

Browse files
committed
feat: added support for Event Time Codes Frame (ETCO)
1 parent fe2cc47 commit b2d30b8

File tree

9 files changed

+375
-25
lines changed

9 files changed

+375
-25
lines changed

src/TagLib/Id3v2/EventTimeCode.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using TagLib.Id3v2;
3+
4+
namespace TagLib.Id3v2
5+
{
6+
public class EventTimeCode : ICloneable
7+
{
8+
#region Private Properties
9+
10+
private EventType typeOfEvent;
11+
12+
private int time;
13+
14+
#endregion
15+
16+
#region Public Properties
17+
18+
public EventType TypeOfEvent
19+
{
20+
get { return typeOfEvent; }
21+
set { typeOfEvent = value; }
22+
}
23+
24+
public int Time
25+
{
26+
get { return time; }
27+
set { time = value; }
28+
}
29+
30+
#endregion
31+
32+
#region Public Constructors
33+
34+
public EventTimeCode(EventType typeOfEvent,
35+
int time)
36+
{
37+
this.typeOfEvent = typeOfEvent;
38+
this.time = time;
39+
}
40+
41+
#endregion
42+
43+
#region Static Methods
44+
45+
public static EventTimeCode CreateEmpty()
46+
{
47+
return new EventTimeCode(EventType.Padding, 0);
48+
}
49+
50+
#endregion
51+
52+
#region ICloneable
53+
54+
public object Clone()
55+
{
56+
return new EventTimeCode(typeOfEvent, time);
57+
}
58+
59+
#endregion
60+
}
61+
}

src/TagLib/Id3v2/EventType.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
namespace TagLib.Id3v2
2+
{
3+
public enum EventType
4+
{
5+
Padding = 0x00,
6+
7+
EndOfInitialSilence = 0x01,
8+
9+
IntroStart = 0x02,
10+
11+
MainPartStart = 0x03,
12+
13+
OutroStart = 0x04,
14+
15+
OutroEnd = 0x05,
16+
17+
VerseStart = 0x06,
18+
19+
RefrainStart = 0x07,
20+
21+
InterludeStart = 0x08,
22+
23+
ThemeStart = 0x09,
24+
25+
VariationStart = 0x0A,
26+
27+
KeyChange = 0x0B,
28+
29+
TimeChange = 0x0C,
30+
31+
MomentaryUnwantedNoise = 0x0D,
32+
33+
SustainedNoise = 0x0E,
34+
35+
SustainedNoiseEnd = 0x0F,
36+
37+
IntroEnd = 0x10,
38+
39+
MainPartEnd = 0x11,
40+
41+
VerseEnd = 0x12,
42+
43+
RefrainEnd = 0x13,
44+
45+
ThemeEnd = 0x14,
46+
47+
Profanity = 0x15,
48+
49+
ProfanityEnd = 0x16,
50+
51+
AudioEnd = 0xFD,
52+
53+
AudioFileEnd = 0xFE,
54+
}
55+
}

src/TagLib/Id3v2/FrameFactory.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,10 @@ public static Frame CreateFrame (ByteVector data, File file,
295295
return new UrlLinkFrame(data, position,
296296
header, version);
297297

298+
if (header.FrameId == FrameType.ETCO)
299+
return new EventTimeCodesFrame(data, offset, header,
300+
version);
301+
298302
return new UnknownFrame (data, position, header,
299303
version);
300304
}

src/TagLib/Id3v2/FrameTypes.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,5 +93,6 @@ internal static class FrameType {
9393
public static readonly ReadOnlyByteVector WPAY = "WPAY";
9494
public static readonly ReadOnlyByteVector WPUB = "WPUB";
9595
public static readonly ReadOnlyByteVector WXXX = "WXXX";
96+
public static readonly ReadOnlyByteVector ETCO = "ETCO";
9697
}
9798
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
using System.Collections.Generic;
2+
3+
namespace TagLib.Id3v2
4+
{
5+
public class EventTimeCodesFrame : Frame
6+
{
7+
#region Private Properties
8+
9+
private TimestampFormat timestampFormat;
10+
11+
private List<EventTimeCode> events;
12+
13+
#endregion
14+
15+
#region Constructors
16+
17+
public EventTimeCodesFrame() : base (FrameType.ETCO, 4)
18+
{
19+
Flags = FrameFlags.FileAlterPreservation;
20+
}
21+
22+
public EventTimeCodesFrame(TimestampFormat timestampFormat) : base(FrameType.ETCO, 4)
23+
{
24+
this.timestampFormat = timestampFormat;
25+
Flags = FrameFlags.FileAlterPreservation;
26+
}
27+
28+
public EventTimeCodesFrame(ByteVector data,
29+
byte version) : base (data, version)
30+
{
31+
SetData(data, 0, version, true);
32+
}
33+
34+
public EventTimeCodesFrame(FrameHeader frameHeader) : base (frameHeader)
35+
{
36+
37+
}
38+
39+
public EventTimeCodesFrame(ByteVector data,
40+
int offset,
41+
FrameHeader header,
42+
byte version) : base (header)
43+
{
44+
SetData(data, offset, version, false);
45+
}
46+
47+
#endregion
48+
49+
#region Public Properties
50+
51+
public TimestampFormat TimestampFormat
52+
{
53+
get { return timestampFormat; }
54+
set { timestampFormat = value; }
55+
}
56+
57+
public List<EventTimeCode> Events
58+
{
59+
get { return events; }
60+
set { events = value; }
61+
}
62+
63+
#endregion
64+
65+
#region Public Static Methods
66+
67+
/// <summary>
68+
/// Gets a play count frame from a specified tag, optionally
69+
/// creating it if it does not exist.
70+
/// </summary>
71+
/// <param name="tag">
72+
/// A <see cref="Tag" /> object to search in.
73+
/// </param>
74+
/// <param name="create">
75+
/// A <see cref="bool" /> specifying whether or not to create
76+
/// and add a new frame to the tag if a match is not found.
77+
/// </param>
78+
/// <returns>
79+
/// A <see cref="EventTimeCodesFrame" /> object containing the
80+
/// matching frame, or <see langword="null" /> if a match
81+
/// wasn't found and <paramref name="create" /> is <see
82+
/// langword="false" />.
83+
/// </returns>
84+
public static EventTimeCodesFrame Get(Tag tag, bool create)
85+
{
86+
EventTimeCodesFrame etco;
87+
foreach (Frame frame in tag)
88+
{
89+
etco = frame as EventTimeCodesFrame;
90+
91+
if (etco != null)
92+
return etco;
93+
}
94+
95+
if (!create)
96+
return null;
97+
98+
etco = new EventTimeCodesFrame();
99+
tag.AddFrame(etco);
100+
return etco;
101+
}
102+
103+
#endregion
104+
105+
#region Protected Methods
106+
107+
protected override void ParseFields(ByteVector data, byte version)
108+
{
109+
events = new List<EventTimeCode>();
110+
timestampFormat = (TimestampFormat)data.Data[0];
111+
112+
var incomingEventsData = data.Mid(1);
113+
for (var i = 0; i < incomingEventsData.Count; i++)
114+
{
115+
var eventType = (EventType)incomingEventsData.Data[1];
116+
i++;
117+
118+
var timestampData = new ByteVector(incomingEventsData.Data[i],
119+
incomingEventsData.Data[i+1],
120+
incomingEventsData.Data[i+2],
121+
incomingEventsData.Data[i+3]);
122+
123+
i += 3;
124+
125+
var timestamp = timestampData.ToInt();
126+
127+
events.Add(new EventTimeCode(eventType, timestamp));
128+
}
129+
}
130+
131+
protected override ByteVector RenderFields(byte version)
132+
{
133+
var data = new List<byte>();
134+
data.Add((byte)timestampFormat);
135+
136+
foreach (var @event in events)
137+
{
138+
data.Add((byte)@event.TypeOfEvent);
139+
140+
var timeData = ByteVector.FromInt(@event.Time);
141+
data.AddRange(timeData.Data);
142+
}
143+
144+
return new ByteVector(data.ToArray());
145+
}
146+
147+
#endregion
148+
149+
#region ICloneable
150+
151+
/// <summary>
152+
/// Creates a deep copy of the current instance.
153+
/// </summary>
154+
/// <returns>
155+
/// A new <see cref="Frame" /> object identical to the
156+
/// current instance.
157+
/// </returns>
158+
public override Frame Clone()
159+
{
160+
var frame = new EventTimeCodesFrame(header);
161+
frame.timestampFormat = timestampFormat;
162+
frame.events = events.ConvertAll(item => (EventTimeCode)item.Clone());
163+
return frame;
164+
}
165+
166+
#endregion
167+
}
168+
}

src/TagLib/Id3v2/Frames/SynchronizedLyricsFrame.cs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,29 +27,6 @@
2727

2828
namespace TagLib.Id3v2
2929
{
30-
/// <summary>
31-
/// Specifies the timestamp format used by a <see
32-
/// cref="SynchronisedLyricsFrame" />.
33-
/// </summary>
34-
public enum TimestampFormat
35-
{
36-
/// <summary>
37-
/// The timestamp is of unknown format.
38-
/// </summary>
39-
Unknown = 0x00,
40-
41-
/// <summary>
42-
/// The timestamp represents the number of MPEG frames since
43-
/// the beginning of the audio stream.
44-
/// </summary>
45-
AbsoluteMpegFrames = 0x01,
46-
47-
/// <summary>
48-
/// The timestamp represents the number of milliseconds since
49-
/// the beginning of the audio stream.
50-
/// </summary>
51-
AbsoluteMilliseconds = 0x02
52-
}
5330

5431
/// <summary>
5532
/// Specifies the type of text contained in a <see

src/TagLib/Id3v2/TimestampFormat.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// SynchronizedLyricsFrame.cs:
3+
//
4+
// Author:
5+
// Brian Nickel ([email protected])
6+
//
7+
// Copyright (C) 2007 Brian Nickel
8+
//
9+
// This library is free software; you can redistribute it and/or modify
10+
// it under the terms of the GNU Lesser General Public License version
11+
// 2.1 as published by the Free Software Foundation.
12+
//
13+
// This library is distributed in the hope that it will be useful, but
14+
// WITHOUT ANY WARRANTY; without even the implied warranty of
15+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
// Lesser General Public License for more details.
17+
//
18+
// You should have received a copy of the GNU Lesser General Public
19+
// License along with this library; if not, write to the Free Software
20+
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21+
// USA
22+
//
23+
24+
using System;
25+
using System.Collections.Generic;
26+
using System.Text;
27+
28+
namespace TagLib.Id3v2
29+
{
30+
/// <summary>
31+
/// Specifies the timestamp format used by a <see
32+
/// cref="SynchronisedLyricsFrame" />.
33+
/// </summary>
34+
public enum TimestampFormat
35+
{
36+
/// <summary>
37+
/// The timestamp is of unknown format.
38+
/// </summary>
39+
Unknown = 0x00,
40+
41+
/// <summary>
42+
/// The timestamp represents the number of MPEG frames since
43+
/// the beginning of the audio stream.
44+
/// </summary>
45+
AbsoluteMpegFrames = 0x01,
46+
47+
/// <summary>
48+
/// The timestamp represents the number of milliseconds since
49+
/// the beginning of the audio stream.
50+
/// </summary>
51+
AbsoluteMilliseconds = 0x02
52+
}
53+
}

0 commit comments

Comments
 (0)