java数字音频最强教程之lowpass、highpass的应用(保留人声)

7 篇文章 7 订阅
7 篇文章 0 订阅
数字音频滤波器的简单使用。

音视频处理都是比较复杂的,本章主要讲解javacv转码视频的时候,如果使得音频仅保留人声段,去除不必要的声音,其实很简单,不需要其他peak滤波器,只需要一组低通、高通滤波器即可(lowpass、highpass),接着往下看

在这里插入图片描述
图我手绘的有点丑,大概意思就是这样吧,lowpass只通过1000hz以内,highpass只通过800以上,一旦这两家伙合体,就进化成了黑色阴影部分,也就是声音只有800-1000hz,其他频率全部被抛弃了。

接下来直接看代码

这是其中的两个数字音频滤波算法,自由组合,可以用来切除声音

/**
 * 双二次滤波器之低通、高通
 * 
 * @author ZJ
 *
 */
public class AudioFilter {

	double b0, b1, b2, a0, a1, a2;
	double x1, x2, y, y1, y2;

	/**
	 * 采样率
	 */
	private float sample_rate = 44100f;
	/**
	 * 中心频率
	 */
	private double center_freq = 0;
	/**
	 * Q值
	 */
	private double Q = 1;

	public AudioFilter(float sample_rate, double center_freq) {
		super();
		this.sample_rate = sample_rate;
		this.center_freq = center_freq;
	}

	public AudioFilter(float sample_rate, double center_freq, double q) {
		super();
		this.sample_rate = sample_rate;
		this.center_freq = center_freq;
		Q = q;
	}

	/**
	 * 低通滤波
	 */
	public void initLowpass() {
		Q = (Q == 0) ? 1e-9 : Q;

		double ov = 2 * Math.PI * center_freq / sample_rate;
		double sn = Math.sin(ov);
		double cs = Math.cos(ov);
		double alpha = sn / (2 * Q);

		b0 = (1 - cs) / 2;
		b1 = 1 - cs;
		b2 = (1 - cs) / 2;
		a0 = 1 + alpha;
		a1 = -2 * cs;
		a2 = 1 - alpha;

		gcompute();
	}

	/**
	 * 高通滤波
	 */
	public void initHighpass() {
		Q = (Q == 0) ? 1e-9 : Q;

		double ov = 2 * Math.PI * center_freq / sample_rate;
		double sn = Math.sin(ov);
		double cs = Math.cos(ov);
		double alpha = sn / (2 * Q);

		b0 = (1 + cs) / 2;
		b1 = -(1 + cs);
		b2 = (1 + cs) / 2;
		a0 = 1 + alpha;
		a1 = -2 * cs;
		a2 = 1 - alpha;

		gcompute();
	}

	/**
	 * 计算
	 */
	private void gcompute() {
		// a0归一化为1
		b0 /= a0;
		b1 /= a0;
		b2 /= a0;
		a1 /= a0;
		a2 /= a0;
	}

	/**
	 * 过滤
	 * 
	 * @param x
	 * @return
	 */
	public double filter(double x) {
		y = b0 * x + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;
		x2 = x1;
		x1 = x;
		y2 = y1;
		y1 = y;
		return y;
	}

在javacv中使用filter过滤

/**
 * java视频转码之音频滤波实现
 * 
 * @author ZJ
 *
 */
public class JavacvStreamAudioFilter {

	static int sampleFormat;
	static int sampleRate;
	static int audioChannels;

	public static void start() throws IOException, ExecutionException, LineUnavailableException {
		FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("d:/flv/file.mp4");
		grabber.start();

		FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("d:/flv/out.flv", 
				grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());

		// 设置flv格式
		recorder.setFormat("flv");
		recorder.setFrameRate(25);// 设置帧率
		recorder.setGopSize(25);// 设置gop
		recorder.setVideoQuality(1);
		recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);// 这种方式也可以
		recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);// 设置音频编码

		recorder.start();

		CanvasFrame canvas = new CanvasFrame("视频预览");// 新建一个窗口
		canvas.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		Frame frame = null;

		sampleFormat = grabber.getSampleFormat();
		sampleRate = grabber.getSampleRate();
		audioChannels = grabber.getAudioChannels();

		// 初始化扬声器
		final AudioFormat audioFormat = new AudioFormat(grabber.getSampleRate(), 16, grabber.getAudioChannels(), true,
				true);
		final DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
		final SourceDataLine soundLine = (SourceDataLine) AudioSystem.getLine(info);
		soundLine.open(audioFormat);
		soundLine.start();

		//线程池
		ExecutorService executor = Executors.newSingleThreadExecutor();

		// 低通滤波
		AudioFilter lowpass = new AudioFilter(sampleRate, 1000);
		lowpass.initLowpass();
		// 高通
		AudioFilter highpass = new AudioFilter(sampleRate, 800);
		highpass.initHighpass();

		// 只抓取图像画面
		for (; (frame = grabber.grab()) != null;) {

			// 判断是否是音频帧
			if (frame.samples != null) {
				// 一般音频都是16位非平面型左右声道在一个buffer中,如果不是得自己解析
				ShortBuffer channelSamples = (ShortBuffer) frame.samples[0];

				// 扬声器采样数据
				channelSamples.rewind();
				ByteBuffer bufferToSourceDataLine = ByteBuffer.allocate(channelSamples.capacity() * 2);

				// recorder采样数据
				channelSamples.rewind();
				ShortBuffer bufferToRecorder = ShortBuffer.allocate(channelSamples.capacity());

				for (int i = 0; i < channelSamples.capacity(); i++) {
					short val = channelSamples.get(i);

					/**
					 * 滤波计算 可能是javacv的缘故,重新编码声音貌似有bug,偶尔会有噪音,我直接用java自己解析就不会,算法是没有问题的
					 */
					val = (short) lowpass.filter(val);
					val = (short) highpass.filter(val);

					bufferToSourceDataLine.putShort(val);
					bufferToRecorder.put(val);
				}

				/**
				 * 写入到扬声器 并准备下一次读取
				 */
				try {
					executor.submit(new Runnable() {
						public void run() {
							soundLine.write(bufferToSourceDataLine.array(), 0, bufferToSourceDataLine.capacity());
							bufferToSourceDataLine.clear();
						}
					}).get();
				} catch (InterruptedException interruptedException) {
					Thread.currentThread().interrupt();
				}

				bufferToRecorder.rewind();
				recorder.recordSamples(sampleRate, audioChannels, bufferToRecorder);
				bufferToRecorder.clear();
			} else {
				// 画面帧直接录制/推流
				recorder.record(frame);
				// 显示画面
				canvas.showImage(frame);
			}
		}

		recorder.close();// close包含stop和release方法。录制文件必须保证最后执行stop()方法,才能保证文件头写入完整,否则文件损坏。
		grabber.close();// close包含stop和release方法
		canvas.dispose();
		soundLine.close();
		System.exit(0);
	}

	public static void main(String[] args) throws IOException, ExecutionException, LineUnavailableException {
		start();
	}

gitee地址

至此简单的音频滤波器功能实现了。

如果觉得可以,请随手点赞,Thanks♪(・ω・)ノ

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值