Add parser for FLAC metadata.
[sonitus.git] / src / main / java / net / pterodactylus / sonitus / io / flac / Data.java
1 /*
2  * Sonitus - Data.java - Copyright © 2013 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sonitus.io.flac;
19
20 import static com.google.common.io.ByteStreams.readFully;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24
25 /**
26  * Accessor type that can parse the contents of a {@link MetadataBlock}.
27  *
28  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
29  */
30 public class Data {
31
32         /** The content of the metadata block. */
33         private final byte[] content;
34
35         /**
36          * Creates a new data accessor.
37          *
38          * @param content
39          *              The content of the metadata block
40          */
41         public Data(byte[] content) {
42                 this.content = content;
43         }
44
45         //
46         // ACCESSORS
47         //
48
49         /**
50          * Returns the content of this metadata block.
51          *
52          * @return The content of the metadata block
53          */
54         public byte[] content() {
55                 return content;
56         }
57
58         //
59         // SUBCLASS METHODS
60         //
61
62         /**
63          * Parses the given number of bits from the content of this metadata block,
64          * starting at the given byte offset and bit offset (bit 0 being the most
65          * significant bit).
66          *
67          * @param byteOffset
68          *              The byte offset at which to start reading
69          * @param bitOffset
70          *              The bit offset at which to start reading
71          * @param numberOfBits
72          *              The number of bits to parse (should be <= 64)
73          * @return The parsed bits
74          */
75         protected long parseBits(int byteOffset, int bitOffset, int numberOfBits) {
76                 long value = 0;
77                 int currentByteOffset = byteOffset;
78                 int currentBitOffset = bitOffset;
79                 int bitsRemaining = numberOfBits;
80
81                 while (bitsRemaining > 0) {
82                         value = (value << Math.min(8, bitsRemaining)) | ((content[currentByteOffset] & (0xff >>> currentBitOffset)) >> (8 - currentBitOffset - Math.min(bitsRemaining, 8 - currentBitOffset)));
83                         bitsRemaining -= Math.min(bitsRemaining, 8 - currentBitOffset);
84                         currentBitOffset = 0;
85                         currentByteOffset++;
86                 }
87
88                 return value;
89         }
90
91         //
92         // STATIC METHODS
93         //
94
95         /**
96          * Creates a new data accessor from the given input stream.
97          *
98          * @param inputStream
99          *              The input stream to read the contents of the metadata block from
100          * @param blockType
101          *              The type of the metadata block
102          * @param length
103          *              The length of the metadata block
104          * @return The parsed metadata block
105          * @throws IOException
106          *              if an I/O error occurs
107          */
108         public static Data parse(InputStream inputStream, BlockType blockType, int length) throws IOException {
109                 byte[] buffer = new byte[length];
110                 readFully(inputStream, buffer);
111                 return blockType.createData(buffer);
112         }
113
114 }