Java下采样和上采样

Posted

技术标签:

【中文标题】Java下采样和上采样【英文标题】:Java down and upsampling upsampling 【发布时间】:2018-03-09 13:25:56 【问题描述】:

我是 java Sound API 的初学者,在 oracle 和 richard brawlin 中阅读了很多东西,并用他的程序尝试了一些项目。我要捕获的第一个是声音并对其进行下采样并以正确的音频格式返回。然后我会尝试相同但不录制。

首先我将向您展示代码的重要部分,然后我将告诉您我的问题:

在这里我用我的麦克风录制声音:

stopCapture = false;
try
    int i = 0;
    System.out.println("Start");
    //Schleife um daten aufzunehmen
  while(!stopCapture)
    //daten vom targetDataLine lesen
    int cnt = targetDataLine.read(tempBuffer,0,tempBuffer.length);


    if(cnt > 0)//Jeder 5. Wert wird übernommen
      //Die daten in der bytearrayoutputstream speichern
      byteArrayOutputStream.write(tempBuffer, 0, cnt);

      


    

然后我将 byteArrayOutputStream 转换为 Bytearray audioData 并尝试以 48000/5=9800 的采样率“下采样”我录制的音频。换句话说,它是我的 bytearray audioData 的每 5 个值。然后我想将它内插回原始采样率 48000。见代码:

public void run()
try
  int cnt;
  int n = 0;
  int k = 0;
  int m = 0;
  double summe = 0;


  ByteArrayOutputStream aufnahme_2 = new ByteArrayOutputStream();

  System.out.println("Replay");
  //Schleife soll solange laufen bis die letzte Datei abgespielt wurde.
  //Am ende gibt die Datei -1 raus
  Downsampling 5==> Jeder 5. Wert wird übernommen
 for(; m<= audioData.length;m++) 

      if ( m%5 < 0.000001 & m != 0) 
          k++;
          n=k;
      

      for(;n<=5+k;n++) 
          if(n*5 < audioData.length) 
              if(Math.abs((double) m/5-n) <0.00001) 
              summe = summe+ audioData[n*5];
          
              else 
              summe = summe + audioData[n*5]*Math.sin(Math.PI*((double) m/5-n))/( Math.PI*((double) m/5-n));    //Der double cast muss sein, damit die zahl als double und nicht als int gerechnet wird 
          
         
      

      //byteBuffer.putShort((short) summe);
      aufnahme_2.write((short) summe);      //Short weil der Datentyp short 2Byte große ist
      summe = 0;
      n=k;
  
  ergebnis = aufnahme_2.toByteArray();

  InputStream byteArrayInputStream_down = new ByteArrayInputStream(ergebnis);
  audioInputStream = new AudioInputStream(byteArrayInputStream_down, audioFormat,ergebnis.length); 

  while((cnt = audioInputStream.read(tempBuffer,0,tempBuffer.length))!=-1) 

    if(cnt > 0)
      sourceDataLine.write(tempBuffer, 0, cnt);

    
  

我的音频格式:

 private AudioFormat getAudioFormat()

float sampleRate = 48000;
int sampleSizeInBits = 16;
int channels = 2;
boolean signed = true;
boolean bigEndian = false;

return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);

使用这种音频格式,我收到的声音质量很差。当我将 sampleSizeInBits 更改为 8 时,声音更清晰,为什么?我想获得 16 位的分辨率。我试图以整数、字节 .... 格式保护我的 sinc 值,但没有任何帮助。所以我希望有人可以帮助并告诉我为什么不起作用。

PS:我发送一张我在 Matlab 中制作的图片向您展示我的目标: 蓝线是我的音频输入,黑线是下采样重建的音频输出

【问题讨论】:

不清楚你的问题是什么。你的图片是正确的。您的问题是值的缩放吗? (short) summe 可能不会产生所有可能的值... 不,我不明白为什么我不能选择 16 位的分辨率。我必须使用 8 位才能获得清晰的声音。为什么? 你所说的“清晰”的声音是什么意思?我怀疑 8 位的范围是 [-128;+127] 但 16 位的范围是一样的,不是吗?无法在您的公式中看到相应地重新调整范围的内容 顺便问一下如何获取所有值?我选择 typ short 是因为这种类型有 2 个字节长。也应该是我的样本大小。所以 16 位而不是 8 位 什么是audioData类型?原始样本位深度是多少? 【参考方案1】:

这是整个程序:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;

import javax.sound.sampled.*;

