/* / Copyright (C) 2009 Risto Känsäkoski- Sesca ISW Ltd / / This file is part of SIP-Applet (www.sesca.com, www.purplescout.com) / / This program is free software; you can redistribute it and/or / modify it under the terms of the GNU 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 General Public License for more details. / / You should have received a copy of the GNU General Public License / along with this program; if not, write to the Free Software / Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package com.sesca.audio; import javax.sound.sampled.AudioFormat; import com.sesca.misc.Logger; public class AudioProcessor implements AudioSourceListener, AudioTranscoderListener, AudioDestinationListener, AudioReceiverListener { boolean disableIncoming = false; boolean disableOutgoing = false; boolean audioPaused=false; //int bitRate=16; //int sampleRate=8000; //int channels=1; //int frameLength=20; int frameSize=320; int payloadType=-1; boolean bigEndian = false; public long psstart; // secret number; static final AudioFormat.Encoding codec = AudioFormat.Encoding.PCM_SIGNED; AudioSource source = null; // audio input device, usually microphone AudioSource suspendedSource=null; AudioTranscoder encoder = null; AudioTranscoder decoder = null; AudioSender sender = null; // outgoing audio handler, usually rtp AudioReceiver receiver = null; // incoming audio handler, usually rtp AudioDestination destination = null; // audio output device, usually speakers public boolean pseudoEchoCancellationMode=false; // use echo cancellation public boolean audioAnalysisAvailable=true; // is audio analysis available from codec (or some other source) boolean calleeIsTalking=false; int frameCounter=0; boolean silentFrame=false; public AudioProcessor(int pt, AudioSource src, AudioSender snd, AudioDestination dst, AudioReceiver rcv, int fs) { Logger.debug("in AudioProcessor constructor"); if (disableOutgoing) { src = null; //snd = null; } init(pt, src, snd, dst, rcv, fs); } public void onIncomingRawFrame(byte[] frame) { // TODO Auto-generated method stub if (encoder!=null) { Logger.hysteria("AudioProcessor.onIncomingRawFrame"); if (encoder.canEncode()) onIncomingEncodedFrame(encoder.encode(frame)); else Logger.error("Selected codec does not support encoding"); } else { onIncomingEncodedFrame(frame); } } public void onIncomingEncodedFrame(byte[] frame) { Logger.hysteria("AudioProcessor.onIncomingEncodedFrame"); if (sender!=null) sender.onReceivedFrame(frame); else { onIncomingReceivedFrame(frame, payloadType); Logger.debug("sender == null. Redirecting to receiver"); } } private void init(int payloadType, AudioSource src, AudioSender snd, AudioDestination dst, AudioReceiver rcv, int fs) { Logger.debug("AudioProcessor.init:"); Logger.debug(" destination="+dst.getClass()); if (src!=null)Logger.debug(" source="+src.getClass()); else Logger.debug(" source=null"); if (snd!=null)Logger.debug(" sender="+snd.getClass()); else Logger.debug(" sender=null"); if (rcv!=null) Logger.debug(" receiver="+rcv.getClass()); else Logger.debug(" receiver=null"); this.payloadType=payloadType; frameSize=fs; { Logger.debug(""); switch (this.payloadType) { case 0: encoder = new PCMUCodec4(this); decoder = new PCMUCodec4(this); break; case 8: encoder = new PCMACodec(this); decoder = new PCMACodec(this); break; default: encoder = null; decoder = null; break; } if (encoder!=null)Logger.debug("encoder="+encoder.getClass()); if (decoder!=null)Logger.debug("decoder="+decoder.getClass()); if (encoder!=null)encoder.init(); if (decoder!=null)decoder.init(); } source=src; sender=snd; destination=dst; //Logger.debug("AudioProcessor constructor: destination="+destination); receiver=rcv; if (sender!=null) sender.init(this.payloadType); AudioFormat format=new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,8000,16,1,2,8000,bigEndian); // AudioFormat format=new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,22050,8,1,1,22050,false); if (source!=null) source.init(this,format, frameSize); if (destination!=null) destination.init(this,format, frameSize); else Logger.error("AudioProcessor: destination=null"); if (receiver!=null) receiver.init(this); } public boolean start() { boolean ok=false; if (source!=null) source.go(); if (sender!=null) sender.go(); if (destination!=null) destination.go(); if (receiver!=null) receiver.go(); ok=true; return ok; } public void stop() { Logger.info("AudioProcessor.stop: closing all inputs and outputs"); if (source!=null) source.close(); if (sender!=null) sender.close(); if (destination!=null) destination.close(); if (receiver!=null) receiver.close(); return; } public void onIncomingDecodedFrame(byte[] b) { Logger.hysteria("AudioPorcessor.onIncomingDecodedFrame"); if (b==null) Logger.error("Decoded frame = null"); if (destination!=null)destination.onReceivedDestinationFrame(b); else Logger.error("Audio destination = null"); } public void onIncomingReceivedFrame(byte[] frame, int payloadType) { if (frame==null) Logger.error("onIncomingReceivedFrame:frame=null"); Logger.hysteria("AudioProcessor: onIncomingReceivedFrame "+frameCounter++); if (payloadType==19 && !audioPaused) pauseAudio(); if (decoder!=null && payloadType==this.payloadType) { if (audioPaused) resumeAudio(); if (decoder.canDecode()) onIncomingDecodedFrame(decoder.decode(frame)); else Logger.error("Selected codec does not support decoding"); } else Logger.error("Unsupported RTP payload type ("+payloadType+")."); } public void newSource(AudioSource s, boolean suspend) { if (!suspend) { // System.out.println("AudioProcessor.newSource suspend=false"); source.close(); source=s; AudioFormat format=new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,8000,16,1,2,8000,bigEndian); if (source!=null) source.init(this,format, frameSize); } else { // System.out.println("AudioProcessor.newSource suspend=true"); // System.out.println(" original source="+source.getClass()); // System.out.println(" new source="+s.getClass()); suspendedSource=source; // System.out.println(" halting original source"); source.halt(); // System.out.println(" original source halted"); source=s; AudioFormat format=new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,8000,16,1,2,8000,bigEndian); if (source!=null) { // System.out.println(" calling initialize method of new source"); source.init(this,format, frameSize);} // System.out.println(" new source initialized"); } // System.out.println("AudioProcessor.newSource exits"); } public boolean resumeSuspendedSource() { Logger.debug("audioprosessor.resumesuspendedsource starts"); if (suspendedSource==null) return false; Logger.debug("close source"); source.close(); Logger.debug("source closed"); source=null; Logger.debug("vanha source"); source=suspendedSource; Logger.debug("unhalt vanha source"); boolean ok = source.unhalt(); Logger.debug("vanha source unhalted"); Logger.debug("audioprosessor.resumesuspendedsource ends"); return ok; } void pauseAudio() { audioPaused=true; destination.stop(); } void resumeAudio() { audioPaused=false; destination.play(); } public boolean setCalleeIsTalking(boolean b) { if (audioAnalysisAvailable) { calleeIsTalking=b; return true; } else return false; } public boolean getCalleeIsTalking() { return (audioAnalysisAvailable & calleeIsTalking); } public boolean getPseudoEchoCancellationMode() { // TODO Auto-generated method stub return pseudoEchoCancellationMode; } @Override public void onEndOfData() { //stop(); } @Override public void setSilentFrame(boolean b) { silentFrame=b; } @Override public boolean isSilentFrame() { return silentFrame; } public void generateDTMF(int n, int duration) { // System.out.println("audioprosessori.generateDTMF alkaa"); if (!(source instanceof MicrophoneInput)) { // System.out.println("Metsään meni!"); return; } else { MicrophoneInput input = (MicrophoneInput)source; input.setToneDuration(duration); switch (n) { case 1: input.tone1(); input.generateTone(); break; case 2: input.tone2(); input.generateTone(); break; case 3: input.tone3(); input.generateTone(); break; case 4: input.tone4(); input.generateTone(); break; case 5: input.tone5(); input.generateTone(); break; case 6: input.tone6(); input.generateTone(); break; case 7: input.tone7(); input.generateTone(); break; case 8: input.tone8(); input.generateTone(); break; case 9: input.tone9(); input.generateTone(); break; case 10: input.toneAsterisk(); input.generateTone(); break; case 11: input.toneSharp(); input.generateTone(); break; case 0: input.tone0(); input.generateTone(); break; default: break; } // System.out.println("Vaihdetaan mikrofoniin"); //resumeSuspendedSource(); // System.out.println("audioprosessori.generateDTMF loppuu"); } } }