package uk.co.mmscomputing.device.phone;
import java.io.*;
import java.util.*;
import java.text.*;
import javax.sound.sampled.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import uk.co.mmscomputing.util.*;
import uk.co.mmscomputing.concurrent.*;
import uk.co.mmscomputing.sound.*;
public class PhoneCallMonitor extends PhoneCallSaver implements WindowListener{
private Semaphore blocker,pickup;
private boolean active=false;
private JFrame gui=null;
private Recorder recorder;
private SourceDataLine speaker=null;
private TargetDataLine microphone=null;
public PhoneCallMonitor(){
pickup = new Semaphore(0,true);
blocker = new Semaphore(0,true);
}
public void run(String local,String remote,InputStream pin,OutputStream pout){
try{
microphone=SoundMixerEnumerator.getInputLine(pcmformat,DefaultPhonePCMBlockSize);
}catch(LineUnavailableException lue){
System.out.println("\3b"+getClass().getName()+".<init>:\n\tCould not create microphone input stream.\n\t"+lue);
}
try{
speaker=SoundMixerEnumerator.getOutputLine(pcmformat,DefaultPhonePCMBlockSize);
}catch(LineUnavailableException lue){
microphone.close();
System.out.println("\3b"+getClass().getName()+".<init>:\n\tCould not create speaker output stream.\n\t"+lue);
}
if((speaker==null)||(microphone==null)){super.run(local,remote,pin,pout);return;}
try{
recorder=new Recorder(pout);
recorder.start();
gui=openMonitorGUI("Remote "+remote+" Local "+local);
pin.skip(pin.available()); // waste whatever we couldn't process in time
super.run(local,remote,new PhoneCallMonitorInputStream(pin),pout);
recorder.interrupt();gui.dispose();
}catch(Exception e){
System.out.println("3\b"+getClass().getName()+".run:\n\t"+e);
e.printStackTrace();
}finally{
deactivate();
microphone.close();
speaker.close();
if(gui!=null){gui.dispose();}
}
}
public void pickup(){
pickup.release();
}
public void activate(){
active=true;
microphone.flush();
speaker.flush();
speaker.start();
blocker.release();
microphone.start();
microphone.flush();
}
public void deactivate(){
active=false;
microphone.stop();
microphone.flush();
speaker.stop();
speaker.flush();
}
public void windowOpened(WindowEvent e){ activate();}
public void windowClosing(WindowEvent e){ stopRunning();}
public void windowClosed(WindowEvent e){ stopRunning();}
public void windowIconified(WindowEvent e){ deactivate();}
public void windowDeiconified(WindowEvent e){ activate();}
public void windowDeactivated(WindowEvent e){ deactivate();}
public void windowActivated(WindowEvent e){ activate();}
private class Recorder extends Thread{ // input: microphone ouput: telephone
private OutputStream pout;
public Recorder(OutputStream pout){
this.pout=pout;
}
public void run(){
int count=-1;
byte[] buffer = new byte[DefaultPhonePCMBlockSize];
try{
pickup.acquire(); // wait until call gets picked up
if(isrunning){
stopPlaying(); // stop parents player thread
activate();
do{
blocker.acquire(); // wait until call becomes active;
if(isrunning){
try{ // give player time to finish and phone output some time to send
new Semaphore(0,true).tryAcquire(500,TimeUnit.MILLISECONDS);
}catch(InterruptedException ie){}
microphone.flush();
while(isrunning&&active){
count=microphone.read(buffer,0,DefaultPhonePCMBlockSize);
if(count==-1){break;}
pout.write(buffer,0,count); // pipe out microphone input
}
}
}while(isrunning&&(count>=0)); // finish when microphone stream was closed
}
}catch(InterruptedException ie){
}catch(Exception e){
System.out.println("9\b"+getClass().getName()+".run\n\tCould not create answer thread."+e);
}
}
}
class PhoneCallMonitorInputStream extends FilterInputStream{
public PhoneCallMonitorInputStream(InputStream in){
super(in);
}
public int read()throws IOException{ // we don't use this
IOException ioe= new IOException(getClass().getName()+".read:\n\tInternal Error.");
ioe.printStackTrace();
throw ioe;
}
public int read(byte[] b, int off, int len)throws IOException{
while(in.available()>len){in.read(b,off,len);} // couldn't process in time, drop data
len=in.read(b,off,len);
if(len==-1){return -1;}
if(isrunning&&active){speaker.write(b,off,len);}
return len;
}
}
private class MonitorGUI extends JComponent{
private PhoneCallMonitor pcm;
private JTextPane text=new JTextPane();
private JButton button;
public MonitorGUI(final PhoneCallMonitor pcm,String title){
this.pcm=pcm;
text.setFont(new Font("Courier",Font.PLAIN,12));
text.setText(title);
JPanel buttons=new JPanel();
buttons.setLayout(new GridLayout(1,0));
button=new JButton(
new AbstractAction("Pick Up"/*,new JarImageIcon(getClass(),"32x32/play.png")*/){
public void actionPerformed(ActionEvent ev){
button.setEnabled(false);
pcm.pickup();
}
}
);
buttons.add(button);
setLayout(new BorderLayout());
add(new JScrollPane(text),BorderLayout.CENTER);
add(buttons,BorderLayout.SOUTH);
}
}
JFrame openMonitorGUI(String title){
try{
MonitorGUI gui=new MonitorGUI(this,title);
JFrame frame=new JFrame(title);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.addWindowListener(this);
frame.getContentPane().add(gui);
GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment();
Rectangle r=ge.getMaximumWindowBounds();
frame.setSize(400,200);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
return frame;
}catch(Exception e){
System.out.println("9\b"+getClass().getName()+"\n\t"+e);
return null;
}
}
static{
try{
SoundMixer mixer=SoundMixerEnumerator.getMixerByVendor("mm's computing");
System.out.println("Mixer "+mixer.getMixerInfo()+" is available");
}catch(Exception e){
System.out.println("9\b"+e.getMessage());
}
}
}