public class AudioCapture01 extends JFrame


  boolean stopCapture = false;
  ByteArrayOutputStream byteArrayOutputStream;
  AudioFormat audioFormat;
  TargetDataLine targetDataLine;
  AudioInputStream audioInputStream;
  SourceDataLine sourceDataLine;
  byte [] audioData;


  public static void main(String args[])

    new AudioCapture01();
  //end main

  public AudioCapture01()//constructor
    final JButton captureBtn =  new JButton("Capture");

    final JButton stopBtn = new JButton("Stop");

    final JButton playBtn = new JButton("Playback");


    captureBtn.setEnabled(true);
    stopBtn.setEnabled(false);
    playBtn.setEnabled(false);

    //Register anonymous listeners
    captureBtn.addActionListener(
      new ActionListener()
        public void actionPerformed(
                        ActionEvent e)
          captureBtn.setEnabled(false);
          stopBtn.setEnabled(true);
          playBtn.setEnabled(false);
          //Capture input data from the
          // microphone until the Stop
          // button is clicked.
          captureAudio();
        //end actionPerformed
      //end ActionListener
    );//end addActionListener()
    getContentPane().add(captureBtn);

    stopBtn.addActionListener(
      new ActionListener()
        public void actionPerformed(
                        ActionEvent e)
          captureBtn.setEnabled(true);
          stopBtn.setEnabled(false);
          playBtn.setEnabled(true);

          stopCapture = true;
        //end actionPerformed
      //end ActionListener
    );//end addActionListener()
    getContentPane().add(stopBtn);

    playBtn.addActionListener(
      new ActionListener()
        public void actionPerformed(
                        ActionEvent e)
          //Play back all of the data
          // that was saved during
          // capture.
          playAudio();
        //end actionPerformed
      //end ActionListener
    );//end addActionListener()
    getContentPane().add(playBtn);

    getContentPane().setLayout(
                     new FlowLayout());
    setTitle("Capture/Playback Demo");
    setDefaultCloseOperation(
                        EXIT_ON_CLOSE);
    setSize(350,70);
    setVisible(true);
  // end constructor

     private void captureAudio()
    try


        //Schema F implementierung... richtige aufnahme mit samplerate 48000Hz
      audioFormat = getAudioFormat();

DataLine.Info dataLineInfo=new DataLine.Info(TargetDataLine.class,audioFormat);


      //hier wird das Objekt TargetDataLine erstellt
      targetDataLine = (TargetDataLine) Audiosystem.getLine(dataLineInfo);     
      targetDataLine.open(audioFormat);
      targetDataLine.start();


      Thread captureThread =  new Thread(new CaptureThread());            

      captureThread.start();
     
    catch (Exception e) 
      System.out.println(e);
      System.exit(0);
    
  

  // Vorteil eines Bytearrayutputstream ist es, dass das Array sich automatisch erweitert
  // Die daten sind im ByteArrayOutputStream gespeichert
  //in der Methode werden die Daten widergegeben und abgespielt
  private void playAudio() 
    try
      // das was gesampelt wurde wird in ein byte array umgewandelt um damit weitere methoden zu nutzen
    int i =0;
    audioData = byteArrayOutputStream.toByteArray();
      System.out.println("laenge von audioData" + audioData.length);    //Um zu sehen was passiert

      /**for(; i< audioData.length ;i++) 
          System.out.println(audioData[i]);
      

      System.out.println("Index i = " + i);
      */           

      //das Array wandel ich in ein inpustream um
      InputStream byteArrayInputStream = new ByteArrayInputStream(audioData); 

         //Um am ende es in ein audioinputstream hinzuzufügen
        //wird benutzt um daraus die daten zu lesen und sie in die sourcedataline zu schreiben
      audioInputStream = new AudioInputStream(byteArrayInputStream, audioFormat,audioData.length/audioFormat.getFrameSize()); 

      //Damit kann ich es sofort abspielen lassen.
      //audioInputStream = AudioSystem.getAudioInputStream(audioFormat_down,new AudioInputStream(targetDataLine)); 
      //Das heißt ich speicher die Daten nicht zuerst in einem Array sondern lass sie sofort abspielen    
      DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);

      //Sourcedataline ist dafür verantworlich das die Daten       
      sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo); 

      sourceDataLine.open(audioFormat);
      sourceDataLine.start();

   // Threat um die daten die aufgenommen wurden abzuspielen
      Thread playThread = new Thread(new PlayThread());

      playThread.start();
     
    catch (Exception e) 
      System.out.println(e);
      System.exit(0);
    
  


  private AudioFormat getAudioFormat()

    float sampleRate = 48000;
    int sampleSizeInBits = 16;//change it to 8 to get a clear sound
    int channels = 2;
    boolean signed = true;
    boolean bigEndian = false;

    return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);

  


