diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerFactory.java b/src/main/java/io/github/dsheirer/source/tuner/TunerFactory.java index d297e1f4..7b3010a8 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerFactory.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerFactory.java @@ -147,7 +147,7 @@ public class TunerFactory case RAFAELMICRO_R820T: return new R820TTunerConfiguration(uniqueID); case RECORDING: - return new RecordingTunerConfiguration(uniqueID); + return RecordingTunerConfiguration.create(); default: throw new IllegalArgumentException("Unrecognized tuner type [" + type.name() + "]"); } diff --git a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationManager.java b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationManager.java index d4467166..cd61a9fa 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationManager.java @@ -29,9 +29,6 @@ import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; import io.github.dsheirer.source.tuner.manager.IDiscoveredTunerStatusListener; import io.github.dsheirer.source.tuner.manager.TunerStatus; import io.github.dsheirer.util.ThreadPool; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -42,6 +39,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Manages settings and configurations for all tuner types. @@ -252,7 +251,6 @@ public class TunerConfigurationManager implements IDiscoveredTunerStatusListener */ public void addTunerConfiguration(TunerConfiguration tunerConfiguration) { - mLog.info("Adding tuner config: " + tunerConfiguration); if(!mTunerConfigurations.stream().filter(config -> config.getTunerType().equals(tunerConfiguration.getTunerType()) && config.getUniqueID().equalsIgnoreCase(tunerConfiguration.getUniqueID())).findFirst().isPresent()) { @@ -291,6 +289,17 @@ public class TunerConfigurationManager implements IDiscoveredTunerStatusListener return config; } + /** + * Get all tuner configurations that match the specified tuner type. + * @param tunerType to match + * @return list of all configurations. + */ + public List getTunerConfigurations(TunerType tunerType) + { + return mTunerConfigurations.stream().filter(tunerConfiguration -> tunerConfiguration.getTunerType() + .equals(tunerType)).toList(); + } + /** * Saves the current tuner configuration state and resets the save pending flag */ diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredRecordingTuner.java b/src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredRecordingTuner.java new file mode 100644 index 00000000..172d3d66 --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredRecordingTuner.java @@ -0,0 +1,96 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 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 + * **************************************************************************** + */ + +package io.github.dsheirer.source.tuner.manager; + +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.TunerClass; +import io.github.dsheirer.source.tuner.recording.RecordingTuner; +import io.github.dsheirer.source.tuner.recording.RecordingTunerConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Discovered tuner for recording tuner type. + */ +public class DiscoveredRecordingTuner extends DiscoveredTuner +{ + private static final Logger mLog = LoggerFactory.getLogger(DiscoveredRecordingTuner.class); + private UserPreferences mUserPreferences; + + /** + * Constructs an instance + * @param userPreferences instance + * @param recordingTunerConfiguration with recording path and frequency + */ + public DiscoveredRecordingTuner(UserPreferences userPreferences, + RecordingTunerConfiguration recordingTunerConfiguration) + { + mUserPreferences = userPreferences; + setTunerConfiguration(recordingTunerConfiguration); + + //Default all recordings to be disabled on startup + setEnabled(false); + } + + /** + * Access the tuner configuration as a recording tuner configuration + */ + public RecordingTunerConfiguration getRecordingTunerConfiguration() + { + return (RecordingTunerConfiguration) getTunerConfiguration(); + } + + @Override + public TunerClass getTunerClass() + { + return TunerClass.RECORDING_TUNER; + } + + @Override + public String getId() + { + return getRecordingTunerConfiguration().getPath(); + } + + @Override + public void start() + { + if(!hasTuner()) + { + mTuner = new RecordingTuner(mUserPreferences, this, getRecordingTunerConfiguration()); + + try + { + mTuner.start(); + } + catch(SourceException se) + { + setErrorMessage("Error - " + se.getMessage()); + } + } + } + + @Override + public String toString() + { + return "Recording [" + getRecordingTunerConfiguration().getPath() + "]"; + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/TunerManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/TunerManager.java index 622d38a4..89ea49b3 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/TunerManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/TunerManager.java @@ -35,6 +35,7 @@ import io.github.dsheirer.source.tuner.channel.TunerChannel; import io.github.dsheirer.source.tuner.channel.TunerChannelSource; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationManager; +import io.github.dsheirer.source.tuner.recording.RecordingTunerConfiguration; import io.github.dsheirer.source.tuner.ui.DiscoveredTunerModel; import io.github.dsheirer.util.ThreadPool; import java.nio.ByteBuffer; @@ -89,6 +90,14 @@ public class TunerManager implements IDiscoveredTunerStatusListener return mDiscoveredTunerModel; } + /** + * Tuner configuration manager + */ + public TunerConfigurationManager getTunerConfigurationManager() + { + return mTunerConfigurationManager; + } + /** * Starts the tuner manager and loads all available tuners */ @@ -290,7 +299,26 @@ public class TunerManager implements IDiscoveredTunerStatusListener */ private void discoverRecordingTuners() { - //placeholder + List tunerConfigurations = getTunerConfigurationManager().getTunerConfigurations(TunerType.RECORDING); + + if(tunerConfigurations.size() > 0) + { + mLog.info("Discovered [" + tunerConfigurations.size() + "] recording tuners"); + } + + for(TunerConfiguration tunerConfiguration: tunerConfigurations) + { + if(tunerConfiguration instanceof RecordingTunerConfiguration recordingTunerConfiguration) + { + DiscoveredRecordingTuner discoveredRecordingTuner = + new DiscoveredRecordingTuner(mUserPreferences, recordingTunerConfiguration); + + discoveredRecordingTuner.addTunerStatusListener(this); + discoveredRecordingTuner.setEnabled(false); + mLog.info("Tuner Added: " + discoveredRecordingTuner); + mDiscoveredTunerModel.addDiscoveredTuner(discoveredRecordingTuner); + } + } } /** @@ -315,13 +343,17 @@ public class TunerManager implements IDiscoveredTunerStatusListener { TunerType tunerType = discoveredTuner.getTuner().getTunerType(); - TunerConfiguration tunerConfiguration = mTunerConfigurationManager - .getTunerConfiguration(tunerType, discoveredTuner.getId()); - - if(tunerConfiguration != null) + //Don't fetch or create a configuration for recording tuners + if(tunerType != TunerType.RECORDING) { - discoveredTuner.setTunerConfiguration(tunerConfiguration); - mTunerConfigurationManager.saveConfigurations(); + TunerConfiguration tunerConfiguration = mTunerConfigurationManager + .getTunerConfiguration(tunerType, discoveredTuner.getId()); + + if(tunerConfiguration != null) + { + discoveredTuner.setTunerConfiguration(tunerConfiguration); + mTunerConfigurationManager.saveConfigurations(); + } } } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/recording/AddRecordingTunerDialog.java b/src/main/java/io/github/dsheirer/source/tuner/recording/AddRecordingTunerDialog.java index 5bf5e4f9..cb7f9b7f 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/recording/AddRecordingTunerDialog.java +++ b/src/main/java/io/github/dsheirer/source/tuner/recording/AddRecordingTunerDialog.java @@ -21,7 +21,15 @@ package io.github.dsheirer.source.tuner.recording; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.properties.SystemProperties; +import io.github.dsheirer.source.tuner.configuration.TunerConfigurationManager; +import io.github.dsheirer.source.tuner.manager.DiscoveredRecordingTuner; import io.github.dsheirer.source.tuner.ui.DiscoveredTunerModel; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import net.miginfocom.swing.MigLayout; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; @@ -35,12 +43,6 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.filechooser.FileFilter; -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Dialog to select a recording and specify a center frequency for use in adding a Recording Tuner @@ -52,6 +54,7 @@ public class AddRecordingTunerDialog extends JFrame private static final String LAST_FILE_BROWSE_LOCATION_KEY = "AddRecordingTunerDialog.lastBrowseLocation"; private UserPreferences mUserPreferences; private DiscoveredTunerModel mDiscoveredTunerModel; + private TunerConfigurationManager mTunerConfigurationManager; private JButton mSelectFileButton; private File mSelectedRecording; private JLabel mRecordingFileLabel; @@ -59,92 +62,91 @@ public class AddRecordingTunerDialog extends JFrame private JTextField mFrequencyTextField; private JButton mAddButton; private JButton mCancelButton; - private static final Pattern TUNER_RECORDING_PATTERN = Pattern.compile("TUNER_(\\d*)_baseband_\\d{8}_\\d{6}\\.wav"); + private static final Pattern TUNER_RECORDING_PATTERN = Pattern.compile(".*_(\\d*)_baseband_\\d{8}_\\d{6}\\.wav"); - public AddRecordingTunerDialog(UserPreferences userPreferences, DiscoveredTunerModel discoveredTunerModel) + public AddRecordingTunerDialog(UserPreferences userPreferences, DiscoveredTunerModel discoveredTunerModel, + TunerConfigurationManager tunerConfigurationManager) { Validate.notNull(userPreferences, "UserPreferences cannot be null"); Validate.notNull(discoveredTunerModel, "TunerModel cannot be null"); + Validate.notNull(tunerConfigurationManager, "TunerConfigurationManager cannot be null"); mDiscoveredTunerModel = discoveredTunerModel; mUserPreferences = userPreferences; + mTunerConfigurationManager = tunerConfigurationManager; setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setTitle("Select Recording File"); setSize(new Dimension(500, 250)); JPanel content = new JPanel(); - content.setLayout(new MigLayout("", "[align right][][]", - "[][][grow][]")); + content.setLayout(new MigLayout("", "[align right][][]", "[][][grow][]")); mSelectFileButton = new JButton("Select ..."); - mSelectFileButton.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) + mSelectFileButton.addActionListener(e -> { + JFileChooser fileChooser = new JFileChooser(); + String lastBrowsedDirectory = SystemProperties.getInstance().get(LAST_FILE_BROWSE_LOCATION_KEY, ""); + + File browseDirectory; + + if(lastBrowsedDirectory != null && !lastBrowsedDirectory.isEmpty()) { - JFileChooser fileChooser = new JFileChooser(); - String lastBrowsedDirectory = SystemProperties.getInstance().get(LAST_FILE_BROWSE_LOCATION_KEY, ""); - - File browseDirectory; - - if(lastBrowsedDirectory != null && !lastBrowsedDirectory.isEmpty()) - { - browseDirectory = new File(lastBrowsedDirectory); - } - else - { - browseDirectory = mUserPreferences.getDirectoryPreference().getDefaultRecordingDirectory().toFile(); - } - - fileChooser.setCurrentDirectory(browseDirectory); - fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - fileChooser.setFileFilter(new FileFilter() - { - @Override - public boolean accept(File f) - { - return f.getAbsolutePath().endsWith(".wav") || f.isDirectory(); - } - - @Override - public String getDescription() - { - return "Recordings (*.wav)"; - } - }); - - int returnVal = fileChooser.showOpenDialog(AddRecordingTunerDialog.this); - - File lastDirectory = fileChooser.getCurrentDirectory(); - - if(lastDirectory != null) - { - SystemProperties.getInstance().set(LAST_FILE_BROWSE_LOCATION_KEY, lastDirectory.getAbsolutePath()); - } - - if(returnVal == JFileChooser.APPROVE_OPTION) - { - mSelectedRecording = fileChooser.getSelectedFile(); - - if(mSelectedRecording != null && - (mFrequencyTextField.getText() == null || mFrequencyTextField.getText().isEmpty())) - { - Matcher m = TUNER_RECORDING_PATTERN.matcher(mSelectedRecording.getName()); - - if(m.matches()) - { - mFrequencyTextField.setText(m.group(1)); - } - } - } - else - { - mSelectedRecording = null; - } - - mRecordingFileLabel.setText(mSelectedRecording != null ? mSelectedRecording.getName() : SELECT_A_FILE); + browseDirectory = new File(lastBrowsedDirectory); } + else + { + browseDirectory = mUserPreferences.getDirectoryPreference().getDefaultRecordingDirectory().toFile(); + } + + fileChooser.setCurrentDirectory(browseDirectory); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + fileChooser.setFileFilter(new FileFilter() + { + @Override + public boolean accept(File f) + { + return f.getAbsolutePath().endsWith(".wav") || f.isDirectory(); + } + + @Override + public String getDescription() + { + return "Recordings (*.wav)"; + } + }); + + int returnVal = fileChooser.showOpenDialog(AddRecordingTunerDialog.this); + + File lastDirectory = fileChooser.getCurrentDirectory(); + + if(lastDirectory != null) + { + SystemProperties.getInstance().set(LAST_FILE_BROWSE_LOCATION_KEY, lastDirectory.getAbsolutePath()); + } + + if(returnVal == JFileChooser.APPROVE_OPTION) + { + mSelectedRecording = fileChooser.getSelectedFile(); + + mAddButton.setEnabled(mSelectedRecording != null); + + if(mSelectedRecording != null && + (mFrequencyTextField.getText() == null || mFrequencyTextField.getText().isEmpty())) + { + Matcher m = TUNER_RECORDING_PATTERN.matcher(mSelectedRecording.getName()); + + if(m.matches()) + { + mFrequencyTextField.setText(m.group(1)); + } + } + } + else + { + mSelectedRecording = null; + } + + mRecordingFileLabel.setText(mSelectedRecording != null ? mSelectedRecording.getName() : SELECT_A_FILE); }); content.add(mSelectFileButton); @@ -163,52 +165,47 @@ public class AddRecordingTunerDialog extends JFrame content.add(new JLabel("")); mAddButton = new JButton("Add"); - mAddButton.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) + mAddButton.setEnabled(false); + mAddButton.addActionListener(e -> { + if(mSelectedRecording == null) { - if(mSelectedRecording == null) - { - JOptionPane.showMessageDialog(AddRecordingTunerDialog.this, - "Please select a recording file", - "Select a Recording File", - JOptionPane.ERROR_MESSAGE); - return; - } - - long frequency = getFrequency(); - - if(frequency <= 0 || frequency > Integer.MAX_VALUE) - { - JOptionPane.showMessageDialog(AddRecordingTunerDialog.this, - "Please provide a recording center frequency (1 Hz to 2.14 GHz)", - "Center Frequency Required", - JOptionPane.ERROR_MESSAGE); - return; - } - - mLog.info("Adding recording tuner = frequency [" + frequency + - "] recording [" + mSelectedRecording.getAbsolutePath() + "]"); - - - try - { -// RecordingTuner tuner = new RecordingTuner(mUserPreferences); -// mDiscoveredTunerModel.addDiscoveredTuner(tuner); -// RecordingTunerController controller = tuner.getTunerController(); -// RecordingTunerConfiguration config = new RecordingTunerConfiguration(); -// config.setFrequency(frequency); -// config.setPath(mSelectedRecording.getAbsolutePath()); -// controller.apply(config); - } - catch(Exception ex) - { - mLog.error("Error adding recording tuner", ex); - } - - AddRecordingTunerDialog.this.setVisible(false); + JOptionPane.showMessageDialog(AddRecordingTunerDialog.this, + "Please select a recording file", + "Select a Recording File", + JOptionPane.ERROR_MESSAGE); + return; } + + long frequency = getFrequency(); + + if(frequency <= 0 || frequency > Integer.MAX_VALUE) + { + JOptionPane.showMessageDialog(AddRecordingTunerDialog.this, + "Please provide a recording center frequency (1 Hz to 2.14 GHz)", + "Center Frequency Required", + JOptionPane.ERROR_MESSAGE); + return; + } + + mLog.info("Adding recording tuner = frequency [" + frequency + + "] recording [" + mSelectedRecording.getAbsolutePath() + "]"); + + + try + { + RecordingTunerConfiguration config = RecordingTunerConfiguration.create(); + config.setFrequency(frequency); + config.setPath(mSelectedRecording.getAbsolutePath()); + mTunerConfigurationManager.addTunerConfiguration(config); + DiscoveredRecordingTuner discoveredRecordingTuner = new DiscoveredRecordingTuner(mUserPreferences, config); + mDiscoveredTunerModel.addDiscoveredTuner(discoveredRecordingTuner); + } + catch(Exception ex) + { + mLog.error("Error adding recording tuner", ex); + } + + AddRecordingTunerDialog.this.setVisible(false); }); content.add(mAddButton, "grow,push"); diff --git a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTuner.java b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTuner.java index 48028df5..67dfd30d 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTuner.java @@ -20,7 +20,6 @@ package io.github.dsheirer.source.tuner.recording; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.preference.source.ChannelizerType; -import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.tuner.ITunerErrorListener; import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.TunerClass; @@ -41,9 +40,10 @@ public class RecordingTuner extends Tuner private final int mInstanceID = mInstanceCounter++; private UserPreferences mUserPreferences; - public RecordingTuner(UserPreferences userPreferences, ITunerErrorListener tunerErrorListener) + public RecordingTuner(UserPreferences userPreferences, ITunerErrorListener tunerErrorListener, + RecordingTunerConfiguration config) { - super(new RecordingTunerController(tunerErrorListener), tunerErrorListener); + super(new RecordingTunerController(tunerErrorListener, config.getPath(), config.getFrequency()), tunerErrorListener); mUserPreferences = userPreferences; @@ -73,7 +73,7 @@ public class RecordingTuner extends Tuner @Override public String getPreferredName() { - return "Recording Tuner-" + mInstanceID; + return "Recording Tuner #" + mInstanceID; } /** @@ -108,39 +108,6 @@ public class RecordingTuner extends Tuner return 16.0; } - @Override - public void process(SourceEvent event) - { - super.process(event); - - if(event.getEvent() == SourceEvent.Event.NOTIFICATION_RECORDING_FILE_LOADED) - { - if(getTunerController().getCurrentSampleRate() < 100000.0d) - { - setChannelSourceManager(new PassThroughSourceManager(getTunerController())); - } - else - { - ChannelizerType channelizerType = mUserPreferences.getTunerPreference().getChannelizerType(); - - mLog.debug("Using Channelizer Type: " + channelizerType); - - if(channelizerType == ChannelizerType.POLYPHASE) - { - setChannelSourceManager(new PolyphaseChannelSourceManager(getTunerController())); - } - else if(channelizerType == ChannelizerType.HETERODYNE) - { - setChannelSourceManager(new HeterodyneChannelSourceManager(getTunerController())); - } - else - { - throw new IllegalArgumentException("Unrecognized channelizer type: " + channelizerType); - } - } - } - } - @Override public int getMaximumUSBBitsPerSecond() { diff --git a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerConfiguration.java index 0075836d..a90cb7f0 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerConfiguration.java +++ b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerConfiguration.java @@ -23,17 +23,31 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Recording tuner configuration for using baseband I/Q recordings as a tuner source */ public class RecordingTunerConfiguration extends TunerConfiguration { + private static final Logger mLog = LoggerFactory.getLogger(RecordingTunerConfiguration.class); private String mPath; + /** + * Jackson constructor + */ public RecordingTunerConfiguration() { - //Empty jackson constructor + } + + /** + * Constructs an instance + * @param uniqueId to use for this configuration + */ + public RecordingTunerConfiguration(String uniqueId) + { + setUniqueID(uniqueId); } @JsonIgnore @@ -43,11 +57,6 @@ public class RecordingTunerConfiguration extends TunerConfiguration return TunerType.RECORDING; } - public RecordingTunerConfiguration(String uniqueId) - { - super(uniqueId); - } - @JacksonXmlProperty(isAttribute = true, localName = "path") public String getPath() { @@ -58,4 +67,9 @@ public class RecordingTunerConfiguration extends TunerConfiguration { mPath = path; } + + public static RecordingTunerConfiguration create() + { + return new RecordingTunerConfiguration("Recording " + System.currentTimeMillis()); + } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerController.java index 710d3f59..417a46cf 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerController.java @@ -18,22 +18,22 @@ */ package io.github.dsheirer.source.tuner.recording; -import io.github.dsheirer.buffer.INativeBuffer; -import io.github.dsheirer.sample.Listener; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.SourceException; import io.github.dsheirer.source.tuner.ITunerErrorListener; import io.github.dsheirer.source.tuner.TunerController; import io.github.dsheirer.source.tuner.TunerType; -import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; import io.github.dsheirer.source.wave.ComplexWaveSource; +import java.io.File; +import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.sound.sampled.UnsupportedAudioFileException; -import java.io.File; -import java.io.IOException; +/** + * Tuner controller for playback of baseband complex recording files. + */ public class RecordingTunerController extends TunerController { private final static Logger mLog = LoggerFactory.getLogger(RecordingTunerController.class); @@ -41,16 +41,24 @@ public class RecordingTunerController extends TunerController public static final int DC_NOISE_BANDWIDTH = 0; public static final double USABLE_BANDWIDTH_PERCENTAGE = 1.00; private ComplexWaveSource mComplexWaveSource; + private String mPath; private long mCenterFrequency; private boolean mRunning; /** - * Tuner controller testing implementation. + * Constructs an instance * @param tunerErrorListener to receive errors from this controller */ - public RecordingTunerController(ITunerErrorListener tunerErrorListener) + public RecordingTunerController(ITunerErrorListener tunerErrorListener, String path, long centerFrequency) { super(tunerErrorListener); + mPath = path; + mCenterFrequency = centerFrequency; + if(mCenterFrequency == 0) + { + mCenterFrequency = 100000000; + } + setMinimumFrequency(1000000l); setMaximumFrequency(3000000000l); setMiddleUnusableHalfBandwidth(DC_NOISE_BANDWIDTH); @@ -60,13 +68,65 @@ public class RecordingTunerController extends TunerController @Override public void start() throws SourceException { + if(mComplexWaveSource == null) + { + try + { + mComplexWaveSource = new ComplexWaveSource(new File(mPath), true); + } + catch(IOException ioe) + { + mLog.error("Error", ioe); + setErrorMessage(ioe.getMessage() + " File:" + mPath); + return; + } + mComplexWaveSource.setListener(complexSamples -> broadcast(complexSamples)); + + try + { + mComplexWaveSource.open(); + mComplexWaveSource.start(); + mLog.info("Tuner Recording Loaded: " + mPath); + } + catch(IOException | UnsupportedAudioFileException e) + { + mLog.error("Error", e); + setErrorMessage(e.getMessage() + " File:" + mPath); + return; + } + + try + { + mFrequencyController.setFrequency(mCenterFrequency); + mFrequencyController.setSampleRate((int)mComplexWaveSource.getSampleRate()); + mFrequencyController.broadcast(SourceEvent.recordingFileLoaded()); + } + catch(SourceException e) + { + mLog.error("Error", e); + setErrorMessage(e.getMessage()); + } + } } @Override public void stop() { + if(mComplexWaveSource != null) + { + try + { + mComplexWaveSource.stop(); + mComplexWaveSource.close(); + } + catch(IOException ioe) + { + mLog.error("Ignoring - error stopping baseband recording playback - " + ioe.getLocalizedMessage()); + } + mComplexWaveSource = null; + } } @Override @@ -75,60 +135,6 @@ public class RecordingTunerController extends TunerController return TunerType.RECORDING; } - /** - * Sets the recording file and center frequency for this controller - * @param recordingPath to play - * @param centerFrequency of the recording - * @throws IOException if there are any errors - */ - private void setRecording(String recordingPath, long centerFrequency) throws IOException - { - if(mComplexWaveSource != null) - { - mComplexWaveSource.close(); - mComplexWaveSource = null; - } - - if(recordingPath == null) - { - return; - } - - mComplexWaveSource = new ComplexWaveSource(new File(recordingPath), true); - mComplexWaveSource.setListener(complexSamples -> broadcast(complexSamples)); - - try - { - mComplexWaveSource.open(); - mLog.info("Tuner Recording Loaded: " + recordingPath); - } - catch(UnsupportedAudioFileException e) - { - mLog.error("Unsupported audio format", e); - } - - - mCenterFrequency = centerFrequency; - - mLog.debug("Set recording center frequency to: " + mCenterFrequency); - - if(mCenterFrequency == 0) - { - mCenterFrequency = 100000000; - } - - try - { - mFrequencyController.setFrequency(mCenterFrequency); - mFrequencyController.setSampleRate((int)mComplexWaveSource.getSampleRate()); - mFrequencyController.broadcast(SourceEvent.recordingFileLoaded()); - } - catch(SourceException e) - { - throw new IOException("Can't set frequency or sample rate", e); - } - } - @Override public int getBufferSampleCount() { @@ -140,55 +146,6 @@ public class RecordingTunerController extends TunerController return 0; } - @Override - public void addBufferListener(Listener listener) - { - super.addBufferListener(listener); - - if(mComplexWaveSource != null) - { - if(!mRunning) - { - mComplexWaveSource.start(); - mRunning = true; - } - } - } - - @Override - public void removeBufferListener(Listener listener) - { - super.removeBufferListener(listener); - - if(!mNativeBufferBroadcaster.hasListeners() && mComplexWaveSource != null) - { - mComplexWaveSource.setListener((Listener)null); - mComplexWaveSource.stop(); - mRunning = false; - } - } - - @Override - public void apply(TunerConfiguration config) throws SourceException - { - //Invoke super for frequency, frequency correction and autoPPM - super.apply(config); - - if(config instanceof RecordingTunerConfiguration rtc) - { - mCenterFrequency = rtc.getFrequency(); - - try - { - setRecording(rtc.getPath(), rtc.getFrequency()); - } - catch(IOException ioe) - { - mLog.debug("Error loading recording tuner baseband recording [" + rtc.getPath() + "] Error: " + ioe.getLocalizedMessage()); - } - } - } - @Override public void setFrequency(long frequency) throws SourceException { diff --git a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerEditor.java index 393fb028..655e8ede 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/recording/RecordingTunerEditor.java @@ -19,7 +19,6 @@ package io.github.dsheirer.source.tuner.recording; import io.github.dsheirer.preference.UserPreferences; -import io.github.dsheirer.source.SourceException; import io.github.dsheirer.source.tuner.manager.DiscoveredTuner; import io.github.dsheirer.source.tuner.manager.TunerManager; import io.github.dsheirer.source.tuner.ui.TunerEditor; @@ -28,6 +27,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.JLabel; +import javax.swing.JSeparator; /** * Recording tuner configuration editor @@ -37,9 +37,6 @@ public class RecordingTunerEditor extends TunerEditor mRowSorter; private JideSplitPane mSplitPane; - private DiscoveredTunerEditor mDiscoveredTunerEditor; - private UserPreferences mUserPreferences; + private JButton mAddRecordingButton; + private JButton mRemoveRecordingButton; /** * Constructs an instance @@ -72,6 +79,7 @@ public class TunerViewPanel extends JPanel { mDiscoveredTunerModel = tunerManager.getDiscoveredTunerModel(); mDiscoveredTunerEditor = new DiscoveredTunerEditor(userPreferences, tunerManager); + mTunerConfigurationManager = tunerManager.getTunerConfigurationManager(); mUserPreferences = userPreferences; init(); } @@ -90,6 +98,8 @@ public class TunerViewPanel extends JPanel mTunerTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); mTunerTable.getSelectionModel().addListSelectionListener(event -> { + getRemoveRecordingButton().setEnabled(false); + if(!event.getValueIsAdjusting()) { int row = mTunerTable.getSelectedRow(); @@ -97,7 +107,10 @@ public class TunerViewPanel extends JPanel if(row >= 0) { int modelRow = mTunerTable.convertRowIndexToModel(row); - mDiscoveredTunerEditor.setItem(mDiscoveredTunerModel.getDiscoveredTuner(modelRow)); + + DiscoveredTuner selected = mDiscoveredTunerModel.getDiscoveredTuner(modelRow); + mDiscoveredTunerEditor.setItem(selected); + getRemoveRecordingButton().setEnabled(selected instanceof DiscoveredRecordingTuner); } } }); @@ -138,7 +151,6 @@ public class TunerViewPanel extends JPanel } }); - TableCellRenderer errorCellRenderer = new TunerStatusCellRenderer(); mTunerTable.getColumnModel().getColumn(DiscoveredTunerModel.COLUMN_TUNER_STATUS).setCellRenderer(errorCellRenderer); @@ -146,20 +158,14 @@ public class TunerViewPanel extends JPanel JScrollPane tunerTableScroller = new JScrollPane(mTunerTable); JPanel tunerTablePanel = new JPanel(); - tunerTablePanel.setLayout(new MigLayout("insets 0 0 0 0", "[fill,grow][]", "[fill,grow][]")); + tunerTablePanel.setLayout(new MigLayout("insets 0", "[fill,grow,align center]", "[fill,grow][]")); tunerTablePanel.add(tunerTableScroller, "span"); - tunerTablePanel.add(new JLabel("")); //Empty spacer - -// JButton addRecordingTunerButton = new JButton("Add Recording Tuner"); -// addRecordingTunerButton.addActionListener(e -> -// { -// AddRecordingTunerDialog dialog = new AddRecordingTunerDialog(mUserPreferences, mDiscoveredTunerModel); -// dialog.setLocationRelativeTo(TunerViewPanel.this); -// EventQueue.invokeLater(() -> dialog.setVisible(true)); -// }); -// -// tunerTablePanel.add(addRecordingTunerButton); + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new MigLayout("insets 0 1 3 0", "", "")); + buttonPanel.add(getAddRecordingButton()); + buttonPanel.add(getRemoveRecordingButton()); + tunerTablePanel.add(buttonPanel); tunerTablePanel.setPreferredSize(new Dimension(200,200)); JScrollPane editorScroller = new JScrollPane(mDiscoveredTunerEditor); @@ -169,10 +175,58 @@ public class TunerViewPanel extends JPanel mSplitPane.setOrientation(JideSplitPane.HORIZONTAL_SPLIT); mSplitPane.add(tunerTablePanel); mSplitPane.add(editorScroller); + mSplitPane.setProportionalLayout(true); + mSplitPane.setProportions(new double[]{0.5}); add(mSplitPane); } + private JButton getAddRecordingButton() + { + if(mAddRecordingButton == null) + { + mAddRecordingButton = new JButton("Add Recording Tuner"); + mAddRecordingButton.addActionListener(e -> + { + AddRecordingTunerDialog dialog = new AddRecordingTunerDialog(mUserPreferences, mDiscoveredTunerModel, + mTunerConfigurationManager); + dialog.setLocationRelativeTo(TunerViewPanel.this); + EventQueue.invokeLater(() -> dialog.setVisible(true)); + }); + } + + return mAddRecordingButton; + } + + private JButton getRemoveRecordingButton() + { + if(mRemoveRecordingButton == null) + { + mRemoveRecordingButton = new JButton("Remove Recording Tuner"); + mRemoveRecordingButton.setEnabled(false); + mRemoveRecordingButton.addActionListener(e -> { + int[] indexes = mTunerTable.getSelectionModel().getSelectedIndices(); + + //With single selection mode this should always be length one + if(indexes.length == 1) + { + int modelIndex = mTunerTable.convertRowIndexToModel(indexes[0]); + DiscoveredTuner selected = mDiscoveredTunerModel.getDiscoveredTuner(modelIndex); + + if(selected instanceof DiscoveredRecordingTuner discoveredRecordingTuner) + { + mLog.info("Removing Tuner: " + discoveredRecordingTuner); + discoveredRecordingTuner.stop(); + mTunerConfigurationManager.removeTunerConfiguration(discoveredRecordingTuner.getTunerConfiguration()); + EventQueue.invokeLater(() -> mDiscoveredTunerModel.removeDiscoveredTuner(discoveredRecordingTuner)); + } + } + }); + } + + return mRemoveRecordingButton; + } + /** * Custom cell renderer for the TunerStatus enumeration column */