mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
buffers. None of the decoders currently support this yet, but the P25 will be updated to provide bitstream output for recording. Renames the ReusableBuffer class to ReusableFloatBuffer for consistency.
386 lines
12 KiB
Java
386 lines
12 KiB
Java
/*******************************************************************************
|
|
* sdr-trunk
|
|
* Copyright (C) 2014-2018 Dennis Sheirer
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
|
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
|
|
* later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with this program.
|
|
* If not, see <http://www.gnu.org/licenses/>
|
|
*
|
|
******************************************************************************/
|
|
package io.github.dsheirer.gui.instrument;
|
|
|
|
import io.github.dsheirer.gui.instrument.decoder.AbstractDecoderPane;
|
|
import io.github.dsheirer.sample.Listener;
|
|
import io.github.dsheirer.sample.buffer.ReusableBufferBroadcaster;
|
|
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
|
import io.github.dsheirer.source.IControllableFileSource;
|
|
import io.github.dsheirer.source.IFrameLocationListener;
|
|
import io.github.dsheirer.source.wave.ComplexWaveSource;
|
|
import io.github.dsheirer.source.wave.RealWaveSource;
|
|
import javafx.event.ActionEvent;
|
|
import javafx.event.EventHandler;
|
|
import javafx.geometry.Pos;
|
|
import javafx.scene.control.Alert;
|
|
import javafx.scene.control.Button;
|
|
import javafx.scene.control.ButtonType;
|
|
import javafx.scene.control.Label;
|
|
import javafx.scene.control.TextField;
|
|
import javafx.scene.layout.HBox;
|
|
import org.kordamp.ikonli.fontawesome.FontAwesome;
|
|
import org.kordamp.ikonli.javafx.FontIcon;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
|
|
public class PlaybackController extends HBox implements IFrameLocationListener
|
|
{
|
|
private final static Logger mLog = LoggerFactory.getLogger(PlaybackController.class);
|
|
|
|
private Button mRewindButton;
|
|
private TextField mPlaybackPositionText;
|
|
private Button mPlay1Button;
|
|
private Button mPlay10Button;
|
|
private Button mPlay30Button;
|
|
private Button mPlay100Button;
|
|
private Button mPlay1000Button;
|
|
private Button mPlay2000Button;
|
|
private HBox mControlsBox;
|
|
private Label mFileLabel;
|
|
private EventHandler<ActionEvent> mPlayEventHandler;
|
|
private AbstractDecoderPane mSampleRateListener;
|
|
|
|
private IControllableFileSource mControllableFileSource;
|
|
private ReusableBufferBroadcaster mReusableBufferBroadcaster = new ReusableBufferBroadcaster();
|
|
|
|
public PlaybackController()
|
|
{
|
|
super(10);
|
|
|
|
getChildren().addAll(getControlsBox(), getFileLabel());
|
|
|
|
disableControls();
|
|
}
|
|
|
|
public void setSampleRateListener(AbstractDecoderPane decoderPane)
|
|
{
|
|
mSampleRateListener = decoderPane;
|
|
|
|
if(mSampleRateListener != null && mControllableFileSource != null)
|
|
{
|
|
mSampleRateListener.setSampleRate(mControllableFileSource.getSampleRate());
|
|
}
|
|
}
|
|
|
|
public void load(File file)
|
|
{
|
|
if(file != null && file.isFile())
|
|
{
|
|
if(ComplexWaveSource.supports(file))
|
|
{
|
|
try
|
|
{
|
|
mControllableFileSource = new ComplexWaveSource(file);
|
|
mControllableFileSource.setListener(this);
|
|
((ComplexWaveSource)mControllableFileSource).setListener(mReusableBufferBroadcaster);
|
|
mControllableFileSource.open();
|
|
|
|
if(mSampleRateListener != null)
|
|
{
|
|
mSampleRateListener.setSampleRate(mControllableFileSource.getSampleRate());
|
|
}
|
|
}
|
|
catch(UnsupportedAudioFileException e)
|
|
{
|
|
mLog.error("Unsupported Audio File Type [" + (file != null ? file.getAbsolutePath() : "null") + "]");
|
|
|
|
Alert alert = new Alert(Alert.AlertType.ERROR, "The file type is unsupported.", ButtonType.OK);
|
|
alert.show();
|
|
return;
|
|
}
|
|
catch(IOException ioe)
|
|
{
|
|
mLog.error("Error opening file [" + (file != null ? file.getAbsolutePath() : "null") + "]", ioe);
|
|
|
|
Alert alert = new Alert(Alert.AlertType.ERROR, "There was an error opening the file",
|
|
ButtonType.OK);
|
|
alert.show();
|
|
return;
|
|
}
|
|
|
|
enableControls();
|
|
}
|
|
else if(RealWaveSource.supports(file))
|
|
{
|
|
try
|
|
{
|
|
mControllableFileSource = new RealWaveSource(file);
|
|
mControllableFileSource.setListener(this);
|
|
((RealWaveSource)mControllableFileSource).setListener(mReusableBufferBroadcaster);
|
|
mControllableFileSource.open();
|
|
|
|
if(mSampleRateListener != null)
|
|
{
|
|
mSampleRateListener.setSampleRate(mControllableFileSource.getSampleRate());
|
|
}
|
|
}
|
|
catch(UnsupportedAudioFileException e)
|
|
{
|
|
mLog.error("Unsupported Audio File Type [" + (file != null ? file.getAbsolutePath() : "null") + "]");
|
|
|
|
Alert alert = new Alert(Alert.AlertType.ERROR, "The file type is unsupported.", ButtonType.OK);
|
|
alert.show();
|
|
return;
|
|
}
|
|
catch(IOException ioe)
|
|
{
|
|
mLog.error("Error opening file [" + (file != null ? file.getAbsolutePath() : "null") + "]", ioe);
|
|
|
|
Alert alert = new Alert(Alert.AlertType.ERROR, "There was an error opening the file",
|
|
ButtonType.OK);
|
|
alert.show();
|
|
return;
|
|
}
|
|
|
|
enableControls();
|
|
}
|
|
|
|
getFileLabel().setText(file.getName());
|
|
}
|
|
}
|
|
|
|
public void close()
|
|
{
|
|
if(mControllableFileSource != null)
|
|
{
|
|
try
|
|
{
|
|
mControllableFileSource.close();
|
|
}
|
|
catch(IOException ioe)
|
|
{
|
|
mLog.error("Error closing file", ioe);
|
|
}
|
|
}
|
|
|
|
disableControls();
|
|
}
|
|
|
|
/**
|
|
* Adds listener to receive complex buffers from this playback
|
|
*/
|
|
public void addListener(Listener<ReusableFloatBuffer> listener)
|
|
{
|
|
mReusableBufferBroadcaster.addListener(listener);
|
|
}
|
|
|
|
/**
|
|
* Removes the listener from receiving complex buffers from this playback
|
|
*/
|
|
public void removeListener(Listener<ReusableFloatBuffer> listener)
|
|
{
|
|
mReusableBufferBroadcaster.removeListener(listener);
|
|
}
|
|
|
|
/**
|
|
* Sets all of the playback controls to the specified enabled state.
|
|
*/
|
|
private void disableControls()
|
|
{
|
|
getRewindButton().setDisable(true);
|
|
getPlaybackPositionText().setDisable(true);
|
|
getPlay1Button().setDisable(true);
|
|
getPlay10Button().setDisable(true);
|
|
getPlay100Button().setDisable(true);
|
|
getPlay1000Button().setDisable(true);
|
|
}
|
|
|
|
/**
|
|
* Sets all of the playback controls to the specified enabled state.
|
|
*/
|
|
private void enableControls()
|
|
{
|
|
getRewindButton().setDisable(false);
|
|
getPlaybackPositionText().setDisable(false);
|
|
getPlay1Button().setDisable(false);
|
|
getPlay10Button().setDisable(false);
|
|
getPlay100Button().setDisable(false);
|
|
getPlay1000Button().setDisable(false);
|
|
}
|
|
|
|
private HBox getControlsBox()
|
|
{
|
|
if(mControlsBox == null)
|
|
{
|
|
mControlsBox = new HBox();
|
|
|
|
mControlsBox.getChildren().addAll(getRewindButton(), getPlaybackPositionText(), getPlay1Button(),
|
|
getPlay10Button(), getPlay30Button(), getPlay100Button(), getPlay1000Button(), getPlay2000Button());
|
|
}
|
|
|
|
return mControlsBox;
|
|
}
|
|
|
|
|
|
private Button getRewindButton()
|
|
{
|
|
if(mRewindButton == null)
|
|
{
|
|
mRewindButton = new Button();
|
|
mRewindButton.setGraphic(new FontIcon(FontAwesome.FAST_BACKWARD));
|
|
}
|
|
|
|
return mRewindButton;
|
|
}
|
|
|
|
private Button getPlay1Button()
|
|
{
|
|
if(mPlay1Button == null)
|
|
{
|
|
mPlay1Button = new Button("1");
|
|
mPlay1Button.setUserData(1);
|
|
mPlay1Button.setOnAction(getPlaybackEventHandler());
|
|
mPlay1Button.setGraphic(new FontIcon(FontAwesome.PLAY));
|
|
}
|
|
|
|
return mPlay1Button;
|
|
}
|
|
|
|
private Button getPlay10Button()
|
|
{
|
|
if(mPlay10Button == null)
|
|
{
|
|
mPlay10Button = new Button("10");
|
|
mPlay10Button.setUserData(10);
|
|
mPlay10Button.setOnAction(getPlaybackEventHandler());
|
|
mPlay10Button.setGraphic(new FontIcon(FontAwesome.FAST_FORWARD));
|
|
}
|
|
|
|
return mPlay10Button;
|
|
}
|
|
|
|
private Button getPlay30Button()
|
|
{
|
|
if(mPlay30Button == null)
|
|
{
|
|
mPlay30Button = new Button("30");
|
|
mPlay30Button.setUserData(30);
|
|
mPlay30Button.setOnAction(getPlaybackEventHandler());
|
|
mPlay30Button.setGraphic(new FontIcon(FontAwesome.FAST_FORWARD));
|
|
}
|
|
|
|
return mPlay30Button;
|
|
}
|
|
|
|
private Button getPlay100Button()
|
|
{
|
|
if(mPlay100Button == null)
|
|
{
|
|
mPlay100Button = new Button("100");
|
|
mPlay100Button.setUserData(100);
|
|
mPlay100Button.setOnAction(getPlaybackEventHandler());
|
|
mPlay100Button.setGraphic(new FontIcon(FontAwesome.FAST_FORWARD));
|
|
}
|
|
|
|
return mPlay100Button;
|
|
}
|
|
|
|
private Button getPlay1000Button()
|
|
{
|
|
if(mPlay1000Button == null)
|
|
{
|
|
mPlay1000Button = new Button("1000");
|
|
mPlay1000Button.setUserData(1000);
|
|
mPlay1000Button.setOnAction(getPlaybackEventHandler());
|
|
mPlay1000Button.setGraphic(new FontIcon(FontAwesome.FAST_FORWARD));
|
|
}
|
|
|
|
return mPlay1000Button;
|
|
}
|
|
|
|
private Button getPlay2000Button()
|
|
{
|
|
if(mPlay2000Button == null)
|
|
{
|
|
mPlay2000Button = new Button("2000");
|
|
mPlay2000Button.setUserData(2000);
|
|
mPlay2000Button.setOnAction(getPlaybackEventHandler());
|
|
mPlay2000Button.setGraphic(new FontIcon(FontAwesome.FAST_FORWARD));
|
|
}
|
|
|
|
return mPlay2000Button;
|
|
}
|
|
|
|
private EventHandler<ActionEvent> getPlaybackEventHandler()
|
|
{
|
|
if(mPlayEventHandler == null)
|
|
{
|
|
mPlayEventHandler = new EventHandler<ActionEvent>()
|
|
{
|
|
@Override
|
|
public void handle(ActionEvent event)
|
|
{
|
|
int count = (int)((Button)event.getSource()).getUserData();
|
|
|
|
if(mControllableFileSource != null)
|
|
{
|
|
try
|
|
{
|
|
mControllableFileSource.next(count, true);
|
|
}
|
|
catch(IOException ioe)
|
|
{
|
|
mLog.error("Error while playing samples");
|
|
}
|
|
}
|
|
|
|
}
|
|
};
|
|
}
|
|
|
|
return mPlayEventHandler;
|
|
}
|
|
|
|
private TextField getPlaybackPositionText()
|
|
{
|
|
if(mPlaybackPositionText == null)
|
|
{
|
|
mPlaybackPositionText = new TextField("0");
|
|
mPlaybackPositionText.setAlignment(Pos.CENTER);
|
|
mPlaybackPositionText.setPrefWidth(80);
|
|
}
|
|
|
|
return mPlaybackPositionText;
|
|
}
|
|
|
|
private Label getFileLabel()
|
|
{
|
|
if(mFileLabel == null)
|
|
{
|
|
mFileLabel = new Label();
|
|
}
|
|
|
|
return mFileLabel;
|
|
}
|
|
|
|
@Override
|
|
public void frameLocationUpdated(int location)
|
|
{
|
|
getPlaybackPositionText().setText(String.valueOf(location));
|
|
}
|
|
|
|
@Override
|
|
public void frameLocationReset()
|
|
{
|
|
mLog.info("Frame location reset ... ignoring?");
|
|
}
|
|
}
|