当前位置: 首页 > 知识库问答 >
问题:

Java麦克风TargetDataLine灵敏度/最大输入幅度

洪德寿
2023-03-14

我正在编写一个核心Java应用程序(JDK 11),它应该录制音频和视频。经过广泛的试验

然而,录制高质量的音频仍然是一个问题。我设法将录音作为短[]样本并对其进行编码,但由于某种原因,它们被TargetDataLine在振幅127处切断。我可以通过简单地将它们乘以一个因子来增加它们的编码,但是超过127的任何记录细节都会被噪声丢失。也就是说,我可以插入麦克风,并在事实发生后将其放大(大声或正常的语音丢失)。不幸的是,我无法控制浮动控制。类型java中的MASTER_增益,因为AudioSystem似乎不支持任何控制类型(如果这可能解决问题);

问题:

如何从TargetDataLine捕获完整的声音/采样振幅,而不在127处被切断?

研究向我指出了以下有用的线索:

如何使用Xuggler获取用于编码的音频

如何在Java中设置SourceDataLine的音量

音频标准化的Java算法

Xuggler编码与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)

共有1个答案

朱明知
2023-03-14

看起来我通过反复试验成功修复了它

正在将wav/wav文件读取到short[]数组中

问题是byte[](原点)转换为短[]。

  1. audioFormat必须设置为BigEndian=false
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 )) );
____________________________________________________________________________________________________      
          }
      }
 类似资料:
  • 问题内容: 我正在开发一个录音应用程序。在其中,我有一个Seekbar来更改输入语音增益。我找不到任何调整输入语音增益的方法。 我正在上课来录制声音。 我已经在Google Play商店中 看到了使用此功能的应用程序。 问题答案: 据我了解,您不需要任何自动调整,只需从用户界面进行手动调整即可。Android中没有内置功能,因此您必须手动修改数据。 假设您使用read(short [] audio

  • 问题内容: 我有一个应用程序可以点击麦克风,还可以根据麦克风输入播放声音(不必同时通过tho)。下面的代码有效。但是一个问题是输出在小型顶部扬声器而不是底部真实扬声器上播放。我可以通过 在播放器开始播放之前 将3行放在下面来奇怪地解决此问题,然后我可以听到扬声器上的声音。 但是,麦克风停止收听 !即使在播放器停止播放之后。基本上麦克风不喜欢 .defaultToSpeaker 任何想法? 这里也记

  • 更新时间:2018-09-17 11:37:10 功能说明 高感度麦克风模块。rf13 是一款高感度麦克风模块,这里我们用该模块采集周围环境声音的大小。 硬件资源 1.ESP32Kit 开发板 2.RF13 模块 3.接线 rf13 GND 引脚接 esp32Kit GND 引脚 rf13 VCC 引脚接 esp32Kit 3.3V 引脚 rf13 AO 引脚接 esp32Kit IO34 引脚

  • 问题内容: 我正在寻找一种将文件中的音频数据馈送到麦克风的方法,因此,当第三方应用程序(例如 arecord 或Chromium的“按语音搜索”功能)使用麦克风进行音频输入时,它们会从文件中接收音频数据代替。 这是我的情况 :我编写的一个应用程序记录了来自麦克风的音频数据(使用ALSA)并将其保存到文件(audioFile0.raw)中。在将来的某个未知时间点,某些未知的第三方应用程序(例如,我没

  • 问题内容: 我正在尝试通过Java访问麦克风的级别。我不需要录制任何东西,我只想知道声音水平的相对范围。 这可以实时吗? 如果这是不可能的,那么这可能会起作用:当电平超过某个值时开始记录,当电平下降到一定水平以下一段时间后停止录制四分之一秒的位并读取它的音量,如果它在阈值以下停止录音。 提前致谢 问题答案: 您可以通过Sound API访问麦克风,但不会给您简单的响度级别。您只需要捕获数据并就其声

  • 问题内容: 我已经读了几天的Java声音API了,我无法理解。我是一个体面的程序员,只是很难理解API。 我一直在尝试从麦克风捕获音频并实时显示波形图。 我在捕捉音频时遇到麻烦,他们在教程中说要这样做,但是我似乎无法使它正常工作。 任何建议和帮助将不胜感激,逐行回答将是理想的。 谢谢,麻烦您了。 问题答案: 这将为您提供操作系统默认的设置。 要选择特定的输入设备(TargetDataLine),最