Java FXML 录音机无法排空目标数据线

Java FXML sound recorder can't drain target data line

编辑: 我通过更改几行找到了解决方案,问题只是部分是由于坏循环造成的。这是问题的主要部分:

'''

public void recordStop(){
    System.out.println("stop recording");
    recordingRunning = false;
    if (dataLine != null) {
        dataLine.flush();
        dataLine.close();
        System.out.println("close and drain line");
    }


}

'''

使用 .drain() 会杀死线程,所以只使用 .flush() 来代替,我们不寒而栗。

(TLDR - 当我按下一个按钮来排空目标数据行时,调用的函数将尝试排空该行,但 GUI 将崩溃并且没有文件输出)

我一直在用 JavaFXML 制作录音机,使用 java 声音采样 class。当 GUI 运行ning 时,我正在使用单独的线程 运行 记录器,但是在尝试停止记录并保存记录时会出现问题。我已经设法让线程正常启动,并在按下按钮时 运行。这是通过以下代码激活的:

按下按钮时的 FXML 事件处理程序:

'''

    @FXML
    private void recordingButtonAction(ActionEvent event) throws LineUnavailableException {
    if(isRecording == false){
    label.setText("Recording");
    fileName = RT.setFileName();
   channelLocationTracker(channelSelectToggleGroup);
   try{
    TargetDataLine dataLine =AudioSystem.getTargetDataLine(audioFormat);
   }
   catch (LineUnavailableException ex) {
       ex.printStackTrace();
   }
   isRecording=true;
    }
    else{
        System.out.println("Already recording");
    }
}

'''

开始单独记录的代码 class 其中对象 "RT" 来自:

'''

    public TargetDataLine getDataLine() throws LineUnavailableException{
    audioFormat = getAudioFormat();
    DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat);
    if (!AudioSystem.isLineSupported(info)) {
        throw new LineUnavailableException(
                "Format Unsupported");
    }

    final TargetDataLine dataLine = AudioSystem.getTargetDataLine(audioFormat);;
    AudioSystem.getLine(info);
    return dataLine;
    }

''' '''

    public void recordStart(boolean isRecording) throws LineUnavailableException{

    isRecording = false;
    audioFormat = getAudioFormat();
    dataLine = getDataLine();
    dataLine.open(audioFormat);
    dataLine.start();

    System.out.println("open line");

    System.out.println("start recording");
    byte[] buffer = new byte[bufferSz];
    int bytesRead = 0;

    recordedBytes = new ByteArrayOutputStream();
    isRecording = true;

    while (isRecording) {
        bytesRead = getDataLine().read(buffer, 0, buffer.length);
        recordedBytes.write(buffer, 0, bytesRead);
    }
}

'''

以及线程本身:

'''

    private void startRecord(){
    Thread startRec = new Thread(new Runnable(){
        public void run(){
            try {
                isRecording=true;
                RT.recordStart(isRecording);
            } catch (LineUnavailableException ex) {
                Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
            }
         }
       });
      startRec.start();
     }

'''

当我按下停止记录按钮时出现问题,它会在 shell 中打印引流线,但 GUI 会崩溃并且不会打印停止记录。我认为问题来自 .drain() 方法。

这里又是 FXML:

'''

    @FXML
    private void stopButtonAction(ActionEvent event) throws IOException, LineUnavailableException, 
    UnsupportedAudioFileException {    
       RT.setFileName("recording"+".wav");
       recordedFileWAV = RT.getFile();
       if(isRecording == true){
       endRecord();
       }

    }

'''

单独class停止录制:

'''

    public void recordStop(boolean isRecording){
    isRecording = false;
    System.out.println("drain line");
    dataLine.drain();
    System.out.println("stop recording");
    dataLine.close();


    }

'''

还有录音中的方法class让你保存:

'''

    public void recordSave(File wavFile) throws IOException {
    byte[] lineData = recordedBytes.toByteArray();
    ByteArrayInputStream bais = new ByteArrayInputStream(lineData);
    AudioInputStream AIS = new AudioInputStream(bais, audioFormat,lineData.length / 
    audioFormat.getFrameSize());

    AudioSystem.write(AIS, AudioFileFormat.Type.WAVE, wavFile);

    AIS.close();
    recordedBytes.close();
    }

如果你能弄清楚哪里出了问题,或者你能给我指明正确的方向,请告诉我:)

以下建议更多地针对一般原则和最佳实践。我没有花时间检查你的代码的细节,除了对我在你的 public 停止和保存方法中看到的内容做出反应。

您在自己的线程中进行了录制(应该如此)。与另一个线程交互的通常方式是使用松散耦合。因此,在您的 public 停止和保存方法中,限制您自己设置一个标志,例如 isRecording。然后,让控制播放的线程中的代码参考那个布尔值。例如,有一个外循环 while(isRecording).

我在将 JavaFX 与 javax.sound.sampled 结合使用时成功地使用了这种松散耦合设计模式,用于控制播放和保存到文件。以这种方式做事有助于澄清 类、实例和线程之间可能发生的混淆和冲突。

希望这能为您指明方向,解决您遇到的问题。

解决方案:

'''

public void recordStop(){
System.out.println("stop recording");
recordingRunning = false;
if (dataLine != null) {
    dataLine.flush();
    dataLine.close();
    System.out.println("close and drain line");
}

}

'''