首页 > 编程语言 >在 Java 中重采样音频

在 Java 中重采样音频

时间:2023-04-14 13:26:33浏览次数:43  
标签:Java  音频

在我的一个项目中,我需要将 PCM 音频数据重新采样为不同的采样率。我正在使用 javax.sound.sampled.AudioSystem 来完成这项任务。重新采样似乎会在帧的开头和结尾添加额外的样本。下面是一个最小的工作示例:

 import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Arrays; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem;

public class ResamplingTest {

public static void main(final String[] args) throws IOException { final int nrOfSamples = 4; final int bytesPerSample = 2; final byte[] data = new byte[nrOfSamples * bytesPerSample]; Arrays.fill(data, (byte) 10); final AudioFormat inputFormat = new AudioFormat(32000, bytesPerSample * 8, 1, true, false); final AudioInputStream inputStream = new AudioInputStream(new ByteArrayInputStream(data), inputFormat, data.length); final AudioFormat outputFormat = new AudioFormat(24000, bytesPerSample * 8, 1, true, false); final AudioInputStream outputStream = AudioSystem.getAudioInputStream(outputFormat, inputStream); final var resampledBytes = outputStream.readAllBytes(); System.out.println("Expected number of samples after resampling " + (int) (nrOfSamples * outputFormat.getSampleRate() / inputFormat.getSampleRate())); System.out.println("Actual number of samples after resampling " + resampledBytes.length / bytesPerSample); System.out.println(Arrays.toString(resampledBytes)); } }

我期望将从 32kHz 到 24kHz 重新采样的 4 个样本精确地变为 3 个样本。然而,上述代码生成了 5 个样本。额外样本的数量似乎取决于输入和输出采样率。例如,如果我将采样率从 8kHz 重新采样到 32kHz,则会生成额外的 8 个样本。为什么重新采样会添加额外的样本,我如何知道帧的开头和结尾添加了多少个样本?

问题翻译:

  • 需要对 PCM 音频数据进行重新采样到不同的采样率,使用了 javax.sound.sampled.AudioSystem 类库。但重新采样后,似乎会在帧的开头和结尾添加额外的样本。提供了一个最小工作示例,期望从 32kHz 到 24kHz 重新采样的 4 个样本精确地变为 3 个样本,但实际生成了 5 个样本。为什么重新采样会添加额外的样本,如何知道帧的开头和结尾添加了多少个样本?

回答:

重新采样带来额外的样本是因为新采样速率可能不是旧采样速率的整数倍,会有样本之间的间距出现。这可能会导致重新采样后添加一些额外的样本,以适应新的采样速率。例如,如果您将 4 个样本从采样率为 32000 Hz 重新采样为采样率为 24000 Hz,则每秒钟必须输出更少的样本。如果在 32000 Hz 中有一个样本,它必须被分成 0.75 个样本(40毫秒/样本)来适应 24000 Hz 的输出。如果将其向上舍入,则会将其分成一个样本和 0.25 个样本,然后必须通过插入一个额外的样本来适应输出样本的整数数量。

要了解添加了多少个样本,需要知道采样率和输出后的采样长度。要计算需要多少个输出样本,可以使用以下公式:

outputSamples = inputSamples * outputSampleRate / inputSampleRate

在给定上面的例子之后,此公式变为:

outputSamples = 4 * 24000 / 32000 = 3

这意味着输出应该只有 3 个样本。但是,因为重新采样导致额外的 2 个样本被添加到输出中,因此最终样本数为 5。

为了知道帧的开头和结尾添加了多少个样本,您需要检查读取的数据中的前几个和最后几个样本是否与期望的值匹配。如果开头或结尾添加了额外的样本,则可以从读取的数据中看到这些样本。您还可以通过手动计算输出数组中的每个样本来确定哪些是额外添加的样本。

标签:Java ,音频
From:

相关文章

  • Java SpringBoot 中,动态执行 bean 对象中的方法
    根据不同的条件,调用不同的bean对象,执行对象中的方法SpringUtils工具类packagecom.vipsoft.web.utils;importcn.hutool.core.util.ArrayUtil;importorg.springframework.aop.framework.AopContext;importorg.springframework.beans.BeansException;importorg.sprin......
  • 原型及原型链-JavaScript教程
    JavaScript是世界上最流行的脚本语言。JavaScript是属于web的语言,它适用于PC、笔记本电脑、平板电脑和移动电话。JavaScript被设计为向HTML页面增加交互性。许多HTML开发者都不是程序员,但是JavaScript却拥有非常简单的语法。几乎每个人都有能力将小的JavaScript......
  • Java基础--数据结构
    数据结构Java工具包提供了强大的数据结构。在Java中的数据结构主要包括以下几种接口和类:枚举(Enumeration)、位集合(BitSet)、向量(Vector)、栈(Stack)、字典(Dictionary)、哈希表(Hashtable)、属性(Properties)以上这些类是传统遗留的,在Java2中引入了一种新的框架-集合框架(Collection)Java......
  • Java: Random
     /***版权所有2023涂聚文有限公司*许可信息查看:*描述:*用100元买100只鸡,大公鸡5元一只,母鸡3元1只,小鸡一元3只,问各能买多少只?*历史版本:JDK8.01*2023-03-12创建者geovindu*2023-03-12添加Lambda*2023-03-12修改:date*接口类*2023-03-12修改......
  • Java_JSTL_extend function tags
    1.createthefunctionclasspackagecn.com.benyoyo.manage.core.common.tools;publicclassElExFuncs{publicstaticintlastIndexOf(Stringtext,StringsearchString){if(text==null)text="";if(searchString==null)searchString="&q......
  • Java_JVM的内存溢出异常
     JVM的内存溢出异常在Java虚拟机规范的描述中,除了PC(程序计数器)寄存器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError异常的可能。当发生OutOfMemoryError异常时,无法用try...catch捕捉。 在开始讲解之前,在这里先简单介绍下虚拟机启动相关的一些内存设置参数。因为Ou......
  • Java_Double&BigDecimal
    importjava.math.BigDecimal;importjava.math.MathContext;publicclassBigDecimalTest{/***@paramargs*@referencearchie2010*@function实现将double类型的值转换为BigDecimal类型的值的不同途径以及各途径间的区别*......
  • Java_获取变量的类型
    如果是对象,那么可以使用getClass().getName()方法获得该对象的类名,然,还有就是利用反射机制获取原数据类型的,这个时候如果需要确定类型, 同样的,反射机制返回值是对象,比如对于类属性的返回,是Field对象,可以 通过里面的getType().getName()获得该属性的类型名称,下面一个例子:Type ......
  • Java_procedure with return value(oracle)
    Java调用Oracle中有返回值的存储过程1) 在编写存储过程时,输入参数用in(如果不写默认为in),输出参数用out --编写过程,要求输入雇员编号,返回雇员姓名。 createorreplaceproceduregetNameByNo(noinnumber,nameoutvarchar2)is begin  selectenameintonamefrom......
  • Java使用TensorFlow
    Java可以使用TensorFlow,TensorFlow为Java提供了一个API,它可以让Java开发者使用TensorFlow构建和训练深度学习模型。以下是如何在Java中使用TensorFlow的基本步骤:首先,需要安装TensorFlow的JavaAPI,可以从TensorFlow官网下载安装包,或者通过Maven或Gradle添加依赖。然后,在Java......