这里有篇文章
大家看

/*
概述 第 1 页(共12 页) 
在本章中,我们将逐步完成录制音频文件的过程。我们将使用类似于 IAudioPlayer 接口,称为 IAudioRecorder 的接口。
IAudioRecorder 接口非常象播放音频中演示的 IAudioPlayer,但它用于录制而不是回放。
与 IAudioPlayer 一样,IAudioRecorder 接口仅包含很通用的方法(例如,startRecording、stopRecording 和 save),
因此简化了使用并封装了特定于 Java Sound 的功能。虽然关于播放音频的一章与关于录制的本章之间有许多相似之处,但在实现方面却有许多很重要的不同之处。
首先,没有简单的对象能象 Clip 简单地播放音频那样来录制声音。
结果,我们不得不直接同 I/O 流、线路、字节数组和音频数据转换等打交道。
在 AudioPlayer 示例中,我们学习了如何从给定的声音文件中检索音频格式。
然而在 AudioRecorder(IAudioRecorder 的基本实现)中,我们控制用于录制实际音频的设置。
由于一旦录制了音频就难以更改音频格式,所以这需要更多地关注音频设置。
每当需要弄清音频术语或概念时,请您参考数字音频速成课程。
*/
/*
接口 第 2 页(共12 页) 
下面介绍 AudioRecorder 接口。AudioRecorder 接口与 AudioPlayer 之间有一点不同,
不同之处在于这个类需要考虑音频格式。对于 AudioPlayer,根据从读入的音频数据中检索的格式,
从 AudioSystem 类检索线路。
然而,在音频录制时,AudioRecorder 将控制录制音频所用的格式。下面是 AudioRecorder 接口代码:
*/
public interface IAudioRecorder 
{ /* start recording */ 
public void startRecording();

/* stop recording */
public void stopRecording();

/* reset all audio buffers, clear recording */
public void reset();

/* set AudioFormat used to record the audio */
public void setAudioFormat();

/* retrieve audio format used to record the audio */
public AudioFormat getAudioFormat();

/* set the default audio format, non-compressed 
   CD-quality settings */
public void setDefaultAudioFormat();

/* save the cached recording to a file */
public boolean 
  saveToFile(File file, AudioFileFormat.Type fileType);
}
/*
 
标准音频设置 第 3 页(共12 页) 
对于缺省 AudioFormat,我们将使用 CD 音频设置和 WAV 文件格式以使示例简单。下面是缺省音频格式的规范:编码:带正负号的 PCM
采样率:44.1 kHz(CD 音质)
采样大小:16 位或两个 8 位字节(CD 音质)
声道数:两个或立体声(CD 音质)
帧大小:32 位或 4 字节(16 位采样大小 * 2 声道 = 每字节 32 位/8 位每字节 = 4 字节)
帧速率:44.1 kHz(采样率)
大尾数法:否 
接下来,我们将为 IAudioRecorder 接口中的缺省音频格式声明 public static final 变量。下面是格式:
 
*/
public static final AudioFormat DEFAULT_AUDIO_FORMAT = new AudioFormat
(
//Encoding
AudioFormat.Encoding.PCM_SIGNED,  

//Sample rate
44100f,  

//Sample size in bytes
2,  

//Number of channels
2, 

//Frame size 
4, 

//Frame rate
44100f,   

//Big endian
false 
);
/*
除了我们必须实例化一个 AudioFormat 对象,而不是从 AudioInputStream 检索它之外,
检索输入 Line 的过程与检索 Clip 的过程相同。要检索一个输入 Line,请完成下列步骤:实例化 AudioFormat 对象。
实例化 DataLine.Info 对象,该对象引用适当的 AudioFormat 并指定请求的 Line 类型。
从 AudioSystem 请求 Line。 
与 AudioPlayer 示例相同,我们将请求一个 TargetDataLine 实例来接收音频输入而不是直接实例化 TargetDataLine。
在有了 TargetDataLine 实例之后,需要打开它以完成初始化。
与回放示例相同,AudioSystem 类返回一个缺少内容的容器。
另外,AudioRecorder 被设计成除非被覆盖,否则使用 DEFAULT_AUDIO_FORMAT。
下面是代码:*/
//Create a DataLine.Info object with a TargetDataLine and the default formatDataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
//Request the line from the AudioSystemline = (TargetDataLine) AudioSystem.getLine(info);
//Open the line to complete the instantiationline.open(format, line.getBufferSize());
/*
TargetDataLine 中的读方法 第 5 页(共12 页) 
一旦提供了音频,我们就需要使用 TargetDataLine 接口中的 read 方法读取并储存音频数据。
让我们查看 read 方法以了解如何捕获音频输入:
public int read(byte[] b, int offset, int length)
byte[] b:音频数据输入被写入这个 byte[]。
状态 int 是由 read 方法返回的,因此必须传入 byte[] 以使它只具有单个返回类型。int offset:指定数组内的偏移量以开始写音频。
当在缓冲区中已有数据并且您想将新数据添加到已有数据的结尾时,可以使用偏移量。对于我们简单的音频录制器用途,偏移量将始终为零。
这将新捕获的音频数据放在缓冲区的开始处。int length:指定从音频输入读取的字节数。在从输入读取 length 个字节以前,该方法会一直阻塞。
因此,read 方法应该在其自己的线程中执行;否则,将冻结该接口。
假设 AudioRecorder 被正确线程化(但我不会详细讨论线程化,因为它超出本教程的范围)。
检查示例源代码(可以在参考资料中获取)以了解更多信息。*/
/*
    
准备检索的字节数组 第 6 页(共12 页) 
首先,我们需要计算传入 read 方法中的 byte 数组的大小。这是通过下列计算完成的:
//Frame size
int frameSizeInBytes = format.getFrameSize(); //The number of frames = the size of the buffer
int bufferLengthInFrames = line.getBufferSize() / 8; //Number of frames * frame size = total bytes
int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes; //Make a new byte[] the size of the AudioBuffer
byte[] data = new byte[bufferLengthInBytes];
接下来,我们实例化一个输出流以将 byte 数组写入。
用这种方法,如果我们进行后续读取,则可以写入到输出流而不需要为数据管理编写更多代码。
ByteArrayOutputStream out = new ByteArrayOutputStream(); 
*/
/*
   
读取音频数据并对其进行高速缓存 第 7 页(共12 页) 
现在,我们已经准备好读入音频。可以通过调用 TargetDataLine 中的 read 方法读取音频数据。
read 方法在 while 循环中,因此我们可以连续地录制而不用创建一个非常大的 byte[]。
从 TargetDataLine 中读取的数据将被写到 ByteArrayOutputStream 进行存储。
如果 read 方法返回 -1(表示错误),则我们还需要执行检查以中断循环。
下面是代码:
int numBytesRead; //Cache the number of bytes we have read
while (recording)
{
//Read in some data from the line
//Read will return -1 if it encounters errors
if((numBytesRead = line.read(data, 0, bufferLengthInBytes)) == -1)
{
break;
}

//Once we read in the data, we'll write it to an 
//audio output stream
out.write(data, 0, numBytesRead);
}
*/
/*
 
将 ByteArrayOutputStream 转换成 AudioInputStream 第 8 页(共12 页) 
接下来,我们将把 ByteArrayOutputStream 转换成 AudioInputStream。这样做有两个原因:将音频保存到一个文件 
将音频缓冲区保存到文件之前回放它 
下面是用于转换的代码:
//Convert the output stream back to a byte[]
byte[] audioBytes = out.toByteArray(); //Make a ByteArrayInputStream from the byte[]
ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes);//Request an AudioInputStream from the AudioSystem 
//passing in the byte stream, audio format, and length
audioInputStream = 
  new AudioInputStream(bais, format, audioBytes.length/frameSizeInBytes);  
*/
/*
 
将音频缓冲区保存到文件 第 9 页(共12 页) 
既然我们已经有了 AudioInputStream,则我们可以调用 AudioSystem 中的 write 方法以将缓冲区保存到文件。在保存该文件之前,我们需要指定 AudioFileFormat。AudioFileFormat 包含下列信息:AudioFormat 
AudioFileFormat.Type 
AudioFileFormat.Type 是 AudioFileFormat 的一个内部类,它定义下列文件类型的全局常量:AIFC 
AIFF 
AU 
SND 
WAV 
我们需要提供输出文件和文件类型常量。
我们将假设调用方法将为我们指定 AudioFileFormat.Type 类.
下面是 save 方法的代码:
public boolean saveToFile(File file, AudioFileFormat.Type fileType) 
  throws Exception
{
//Reset to the beginning of the captured data
audioInputStream.reset();  //Notify if an error has occurred
     return (AudioSystem.write(audioInputStream, fileType, file) == -1); 
}
 
*/
/*
   
支持的文件类型 第 10 页(共12 页) 
AudioFileFormat.Type 常量对应的文件类型不是保证受支持的。
您可以通过使用 isFileTypeSupported(AudioFileFormat.Type type) 方法询问 AudioSystem 类来确定受支持的文件类型。 
*/
/*
回放音频缓冲区 第 11 页(共12 页) 
既然您了解了如何使用更高级的 Line 实现,那