Создайте звуковой сигнал в режиме реального времени, используя Java - PullRequest
0 голосов
/ 20 марта 2020

Я пытаюсь сгенерировать звуковую форму волны, читая существующий WAV-файл, который обновляется при воспроизведении музыки c в режиме реального времени. Например, если песня воспроизводится, я хочу, чтобы форма волны непрерывно генерировалась по ходу песни. До сих пор мне удавалось читать данные из файла .wav и генерировать сигнал STATI C, используя javafx для примерно первых 2500 сэмплов песни. Я сравнил форму волны с тем, как она выглядит в смелости, и она очень хорошо ей соответствует. Вот как это выглядит:

Generated Waveform using javafx

Однако я не совсем понимаю, как заставить генерацию сигнала генерироваться не только статически для конкретного окна, но и генерировать постоянно обновляемый сигнал на протяжении всей песни. Я изучал графики в реальном времени и использовал ScheduledExecutorService, но я не могу заставить линейный график рисовать новую точку быстрее, чем раз в секунду, не вызывая проблем. Есть ли другой способ, которым я должен go сделать это? Я потратил много времени на это и не хочу сейчас сдаваться. Я приложил свой код ниже, посмотрите. Спасибо.

import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.shape.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.geometry.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.scene.text.Text;
import javafx.scene.chart.LineChart; 
import javafx.scene.chart.NumberAxis; 
import javafx.scene.chart.XYChart;
import javafx.scene.Group; 


import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.TargetDataLine;
import java.net.*;
import java.io.*;
import java.nio.*;

public class Practice2 extends Application {
   public static AudioInputStream audioInputStream;   

   @Override
   public void start(Stage primaryStage) {
  int channelCounter = 2; //used to jump between left and right channel in the loop 

   try {
       File file = new File("testData/record.wav"); 
   byte[] data = new byte[(int)file.length()];
   BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
   in.read(data);
   in.close();

   File leftChannelFile = new File("audioData/leftChannelAmpData.txt");
   File rightChannelFile = new File("audioData/rightChannelAmpData.txt");

   //Defining the x axis             
   NumberAxis xAxis = new NumberAxis(0, 2490, 1); 
   xAxis.setLabel("Samples"); 

   //Defining the y axis   
   NumberAxis yAxis = new NumberAxis   (-675, 675, 1); 
   yAxis.setLabel("Amplitude"); 

   //Creating the line chart 
   LineChart linechart = new LineChart(xAxis, yAxis);
   linechart.setPrefHeight(1350); //picked 1350 and 2500 because it is slightly less than the height and width of my monitor. Will definitely change these values in the future
   linechart.setPrefWidth(2500);
   linechart.setMaxHeight(1350);
   linechart.setMaxWidth(2500);  

   //Prepare XYChart.Series objects by setting data 
   XYChart.Series series = new XYChart.Series(); 
   series.setName("Amplitude from sample"); 

   for (int i=44; i < data.length - 1; i+=2) { //start at the 44th byte of the .wav file. This skips the header info and reads the raw data
     if (channelCounter % 2 == 0) { //This represents the loop for the left channel
       channelCounter++;
       short tempAmp = calculateAmpValue(data[i], data[i+1]); // Since the amplitude value for each sample is represented by 2 bytes instead of 1, this function takes the next 2 bytes and gives an integer representation for it (-32728 to 32727)
       if (i < 2491) 
         series.getData().add(new XYChart.Data(i-43, tempAmp / 50)); //divide by 50 so the amplitude values can fit within the range of my graph.
     } 
     else if (channelCounter % 2 == 1) { //this represents the loop for the right channel. ***I'M ONLY BUILDING A WAVEFORM FOR THE LEFT CHANNEL AT THE MOMENT. ***
       channelCounter++;
       short tempAmp = calculateAmpValue(data[i], data[i+1]);
     }    
   }
    //Setting the data to Line chart    
    linechart.getData().add(series);
    Group root = new Group(linechart);
    Scene scene = new Scene(root, 2500, 1350); 
    primaryStage.setTitle("Generated Waveform");
    primaryStage.setScene(scene);
    primaryStage.show();       
 } 
 catch (FileNotFoundException ex){ex.printStackTrace();}
 catch (IOException ex){ex.printStackTrace();}        
 } 

  public static short calculateAmpValue(byte byte1, byte byte2) {
ByteBuffer bb = ByteBuffer.allocate(2);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.put(byte1);
bb.put(byte2);
short ampVal = bb.getShort(0);
return ampVal;
}

public static void main(String[] args) {
  Application.launch(args);
}
}

1 Ответ

0 голосов
/ 20 марта 2020

Если бы я искал диаграмму в реальном времени с помощью JavaFX, я бы, вероятно, предположил, что для рисования я бы использовал AnimationTimer. Он работает на скорости 60 кадров в секунду. Таким образом, все, что рисуется, будет отражать изменения, сделанные за этот период времени. Если звук составляет 44100 кадров в секунду, это 735 точек аудиоданных за период цикла анимации. Я не понимаю, что вы пытаетесь, хотя. Вы пытаетесь написать эквивалент осциллографа?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...