diff --git a/.idea/dictionaries/denny.xml b/.idea/dictionaries/denny.xml index 408050c7..24e59925 100644 --- a/.idea/dictionaries/denny.xml +++ b/.idea/dictionaries/denny.xml @@ -8,6 +8,7 @@ icecast ident polyphase + remez samplerate sdrtrunk sheirer diff --git a/src/alias/AliasList.java b/src/alias/AliasList.java index cc1c4662..10c3e214 100644 --- a/src/alias/AliasList.java +++ b/src/alias/AliasList.java @@ -1,6 +1,6 @@ /******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer + * sdrtrunk + * Copyright (C) 2014-2017 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 @@ -14,6 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see + * ******************************************************************************/ package alias; @@ -29,7 +30,6 @@ import alias.id.siteID.SiteID; import alias.id.status.StatusID; import alias.id.talkgroup.TalkgroupID; import alias.id.uniqueID.UniqueID; -import audio.metadata.MetadataType; import module.decode.lj1200.LJ1200Message.Function; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,16 +48,16 @@ public class AliasList implements Listener public static final String WILDCARD = "*"; public static final String REGEX_WILDCARD = "."; - private Map mESN = new HashMap<>(); - private Map mFleetsync = new HashMap<>(); - private Map mLoJack = new HashMap<>(); - private Map mMDC1200 = new HashMap<>(); - private Map mMobileID = new HashMap<>(); - private Map mMPT1327 = new HashMap<>(); - private Map mSiteID = new HashMap<>(); - private Map mStatus = new HashMap<>(); - private Map mTalkgroup = new HashMap<>(); - private Map mUniqueID = new HashMap<>(); + private Map mESN = new HashMap<>(); + private Map mFleetsync = new HashMap<>(); + private Map mLoJack = new HashMap<>(); + private Map mMDC1200 = new HashMap<>(); + private Map mMobileID = new HashMap<>(); + private Map mMPT1327 = new HashMap<>(); + private Map mSiteID = new HashMap<>(); + private Map mStatus = new HashMap<>(); + private Map mTalkgroup = new HashMap<>(); + private Map mUniqueID = new HashMap<>(); private List mESNWildcards = new ArrayList<>(); private List mMobileIDWildcards = new ArrayList<>(); @@ -85,9 +85,9 @@ public class AliasList implements Listener */ public void addAlias(Alias alias) { - if (alias != null) + if(alias != null) { - for (AliasID aliasID : alias.getId()) + for(AliasID aliasID : alias.getId()) { addAliasID(aliasID, alias); } @@ -99,18 +99,18 @@ public class AliasList implements Listener */ private void addAliasID(AliasID id, Alias alias) { - if (id.isValid()) + if(id.isValid()) { try { - switch (id.getType()) + switch(id.getType()) { case ESN: - String esn = ((Esn)id).getEsn(); + String esn = ((Esn) id).getEsn(); if(esn != null) { - if (esn.contains(WILDCARD)) + if(esn.contains(WILDCARD)) { mESNWildcards.add(new WildcardID(esn)); Collections.sort(mESNWildcards); @@ -120,11 +120,11 @@ public class AliasList implements Listener } break; case FLEETSYNC: - String fleetsync = ((FleetsyncID)id).getIdent(); + String fleetsync = ((FleetsyncID) id).getIdent(); if(fleetsync != null) { - if (fleetsync.contains(WILDCARD)) + if(fleetsync.contains(WILDCARD)) { mFleetsyncWildcards.add(new WildcardID(fleetsync)); Collections.sort(mFleetsyncWildcards); @@ -137,11 +137,11 @@ public class AliasList implements Listener mLoJack.put((LoJackFunctionAndID) id, alias); break; case MDC1200: - String mdc = ((MDC1200ID)id).getIdent(); + String mdc = ((MDC1200ID) id).getIdent(); if(mdc != null) { - if (mdc.contains(WILDCARD)) + if(mdc.contains(WILDCARD)) { mMDC1200Wildcards.add(new WildcardID(mdc)); Collections.sort(mMDC1200Wildcards); @@ -151,11 +151,11 @@ public class AliasList implements Listener } break; case MPT1327: - String mpt = ((MPT1327ID)id).getIdent(); + String mpt = ((MPT1327ID) id).getIdent(); if(mpt != null) { - if (mpt.contains(WILDCARD)) + if(mpt.contains(WILDCARD)) { mMPT1327Wildcards.add(new WildcardID(mpt)); Collections.sort(mMPT1327Wildcards); @@ -165,11 +165,11 @@ public class AliasList implements Listener } break; case MIN: - String min = ((Min)id).getMin(); + String min = ((Min) id).getMin(); if(min != null) { - if (min.contains(WILDCARD)) + if(min.contains(WILDCARD)) { mMobileIDWildcards.add(new WildcardID(min)); Collections.sort(mMobileIDWildcards); @@ -179,14 +179,14 @@ public class AliasList implements Listener } break; case LTR_NET_UID: - mUniqueID.put(((UniqueID)id).getUid(), alias); + mUniqueID.put(((UniqueID) id).getUid(), alias); break; case SITE: - String siteID = ((SiteID)id).getSite(); + String siteID = ((SiteID) id).getSite(); if(siteID != null) { - if (siteID.contains(WILDCARD)) + if(siteID.contains(WILDCARD)) { mSiteWildcards.add(new WildcardID(siteID)); Collections.sort(mSiteWildcards); @@ -199,11 +199,11 @@ public class AliasList implements Listener mStatus.put(((StatusID) id).getStatus(), alias); break; case TALKGROUP: - String tgid = ((TalkgroupID)id).getTalkgroup(); + String tgid = ((TalkgroupID) id).getTalkgroup(); if(tgid != null) { - if (tgid.contains(WILDCARD)) + if(tgid.contains(WILDCARD)) { mTalkgroupWildcards.add(new WildcardID(tgid)); Collections.sort(mTalkgroupWildcards); @@ -222,7 +222,7 @@ public class AliasList implements Listener break; } } - catch (Exception e) + catch(Exception e) { mLog.error("Couldn't add alias ID " + id + " for alias " + alias); } @@ -234,9 +234,9 @@ public class AliasList implements Listener */ public void removeAlias(Alias alias) { - if (alias != null) + if(alias != null) { - for (AliasID aliasID : alias.getId()) + for(AliasID aliasID : alias.getId()) { removeAliasID(aliasID, alias); } @@ -247,7 +247,7 @@ public class AliasList implements Listener { if(value != null) { - for(WildcardID wildcardID: wildcards) + for(WildcardID wildcardID : wildcards) { if(wildcardID.value().equals(value)) { @@ -262,12 +262,12 @@ public class AliasList implements Listener */ private void removeAliasID(AliasID id, Alias alias) { - if (id.isValid()) + if(id.isValid()) { - switch (id.getType()) + switch(id.getType()) { case ESN: - String esn = ((Esn)id).getEsn(); + String esn = ((Esn) id).getEsn(); if(esn != null) { @@ -280,13 +280,13 @@ public class AliasList implements Listener mESN.remove(esn); break; case FLEETSYNC: - String fleetsync = ((FleetsyncID)id).getIdent(); + String fleetsync = ((FleetsyncID) id).getIdent(); if(fleetsync != null) { if(fleetsync.contains(WILDCARD)) { - removeWildcard(fleetsync,mFleetsyncWildcards); + removeWildcard(fleetsync, mFleetsyncWildcards); } mFleetsync.remove(fleetsync); @@ -296,7 +296,7 @@ public class AliasList implements Listener mLoJack.remove((LoJackFunctionAndID) id); break; case MDC1200: - String mdc = ((MDC1200ID)id).getIdent(); + String mdc = ((MDC1200ID) id).getIdent(); if(mdc != null) { @@ -309,7 +309,7 @@ public class AliasList implements Listener } break; case MPT1327: - String mpt = ((MPT1327ID)id).getIdent(); + String mpt = ((MPT1327ID) id).getIdent(); if(mpt != null) { @@ -322,7 +322,7 @@ public class AliasList implements Listener } break; case MIN: - String min = ((Min)id).getMin(); + String min = ((Min) id).getMin(); if(min != null) { @@ -344,7 +344,7 @@ public class AliasList implements Listener mStatus.remove(((StatusID) id).getStatus()); break; case TALKGROUP: - String tgid = ((TalkgroupID)id).getTalkgroup(); + String tgid = ((TalkgroupID) id).getTalkgroup(); if(tgid != null) { @@ -378,11 +378,11 @@ public class AliasList implements Listener */ private String getWildcardMatch(String id, List wildcards) { - if (id != null) + if(id != null) { - for (WildcardID wildcard : wildcards) + for(WildcardID wildcard : wildcards) { - if (wildcard.matches(id)) + if(wildcard.matches(id)) { return wildcard.value(); } @@ -399,15 +399,15 @@ public class AliasList implements Listener { Alias alias = null; - if (siteID != null) + if(siteID != null) { alias = mSiteID.get(siteID); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(siteID, mSiteWildcards); - if (wildcard != null) + if(wildcard != null) { alias = mSiteID.get(wildcard); } @@ -440,15 +440,15 @@ public class AliasList implements Listener { Alias alias = null; - if (esn != null) + if(esn != null) { alias = mESN.get(esn); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(esn, mESNWildcards); - if (wildcard != null) + if(wildcard != null) { alias = mESN.get(wildcard); } @@ -465,15 +465,15 @@ public class AliasList implements Listener { Alias alias = null; - if (ident != null) + if(ident != null) { alias = mFleetsync.get(ident); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(ident, mFleetsyncWildcards); - if (wildcard != null) + if(wildcard != null) { alias = mFleetsync.get(wildcard); } @@ -488,11 +488,11 @@ public class AliasList implements Listener */ public Alias getLoJackAlias(Function function, String id) { - if (id != null) + if(id != null) { - for (LoJackFunctionAndID lojack : mLoJack.keySet()) + for(LoJackFunctionAndID lojack : mLoJack.keySet()) { - if (lojack.matches(function, id)) + if(lojack.matches(function, id)) { return mLoJack.get(lojack); } @@ -509,15 +509,15 @@ public class AliasList implements Listener { Alias alias = null; - if (ident != null) + if(ident != null) { alias = mMDC1200.get(ident); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(ident, mMDC1200Wildcards); - if (wildcard != null) + if(wildcard != null) { alias = mMDC1200.get(wildcard); } @@ -534,15 +534,15 @@ public class AliasList implements Listener { Alias alias = null; - if (ident != null) + if(ident != null) { alias = mMPT1327.get(ident); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(ident, mMPT1327Wildcards); - if (wildcard != null) + if(wildcard != null) { alias = mMPT1327.get(wildcard); } @@ -559,15 +559,15 @@ public class AliasList implements Listener { Alias alias = null; - if (ident != null) + if(ident != null) { alias = mMobileID.get(ident); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(ident, mMobileIDWildcards); - if (wildcard != null) + if(wildcard != null) { alias = mMobileID.get(wildcard); } @@ -584,15 +584,15 @@ public class AliasList implements Listener { Alias alias = null; - if (tgid != null) + if(tgid != null) { alias = mTalkgroup.get(tgid); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(tgid, mTalkgroupWildcards); - if (wildcard != null) + if(wildcard != null) { alias = mTalkgroup.get(wildcard); } @@ -619,61 +619,6 @@ public class AliasList implements Listener return mName; } - /** - * Lookup an alias by metadata type and string value - */ - public Alias getAlias(String value, MetadataType type) - { - switch (type) - { - case ESN: - return getESNAlias(value); - case FLEETSYNC: - return getFleetsyncAlias(value); - case MDC1200: - return getMDC1200Alias(value); - case MOBILE_ID: - return getMobileIDNumberAlias(value); - case MPT1327: - return getMPT1327Alias(value); - case SITE_ID: - return getSiteID(value); - case STATUS: - if (value != null) - { - try - { - return getStatus(Integer.valueOf(value)); - } - catch (Exception e) - { - //do nothing, we couldn't parse the int value - } - } - break; - case FROM: - case TO: - return getTalkgroupAlias(value); - case UNIQUE_ID: - if (value != null) - { - try - { - return getUniqueID(Integer.valueOf(value)); - } - catch (Exception e) - { - //do nothing, we couldn't parse the int value - } - } - break; - default: - break; - } - - return null; - } - /** * Receive alias change event notifications and modify this list accordingly */ @@ -682,22 +627,22 @@ public class AliasList implements Listener { Alias alias = event.getAlias(); - switch (event.getEvent()) + switch(event.getEvent()) { case ADD: - if (alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) + if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) { addAlias(alias); } break; case CHANGE: - if (alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) + if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) { addAlias(alias); } break; case DELETE: - if (alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) + if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) { removeAlias(alias); } diff --git a/src/audio/AudioChannelPanel.java b/src/audio/AudioChannelPanel.java index 72f4a091..5532fdb7 100644 --- a/src/audio/AudioChannelPanel.java +++ b/src/audio/AudioChannelPanel.java @@ -19,10 +19,8 @@ package audio; import alias.Alias; -import audio.metadata.AudioMetadata; -import audio.metadata.Metadata; -import audio.metadata.MetadataType; import audio.output.AudioOutput; +import channel.metadata.Metadata; import icon.IconManager; import net.miginfocom.swing.MigLayout; import sample.Listener; @@ -50,12 +48,12 @@ public class AudioChannelPanel extends JPanel private JLabel mChannelName = new JLabel(" "); private JLabel mToLabel = new JLabel("TO:"); - private JLabel mTo = new JLabel(""); + private JLabel mTo = new JLabel("-----"); private JLabel mToAlias = new JLabel(""); private JLabel mMutedLabel = new JLabel(" "); private JLabel mFromLabel = new JLabel("FROM:"); - private JLabel mFrom = new JLabel(""); + private JLabel mFrom = new JLabel("-----"); private JLabel mFromAlias = new JLabel(""); private boolean mConfigured = false; @@ -71,7 +69,7 @@ public class AudioChannelPanel extends JPanel if(mAudioOutput != null) { mAudioOutput.addAudioEventListener(this); - mAudioOutput.setAudioMetadataListener(new AudioMetadataProcessor()); + mAudioOutput.setMetadataListener(new AudioMetadataProcessor()); } mLabelColor = mSettingsManager.getColorSetting( @@ -173,11 +171,8 @@ public class AudioChannelPanel extends JPanel */ private void resetLabels() { - mFrom.setText(""); - updateAlias(mFromAlias, null); - - mTo.setText(""); - updateAlias(mToAlias, null); + updateLabel(mFromLabel, null, mFromAlias, null); + updateLabel(mToLabel, null, mToAlias, null); mConfigured = false; } @@ -187,27 +182,25 @@ public class AudioChannelPanel extends JPanel * does not occur on the Swing event thread -- wrap any calls to this * method with an event thread call. */ - private void updateAlias(JLabel label, Alias alias) + private void updateLabel(JLabel valueLabel, String value, JLabel aliasLabel, Alias alias) { - if(alias != null) + if(value != null) { - label.setText(alias.getName()); + valueLabel.setText(value); - String icon = alias.getIconName(); - - if(icon != null) + if(alias != null) { - label.setIcon(mIconManager.getIcon(icon, IconManager.DEFAULT_ICON_SIZE)); + aliasLabel.setIcon(mIconManager.getIcon(alias.getIconName(), IconManager.DEFAULT_ICON_SIZE)); } else { - label.setIcon(null); + aliasLabel.setIcon(null); } } else { - label.setText(""); - label.setIcon(null); + valueLabel.setText("-----"); + aliasLabel.setIcon(null); } } @@ -215,45 +208,28 @@ public class AudioChannelPanel extends JPanel /** * Processes audio metadata to update this panel's display values */ - public class AudioMetadataProcessor implements Listener + public class AudioMetadataProcessor implements Listener { @Override - public void receive(AudioMetadata audioMetadata) + public void receive(final Metadata metadata) { - if(!mConfigured || audioMetadata.isUpdated()) + if(metadata.isUpdated() || !mConfigured) { - final Metadata from = audioMetadata.getMetadata(MetadataType.FROM); - - final Metadata to = audioMetadata.getMetadata(MetadataType.TO); - EventQueue.invokeLater(new Runnable() { @Override public void run() { - if(from != null) - { - mFrom.setText(from.getValue()); - updateAlias(mFromAlias, from.getAlias()); - } - else - { - mFrom.setText("-----"); - } + updateLabel(mFromLabel, metadata.getPrimaryAddressFrom().getIdentifier(), + mFromAlias, metadata.getPrimaryAddressFrom().getAlias()); - if(to != null) - { - mTo.setText(to.getValue()); - updateAlias(mToAlias, to.getAlias()); - } - else - { - mTo.setText("-----"); - } + updateLabel(mToLabel, metadata.getPrimaryAddressTo().getIdentifier(), + mToAlias, metadata.getPrimaryAddressTo().getAlias()); + + mConfigured = true; } }); - mConfigured = true; } } } diff --git a/src/audio/AudioManager.java b/src/audio/AudioManager.java index fbc7e7b1..6be25898 100644 --- a/src/audio/AudioManager.java +++ b/src/audio/AudioManager.java @@ -1,22 +1,40 @@ /******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 + * sdrtrunk + * Copyright (C) 2014-2017 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 audio; +import audio.AudioEvent.Type; +import audio.output.AudioOutput; +import audio.output.MonoAudioOutput; +import audio.output.StereoAudioOutput; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import properties.SystemProperties; +import sample.Broadcaster; +import sample.Listener; +import source.mixer.MixerChannel; +import source.mixer.MixerChannelConfiguration; +import source.mixer.MixerManager; + +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Mixer; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -27,523 +45,495 @@ import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.Mixer; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import properties.SystemProperties; -import sample.Broadcaster; -import sample.Listener; -import source.mixer.MixerChannel; -import source.mixer.MixerChannelConfiguration; -import source.mixer.MixerManager; -import audio.AudioEvent.Type; -import audio.output.AudioOutput; -import audio.output.MonoAudioOutput; -import audio.output.StereoAudioOutput; -import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; - public class AudioManager implements Listener, IAudioController { - private static final Logger mLog = LoggerFactory.getLogger( AudioManager.class ); + private static final Logger mLog = LoggerFactory.getLogger(AudioManager.class); - public static final int AUDIO_TIMEOUT = 1000; //1 second - - public static final String AUDIO_CHANNELS_PROPERTY = "audio.manager.channels"; - public static final String AUDIO_MIXER_PROPERTY = "audio.manager.mixer"; - - public static final AudioEvent CONFIGURATION_CHANGE_STARTED = - new AudioEvent( Type.AUDIO_CONFIGURATION_CHANGE_STARTED, null ); - - public static final AudioEvent CONFIGURATION_CHANGE_COMPLETE = - new AudioEvent( Type.AUDIO_CONFIGURATION_CHANGE_COMPLETE, null ); + public static final int AUDIO_TIMEOUT = 1000; //1 second - private LinkedTransferQueue mAudioPacketQueue = new LinkedTransferQueue<>(); - private Map mChannelConnectionMap = new HashMap<>(); - private List mAudioOutputConnections = new ArrayList<>(); - private AudioOutputConnection mLowestPriorityConnection; - private int mAvailableConnectionCount; + public static final String AUDIO_CHANNELS_PROPERTY = "audio.manager.channels"; + public static final String AUDIO_MIXER_PROPERTY = "audio.manager.mixer"; - private Map mAudioOutputMap = new HashMap<>(); - - private Broadcaster mControllerBroadcaster = new Broadcaster<>(); - - private ThreadPoolManager mThreadPoolManager; - private ScheduledFuture mProcessingTask; - private MixerManager mMixerManager; - private MixerChannelConfiguration mMixerChannelConfiguration; - + public static final AudioEvent CONFIGURATION_CHANGE_STARTED = + new AudioEvent(Type.AUDIO_CONFIGURATION_CHANGE_STARTED, null); - /** - * Processes all audio produced by the decoding channels and routes audio - * packets to any combination of outputs based on any alias audio routing - * options specified by the user. - */ - public AudioManager( ThreadPoolManager manager, MixerManager mixerManager ) - { - mThreadPoolManager = manager; - mMixerManager = mixerManager; + public static final AudioEvent CONFIGURATION_CHANGE_COMPLETE = + new AudioEvent(Type.AUDIO_CONFIGURATION_CHANGE_COMPLETE, null); - loadSettings(); - } - - private void loadSettings() - { - MixerChannelConfiguration configuration = null; - - SystemProperties properties = SystemProperties.getInstance(); - - Mixer defaultMixer = AudioSystem.getMixer( null ); + private LinkedTransferQueue mAudioPacketQueue = new LinkedTransferQueue<>(); + private Map mChannelConnectionMap = new HashMap<>(); + private List mAudioOutputConnections = new ArrayList<>(); + private AudioOutputConnection mLowestPriorityConnection; + private int mAvailableConnectionCount; - String mixer = properties.get( AUDIO_MIXER_PROPERTY, defaultMixer.getMixerInfo().getName() ); - - String channels = properties.get( AUDIO_CHANNELS_PROPERTY, MixerChannel.MONO.name() ); - - MixerChannelConfiguration[] mixerConfigurations = mMixerManager.getOutputMixers(); + private Map mAudioOutputMap = new HashMap<>(); - for( MixerChannelConfiguration mixerConfig: mixerConfigurations ) - { - if( mixerConfig.matches( mixer, channels ) ) - { - configuration = mixerConfig; - } - } - - if( configuration == null ) - { - configuration = getDefaultConfiguration(); - } + private Broadcaster mControllerBroadcaster = new Broadcaster<>(); - try - { - setMixerChannelConfiguration( configuration ); - } - catch( Exception e ) - { - mLog.error( "Couldn't set stored audio mixer/channel configuration" - + " - using default", e ); - - try - { - setMixerChannelConfiguration( getDefaultConfiguration() ); - } - catch( Exception e2 ) - { - mLog.error( "Couldn't set default audio mixer/channel " - + "configuration - no audio will be available", e2 ); - } - } - } - - private MixerChannelConfiguration getDefaultConfiguration() - { - /* Use the system default mixer and mono channel as default startup */ - Mixer defaultMixer = AudioSystem.getMixer( null ); - - return new MixerChannelConfiguration( defaultMixer, MixerChannel.MONO ); - } - - public void dispose() - { - if( mThreadPoolManager != null && mProcessingTask != null ) - { - mThreadPoolManager.cancel( mProcessingTask ); - } - - mAudioPacketQueue.clear(); - - mThreadPoolManager = null; - mProcessingTask = null; + private ThreadPoolManager mThreadPoolManager; + private ScheduledFuture mProcessingTask; + private MixerManager mMixerManager; + private MixerChannelConfiguration mMixerChannelConfiguration; - mChannelConnectionMap.clear(); - - for( AudioOutputConnection connection: mAudioOutputConnections ) - { - connection.dispose(); - } - - mAudioOutputConnections.clear(); - } - @Override - public synchronized void receive( AudioPacket packet ) - { - mAudioPacketQueue.add( packet ); - } - - /** - * Checks each audio channel assignment and disconnects any inactive connections - */ - private void disconnectInactiveChannelAssignments() - { - boolean changed = false; - - for( AudioOutputConnection connection: mAudioOutputConnections ) - { - if( connection.isInactive() && - mChannelConnectionMap.containsKey( connection.getSource() ) ) - { - mChannelConnectionMap.remove( connection.getSource() ); - connection.disconnect(); - changed = true; - } - } - - if( changed ) - { - updateLowestPriorityAssignment(); - } - } + /** + * Processes all audio produced by the decoding channels and routes audio + * packets to any combination of outputs based on any alias audio routing + * options specified by the user. + */ + public AudioManager(ThreadPoolManager manager, MixerManager mixerManager) + { + mThreadPoolManager = manager; + mMixerManager = mixerManager; - /** - * Identifies the lowest priority channel connection - */ - private void updateLowestPriorityAssignment() - { - mLowestPriorityConnection = null; - - for( AudioOutputConnection connection: mAudioOutputConnections ) - { - if( connection.isConnected() && - ( mLowestPriorityConnection == null || - mLowestPriorityConnection.getPriority() > connection.getPriority() ) ) - { - mLowestPriorityConnection = connection; - } - } - } - - @Override - public void setMixerChannelConfiguration( MixerChannelConfiguration entry ) throws AudioException - { - if( entry != null && - ( entry.getMixerChannel() == MixerChannel.MONO || - entry.getMixerChannel() == MixerChannel.STEREO ) ) - { - mControllerBroadcaster.broadcast( CONFIGURATION_CHANGE_STARTED ); - - if( mThreadPoolManager != null && mProcessingTask != null ) - { - mThreadPoolManager.cancel( mProcessingTask ); - } - - disposeCurrentConfiguration(); - - switch( entry.getMixerChannel() ) - { - case MONO: - AudioOutput mono = new MonoAudioOutput( mThreadPoolManager, - entry.getMixer() ); - mAudioOutputConnections.add( new AudioOutputConnection( mono ) ); - mAvailableConnectionCount++; - mAudioOutputMap.put( mono.getChannelName(), mono ); - break; - case STEREO: - AudioOutput left = new StereoAudioOutput( mThreadPoolManager, - entry.getMixer(), MixerChannel.LEFT ); - mAudioOutputConnections.add( new AudioOutputConnection( left ) ); - mAvailableConnectionCount++; - mAudioOutputMap.put( left.getChannelName(), left ); - - AudioOutput right = new StereoAudioOutput( mThreadPoolManager, - entry.getMixer(), MixerChannel.RIGHT ); - mAudioOutputConnections.add( new AudioOutputConnection( right ) ); - mAvailableConnectionCount++; - mAudioOutputMap.put( right.getChannelName(), right ); - break; - default: - throw new AudioException( "Unsupported mixer channel " - + "configuration: " + entry.getMixerChannel() ); - } + loadSettings(); + } - mProcessingTask = mThreadPoolManager.scheduleFixedRate( - ThreadType.AUDIO_PROCESSING, new AudioPacketProcessor(), 15, - TimeUnit.MILLISECONDS ); + private void loadSettings() + { + MixerChannelConfiguration configuration = null; - mControllerBroadcaster.broadcast( CONFIGURATION_CHANGE_COMPLETE ); - - SystemProperties properties = SystemProperties.getInstance(); - - properties.set( AUDIO_MIXER_PROPERTY, entry.getMixer().getMixerInfo().getName() ); - properties.set( AUDIO_CHANNELS_PROPERTY, entry.getMixerChannel().name() ); - properties.save(); - } - } + SystemProperties properties = SystemProperties.getInstance(); - /** - * Clears all channel assignments and terminates all audio outputs in - * preparation for complete shutdown or change to another mixer/channel - * configuration - */ - private void disposeCurrentConfiguration() - { - mChannelConnectionMap.clear(); + Mixer defaultMixer = AudioSystem.getMixer(null); - for( AudioOutputConnection connection : mAudioOutputConnections ) - { - connection.dispose(); - } - - mAvailableConnectionCount = 0; + String mixer = properties.get(AUDIO_MIXER_PROPERTY, defaultMixer.getMixerInfo().getName()); - mAudioOutputConnections.clear(); - - mAudioOutputMap.clear(); - - mLowestPriorityConnection = null; - } + String channels = properties.get(AUDIO_CHANNELS_PROPERTY, MixerChannel.MONO.name()); - @Override - public MixerChannelConfiguration getMixerChannelConfiguration() throws AudioException - { - return mMixerChannelConfiguration; - } + MixerChannelConfiguration[] mixerConfigurations = mMixerManager.getOutputMixers(); - @Override - public List getAudioOutputs() - { - List outputs = new ArrayList<>( mAudioOutputMap.values() ); + for(MixerChannelConfiguration mixerConfig : mixerConfigurations) + { + if(mixerConfig.matches(mixer, channels)) + { + configuration = mixerConfig; + } + } - Collections.sort( outputs, new Comparator() - { - @Override - public int compare( AudioOutput first, AudioOutput second ) - { - return first.getChannelName().compareTo( second.getChannelName() ); - } - } ); - - return outputs; - } + if(configuration == null) + { + configuration = getDefaultConfiguration(); + } - @Override - public void addControllerListener( Listener listener ) - { - mControllerBroadcaster.addListener( listener ); - } + try + { + setMixerChannelConfiguration(configuration); + } + catch(Exception e) + { + mLog.error("Couldn't set stored audio mixer/channel configuration" + + " - using default", e); - @Override - public void removeControllerListener( Listener listener ) - { - mControllerBroadcaster.removeListener( listener ); - } - - /** - * Returns an audio output connection for the packet if one is - * available, or overrides an existing lower priority connection. - * Returns null if no connection is available for the audio packet. - * - * @param packet from a source - * @return an audio output connection or null - */ - private AudioOutputConnection getConnection( AudioPacket packet ) - { - int source = packet.getAudioMetadata().getSource(); - - //Use an existing connection - if( mChannelConnectionMap.containsKey( source ) ) - { - return mChannelConnectionMap.get( source ); - } + try + { + setMixerChannelConfiguration(getDefaultConfiguration()); + } + catch(Exception e2) + { + mLog.error("Couldn't set default audio mixer/channel " + + "configuration - no audio will be available", e2); + } + } + } - //Connect to and use an available connection - if( mAvailableConnectionCount > 0 ) - { - for( AudioOutputConnection connection: mAudioOutputConnections ) - { - if( connection.isDisconnected() ) - { - connection.connect( source ); - mChannelConnectionMap.put( source, connection ); - return connection; - } - } - } - //Preempt an existing connection and connect it to this higher priority source - else //Check for a channel priority override - { - int priority = packet.getAudioMetadata().getPriority(); + private MixerChannelConfiguration getDefaultConfiguration() + { + /* Use the system default mixer and mono channel as default startup */ + Mixer defaultMixer = AudioSystem.getMixer(null); - if( mLowestPriorityConnection != null && - priority < mLowestPriorityConnection.getPriority() ) - { - AudioOutputConnection connection = mLowestPriorityConnection; + return new MixerChannelConfiguration(defaultMixer, MixerChannel.MONO); + } - mChannelConnectionMap.remove( connection.getSource() ); - - connection.connect( source ); - - mChannelConnectionMap.put( source, connection ); - - updateLowestPriorityAssignment(); - - return connection; - } - } - - return null; - } - - public class AudioPacketProcessor implements Runnable - { - @Override - public void run() - { - try - { - disconnectInactiveChannelAssignments(); - - if( mAudioPacketQueue != null ) - { - List packets = new ArrayList(); + public void dispose() + { + if(mThreadPoolManager != null && mProcessingTask != null) + { + mThreadPoolManager.cancel(mProcessingTask); + } - mAudioPacketQueue.drainTo( packets ); - - for( AudioPacket packet: packets ) - { + mAudioPacketQueue.clear(); + + mThreadPoolManager = null; + mProcessingTask = null; + + mChannelConnectionMap.clear(); + + for(AudioOutputConnection connection : mAudioOutputConnections) + { + connection.dispose(); + } + + mAudioOutputConnections.clear(); + } + + @Override + public synchronized void receive(AudioPacket packet) + { + mAudioPacketQueue.add(packet); + } + + /** + * Checks each audio channel assignment and disconnects any inactive connections + */ + private void disconnectInactiveChannelAssignments() + { + boolean changed = false; + + for(AudioOutputConnection connection : mAudioOutputConnections) + { + if(connection.isInactive() && + mChannelConnectionMap.containsKey(connection.getChannelMetadataID())) + { + mChannelConnectionMap.remove(connection.getChannelMetadataID()); + connection.disconnect(); + changed = true; + } + } + + if(changed) + { + updateLowestPriorityAssignment(); + } + } + + /** + * Identifies the lowest priority channel connection + */ + private void updateLowestPriorityAssignment() + { + mLowestPriorityConnection = null; + + for(AudioOutputConnection connection : mAudioOutputConnections) + { + if(connection.isConnected() && + (mLowestPriorityConnection == null || mLowestPriorityConnection.getPriority() > connection.getPriority())) + { + mLowestPriorityConnection = connection; + } + } + } + + @Override + public void setMixerChannelConfiguration(MixerChannelConfiguration entry) throws AudioException + { + if(entry != null && + (entry.getMixerChannel() == MixerChannel.MONO || entry.getMixerChannel() == MixerChannel.STEREO)) + { + mControllerBroadcaster.broadcast(CONFIGURATION_CHANGE_STARTED); + + if(mThreadPoolManager != null && mProcessingTask != null) + { + mThreadPoolManager.cancel(mProcessingTask); + } + + disposeCurrentConfiguration(); + + switch(entry.getMixerChannel()) + { + case MONO: + AudioOutput mono = new MonoAudioOutput(mThreadPoolManager, + entry.getMixer()); + mAudioOutputConnections.add(new AudioOutputConnection(mono)); + mAvailableConnectionCount++; + mAudioOutputMap.put(mono.getChannelName(), mono); + break; + case STEREO: + AudioOutput left = new StereoAudioOutput(mThreadPoolManager, entry.getMixer(), MixerChannel.LEFT); + mAudioOutputConnections.add(new AudioOutputConnection(left)); + mAvailableConnectionCount++; + mAudioOutputMap.put(left.getChannelName(), left); + + AudioOutput right = new StereoAudioOutput(mThreadPoolManager, entry.getMixer(), MixerChannel.RIGHT); + mAudioOutputConnections.add(new AudioOutputConnection(right)); + mAvailableConnectionCount++; + mAudioOutputMap.put(right.getChannelName(), right); + break; + default: + throw new AudioException("Unsupported mixer channel " + + "configuration: " + entry.getMixerChannel()); + } + + mProcessingTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.AUDIO_PROCESSING, new AudioPacketProcessor(), 15, + TimeUnit.MILLISECONDS); + + mControllerBroadcaster.broadcast(CONFIGURATION_CHANGE_COMPLETE); + + SystemProperties properties = SystemProperties.getInstance(); + + properties.set(AUDIO_MIXER_PROPERTY, entry.getMixer().getMixerInfo().getName()); + properties.set(AUDIO_CHANNELS_PROPERTY, entry.getMixerChannel().name()); + properties.save(); + } + } + + /** + * Clears all channel assignments and terminates all audio outputs in + * preparation for complete shutdown or change to another mixer/channel + * configuration + */ + private void disposeCurrentConfiguration() + { + mChannelConnectionMap.clear(); + + for(AudioOutputConnection connection : mAudioOutputConnections) + { + connection.dispose(); + } + + mAvailableConnectionCount = 0; + + mAudioOutputConnections.clear(); + + mAudioOutputMap.clear(); + + mLowestPriorityConnection = null; + } + + @Override + public MixerChannelConfiguration getMixerChannelConfiguration() throws AudioException + { + return mMixerChannelConfiguration; + } + + @Override + public List getAudioOutputs() + { + List outputs = new ArrayList<>(mAudioOutputMap.values()); + + Collections.sort(outputs, new Comparator() + { + @Override + public int compare(AudioOutput first, AudioOutput second) + { + return first.getChannelName().compareTo(second.getChannelName()); + } + }); + + return outputs; + } + + @Override + public void addControllerListener(Listener listener) + { + mControllerBroadcaster.addListener(listener); + } + + @Override + public void removeControllerListener(Listener listener) + { + mControllerBroadcaster.removeListener(listener); + } + + /** + * Returns an audio output connection for the packet if one is + * available, or overrides an existing lower priority connection. + * Returns null if no connection is available for the audio packet. + * + * @param packet from a source + * @return an audio output connection or null + */ + private AudioOutputConnection getConnection(AudioPacket packet) + { + int channelMetadataID = packet.getMetadata().getMetadataID(); + + //Use an existing connection + if(mChannelConnectionMap.containsKey(channelMetadataID)) + { + return mChannelConnectionMap.get(channelMetadataID); + } + + //Connect to and use an available connection + if(mAvailableConnectionCount > 0) + { + for(AudioOutputConnection connection : mAudioOutputConnections) + { + if(connection.isDisconnected()) + { + connection.connect(channelMetadataID); + mChannelConnectionMap.put(channelMetadataID, connection); + return connection; + } + } + } + //Preempt an existing connection and connect it to this higher priority source + else //Check for a channel priority override + { + int priority = packet.getMetadata().getAudioPriority(); + + if(mLowestPriorityConnection != null && + priority < mLowestPriorityConnection.getPriority()) + { + AudioOutputConnection connection = mLowestPriorityConnection; + + mChannelConnectionMap.remove(connection.getChannelMetadataID()); + + connection.connect(channelMetadataID); + + mChannelConnectionMap.put(channelMetadataID, connection); + + updateLowestPriorityAssignment(); + + return connection; + } + } + + return null; + } + + public class AudioPacketProcessor implements Runnable + { + @Override + public void run() + { + try + { + disconnectInactiveChannelAssignments(); + + if(mAudioPacketQueue != null) + { + List packets = new ArrayList(); + + mAudioPacketQueue.drainTo(packets); + + for(AudioPacket packet : packets) + { /* Don't process any packet's marked as do not monitor */ - if( !packet.getAudioMetadata().isDoNotMonitor() && - packet.getType() == AudioPacket.Type.AUDIO ) - { - AudioOutputConnection connection = getConnection( packet ); + if(!packet.getMetadata().isDoNotMonitor() && packet.getType() == AudioPacket.Type.AUDIO) + { + AudioOutputConnection connection = getConnection(packet); - if( connection != null ) - { - connection.receive( packet ); - } - } - } - } - } - catch( Exception e ) - { - mLog.error( "Encountered error while processing audio packets", e ); - } - } - } - - /** - * Audio output connection manages a connection between a source and an audio - * output and maintains current state information about the audio activity - * received form the source. - */ - public class AudioOutputConnection - { - private static final int DISCONNECTED = -1; - - private AudioOutput mAudioOutput; - private int mPriority = 0; - private int mSource = DISCONNECTED; - - public AudioOutputConnection( AudioOutput audioOutput ) - { - mAudioOutput = audioOutput; - } - - public void receive( AudioPacket packet ) - { - if( packet.hasAudioMetadata() && - packet.getAudioMetadata().getSource() == mSource ) - { - int priority = packet.getAudioMetadata().getPriority(); + if(connection != null) + { + connection.receive(packet); + } + } + } + } + } + catch(Exception e) + { + mLog.error("Encountered error while processing audio packets", e); + } + } + } - if( mPriority != priority ) - { - mPriority = priority; - updateLowestPriorityAssignment(); - } - - if( mAudioOutput != null ) - { - mAudioOutput.receive( packet ); - } - } - else - { - if( packet.hasAudioMetadata() ) - { - mLog.error( "Received audio packet from channel source [" + - packet.getAudioMetadata().getSource() + - "] however this assignment is currently connected " - + "to channel source [" + mSource + "]" ); - } - else - { - mLog.error( "Received audio packet with no metadata - " - + "cannot route audio packet" ); - } - } - } - - /** - * Terminates the audio output and prepares this connection for disposal - */ - public void dispose() - { - mAudioOutput.dispose(); - mAudioOutput = null; - } + /** + * Audio output connection manages a connection between a source and an audio + * output and maintains current state information about the audio activity + * received form the source. + */ + public class AudioOutputConnection + { + private static final int DISCONNECTED = -1; - /** - * Indicates if this assignment is currently disconnected from a channel source - */ - public boolean isDisconnected() - { - return mSource == DISCONNECTED; - } - - /** - * Indicates if this assignment is currently connected to a channel source - */ - public boolean isConnected() - { - return !isDisconnected(); - } + private AudioOutput mAudioOutput; + private int mPriority = 0; + private int mChannelMetadataID = DISCONNECTED; - /** - * Connects this assignment to the indicated source so that audio - * packets from this source can be sent to the audio output - */ - public void connect( int source ) - { - mSource = source; - - mAudioOutput.updateTimestamp(); - } + public AudioOutputConnection(AudioOutput audioOutput) + { + mAudioOutput = audioOutput; + } - /** - * Currently connected source or -1 if disconnected - */ - public int getSource() - { - return mSource; - } + public void receive(AudioPacket packet) + { + if(packet.hasMetadata() && packet.getMetadata().getMetadataID() == mChannelMetadataID) + { + int priority = packet.getMetadata().getAudioPriority(); - /** - * Disconnects this assignment from the source and prevents any audio - * from being routed to the audio output until another source is assigned - */ - public void disconnect() - { - mSource = DISCONNECTED; - mPriority = 0; - } + if(mPriority != priority) + { + mPriority = priority; + updateLowestPriorityAssignment(); + } - /** - * Indicates if audio output is current inactive, meaning that the - * audio output hasn't recently processed any audio packets. - */ - public boolean isInactive() - { - return ( mAudioOutput.getLastActivityTimestamp() + AUDIO_TIMEOUT ) < System.currentTimeMillis(); - } - - public int getPriority() - { - return mPriority; - } - } + if(mAudioOutput != null) + { + mAudioOutput.receive(packet); + } + } + else + { + if(packet.hasMetadata()) + { + mLog.error("Received audio packet from channel metadata [" + packet.getMetadata().getMetadataID() + + "] however this assignment is currently connected to metadata [" + mChannelMetadataID + "]"); + } + else + { + mLog.error("Received audio packet with no metadata - cannot route audio packet"); + } + } + } + + /** + * Terminates the audio output and prepares this connection for disposal + */ + public void dispose() + { + mAudioOutput.dispose(); + mAudioOutput = null; + } + + /** + * Indicates if this assignment is currently disconnected from a channel source + */ + public boolean isDisconnected() + { + return mChannelMetadataID == DISCONNECTED; + } + + /** + * Indicates if this assignment is currently connected to a channel source + */ + public boolean isConnected() + { + return !isDisconnected(); + } + + /** + * Connects this assignment to the indicated source so that audio + * packets from this source can be sent to the audio output + */ + public void connect(int source) + { + mChannelMetadataID = source; + + mAudioOutput.updateTimestamp(); + } + + /** + * Currently connected source or -1 if disconnected + */ + public int getChannelMetadataID() + { + return mChannelMetadataID; + } + + /** + * Disconnects this assignment from the source and prevents any audio + * from being routed to the audio output until another source is assigned + */ + public void disconnect() + { + mChannelMetadataID = DISCONNECTED; + mPriority = 0; + } + + /** + * Indicates if audio output is current inactive, meaning that the + * audio output hasn't recently processed any audio packets. + */ + public boolean isInactive() + { + return (mAudioOutput.getLastActivityTimestamp() + AUDIO_TIMEOUT) < System.currentTimeMillis(); + } + + public int getPriority() + { + return mPriority; + } + } } diff --git a/src/audio/AudioModule.java b/src/audio/AudioModule.java index 4a02298f..58b0be0f 100644 --- a/src/audio/AudioModule.java +++ b/src/audio/AudioModule.java @@ -1,29 +1,38 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 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 audio; -import java.util.Arrays; -import java.util.concurrent.ScheduledExecutorService; - +import audio.squelch.ISquelchStateListener; +import audio.squelch.SquelchState; +import channel.metadata.Metadata; import dsp.filter.design.FilterDesignException; import dsp.filter.fir.FIRFilterSpecification; import dsp.filter.fir.remez.RemezFIRFilterDesigner; import dsp.filter.polyphase.PolyphaseFIRDecimatingFilter_RB; import module.Module; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import sample.Listener; import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; -import audio.metadata.AudioMetadata; -import audio.metadata.IMetadataListener; -import audio.metadata.Metadata; -import audio.squelch.ISquelchStateListener; -import audio.squelch.SquelchState; -import controller.channel.ChannelEvent; -import controller.channel.IChannelEventListener; -import dsp.filter.Filters; -import dsp.filter.fir.real.RealFIRFilter_R_R; + +import java.util.concurrent.ScheduledExecutorService; /** * Provides packaging of demodulated audio sample buffers into audio packets for @@ -33,12 +42,8 @@ import dsp.filter.fir.real.RealFIRFilter_R_R; * Incorporates audio squelch state listener to control if audio packets are * broadcast or ignored. */ -public class AudioModule extends Module implements IAudioPacketProvider, - IChannelEventListener, - IMetadataListener, - IFilteredRealBufferListener, - ISquelchStateListener, - Listener +public class AudioModule extends Module implements IAudioPacketProvider, IFilteredRealBufferListener, + ISquelchStateListener, Listener { protected static final Logger mLog = LoggerFactory.getLogger(AudioModule.class); @@ -48,48 +53,41 @@ public class AudioModule extends Module implements IAudioPacketProvider, { //Band-pass filter to both decimate (48kHz to 8kHz) and block 0 - 300 Hz (LTR signalling). FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder() - .sampleRate( 48000 ) - .stopFrequency1( 150 ) - .passFrequencyBegin( 300 ) - .passFrequencyEnd( 3500 ) - .stopFrequency2( 4000 ) - .stopRipple( 0.0004 ) - .passRipple( 0.008) - .build(); + .sampleRate(48000) + .stopFrequency1(150) + .passFrequencyBegin(300) + .passFrequencyEnd(3500) + .stopFrequency2(4000) + .stopRipple(0.0004) + .passRipple(0.008) + .build(); RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification); try { - if (!designer.isValid()) + if(!designer.isValid()) { throw new FilterDesignException("Couldn't design the audio decimation filter"); } mDecimationCoefficients = designer.getImpulseResponse(); } - catch (FilterDesignException e) + catch(FilterDesignException e) { mLog.debug("Error designing filter", e); } } - /* Provides a unique identifier for this audio module instance to use as a - * source identifier for all audio packets */ - private static int UNIQUE_ID = 0; - private int mSourceID; - - private AudioMetadata mAudioMetadata; - private ChannelEventListener mChannelEventListener = new ChannelEventListener(); private SquelchStateListener mSquelchStateListener = new SquelchStateListener(); private SquelchState mSquelchState = SquelchState.SQUELCH; private Listener mAudioPacketListener; private PolyphaseFIRDecimatingFilter_RB mAudioDecimationFilter; + private Metadata mMetadata; - public AudioModule(boolean record) + public AudioModule(Metadata metadata) { - mSourceID = ++UNIQUE_ID; - mAudioMetadata = new AudioMetadata(mSourceID, record); + mMetadata = metadata; mAudioDecimationFilter = new PolyphaseFIRDecimatingFilter_RB(mDecimationCoefficients, 6, 2.0f); mAudioDecimationFilter.setListener(new Listener() @@ -97,9 +95,9 @@ public class AudioModule extends Module implements IAudioPacketProvider, @Override public void receive(RealBuffer realBuffer) { - if (mAudioPacketListener != null) + if(mAudioPacketListener != null) { - AudioPacket packet = new AudioPacket(realBuffer.getSamples(), mAudioMetadata.copyOf()); + AudioPacket packet = new AudioPacket(realBuffer.getSamples(), mMetadata.copyOf()); mAudioPacketListener.receive(packet); } } @@ -116,7 +114,6 @@ public class AudioModule extends Module implements IAudioPacketProvider, @Override public void reset() { - mAudioMetadata.reset(); } @Override @@ -128,11 +125,10 @@ public class AudioModule extends Module implements IAudioPacketProvider, @Override public void stop() { - /* Issue an end audio packet in case a recorder is still rolling */ - if (mAudioPacketListener != null) + /* Issue an end audio packet in case a recorder is still rolling */ + if(mAudioPacketListener != null) { - mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, - mAudioMetadata.copyOf())); + mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf())); } } @@ -143,15 +139,9 @@ public class AudioModule extends Module implements IAudioPacketProvider, @Override public void receive(RealBuffer buffer) { - if (mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH) + if(mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH) { mAudioDecimationFilter.receive(buffer); - -// /* We make a copy of the samples, so that we don't affect any other -// * processes that might be concurrently processing the same buffer */ -// float[] audio = Arrays.copyOf( buffer.getSamples(), buffer.getSamples().length ); -// -// mAudioFilter.filter( audio ); } } @@ -161,12 +151,6 @@ public class AudioModule extends Module implements IAudioPacketProvider, return this; } - @Override - public Listener getMetadataListener() - { - return mAudioMetadata; - } - @Override public void setAudioPacketListener(Listener listener) { @@ -179,41 +163,12 @@ public class AudioModule extends Module implements IAudioPacketProvider, mAudioPacketListener = null; } - @Override - public Listener getChannelEventListener() - { - return mChannelEventListener; - } - @Override public Listener getSquelchStateListener() { return mSquelchStateListener; } - /** - * Wrapper for channel event listener. Responds to channel state reset - * events to remove/cleanup current audio metadata - */ - public class ChannelEventListener implements Listener - { - @Override - public void receive(ChannelEvent event) - { - switch (event.getEvent()) - { - case NOTIFICATION_STATE_RESET: - mAudioMetadata.reset(); - break; - case NOTIFICATION_SELECTION_CHANGE: - mAudioMetadata.setSelected(event.getChannel().isSelected()); - break; - default: - break; - } - } - } - /** * Wrapper for squelch state listener */ @@ -222,10 +177,9 @@ public class AudioModule extends Module implements IAudioPacketProvider, @Override public void receive(SquelchState state) { - if (state == SquelchState.SQUELCH && mAudioPacketListener != null) + if(state == SquelchState.SQUELCH && mAudioPacketListener != null) { - mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, - mAudioMetadata.copyOf())); + mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf())); } mSquelchState = state; diff --git a/src/audio/AudioPacket.java b/src/audio/AudioPacket.java index 2936557e..aa655975 100644 --- a/src/audio/AudioPacket.java +++ b/src/audio/AudioPacket.java @@ -1,77 +1,78 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 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 audio; +import channel.metadata.Metadata; import sample.real.RealBuffer; -import audio.metadata.AudioMetadata; -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2016 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 - ******************************************************************************/ public class AudioPacket { - private Type mType; - private RealBuffer mAudioData; - private AudioMetadata mAudioMetadata; - - public AudioPacket( Type type, AudioMetadata metadata ) - { - mType = type; - mAudioMetadata = metadata; - } - - public AudioPacket( float[] audio, AudioMetadata metadata ) - { - this( Type.AUDIO, metadata ); + private Type mType; + private RealBuffer mAudioData; + private Metadata mMetadata; - mAudioData = new RealBuffer( audio ); - } - - public boolean hasAudioMetadata() - { - return mAudioMetadata != null; - } - - public AudioMetadata getAudioMetadata() - { - return mAudioMetadata; - } - - public void setMetadata( AudioMetadata metadata ) - { - mAudioMetadata = metadata; - } - - public Type getType() - { - return mType; - } - - public RealBuffer getAudioBuffer() - { - return mAudioData; - } - - public boolean hasAudioBuffer() - { - return mAudioData != null; - } - - public enum Type - { - AUDIO, - END; - } + public AudioPacket(Type type, Metadata metadata) + { + mType = type; + mMetadata = metadata; + } + + public AudioPacket(float[] audio, Metadata metadata) + { + this(Type.AUDIO, metadata); + + mAudioData = new RealBuffer(audio); + } + + public boolean hasMetadata() + { + return mMetadata != null; + } + + public Metadata getMetadata() + { + return mMetadata; + } + + public void setMetadata(Metadata metadata) + { + mMetadata = metadata; + } + + public Type getType() + { + return mType; + } + + public RealBuffer getAudioBuffer() + { + return mAudioData; + } + + public boolean hasAudioBuffer() + { + return mAudioData != null; + } + + public enum Type + { + AUDIO, + END; + } } diff --git a/src/audio/broadcast/AudioBroadcaster.java b/src/audio/broadcast/AudioBroadcaster.java index 7467a2b6..2115480b 100644 --- a/src/audio/broadcast/AudioBroadcaster.java +++ b/src/audio/broadcast/AudioBroadcaster.java @@ -1,6 +1,6 @@ /******************************************************************************* * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer + * Copyright (C) 2014-2017 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 @@ -18,14 +18,11 @@ ******************************************************************************/ package audio.broadcast; -import audio.AudioPacket; -import audio.IAudioPacketListener; import audio.convert.ISilenceGenerator; -import audio.metadata.AudioMetadata; +import channel.metadata.Metadata; import controller.ThreadPoolManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import properties.SystemProperties; import sample.Listener; import java.io.ByteArrayInputStream; @@ -34,14 +31,13 @@ import java.nio.file.Files; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public abstract class AudioBroadcaster implements Listener { - private final static Logger mLog = LoggerFactory.getLogger( AudioBroadcaster.class ); + private final static Logger mLog = LoggerFactory.getLogger(AudioBroadcaster.class); public static final int PROCESSOR_RUN_INTERVAL_MS = 1000; @@ -107,9 +103,10 @@ public abstract class AudioBroadcaster implements Listener /** * Broadcasts the next song's audio metadata prior to streaming the next song. + * * @param metadata for the next recording that will be streamed */ - protected void broadcastMetadata(AudioMetadata metadata) + protected void broadcastMetadata(Metadata metadata) { IBroadcastMetadataUpdater metadataUpdater = getMetadataUpdater(); @@ -136,7 +133,7 @@ public abstract class AudioBroadcaster implements Listener if(mThreadPoolManager != null) { mScheduledTask = mThreadPoolManager.scheduleFixedRate(ThreadPoolManager.ThreadType.AUDIO_PROCESSING, - mRecordingQueueProcessor, PROCESSOR_RUN_INTERVAL_MS, TimeUnit.MILLISECONDS ); + mRecordingQueueProcessor, PROCESSOR_RUN_INTERVAL_MS, TimeUnit.MILLISECONDS); } } } @@ -160,6 +157,7 @@ public abstract class AudioBroadcaster implements Listener /** * Stream name for the broadcast configuration for this broadcaster + * * @return stream name or null */ public String getStreamName() @@ -246,7 +244,7 @@ public abstract class AudioBroadcaster implements Listener { if(mBroadcastState != state) { - mLog.info("[" + getStreamName() + "] status: " + state); + mLog.info("[" + getStreamName() + "] status: " + state); mBroadcastState = state; broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_STATE_CHANGE)); @@ -386,7 +384,7 @@ public abstract class AudioBroadcaster implements Listener { mStreamedAudioCount++; broadcast(new BroadcastEvent(AudioBroadcaster.this, - BroadcastEvent.Event.BROADCASTER_STREAMED_COUNT_CHANGE)); + BroadcastEvent.Event.BROADCASTER_STREAMED_COUNT_CHANGE)); metadataUpdateRequired = true; } @@ -431,7 +429,7 @@ public abstract class AudioBroadcaster implements Listener if(connected()) { - broadcastMetadata(nextRecording.getAudioMetadata()); + broadcastMetadata(nextRecording.getMetadata()); } metadataUpdateRequired = false; diff --git a/src/audio/broadcast/AudioRecording.java b/src/audio/broadcast/AudioRecording.java index 1d4c3f16..f80a0c66 100644 --- a/src/audio/broadcast/AudioRecording.java +++ b/src/audio/broadcast/AudioRecording.java @@ -1,6 +1,6 @@ /******************************************************************************* * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer + * Copyright (C) 2014-2017 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 @@ -18,7 +18,7 @@ ******************************************************************************/ package audio.broadcast; -import audio.metadata.AudioMetadata; +import channel.metadata.Metadata; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,22 +30,23 @@ public class AudioRecording implements Comparable private final static Logger mLog = LoggerFactory.getLogger(StreamManager.class); private Path mPath; - private AudioMetadata mAudioMetadata; private long mStartTime; private long mRecordingLength; private AtomicInteger mPendingReplayCount = new AtomicInteger(); + private Metadata mMetadata; /** * Audio recording that is ready to be streamed + * * @param path to the audio recording file - * @param audioMetadata associated with the recording + * @param metadata associated with the recording * @param start time of recording in milliseconds since epoch * @param recordingLength in milliseconds */ - public AudioRecording(Path path, AudioMetadata audioMetadata, long start, long recordingLength) + public AudioRecording(Path path, Metadata metadata, long start, long recordingLength) { mPath = path; - mAudioMetadata = audioMetadata; + mMetadata = metadata; mStartTime = start; mRecordingLength = recordingLength; } @@ -61,9 +62,9 @@ public class AudioRecording implements Comparable /** * Optional audio metadata for the recording. */ - public AudioMetadata getAudioMetadata() + public Metadata getMetadata() { - return mAudioMetadata; + return mMetadata; } /** diff --git a/src/audio/broadcast/BroadcastModel.java b/src/audio/broadcast/BroadcastModel.java index 911eaf39..f6e9b587 100644 --- a/src/audio/broadcast/BroadcastModel.java +++ b/src/audio/broadcast/BroadcastModel.java @@ -20,7 +20,7 @@ package audio.broadcast; import alias.id.broadcast.BroadcastChannel; import audio.AudioPacket; -import audio.metadata.AudioMetadata; +import channel.metadata.Metadata; import controller.ThreadPoolManager; import icon.IconManager; import org.slf4j.Logger; @@ -48,7 +48,7 @@ import java.util.regex.Pattern; public class BroadcastModel extends AbstractTableModel implements Listener { - private final static Logger mLog = LoggerFactory.getLogger( BroadcastModel.class ); + private final static Logger mLog = LoggerFactory.getLogger(BroadcastModel.class); public static final String TEMPORARY_STREAM_DIRECTORY = "streaming"; public static final String TEMPORARY_STREAM_FILE_SUFFIX = "temporary_streaming_file_"; @@ -84,7 +84,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener