Проблемы синхронизации при сворачивании окна JavaFX - PullRequest
0 голосов
/ 26 апреля 2020

, поэтому у меня есть программа, которая воспроизводит aws звуковую волну в режиме реального времени любого пути песни, который вы передаете в аргументах времени выполнения (должен быть полный абсолютный путь). Это работает хорошо, однако есть 2 проблемы, на которые я поставлен в тупик. Во-первых, если окно свернуто и вновь открыто, сгенерированный сигнал полностью не синхронизирован c и в конечном итоге просто превращается в горизонтальную линию. Во-вторых, если пользователь нажимает кнопку воспроизведения, чтобы перезапустить песню, она работает нормально, но если вы нажмете ее примерно за 10 секунд до окончания песни, он перезапустит сигнал без воспроизведения звука. Может кто-нибудь сказать мне, почему это? Я думаю, что это как-то связано с моими переменными atomi c, но я не совсем уверен. Вот мой код:

import org.mp3transform.wav.WavConverter;

import javafx.application.Application; 
import javafx.geometry.Insets; 
import javafx.geometry.Pos; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.layout.*;
import javafx.scene.shape.*; 
import javafx.scene.Group;
import javafx.stage.Stage;
import javafx.scene.paint.Color;
import javafx.animation.AnimationTimer;

import java.util.ArrayList;

import javax.sound.sampled.TargetDataLine;
import javax.sound.sampled.SourceDataLine;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ExecutionException;

import javafx.stage.Screen;
import javafx.geometry.Rectangle2D;

public class SongLibraryJar extends Application {
  private Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
  private int queueSize;
  private int numberOfSamplesPerFrame;
  private int startWindowIndex = 0;
  private int endWindowIndex;
  private int i = 0;      
  private static ArrayList<Integer> values = new ArrayList<>();
  private final static AtomicBoolean stopped = new AtomicBoolean(false);
  private final static AtomicBoolean play = new AtomicBoolean(false); 
  private final static AtomicBoolean calculated = new AtomicBoolean(true);
  private Line[] lines;
  private int oldY = 0;
  private int newY = 0;

@Override 
public void start(Stage primaryStage) {          
 PrepareSong data = new PrepareSong(getParameters().getUnnamed().get(0));

 ExecutorService service = Executors.newSingleThreadExecutor(new ThreadFactory() {
   @Override
   public Thread newThread(Runnable runnable) {
     Thread thread = Executors.defaultThreadFactory().newThread(runnable);
     thread.setDaemon(true);
     return thread;
   }
 });

 Future<ArrayList<Integer>> future = service.submit(data); //Stores the audio data into the variable future
 service.shutdown(); 
 boolean flag = true; //when flag turns to false, signifies that we don't want to loop anymore
 while (flag) { //Constantly loops to check if the future variable is done receiving data. Once the variable has received data, finish the loop and exit.
   if (future.isDone()) { //Only want to go into this loop if the future variable is done receiving data.
     try {
       values = future.get(); //THIS IS WHERE THE AMPLITUDE VALUES FOR THE SONG ARE STORED. THIS STORES THEM AFTER THEY ARE COMPRESSED. THE ORIGINAL UNCOMPRESSED VALUES ARE STORED IN AN ARRAYLIST CALLED songData IN THE PrepareSong CLASS ************ READ ME **************************
     }
     catch (InterruptedException ex) {
       ex.printStackTrace();
     }
     catch (ExecutionException ex) {
       ex.printStackTrace();
     }
     flag = false; 
   }
 }

 int waveformHeight = (int)(visualBounds.getHeight() - visualBounds.getHeight() * .15);
 int waveformWidth = (int)(visualBounds.getWidth() - visualBounds.getWidth() * .10);   

 numberOfSamplesPerFrame = data.getSampleRate() / 60;
 endWindowIndex = numberOfSamplesPerFrame;

 Group root = new Group();
 BorderPane pane = new BorderPane();
 pane.setBackground(new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)));
 pane.setPadding(new Insets(0,15,15,0));
 pane.setCenter(root);
 pane.setBottom(getButtons(data.getAudioFilePath()));

 lines = new Line[numberOfSamplesPerFrame]; 
 for (int i = 0; i < numberOfSamplesPerFrame; i++) { //Initalizes all the lines that are going to be drawn for each frame.
   lines[i] = new Line(0,0,0,0);
   root.getChildren().add(lines[i]);
 }
 System.out.println("Calculate complete");

  LinkedBlockingQueue<ArrayList> queue = new LinkedBlockingQueue();

 Thread passRawAudioData = new Thread() {
    @Override public void run() {       
     boolean flag = true;  
     while (flag) {                       
       while (play.get() == true && stopped.get() == false) {                                     
         ArrayList<Integer> amplitudes = new ArrayList<>();
         calculated.set(false);
         queue.clear();
         for (i = startWindowIndex; i < endWindowIndex; i++) {
           if (i >= values.size()) {
             queueSize = queue.size();
             stopped.set(true);
             play.set(false);
             break;
           }
           amplitudes.add(values.get(i));
           try {
             queue.put(amplitudes); //THIS PASSES THE VALUES TO THE QUEUE SO THE WAVEFORM CAN BE DRAWN IN REAL TIME
           }
           catch(InterruptedException ex) {
             ex.printStackTrace();
           }
         }
         queueSize = lines.length;
         startWindowIndex += numberOfSamplesPerFrame;
         endWindowIndex = startWindowIndex + numberOfSamplesPerFrame;
         boolean notDone = true;

         if (stopped.get())
           break;  
         while (notDone) { 
           if (calculated.get() == true) {
             notDone = false;
           }                
         }           
        }         
      }
    }
  };     

