1 package net.sf.jack4j.util.file.wav;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.nio.ByteBuffer;
24 import java.nio.ByteOrder;
25 import java.nio.channels.FileChannel;
26
27 import net.sf.jack4j.util.IntSampleConverter;
28
29
30
31
32
33
34
35
36
37
38
39 public class WavFileReader {
40
41 private static final int OPTIMAL_BUFFER_SIZE = 4096;
42
43 private FileChannel fileChannel;
44 private IntSampleConverter sampleConverter;
45 private int bitsPerSample;
46 private int samplesPerSecond;
47 private int channelCount;
48 private int blockAlign;
49 private int avgBytesPerSecond;
50 private long dataPosition;
51 private long dataSize;
52 private ByteBuffer buffer;
53 private int bufferSamples;
54 private long currentSample;
55 private long totalSamples;
56
57 public WavFileReader(FileChannel fileChannel) throws IOException {
58 this.fileChannel = fileChannel;
59
60 parseHeaders();
61
62 bufferSamples = OPTIMAL_BUFFER_SIZE / blockAlign;
63 if (bufferSamples == 0) {
64 bufferSamples = 1;
65 }
66 this.buffer = ByteBuffer.allocateDirect(bufferSamples * blockAlign);
67
68 initialSeek();
69 }
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 public int readSamples(ByteBuffer[] channelBuffers, int nframes) throws IOException {
86 int totalRead = 0;
87 int remainsToRead = nframes;
88
89 while (remainsToRead > 0) {
90 long remainingSamples = totalSamples - currentSample;
91 int toRead = (remainsToRead < remainingSamples) ? remainsToRead : (int) remainingSamples;
92 if (toRead == 0) {
93 break;
94 } else if (toRead > bufferSamples) {
95 toRead = bufferSamples;
96 }
97
98 buffer.clear();
99 buffer.limit(toRead * blockAlign);
100 int bytesRead = fileChannel.read(buffer);
101 if (bytesRead == -1) {
102 break;
103 }
104 buffer.flip();
105 int samplesRead = bytesRead / blockAlign;
106
107 for (int sampleIdx = 0; sampleIdx < samplesRead; sampleIdx++) {
108 for (int channelIdx = 0; channelIdx < channelCount; channelIdx++) {
109 float sample = sampleConverter.intSampleToFloat(buffer);
110
111 if (channelIdx >= channelBuffers.length) {
112 continue;
113 }
114 ByteBuffer channelBuffer = channelBuffers[channelIdx];
115 if (channelBuffer == null) {
116 continue;
117 }
118
119 channelBuffer.putFloat(sample);
120 }
121 for (int channelIdx = channelCount; channelIdx < channelBuffers.length; channelIdx++) {
122 ByteBuffer channelBuffer = channelBuffers[channelIdx];
123 if (channelBuffer == null) {
124 continue;
125 }
126 channelBuffer.putFloat(0.0f);
127 }
128 }
129
130 totalRead += samplesRead;
131 currentSample += samplesRead;
132 remainsToRead -= samplesRead;
133 }
134
135 return totalRead;
136 }
137
138 private void parseHeaders() throws IOException {
139 ByteBuffer buffer = ByteBuffer.allocate(1024).order(ByteOrder.LITTLE_ENDIAN);
140
141 fileChannel.position(0);
142 readBytesToBuffer(buffer, WavFormat.AIFF_HEADER_LENGTH);
143 buffer.flip();
144
145 if (buffer.getInt() != WavFormat.RIFF_GROUP_ID) {
146 throw new IOException("Wrong AIFF groupId");
147 }
148
149 long totalDataSize = 0xffffffffL & (buffer.getInt());
150 long maxPosition = buffer.position() + totalDataSize;
151
152 if (buffer.getInt() != WavFormat.WAVE_FILE_TYPE) {
153 throw new IOException("Wrong RIFF file type");
154 }
155
156 while (fileChannel.position() < maxPosition) {
157 readBytesToBuffer(buffer, WavFormat.CHUNK_HEADER_LENGTH);
158 buffer.flip();
159
160 int chunkType = buffer.getInt();
161 long chunkSize = 0xffffffffL & (buffer.getInt());
162 long paddedChunkSize = (chunkSize % 2 == 0) ? chunkSize : chunkSize + 1;
163
164 long chunkStartPosition = fileChannel.position();
165
166 if (chunkType == WavFormat.FMT_CHUNK_TYPE) {
167 readBytesToBuffer(buffer, WavFormat.FORMAT_FIELDS_SIZE);
168 buffer.flip();
169
170 int formatTag = buffer.getShort();
171 if (formatTag != WavFormat.REQUIRED_FORMAT_TAG) {
172 throw new UnsupportedOperationException("Unsupported WAV format #" + formatTag);
173 }
174 channelCount = buffer.getShort();
175 samplesPerSecond = buffer.getInt();
176 avgBytesPerSecond = buffer.getInt();
177 blockAlign = buffer.getShort();
178 bitsPerSample = buffer.getShort();
179
180 sampleConverter = IntSampleConverter.createIntSampleConverter(bitsPerSample,
181 !(bitsPerSample <= 8),
182 false);
183 } else if (chunkType == WavFormat.DATA_CHUNK_TYPE) {
184 dataPosition = chunkStartPosition;
185 dataSize = chunkSize;
186 }
187
188 fileChannel.position(chunkStartPosition + paddedChunkSize);
189 }
190 }
191
192 private void initialSeek() throws IOException {
193 fileChannel.position(dataPosition);
194 currentSample = 0;
195 totalSamples = dataSize / blockAlign;
196 }
197
198 private void readBytesToBuffer(ByteBuffer buffer, int size) throws IOException {
199 buffer.clear();
200 buffer.limit(size);
201 if (fileChannel.read(buffer) != size) {
202 throw new IOException("Can't read " + size + " bytes from WAV file");
203 }
204 }
205
206
207
208
209 public int getBitsPerSample() {
210 return bitsPerSample;
211 }
212
213
214
215
216 public int getSamplesPerSecond() {
217 return samplesPerSecond;
218 }
219
220
221
222
223 public int getChannelCount() {
224 return channelCount;
225 }
226
227
228
229
230 public int getBlockAlign() {
231 return blockAlign;
232 }
233
234
235
236
237 public int getAvgBytesPerSecond() {
238 return avgBytesPerSecond;
239 }
240 }