?? captureplaybackserver.java
字號:
}
catch (LineUnavailableException ex) {
shutDown("Unable to open the line: " + ex);
return;
}
//對line的操作次序:open();getBufferSize();start();write(data,0,num);drain();
/**
* line的最大BufferSize是16384;一個frame大小由AudioFormat決定;frame大小乘上(buffer里的frame數量)=這里line實際用的bufferData大小
*/
// 重放捕獲的聲音數據 play back the captured audio data
//**frame大小由AudioFormat決定,這里為4字節長;
int frameSizeInBytes = format.getFrameSize();
//For a source data line, this is maximum size of the buffer to which data can be written. For a target data line, it is maximum size of the buffer from which data can be read.
int bufferLengthInFrames = line.getBufferSize() / 8;
//bufferLengthInBytes為8192----bufferLengthInFrames為2048
int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
//在線程里,data被不斷初始化!
byte[] data = new byte[bufferLengthInBytes];
int numBytesRead = 0;
// Allows a line to engage in data I/O.(the line must be flushed or stoped)
line.start();
//playbackInputStream->data[]->dataline
while (thread != null) {
try {
//取出playbackInputStream并寫到字節流data[byte]中!返回事實讀取的量,跳出。
if ( (numBytesRead = playbackInputStream.read(data)) == -1) {
break;
}
int numBytesRemaining = numBytesRead;
while (numBytesRemaining > 0) {
//line.write(data, 0, numBytesRemaining)返回已寫入line的數據數量
numBytesRemaining -= line.write(data, 0, numBytesRemaining);
}
}
catch (Exception e) {
shutDown("Error during playback: " + e);
break;
}
}
// 向line寫完字節流,結束when reached the end of the stream,let the data play out, then
// stop and close the line.
if (thread != null) {
//排出Drains queued data from the line by continuing data I/O until line's internal buffer has been emptied.
line.drain();
//line.flush()---Flushes queued data from the line.
}
line.stop();
line.close();
line = null;
shutDown(null);
}
} // End class Playback
//fuwuqi
/**
* 從輸入渠道中捕獲數據,并寫到輸出流中;Reads data from the input channel and writes to the output stream
*/
class Capture
implements Runnable {
TargetDataLine line;
Thread thread;
public void start() {
errStr = null;
thread = new Thread(this);
//thread是有名字的;
thread.setName("Capture");
thread.start();
}
public void stop() {
thread = null;
}
private void shutDown(String message) {
if ( (errStr = message) != null && thread != null) {
thread = null;
samplingGraph.stop();
loadB.setEnabled(true);
playB.setEnabled(true);
pausB.setEnabled(false);
auB.setEnabled(true);
aiffB.setEnabled(true);
waveB.setEnabled(true);
captB.setText("Record");
System.err.println(errStr);
samplingGraph.repaint();
}
}
public void run() {
duration = 0;
//將audioInputStream置空!
audioInputStream = null;
// define the required attributes for our line,
// and make sure a compatible line is supported.
AudioFormat format = formatControls.getFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class,
format);
if (!AudioSystem.isLineSupported(info)) {
shutDown("Line matching " + info + " not supported.");
return;
}
// 獲得并打開目標dataline來捕獲get and open the target data line for capture.
try {
line = (TargetDataLine) AudioSystem.getLine(info);
//這里將獲得聲音!!
line.open(format, line.getBufferSize());
}
catch (LineUnavailableException ex) {
shutDown("Unable to open the line: " + ex);
return;
}
catch (SecurityException ex) {
shutDown(ex.toString());
// JavaSound.showInfoDialog();
return;
}
catch (Exception ex) {
shutDown(ex.toString());
return;
}
// line.read(data[])——播放
ByteArrayOutputStream out = new ByteArrayOutputStream();
int frameSizeInBytes = format.getFrameSize();
int bufferLengthInFrames = line.getBufferSize() / 8;
int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
byte[] data = new byte[bufferLengthInBytes];
int numBytesRead;
line.start();
//循環——從IO中讀取到data[byte],長為bufferLengthInBytes !
while (thread != null) {
if ( (numBytesRead = line.read(data, 0, bufferLengthInBytes)) == -1) {
break;
}
out.write(data, 0, numBytesRead);
}
// we reached the end of the stream. stop and close the line.
line.stop();
line.close();
line = null;
// 停止和關閉輸出流stop and close the output stream
try {
out.flush();
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
// !!!將輸入流載入到audioInputStream對象中!load bytes into the audio input stream for playback
byte audioBytes[] = out.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes);
audioInputStream = new AudioInputStream(bais, format,
audioBytes.length /
frameSizeInBytes);
long milliseconds = (long) ( (audioInputStream.getFrameLength() * 1000) /
format.getFrameRate());
duration = milliseconds / 1000.0;
try {
audioInputStream.reset();
}
catch (Exception ex) {
ex.printStackTrace();
return;
}
//在捕獲的線程里調用畫圖!hlf //kehu---duankai2---huahua
samplingGraph.createWaveForm(audioBytes);
}
} // End class Capture
//fuwuqi
/**
* 畫波形圖.//hlf!!
*/
class SamplingGraph extends JPanel implements Runnable {
private Thread thread;
private Font font10 = new Font("serif", Font.PLAIN, 10);
private Font font12 = new Font("serif", Font.PLAIN, 12);
//深色底面
Color jfcBlue = new Color(204, 204, 255);
//粉色畫當前位置線
Color pink = new Color(255, 175, 175);
public SamplingGraph() {
setBackground(new Color(20, 20, 20));
}
//畫波形圖!
public void createWaveForm(byte[] audioBytes) {
lines.removeAllElements(); // clear the old vector
AudioFormat format = audioInputStream.getFormat();
if (audioBytes == null) {
try {
audioBytes = new byte[ (int) (audioInputStream.getFrameLength()
* format.getFrameSize())];
audioInputStream.read(audioBytes);
}
catch (Exception ex) {
reportStatus(ex.toString());
return;
}
}
Dimension d = getSize();
int w = d.width;
int h = d.height - 15;
int[] audioData = null;
if (format.getSampleSizeInBits() == 16) {
int nlengthInSamples = audioBytes.length / 2;
audioData = new int[nlengthInSamples];
if (format.isBigEndian()) {
for (int i = 0; i < nlengthInSamples; i++) {
/* First byte is MSB (high order) */
int MSB = (int) audioBytes[2 * i];
/* Second byte is LSB (low order) */
int LSB = (int) audioBytes[2 * i + 1];
audioData[i] = MSB << 8 | (255 & LSB);
}
}
else {
for (int i = 0; i < nlengthInSamples; i++) {
/* First byte is LSB (low order) */
int LSB = (int) audioBytes[2 * i];
/* Second byte is MSB (high order) */
int MSB = (int) audioBytes[2 * i + 1];
audioData[i] = MSB << 8 | (255 & LSB);
}
}
}
else if (format.getSampleSizeInBits() == 8) {
int nlengthInSamples = audioBytes.length;
audioData = new int[nlengthInSamples];
if (format.getEncoding().toString().startsWith("PCM_SIGN")) {
for (int i = 0; i < audioBytes.length; i++) {
audioData[i] = audioBytes[i];
}
}
else {
for (int i = 0; i < audioBytes.length; i++) {
audioData[i] = audioBytes[i] - 128;
}
}
}
int frames_per_pixel = audioBytes.length / format.getFrameSize() / w;
byte my_byte = 0;
double y_last = 0;
int numChannels = format.getChannels();
for (double x = 0; x < w && audioData != null; x++) {
int idx = (int) (frames_per_pixel * numChannels * x);
if (format.getSampleSizeInBits() == 8) {
my_byte = (byte) audioData[idx];
}
else {
my_byte = (byte) (128 * audioData[idx] / 32768);
}
double y_new = (double) (h * (128 - my_byte) / 256);
//增加點!
lines.add(new Line2D.Double(x, y_last, x, y_new));
y_last = y_new;
}
//repaint();//???
}
//使用線程畫波形圖!hlf
public void paint(Graphics g) {
// 組件JPanel的大小;Returns the size of this component in the form of a <code>Dimension</code> object
Dimension d = getSize();
int w = d.width;
int h = d.height;
int INFOPAD = 15;
// 精確控制幾何圖形 Graphics2D——sophisticated control over geometry
// 在JPanel上畫畫!
Graphics2D g2 = (Graphics2D) g;
g2.setBackground(getBackground());
g2.clearRect(0, 0, w, h);//???300,60
g2.setColor(Color.white);
g2.fillRect(0, h - INFOPAD, w, INFOPAD);
//捕獲出錯的情況1
if (errStr != null) {
g2.setColor(jfcBlue);
g2.setFont(new Font("serif", Font.BOLD, 18));
g2.drawString("ERROR", 5, 20);
AttributedString as = new AttributedString(errStr);
as.addAttribute(TextAttribute.FONT, font12, 0, errStr.length());
AttributedCharacterIterator aci = as.getIterator();
FontRenderContext frc = g2.getFontRenderContext();
LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);
float x = 5, y = 25;
lbm.setPosition(0);
while (lbm.getPosition() < errStr.length()) {
TextLayout tl = lbm.nextLayout(w - x - 5);
if (!tl.isLeftToRight()) {
x = w - tl.getAdvance();
}
tl.draw(g2, x, y += tl.getAscent());
y += tl.getDescent() + tl.getLeading();
}
}
//正捕獲中的情況2
else if (capture.thread != null) {
g2.setColor(Color.black);
g2.setFont(font12);
g2.drawString("Length: " + String.valueOf(seconds), 3, h - 4);
}
//捕獲結束的情況3
else {
g2.setColor(Color.black);
g2.setFont(font12);
g2.drawString("文件名: " + fileName + " 長度: " +
String.valueOf(duration) + " 當前位置(秒): " +
String.valueOf(seconds), 3, h - 4);
if (audioInputStream != null) {
// 畫聲音波形圖.. render sampling graph ..
g2.setColor(jfcBlue);
for (int i = 1; i < lines.size(); i++) {
g2.draw( (Line2D) lines.get(i));
}
// 畫當前位置圖.. draw current position ..
if (seconds != 0) {
double loc = seconds / duration * w;
g2.setColor(pink);
g2.setStroke(new BasicStroke(3));
g2.draw(new Line2D.Double(loc, 0, loc, h - INFOPAD - 2));
}
}
}
}
public void start() {
thread = new Thread(this);
thread.setName("SamplingGraph");
thread.start();
seconds = 0;
}
public void stop() {
if (thread != null) {
thread.interrupt();
}
thread = null;
}
public void run() {
seconds = 0;
while (thread != null) {
if ( (playback.line != null) && (playback.line.isOpen())) {
long milliseconds = (long) (playback.line.getMicrosecondPosition() /
1000);
seconds = milliseconds / 1000.0;
}
else if ( (capture.line != null) && (capture.line.isActive())) {
long milliseconds = (long) (capture.line.getMicrosecondPosition() /
1000);
seconds = milliseconds / 1000.0;
}
try {
thread.sleep(100);
}
catch (Exception e) {
break;
}
repaint();
while ( (capture.line != null && !capture.line.isActive()) ||
(playback.line != null && !playback.line.isOpen())) {
try {
thread.sleep(10);
}
catch (Exception e) {
break;
}
}
}
seconds = 0;
repaint();
}
} // End class SamplingGraph
public static void main(String s[]) {
CapturePlaybackServer CapturePlaybackServer = new CapturePlaybackServer();
JFrame f = new JFrame("歡迎使用服務端語音系統");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.getContentPane().add("Center", CapturePlaybackServer);
f.pack();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int w = 720;
int h = 340;
//使frame居中
f.setLocation(screenSize.width / 2 - w / 2, screenSize.height / 2 - h / 2);
f.setSize(w, h);
f.show();
//打開監聽
CapturePlaybackServer.open();
}
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -