- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在编写一个核心Java应用程序(JDK 11),该应用程序应该记录音频和视频。
经过对各种库的广泛尝试和错误,我设法使用不推荐使用的Xuggler库来同时运行这两个库。
但是,以不错的质量录制音频仍然是一个问题。
我设法将录音作为short []样本进行编码,但是由于某些原因,它们被TargetDataLine在振幅127处截断了。我可以通过将它们乘以一个系数来增加编码量,但是高于127的任何录音细节都是迷失了声音。
就是事实发生之后(我失去了声音或正常语音),我可以将麦克风扩音并放大。
不幸的是,我无法在Java中控制FloatControl.Type.MASTER_GAIN,因为AudioSystem似乎不支持任何控件类型(如果这样做可能会解决问题);
问题:
如何从TargetDataLine捕获完整的声音/采样幅度,而在127处不被截断?
研究指出了以下有用的线索:
How to get Audio for encoding using Xuggler
How to set volume of a SourceDataLine in Java
Java algorithm for normalizing audio
Xuggler encoding and muxing
这是我的代码:
private static void startRecordingVideo() {
// total duration of the media
long duration = DEFAULT_TIME_UNIT.convert(1, SECONDS);
// video parameters
//Dimension size = WebcamResolution.QVGA.getSize();
//webcam.setViewSize(size);
BufferedImage img = webCamImageStream.get();
final int videoStreamIndex = 0;
final int videoStreamId = 0;
final long frameRate = DEFAULT_TIME_UNIT.convert(2, MILLISECONDS);
// audio parameters
TargetDataLine mic = null;
final int audioStreamIndex = 1;
final int audioStreamId = 0;
final int channelCount = 2; //1 mono 2Stereo
final int sampleRate = 44100; // Hz
final int sampleSizeInBits = 16; // bit in sample
final int frameSizeInByte = 4;
final int sampleCount = 588; //CD standard (588 lines per frame)
// the clock time of the next frame
long nextFrameTime = 0;
// the total number of audio samples
long totalSampleCount = 0;
// create a media writer and specify the output file
final IMediaWriter writer = ToolFactory.makeWriter("capture.mp4");
// add the video stream
writer.addVideoStream(videoStreamIndex, videoStreamId,
img.getWidth(), img.getHeight());
// add the audio stream
writer.addAudioStream(audioStreamIndex, audioStreamId,
channelCount, sampleRate);
//define audio format
AudioFormat audioFormat = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
sampleRate,
sampleSizeInBits,
channelCount,
frameSizeInByte,
sampleRate,
true);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat);
AudioInputStream audioInputStream = null;
try {
mic = (TargetDataLine) AudioSystem.getLine(info);
//mic.open();
mic.open(audioFormat, mic.getBufferSize());
// Adjust the volume on the output line.
if (mic.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
FloatControl gain = (FloatControl) mic.getControl(FloatControl.Type.MASTER_GAIN);
gain.setValue(-10.0f); // attempt to Reduce volume by 10 dB.
}else {
System.out.println("Not supported in my case :'( ");
}
mic.start();
audioInputStream = new AudioInputStream(mic);
} catch (Exception e) {
e.printStackTrace();
}
// loop through clock time, which starts at zero and increases based
// on the total number of samples created thus far
long start = System.currentTimeMillis();
//duration = frameRate;
recordingVideo = true;
updateUI("Recording");
System.out.println("Audio Buffer size : " + mic.getBufferSize());
coverImage = webCamImageStream.get();
int frameCount = 0;
//IGNOR Complexity of for Loop*******************************************************************
for (long clock = 0; clock < duration; clock = IAudioSamples.samplesToDefaultPts(totalSampleCount, sampleRate)){
// while the clock time exceeds the time of the next video frame,
// get and encode the next video frame
while (frameCount * clock >= nextFrameTime) {
BufferedImage image = webCamImageStream.get();
IConverter converter = ConverterFactory.createConverter(image, IPixelFormat.Type.YUV420P);
IVideoPicture frame = converter.toPicture(image, (System.currentTimeMillis() - start) * 1000);
writer.encodeVideo(videoStreamIndex, frame);
nextFrameTime += frameRate;
}
//##################################### Audio Recording section #######################################
int factor = 2;
byte[] audioBytes = new byte[mic.getBufferSize() ]; // best size?
int numBytesRead = 0;
try {
numBytesRead = audioInputStream.read(audioBytes, 0, audioBytes.length);
//error is probably here as it is only reading up to 127
} catch (IOException e) {
numBytesRead = mic.read(audioBytes, 0, audioBytes.length);
e.printStackTrace();
}
mic.flush();
// max for normalizing
short rawMax = Short.MIN_VALUE;
for (int i = 0; i < numBytesRead; ++i) {
short value = audioBytes[i];
rawMax = (short) Math.max(rawMax, value);
}
//127 is max input amplitude (microphone could go higher but its cut off) ###############################
//values at and over 127 are static noises
System.out.println("MAX = " +rawMax );
// convert to signed shorts representing samples
int volumeGainfactor = 2;
int numSamplesRead = numBytesRead / factor;
short[] audioSamples = new short[ numSamplesRead ];
if (audioFormat.isBigEndian()) {
for (int i = 0; i < numSamplesRead; i++) {
audioSamples[i] = (short)((audioBytes[factor*i] << 8) | audioBytes[factor*i + 1]);
}
}
else {
for (int i = 0; i < numSamplesRead; i++) {
audioSamples[i] = (short)(((audioBytes[factor*i + 1] ) << 8) |(audioBytes[factor*i])) ;
//normalization -> does not help (issue lies in Max read value)
//short targetMax = 127; //maximum volume
//Normalization method
/*
double maxReduce = 1 - targetMax/(double)rawMax;
int abs = Math.abs(audioSamples[i]);
double factor1 = (maxReduce * abs/(double)rawMax);
audioSamples[i] = (short) Math.round((1 - factor1) * audioSamples[i]);
*/
//https://stackoverflow.com/questions/12469361/java-algorithm-for-normalizing-audio
}
}
//##################################### END Audio Recording Section #####################################
writer.encodeAudio(audioStreamIndex, audioSamples, clock,
DEFAULT_TIME_UNIT);
//extend duration if video is not terminated
if(!recordingVideo) {break;}
else {duration += 22675;} //should never catch up to duration
// 22675 = IAudioSamples.samplesToDefaultPts(588, sampleRate)
//totalSampleCount += sampleCount;
totalSampleCount = sampleCount;
frameCount++;
}
// manually close the writer
writer.close();
mic.close();
}
调试打印示例:
MAX = 48 (is recorded)
MAX = 127 (is static noise)
最佳答案
好的,看来我设法通过反复试验来解决此问题,并发布了这篇文章:
reading wav/wave file into short[] array
问题在于将byte [](起源)转换为short []。
AudioFormat audioFormat = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
sampleRate,
sampleSizeInBits,
channelCount,
frameSizeInByte,
sampleRate,
false);`
int factor = 2;
byte[] audioBytes = new byte[mic.getBufferSize() ];
int numBytesRead = 0;
numBytesRead = audioInputStream.read(audioBytes, 0, audioBytes.length);
mic.flush();
// convert to signed shorts representing samples
int volumeGainfactor = 2;
int numSamplesRead = numBytesRead / factor;
short[] audioSamples = new short[ numSamplesRead ];
if (audioFormat.isBigEndian()) {
for (int i = 0; i < numSamplesRead; i++) {
//BigEndian Conversion not working
audioSamples[i] = (short)((audioBytes[factor*i] << 8) | audioBytes[factor*i + 1]);
}
}
else {
for (int i = 0; i < numSamplesRead; i++) {
____________________________________________________ ISSUE WAS HERE __________________________________________
audioSamples[i] = ( (short)( ( audioBytes[i*2] & 0xff )|( audioBytes[i*2 + 1] << 8 )) );
____________________________________________________________________________________________________
}
}
关于java - Java麦克风TargetDataLine灵敏度/最大输入振幅,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64062759/
我正在编写一个函数,使用 TargetDataLine 捕获大约 7.5 秒的音频剪辑。代码执行并呈现一个“input.wav”文件,但是当我播放它时没有声音。 如本文底部的代码所示,我的方法是执行以
我有一个麦克风,它使用 48000 采样率、24 位和 1 个 channel 。因此,我将这些数据输入 AudioSystem.isLineSupported() 并得到 false。 我尝试输入
如果我在关闭并停止 TargetDataLine 后尝试使用另一个音频输入设备再次打开它,那么它只会产生噪音而不是信号。为什么? 这是我正在谈论的代码:https://gist.github.com/
我有一个 Runnable,它获取一条通往麦克风的线路,从中读取数据并将其存储在 OutputStream 中。当我启动该线程的第一个实例时,它起作用了。第一个线程完成后(存在 run() 方法),我
根据Javadocs,当我使用javax.sound.sampledTargetDataLine的以下方法时: public void open(AudioFormat format,int buff
目前我正在尝试用 Java 记录麦克风中的声波并实时显示幅度值。我遇到了 Targetdataline,但我在理解从中获取数据时遇到了一些困难。 来自 Oracle 的示例代码指出: line = (
所以,我已经在一个宠物项目上工作了一段时间,我终于让它一切正常......主要是。我的项目代码已经出现在 JAVA audio data streaming 0s (请原谅我忘记启动我的 Target
因此,我一直致力于学习型项目,从线路中获取音频数据并将其呈现到我的扬声器。现在,我已经到了最后一根稻草和最后几行代码,我不知道什么会先发生。设置两条数据线后,我尝试将 TargetDataLine 读
我正在尝试从我的 PC 的线路输入中捕获音频,为此我正在使用 AudioSystem类(class)。静态 AudioSystem.write 方法有两种选择之一:写入文件或写入流。我可以让它很好地写
“当前”框架位置实际上是什么意思?文档中未对此进行描述。 如果我刚刚从TargetDataLine读取了一些缓冲区,然后调用了getFramePosition,则这将是 首先 最后 其他 读取缓冲区中
我有一个程序可以录制麦克风 5 秒,它应该在录制后播放,但是在将它转换到我的线路时出现错误。当 TargetDataLine 被转换为一行时会发生这种情况。 代码: AudioFormat forma
我创建了一个字节数组 WebSocket,它从客户端麦克风 ( navigator.getUserMedia ) 实时接收音频 block 。经过一段时间 WebSocket 停止接收新的字节数组后,
到目前为止, TargetDataLine 对我来说是用 Java 捕获麦克风输入的最简单方法。我想对[在屏幕录像机软件中]使用屏幕视频捕获的音频进行编码,以便用户可以创建教程、幻灯片案例等。 我使用
我的设置如下: 我有一个在浏览器中运行的 Java 小程序,用于录制和播放音频。 我的问题是: 当我刷新浏览器时,SourceDataLine 会在刷新后正确重新打开,而 TargetDataLine
我正在创建一个 VOIP 服务器和客户端系统,但仅播放 1/连接用户数量的语音数据包。我认为这是因为它只能播放来自一个 TargetDataLine 的音频流,并且每个设备只能播放一个 TargetD
我有一个线程,我从 TargetDataLine(麦克风)读取数据并将其写入 ByteArrayOutputStream。我有一个小“计时器”,因为我想录制 10 毫秒的声音,然后将捕获的数据传递给另
我可以通过操作 targetDataLine 生成的字节数组来更改音频内容的音量吗?我知道我无法使用 java 更改操作系统音量级别。所以我问自己是否可以通过操纵信号本身来改变音量。是否可以?如果是的
我为测试目的编写了一个小型 Java 应用程序,它从 ubuntu 12.04 上的混音器中捕获声音。 代码运行良好,我可以捕捉所有应用程序的声音,除了在 Wine 下运行的任何应用程序。 每当我启动
抱歉,如果这看起来是一个简单的问题,但这是我第一次在编程中尝试声音。 我在这里想要实现的是将通过 TargetDataLine 对象获得的数据编码到 .ogg 文件中。我的出发点是官方 Java 文档
我正在尝试找到FFT的输入,以便获取每个音频输入的时频数据。我浏览过一些网站,我知道输入应该从 targetDataLine 获取。我编写了一个程序来捕获音频数据并且它可以工作。 这是我打印targe
我是一名优秀的程序员,十分优秀!