//die Innere Thread Methode um den "run" ablauf der signalaufnahme zu regulieren
class CaptureThread extends Thread
  //temporerer buffer
  byte tempBuffer[] = new byte[10000];
  public void run()
    byteArrayOutputStream = new ByteArrayOutputStream(); //hier initialisiere ich die variabele bytearrayoutputstream

    stopCapture = false;
    try
        int i = 0;
        System.out.println("Start");
        //Schleife um daten aufzunehmen
      while(!stopCapture)
        //daten vom targetDataLine lesen
        int cnt = targetDataLine.read(tempBuffer,0,tempBuffer.length);


        if(cnt > 0)//Jeder 5. Wert wird übernommen
          //Die daten in der bytearrayoutputstream speichern
          byteArrayOutputStream.write(tempBuffer, 0, cnt);

          
        i++;

        
      System.out.println("Aufnahme hat Werte = "+ i +"gespeichert");

      byteArrayOutputStream.close();
    catch (Exception e) 
      System.out.println(e);
      System.exit(0);
    
  


//Die innere klasse von Thread um die gespeicherten daten im bytearrayoutputstream abzuspielen
class PlayThread extends Thread
  byte tempBuffer[] = new byte[200000];
  byte ergebnis[] = new byte[audioData.length];

  ByteBuffer byteBuffer;
  ShortBuffer shortBuffer;


  public void run()
    try
      int cnt;
      int n = 0;
      int k = 0;
      int m = 0;
      double summe = 0;

      //byteBuffer = ByteBuffer.wrap(ergebnis);
      //shortBuffer = byteBuffer.asShortBuffer();
      ByteArrayOutputStream aufnahme_2 = new ByteArrayOutputStream();

      System.out.println("Replay");
      //Schleife soll solange laufen bis die letzte Datei abgespielt wurde.
      //Am ende gibt die Datei -1 raus
     /** while((cnt = audioInputStream.read(tempBuffer,0,tempBuffer.length))!=-1) 
          System.out.println(tempBuffer[i]);
          i++;
         if(cnt > 0)
            //schreibt daten in den Mixer
          //sourceDataLine.write(tempBuffer, 0, cnt);

        

      
      System.out.println("index i = "+i);
      */
      //Versuch :SI-interpolation tempBuffer hat die Werte gespeichert. Downsampling 5==> Jeder 5. Wert wird übernommen
     for(; m<= audioData.length;m++) 

          if ( m%5 < 0.000001 & m != 0) 
              k++;
              n=k;
          

          for(;n<=5+k;n++) 
              if(n*5 < audioData.length) 
                  if(Math.abs((double) m/5-n) <0.00001) 
                  summe = summe+ audioData[n*5];
              
                  else 
                  summe = summe + audioData[n*5]*Math.sin(Math.PI*((double) m/5-n))/( Math.PI*((double) m/5-n));    //Der double cast muss sein, damit die zahl als double und nicht als int gerechnet wird 
              
             
          

          //byteBuffer.putShort((short) summe);
          aufnahme_2.write((int) summe);        //Short weil der Datentyp short 2Byte große ist
          summe = 0;
          n=k;
      
      ergebnis = aufnahme_2.toByteArray();

      /** 
      System.out.println("Ergebnisse");
      for(n=0;n < audioData.length; n++)                            Die Arrayliste auf die Werte von audioData und ergebnisse zu prüfen
          System.out.println(ergebnis[n]+" "+ audioData[n]+ " "+n);
      
      */


      InputStream byteArrayInputStream_down = new ByteArrayInputStream(ergebnis);
      audioInputStream = new AudioInputStream(byteArrayInputStream_down, audioFormat,ergebnis.length); 

      while((cnt = audioInputStream.read(tempBuffer,0,tempBuffer.length))!=-1) 

        if(cnt > 0)
          sourceDataLine.write(tempBuffer, 0, cnt);

        
      

      sourceDataLine.stop();
      sourceDataLine.close();
    
    catch (Exception e) 
      System.out.println(e);
      System.exit(0);
    
  


【讨论】:

不要将此作为答案(不是),编辑您的问题并最终添加注释。 好的注意到了。大 ty 为您提供帮助:D

以上是关于Java下采样和上采样的主要内容,如果未能解决你的问题,请参考以下文章

图像上采样和图像下采样

如何禁用Oracle AWR自动采样功能

[Paper Weekly]CNN采样方法:空间变换网络(STN)与可变形卷积网络(DCN)

FCN,FPN,UNet对比总结 2021-02-27

重采样上采样下采样

上采样/下采样