1 | package org.farng.mp3.id3; |
2 | |
3 | import org.farng.mp3.InvalidTagException; |
4 | import org.farng.mp3.object.AbstractMP3Object; |
5 | import org.farng.mp3.object.ObjectGroupRepeated; |
6 | import org.farng.mp3.object.ObjectNumberFixedLength; |
7 | import org.farng.mp3.object.ObjectNumberHashMap; |
8 | |
9 | import java.io.IOException; |
10 | import java.io.RandomAccessFile; |
11 | |
12 | /** |
13 | * <h3>4.5. Event timing codes</h3> |
14 | * <p/> |
15 | * <p> This frame allows synchronisation with key events in the audio. The<br> header is:</p> |
16 | * <p/> |
17 | * <p> <Header for 'Event timing codes', ID: "ETCO"><br> |
18 | * <p/> |
19 | * Time stamp format $xx</p> |
20 | * <p/> |
21 | * <p> Where time stamp format is:</p> |
22 | * <p/> |
23 | * <p> $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit<br> |
24 | * $02 Absolute time, 32 bit sized, using milliseconds as unit</p> |
25 | * <p/> |
26 | * <p> Absolute time means that every stamp contains the time from the<br> beginning of the |
27 | * file.</p> |
28 | * <p/> |
29 | * <p> Followed by a list of key events in the following format:</p> |
30 | * <p/> |
31 | * <p> Type of event $xx<br> |
32 | * <p/> |
33 | * Time stamp $xx (xx ...)</p> |
34 | * <p/> |
35 | * <p> The 'Time stamp' is set to zero if directly at the beginning of the<br> sound or after |
36 | * the previous event. All events MUST be sorted in<br> chronological order. The type of event is as |
37 | * follows:</p> |
38 | * <p/> |
39 | * <p> $00 padding (has no meaning)<br> $01 end of initial |
40 | * silence<br> $02 intro start<br> |
41 | * <p/> |
42 | * $03 main part start<br> $04 outro start<br> |
43 | * $05 outro end<br> |
44 | * <p/> |
45 | * $06 verse start<br> $07 refrain start<br> |
46 | * $08 interlude start<br> |
47 | * <p/> |
48 | * $09 theme start<br> $0A variation start<br> |
49 | * $0B key change<br> |
50 | * <p/> |
51 | * $0C time change<br> $0D momentary unwanted noise (Snap, |
52 | * Crackle & Pop)<br> $0E sustained noise<br> |
53 | * <p/> |
54 | * $0F sustained noise end<br> $10 intro end<br> |
55 | * $11 main part end<br> |
56 | * <p/> |
57 | * $12 verse end<br> $13 refrain end<br> |
58 | * $14 theme end<br> |
59 | * <p/> |
60 | * $15 profanity<br> $16 profanity end</p> |
61 | * <p/> |
62 | * <p> $17-$DF reserved for future use</p> |
63 | * <p/> |
64 | * <p> $E0-$EF not predefined synch 0-F</p> |
65 | * <p/> |
66 | * <p> $F0-$FC reserved for future use</p> |
67 | * <p/> |
68 | * <p> $FD audio end (start of silence)<br> |
69 | * <p/> |
70 | * $FE audio file ends<br> $FF one more byte of events |
71 | * follows (all the following bytes with<br> the value $FF have |
72 | * the same function)</p> |
73 | * <p/> |
74 | * <p> Terminating the start events such as "intro start" is OPTIONAL. The<br> 'Not |
75 | * predefined synch's ($E0-EF) are for user events. You might want<br> to synchronise your music to |
76 | * something, like setting off an explosion<br> on-stage, activating a screensaver etc.</p> |
77 | * <p/> |
78 | * <p> There may only be one "ETCO" frame in each tag.<br> </p> |
79 | * |
80 | * @author Eric Farng |
81 | * @version $Revision: 1.4 $ |
82 | */ |
83 | public class FrameBodyETCO extends AbstractID3v2FrameBody { |
84 | |
85 | /** |
86 | * Creates a new FrameBodyETCO object. |
87 | */ |
88 | public FrameBodyETCO() { |
89 | super(); |
90 | } |
91 | |
92 | /** |
93 | * Creates a new FrameBodyETCO object. |
94 | */ |
95 | public FrameBodyETCO(final FrameBodyETCO body) { |
96 | super(body); |
97 | } |
98 | |
99 | /** |
100 | * Creates a new FrameBodyETCO object. |
101 | */ |
102 | public FrameBodyETCO(final byte timeStampFormat, final byte event, final int timeStamp) { |
103 | setObject("Time Stamp Format", new Long(timeStampFormat)); |
104 | this.addGroup(event, timeStamp); |
105 | } |
106 | |
107 | /** |
108 | * Creates a new FrameBodyETCO object. |
109 | */ |
110 | public FrameBodyETCO(final RandomAccessFile file) throws IOException, InvalidTagException { |
111 | this.read(file); |
112 | } |
113 | |
114 | public String getIdentifier() { |
115 | return "ETCO" + ((char) 0) + getOwner(); |
116 | } |
117 | |
118 | public String getOwner() { |
119 | return (String) getObject("Owner"); |
120 | } |
121 | |
122 | public void getOwner(final String description) { |
123 | setObject("Owner", description); |
124 | } |
125 | |
126 | public void addGroup(final byte event, final int timeStamp) { |
127 | final ObjectGroupRepeated group = (ObjectGroupRepeated) this.getObject("Data"); |
128 | final AbstractMP3Object ev = new ObjectNumberHashMap("Type Of Event", 1); |
129 | final AbstractMP3Object ts = new ObjectNumberFixedLength("Time Stamp", 4); |
130 | group.addObject(ev); |
131 | group.addObject(ts); |
132 | setObject("Data", group); |
133 | } |
134 | |
135 | protected void setupObjectList() { |
136 | appendToObjectList(new ObjectNumberHashMap("Time Stamp Format", 1)); |
137 | final ObjectGroupRepeated group = new ObjectGroupRepeated("Data"); |
138 | group.addProperty(new ObjectNumberHashMap("Type Of Event", 1)); |
139 | group.addProperty(new ObjectNumberFixedLength("Time Stamp", 4)); |
140 | appendToObjectList(group); |
141 | } |
142 | } |