1 package com.hammurapi.jcapture;
2 
3 // This file was taken from http://www.labbookpages.co.uk/audio/javaWavFiles.html
4 
5 // Wav file IO class
6 // A.Greensted
7 // http://www.labbookpages.co.uk
8 
9 // File format is based on the information from
10 // http://www.sonicspot.com/guide/wavefiles.html
11 // http://www.blitter.com/~russtopia/MIDI/~jglatt/tech/wave.htm
12 
13 // Version 1.0
14 
15 import java.io.*;
16 
17 public class WavFile {
18 	private enum IOState {
19 		READING, WRITING, CLOSED
20 	};
21 
22 	private final static int BUFFER_SIZE = 4096;
23 
24 	private final static int FMT_CHUNK_ID = 0x20746D66;
25 	private final static int DATA_CHUNK_ID = 0x61746164;
26 	private final static int RIFF_CHUNK_ID = 0x46464952;
27 	private final static int RIFF_TYPE_ID = 0x45564157;
28 
29 	private File file; // File that will be read from or written to
30 	private IOState ioState; // Specifies the IO State of the Wav File (used for
31 								// snaity checking)
32 	private int bytesPerSample; // Number of bytes required to store a single
33 								// sample
34 	private long numFrames; // Number of frames within the data section
35 	private FileOutputStream oStream; // Output stream used for writting data
36 	private FileInputStream iStream; // Input stream used for reading data
37 	private double floatScale; // Scaling factor used for int <-> float
38 								// conversion
39 	private double floatOffset; // Offset factor used for int <-> float
40 								// conversion
41 	private boolean wordAlignAdjust; // Specify if an extra byte at the end of
42 										// the data chunk is required for word
43 										// alignment
44 
45 	// Wav Header
46 	private int numChannels; // 2 bytes unsigned, 0x0001 (1) to 0xFFFF (65,535)
47 	private long sampleRate; // 4 bytes unsigned, 0x00000001 (1) to 0xFFFFFFFF
48 								// (4,294,967,295)
49 								// Although a java int is 4 bytes, it is signed,
50 								// so need to use a long
51 	private int blockAlign; // 2 bytes unsigned, 0x0001 (1) to 0xFFFF (65,535)
52 	private int validBits; // 2 bytes unsigned, 0x0002 (2) to 0xFFFF (65,535)
53 
54 	// Buffering
55 	private byte[] buffer; // Local buffer used for IO
56 	private int bufferPointer; // Points to the current position in local buffer
57 	private int bytesRead; // Bytes read after last read into local buffer
58 	private long frameCounter; // Current number of frames read or written
59 
60 	// Cannot instantiate WavFile directly, must either use newWavFile() or
61 	// openWavFile()
WavFile()62 	private WavFile() {
63 		buffer = new byte[BUFFER_SIZE];
64 	}
65 
getNumChannels()66 	public int getNumChannels() {
67 		return numChannels;
68 	}
69 
getNumFrames()70 	public long getNumFrames() {
71 		return numFrames;
72 	}
73 
getFramesRemaining()74 	public long getFramesRemaining() {
75 		return numFrames - frameCounter;
76 	}
77 
getSampleRate()78 	public long getSampleRate() {
79 		return sampleRate;
80 	}
81 
getValidBits()82 	public int getValidBits() {
83 		return validBits;
84 	}
85 
newWavFile(File file, int numChannels, long numFrames, int validBits, long sampleRate)86 	public static WavFile newWavFile(File file, int numChannels,
87 			long numFrames, int validBits, long sampleRate) throws IOException,
88 			WavFileException {
89 		// Instantiate new Wavfile and initialise
90 		WavFile wavFile = new WavFile();
91 		wavFile.file = file;
92 		wavFile.numChannels = numChannels;
93 		wavFile.numFrames = numFrames;
94 		wavFile.sampleRate = sampleRate;
95 		wavFile.bytesPerSample = (validBits + 7) / 8;
96 		wavFile.blockAlign = wavFile.bytesPerSample * numChannels;
97 		wavFile.validBits = validBits;
98 
99 		// Sanity check arguments
100 		if (numChannels < 1 || numChannels > 65535)
101 			throw new WavFileException(
102 					"Illegal number of channels, valid range 1 to 65536");
103 		if (numFrames < 0)
104 			throw new WavFileException("Number of frames must be positive");
105 		if (validBits < 2 || validBits > 65535)
106 			throw new WavFileException(
107 					"Illegal number of valid bits, valid range 2 to 65536");
108 		if (sampleRate < 0)
109 			throw new WavFileException("Sample rate must be positive");
110 
111 		// Create output stream for writing data
112 		wavFile.oStream = new FileOutputStream(file);
113 
114 		// Calculate the chunk sizes
115 		long dataChunkSize = wavFile.blockAlign * numFrames;
116 		long mainChunkSize = 4 + // Riff Type
117 				8 + // Format ID and size
118 				16 + // Format data
119 				8 + // Data ID and size
120 				dataChunkSize;
121 
122 		// Chunks must be word aligned, so if odd number of audio data bytes
123 		// adjust the main chunk size
124 		if (dataChunkSize % 2 == 1) {
125 			mainChunkSize += 1;
126 			wavFile.wordAlignAdjust = true;
127 		} else {
128 			wavFile.wordAlignAdjust = false;
129 		}
130 
131 		// Set the main chunk size
132 		putLE(RIFF_CHUNK_ID, wavFile.buffer, 0, 4);
133 		putLE(mainChunkSize, wavFile.buffer, 4, 4);
134 		putLE(RIFF_TYPE_ID, wavFile.buffer, 8, 4);
135 
136 		// Write out the header
137 		wavFile.oStream.write(wavFile.buffer, 0, 12);
138 
139 		// Put format data in buffer
140 		long averageBytesPerSecond = sampleRate * wavFile.blockAlign;
141 
142 		putLE(FMT_CHUNK_ID, wavFile.buffer, 0, 4); // Chunk ID
143 		putLE(16, wavFile.buffer, 4, 4); // Chunk Data Size
144 		putLE(1, wavFile.buffer, 8, 2); // Compression Code (Uncompressed)
145 		putLE(numChannels, wavFile.buffer, 10, 2); // Number of channels
146 		putLE(sampleRate, wavFile.buffer, 12, 4); // Sample Rate
147 		putLE(averageBytesPerSecond, wavFile.buffer, 16, 4); // Average Bytes
148 																// Per Second
149 		putLE(wavFile.blockAlign, wavFile.buffer, 20, 2); // Block Align
150 		putLE(validBits, wavFile.buffer, 22, 2); // Valid Bits
151 
152 		// Write Format Chunk
153 		wavFile.oStream.write(wavFile.buffer, 0, 24);
154 
155 		// Start Data Chunk
156 		putLE(DATA_CHUNK_ID, wavFile.buffer, 0, 4); // Chunk ID
157 		putLE(dataChunkSize, wavFile.buffer, 4, 4); // Chunk Data Size
158 
159 		// Write Format Chunk
160 		wavFile.oStream.write(wavFile.buffer, 0, 8);
161 
162 		// Calculate the scaling factor for converting to a normalised double
163 		if (wavFile.validBits > 8) {
164 			// If more than 8 validBits, data is signed
165 			// Conversion required multiplying by magnitude of max positive
166 			// value
167 			wavFile.floatOffset = 0;
168 			wavFile.floatScale = Long.MAX_VALUE >> (64 - wavFile.validBits);
169 		} else {
170 			// Else if 8 or less validBits, data is unsigned
171 			// Conversion required dividing by max positive value
172 			wavFile.floatOffset = 1;
173 			wavFile.floatScale = 0.5 * ((1 << wavFile.validBits) - 1);
174 		}
175 
176 		// Finally, set the IO State
177 		wavFile.bufferPointer = 0;
178 		wavFile.bytesRead = 0;
179 		wavFile.frameCounter = 0;
180 		wavFile.ioState = IOState.WRITING;
181 
182 		return wavFile;
183 	}
184 
openWavFile(File file)185 	public static WavFile openWavFile(File file) throws IOException,
186 			WavFileException {
187 		// Instantiate new Wavfile and store the file reference
188 		WavFile wavFile = new WavFile();
189 		wavFile.file = file;
190 
191 		// Create a new file input stream for reading file data
192 		wavFile.iStream = new FileInputStream(file);
193 
194 		// Read the first 12 bytes of the file
195 		int bytesRead = wavFile.iStream.read(wavFile.buffer, 0, 12);
196 		if (bytesRead != 12)
197 			throw new WavFileException("Not enough wav file bytes for header");
198 
199 		// Extract parts from the header
200 		long riffChunkID = getLE(wavFile.buffer, 0, 4);
201 		long chunkSize = getLE(wavFile.buffer, 4, 4);
202 		long riffTypeID = getLE(wavFile.buffer, 8, 4);
203 
204 		// Check the header bytes contains the correct signature
205 		if (riffChunkID != RIFF_CHUNK_ID)
206 			throw new WavFileException(
207 					"Invalid Wav Header data, incorrect riff chunk ID");
208 		if (riffTypeID != RIFF_TYPE_ID)
209 			throw new WavFileException(
210 					"Invalid Wav Header data, incorrect riff type ID");
211 
212 		// Check that the file size matches the number of bytes listed in header
213 		if (file.length() != chunkSize + 8) {
214 			throw new WavFileException("Header chunk size (" + chunkSize
215 					+ ") does not match file size (" + file.length() + ")");
216 		}
217 
218 		boolean foundFormat = false;
219 		boolean foundData = false;
220 
221 		// Search for the Format and Data Chunks
222 		while (true) {
223 			// Read the first 8 bytes of the chunk (ID and chunk size)
224 			bytesRead = wavFile.iStream.read(wavFile.buffer, 0, 8);
225 			if (bytesRead == -1)
226 				throw new WavFileException(
227 						"Reached end of file without finding format chunk");
228 			if (bytesRead != 8)
229 				throw new WavFileException("Could not read chunk header");
230 
231 			// Extract the chunk ID and Size
232 			long chunkID = getLE(wavFile.buffer, 0, 4);
233 			chunkSize = getLE(wavFile.buffer, 4, 4);
234 
235 			// Word align the chunk size
236 			// chunkSize specifies the number of bytes holding data. However,
237 			// the data should be word aligned (2 bytes) so we need to calculate
238 			// the actual number of bytes in the chunk
239 			long numChunkBytes = (chunkSize % 2 == 1) ? chunkSize + 1
240 					: chunkSize;
241 
242 			if (chunkID == FMT_CHUNK_ID) {
243 				// Flag that the format chunk has been found
244 				foundFormat = true;
245 
246 				// Read in the header info
247 				bytesRead = wavFile.iStream.read(wavFile.buffer, 0, 16);
248 
249 				// Check this is uncompressed data
250 				int compressionCode = (int) getLE(wavFile.buffer, 0, 2);
251 				if (compressionCode != 1)
252 					throw new WavFileException("Compression Code "
253 							+ compressionCode + " not supported");
254 
255 				// Extract the format information
256 				wavFile.numChannels = (int) getLE(wavFile.buffer, 2, 2);
257 				wavFile.sampleRate = getLE(wavFile.buffer, 4, 4);
258 				wavFile.blockAlign = (int) getLE(wavFile.buffer, 12, 2);
259 				wavFile.validBits = (int) getLE(wavFile.buffer, 14, 2);
260 
261 				if (wavFile.numChannels == 0)
262 					throw new WavFileException(
263 							"Number of channels specified in header is equal to zero");
264 				if (wavFile.blockAlign == 0)
265 					throw new WavFileException(
266 							"Block Align specified in header is equal to zero");
267 				if (wavFile.validBits < 2)
268 					throw new WavFileException(
269 							"Valid Bits specified in header is less than 2");
270 				if (wavFile.validBits > 64)
271 					throw new WavFileException(
272 							"Valid Bits specified in header is greater than 64, this is greater than a long can hold");
273 
274 				// Calculate the number of bytes required to hold 1 sample
275 				wavFile.bytesPerSample = (wavFile.validBits + 7) / 8;
276 				if (wavFile.bytesPerSample * wavFile.numChannels != wavFile.blockAlign)
277 					throw new WavFileException(
278 							"Block Align does not agree with bytes required for validBits and number of channels");
279 
280 				// Account for number of format bytes and then skip over
281 				// any extra format bytes
282 				numChunkBytes -= 16;
283 				if (numChunkBytes > 0)
284 					wavFile.iStream.skip(numChunkBytes);
285 			} else if (chunkID == DATA_CHUNK_ID) {
286 				// Check if we've found the format chunk,
287 				// If not, throw an exception as we need the format information
288 				// before we can read the data chunk
289 				if (foundFormat == false)
290 					throw new WavFileException(
291 							"Data chunk found before Format chunk");
292 
293 				// Check that the chunkSize (wav data length) is a multiple of
294 				// the
295 				// block align (bytes per frame)
296 				if (chunkSize % wavFile.blockAlign != 0)
297 					throw new WavFileException(
298 							"Data Chunk size is not multiple of Block Align");
299 
300 				// Calculate the number of frames
301 				wavFile.numFrames = chunkSize / wavFile.blockAlign;
302 
303 				// Flag that we've found the wave data chunk
304 				foundData = true;
305 
306 				break;
307 			} else {
308 				// If an unknown chunk ID is found, just skip over the chunk
309 				// data
310 				wavFile.iStream.skip(numChunkBytes);
311 			}
312 		}
313 
314 		// Throw an exception if no data chunk has been found
315 		if (foundData == false)
316 			throw new WavFileException("Did not find a data chunk");
317 
318 		// Calculate the scaling factor for converting to a normalised double
319 		if (wavFile.validBits > 8) {
320 			// If more than 8 validBits, data is signed
321 			// Conversion required dividing by magnitude of max negative value
322 			wavFile.floatOffset = 0;
323 			wavFile.floatScale = 1 << (wavFile.validBits - 1);
324 		} else {
325 			// Else if 8 or less validBits, data is unsigned
326 			// Conversion required dividing by max positive value
327 			wavFile.floatOffset = -1;
328 			wavFile.floatScale = 0.5 * ((1 << wavFile.validBits) - 1);
329 		}
330 
331 		wavFile.bufferPointer = 0;
332 		wavFile.bytesRead = 0;
333 		wavFile.frameCounter = 0;
334 		wavFile.ioState = IOState.READING;
335 
336 		return wavFile;
337 	}
338 
339 	// Get and Put little endian data from local buffer
340 	// ------------------------------------------------
getLE(byte[] buffer, int pos, int numBytes)341 	private static long getLE(byte[] buffer, int pos, int numBytes) {
342 		numBytes--;
343 		pos += numBytes;
344 
345 		long val = buffer[pos] & 0xFF;
346 		for (int b = 0; b < numBytes; b++)
347 			val = (val << 8) + (buffer[--pos] & 0xFF);
348 
349 		return val;
350 	}
351 
putLE(long val, byte[] buffer, int pos, int numBytes)352 	private static void putLE(long val, byte[] buffer, int pos, int numBytes) {
353 		for (int b = 0; b < numBytes; b++) {
354 			buffer[pos] = (byte) (val & 0xFF);
355 			val >>= 8;
356 			pos++;
357 		}
358 	}
359 
360 	// Sample Writing and Reading
361 	// --------------------------
writeSample(long val)362 	private void writeSample(long val) throws IOException {
363 		for (int b = 0; b < bytesPerSample; b++) {
364 			if (bufferPointer == BUFFER_SIZE) {
365 				oStream.write(buffer, 0, BUFFER_SIZE);
366 				bufferPointer = 0;
367 			}
368 
369 			buffer[bufferPointer] = (byte) (val & 0xFF);
370 			val >>= 8;
371 			bufferPointer++;
372 		}
373 	}
374 
readSample()375 	private long readSample() throws IOException, WavFileException {
376 		long val = 0;
377 
378 		for (int b = 0; b < bytesPerSample; b++) {
379 			if (bufferPointer == bytesRead) {
380 				int read = iStream.read(buffer, 0, BUFFER_SIZE);
381 				if (read == -1)
382 					throw new WavFileException("Not enough data available");
383 				bytesRead = read;
384 				bufferPointer = 0;
385 			}
386 
387 			int v = buffer[bufferPointer];
388 			if (b < bytesPerSample - 1 || bytesPerSample == 1)
389 				v &= 0xFF;
390 			val += v << (b * 8);
391 
392 			bufferPointer++;
393 		}
394 
395 		return val;
396 	}
397 
398 	// Integer
399 	// -------
readFrames(int[] sampleBuffer, int numFramesToRead)400 	public int readFrames(int[] sampleBuffer, int numFramesToRead)
401 			throws IOException, WavFileException {
402 		return readFrames(sampleBuffer, 0, numFramesToRead);
403 	}
404 
readFrames(int[] sampleBuffer, int offset, int numFramesToRead)405 	public int readFrames(int[] sampleBuffer, int offset, int numFramesToRead)
406 			throws IOException, WavFileException {
407 		if (ioState != IOState.READING)
408 			throw new IOException("Cannot read from WavFile instance");
409 
410 		for (int f = 0; f < numFramesToRead; f++) {
411 			if (frameCounter == numFrames)
412 				return f;
413 
414 			for (int c = 0; c < numChannels; c++) {
415 				sampleBuffer[offset] = (int) readSample();
416 				offset++;
417 			}
418 
419 			frameCounter++;
420 		}
421 
422 		return numFramesToRead;
423 	}
424 
readFrames(int[][] sampleBuffer, int numFramesToRead)425 	public int readFrames(int[][] sampleBuffer, int numFramesToRead)
426 			throws IOException, WavFileException {
427 		return readFrames(sampleBuffer, 0, numFramesToRead);
428 	}
429 
readFrames(int[][] sampleBuffer, int offset, int numFramesToRead)430 	public int readFrames(int[][] sampleBuffer, int offset, int numFramesToRead)
431 			throws IOException, WavFileException {
432 		if (ioState != IOState.READING)
433 			throw new IOException("Cannot read from WavFile instance");
434 
435 		for (int f = 0; f < numFramesToRead; f++) {
436 			if (frameCounter == numFrames)
437 				return f;
438 
439 			for (int c = 0; c < numChannels; c++)
440 				sampleBuffer[c][offset] = (int) readSample();
441 
442 			offset++;
443 			frameCounter++;
444 		}
445 
446 		return numFramesToRead;
447 	}
448 
writeFrames(int[] sampleBuffer, int numFramesToWrite)449 	public int writeFrames(int[] sampleBuffer, int numFramesToWrite)
450 			throws IOException, WavFileException {
451 		return writeFrames(sampleBuffer, 0, numFramesToWrite);
452 	}
453 
writeFrames(int[] sampleBuffer, int offset, int numFramesToWrite)454 	public int writeFrames(int[] sampleBuffer, int offset, int numFramesToWrite)
455 			throws IOException, WavFileException {
456 		if (ioState != IOState.WRITING)
457 			throw new IOException("Cannot write to WavFile instance");
458 
459 		for (int f = 0; f < numFramesToWrite; f++) {
460 			if (frameCounter == numFrames)
461 				return f;
462 
463 			for (int c = 0; c < numChannels; c++) {
464 				writeSample(sampleBuffer[offset]);
465 				offset++;
466 			}
467 
468 			frameCounter++;
469 		}
470 
471 		return numFramesToWrite;
472 	}
473 
writeFrames(int[][] sampleBuffer, int numFramesToWrite)474 	public int writeFrames(int[][] sampleBuffer, int numFramesToWrite)
475 			throws IOException, WavFileException {
476 		return writeFrames(sampleBuffer, 0, numFramesToWrite);
477 	}
478 
writeFrames(int[][] sampleBuffer, int offset, int numFramesToWrite)479 	public int writeFrames(int[][] sampleBuffer, int offset,
480 			int numFramesToWrite) throws IOException, WavFileException {
481 		if (ioState != IOState.WRITING)
482 			throw new IOException("Cannot write to WavFile instance");
483 
484 		for (int f = 0; f < numFramesToWrite; f++) {
485 			if (frameCounter == numFrames)
486 				return f;
487 
488 			for (int c = 0; c < numChannels; c++)
489 				writeSample(sampleBuffer[c][offset]);
490 
491 			offset++;
492 			frameCounter++;
493 		}
494 
495 		return numFramesToWrite;
496 	}
497 
498 	// Long
499 	// ----
readFrames(long[] sampleBuffer, int numFramesToRead)500 	public int readFrames(long[] sampleBuffer, int numFramesToRead)
501 			throws IOException, WavFileException {
502 		return readFrames(sampleBuffer, 0, numFramesToRead);
503 	}
504 
readFrames(long[] sampleBuffer, int offset, int numFramesToRead)505 	public int readFrames(long[] sampleBuffer, int offset, int numFramesToRead)
506 			throws IOException, WavFileException {
507 		if (ioState != IOState.READING)
508 			throw new IOException("Cannot read from WavFile instance");
509 
510 		for (int f = 0; f < numFramesToRead; f++) {
511 			if (frameCounter == numFrames)
512 				return f;
513 
514 			for (int c = 0; c < numChannels; c++) {
515 				sampleBuffer[offset] = readSample();
516 				offset++;
517 			}
518 
519 			frameCounter++;
520 		}
521 
522 		return numFramesToRead;
523 	}
524 
readFrames(long[][] sampleBuffer, int numFramesToRead)525 	public int readFrames(long[][] sampleBuffer, int numFramesToRead)
526 			throws IOException, WavFileException {
527 		return readFrames(sampleBuffer, 0, numFramesToRead);
528 	}
529 
readFrames(long[][] sampleBuffer, int offset, int numFramesToRead)530 	public int readFrames(long[][] sampleBuffer, int offset, int numFramesToRead)
531 			throws IOException, WavFileException {
532 		if (ioState != IOState.READING)
533 			throw new IOException("Cannot read from WavFile instance");
534 
535 		for (int f = 0; f < numFramesToRead; f++) {
536 			if (frameCounter == numFrames)
537 				return f;
538 
539 			for (int c = 0; c < numChannels; c++)
540 				sampleBuffer[c][offset] = readSample();
541 
542 			offset++;
543 			frameCounter++;
544 		}
545 
546 		return numFramesToRead;
547 	}
548 
writeFrames(long[] sampleBuffer, int numFramesToWrite)549 	public int writeFrames(long[] sampleBuffer, int numFramesToWrite)
550 			throws IOException, WavFileException {
551 		return writeFrames(sampleBuffer, 0, numFramesToWrite);
552 	}
553 
writeFrames(long[] sampleBuffer, int offset, int numFramesToWrite)554 	public int writeFrames(long[] sampleBuffer, int offset, int numFramesToWrite)
555 			throws IOException, WavFileException {
556 		if (ioState != IOState.WRITING)
557 			throw new IOException("Cannot write to WavFile instance");
558 
559 		for (int f = 0; f < numFramesToWrite; f++) {
560 			if (frameCounter == numFrames)
561 				return f;
562 
563 			for (int c = 0; c < numChannels; c++) {
564 				writeSample(sampleBuffer[offset]);
565 				offset++;
566 			}
567 
568 			frameCounter++;
569 		}
570 
571 		return numFramesToWrite;
572 	}
573 
writeFrames(long[][] sampleBuffer, int numFramesToWrite)574 	public int writeFrames(long[][] sampleBuffer, int numFramesToWrite)
575 			throws IOException, WavFileException {
576 		return writeFrames(sampleBuffer, 0, numFramesToWrite);
577 	}
578 
writeFrames(long[][] sampleBuffer, int offset, int numFramesToWrite)579 	public int writeFrames(long[][] sampleBuffer, int offset,
580 			int numFramesToWrite) throws IOException, WavFileException {
581 		if (ioState != IOState.WRITING)
582 			throw new IOException("Cannot write to WavFile instance");
583 
584 		for (int f = 0; f < numFramesToWrite; f++) {
585 			if (frameCounter == numFrames)
586 				return f;
587 
588 			for (int c = 0; c < numChannels; c++)
589 				writeSample(sampleBuffer[c][offset]);
590 
591 			offset++;
592 			frameCounter++;
593 		}
594 
595 		return numFramesToWrite;
596 	}
597 
598 	// Double
599 	// ------
readFrames(double[] sampleBuffer, int numFramesToRead)600 	public int readFrames(double[] sampleBuffer, int numFramesToRead)
601 			throws IOException, WavFileException {
602 		return readFrames(sampleBuffer, 0, numFramesToRead);
603 	}
604 
readFrames(double[] sampleBuffer, int offset, int numFramesToRead)605 	public int readFrames(double[] sampleBuffer, int offset, int numFramesToRead)
606 			throws IOException, WavFileException {
607 		if (ioState != IOState.READING)
608 			throw new IOException("Cannot read from WavFile instance");
609 
610 		for (int f = 0; f < numFramesToRead; f++) {
611 			if (frameCounter == numFrames)
612 				return f;
613 
614 			for (int c = 0; c < numChannels; c++) {
615 				sampleBuffer[offset] = floatOffset + (double) readSample()
616 						/ floatScale;
617 				offset++;
618 			}
619 
620 			frameCounter++;
621 		}
622 
623 		return numFramesToRead;
624 	}
625 
readFrames(double[][] sampleBuffer, int numFramesToRead)626 	public int readFrames(double[][] sampleBuffer, int numFramesToRead)
627 			throws IOException, WavFileException {
628 		return readFrames(sampleBuffer, 0, numFramesToRead);
629 	}
630 
readFrames(double[][] sampleBuffer, int offset, int numFramesToRead)631 	public int readFrames(double[][] sampleBuffer, int offset,
632 			int numFramesToRead) throws IOException, WavFileException {
633 		if (ioState != IOState.READING)
634 			throw new IOException("Cannot read from WavFile instance");
635 
636 		for (int f = 0; f < numFramesToRead; f++) {
637 			if (frameCounter == numFrames)
638 				return f;
639 
640 			for (int c = 0; c < numChannels; c++)
641 				sampleBuffer[c][offset] = floatOffset + (double) readSample()
642 						/ floatScale;
643 
644 			offset++;
645 			frameCounter++;
646 		}
647 
648 		return numFramesToRead;
649 	}
650 
writeFrames(double[] sampleBuffer, int numFramesToWrite)651 	public int writeFrames(double[] sampleBuffer, int numFramesToWrite)
652 			throws IOException, WavFileException {
653 		return writeFrames(sampleBuffer, 0, numFramesToWrite);
654 	}
655 
writeFrames(double[] sampleBuffer, int offset, int numFramesToWrite)656 	public int writeFrames(double[] sampleBuffer, int offset,
657 			int numFramesToWrite) throws IOException, WavFileException {
658 		if (ioState != IOState.WRITING)
659 			throw new IOException("Cannot write to WavFile instance");
660 
661 		for (int f = 0; f < numFramesToWrite; f++) {
662 			if (frameCounter == numFrames)
663 				return f;
664 
665 			for (int c = 0; c < numChannels; c++) {
666 				writeSample((long) (floatScale * (floatOffset + sampleBuffer[offset])));
667 				offset++;
668 			}
669 
670 			frameCounter++;
671 		}
672 
673 		return numFramesToWrite;
674 	}
675 
writeFrames(double[][] sampleBuffer, int numFramesToWrite)676 	public int writeFrames(double[][] sampleBuffer, int numFramesToWrite)
677 			throws IOException, WavFileException {
678 		return writeFrames(sampleBuffer, 0, numFramesToWrite);
679 	}
680 
writeFrames(double[][] sampleBuffer, int offset, int numFramesToWrite)681 	public int writeFrames(double[][] sampleBuffer, int offset,
682 			int numFramesToWrite) throws IOException, WavFileException {
683 		if (ioState != IOState.WRITING)
684 			throw new IOException("Cannot write to WavFile instance");
685 
686 		for (int f = 0; f < numFramesToWrite; f++) {
687 			if (frameCounter == numFrames)
688 				return f;
689 
690 			for (int c = 0; c < numChannels; c++)
691 				writeSample((long) (floatScale * (floatOffset + sampleBuffer[c][offset])));
692 
693 			offset++;
694 			frameCounter++;
695 		}
696 
697 		return numFramesToWrite;
698 	}
699 
close()700 	public void close() throws IOException {
701 		// Close the input stream and set to null
702 		if (iStream != null) {
703 			iStream.close();
704 			iStream = null;
705 		}
706 
707 		if (oStream != null) {
708 			// Write out anything still in the local buffer
709 			if (bufferPointer > 0)
710 				oStream.write(buffer, 0, bufferPointer);
711 
712 			// If an extra byte is required for word alignment, add it to the
713 			// end
714 			if (wordAlignAdjust)
715 				oStream.write(0);
716 
717 			// Close the stream and set to null
718 			oStream.close();
719 			oStream = null;
720 		}
721 
722 		// Flag that the stream is closed
723 		ioState = IOState.CLOSED;
724 	}
725 
display()726 	public void display() {
727 		display(System.out);
728 	}
729 
display(PrintStream out)730 	public void display(PrintStream out) {
731 		out.printf("File: %s\n", file);
732 		out.printf("Channels: %d, Frames: %d\n", numChannels, numFrames);
733 		out.printf("IO State: %s\n", ioState);
734 		out.printf("Sample Rate: %d, Block Align: %d\n", sampleRate, blockAlign);
735 		out.printf("Valid Bits: %d, Bytes per sample: %d\n", validBits,
736 				bytesPerSample);
737 	}
738 
main(String[] args)739 	public static void main(String[] args) {
740 		if (args.length < 1) {
741 			System.err.println("Must supply filename");
742 			System.exit(1);
743 		}
744 
745 		try {
746 			for (String filename : args) {
747 				WavFile readWavFile = openWavFile(new File(filename));
748 				readWavFile.display();
749 
750 				long numFrames = readWavFile.getNumFrames();
751 				int numChannels = readWavFile.getNumChannels();
752 				int validBits = readWavFile.getValidBits();
753 				long sampleRate = readWavFile.getSampleRate();
754 
755 				WavFile writeWavFile = newWavFile(new File("out.wav"),
756 						numChannels, numFrames, validBits, sampleRate);
757 
758 				final int BUF_SIZE = 5001;
759 
760 				// int[] buffer = new int[BUF_SIZE * numChannels];
761 				// long[] buffer = new long[BUF_SIZE * numChannels];
762 				double[] buffer = new double[BUF_SIZE * numChannels];
763 
764 				int framesRead = 0;
765 				int framesWritten = 0;
766 
767 				do {
768 					framesRead = readWavFile.readFrames(buffer, BUF_SIZE);
769 					framesWritten = writeWavFile.writeFrames(buffer, BUF_SIZE);
770 					System.out.printf("%d %d\n", framesRead, framesWritten);
771 				} while (framesRead != 0);
772 
773 				readWavFile.close();
774 				writeWavFile.close();
775 			}
776 
777 			WavFile writeWavFile = newWavFile(new File("out2.wav"), 1, 10, 23,
778 					44100);
779 			double[] buffer = new double[10];
780 			writeWavFile.writeFrames(buffer, 10);
781 			writeWavFile.close();
782 		} catch (Exception e) {
783 			System.err.println(e);
784 			e.printStackTrace();
785 		}
786 	}
787 }
788