/**
* This file is protected by Copyright.
* Please refer to the COPYRIGHT file distributed with this source distribution.
*
* This file is part of REDHAWK IDE.
*
* All rights reserved. This program and the accompanying materials are made available under
* the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html.
*
*/
package gov.redhawk.ui.port.playaudio.internal.corba;
import gov.redhawk.sca.util.ORBUtil;
import gov.redhawk.sca.util.OrbSession;
import gov.redhawk.sca.util.PluginUtil;
import gov.redhawk.ui.port.playaudio.controller.AudioController;
import gov.redhawk.ui.port.playaudio.internal.Activator;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.TCKind;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAPackage.ObjectNotActive;
import org.omg.PortableServer.POAPackage.WrongAdapter;
import org.omg.PortableServer.POAPackage.WrongPolicy;
import BULKIO.PortStatistics;
import BULKIO.PortUsageType;
import BULKIO.PrecisionUTCTime;
import BULKIO.StreamSRI;
import BULKIO.dataCharOperations;
import BULKIO.dataDoubleOperations;
import BULKIO.dataFloatOperations;
import BULKIO.dataOctetOperations;
import BULKIO.dataShortOperations;
import BULKIO.dataUlongOperations;
import CF.DataType;
import CF.Port;
import CF.PortHelper;
import CF.PortPackage.InvalidPort;
/**
* This class connects to the specified CORBA host and receives data. It then
* writes the data for sound playback. Generics are used to receive all types of
* data(Octet(byte)/Short/Long/Float/Double).
* @deprecated Use new {@link gov.redhawk.ui.port.playaudio.controller.AudioReceiver}
*/
@Deprecated
public class CorbaReceiver implements dataShortOperations, dataCharOperations, dataOctetOperations, dataUlongOperations, dataFloatOperations,
dataDoubleOperations {
private static final int PIPE_SIZE = 1024000;
// Keyword Constants
//private static final String BIGENDIAN = "BigEndian";
private static final String CHANNELS = "AUDIO_CHANNELS";
private static final String ENCODING = "AUDIO_ENCODING";
private static final String FRAMERATE = "AUDIO_FRAME_RATE";
private static final String FRAMESIZE = "AUDIO_FRAME_SIZE";
/** The maximum number of times to retry the initial ORB connection */
private static final int MAX_RETRIES = 5;
// Sample size constants for 8, 16 and 32bit sample sizes
private static final int BYTE_SAMPLE_SIZE = 8;
private static final int SHORT_SAMPLE_SIZE = 16;
private static final int INT_SAMPLE_SIZE = 32;
/** Time to sleep when waiting */
private static final long SLEEP_TIME = 1000;
/** Time to sleep when waiting */
private static final long EMPTY_QUEUE_TIME = 10;
private static final int DOUBLE_BYTE_SIZE = 8;
private OrbSession session = OrbSession.createSession();
private POA rootpoa;
/** flag if we're done processing */
private boolean isDone = false;
/** The list of port connections */
private final Map<Port, String> connections = new HashMap<Port, String>();
/** The list of ior to ports */
private final Map<String, Port> ports = new HashMap<String, Port>();
/** The list of port connections */
private final Map<Port, org.omg.CORBA.Object> ties = new HashMap<Port, org.omg.CORBA.Object>();
/** Format of the incoming data */
private String format = "";
/** whether or not the class has been configured for receiving data */
private boolean configured = false;
/** The temporary buffer to store samples in when input size != bufferLength */
private ByteBuffer tempBuffer = null;
/** Lock object for the tempBuffer */
private final Object tempBuffLock = new Object();
/** The parent of this object, used to update the audio format display */
private final AudioController parent;
/** current length of the buffer */
private int bufferLength = 0;
/** The current AudioFormat used for playback */
private AudioFormat audioFormat = null;
/** The source line that data is written to */
private SourceDataLine sourceDataLine = null;
/** Flag to stop playing data, also shutsdown the playThread */
private volatile boolean stopPlayback = false;
/** Flag to pause playback */
private boolean paused = false;
/** The thread that initializes playing back the data */
private volatile Thread playThread = null;
/** The lock object for setting up the playThread */
private final Object playThreadLock = new Object();
/** The current audio format samplesize */
private int sampleSize = CorbaReceiver.SHORT_SAMPLE_SIZE;
/** Lock object for the pipes */
private final Object pipeLock = new Object();
/** The pipe for pushing data to the sourceDataLine for playback */
private PipedInputStream pipeIn;
/** The pipe for queueing data for playback */
private PipedOutputStream pipeOut;
/**
* The constructor to connect to ports and playback data.
*
* @param audioController the parent that spawned me
* @param port the IOR of the port to connect to
* @param format the format of the ports data
*/
public CorbaReceiver(final AudioController audioController, final String format) throws CoreException {
this.isDone = false;
this.createPipes();
rootpoa = session.getPOA();
this.parent = audioController;
this.format = format;
switch (this.format.charAt(1)) {
case 'B':
this.sampleSize = CorbaReceiver.BYTE_SAMPLE_SIZE;
break;
case 'I':
this.sampleSize = CorbaReceiver.SHORT_SAMPLE_SIZE;
break;
case 'L':
this.sampleSize = CorbaReceiver.INT_SAMPLE_SIZE;
break;
default:
break;
}
}
private void createPipes() throws CoreException {
synchronized (this.pipeLock) {
this.pipeOut = new PipedOutputStream();
this.pipeIn = new PipedInputStream() {
@Override
public void connect(final PipedOutputStream src) throws IOException {
this.buffer = new byte[CorbaReceiver.PIPE_SIZE];
super.connect(src);
}
};
try {
this.pipeIn.connect(this.pipeOut);
} catch (final IOException e) {
throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unable to connect pipes", e));
}
}
}
/**
* This method is used to connect to a different port for playback.
*
* @param format the format of the port's data
* @param port the IOR of the port
* @return true if a successful connection was made
*/
public boolean connectToPort(final String format, final String port) {
this.format = format;
switch (this.format.charAt(1)) {
case 'B':
this.sampleSize = CorbaReceiver.BYTE_SAMPLE_SIZE;
break;
case 'I':
this.sampleSize = CorbaReceiver.SHORT_SAMPLE_SIZE;
break;
case 'L':
this.sampleSize = CorbaReceiver.INT_SAMPLE_SIZE;
break;
default:
break;
}
this.disconnect(null, false);
return connectPort(TieFactory.getTie(this.format, session.getOrb(), this.rootpoa, this), port);
}
/**
* This method disconnects all the ports and stops playback.
*/
public void shutdown() {
this.stopPlayback = true;
try {
if (this.playThread != null) {
this.playThread.join();
}
} catch (final InterruptedException e) {
// PASS
}
this.playThread = null;
// Disconnect the ports
disconnect(null, false);
// Shutdown the ORB connection
session.dispose();
rootpoa = null;
this.isDone = true;
}
/**
* This method will pause or resume writing of data to the audio playback.
*
* @param paused true if we should pause playback
*/
public void setPaused(final boolean paused) {
this.paused = paused;
}
/**
* This method connects the given Tie object to the port from the arguments
* list. It will retry MAX_RETRIES times before giving up trying to connect.
*
* @param tie the Tie CORBA object. This is generic to allow any type of
* connection.
* @return true if the port was successfully connected
*/
private boolean connectPort(final org.omg.CORBA.Object tie, final String ior) {
if (tie == null) {
return false;
}
this.stopPlayback = false;
int retry = 0;
while (retry < CorbaReceiver.MAX_RETRIES) {
try {
// If ncRef was null, we were passed an IOR for the
// port. Use this to directly resolve the port.
final org.omg.CORBA.Object portRef = session.getOrb().string_to_object(ior);
final Port port = PortHelper.narrow(portRef);
// Make a new connection from the port to the given object
final UUID uuid = UUID.randomUUID();
port.connectPort(tie, uuid.toString());
// Store the connected port for later cleanup
this.ports.put(ior, port);
this.connections.put(port, uuid.toString());
this.ties.put(port, tie);
break;
} catch (final Exception e) { // SUPPRESS CHECKSTYLE Exception on Connect retry
if (++retry == CorbaReceiver.MAX_RETRIES) {
this.isDone = true;
}
try {
Thread.sleep(CorbaReceiver.SLEEP_TIME);
} catch (final InterruptedException i) {
// PASS
}
}
}
return !this.isDone;
}
/**
* This method will disconnect one or all of the connected port from
* playback. Currently there is only one connected port at a time.
*
* @param ior the IOR if known, or null to disconnect everything
* @param force this is called when the waveform has been released, the port
* isn't there anymore
*/
public void disconnect(final String ior, final boolean force) {
// Set the stop flags
this.stopPlayback = true;
try {
if (this.playThread != null) {
this.playThread.join();
}
} catch (final InterruptedException e) {
// PASS
}
this.playThread = null;
// cleanup the port connections
if (ior != null) {
final Port port = this.ports.remove(ior);
final String conn = this.connections.remove(port);
final org.omg.CORBA.Object tie = this.ties.remove(port);
if (port != null) {
byte[] oid;
try {
if (!force) {
port.disconnectPort(conn);
}
} catch (final InvalidPort i) {
// PASS
} catch (final SystemException s) {
// PASS
}
try {
oid = this.rootpoa.reference_to_id(tie);
this.rootpoa.deactivate_object(oid);
} catch (final ObjectNotActive e) {
// PASS
} catch (final WrongPolicy e) {
// PASS
} catch (final WrongAdapter e) {
// PASS
}
ORBUtil.release(port);
}
} else {
// loop through all the ports and disconnect/release
for (final String portIOR : this.ports.keySet()) {
final Port port = this.ports.get(portIOR);
final org.omg.CORBA.Object tie = this.ties.get(port);
try {
if (!force) {
port.disconnectPort(this.connections.get(port));
}
} catch (final InvalidPort i) {
// PASS
} catch (final SystemException s) {
// PASS
}
try {
final byte[] oid = this.rootpoa.reference_to_id(tie);
this.rootpoa.deactivate_object(oid);
} catch (final ObjectNotActive e) {
// PASS
} catch (final WrongPolicy e) {
// PASS
} catch (final WrongAdapter e) {
// PASS
}
}
this.ports.clear();
this.connections.clear();
this.ties.clear();
}
}
/**
* This will determine if we should continue processing the packet. To
* process, the endOfStream must be false and we must have previously
* received SRI.
*
* @param endOfStream true if the received stream has closed
* @param streamID the ID of the stream this packet is for
* @param length length of the data received
* @return true if we should process the data
*/
private final boolean processPacket(final boolean endOfStream, final String streamID, final int length) {
boolean status = true;
if (endOfStream) {
this.isDone = true;
status = false;
} else if (this.paused || this.stopPlayback || !this.configured || (this.sourceDataLine == null)) {
status = false;
} else {
if ((this.tempBuffer == null) || (this.bufferLength < length)) {
this.bufferLength = length;
final int capacity = (this.tempBuffer == null) ? 0 : this.tempBuffer.capacity(); // SUPPRESS CHECKSTYLE AvoidInline
final int newSize = this.bufferLength * CorbaReceiver.DOUBLE_BYTE_SIZE;
if (newSize > capacity) {
synchronized (this.tempBuffLock) {
this.tempBuffer = ByteBuffer.allocate(newSize);
final boolean bigEndian = this.audioFormat.isBigEndian();
this.tempBuffer.order((bigEndian) ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); // SUPPRESS CHECKSTYLE AvoidInline
}
}
}
}
return status;
}
/**
* This method receives a byte array from CORBA and writes it to the opened
* pipe.
*
* @param byteArray the array of byte data to write out
* @param time the time the data was received
* @param endOfStream true if the stream has ended
* @param streamID string ID of the stream of data
*/
@Override
public void pushPacket(final byte[] byteArray, final PrecisionUTCTime time, final boolean endOfStream, final String streamID) {
if (!processPacket(endOfStream, streamID, byteArray.length)) {
return;
}
writeByteData(byteArray);
}
/**
* This method receives a char array from CORBA and writes it to the opened
* pipe.
*
* @param charArray the array of char data to write out
* @param time the time the data was received
* @param endOfStream true if the stream has ended
* @param streamID string ID of the stream of data
*/
@Override
public void pushPacket(final char[] charArray, final PrecisionUTCTime time, final boolean endOfStream, final String streamID) {
if (!processPacket(endOfStream, streamID, charArray.length)) {
return;
}
writeCharData(charArray);
}
/**
* This method receives a short array from CORBA and writes it to the opened
* pipe.
*
* @param shortArray the array of short data to write out
* @param time the time the data was received
* @param endOfStream true if the stream has ended
* @param streamID string ID of the stream of data
*/
@Override
public void pushPacket(final short[] shortArray, final PrecisionUTCTime time, final boolean endOfStream, final String streamID) {
if (!processPacket(endOfStream, streamID, shortArray.length)) {
return;
}
writeShortData(shortArray);
}
/**
* This method receives an int array from CORBA and writes it to the opened
* pipe.
*
* @param intArray the array of int data to write out
* @param time the time the data was received
* @param endOfStream true if the stream has ended
* @param streamID string ID of the stream of data
*/
@Override
public void pushPacket(final int[] intArray, final PrecisionUTCTime time, final boolean endOfStream, final String streamID) {
if (!processPacket(endOfStream, streamID, intArray.length)) {
return;
}
writeIntData(intArray);
}
/**
* This method receives an float array from CORBA and writes it to the
* opened pipe.
*
* @param intArray the array of float data to write out
* @param time the time the data was received
* @param endOfStream true if the stream has ended
* @param streamID string ID of the stream of data
*/
@Override
public void pushPacket(final float[] floatArray, final PrecisionUTCTime time, final boolean endOfStream, final String streamID) {
if (!processPacket(endOfStream, streamID, floatArray.length)) {
return;
}
writeFloatData(floatArray);
}
/**
* This method receives an double array from CORBA and writes it to the
* opened pipe.
*
* @param doubleArray the array of double data to write out
* @param time the time the data was received
* @param endOfStream true if the stream has ended
* @param streamID string ID of the stream of data
*/
@Override
public void pushPacket(final double[] doubleArray, final PrecisionUTCTime time, final boolean endOfStream, final String streamID) {
if (!processPacket(endOfStream, streamID, doubleArray.length)) {
return;
}
writeDoubleData(doubleArray);
}
@Override
public PortUsageType state() {
// TODO Auto-generated method stub
return null;
}
@Override
public PortStatistics statistics() {
// TODO Auto-generated method stub
return null;
}
/**
* This method writes out a byte Array to the pipe in the desired format for
* playback.
*
* @param byteArray the data to write out
*/
private void writeByteData(final byte[] byteArray) {
synchronized (this.tempBuffLock) {
for (final byte b : byteArray) {
switch (this.sampleSize) {
case BYTE_SAMPLE_SIZE:
this.tempBuffer.put(b);
break;
case SHORT_SAMPLE_SIZE:
this.tempBuffer.putShort(b);
break;
case INT_SAMPLE_SIZE:
this.tempBuffer.putInt(b);
break;
default:
break;
}
}
if (!this.stopPlayback) {
try {
this.pipeOut.write(this.tempBuffer.array(), 0, byteArray.length * this.audioFormat.getFrameSize());
} catch (final IOException e) {
synchronized (this.pipeLock) {
try {
this.createPipes();
} catch (final CoreException e1) {
// PASS
}
}
}
}
this.tempBuffer.position(0);
}
}
/**
* This method writes out a char Array to the pipe in the desired format for
* playback.
*
* @param charArray the data to write out
*/
private void writeCharData(final char[] charArray) {
synchronized (this.tempBuffLock) {
for (final char c : charArray) {
switch (this.sampleSize) {
case BYTE_SAMPLE_SIZE:
this.tempBuffer.putChar(c);
break;
case SHORT_SAMPLE_SIZE:
this.tempBuffer.putShort((short) c);
break;
case INT_SAMPLE_SIZE:
this.tempBuffer.putInt(c);
break;
default:
break;
}
}
if (!this.stopPlayback) {
try {
this.pipeOut.write(this.tempBuffer.array(), 0, charArray.length * this.audioFormat.getFrameSize());
} catch (final IOException e) {
synchronized (this.pipeLock) {
try {
this.createPipes();
} catch (final CoreException e1) {
// PASS
}
}
}
}
this.tempBuffer.position(0);
}
}
/**
* This method writes out a short Array to the pipe in the desired format
* for playback.
*
* @param shortArray the data to write out
*/
private void writeShortData(final short[] shortArray) {
synchronized (this.tempBuffLock) {
for (final short s : shortArray) {
switch (this.sampleSize) {
case BYTE_SAMPLE_SIZE:
this.tempBuffer.put((byte) s);
break;
case SHORT_SAMPLE_SIZE:
this.tempBuffer.putShort(s);
break;
case INT_SAMPLE_SIZE:
this.tempBuffer.putInt(s);
break;
default:
break;
}
}
if (!this.stopPlayback) {
try {
this.pipeOut.write(this.tempBuffer.array(), 0, shortArray.length * this.audioFormat.getFrameSize());
} catch (final IOException e) {
synchronized (this.pipeLock) {
try {
this.createPipes();
} catch (final CoreException e1) {
// PASS
}
}
}
}
this.tempBuffer.position(0);
}
}
/**
* This method writes out a int Array to the pipe in the desired format for
* playback.
*
* @param intArray the data to write out
*/
private void writeIntData(final int[] intArray) {
synchronized (this.tempBuffLock) {
for (final int i : intArray) {
switch (this.sampleSize) {
case BYTE_SAMPLE_SIZE:
this.tempBuffer.put((byte) i);
break;
case SHORT_SAMPLE_SIZE:
this.tempBuffer.putShort((short) i);
break;
case INT_SAMPLE_SIZE:
this.tempBuffer.putInt(i);
break;
default:
break;
}
}
try {
this.pipeOut.write(this.tempBuffer.array(), 0, intArray.length * this.audioFormat.getFrameSize());
} catch (final IOException e) {
synchronized (this.pipeLock) {
try {
this.createPipes();
} catch (final CoreException e1) {
// PASS
}
}
}
this.tempBuffer.position(0);
}
}
/**
* This method writes out a float Array to the pipe in the desired format
* for playback.
*
* @param floatArray the data to write out
*/
private void writeFloatData(final float[] floatArray) {
synchronized (this.tempBuffLock) {
for (final float f : floatArray) {
switch (this.sampleSize) {
case BYTE_SAMPLE_SIZE:
this.tempBuffer.put((byte) f);
break;
case SHORT_SAMPLE_SIZE:
this.tempBuffer.putShort((short) f);
break;
case INT_SAMPLE_SIZE:
this.tempBuffer.putInt((int) f);
break;
default:
break;
}
}
try {
this.pipeOut.write(this.tempBuffer.array(), 0, floatArray.length * this.audioFormat.getFrameSize());
} catch (final IOException e) {
synchronized (this.pipeLock) {
try {
this.createPipes();
} catch (final CoreException e1) {
// PASS
}
}
}
this.tempBuffer.position(0);
}
}
/**
* This method writes out a double Array to the pipe in the desired format
* for playback.
*
* @param doubleArray the data to write out
*/
private void writeDoubleData(final double[] doubleArray) {
synchronized (this.tempBuffLock) {
for (final double d : doubleArray) {
switch (this.sampleSize) {
case BYTE_SAMPLE_SIZE:
this.tempBuffer.put((byte) d);
break;
case SHORT_SAMPLE_SIZE:
this.tempBuffer.putShort((short) d);
break;
case INT_SAMPLE_SIZE:
this.tempBuffer.putInt((int) d);
break;
default:
break;
}
}
try {
this.pipeOut.write(this.tempBuffer.array(), 0, doubleArray.length * this.audioFormat.getFrameSize());
} catch (final IOException e) {
synchronized (this.pipeLock) {
try {
this.createPipes();
} catch (final CoreException e1) {
// PASS
}
}
}
this.tempBuffer.position(0);
}
}
/**
* This receives the SRI and updates or modifies the output pipe.
*
* @param sri the SRI containing information about the incoming data
*/
@Override
public void pushSRI(final StreamSRI sri) {
final boolean oldStop = this.stopPlayback;
// Check if the SRI is different
if (!this.checkSRI(sri)) {
// If it is, stop the playback so we can reset the audio
this.stopPlayback = true;
try {
if (this.playThread != null) {
this.playThread.join();
}
} catch (final InterruptedException e) {
// PASS
}
this.stopPlayback = oldStop;
}
if (!oldStop && (this.playThread == null)) {
AudioFormat.Encoding enc = AudioFormat.Encoding.PCM_SIGNED; // Default for BULKIO is just linear data, although we should check the port type to see if signed or unsigned is appropriate.
int channels = 1; // by default for BULKIO everything is mono
int frameSize = (this.sampleSize / 8) * channels; // By default for PCM_SIGNED the frameSize is always equals to number of bytes * number of channels
float sampleRate = Math.round(1.0f / (float) sri.xdelta);
float frameRate = sampleRate; // By default, for PCM frameRate equals sampleRate
for (final DataType word : sri.keywords) {
final int tkValue = word.value.type().kind().value();
if (CorbaReceiver.ENCODING.equals(word.id)) {
if (tkValue == TCKind._tk_string) {
enc = new AudioFormat.Encoding(word.value.extract_string());
} else {
Activator.logWarn("Received invalid type for " + CorbaReceiver.ENCODING + " keyword. Expected string, got: " + tkValue, null);
}
} else if (CorbaReceiver.CHANNELS.equals(word.id)) {
if (tkValue == TCKind._tk_long) {
channels = word.value.extract_long();
} else {
Activator.logWarn("Received invalid type for " + CorbaReceiver.CHANNELS + " keyword. Expected long, got: " + tkValue, null);
}
} else if (CorbaReceiver.FRAMESIZE.equals(word.id)) {
if (tkValue == TCKind._tk_long) {
frameSize = word.value.extract_long();
} else {
Activator.logWarn("Received invalid type for " + CorbaReceiver.FRAMESIZE + " keyword. Expected long, got: " + tkValue, null);
}
} else if (CorbaReceiver.FRAMERATE.equals(word.id)) {
if (tkValue == TCKind._tk_float) {
frameRate = word.value.extract_float();
} else {
Activator.logWarn("Received invalid type for " + CorbaReceiver.FRAMERATE + " keyword. Expected float, got: " + tkValue, null);
}
}
}
// If no encoding is given we cannot play the port. Just return, the ViewPart will inform the user that the encoding is null
if (enc == null) {
return;
}
try {
this.audioFormat = new AudioFormat(enc,
sampleRate,
this.sampleSize,
channels,
frameSize,
frameRate,
(ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN));
this.parent.newFormat(this.audioFormat);
final DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, this.audioFormat);
this.sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
} catch (final NullPointerException e) {
Activator.logError("No Audio keywords in SRI", e);
this.sourceDataLine = null;
} catch (final LineUnavailableException e) {
Activator.logError("Error getting audio line for playback", e);
this.sourceDataLine = null;
} catch (Exception e) { // SUPPRESS CHECKSTYLE Logged Catch all exception
Activator.logError("Error getting audio line for playback", e);
this.sourceDataLine = null;
}
if (this.sourceDataLine != null) {
this.stopPlayback = false;
synchronized (this.playThreadLock) {
this.playThread = new PlayThread("Audio Playback for Stream: " + sri.streamID);
this.playThread.start();
}
}
}
}
/**
* This method checks to see if the relevant SRI has changed
*
* @param sri the StreamSRI to check
* @return true if the SRI is the same, false if something has changed
*/
private boolean checkSRI(final StreamSRI sri) {
if (this.audioFormat == null) {
return false;
}
AudioFormat.Encoding enc = this.audioFormat.getEncoding();
int channels = this.audioFormat.getChannels();
int frameSize = this.audioFormat.getFrameSize();
float frameRate = this.audioFormat.getFrameRate();
for (final DataType word : sri.keywords) {
final int tkValue = word.value.type().kind().value();
if (CorbaReceiver.ENCODING.equals(word.id)) {
if (tkValue == TCKind._tk_string) {
enc = new AudioFormat.Encoding(word.value.extract_string());
}
} else if (CorbaReceiver.CHANNELS.equals(word.id)) {
if (tkValue == TCKind._tk_long) {
channels = word.value.extract_long();
}
} else if (CorbaReceiver.FRAMESIZE.equals(word.id)) {
if (tkValue == TCKind._tk_long) {
frameSize = word.value.extract_long();
}
} else if (CorbaReceiver.FRAMERATE.equals(word.id)) {
if (tkValue == TCKind._tk_float) {
frameRate = word.value.extract_float();
}
}
}
return (enc.equals(this.audioFormat.getEncoding()) && (channels == this.audioFormat.getChannels()) && (frameSize == this.audioFormat.getFrameSize())
&& compareFloats(frameRate, this.audioFormat.getFrameRate()) && compareFloats((1.0f / (float) sri.xdelta), this.audioFormat.getSampleRate()));
}
private boolean compareFloats(float f1, float f2) {
return PluginUtil.equals(f1, f2);
}
private class PlayThread extends Thread {
public PlayThread(final String string) {
super(string);
}
@Override
public void run() {
// Have a buffer of ~ .5 sec. Basically, it will take 0.5 seconds before the audio
// starts to play...but this ensures that the incoming stream can have a bit of
// jitter and still play continously
final int frameSize = CorbaReceiver.this.audioFormat.getFrameSize();
final int PLAYBACK_SIZE = (int) ((0.5 * CorbaReceiver.this.audioFormat.getFrameRate()) * frameSize);
final byte[] buf = new byte[PLAYBACK_SIZE];
try {
CorbaReceiver.this.configured = true;
CorbaReceiver.this.sourceDataLine.open(CorbaReceiver.this.audioFormat, PLAYBACK_SIZE);
CorbaReceiver.this.sourceDataLine.start();
while (!CorbaReceiver.this.stopPlayback) {
try {
if (CorbaReceiver.this.pipeIn.available() > 0) {
try {
int avail;
final int read;
synchronized (CorbaReceiver.this.pipeLock) {
avail = CorbaReceiver.this.pipeIn.available();
avail = Math.min(avail, PLAYBACK_SIZE);
read = CorbaReceiver.this.pipeIn.read(buf, 0, (avail - (avail % frameSize)));
}
if (read == -1) {
CorbaReceiver.this.stopPlayback = true;
break;
}
CorbaReceiver.this.sourceDataLine.write(buf, 0, read);
} catch (final IOException e) {
// PASS
}
} else {
Thread.sleep(CorbaReceiver.EMPTY_QUEUE_TIME);
}
} catch (final IOException e) {
Thread.sleep(CorbaReceiver.EMPTY_QUEUE_TIME);
}
}
} catch (final LineUnavailableException e) {
Activator.logError("Error opening Audio Playback line. Playback unavailable.", e);
} catch (final InterruptedException e) {
// PASS
} catch (Exception e) { // SUPPRESS CHECKSTYLE Logged Error
Activator.logError("Error opening Audio Playback line. Playback unavailable.", e);
} finally {
CorbaReceiver.this.configured = false;
CorbaReceiver.this.audioFormat = null;
flushStreams();
CorbaReceiver.this.sourceDataLine = null;
synchronized (CorbaReceiver.this.playThreadLock) {
CorbaReceiver.this.playThread = null;
}
}
}
private void flushStreams() {
try {
CorbaReceiver.this.pipeOut.flush();
} catch (final IOException e) {
// PASS
}
int avail = 0;
try {
avail = CorbaReceiver.this.pipeIn.available();
} catch (final IOException e) {
// PASS
}
while (avail > 0) {
try {
CorbaReceiver.this.pipeIn.skip(avail);
avail = CorbaReceiver.this.pipeIn.available();
} catch (final IOException e) {
// PASS
}
}
if (CorbaReceiver.this.sourceDataLine != null) {
CorbaReceiver.this.sourceDataLine.stop();
CorbaReceiver.this.sourceDataLine.flush();
CorbaReceiver.this.sourceDataLine.close();
}
}
}
@Override
public StreamSRI[] activeSRIs() {
// TODO Auto-generated method stub
return null;
}
}