diff --git a/src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java b/src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java index fdae2c9d6..f7c21928c 100644 --- a/src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java +++ b/src/main/java/io/github/dsheirer/audio/AbstractAudioModule.java @@ -90,7 +90,7 @@ protected void closeAudioSegment() { mAudioSegment.completeProperty().set(true); mIdentifierUpdateNotificationBroadcaster.removeListener(mAudioSegment); - mAudioSegment.decrementConsumerCount(); + mAudioSegment.removeLease(getClass().toString()); mAudioSegment = null; } } @@ -99,6 +99,10 @@ protected void closeAudioSegment() @Override public void stop() { + if(getAudioSegment().isDuplicate()) + { + mLog.warn("Audio Module [" + getClass() + "] stop() invoked AND DUPLICATE=TRUE is detected"); + } closeAudioSegment(); } @@ -113,7 +117,7 @@ public AudioSegment getAudioSegment() if(mAudioSegment == null) { mAudioSegment = new AudioSegment(mAliasList, getTimeslot()); - mAudioSegment.incrementConsumerCount(); + mAudioSegment.addLease(getClass().toString()); mAudioSegment.addIdentifiers(mIdentifierCollection.getIdentifiers()); mIdentifierUpdateNotificationBroadcaster.addListener(mAudioSegment); @@ -124,7 +128,6 @@ public AudioSegment getAudioSegment() if(mAudioSegmentListener != null) { - mAudioSegment.incrementConsumerCount(); mAudioSegmentListener.receive(mAudioSegment); } diff --git a/src/main/java/io/github/dsheirer/audio/AudioManager.java b/src/main/java/io/github/dsheirer/audio/AudioManager.java index 09c332e90..755009f1a 100644 --- a/src/main/java/io/github/dsheirer/audio/AudioManager.java +++ b/src/main/java/io/github/dsheirer/audio/AudioManager.java @@ -168,11 +168,15 @@ public void remove(ICallEventListener listener) public void receive(AudioSegment audioSegment) { //Let the duplicate call detector process the segment first to detect duplicates. + audioSegment.addLease(mDuplicateCallDetector.getClass().toString()); mDuplicateCallDetector.receive(audioSegment); + audioSegment.addLease(mProcessor.getClass().toString()); mProcessor.add(audioSegment); - //TODO: redesign audio playback manager to work with Call entities instead of audio segments. + audioSegment.addLease(mAudioPlaybackManager.getClass().toString()); mAudioPlaybackManager.receive(audioSegment); + audioSegment.addLease(mAudioRecordingManager.getClass().toString()); mAudioRecordingManager.receive(audioSegment); + audioSegment.addLease(mAudioStreamingManager.getClass().toString()); mAudioStreamingManager.receive(audioSegment); } @@ -210,7 +214,7 @@ public void shutdown() for(AudioSegment audioSegment: mQueuedAudioSegments) { - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); } mQueuedAudioSegments.clear(); } diff --git a/src/main/java/io/github/dsheirer/audio/DuplicateCallDetector.java b/src/main/java/io/github/dsheirer/audio/DuplicateCallDetector.java index 72d3c04b4..b47750161 100644 --- a/src/main/java/io/github/dsheirer/audio/DuplicateCallDetector.java +++ b/src/main/java/io/github/dsheirer/audio/DuplicateCallDetector.java @@ -96,7 +96,9 @@ public void receive(AudioSegment audioSegment) mDetectorMap.put(system, detector); } + audioSegment.addLease(detector.getClass().toString()); detector.add(audioSegment); + audioSegment.removeLease(getClass().toString()); } } } @@ -256,7 +258,7 @@ private void process() if(complete) { - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); } return complete; @@ -285,7 +287,7 @@ private void process() if(isDuplicate(current, toCheck)) { toCheck.setDuplicate(true); - toCheck.decrementConsumerCount(); + toCheck.removeLease(getClass().toString()); duplicates.add(toCheck); } } diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/AudioStreamingManager.java b/src/main/java/io/github/dsheirer/audio/broadcast/AudioStreamingManager.java index f91076fc1..fc1a235cc 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/AudioStreamingManager.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/AudioStreamingManager.java @@ -94,14 +94,14 @@ public void stop() for(AudioSegment audioSegment: mNewAudioSegments) { - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); } mNewAudioSegments.clear(); for(AudioSegment audioSegment: mAudioSegments) { - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); } mAudioSegments.clear(); @@ -132,7 +132,7 @@ private void processAudioSegments() if(audioSegment.isDuplicate() && mUserPreferences.getDuplicateCallDetectionPreference().isDuplicateStreamingSuppressionEnabled()) { it.remove(); - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); } else if(audioSegment.completeProperty().get()) { @@ -166,7 +166,7 @@ else if(audioSegment.completeProperty().get()) } } - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); } } } diff --git a/src/main/java/io/github/dsheirer/audio/call/ActiveAudioSegment.java b/src/main/java/io/github/dsheirer/audio/call/ActiveAudioSegment.java index 08e2a2868..4b811ccc6 100644 --- a/src/main/java/io/github/dsheirer/audio/call/ActiveAudioSegment.java +++ b/src/main/java/io/github/dsheirer/audio/call/ActiveAudioSegment.java @@ -32,6 +32,8 @@ import io.github.dsheirer.identifier.configuration.SiteConfigurationIdentifier; import io.github.dsheirer.identifier.configuration.SystemConfigurationIdentifier; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Manages an active audio segment through to completion and provides options for refreshing the associated call @@ -39,6 +41,7 @@ */ public class ActiveAudioSegment { + private static final Logger LOGGER = LoggerFactory.getLogger(ActiveAudioSegment.class); private AudioSegment mAudioSegment; private Call mCall; @@ -50,6 +53,7 @@ public class ActiveAudioSegment public ActiveAudioSegment(AudioSegment audioSegment, Call call) { mAudioSegment = audioSegment; + mAudioSegment.addLease(getClass().toString()); mCall = call; } @@ -59,7 +63,7 @@ public ActiveAudioSegment(AudioSegment audioSegment, Call call) public void dispose() { //TODO: perform disposal operations - mAudioSegment.decrementConsumerCount(); + mAudioSegment.removeLease(getClass().toString()); mAudioSegment = null; mCall = null; } @@ -102,6 +106,7 @@ public boolean update() updated |= updateTo(mCall, mAudioSegment.getIdentifierCollection().getToIdentifier(), getAudioSegment().getAliasList()); updated |= updateFrom(mCall, mAudioSegment.getIdentifierCollection().getFromIdentifier(), getAudioSegment().getAliasList()); updated |= updateDuration(mCall, mAudioSegment.getDuration()); + updated |= updateFlags(mCall, mAudioSegment); return updated; } @@ -164,12 +169,50 @@ public static Call create(AudioSegment audioSegment) } updateFrom(call, audioSegment.getIdentifierCollection().getFromIdentifier(), audioSegment.getAliasList()); - updateTo(call, audioSegment.getIdentifierCollection().getFromIdentifier(), audioSegment.getAliasList()); + updateTo(call, audioSegment.getIdentifierCollection().getToIdentifier(), audioSegment.getAliasList()); updateDuration(call, audioSegment.getDuration()); + updateFlags(call, audioSegment); return call; } + /** + * Updates the duplicate, record, stream and other call flags/info + * @param call to update + * @param audioSegment containing flags/info + * @return true if the call is updated. + */ + private static boolean updateFlags(Call call, AudioSegment audioSegment) + { + boolean updated = false; + + if(call.isDuplicate() ^ audioSegment.isDuplicate()) + { + call.setDuplicate(true); + updated = true; + } + + if(call.isRecord() ^ audioSegment.recordAudioProperty().get()) + { + call.setRecord(true); + updated = true; + } + + if(call.isStream() ^ audioSegment.hasBroadcastChannels()) + { + call.setStream(true); + updated = true; + } + + if(call.getMonitor() != audioSegment.monitorPriorityProperty().get()) + { + call.setMonitor(audioSegment.monitorPriorityProperty().get()); + updated = true; + } + + return updated; + } + /** * Updates the call duration field * @param call to update @@ -253,7 +296,11 @@ private static boolean updateTo(Call call, Identifier toIdentifier, AliasList al switch(toIdentifier.getForm()) { case TALKGROUP -> call.setCallType("Talk Group"); - case RADIO -> call.setCallType("Private"); + case RADIO -> + { + LOGGER.info("Private Call detected - TO identifier: " + toIdentifier); + call.setCallType("Private"); + } case PATCH_GROUP -> call.setCallType("Patch Group"); default -> call.setCallType(toIdentifier.getForm().toString()); } diff --git a/src/main/java/io/github/dsheirer/audio/call/AudioSegment.java b/src/main/java/io/github/dsheirer/audio/call/AudioSegment.java index 6bd80eee8..82de461b9 100644 --- a/src/main/java/io/github/dsheirer/audio/call/AudioSegment.java +++ b/src/main/java/io/github/dsheirer/audio/call/AudioSegment.java @@ -29,6 +29,7 @@ import io.github.dsheirer.identifier.MutableIdentifierCollection; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -36,7 +37,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicInteger; import javafx.beans.property.BooleanProperty; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -74,13 +74,13 @@ public class AudioSegment implements Listener private MutableIdentifierCollection mIdentifierCollection = new MutableIdentifierCollection(); private Broadcaster mIdentifierUpdateNotificationBroadcaster = new Broadcaster<>(); private List mAudioBuffers = new CopyOnWriteArrayList(); - private AtomicInteger mConsumerCount = new AtomicInteger(); private AliasList mAliasList; private long mStartTimestamp = System.currentTimeMillis(); private long mSampleCount = 0; private boolean mDisposing = false; private AudioSegment mLinkedAudioSegment; private int mTimeslot; + private List mLeases = new ArrayList<>(); /** * Constructs an instance @@ -94,6 +94,50 @@ public AudioSegment(AliasList aliasList, int timeslot) mIdentifierCollection.setTimeslot(timeslot); } + /** + * Adds a lease to this audio segment. This audio segment will remain alive until all leases are removed. + * @param lease to add + */ + public void addLease(String lease) + { + synchronized(mLeases) + { + if(mLeases.contains(lease)) + { + mLog.warn("Attempt to obtain duplicate lease: " + lease); + } + else + { + mLeases.add(lease); + } + } + } + + /** + * Removes the lease from this audio segment. Once all leases are removed, the audio segment is disposed. + * @param lease to remove + */ + public void removeLease(String lease) + { + synchronized(mLeases) + { + if(mLeases.contains(lease)) + { + mLeases.remove(lease); + } + else + { + mLog.warn("Attempt to remove a non-existent lease: " + lease); + } + + if(mLeases.isEmpty()) + { + mLog.warn("Invoking dispose() on non-leased audio segment - last lease was: " + lease); + dispose(); + } + } + } + /** * Timeslot for this audio segment */ @@ -149,28 +193,6 @@ public boolean isComplete() return mComplete.get(); } - /** - * Duplicate call audio property. This flag is set to true whenever a duplicate call detection function detects - * an audio segment is a duplicate. - */ - public BooleanProperty duplicateProperty() - { - return mDuplicate; - } - - /** - * An observable list of broadcast channels for this audio segment. Broadcast channels are added to this segment - * across the life-cycle of the segment. As each new alias identifier is added to the segment, any broadcast - * channels assigned to the alias are added to this list. The audio segment producer can also add broadcast - * channels to this list. - * - * @return observable set of broadcast channels - */ - public ObservableSet broadcastChannelsProperty() - { - return mBroadcastChannels; - } - /** * Set of broadcast channels from identifier associated aliases for this segment. */ @@ -301,24 +323,6 @@ public int getAudioBufferCount() return mAudioBuffers.size(); } - /** - * Gets the audio buffer at the specified index - * @param index of the buffer to fetch - * @return audio buffer - * @throws IllegalArgumentException if requested index is not valid - */ - public float[] getAudioBuffer(int index) - { - if(0 <= index && index < getAudioBufferCount()) - { - return mAudioBuffers.get(index); - } - else - { - throw new IllegalArgumentException("Requested audio buffer at index [" + index + "] does not exist"); - } - } - /** * Indicates if this audio segment has one or more audio buffers */ @@ -339,34 +343,6 @@ private void dispose() mLinkedAudioSegment = null; } - /** - * Increments the consumer count to indicate that a consumer is currently processing this segment. When the - * consumer count returns to zero, this indicates that all consumers are finished with the audio segment and the - * resources can be reclaimed. - * - * Consumer count should only be increased by the producer of the audio segment, or if a consumer distributes the - * segment to additional consumers. - */ - public void incrementConsumerCount() - { - mConsumerCount.incrementAndGet(); - } - - /** - * Decrements the consumer count. Consumers of this audio segment should invoke this method to signal that they - * will no longer need this audio segment. When all consumers are finished with an audio segment, the audio - * segment resources will be reclaimed. - */ - public void decrementConsumerCount() - { - int count = mConsumerCount.decrementAndGet(); - - if(count <= 0) - { - dispose(); - } - } - /** * Adds an audio buffer to this segment. Note the producer of the audio buffer should increment the user count * of the buffer prior to adding it to this segment. This segment will decrement the audio buffer user count once diff --git a/src/main/java/io/github/dsheirer/audio/call/AudioSegmentBroadcaster.java b/src/main/java/io/github/dsheirer/audio/call/AudioSegmentBroadcaster.java index 7ea56ee2e..4be59b5e5 100644 --- a/src/main/java/io/github/dsheirer/audio/call/AudioSegmentBroadcaster.java +++ b/src/main/java/io/github/dsheirer/audio/call/AudioSegmentBroadcaster.java @@ -41,11 +41,8 @@ public void broadcast(T audioSegment) { for(Listener listener : getListeners()) { - audioSegment.incrementConsumerCount(); + audioSegment.addLease(listener.getClass().toString()); listener.receive(audioSegment); } - - //Decrement consumer counter for this broadcaster - audioSegment.decrementConsumerCount(); } } diff --git a/src/main/java/io/github/dsheirer/audio/call/Call.java b/src/main/java/io/github/dsheirer/audio/call/Call.java index d5096a7ff..42e9eebd9 100644 --- a/src/main/java/io/github/dsheirer/audio/call/Call.java +++ b/src/main/java/io/github/dsheirer/audio/call/Call.java @@ -35,65 +35,64 @@ @Table(name="calls", schema="sdrtrunk") public class Call { - public static final String COLUMN_ID = "id"; - public static final String COLUMN_EVENT_TIME = "eventTime"; - public static final String COLUMN_DURATION = "duration"; public static final String COLUMN_CALL_TYPE = "callType"; - public static final String COLUMN_TO_ID = "toId"; - public static final String COLUMN_TO_ALIAS = "toAlias"; - public static final String COLUMN_FROM_ID = "fromId"; - public static final String COLUMN_FROM_ALIAS = "fromAlias"; + public static final String COLUMN_CHANNEL = "channel"; + public static final String COLUMN_DUPLICATE = "duplicate"; + public static final String COLUMN_DURATION = "duration"; + public static final String COLUMN_EVENT_TIME = "eventTime"; public static final String COLUMN_FILE = "file"; - public static final String COLUMN_PROTOCOL = "protocol"; public static final String COLUMN_FREQUENCY = "frequency"; - public static final String COLUMN_SYSTEM = "system"; + public static final String COLUMN_FROM_ALIAS = "fromAlias"; + public static final String COLUMN_FROM_ID = "fromId"; + public static final String COLUMN_ID = "id"; + public static final String COLUMN_MONITOR = "monitor"; + public static final String COLUMN_PROTOCOL = "protocol"; + public static final String COLUMN_RECORD = "record"; + public static final String COLUMN_STREAM = "stream"; + public static final String COLUMN_TO_ALIAS = "toAlias"; + public static final String COLUMN_TO_ID = "toId"; public static final String COLUMN_SITE = "site"; - public static final String COLUMN_CHANNEL = "channel"; + public static final String COLUMN_SYSTEM = "system"; @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "id_generator") @SequenceGenerator(name = "id_generator", allocationSize = 1) @Column(name=COLUMN_ID) private long mId; - - @Column(name=COLUMN_EVENT_TIME) - private long mEventTime; - @Column(name=COLUMN_CALL_TYPE) private String mCallType; - - @Column(name=COLUMN_FROM_ID) - private String mFromId; - - @Column(name=COLUMN_FROM_ALIAS) - private String mFromAlias; - - @Column(name=COLUMN_TO_ID) - private String mToId; - - @Column(name=COLUMN_TO_ALIAS) - private String mToAlias; - - @Column(name=COLUMN_SYSTEM) - private String mSystem; - - @Column(name=COLUMN_SITE) - private String mSite; - @Column(name=COLUMN_CHANNEL) private String mChannel; - + @Column(name=COLUMN_DUPLICATE) + private boolean mDuplicate; + @Column(name=COLUMN_DURATION) + private double mDuration; + @Column(name=COLUMN_EVENT_TIME) + private long mEventTime; + @Column(name=COLUMN_FILE) + private String mFile; @Column(name=COLUMN_FREQUENCY) private double mFrequency; - + @Column(name=COLUMN_FROM_ALIAS) + private String mFromAlias; + @Column(name=COLUMN_FROM_ID) + private String mFromId; + @Column(name=COLUMN_MONITOR) + private int mMonitor; @Column(name=COLUMN_PROTOCOL) private String mProtocol; - - @Column(name=COLUMN_FILE) - private String mFile; - - @Column(name=COLUMN_DURATION) - private double mDuration; + @Column(name=COLUMN_RECORD) + private boolean mRecord; + @Column(name=COLUMN_SITE) + private String mSite; + @Column(name=COLUMN_STREAM) + private boolean mStream; + @Column(name=COLUMN_SYSTEM) + private String mSystem; + @Column(name=COLUMN_TO_ALIAS) + private String mToAlias; + @Column(name=COLUMN_TO_ID) + private String mToId; @Transient private boolean mComplete = true; @@ -365,5 +364,77 @@ public void setCallType(String callType) { mCallType = callType; } + + /** + * Indicates if this call was flagged as a duplicate + * @return true if duplicate + */ + public boolean isDuplicate() + { + return mDuplicate; + } + + /** + * Sets the duplicate flag for this call. + * @param duplicate flag + */ + public void setDuplicate(boolean duplicate) + { + mDuplicate = duplicate; + } + + /** + * Monitor/listen priority + * @return priority where a -1 value is do not monitor. + */ + public int getMonitor() + { + return mMonitor; + } + + /** + * Sets the monitor/listen priority + * @param monitor priority + */ + public void setMonitor(int monitor) + { + mMonitor = monitor; + } + + /** + * Indicates if the call is recordable/retainable. + * @return is recordable + */ + public boolean isRecord() + { + return mRecord; + } + + /** + * Sets the record/retain flag. + * @param record flag + */ + public void setRecord(boolean record) + { + mRecord = record; + } + + /** + * Indicates if the call is streamable. + * @return streamable + */ + public boolean isStream() + { + return mStream; + } + + /** + * Sets the stream flag + * @param stream flag + */ + public void setStream(boolean stream) + { + mStream = stream; + } } diff --git a/src/main/java/io/github/dsheirer/audio/call/CallViewPanel.java b/src/main/java/io/github/dsheirer/audio/call/CallViewPanel.java index 6123ce8bf..51367d1f4 100644 --- a/src/main/java/io/github/dsheirer/audio/call/CallViewPanel.java +++ b/src/main/java/io/github/dsheirer/audio/call/CallViewPanel.java @@ -19,6 +19,7 @@ package io.github.dsheirer.audio.call; +import io.github.dsheirer.alias.Alias; import io.github.dsheirer.audio.AudioManager; import io.github.dsheirer.gui.control.TimestampTableCellFactory; import io.github.dsheirer.util.ThreadPool; @@ -39,6 +40,7 @@ import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; @@ -47,6 +49,10 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.util.Callback; +import jiconfont.icons.font_awesome.FontAwesome; +import jiconfont.javafx.IconNode; import org.controlsfx.control.textfield.TextFields; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -143,7 +149,6 @@ public void showPage(int page) try { Page callPage = mCallRepository.findAll(PageRequest.of(page - 1, getPageSize(), mSort)); -// Page callPage = mCallRepository.findAll(PageRequest.of(page - 1, getPageSize())); Platform.runLater(() -> { mCalls.clear(); mCalls.addAll(callPage.stream().toList()); @@ -362,9 +367,39 @@ private TableView getCallTableView() channelColumn.setCellValueFactory(new PropertyValueFactory(Call.COLUMN_CHANNEL)); channelColumn.setPrefWidth(80); + TableColumn duplicateColumn = new TableColumn(); + duplicateColumn.setId("mDuplicate"); + duplicateColumn.sortTypeProperty().addListener(mSortOrderListener); + duplicateColumn.setText("Duplicate"); + duplicateColumn.setCellValueFactory(new PropertyValueFactory(Call.COLUMN_DUPLICATE)); + duplicateColumn.setPrefWidth(80); + + TableColumn recordColumn = new TableColumn(); + recordColumn.setId("mRecord"); + recordColumn.sortTypeProperty().addListener(mSortOrderListener); + recordColumn.setText("Record"); + recordColumn.setCellValueFactory(new PropertyValueFactory(Call.COLUMN_RECORD)); + recordColumn.setPrefWidth(80); + + TableColumn streamColumn = new TableColumn(); + streamColumn.setId("mStream"); + streamColumn.sortTypeProperty().addListener(mSortOrderListener); + streamColumn.setText("Stream"); + streamColumn.setCellValueFactory(new PropertyValueFactory(Call.COLUMN_STREAM)); + streamColumn.setPrefWidth(80); + + TableColumn monitorColumn = new TableColumn(); + monitorColumn.setId("mMonitor"); + monitorColumn.sortTypeProperty().addListener(mSortOrderListener); + monitorColumn.setText("Monitor"); + monitorColumn.setCellValueFactory(new PropertyValueFactory(Call.COLUMN_MONITOR)); + monitorColumn.setCellFactory(new PriorityCellFactory()); + monitorColumn.setPrefWidth(80); + mCallTableView.getSortOrder().addListener(mSortOrderListener); mCallTableView.getColumns().addAll(eventTimeColumn, durationColumn, callTypeColumn, toIdColumn, toAliasColumn, - fromIdColumn, fromAliasColumn, protocolColumn, frequencyColumn, systemColumn, siteColumn, channelColumn); + fromIdColumn, fromAliasColumn, protocolColumn, frequencyColumn, systemColumn, siteColumn, channelColumn, + duplicateColumn, recordColumn, streamColumn, monitorColumn); mCallTableView.setPlaceholder(getPlaceholderProgressIndicator()); getPlaceholderProgressIndicator().setVisible(true); } @@ -464,10 +499,10 @@ public void added(Call call) { if(mCalls.size() == getPageSize()) { - mCalls.remove(0); + mCalls.remove(mCalls.size() - 1); } - mCalls.add(call); + mCalls.add(0, call); getCallTableView().sort(); @@ -515,4 +550,51 @@ public void deleted(Call call) }); } } + + public class PriorityCellFactory implements Callback, TableCell> + { + @Override + public TableCell call(TableColumn param) + { + TableCell tableCell = new TableCell() + { + @Override + protected void updateItem(Integer item, boolean empty) + { + if(empty) + { + setText(null); + setGraphic(null); + } + else if(item == io.github.dsheirer.alias.id.priority.Priority.DO_NOT_MONITOR) + { + setText("Mute"); + final IconNode iconNode = new IconNode(FontAwesome.VOLUME_OFF); + iconNode.setIconSize(20); + iconNode.setFill(Color.RED); + setGraphic(iconNode); + } + else if(item == io.github.dsheirer.alias.id.priority.Priority.DEFAULT_PRIORITY) + { + setText("Default"); + final IconNode iconNode = new IconNode(FontAwesome.VOLUME_UP); + iconNode.setIconSize(20); + iconNode.setFill(Color.GREEN); + setGraphic(iconNode); + } + else + { + setText(item.toString()); + final IconNode iconNode = new IconNode(FontAwesome.VOLUME_UP); + iconNode.setIconSize(20); + iconNode.setFill(Color.GREEN); + setGraphic(iconNode); + } + } + }; + + return tableCell; + } + } + } diff --git a/src/main/java/io/github/dsheirer/audio/playback/AudioOutput.java b/src/main/java/io/github/dsheirer/audio/playback/AudioOutput.java index af7b8c148..d0ae75d04 100644 --- a/src/main/java/io/github/dsheirer/audio/playback/AudioOutput.java +++ b/src/main/java/io/github/dsheirer/audio/playback/AudioOutput.java @@ -315,7 +315,7 @@ private void disposeCurrentAudioSegment() { if(mCurrentAudioSegment != null) { - mCurrentAudioSegment.decrementConsumerCount(); + mCurrentAudioSegment.removeLease(getClass().toString()); mCurrentAudioSegment.removeIdentifierUpdateNotificationListener(this); mCurrentAudioSegment = null; broadcast(null); @@ -338,7 +338,7 @@ private void loadNextAudioSegment() //Throw away the audio segment if it has been flagged as do not monitor or is duplicate if(isThrowaway(audioSegment)) { - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); audioSegment = mAudioSegmentQueue.poll(); } else diff --git a/src/main/java/io/github/dsheirer/audio/playback/AudioPlaybackManager.java b/src/main/java/io/github/dsheirer/audio/playback/AudioPlaybackManager.java index c9ac8d917..f34aa753d 100644 --- a/src/main/java/io/github/dsheirer/audio/playback/AudioPlaybackManager.java +++ b/src/main/java/io/github/dsheirer/audio/playback/AudioPlaybackManager.java @@ -135,7 +135,7 @@ private void processAudioSegments() if(newSegment.isDuplicate() && mUserPreferences.getDuplicateCallDetectionPreference().isDuplicatePlaybackSuppressionEnabled()) { - newSegment.decrementConsumerCount(); + newSegment.removeLease(getClass().toString()); } else if(newSegment.hasAudio()) { @@ -164,7 +164,7 @@ else if(newSegment.hasAudio()) mUserPreferences.getDuplicateCallDetectionPreference().isDuplicatePlaybackSuppressionEnabled()) { it.remove(); - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); } else if(audioSegment.hasAudio()) { @@ -176,7 +176,7 @@ else if(audioSegment.completeProperty().get()) { //Rare situation: the audio segment completed but never had audio ... dispose it it.remove(); - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); } } } @@ -198,7 +198,7 @@ else if(audioSegment.completeProperty().get()) mUserPreferences.getDuplicateCallDetectionPreference().isDuplicatePlaybackSuppressionEnabled())) { it.remove(); - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); } else if(audioSegment.isLinked()) { @@ -207,7 +207,9 @@ else if(audioSegment.isLinked()) if(audioOutput.isLinkedTo(audioSegment)) { it.remove(); + audioSegment.addLease(audioOutput.getClass().toString()); audioOutput.play(audioSegment); + audioSegment.removeLease(getClass().toString()); } } } @@ -224,7 +226,10 @@ else if(audioSegment.isLinked()) { if(audioOutput.isEmpty()) { - audioOutput.play(mAudioSegments.remove(0)); + AudioSegment audioSegment1 = mAudioSegments.remove(0); + audioSegment1.addLease(audioOutput.getClass().toString()); + audioOutput.play(audioSegment1); + audioSegment1.removeLease(getClass().toString()); if(mAudioSegments.isEmpty()) { @@ -244,7 +249,7 @@ else if(audioSegment.isLinked()) mUserPreferences.getDuplicateCallDetectionPreference().isDuplicatePlaybackSuppressionEnabled())) { it.remove(); - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); } } } diff --git a/src/main/java/io/github/dsheirer/record/AudioRecordingManager.java b/src/main/java/io/github/dsheirer/record/AudioRecordingManager.java index ca5dd3863..281f90af0 100644 --- a/src/main/java/io/github/dsheirer/record/AudioRecordingManager.java +++ b/src/main/java/io/github/dsheirer/record/AudioRecordingManager.java @@ -114,12 +114,6 @@ public void receive(AudioSegment audioSegment) */ public void processCompletedAudioSegment(AudioSegment audioSegment) { - //Debug - if(audioSegment.getAudioBufferCount() == 0) - { - mLog.debug("Audio Segment detected with 0 audio buffers"); - } - List toIdentifiers = audioSegment.getIdentifierCollection().getIdentifiers(Role.TO); if(toIdentifiers.isEmpty()) @@ -133,7 +127,7 @@ public void processCompletedAudioSegment(AudioSegment audioSegment) } else { - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); } } @@ -149,7 +143,7 @@ private void processAudioSegments() { if(audioSegment.isDuplicate() && mUserPreferences.getDuplicateCallDetectionPreference().isDuplicateRecordingSuppressionEnabled()) { - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); } else { @@ -164,7 +158,7 @@ private void processAudioSegments() mLog.error("Error recording audio segment to [" + path.toString() + "]"); } - audioSegment.decrementConsumerCount(); + audioSegment.removeLease(getClass().toString()); } //Grab the next one to record