1 | package org.farng.mp3.id3; |
2 | |
3 | import org.farng.mp3.InvalidTagException; |
4 | |
5 | import java.io.IOException; |
6 | import java.io.RandomAccessFile; |
7 | |
8 | /** |
9 | * <h3>4.12.Relative volume adjustment</h3> |
10 | * <p/> |
11 | * <p class=t> This is a more subjective function than the previous ones. It allows the user to say how much he wants to |
12 | * increase/decrease the volume on each channel while the file is played. The purpose is to be able to align all files |
13 | * to a reference volume, so that you don't have to change the volume constantly. This frame may also be used to balance |
14 | * adjust the audio. If the volume peak levels are known then this could be described with the 'Peak volume right' and |
15 | * 'Peak volume left' field. If Peakvolume is not known these fields could be left zeroed or, if no other data follows, |
16 | * be completely omitted. There may only be one "RVAD" frame in each tag. |
17 | * <p/> |
18 | * </p> |
19 | * <p/> |
20 | * <p><center> <table border=0> <tr><td colspan=2><Header for 'Relative volume adjustment', ID: "RVAD"></td></tr> |
21 | * <tr><td>Increment/decrement</td><td>%00xxxxxx</td></tr> <tr><td>Bits used for volume descr.</td><td>$xx</td></tr> |
22 | * <tr><td>Relative volume change, right</td><td>$xx xx (xx ...)</td></tr> |
23 | * <p/> |
24 | * <tr><td>Relative volume change, left</td><td>$xx xx (xx ...)</td></tr> <tr><td>Peak volume right</td><td>$xx xx (xx |
25 | * ...)</td></tr> <tr><td>Peak volume left</td><td>$xx xx (xx ...)</td></tr> </table> </center> |
26 | * <p/> |
27 | * <p class=t> |
28 | * <p/> |
29 | * In the increment/decrement field bit 0 is used to indicate the right channel and bit 1 is used to indicate the left |
30 | * channel. 1 is increment and 0 is decrement. </p> |
31 | * <p/> |
32 | * <p class=t> The 'bits used for volume description' field is normally $10 (16 bits) for <a href="#MPEG">MPEG</a> 2 |
33 | * layer I, II and III and MPEG 2.5. This value may not be $00. The volume is always represented with whole bytes, |
34 | * padded in the beginning (highest bits) when 'bits used for volume description' is not a multiple of eight. </p> |
35 | * <p/> |
36 | * <p class=t> This datablock is then optionally followed by a volume definition for the left and right back channels. |
37 | * If this information is appended to the frame the first two channels will be treated as front channels. In the |
38 | * increment/decrement field bit 2 is used to indicate the right back channel and bit 3 for the left back channel. </p> |
39 | * <p/> |
40 | * <p><center> <table border=0> <tr><td nowrap>Relative volume change, right back</td><td>$xx xx (xx ...)</td></tr> |
41 | * <tr><td>Relative volume change, left back</td><td>$xx xx (xx ...)</td></tr> <tr><td>Peak volume right |
42 | * back</td><td>$xx xx (xx ...)</td></tr> <tr><td>Peak volume left back</td><td>$xx xx (xx ...)</td></tr> |
43 | * <p/> |
44 | * </table> </center> |
45 | * <p/> |
46 | * <p class=t> If the center channel adjustment is present the following is appended to the existing frame, after the |
47 | * left and right back channels. The center channel is represented by bit 4 in the increase/decrease field. </p> |
48 | * <p/> |
49 | * <p><center> <table border=0> <tr><td nowrap>Relative volume change, center</td><td>$xx xx (xx ...)</td></tr> |
50 | * <tr><td>Peak volume center</td><td>$xx xx (xx ...)</td></tr> |
51 | * <p/> |
52 | * </table> </center> |
53 | * <p/> |
54 | * <p class=t> If the bass channel adjustment is present the following is appended to the existing frame, after the |
55 | * center channel. The bass channel is represented by bit 5 in the increase/decrease field. </p> |
56 | * <p/> |
57 | * <p><center> <table border=0> <tr><td nowrap>Relative volume change, bass</td><td>$xx xx (xx ...)</td></tr> |
58 | * <tr><td>Peak volume bass</td><td>$xx xx (xx ...)</td></tr> |
59 | * <p/> |
60 | * </table> </center> |
61 | * |
62 | * @author Eric Farng` |
63 | * @version $Revision: 1.5 $ |
64 | */ |
65 | public class FrameBodyRVAD extends AbstractID3v2FrameBody { |
66 | |
67 | byte bytesUsed = 16; |
68 | byte increment = 0; |
69 | long peakBass = 0; |
70 | long peakCenter = 0; |
71 | long peakLeft = 0; |
72 | long peakLeftBack = 0; |
73 | long peakRight = 0; |
74 | long peakRightBack = 0; |
75 | long relativeBass = 0; |
76 | long relativeCenter = 0; |
77 | long relativeLeft = 0; |
78 | long relativeLeftBack = 0; |
79 | long relativeRight = 0; |
80 | long relativeRightBack = 0; |
81 | |
82 | /** |
83 | * Creates a new FrameBodyRVAD object. |
84 | */ |
85 | public FrameBodyRVAD() { |
86 | super(); |
87 | } |
88 | |
89 | /** |
90 | * Creates a new FrameBodyRVAD object. |
91 | */ |
92 | public FrameBodyRVAD(final FrameBodyRVAD copyObject) { |
93 | super(copyObject); |
94 | this.bytesUsed = copyObject.bytesUsed; |
95 | this.increment = copyObject.increment; |
96 | this.peakBass = copyObject.peakBass; |
97 | this.peakCenter = copyObject.peakCenter; |
98 | this.peakLeft = copyObject.peakLeft; |
99 | this.peakLeftBack = copyObject.peakLeftBack; |
100 | this.peakRight = copyObject.peakRight; |
101 | this.peakRightBack = copyObject.peakRightBack; |
102 | this.relativeBass = copyObject.relativeBass; |
103 | this.relativeCenter = copyObject.relativeCenter; |
104 | this.relativeLeft = copyObject.relativeLeft; |
105 | this.relativeLeftBack = copyObject.relativeLeftBack; |
106 | this.relativeRight = copyObject.relativeRight; |
107 | this.relativeRightBack = copyObject.relativeRightBack; |
108 | } |
109 | |
110 | /** |
111 | * Creates a new FrameBodyRVAD object. |
112 | */ |
113 | public FrameBodyRVAD(final byte increment, |
114 | final byte bitsUsed, |
115 | final long relativeRight, |
116 | final long relativeLeft, |
117 | final long peakRight, |
118 | final long peakLeft, |
119 | final long relativeRightBack, |
120 | final long relativeLeftBack, |
121 | final long peakRightBack, |
122 | final long peakLeftBack, |
123 | final long relativeCenter, |
124 | final long peakCenter, |
125 | final long relativeBass, |
126 | final long peakBass) { |
127 | this.increment = increment; |
128 | this.bytesUsed = (byte) (((bitsUsed - 1) / 8) + 1); // convert to bytes. |
129 | this.relativeRight = relativeRight; |
130 | this.relativeLeft = relativeLeft; |
131 | this.peakRight = peakRight; |
132 | this.peakLeft = peakLeft; |
133 | this.relativeRightBack = relativeRightBack; |
134 | this.relativeLeftBack = relativeLeftBack; |
135 | this.peakRightBack = peakRightBack; |
136 | this.peakLeftBack = peakLeftBack; |
137 | this.relativeCenter = relativeCenter; |
138 | this.peakCenter = peakCenter; |
139 | this.relativeBass = relativeBass; |
140 | this.peakBass = peakBass; |
141 | } |
142 | |
143 | /** |
144 | * Creates a new FrameBodyRVAD object. |
145 | */ |
146 | public FrameBodyRVAD(final RandomAccessFile file) throws IOException, InvalidTagException { |
147 | this.read(file); |
148 | } |
149 | |
150 | public String getIdentifier() { |
151 | return "RVAD"; |
152 | } |
153 | |
154 | public int getSize() { |
155 | int size = 2 + (this.bytesUsed * 4); |
156 | if ((this.relativeRightBack != 0) || |
157 | (this.relativeLeftBack != 0) || |
158 | (this.peakRightBack != 0) || |
159 | (this.peakLeftBack != 0)) { |
160 | size += (this.bytesUsed * 4); |
161 | } |
162 | if ((this.relativeCenter != 0) || (this.peakCenter != 0)) { |
163 | size += (this.bytesUsed * 2); |
164 | } |
165 | if ((this.relativeBass != 0) || (this.peakBass != 0)) { |
166 | size += (this.bytesUsed * 2); |
167 | } |
168 | return size; |
169 | } |
170 | |
171 | /** |
172 | * This method is not yet supported. |
173 | * |
174 | * @throws java.lang.UnsupportedOperationException |
175 | * This method is not yet supported |
176 | */ |
177 | public void equals() { |
178 | //todo Implement this java.lang.Object method |
179 | throw new java.lang.UnsupportedOperationException("Method equals() not yet implemented."); |
180 | } |
181 | |
182 | protected void setupObjectList() { |
183 | throw new UnsupportedOperationException(); |
184 | } |
185 | |
186 | public void read(final RandomAccessFile file) throws IOException, InvalidTagException { |
187 | final int size; |
188 | int offset = 0; |
189 | final byte[] buffer; |
190 | size = readHeader(file); |
191 | buffer = new byte[size]; |
192 | file.read(buffer); |
193 | this.increment = buffer[offset++]; |
194 | this.bytesUsed = (byte) (((buffer[offset++] - 1) / 8 * 8) + 1); |
195 | for (int i = 0; i < this.bytesUsed; i++) { |
196 | this.relativeRight = (this.relativeRight << 8) + buffer[i + offset]; |
197 | } |
198 | offset += this.bytesUsed; |
199 | for (int i = 0; i < this.bytesUsed; i++) { |
200 | this.relativeLeft = (this.relativeLeft << 8) + buffer[i + offset]; |
201 | } |
202 | offset += this.bytesUsed; |
203 | for (int i = 0; i < this.bytesUsed; i++) { |
204 | this.peakRight = (this.peakRight << 8) + buffer[i + offset]; |
205 | } |
206 | offset += this.bytesUsed; |
207 | for (int i = 0; i < this.bytesUsed; i++) { |
208 | this.peakLeft = (this.peakLeft << 8) + buffer[i + offset]; |
209 | } |
210 | offset += this.bytesUsed; |
211 | if (size > (2 + (this.bytesUsed * 4))) { |
212 | for (int i = 0; i < this.bytesUsed; i++) { |
213 | this.relativeRightBack = (this.relativeRightBack << 8) + buffer[i + offset]; |
214 | } |
215 | offset += this.bytesUsed; |
216 | for (int i = 0; i < this.bytesUsed; i++) { |
217 | this.relativeLeftBack = (this.relativeLeftBack << 8) + buffer[i + offset]; |
218 | } |
219 | offset += this.bytesUsed; |
220 | for (int i = 0; i < this.bytesUsed; i++) { |
221 | this.peakRightBack = (this.peakRightBack << 8) + buffer[i + offset]; |
222 | } |
223 | offset += this.bytesUsed; |
224 | for (int i = 0; i < this.bytesUsed; i++) { |
225 | this.peakLeftBack = (this.peakLeftBack << 8) + buffer[i + offset]; |
226 | } |
227 | offset += this.bytesUsed; |
228 | } |
229 | if (size > (2 + (this.bytesUsed * 8))) { |
230 | for (int i = 0; i < this.bytesUsed; i++) { |
231 | this.relativeCenter = (this.relativeCenter << 8) + buffer[i + offset]; |
232 | } |
233 | offset += this.bytesUsed; |
234 | for (int i = 0; i < this.bytesUsed; i++) { |
235 | this.peakCenter = (this.peakCenter << 8) + buffer[i + offset]; |
236 | } |
237 | offset += this.bytesUsed; |
238 | } |
239 | if (size > (2 + (this.bytesUsed * 10))) { |
240 | for (int i = 0; i < this.bytesUsed; i++) { |
241 | this.relativeBass = (this.relativeBass << 8) + buffer[i + offset]; |
242 | } |
243 | offset += this.bytesUsed; |
244 | for (int i = 0; i < this.bytesUsed; i++) { |
245 | this.peakBass = (this.peakBass << 8) + buffer[i + offset]; |
246 | } |
247 | offset += this.bytesUsed; |
248 | } |
249 | } |
250 | |
251 | public String toString() { |
252 | return this |
253 | .increment + |
254 | " " + |
255 | (this.bytesUsed * 8) + |
256 | " " + |
257 | // convert back to bits |
258 | this |
259 | .relativeRight + |
260 | " " + |
261 | this |
262 | .relativeLeft + |
263 | " " + |
264 | this |
265 | .peakRight + |
266 | " " + |
267 | this |
268 | .peakLeft + |
269 | " " + |
270 | this |
271 | .relativeRightBack + |
272 | " " + |
273 | this |
274 | .relativeLeftBack + |
275 | " " + |
276 | this |
277 | .peakRightBack + |
278 | " " + |
279 | this |
280 | .peakLeftBack + |
281 | " " + |
282 | this |
283 | .relativeCenter + |
284 | " " + |
285 | this |
286 | .peakCenter + |
287 | " " + |
288 | this |
289 | .relativeBass + |
290 | " " + |
291 | this |
292 | .peakBass; |
293 | } |
294 | |
295 | public void write(final RandomAccessFile file) throws IOException { |
296 | final byte[] buffer; |
297 | int offset = 0; |
298 | writeHeader(file, this.getSize()); |
299 | buffer = new byte[this.getSize()]; |
300 | buffer[offset++] = this.increment; |
301 | buffer[offset++] = this.bytesUsed; |
302 | for (int i = 0; i < this.bytesUsed; i++) { |
303 | buffer[i + offset] = (byte) (this.relativeRight >> ((this.bytesUsed - i - 1) * 8)); |
304 | } |
305 | offset += this.bytesUsed; |
306 | for (int i = 0; i < this.bytesUsed; i++) { |
307 | buffer[i + offset] = (byte) (this.relativeLeft >> ((this.bytesUsed - i - 1) * 8)); |
308 | } |
309 | offset += this.bytesUsed; |
310 | for (int i = 0; i < this.bytesUsed; i++) { |
311 | buffer[i + offset] = (byte) (this.peakRight >> ((this.bytesUsed - i - 1) * 8)); |
312 | } |
313 | offset += this.bytesUsed; |
314 | for (int i = 0; i < this.bytesUsed; i++) { |
315 | buffer[i + offset] = (byte) (this.peakLeft >> ((this.bytesUsed - i - 1) * 8)); |
316 | } |
317 | offset += this.bytesUsed; |
318 | if ((this.relativeRightBack != 0) || |
319 | (this.relativeLeftBack != 0) || |
320 | (this.peakRightBack != 0) || |
321 | (this.peakLeftBack != 0)) { |
322 | for (int i = 0; i < this.bytesUsed; i++) { |
323 | buffer[i + offset] = (byte) (this.relativeRightBack >> ((this.bytesUsed - i - 1) * 8)); |
324 | } |
325 | offset += this.bytesUsed; |
326 | for (int i = 0; i < this.bytesUsed; i++) { |
327 | buffer[i + offset] = (byte) (this.relativeLeftBack >> ((this.bytesUsed - i - 1) * 8)); |
328 | } |
329 | offset += this.bytesUsed; |
330 | for (int i = 0; i < this.bytesUsed; i++) { |
331 | buffer[i + offset] = (byte) (this.peakRightBack >> ((this.bytesUsed - i - 1) * 8)); |
332 | } |
333 | offset += this.bytesUsed; |
334 | for (int i = 0; i < this.bytesUsed; i++) { |
335 | buffer[i + offset] = (byte) (this.peakLeftBack >> ((this.bytesUsed - i - 1) * 8)); |
336 | } |
337 | offset += this.bytesUsed; |
338 | } |
339 | if ((this.relativeCenter != 0) || (this.peakCenter != 0)) { |
340 | for (int i = 0; i < this.bytesUsed; i++) { |
341 | buffer[i + offset] = (byte) (this.relativeCenter >> ((this.bytesUsed - i - 1) * 8)); |
342 | } |
343 | offset += this.bytesUsed; |
344 | for (int i = 0; i < this.bytesUsed; i++) { |
345 | buffer[i + offset] = (byte) (this.peakCenter >> ((this.bytesUsed - i - 1) * 8)); |
346 | } |
347 | offset += this.bytesUsed; |
348 | } |
349 | if ((this.relativeBass != 0) || (this.peakBass != 0)) { |
350 | for (int i = 0; i < this.bytesUsed; i++) { |
351 | buffer[i + offset] = (byte) (this.relativeBass >> ((this.bytesUsed - i - 1) * 8)); |
352 | } |
353 | offset += this.bytesUsed; |
354 | for (int i = 0; i < this.bytesUsed; i++) { |
355 | buffer[i + offset] = (byte) (this.peakBass >> ((this.bytesUsed - i - 1) * 8)); |
356 | } |
357 | offset += this.bytesUsed; |
358 | } |
359 | file.write(buffer); |
360 | } |
361 | } |