📄 voicemicrophoneinput.java
字号:
/* * Created on Oct 17, 2004 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */package net.jxta.myjxta.plugins.vojxta;import net.jxta.endpoint.ByteArrayMessageElement;import net.jxta.endpoint.StringMessageElement;import net.jxta.logging.Logging;import net.jxta.myjxta.dialog.Dialog;import net.jxta.myjxta.dialog.DialogMessage;import javax.sound.sampled.*;import java.util.logging.Level;import java.util.logging.Logger;/** * @author Ravi * @author jamoore * @modified 2005-03-05 jamoore add producer/consumer threads, line state * @modified 2005-03-05 jamoore audio encoding is now dynamic based on quality * @modified 2005-03-10 jamoore rework encoding to fit dynamic block sizes * @modified 2005-03-12 jamoore add statistical accessors * @modified 2005-03-18 jamoore refactor * @modified 2005-03-18 jamoore add in/out buffer size/capacity accessor/stats * */public final class VoiceMicrophoneInput extends Thread implements AudioResource{ static final Logger LOG = Logger.getLogger (VoiceMicrophoneInput.class.getName()); /** default number of speex encoded chunks to place in each message*/ private static final int DEFAULT_MESSAGE_SIZE_MULTIPLIER = 10; /** default number of speex encoded chunks the out going buffer will hold*/ private static final int DEFAULT_BUFFER_SIZE_MULTIPLIER = 50; /** these represetn the logical state of the hardware to the call control*/ private static final int STATE_ON = 20; private static final int STATE_OFF = 10; private static final int STATE_PAUSE = 30; /** represents the current state of the audio targetline */ private int micState = STATE_PAUSE; /** type of audio we want from the target line */ private final AudioFileFormat.Type targetType = null; /** data line to microphone*/ private TargetDataLine targetLine = null;; /** wrapper to the speex encoder */ private Encoder encoder = null;; /** call control object - performs protocol navigation */ private VoJxtaCallControl vojxtaCallControl = null; /** outgoing buffer that holds que'd up speex audio blocks */ private VoiceDataBuffer sendBuffer = null; /** wait lock to pause the target line from reading*/ private Object pauseLock = null; /** wait lock till the buffer recovers from starvation*/ private Object messageThreadLock = null; /** dialog that we dispatch messages to*/ public Dialog vojxtaDialog = null; // debugging boolean firstSentMessage = true; byte[] firstSentBytes = null; /** statistic*/ private long sentMessages = 0; /** statistic*/ private long sentBytes = 0; /** true if message thread is waiting for the buffer to fill up*/ private boolean messageThreadWaiting = false; /** size of audio chucks speex can encode at the current sample rate*/ private final int rawChunkSize = BLOCK_SIZE; // basically at the sample rate this is 640 bytes /** size of the speex encoded audio. this depends on the quality setting*/ private int speexChunkSize = 0;; /** the size in bytes each outgoing message contains*/ private int speexMessageSize = 0; /** size in bytes of the outgoing buffer holding speex encoded bytes*/ private int speexBufferSize = 0; /** target line buffer size*/ private final int rawBufferSize = rawChunkSize * 10; /** size in bytes of buffer read from target Line */ private final int audioReadChunkSize = rawChunkSize * 5; /** default mixer on target line */ private Mixer micMixer = null; /** default gain control on target line*/ private FloatControl gainControl = null; /** statistic */ private int averageEncodeTime = 0; /** speex quality level */ private int encodeQuality = 0; /** consumer thread, dispatches messages to dialog*/ private Thread messageThread = null; /** * */ public VoiceMicrophoneInput (VoJxtaCallControl vojxtaCallControl) { LOG.setLevel (Level.INFO); //LOG.setLevel (Level.SEVERE); pauseLock = new Object (); messageThreadLock = new Object (); this.vojxtaCallControl = vojxtaCallControl; this.vojxtaDialog = this.vojxtaCallControl.getDialog (); /** This thread dispatches messages to the vojxta dialog and * blocks on outgoing buffer starvation */ messageThread = new Thread (new Runnable () { public void run () { while(true) { if(getMicState () == STATE_OFF) { //we should send any remaining data in buffer first break; } if (sendBuffer.size () >= speexMessageSize) { dispatchVoiceData (sendBuffer.get (speexMessageSize)); }else { try { synchronized(messageThreadLock) { messageThreadWaiting = true; messageThreadLock.wait (); } }catch(InterruptedException ix) { ix.printStackTrace (); } } } } }); }//constructor /** * Retain the mic line from the audiosubsystem. Try for a gain control too. * For some raeson target lines do not have available controls. we might have * to add some amplitude to the line oourselve. */ public void obtainHardware () { AudioFormat audioFormat = new AudioFormat (AudioFormat.Encoding.PCM_SIGNED, 16000.0F, 16, 1, 2, 16000.0F, false); DataLine.Info info = new DataLine.Info (TargetDataLine.class, audioFormat); try { targetLine = (TargetDataLine) AudioSystem.getLine (info); targetLine.open (audioFormat, rawBufferSize ); } catch (LineUnavailableException e) { // TODO Auto-generated catch block e.printStackTrace (); } AudioFileFormat.Type targetType = AudioFileFormat.Type.WAVE; if(targetLine.isControlSupported (FloatControl.Type.MASTER_GAIN)) { gainControl = (FloatControl)targetLine.getControl (FloatControl.Type.MASTER_GAIN); } } /** * Return block size of encoded audio bytes */ public int getEncodedBlockSize () { return speexChunkSize; } /** * Return the size in byte of voice data in a message */ public int getEncodedMessageSize () { return speexMessageSize; } /** * Set the size in bytes of voice data in a message */ public void setEncodedMessageSize (int encodedMessageSize) { this.speexMessageSize = encodedMessageSize; } /** * Returns the size in bytes of the buffer holding outgoing encoded voice * bytes */ public int getEncodedBufferSize () { return speexBufferSize; } /** * Sets the size in bytes of the buffer that holds encoded voice bytes. * The current buffer impl does not allow dynamic resizing */ public void setEncodedBufferSize (int encodedBufferSize) { this.speexBufferSize = encodedBufferSize; } /* * producer thread. read from target line blocks till buffer size can be * filled. data is encoded then added to the outgoing buffer. */ public void run () { /** start the message dispatch thread*/ messageThread.start (); /** local buff to read raw audio into */ byte[] buff = new byte[audioReadChunkSize]; /** bytes read from targetline... if zero after read that mean the line has been stopped */ int bytesRead = 0; try { while((bytesRead = this.targetLine.read (buff,0,buff.length)) != -1) { if(getMicState () == this.STATE_PAUSE) { synchronized(pauseLock) { pauseLock.wait (); } } if(getMicState () == this.STATE_OFF) { //we should send any remaining data in buffer first break; } if(bytesRead > 0) { encodeAndStore (buff); if (messageThreadWaiting && sendBuffer.size () >= speexMessageSize) { messageThreadWaiting = false; synchronized(messageThreadLock) { messageThreadLock.notify (); } } }else{ /** if bytes read is zero something is wrong.. break */ if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { this.printMicState (); if(!targetLine.isOpen ()) { LOG.info("targetline != open"); } LOG.info("ZERO Bytes READ"); break; } } }//while }catch (InterruptedException ix ) { setMicState (this.STATE_OFF); if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("run : interuptedexception"); } ix.printStackTrace (); } }//run /** * encodes the raw pcm audio from target line then stores it in the * outgoing buffer. at the current sample rate speex encodes 640byte * blocks. we break the buff up into 640byte chunks -> encode -> store */ private void encodeAndStore (byte[] buff) { int chunks = buff.length / rawChunkSize; int[] startPos = new int[chunks]; for(int j = 0; j < chunks; j++) { startPos[j] = j * rawChunkSize; } for(int i = 0 ; i < chunks ; i++) { byte[] preEncodeBuff = new byte[rawChunkSize]; System.arraycopy (buff,startPos[i], preEncodeBuff, 0, rawChunkSize ); long in = System.currentTimeMillis (); byte[] postEncodeBuff = encoder.encode (preEncodeBuff);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -