Java如何播放/录制音频?

前言:今天早上突发奇想想要玩一玩Java自带的声音API,闲着无聊学了一下午搞明白了它的用法(没有视频光看文字果然学习效率低下)
VoiceMonitor 的GitHub仓库

本来其实想做一个实时翻译的功能,用Aliyun的API,但是阿里云的API好像要付费,虽然GitHub上有做好的DEMO了,还挺好,自带字幕在屏幕底部,但是没有买套餐包,所以没法用,太难了。
然后闲着无聊想要搞一个耳机返听麦克风功能,听到自己的环境音,所以Bing了各种问题,敲敲打打缝缝补补搞出来一个DEMO,想要测试的时候发现,我电脑麦克风坏了你能信,插耳机或者笔记本自带的音响都没法用,佛了,虽然做出来了,逻辑上觉得还可以,应该没啥问题,但是估计跑起来有问题吧。有空了再去修一修。


在这里立下一个介绍(浅谈)javax.sound相关包的适用方法教程博文的Flag,有空一定!
——2020 05-17 17:23
时隔几天,我又回来了。开始更新。我可真是鸽子?
——2020 05-23 08:13


如何通过Java原生API录制音频?

要录制音频之前,我们要先认识一下AudioFormat 这个类,

AudioFormat (Java Platform SE 8 )


AudioFormat类适用于多种常见的声音文件编码技术,包括脉码调制(PCM),多律法编码和法律编码。 这些编码技术是预定义的,但服务提供商可以创建新的编码类型。 特定格式使用的编码由其encoding字段命名。

这个类是用来定义音频文件的格式的,比如说:采样频率,双声道,位数等。接下来列出来一个比较简单的写好的getAudioFormat()方法

	public static AudioFormat getAudioFormat(){
		//下面注释部分是另外一种音频格式,两者都可以
		AudioFormat.Encoding encoding = AudioFormat.Encoding.
        PCM_SIGNED ;
		float rate = 8000f;//采样频率
		int sampleSize = 16;//表示每个具有此格式的声音样本中的位数
		String signedString = "signed";
		boolean bigEndian = true;
		int channels = 1;//单声道
		return new AudioFormat(encoding, rate, sampleSize, channels,
				(sampleSize / 8) * channels, rate, bigEndian);//返回一个创建好的AudioFormat类
      //		//采样率是每秒播放和录制的样本数
      //		float sampleRate = 16000.0F;
      //		// 采样率8000,11025,16000,22050,44100
      //		//sampleSizeInBits表示每个具有此格式的声音样本中的位数
      //		int sampleSizeInBits = 16;
      //		// 8,16
      //		int channels = 1;
      //		// 单声道为1,立体声为2
      //		boolean signed = true;
      //		// true,false
      //		boolean bigEndian = true;
      //		// true,false
      //		return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed,bigEndian);
	}
public static InputStream streamTran(ByteArrayOutputStream baos) {//输出流转换成输入流工具
		return new ByteArrayInputStream(baos.toByteArray());
	}

有了AudioFormat之后,我们就可以根据我们设置好的声音参数来采集声音了。

public static ByteArrayOutputStream out = new ByteArrayOutputStream();//写出流
public void record() {
		try {
			AudioFormat format = AudioUtils.getAudioFormat();//获取需要采样声音的参数信息
			DataLine.Info info = new DataLine.Info(TargetDataLine.class,format); // format is an AudioFormat object
			TargetDataLine  line = (TargetDataLine)(AudioSystem.getLine(info));//获取与指定的Line.Info对象中的描述匹配的行。 
          //通俗点就是获取一下话筒Line
			line.open(format);//以指定的格式打开行,使该行获取任何所需的系统资源,并可以运行。 
          /*
          *****这里的DataLine等操作,可以直接CV拷贝,这些都是固定格式。*****
          */
          
			// Assume that the TargetDataLine, line, has already
			// been obtained and opened.
			int numBytesRead;
			byte[] data = new byte[line.getBufferSize() / 5];//设置缓冲区大小
			// Begin audio capture.
			line.start();//开始记录声音
          //允许线路从事数据I / O。 如果在已经运行的行上调用该方法,该方法什么也不做。 除非缓冲区中的数据已被刷新,
          //否则线路将从线路停止时未处理的第一帧开始恢复I / O。 当音频捕获或播放开始时,会生成一个START事件。
			// Here, stopped is a global boolean set by another thread.
			for(;;) {//这里设置成为死循环,方便一直记录,不停歇。
				// Read the next chunk of data from the TargetDataLine.
				numBytesRead =  line.read(data, 0, data.length);//读取字节到字节数组
				// Save this chunk of data.
				out.write(data, 0, numBytesRead);//写出流
			}
		} catch (LineUnavailableException e) {
			e.printStackTrace();
		}	
	}

什么是DataLine.Info

DataLine.Info (Java Platform SE 8 )


除了从其超类继承的类信息, DataLine.Info还提供了特定于数据行的附加信息。 此信息包括: 数据线支持的音频格式
其内部缓冲区的最小和最大大小
因为一个Line.Info知道类其描述了线,一个DataLine.Info对象可以描述DataLine子接口,如SourceDataLineTargetDataLineClip 。 可以查询的任何这些类型的线混合器,传递的适当实例DataLine.Info作为参数的方法,如Mixer.getLine(Line.Info)

什么是TargetDataLine

TargetDataLine (Java Platform SE 8 )
目标数据线是可DataLine读取音频数据的类型DataLine 。 最常见的示例是从音频捕获设备获取其数据的数据线。 (该设备实现为写入目标数据线的混音器。)
目标数据线可以通过调用具有适当的DataLine.Info对象的MixergetLine方法从混合器获得。

写好这写代码之后,就可以在out流中读取到捕捉的音频字节了!

如何播放音频?

	private InputStream is = null;
public void play() {
  		is = (ByteArrayInputStream) AudioUtils.streamTran(RecordVoice.out);
		SourceDataLine sdl = null;
		try {
			DataLine.Info info = new DataLine.Info(SourceDataLine.class, AudioUtils.getAudioFormat());
			sdl = (SourceDataLine) AudioSystem.getLine(info);//获取音频输出的Line
			sdl.open(AudioUtils.getAudioFormat());
			sdl.start();
			byte[] buffer = new byte[1024];//缓存数组
			int temp = 0;
			while(true) {
				temp = is.read(buffer);//读取字节
				//(temp = inputStream.read(buffer))>=0
				try {
					sdl.write(buffer, 0, temp);//写出播放到音响的Line里。
				}catch(Exception e1) {updateIs();continue;}//如果流里没有东西了,就手动更新一下
				//输出声音
				updateIs();
				//每一次都重新获取一下声音流
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			sdl.drain();
			sdl.close();
		}
	}

什么是SourceDataLine

SourceDataLine (Java Platform SE 8 )
源数据线是可以写入数据的数据线。 它作为混音器的源头。 应用程序将音频字节写入源数据线,该数据线处理字节的缓冲并将其传递到混频器。 混合器可以将样品与来自其他来源的样品混合,然后将混合物输送到目标,例如输出端口(其可以表示声卡上的音频输出设备)。
可以通过调用具有适当的DataLine.Info对象的MixergetLine方法从混合器获得源数据线。

这样,我们就简单的实现了一个声音的录制/播放功能。

标签:

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

Captcha Code