diff --git a/src/Acquisition/AcquisitionControl.java b/src/Acquisition/AcquisitionControl.java index 8fdd0826..3d198980 100644 --- a/src/Acquisition/AcquisitionControl.java +++ b/src/Acquisition/AcquisitionControl.java @@ -38,6 +38,9 @@ import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.SwingConstants; +import org.pamguard.x3.sud.Chunk; +import org.pamguard.x3.sud.SudFileListener; + import dataGram.DatagramManager; import dataMap.OfflineDataMapPoint; import dataMap.filemaps.OfflineFileServer; @@ -49,12 +52,12 @@ import simulatedAcquisition.SimProcess; import asiojni.ASIOSoundSystem; import asiojni.NewAsioSoundSystem; import nidaqdev.NIDAQProcess; -import nidaqdev.networkdaq.NINetworkDaq; import Acquisition.filedate.FileDate; import Acquisition.filedate.StandardFileDate; import Acquisition.layoutFX.AquisitionGUIFX; import Acquisition.offlineFuncs.OfflineWavFileServer; import Acquisition.rona.RonaOfflineFileServer; +import Acquisition.sud.SUDNotificationManager; import Array.ArrayManager; import Array.PamArray; import Array.Preamplifier; @@ -132,7 +135,8 @@ public class AcquisitionControl extends PamControlledUnit implements PamSettings * The JavaFX GUI for the sound acquisition module. */ private AquisitionGUIFX aquisitionGUIFX; - + + private SUDNotificationManager sudNotificationManager; /** * Main control unit for audio data acquisition. @@ -833,5 +837,16 @@ public class AcquisitionControl extends PamControlledUnit implements PamSettings public String getModuleSummary(boolean clear) { return getDaqProcess().getRawDataBlock().getSummaryString(clear); } - + + /** + * Get the SUD processing notification manager. + * @return SUD processing notification manager. + */ + public SUDNotificationManager getSUDNotificationManager() { + if (sudNotificationManager == null) { + sudNotificationManager = new SUDNotificationManager(); + } + return sudNotificationManager; + } + } diff --git a/src/Acquisition/FileInputSystem.java b/src/Acquisition/FileInputSystem.java index 771af349..9ec48538 100644 --- a/src/Acquisition/FileInputSystem.java +++ b/src/Acquisition/FileInputSystem.java @@ -7,6 +7,8 @@ import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -31,6 +33,10 @@ import javax.swing.border.TitledBorder; import org.jflac.FLACDecoder; import org.jflac.frame.Frame; import org.jflac.util.ByteData; +import org.pamguard.x3.sud.Chunk; +import org.pamguard.x3.sud.SudAudioInputStream; +import org.pamguard.x3.sud.SudFileListener; +import org.pamguard.x3.sud.SudParams; import org.jflac.PCMProcessor; import org.jflac.metadata.StreamInfo; import org.jflac.sound.spi.FlacEncoding; @@ -49,13 +55,11 @@ import Acquisition.filedate.FileDateDialogStrip; import Acquisition.filedate.FileDateObserver; import Acquisition.pamAudio.PamAudioFileManager; import Acquisition.pamAudio.PamAudioFileFilter; -import Acquisition.pamAudio.PamAudioSystem; import PamController.PamControlledUnitSettings; import PamController.PamController; import PamController.PamSettingManager; import PamController.PamSettings; import PamDetection.RawDataUnit; -import PamModel.SMRUEnable; import PamUtils.PamCalendar; import PamUtils.PamFileChooser; import PamView.dialog.PamLabel; @@ -151,6 +155,9 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe */ private PamWarning fileWarning; + private SudAudioInputStream sudAudioInputStream; + + private SudListener sudListener; public FileInputSystem(AcquisitionControl acquisitionControl) { this.acquisitionControl = acquisitionControl; @@ -177,7 +184,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe } return daqDialog; } - + protected JPanel createDaqDialogPanel() { JPanel p = new JPanel(); @@ -216,18 +223,18 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe fileDateStrip.addObserver(this); p.add(fileDateStrip.getDialogComponent(), constraints); -// if (SMRUEnable.isEnable()) { + // if (SMRUEnable.isEnable()) { // no reason to hide this option from users. - constraints.gridy++; - constraints.gridx = 0; - constraints.gridwidth = 1; - addComponent(p, new JLabel("Skip initial"), constraints); - constraints.gridx++; - addComponent(p, skipSecondsField = new JTextField(4), constraints); - constraints.gridx++; - addComponent(p, new JLabel("seconds"), constraints); - constraints.anchor = GridBagConstraints.EAST; -// } + constraints.gridy++; + constraints.gridx = 0; + constraints.gridwidth = 1; + addComponent(p, new JLabel("Skip initial"), constraints); + constraints.gridx++; + addComponent(p, skipSecondsField = new JTextField(4), constraints); + constraints.gridx++; + addComponent(p, new JLabel("seconds"), constraints); + constraints.anchor = GridBagConstraints.EAST; + // } // addComponent(p, new JLabel("File date :"), constraints); // constraints.gridx++; @@ -251,7 +258,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe // would hold a null. The system type is used by the getParameterSet method to decide // whether or not to include the parameters in the XML output if (fileInputParameters.systemType==null) fileInputParameters.systemType=getSystemType(); - + fillFileList(); if (repeat != null) { @@ -289,7 +296,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe fileInputParameters.recentFiles.trimToSize(); } } - + if (repeat == null) { fileInputParameters.repeatLoop = false; } @@ -306,7 +313,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe return false; } } - + return true; } @@ -337,7 +344,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe protected void selectFile() { //IshmaelDetector.MatchFiltParamsDialog copies a bunch of this. If you //modifiy this, please check that too. - + String currFile = (String) fileNameCombo.getSelectedItem(); // seems to just support aif and wav files at the moment // Type[] audioTypes = AudioSystem.getAudioFileTypes(); @@ -421,6 +428,10 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe // } // } + if (audioStream instanceof SudAudioInputStream) { + acquisitionControl.getSUDNotificationManager().interpretNewFile(newFile, (SudAudioInputStream) audioStream); + } + AudioFormat audioFormat = audioStream.getFormat(); // fileLength = file.length(); fileSamples = audioStream.getFrameLength(); @@ -477,7 +488,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe public long getSkipStartFileTime() { return fileInputParameters.skipStartFileTime; } - + @Override public boolean canPlayBack(float sampleRate) { // TODO Auto-generated method stub @@ -538,7 +549,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe } public File getCurrentFile() { - System.out.println("fileInputParameters: " + fileInputParameters); +// System.out.println("fileInputParameters: " + fileInputParameters); if (fileInputParameters.recentFiles == null) return null; if (fileInputParameters.recentFiles.size() < 1) return null; String fileName = fileInputParameters.recentFiles.get(0); @@ -575,12 +586,25 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe audioStream = PamAudioFileManager.getInstance().getAudioInputStream(currentFile); + if (audioStream instanceof SudAudioInputStream) { + sudAudioInputStream = (SudAudioInputStream) audioStream; + if (sudListener == null) { + sudListener = new SudListener(); + } + sudAudioInputStream.addSudFileListener(sudListener); +// sudAudioInputStream.ad + acquisitionControl.getSUDNotificationManager().newSudInputStream(sudAudioInputStream); + } + else { + sudAudioInputStream = null; + } + if (audioStream == null) { return false; } audioFormat = audioStream.getFormat(); - + if (audioFormat==null) { System.err.println("AudioFormat was null: " + currentFile.getAbsolutePath()); return false; @@ -592,7 +616,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe acquisitionControl.getAcquisitionProcess().setSampleRate(audioFormat.getSampleRate(), true); fileInputParameters.bitDepth = audioFormat.getSampleSizeInBits(); - + loadByteConverter(audioFormat); } catch (UnsupportedAudioFileException ex) { @@ -608,18 +632,27 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe return true; } + private class SudListener implements SudFileListener { + + @Override + public void chunkProcessed(int chunkID, Chunk sudChunk) { + acquisitionControl.getSUDNotificationManager().chunkProcessed(chunkID, sudChunk); + } + + } + public boolean runFileAnalysis() { // keep a reference to where data will be put. this.newDataUnits = acquisitionControl.getDaqProcess().getNewDataQueue(); - -// if (this.newDataUnits == null) { -// System.err.println("newDataUnits: == null: "); -// return false; -// } + + // if (this.newDataUnits == null) { + // System.err.println("newDataUnits: == null: "); + // return false; + // } if (!prepareInputFile() && getCurrentFile()!=null) { - + String audioFileStr = getCurrentFile()==null? "Null File": getCurrentFile().getAbsolutePath(); String title = "Error loading audio file"; String msg = "
There was an error trying to access the audio file
" + @@ -647,14 +680,14 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe this.startTimeMS = PamCalendar.getTimeInMillis(); if (audioFormat==null) { - + String audioFileStr = getCurrentFile()==null? "Null File": getCurrentFile().getAbsolutePath(); System.err.println("FileInputSystem: runFileAnalysis: AudioFile format is null: " + audioFileStr); - + return false; } - + nChannels = audioFormat.getChannels(); acquisitionControl.getDaqProcess().setSampleRate(sampleRate = audioFormat.getSampleRate(), true); @@ -997,6 +1030,9 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe } } if (audioStream != null) { + if (audioStream instanceof SudAudioInputStream) { + acquisitionControl.getSUDNotificationManager().sudStreamClosed(); + } try { audioStream.close(); audioStream = null; @@ -1124,7 +1160,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe } public void sayEta(long timeMs) { - + if (etaLabel==null) return; if (timeMs < 0) { diff --git a/src/Acquisition/filedate/StandardFileDate.java b/src/Acquisition/filedate/StandardFileDate.java index 8fa56de6..aa87e973 100644 --- a/src/Acquisition/filedate/StandardFileDate.java +++ b/src/Acquisition/filedate/StandardFileDate.java @@ -13,6 +13,7 @@ import java.util.TimeZone; import Acquisition.KETime; import Acquisition.layoutFX.FileDatePane; import Acquisition.layoutFX.StandardFileDatePane; +import Acquisition.sud.SUDFileTime; import PamController.PamControlledUnitSettings; import PamController.PamSettingManager; import PamController.PamSettings; @@ -157,6 +158,11 @@ public class StandardFileDate implements FileDate, PamSettings { if (settings.isUseBespokeFormat() && settings.getForcedDateFormat() != null) { return forcedDataFormat(file, settings.getForcedDateFormat()); } + + long sudTime = SUDFileTime.getSUDFileTime(file); + if (sudTime != Long.MIN_VALUE) { + return sudTime; + } /* * Dtag files have an accompanying XML file which @@ -168,6 +174,7 @@ public class StandardFileDate implements FileDate, PamSettings { setLastFormat("D3 time from xml file"); return dTagTime; } + long stTime = SoundTrapTime.getSoundTrapTime(file, settings.getDateTimeFormatToUse()); if (stTime != Long.MIN_VALUE) { diff --git a/src/Acquisition/sud/SUDFileTime.java b/src/Acquisition/sud/SUDFileTime.java new file mode 100644 index 00000000..b5e7ca2d --- /dev/null +++ b/src/Acquisition/sud/SUDFileTime.java @@ -0,0 +1,86 @@ +package Acquisition.sud; + +import java.io.File; + +import org.pamguard.x3.sud.ChunkHeader; +import org.pamguard.x3.sud.SudAudioInputStream; +import org.pamguard.x3.sud.SudFileMap; +import org.pamguard.x3.sud.SudParams; + +import PamUtils.PamCalendar; + +public class SUDFileTime { + + private static long sudTime; + + private static String lastFilePath = ""; + /** + * Temp measure to get the time from the first available sud record. + * @param file + * @return + */ + public static long getSUDFileTime(File file) { + if (file == null || file.exists() == false) { + return Long.MIN_VALUE; + } + if (file.getName().toLowerCase().endsWith(".sud") == false) { + return Long.MIN_VALUE; + } + String filePath = file.getAbsolutePath(); + if (filePath.equals(lastFilePath)) { + return sudTime; + } + /** + * Open the sud file and read it until the first chunk arrive, get the time + * from there and close it again. I don't really see another way. + */ + long t1 = System.currentTimeMillis(); + sudTime = Long.MIN_VALUE; + SudParams sudParams = new SudParams(); + sudParams.saveMeta = false; + sudParams.saveWav = false; + try { + SudAudioInputStream sudAudioInputStream = SudAudioInputStream.openInputStream(file, sudParams, false); + if (sudAudioInputStream == null) { + return Long.MIN_VALUE; + } + SudFileMap sudMap = sudAudioInputStream.getSudMap(); + if (sudMap == null) { + return Long.MIN_VALUE; + } + long t = sudMap.getFirstChunkTime(); + if (t != 0) { + sudTime = t; + } +// sudAudioInputStream.addSudFileListener((chunkID, sudChunk)->{ +// ChunkHeader chunkHead = sudChunk.chunkHeader; +// if (chunkHead == null || sudTime != Long.MIN_VALUE) { +// return; +// } +// long millis = (long) chunkHead.TimeS*1000 + (long) chunkHead.TimeOffsetUs/1000; +// if (millis > 0) { +// sudTime = millis; +// lastFilePath = filePath; +// } +// }); +// +// while (sudAudioInputStream.available() > 0 && sudTime == Long.MIN_VALUE) { +// +// //note this is reading bytes of uncompressed continuous recordings only. +// sudAudioInputStream.read(); +// } +// + sudAudioInputStream.close(); + long t2 = System.currentTimeMillis(); + System.out.printf("SUD file time %s extracted in %d milliseconds\n", PamCalendar.formatDBDateTime(sudTime), t2-t1); + + } catch (Exception e) { + System.err.println("Error getting time from SUD file: " + e.getMessage()); + } + + return sudTime; + } + + + +} diff --git a/src/Acquisition/sud/SUDNotificationHandler.java b/src/Acquisition/sud/SUDNotificationHandler.java new file mode 100644 index 00000000..ed5ed429 --- /dev/null +++ b/src/Acquisition/sud/SUDNotificationHandler.java @@ -0,0 +1,36 @@ +package Acquisition.sud; + +import org.pamguard.x3.sud.Chunk; +import org.pamguard.x3.sud.SudAudioInputStream; +import org.pamguard.x3.sud.SudFileListener; +import org.pamguard.x3.sud.SudProgressListener; + +public interface SUDNotificationHandler extends SudFileListener, SudProgressListener { + + /** + * A new SUD file input stream has been opened. + * @param sudAudioInputStream + */ + public void newSudInputStream(SudAudioInputStream sudAudioInputStream); + + /** + * SUD stream has closed. + */ + public void sudStreamClosed(); + + @Override + public void progress(double arg0, int arg1, int arg2); + + @Override + public void chunkProcessed(int chunkId, Chunk sudChunk); + + /** + * Notification that a new file or folder is selected. This is called when a file + * or folder is selected in the dialog, NOT when acquisition starts, so is a good + * opportunity for the SUD Click Detector to work out channel maps and sample rates. + * @param newFile + * @param sudAudioStream + */ + public void interpretNewFile(String newFile, SudAudioInputStream sudAudioStream); + +} diff --git a/src/Acquisition/sud/SUDNotificationManager.java b/src/Acquisition/sud/SUDNotificationManager.java new file mode 100644 index 00000000..28bed217 --- /dev/null +++ b/src/Acquisition/sud/SUDNotificationManager.java @@ -0,0 +1,66 @@ +package Acquisition.sud; + +import java.util.ArrayList; + +import org.pamguard.x3.sud.Chunk; +import org.pamguard.x3.sud.SudAudioInputStream; +import org.pamguard.x3.sud.SudFileListener; +import org.pamguard.x3.sud.SudProgressListener; + +/** + * Class to handle appropriate notifications for SUD files, which go a bit + * beyond what's handled in the chunk notifications. + * @author dg50 + * + */ +public class SUDNotificationManager implements SUDNotificationHandler { + + private ArrayList