/*
* @(#)SunAudioOutput.java 1.15 02/08/21
*
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved.
*/
package com.sun.media.renderer.audio.device;
import java.io.*;
import javax.media.format.AudioFormat;
import com.sun.media.*;
import com.sun.media.util.LoopThread;
import com.sun.media.util.jdk12;
import com.sun.media.renderer.audio.SunAudioRenderer;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import com.ms.security.PermissionID;
import com.ms.security.PolicyEngine;
public class SunAudioOutput extends InputStream implements AudioOutput {
protected sun.audio.AudioStream audioStream;
protected int bufLength;
protected byte buffer[];
protected static int EOM = -1;
protected boolean paused = false;
protected boolean started = false;
protected boolean flushing = false;
private boolean startAfterWrite = false;
protected AudioFormat format;
private int SUN_MAGIC = 0x2e736e64; // au file magic number
private int HDR_SIZE = 24; // minimum au header file size
private int FILE_LENGTH = 0; // file length (optional)
private int SAMPLE_RATE = 8000;
private int ENCODING = 1; // ULAW
private int CHANNELS = 1;
// in == out implies buffer is empty.
// (in + 1) % buffer.length == out implies buffer is full.
int in = 0;
int out = 0;
boolean eom = false;
/*private*/ int samplesPlayed = 0;
private boolean isMuted = false;
private double gain = 0;
private byte silence[];
//private boolean fConvertToULAW = false;
/** padding length of silence at the end of the media (default constant) **/
private static final int END_OF_MEDIA_PADDING_LENGTH=800;
/** padding length of silence at the end of the media **/
private int endOfMediaPaddingLength;
private byte[] conversionBuffer ;
//private boolean AudioPlayerStoppingPhase=false;
//private FileOutputStream IN;
/*private*/ static final int SLEEP_TIME=50;
//protected static final int SUN_AUDIO_INTERNAL_DELAY=400; // for debuging purposes
//protected static final int SUN_AUDIO_INTERNAL_DELAY=0; // for debuging purposes
protected boolean internalDelayUpdate=false;
private SunAudioPlayThread timeUpdatingThread=null;
protected int sunAudioInitialCount=0;
protected int sunAudioFinalCount=0;
protected int silenceCount = 0;
private static JMFSecurity jmfSecurity = null;
private static boolean securityPrivelege=false;
private Method m[] = new Method[1];
private Class cl[] = new Class[1];
private Object args[][] = new Object[1][0];
static {
try {
jmfSecurity = JMFSecurityManager.getJMFSecurity();
securityPrivelege = true;
} catch (SecurityException e) {
}
}
public SunAudioOutput() {
}
public boolean initialize(AudioFormat format, int length) {
this.format = format;
//bufLength = length;
//bufLength = 8000; // hardcode the number to 8000.
bufLength = 12000; // hardcode the number to 12000.
buffer = new byte[bufLength];
silence = new byte[bufLength];
for (int i = 0; i < bufLength; i++)
silence[i] = 127;
if ( /*securityPrivelege &&*/ (jmfSecurity != null) ) {
String permission = null;
try {
if (jmfSecurity.getName().startsWith("jmf-security")) {
permission = "thread";
jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD);
m[0].invoke(cl[0], args[0]);
permission = "thread group";
jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD_GROUP);
m[0].invoke(cl[0], args[0]);
} else if (jmfSecurity.getName().startsWith("internet")) {
PolicyEngine.checkPermission(PermissionID.THREAD);
PolicyEngine.assertPermission(PermissionID.THREAD);
}
} catch (Throwable e) {
if (JMFSecurityManager.DEBUG) {
System.err.println("Unable to get " + permission +
" privilege " + e);
}
securityPrivelege = false;
// TODO: Do the right thing if permissions cannot be obtained.
// User should be notified via an event
}
}
if ( (jmfSecurity != null) && (jmfSecurity.getName().startsWith("jdk12"))) {
try {
Constructor cons = jdk12CreateThreadAction.cons;
timeUpdatingThread = (SunAudioPlayThread) jdk12.doPrivM.invoke(
jdk12.ac,
new Object[] {
cons.newInstance(
new Object[] {
SunAudioPlayThread.class,
})});
} catch (Exception e) {
}
} else {
timeUpdatingThread = new SunAudioPlayThread();
}
timeUpdatingThread.setStream(this);
setPaddingLength(END_OF_MEDIA_PADDING_LENGTH); // defualt size
// BB
ByteArrayOutputStream tempOut = new ByteArrayOutputStream();
DataOutputStream tempData = new DataOutputStream(tempOut);
try {
tempData.writeInt(SUN_MAGIC);
tempData.writeInt(HDR_SIZE);
tempData.writeInt(FILE_LENGTH);
tempData.writeInt(ENCODING);
tempData.writeInt(SAMPLE_RATE);
tempData.writeInt(CHANNELS);
} catch (Exception e) {}
byte[] buf = tempOut.toByteArray();
write(buf, 0, buf.length);
String encoding = format.getEncoding();
int sampleRate = (int)format.getSampleRate();
if (!( (format.getChannels() == 1) &&
(sampleRate == 8000) &&
(encoding.equals(AudioFormat.ULAW)) ) ) {
System.out.println("AudioPlay:Unsupported Audio Format");
return false;
}
try {
audioStream = new sun.audio.AudioStream(this);
} catch (Exception e) {
System.err.println("Exception: " + e);
audioStream = null;
return false;
}
return true;
}
public void finalize() throws Throwable {
super.finalize();
dispose();
}
public void pause() {
//System.out.println("SunAudioOutput pause ");
if (audioStream != null) {
timeUpdatingThread.pause();
sun.audio.AudioPlayer.player.stop(audioStream);
}
paused = true;
}
public synchronized void resume() {
//System.out.println("SunAudioOutput resume ");
if ( (audioStream != null) && (!started || paused) ) {
started=true;
//System.out.println("start the player "+dataAvailable());
sun.audio.AudioPlayer.player.start(audioStream);
timeUpdatingThread.start();
}
paused = false;
}
// $$ AudioRenderer's abortPrefetch() calls this method
public synchronized void dispose() {
if (audioStream != null) {
timeUpdatingThread.kill();
sun.audio.AudioPlayer.player.stop(audioStream);
}
buffer = null;
}
public void drain() {
int remain;
int len;
synchronized (this) {
remain = endOfMediaPaddingLength;
// pad the end of the media with silence
// (used to drain sun.audio.AudioPlayer.player)
while (remain > 0) {
len = write(silence,0,remain);
remain -= len;
}
// drain the JMF buffer.
while (in != out && !paused) {
try {
wait();
} catch (InterruptedException e) { }
}
// We'll need to drain longer on the Mac.
if (SunAudioRenderer.runningOnMac) {
try {
Thread.sleep(SunAudioRenderer.DEVICE_LATENCY/1000000L);
} catch (InterruptedException e) {}
}
}
}
// Clean the buffer.
public synchronized void flush() {
//System.out.println("SunAudioOutput flush ");
in = 0;
out = 0;
sunAudioInitialCount = sunAudioFinalCount = samplesPlayed;
flushing = true;
notifyAll();
}
public long getMediaNanoseconds() {
/*
double samples = (double)samplesPlayed;
//samples = samples/(double)format.getSampleRate();
// SunAudioOutput plays at 8 Khz
samples = samples/(double)8000;
// System.out.println("AudioPlay.getTick() " + samples);
return (audioStream == null ? 0 : (long)(samples * 1000000000L) );
*/
return (audioStream == null ? 0 : samplesPlayed * 125000L);
}
public void setMute(boolean m) {
// System.out.println("AudioPlay.setMute()");
isMuted = m;
}
public boolean getMute() {
return isMuted;
}
public void setGain(double g) {
}
public double getGain() {
return 0f;
}
public float setRate(float r) {
return 1.0f;
}
public float getRate() {
return 1.0f;
}
public int dataAvailable() {
if (in == out)
return 0;
else {
if (in > out)
return in - out;
else
return bufLength - (out - in);
}
}
public int bufferAvailable() {
if (SunAudioRenderer.runningOnMac)
return 0;
return bufLength - dataAvailable() - 1;
}
// Read a byte of data. Block if there is no data to read.
public synchronized int read() {
// Block if the buffer is empty.
while (in == out) {
if (eom) {
eom = false;
return EOM;
}
try {
wait();
} catch (InterruptedException e) {
}
}
int ret = buffer[out++] & 0xFF;
if (out >= buffer.length) {
out = 0;
}
return ret;
}
// $$ Non blocking read
public synchronized int read(byte b[], int off, int len) {
//System.out.println("AP:needs: " + len + " available: " + dataAvailable());
//System.out.println("AP: read3: " + Thread.currentThread() + ": " +
// Thread.currentThread().getPriority() +
// ": paused, avail: " + paused + ": " + available());
int inputLength=len;
if (len <= 0) {
return -1;
}
if ( (len>4) && (!internalDelayUpdate) ) {
//System.out.println("sunAudioInternalDelay "+len);
internalDelayUpdate=true;
timeUpdatingThread.setInternalDelay(len);
}
if (dataAvailable() == 0) {
//System.out.println("underflow - no data: " + inputLength);
System.arraycopy(silence, 0, b, off, inputLength);
//timeUpdatingThread.resetSampleCountTime();
//sunAudioInitialCount=sunAudioFinalCount;
silenceCount += inputLength;
return inputLength;
}
// This read will not block
int c = read();
if (c < 0) {
return -1;
}
b[off] = (byte) c;
int rlen = 1;
if ( in != out ) {
int avail, need, size;
len--; // 1 byte read and copied.
if (out < in) {
avail = (in - out);
if (avail > len)
avail = len;
System.arraycopy(buffer, out, b, off+1, avail);
out += avail;
rlen += avail;
} else if ( out > in ) {
avail = bufLength - out;
if (avail >= len) {
avail = len;
System.arraycopy(buffer, out, b, off+1, avail);
out += avail;
if (out >= bufLength)
out = 0;
rlen += avail;
} else {
System.arraycopy(buffer, out, b, off+1, avail);
out += avail;
if (out >= bufLength)
out = 0;
int copied = avail;
rlen += avail;
need = (len - avail);
avail = (in - out);
if (need <= avail)
size = need;
else
size = avail;
System.arraycopy(buffer, 0, b, off+1+copied, size);
out += size;
rlen += size;
}
}
}
// Notify if there's any waiting writer.
if (isMuted) {
//System.err.println("muted -- fill with silence");
System.arraycopy(silence, 0, b, off, inputLength);
} else {
if (rlen<inputLength) {
// pad the rest of the buffer with silence
// but don't update the sample count
//System.out.println("underflow - pad with silence: " + (inputLength-rlen));
System.arraycopy(silence, 0, b, off+rlen, inputLength-rlen);
silenceCount += (inputLength-rlen);
} else if (silenceCount > 0) {
// There were some silence filled in before. We'll
// need to compensate for that.
if (silenceCount > rlen) {
silenceCount -= rlen;
rlen = 0;
} else {
rlen -= silenceCount;
silenceCount = 0;
}
}
}
timeUpdatingThread.resetSampleCountTime();
sunAudioInitialCount=sunAudioFinalCount;
sunAudioFinalCount+=rlen;
notifyAll();
return inputLength;
}
// Write an array of bytes to the buffer. Block until there's
// enough space in the buffer.
public synchronized int write(byte data[], int off, int len) {
//System.out.println("SunAudioOutput.write , try len"+len+" in "+in+" out "+out);
//System.out.println("abc: SunAudioOutput.write "+len);
flushing = false;
if (len <= 0)
return 0;
// Block if the buffer is full.
while ((in + 1) % buffer.length == out) {
try {
wait();
} catch (InterruptedException e) {
}
}
if (flushing) {
return 0;
}
int wlen = 0;
if (true) {
int canWrite, actualWrite, actualWrite1, length1;
if (in < out) {
canWrite = out - in -1;
actualWrite = (canWrite < len) ? canWrite : len;
System.arraycopy(data, off, buffer, in, actualWrite);
in += actualWrite;
wlen += actualWrite;
} else {
if (out == 0)
length1 = bufLength - in - 1;
else
length1 = bufLength - in;
if (length1 >= len) {
actualWrite = len;
System.arraycopy(data, off, buffer, in, actualWrite);
in += actualWrite;
if (in >= bufLength)
in = 0;
wlen += actualWrite;
} else {
actualWrite = length1;
System.arraycopy(data, off, buffer, in, actualWrite);
in += actualWrite;
if (in >= bufLength)
in = 0;
wlen += actualWrite;
len -= actualWrite;
actualWrite1 = actualWrite;
if (out > 0) {
canWrite = out -in -1;
actualWrite = (canWrite < len) ? canWrite : len;
System.arraycopy(data, off+actualWrite1,
buffer, 0, actualWrite);
wlen += actualWrite;
in = actualWrite;
}
}
}
}
// Notify the waiting reader.
notifyAll();
//System.out.println("before wlen "+wlen+" in "+in+" out "+out);
//saveInput(data,off,wlen);
return wlen;
}
/*
private void saveInput(byte [] indata,int offset, int length) {
try {
if (IN == null)
IN = new FileOutputStream("audio.pcm");
IN.write(indata, offset, length);
IN.flush();
} catch (Exception e) {
System.out.println("Frame not saved: "+e);
}
}
*/
protected void setPaddingLength(int paddingLength) {
//System.out.println("SunAudioOutput setPaddingLength "+ paddingLength);
endOfMediaPaddingLength = paddingLength;
if (endOfMediaPaddingLength > silence.length)
endOfMediaPaddingLength = silence.length;
}
}
/**
* This class used to be an inner class, which is the correct thing to do.
* Changed it to a package private class because of jdk1.2 security.
* For jdk1.2 and above applets, SunAudioPlayThread is created in a
* privileged block using jdk12CreateThreadAction. jdk12CreateThreadAction
* class is unable to create and instantiate an inner class
* in SunAudioOutput class
*/
class SunAudioPlayThread extends LoopThread {
long initialTime;
long currentTime;
int samplesUpdated;
int sunAudioInternalDelay=-1;
SunAudioOutput sunAudioOutput;
SunAudioPlayThread() {
setName(getName() + ": " + this);
}
void setStream(SunAudioOutput s) {
sunAudioOutput = s;
}
public void resetSampleCountTime() {
initialTime=System.currentTimeMillis();
}
public synchronized void start() {
currentTime=System.currentTimeMillis();
super.start();
}
public void setInternalDelay(int delay) {
if (delay>=0)
sunAudioInternalDelay=delay;
// SunAudioOutput.this.setPaddingLength(sunAudioInternalDelay*2);
sunAudioOutput.setPaddingLength(sunAudioInternalDelay*2);
}
protected boolean process() {
try {
Thread.sleep(sunAudioOutput. SLEEP_TIME); // constant updating delay
}
catch (InterruptedException e) {
//System.out.println("SunAudioPlayThread interrupted");
}
if (sunAudioInternalDelay>=0) {
currentTime = System.currentTimeMillis();
samplesUpdated = (int) ((currentTime-initialTime)*8L);
if ( (samplesUpdated>=0) && (!sunAudioOutput.paused) ){
int tmpSamplesPlayed= sunAudioOutput.sunAudioInitialCount+samplesUpdated;
if ( (tmpSamplesPlayed > sunAudioOutput.samplesPlayed) &&
( tmpSamplesPlayed<=sunAudioOutput.sunAudioFinalCount) ) {
if ( (tmpSamplesPlayed-sunAudioInternalDelay) > sunAudioOutput.samplesPlayed) {
sunAudioOutput.samplesPlayed=tmpSamplesPlayed-sunAudioInternalDelay;
}
}
}
}
return true;
}
}
|