package uk.co.mmscomputing.device.capi;
import java.io.*;
import javax.sound.sampled.*;
import uk.co.mmscomputing.concurrent.*;
import uk.co.mmscomputing.device.capi.sound.*;
import uk.co.mmscomputing.device.capi.protocol.*;
import uk.co.mmscomputing.device.capi.exception.*;
import uk.co.mmscomputing.device.capi.facility.*;
abstract public class CapiChannel implements CapiConstants{
static public final AudioFormat pcmformat = new AudioFormat(8000,16,1,true,false);
static public final AudioFormat alawformat= new AudioFormat(AudioFormat.Encoding.ALAW,8000,8,1,1,8000,false);
static public final AudioFormat ulawformat= new AudioFormat(AudioFormat.Encoding.ULAW,8000,8,1,1,8000,false);
protected boolean isopen = false;
private int connectState = 0; // 0 = not connected yet; 1 = connected; 2 = disconnected
protected boolean pickedUp = false;
protected boolean changedProtocol = false;
protected CapiApplication appl = null;
protected int applid = -1;
protected int lineid = -1;
protected BProtocol protocol = null;
protected CapiPlugin plugin = null;
private String localno = "";
private String remoteno = "";
protected int speechcoding = -1;
private Semaphore pickupBlocker = null; // see CapiCallApplication, CapiServerApplication
public CapiChannel(CapiApplication appl,int lineid){
this.appl=appl;
this.applid=appl.getApplID();
this.lineid=lineid;
}
public boolean isOpen(){return isopen;}
public void close()throws IOException{};
synchronized protected boolean checkIsOpen(){
if(isopen==false){return false;}
isopen=false;
return true;
}
public boolean isConnected(){return (connectState==1);}
synchronized protected boolean checkIsNotConnected(){
if(connectState==0){
connectState=1;
isopen=true;
return true;
}
return false;
}
public boolean isDisconnected(){return (connectState==2);}
synchronized protected boolean checkIsNotDisconnected(){
if(connectState==2){return false;}
connectState=2;
isopen=false;
return true;
}
void setPickupBlocker(Semaphore s){pickupBlocker=s;} // the calling thread waits on 'pickupBlocker' until plc releases blocker
Semaphore getPickupBlocker(){return pickupBlocker;}
void releasePickupBlocker(){
if(pickupBlocker!=null){pickupBlocker.release();}
};
abstract public CapiInputStream getInputStream()throws IOException;
abstract public CapiOutputStream getOutputStream()throws IOException;
public PCMInputStream getPCMInputStream()throws IOException{
return new PCMInputStream(getInputStream(),isALaw());
}
public PCMOutputStream getPCMOutputStream()throws IOException{
return new PCMOutputStream(getOutputStream(),isALaw());
}
public AudioInputStream getAudioInputStream()throws IOException{
return new AudioInputStream(getPCMInputStream(),pcmformat,AudioSystem.NOT_SPECIFIED);
}
public int getApplID(){return applid;}
public int getLineID(){return lineid;}
public void put(MsgOut msg)throws CapiException{
appl.put(msg);
}
public int getCtrlId(){return lineid&0x0000007F;}
public BProtocol getProtocol(){return protocol;}
public void setProtocol(BProtocol protocol){this.protocol=protocol;}
public CapiPlugin getPlugin(){return plugin;}
public void setPlugin(CapiPlugin plugin){this.plugin=plugin;}
public String getLocalNo(){ return localno;}
public void setLocalNo(String no){ localno=no;}
public String getRemoteNo(){ return remoteno;}
public void setRemoteNo(String no){remoteno = no;}
public void setSpeechCoding(int sc){ speechcoding=sc;}
public void setALawSpeechCoding(){ speechcoding=LAYER1USERINFO_ALAW;}
public void setuLawSpeechCoding(){ speechcoding=LAYER1USERINFO_uLAW;}
public int getSpeechCoding(){ return speechcoding;}
public boolean isALaw()throws CapiException{
switch(speechcoding){
case LAYER1USERINFO_ALAW: return true;
case LAYER1USERINFO_uLAW: return false;
default: throw new CapiInformation(getClass().getName()+".isALaw:\n\tThis Channel does not seem to carry sound data !");
}
}
public boolean isuLaw()throws CapiException{
return !isALaw();
}
void setPickedUp(boolean pu){pickedUp=pu;}
public boolean getPickedUp(){return pickedUp;}
void setChangedProtocol(boolean value){changedProtocol=value;}
public boolean getChangedProtocol(){return changedProtocol;}
public void writeInputTo(OutputStream out)throws IOException{
InputStream in = getInputStream();
int count;
byte[] buffer = new byte[DefaultB3DataBlockSize];
while(isOpen()&&((count=in.read(buffer))!=-1)){
out.write(buffer,0,count);
}
out.flush();
}
public void writePCMInputTo(OutputStream out)throws IOException{
InputStream in = getPCMInputStream();
int count;
byte[] buffer = new byte[DefaultPCMBlockSize];
while(isOpen()&&((count=in.read(buffer))!=-1)){
out.write(buffer,0,count);
}
out.flush();
}
public void writeInputTo(SourceDataLine out)throws IOException{
InputStream in = getPCMInputStream();
int count;
byte[] buffer = new byte[DefaultPCMBlockSize];
out.start();
in.skip(in.available()); // discard data we couldn't process in time
while(isOpen()&&((count=in.read(buffer))!=-1)){
out.write(buffer,0,count);
}
out.drain();
out.stop();
}
public void writeToOutput(InputStream in)throws IOException{
OutputStream out = getOutputStream();
int count;
byte[] buffer = new byte[DefaultB3DataBlockSize];
while(isOpen()&&((count=in.read(buffer))!=-1)){
out.write(buffer,0,count);
}
out.flush();
}
public void writeToPCMOutput(InputStream in)throws IOException{
OutputStream out = getPCMOutputStream();
int count;
byte[] buffer = new byte[DefaultPCMBlockSize];
while(isOpen()&&((count=in.read(buffer))!=-1)){
out.write(buffer,0,count);
}
out.flush();
}
public void writeToOutput(TargetDataLine in)throws IOException{
OutputStream out = getPCMOutputStream();
int count;
byte[] buf=new byte[DefaultPCMBlockSize];
in.start();
while(isOpen()&&((count=in.read(buf,0,buf.length))!=-1)){
out.write(buf,0,count);
}
out.flush();
}
public void writeToOutput(AudioInputStream in)throws IOException{
if(!((AudioInputStream)in).getFormat().equals(pcmformat)){
in=AudioSystem.getAudioInputStream(pcmformat,(AudioInputStream)in);
}
OutputStream out = getPCMOutputStream();
int count;
byte[] buffer = new byte[DefaultPCMBlockSize];
while(isOpen()&&((count=in.read(buffer))!=-1)){
out.write(buffer,0,count);
}
out.flush();
}
// ---- DTMF ----
private boolean isdtmfenabled=false;
public boolean isDTMFEnabled(){return isdtmfenabled;}
public void startDTMF()throws IOException{
isdtmfenabled=(isopen)?true:false;
if(isopen){appl.put(DTMFReq.getStartReq(applid,lineid));}
}
public void stopDTMF()throws IOException{
isdtmfenabled=false;
if(isopen){appl.put(DTMFReq.getStopReq(applid,lineid));}
}
public void sendDTMF(String digits)throws IOException{
if(isopen){appl.put(DTMFReq.getSendReq(applid,lineid,digits));}
}
public void receivedDTMFConf(DTMFConf msg){
if(msg.getInfo()!=0){ // if error with dtmf request
isdtmfenabled=false; // assume controller does not support dtmf
}
}
private Semaphore blocker = null;
private StringBuffer dtmf = null;
public synchronized void receivedDTMFInd(DTMFInd msg){ // capi thread
/*
dtmf 0..9,*,#,A..D
cng-fax tone ='X' 1.1kHz
ced-fax tone ='Y' 2.1kHz
*/
String digits=msg.getDigits(); // System.out.println("Received digits: "+digits);
if(blocker!=null){ // if a thread is waiting
dtmf.append(digits); // append digits to buffer
blocker.release(); // tell appl thread
}
}
public String getDTMFDigits(int count, int timeout)
throws InterruptedException
{ // appl thread
dtmf=new StringBuffer(); // wait for 'count' digits within 'timeout' time
blocker=new Semaphore(1-count,true);
blocker.tryAcquire(timeout,TimeUnit.MILLISECONDS);
blocker=null;
return dtmf.toString();
}
// ---- END : DTMF ----
// ---- SUP SERVICE ----
public void explicitCallTransferTo(CapiChannel channel)throws CapiException{ // ECT
CapiPLC plc0=appl.getPLC(getLineID());
CapiPLC plc1=appl.getPLC(channel.getLineID());
plc0.explicitCallTransferTo(plc1);
}
// ---- END : SUP SERVICE ----
}