org.tritonus.share.sampled.file.TAudioOutputStream Maven / Gradle / Ivy
The newest version!
/*
* TAudioOutputStream.java
*
* This file is part of Tritonus: http://www.tritonus.org/
*/
/*
* Copyright (c) 2000 by Matthias Pfisterer
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/*
|<--- this code is formatted to fit into 80 columns --->|
*/
package org.tritonus.share.sampled.file;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import static org.tritonus.share.sampled.AudioUtils.isPCM;
import static org.tritonus.share.sampled.TConversionTool.convertSign8;
import static org.tritonus.share.sampled.TConversionTool.swapOrder16;
import static org.tritonus.share.sampled.TConversionTool.swapOrder24;
import static org.tritonus.share.sampled.TConversionTool.swapOrder32;
import org.tritonus.share.TDebug;
/**
* Base class for classes implementing AudioOutputStream.
*
* @author Matthias Pfisterer
*/
public abstract class TAudioOutputStream
implements AudioOutputStream
{
private AudioFormat m_audioFormat;
private long m_lLength; // in bytes
private long m_lCalculatedLength;
private TDataOutputStream m_dataOutputStream;
private boolean m_bDoBackPatching;
private boolean m_bHeaderWritten;
/** if this flag is set, do sign conversion for 8-bit PCM data */
private boolean m_doSignConversion;
/** if this flag is set, do endian conversion for 16-bit PCM data */
private boolean m_doEndianConversion;
protected TAudioOutputStream(AudioFormat audioFormat,
long lLength,
TDataOutputStream dataOutputStream,
boolean bDoBackPatching)
{
m_audioFormat = audioFormat;
m_lLength = lLength;
m_lCalculatedLength = 0;
m_dataOutputStream = dataOutputStream;
m_bDoBackPatching = bDoBackPatching;
m_bHeaderWritten = false;
}
/**
* descendants should call this method if implicit sign conversion for 8-bit
* data should be done
*/
protected void requireSign8bit(boolean signed) {
if (m_audioFormat.getSampleSizeInBits() == 8 && isPCM(m_audioFormat)) {
boolean si = m_audioFormat.getEncoding().equals(
AudioFormat.Encoding.PCM_SIGNED);
m_doSignConversion = signed != si;
}
}
/**
* descendants should call this method if implicit endian conversion should
* be done. Currently supported for 16, 24, and 32 bits per sample.
*/
protected void requireEndianness(boolean bigEndian) {
int ssib = m_audioFormat.getSampleSizeInBits();
if ((ssib == 16 || ssib == 24 || ssib == 32) && isPCM(m_audioFormat)) {
m_doEndianConversion = bigEndian != m_audioFormat.isBigEndian();
}
}
public AudioFormat getFormat()
{
return m_audioFormat;
}
/** Gives length of the stream.
* This value is in bytes. It may be AudioSystem.NOT_SPECIFIED
* to express that the length is unknown.
*/
public long getLength()
{
return m_lLength;
}
/** Gives number of bytes already written.
*/
// IDEA: rename this to BytesWritten or something like that ?
public long getCalculatedLength()
{
return m_lCalculatedLength;
}
protected TDataOutputStream getDataOutputStream()
{
return m_dataOutputStream;
}
/** do sign or endianness conversion */
private void handleImplicitConversions(byte[] abData, int nOffset, int nLength) {
if (m_doSignConversion) {
convertSign8(abData, nOffset, nLength);
}
if (m_doEndianConversion) {
switch (m_audioFormat.getSampleSizeInBits()) {
case 16: swapOrder16(abData, nOffset, nLength / 2);
break;
case 24: swapOrder24(abData, nOffset, nLength / 3);
break;
case 32:
swapOrder32(abData, nOffset, nLength / 4);
break;
}
}
}
/** Writes audio data to the destination (file or output stream).
*/
// IDEA: use long?
public int write(byte[] abData, int nOffset, int nLength)
throws IOException
{
if (TDebug.TraceAudioOutputStream)
{
TDebug.out("TAudioOutputStream.write(): wanted length: " + nLength);
}
if (! m_bHeaderWritten)
{
writeHeader();
m_bHeaderWritten = true;
}
// $$fb added
// check that total writes do not exceed specified length
long lTotalLength=getLength();
if (lTotalLength!=AudioSystem.NOT_SPECIFIED && (m_lCalculatedLength+nLength)>lTotalLength) {
if (TDebug.TraceAudioOutputStream) {
TDebug.out("TAudioOutputStream.write(): requested more bytes to write than possible.");
}
nLength=(int) (lTotalLength-m_lCalculatedLength);
// sanity
if (nLength<0) {
nLength=0;
}
}
// TODO: throw an exception if nLength==0 ? (to indicate end of file ?)
if (nLength>0) {
handleImplicitConversions(abData, nOffset, nLength);
m_dataOutputStream.write(abData, nOffset, nLength);
m_lCalculatedLength += nLength;
// if we converted something, need to undo the conversion to
// guarantee integrity of data
handleImplicitConversions(abData, nOffset, nLength);
}
if (TDebug.TraceAudioOutputStream)
{
TDebug.out("TAudioOutputStream.write(): calculated (total) length: " + m_lCalculatedLength+" bytes = "+(m_lCalculatedLength/getFormat().getFrameSize())+" frames");
}
return nLength;
}
/** Writes the header of the audio file.
*/
protected abstract void writeHeader()
throws IOException;
/** Closes the stream.
* This does write remaining buffered data to the destination,
* backpatch the header, if necessary, and closes the destination.
*/
public void close()
throws IOException
{
if (TDebug.TraceAudioOutputStream)
{
TDebug.out("TAudioOutputStream.close(): called");
}
// flush?
if (m_bDoBackPatching)
{
if (TDebug.TraceAudioOutputStream)
{
TDebug.out("TAudioOutputStream.close(): patching header");
}
patchHeader();
}
m_dataOutputStream.close();
}
protected void patchHeader()
throws IOException
{
TDebug.out("TAudioOutputStream.patchHeader(): called");
// DO NOTHING
}
protected void setLengthFromCalculatedLength()
{
m_lLength = m_lCalculatedLength;
}
}
/*** TAudioOutputStream.java ***/