/*
* Copyright 2007 Sun Microsystems, Inc.
*
* This file is part of jVoiceBridge.
*
* jVoiceBridge is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation and distributed hereunder
* to you.
*
* jVoiceBridge is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the License file that accompanied this
* code.
*/
package com.sun.voip;
import java.io.IOException;
import java.io.InputStream;
/**
* Read audio samples from a Sun .au file.
* Up/down sample as necessary.
*/
public class DotAuAudioSource extends FileAudioSource {
private String path;
private int sampleRate;
private int channels;
private int encoding;
private static final int AUDIO_FILE_HEADER_SIZE = 24;
private InputStream in;
/*
* Read an audio file. Pad with linear silence.
*/
public DotAuAudioSource(String path) throws IOException {
this.path = path;
initialize();
}
private void initialize() throws IOException {
done();
in = getInputStream(path);
/*
* Audio file header
*
* int magic = 0x2e 0x73 0x6e 0x64 which is ".snd"
* int hdr_size;
* int data_size;
* int encoding = 1 for ulaw, 3 for linear;
* int sample_rate;
* int channels;
*/
int bytesAvailable = 0;
try {
bytesAvailable = in.available();
} catch (IOException ioe) {
throw new IOException("available() failed " + path);
}
if (bytesAvailable < AUDIO_FILE_HEADER_SIZE) {
throw new IOException ("audiofile " + path + " is too small " +
bytesAvailable);
}
byte[] audioFileHeader = new byte[AUDIO_FILE_HEADER_SIZE];
try {
in.read(audioFileHeader, 0, AUDIO_FILE_HEADER_SIZE);
} catch (Exception e) {
throw new IOException("error reading " + path + " "
+ e.getMessage());
}
encoding = audioFileHeader[15];
channels = audioFileHeader[23];
if (audioFileHeader[0] != 0x2e || audioFileHeader[1] != 0x73 ||
audioFileHeader[2] != 0x6e || audioFileHeader[3] != 0x64 ||
(encoding != ULAW && encoding != LINEAR) ||
channels > 16) {
throw new IOException("bad audio file header " + path);
}
sampleRate =
((((int)audioFileHeader[16]) << 24) & 0xff000000) +
((((int)audioFileHeader[17]) << 16) & 0x00ff0000) +
((((int)audioFileHeader[18]) << 8) & 0x0000ff00) +
(((int)audioFileHeader[19]) & 0xff);
if (Logger.logLevel >= Logger.LOG_MOREINFO) {
Logger.println("AudioFile is " + path + ". Resource is " +
DotAuAudioSource.class.getResource(path) + ". size "
+ in.available() + " encoding " + encoding
+ " channels " + channels + " sampleRate "
+ sampleRate);
}
try {
bytesAvailable = in.available();
int hdr_size =
((audioFileHeader[4] << 24) & 0xff0000) |
((audioFileHeader[5] << 16) & 0xff0000) |
((audioFileHeader[6] << 8) & 0xff00) |
(audioFileHeader[7] & 0xff);
if (hdr_size > AUDIO_FILE_HEADER_SIZE) {
/*
* read remainder of the header and discard
*/
int excess_hdr_size =
hdr_size - AUDIO_FILE_HEADER_SIZE;
byte[] data = new byte[excess_hdr_size];
in.read(data, 0, excess_hdr_size);
if (Logger.logLevel >= Logger.LOG_MOREINFO) {
Logger.println("Reading excess header "
+ " hdr size " + hdr_size
+ " excess " + excess_hdr_size);
}
}
} catch (Exception e) {
throw new IOException("Can't read data! " + path + " "
+ e.getMessage());
}
}
public int[] getLinearData(int sampleTime) throws IOException {
byte[] fileData = readAudioFile(sampleTime);
if (fileData == null) {
return null;
}
int[] linearData;
if (encoding == ULAW) {
// 1 ulaw byte for each int
linearData = new int[fileData.length];
AudioConversion.ulawToLinear(fileData, 0, fileData.length,
linearData);
} else {
// 2 linear bytes for each int
linearData = new int[fileData.length / 2];
for (int i = 0; i < linearData.length; i++) {
linearData[i] = (int)
((short)(((fileData[2 * i] << 8) & 0xff00) |
(fileData[(2 * i) + 1] & 0xff)));
}
}
return linearData;
}
private byte[] readAudioFile(int sampleTime) throws IOException {
int bytesAvailable;
if (in == null || (bytesAvailable = in.available()) == 0) {
done();
return null;
}
int sampleSize = 2;
if (encoding == AudioSource.ULAW) {
sampleSize = 1;
}
int len =
sampleRate * sampleTime * channels * sampleSize / 1000;
byte[] data = new byte[len];
try {
int readSize;
readSize = Math.min(len, bytesAvailable);
/*
* Read the file
*/
in.read(data, 0, readSize);
byte b;
if (encoding == ULAW) {
b = AudioConversion.PCMU_SILENCE;
} else {
b = AudioConversion.PCM_SILENCE;
}
for (int i = readSize; i < len; i++) {
data[i] = b;
}
} catch (IOException e) {
throw new IOException("Can't read data! " + path + " "
+ e.getMessage());
}
return data;
}
public int getSampleRate() {
return sampleRate;
}
public int getChannels() {
return channels;
}
public void rewind() throws IOException {
initialize();
}
public void done() {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
}
}