Thread drawWaveform = new Thread() {
  @Override 
  public void run() {
    AnimationTimer timer = new AnimationTimer() {
       @Override
       public void handle(long now) { 
        int i = 0;
        int xAxis = 0;
        int counter = waveformWidth / numberOfSamplesPerFrame;
         int y = 0;
         boolean firstIteration = true;

        while (i < queueSize) {
          try {
              y = (Integer)queue.take().get(i);
             if (firstIteration) {
               if (y <= 0) {
                 newY = ((Math.abs(y)) + (waveformWidth / 2));
                 oldY = newY;
                 firstIteration = false;
               }
               else {
                 newY = ((waveformWidth / 2) - y);
                 oldY = newY;
                 firstIteration = false;
               }
             }
             else {
               if (y <= 0) { 
                 oldY = newY;
                 newY = ((Math.abs(y)) + (waveformWidth / 2));
               }                   
               else {
                 oldY = newY;
                 newY = ((waveformWidth / 2) - y);
               }
             }
           }
           catch (InterruptedException ex) {
             ex.printStackTrace();
           }
          lines[i].setStroke(Color.RED);
           lines[i].setStartX(xAxis);
           lines[i].setEndX(xAxis + counter);
           lines[i].setStartY(oldY);
           lines[i].setEndY(newY);
           i++;
          xAxis += counter;               
         }
        calculated.set(true); 
       }
    };
    while (true) { 
       if (stopped.get()) {
        timer.stop();
       }
      while (stopped.get() == false) {
        if (queue.size() >= numberOfSamplesPerFrame) {
          timer.start();
         }
       }  
     }
  }     
};
passRawAudioData.setDaemon(true);
passRawAudioData.start();
drawWaveform.setDaemon(true);
drawWaveform.start(); 

Scene scene = new Scene(pane, visualBounds.getWidth() - visualBounds.getWidth() * .05, visualBounds.getHeight() - visualBounds.getHeight() * .05);
primaryStage.setTitle("Generated Waveform");
primaryStage.setScene(scene);
primaryStage.show();
}

//Play Music
private HBox getButtons(String path) {
HBox hbox = new HBox();
hbox.setPadding(new Insets(15, 0, 15, 0));
Button playMusic = new Button("Play");
playMusic.setMinSize(60, 35);

Button pauseMusic = new Button("Pause");
pauseMusic.setMinSize(60, 35);

Button resumeMusic = new Button("Resume");
resumeMusic.setMinSize(60, 35);

    hbox.getChildren().add(playMusic); //Add buttons to the pane
    hbox.getChildren().add(pauseMusic);
    hbox.getChildren().add(resumeMusic);
    hbox.setAlignment(Pos.BOTTOM_CENTER);
    hbox.setMargin(playMusic, new Insets(0,0,20,0));
    hbox.setMargin(pauseMusic, new Insets(0,0,20,0));
    hbox.setMargin(resumeMusic, new Insets(0,0,20,0));
    hbox.setBackground(new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)));
    hbox.setSpacing(15);

    AudioPlayback player = new AudioPlayback(path);
    AudioThread audioPlaybackThread = new AudioThread(player);
    audioPlaybackThread.setDaemon(true);
    audioPlaybackThread.start();
    //Even handler for play button
    playMusic.setOnAction(e -> { 
    player.play();
    stopped.set(false);
    i = 0;
    startWindowIndex = 0;
    endWindowIndex = numberOfSamplesPerFrame;
    play.set(true);
    });

    //Event handler for resume button
    resumeMusic.setOnAction(e -> {
    play.set(true);
    stopped.set(false);
    player.resume();
    });  

    //Even handler for pause button
    pauseMusic.setOnAction(e -> {
    play.set(false);
    stopped.set(true);
    player.pause();
    });

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