Java понижающая и повышающая дискретизация

Я новичок в Java Sound API, много читал в оракуле и Ричарде Браулине и пробую некоторые проекты с его программами. Первое, что я хочу захватить, это звук, понизить его дискретизацию и вернуть обратно в правильный аудиоформат. Тогда я попробую то же самое, но без записи.

Сначала я покажу вам важные части кода, а затем расскажу о своей проблеме:

здесь я записываю звук с микрофона:

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 и пытаюсь «понизить дискретизацию» моего записанного звука с частотой дискретизации 48000/5 = 9800. Другими словами, это каждое 5-е значение моего байтового массива audioData. Затем я хочу, чтобы sinc интерполировал его обратно к исходной частоте дискретизации 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, чтобы показать вам мою цель: синяя линия - это мой аудиовход, а черная - реконструированный аудиовыход с пониженной частотой дискретизации.

введите здесь описание изображения


person Reazelruss    schedule 09.03.2018    source источник
comment
Не понятно в чем у вас проблема. Ваша картинка правильная. Ваша проблема заключается в масштабировании значений? (short) summe может не давать полный диапазон возможных значений...   -  person Jean-Baptiste Yunès    schedule 09.03.2018
comment
Нет, я не понимаю, почему я не могу выбрать разрешение 16 бит. Я должен использовать 8 бит, чтобы получить чистый звук. Зачем?   -  person Reazelruss    schedule 09.03.2018
comment
Что вы подразумеваете под чистым звуком? Я подозреваю, что с 8 битами ваш диапазон [-128;+127], но то же самое с 16 битами, не так ли? Не вижу в ваших формулах что-то, что соответствующим образом масштабирует диапазон   -  person Jean-Baptiste Yunès    schedule 09.03.2018
comment
Кстати, как я могу получить все значения? Я выбираю тип short, потому что этот тип имеет длину 2 байта. Также должен быть мой размер выборки в битах. Так 16 а не 8 бит   -  person Reazelruss    schedule 09.03.2018
comment
Что такое тип audioData? И какова была битовая глубина исходного семпла?   -  person Jean-Baptiste Yunès    schedule 09.03.2018
comment
Когда я воспроизвожу свой звук с 16-битным звуком, у меня появляется жесткий свист. С 8 битами шум тише. Но обычно шум должен быть тише с 16 битами. Или не?   -  person Reazelruss    schedule 09.03.2018
comment
audioData имеет тип byte[]. И записал в аудиоформате (48000,16,2,true false). Итак, я хочу записать его в аудиоформате (48000,16,2, правда, ложь) и хочу передать свои преобразованные аудиоданные с тем же аудиоформатом в мою исходную строку данных. Итак, одинаковая битовая глубина   -  person Reazelruss    schedule 09.03.2018
comment
Шум улавливается при высокочастотной выборке, но не зависит от разрешения. Трудно ответить, не имея возможности слушать, что происходит на самом деле   -  person Jean-Baptiste Yunès    schedule 09.03.2018
comment
я должен отправить всю свою программу? у него 334 строки и много комментариев, потому что сделано несколько замечаний   -  person Reazelruss    schedule 09.03.2018
comment
Если audioData имеет тип byte[], значит, вы что-то упустили, потому что, когда вы пишете audioData[n*5], вы записываете не те данные! Если ваш сэмпл 16-битный, вам нужно получить 2 байта: audioData[2*n*5] и audioData[2*n*5+1], которые в совокупности составляют семпл.   -  person Jean-Baptiste Yunès    schedule 09.03.2018
comment
Что-то вроде stackoverflow.com/questions/736815/2-bytes-to-short-java   -  person Jean-Baptiste Yunès    schedule 09.03.2018
comment
Ах хорошо, так что я только поймать половину. Поэтому работает только с 8 битами а не с 16. ты :)   -  person Reazelruss    schedule 09.03.2018
comment
На самом деле хуже, когда вы берете одну верхнюю часть, а затем одну нижнюю часть семпла и т. д. Вот почему, вероятно, у вас есть такой свистящий эффект.   -  person Jean-Baptiste Yunès    schedule 09.03.2018


Ответы (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);
    }
  }
}
}
person Reazelruss    schedule 09.03.2018
comment
Не ставьте это как ответ (это не так), отредактируйте свой вопрос и, в конце концов, добавьте примечание. - person Jean-Baptiste Yunès; 09.03.2018
comment
ОК заметил. Большая тебе помощь :D - person Reazelruss; 09.03.2018