Resolves issue #23.

Adds audio output display and management options supporting mono and 
stereo output options.

Provides audio output supporting prioritization by talkgroup and/or 
channel selection.

Supports muting all channels, or muting individual channels and
individual audio gain/volume controls.
This commit is contained in:
denny
2015-10-03 17:50:03 -04:00
parent 20f5d06104
commit 3e05a9eb03
27 changed files with 1174 additions and 458 deletions

View File

@@ -17,7 +17,40 @@
******************************************************************************/
package source.mixer;
import java.util.EnumSet;
public enum MixerChannel
{
LEFT,RIGHT,MONO;
LEFT( "L" ),
RIGHT( "R" ),
MONO( "M" ),
STEREO( "S" );
private String mLabel;
private MixerChannel( String label )
{
mLabel = label;
}
public String getLabel()
{
return mLabel;
}
/**
* Mixer Target Data Line (ie sample source) channel options
*/
public static EnumSet<MixerChannel> getTargetChannels()
{
return EnumSet.of( LEFT,RIGHT,MONO );
}
/**
* Mixer Source Data Line (ie sound card output) channel options
*/
public static EnumSet<MixerChannel> getSourceChannels()
{
return EnumSet.of( MONO,STEREO );
}
}

View File

@@ -0,0 +1,68 @@
package source.mixer;
import javax.sound.sampled.Mixer;
public class MixerChannelConfiguration
{
private Mixer mMixer;
private MixerChannel mMixerChannel;
public MixerChannelConfiguration( Mixer mixer, MixerChannel channel )
{
mMixer = mixer;
mMixerChannel = channel;
}
public Mixer getMixer()
{
return mMixer;
}
public MixerChannel getMixerChannel()
{
return mMixerChannel;
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( mMixer.getMixerInfo().getName() );
sb.append( " - " );
sb.append( mMixerChannel.name() );
return sb.toString();
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ( ( mMixer == null ) ? 0 : mMixer.hashCode() );
result = prime * result
+ ( ( mMixerChannel == null ) ? 0 : mMixerChannel.hashCode() );
return result;
}
@Override
public boolean equals( Object obj )
{
if ( this == obj )
return true;
if ( obj == null )
return false;
if ( getClass() != obj.getClass() )
return false;
MixerChannelConfiguration other = (MixerChannelConfiguration) obj;
if ( mMixer == null )
{
if ( other.mMixer != null )
return false;
} else if ( !mMixer.equals( other.mMixer ) )
return false;
if ( mMixerChannel != other.mMixerChannel )
return false;
return true;
}
}

View File

@@ -17,6 +17,8 @@
******************************************************************************/
package source.mixer;
import java.util.EnumSet;
import javax.sound.sampled.Mixer;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
@@ -25,13 +27,13 @@ import javax.swing.JLabel;
import source.SourceEditor;
import source.config.SourceConfigMixer;
import source.config.SourceConfiguration;
import source.mixer.MixerManager.DiscoveredMixer;
import source.mixer.MixerManager.InputMixerConfiguration;
import controller.ResourceManager;
public class MixerEditor extends SourceEditor
{
private static final long serialVersionUID = 1L;
private JComboBox<DiscoveredMixer> mComboMixers;
private JComboBox<InputMixerConfiguration> mComboMixers;
private JComboBox<MixerChannel>mComboChannels;
protected Mixer.Info mSelectedMixer = null;
@@ -49,7 +51,7 @@ public class MixerEditor extends SourceEditor
JLabel mixerLabel = new JLabel( "Mixer:" );
add( mixerLabel, "align right" );
mComboMixers = new JComboBox<DiscoveredMixer>();
mComboMixers = new JComboBox<InputMixerConfiguration>();
add( mComboMixers, "wrap" );
@@ -75,7 +77,7 @@ public class MixerEditor extends SourceEditor
{
SourceConfigMixer config = (SourceConfigMixer)mConfig;
DiscoveredMixer selectedMixer =
InputMixerConfiguration selectedMixer =
mComboMixers.getItemAt( mComboMixers.getSelectedIndex() );
if( selectedMixer != null )
@@ -103,10 +105,10 @@ public class MixerEditor extends SourceEditor
@Override
public void run()
{
DiscoveredMixer[] mixers = MixerManager.getInstance().getMixers();
InputMixerConfiguration[] mixers = MixerManager.getInstance().getInputMixers();
mComboMixers.setModel(
new DefaultComboBoxModel<DiscoveredMixer>( mixers ) );
new DefaultComboBoxModel<InputMixerConfiguration>( mixers ) );
SourceConfigMixer config = (SourceConfigMixer)mConfig;
@@ -125,8 +127,8 @@ public class MixerEditor extends SourceEditor
{
mComboMixers.setSelectedIndex( 0 );
DiscoveredMixer selectedMixer =
(DiscoveredMixer)mComboMixers.getSelectedItem();
InputMixerConfiguration selectedMixer =
(InputMixerConfiguration)mComboMixers.getSelectedItem();
config.setMixer( selectedMixer.getMixerName() );
}
@@ -142,8 +144,10 @@ public class MixerEditor extends SourceEditor
@Override
public void run()
{
mComboChannels.setModel(
new DefaultComboBoxModel<MixerChannel>( MixerChannel.values() ) );
EnumSet<MixerChannel> channels = MixerChannel.getTargetChannels();
mComboChannels.setModel( new DefaultComboBoxModel<MixerChannel>(
channels.toArray( new MixerChannel[ channels.size() ] ) ) );
MixerChannel channel = ((SourceConfigMixer)mConfig).getChannel();

View File

@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
@@ -28,6 +29,8 @@ import javax.sound.sampled.DataLine;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.Port;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
import org.slf4j.Logger;
@@ -39,6 +42,7 @@ import source.config.SourceConfigMixer;
import source.config.SourceConfiguration;
import source.tuner.MixerTunerDataLine;
import source.tuner.MixerTunerType;
import audio.AudioFormats;
public class MixerManager
{
@@ -47,23 +51,9 @@ public class MixerManager
private static MixerManager sInstance = null;
private ArrayList<DiscoveredMixer> mDiscoveredMixers =
new ArrayList<DiscoveredMixer>();
private HashMap<String,MixerTunerDataLine> mMixerTuners =
new HashMap<String,MixerTunerDataLine>();
public static AudioFormat MONO = new AudioFormat( 48000, //SampleRate
16, //Sample Size
1, //Channels
true, //Signed
false ); //Little Endian
public static AudioFormat STEREO = new AudioFormat( 48000, //SampleRate
16, //Sample Size
2, //Channels
true, //Signed
false ); //Little Endian
private List<InputMixerConfiguration> mInputMixers = new ArrayList<>();
private List<MixerChannelConfiguration> mOutputMixers = new ArrayList<>();
private HashMap<String,MixerTunerDataLine> mMixerTuners = new HashMap<>();
private MixerManager()
{
@@ -92,7 +82,7 @@ public class MixerManager
if( mixerName != null )
{
DiscoveredMixer mixer = getDiscoveredMixer( mixerName );
InputMixerConfiguration mixer = getInputMixer( mixerName );
if( mixer != null )
{
@@ -104,7 +94,8 @@ public class MixerManager
if( channel == MixerChannel.MONO )
{
DataLine.Info info =
new DataLine.Info(TargetDataLine.class, MONO );
new DataLine.Info(TargetDataLine.class,
AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO );
TargetDataLine dataLine;
@@ -116,7 +107,7 @@ public class MixerManager
if( dataLine != null )
{
return new RealMixerSource( dataLine,
MONO,
AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO,
mixerName,
new ShortAdapter() );
}
@@ -132,7 +123,8 @@ public class MixerManager
else
{
DataLine.Info info =
new DataLine.Info(TargetDataLine.class, STEREO );
new DataLine.Info(TargetDataLine.class,
AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO );
TargetDataLine dataLine;
@@ -143,7 +135,8 @@ public class MixerManager
if( dataLine != null )
{
return new RealMixerSource( dataLine, STEREO,
return new RealMixerSource( dataLine,
AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO,
mixerName, new ChannelShortAdapter(
mixerConfig.getChannel() ) );
}
@@ -163,15 +156,14 @@ public class MixerManager
return null;
}
public DiscoveredMixer[] getMixers()
public InputMixerConfiguration[] getInputMixers()
{
return mDiscoveredMixers.toArray(
new DiscoveredMixer[ mDiscoveredMixers.size() ] );
return mInputMixers.toArray( new InputMixerConfiguration[ mInputMixers.size() ] );
}
public DiscoveredMixer getDiscoveredMixer( String name )
public InputMixerConfiguration getInputMixer( String name )
{
for( DiscoveredMixer mixer: mDiscoveredMixers )
for( InputMixerConfiguration mixer: mInputMixers )
{
if( mixer.getMixerName().contentEquals( name ) )
{
@@ -181,6 +173,11 @@ public class MixerManager
return null;
}
public MixerChannelConfiguration[] getOutputMixers()
{
return mOutputMixers.toArray( new MixerChannelConfiguration[ mOutputMixers.size() ] );
}
public Collection<MixerTunerDataLine> getMixerTunerDataLines()
{
@@ -204,21 +201,28 @@ public class MixerManager
if( mixer != null )
{
EnumSet<MixerChannel> channels = getSupportedChannels( mixer );
EnumSet<MixerChannel> inputChannels = getSupportedTargetChannels( mixer );
if( channels != null )
if( inputChannels != null )
{
mDiscoveredMixers.add(
new DiscoveredMixer( mixer, channels ) );
mInputMixers.add(
new InputMixerConfiguration( mixer, inputChannels ) );
sb.append( "\t[LOADED] Mixer:" + mixerInfo.getName() +
" CHANNELS: " + channels + "\n" );
sb.append( "\t[LOADED] Input: " + mixerInfo.getName() +
" CHANNELS: " + inputChannels + "\n" );
}
else
EnumSet<MixerChannel> outputChannels = getSupportedSourceChannels( mixer );
if( outputChannels != null )
{
sb.append( "\t[NOT LOADED] Mixer:" +
mixerInfo.getName() +
" - audio format not supported\n" );
for( MixerChannel channel: outputChannels )
{
mOutputMixers.add( new MixerChannelConfiguration( mixer, channel ) );
}
sb.append( "\t[LOADED] Output: " + mixerInfo.getName() +
" CHANNELS: " + outputChannels + "\n" );
}
}
else
@@ -249,10 +253,6 @@ public class MixerManager
break;
}
}
else
{
sb.append( "\t[NOT LOADED] Tuner:" + mixerInfo.getName() + " - couldn't get target data line\n" );
}
}
}
@@ -288,15 +288,17 @@ public class MixerManager
return retVal;
}
private EnumSet<MixerChannel> getSupportedChannels( Mixer mixer )
private EnumSet<MixerChannel> getSupportedTargetChannels( Mixer mixer )
{
DataLine.Info stereoInfo=
new DataLine.Info(TargetDataLine.class, STEREO );
new DataLine.Info(TargetDataLine.class,
AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO );
boolean stereoSupported = mixer.isLineSupported( stereoInfo );
DataLine.Info monoInfo=
new DataLine.Info(TargetDataLine.class, MONO );
new DataLine.Info(TargetDataLine.class,
AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO );
boolean monoSupported = mixer.isLineSupported( monoInfo );
@@ -317,6 +319,42 @@ public class MixerManager
return EnumSet.of( MixerChannel.MONO );
}
return null;
}
/**
* Returns enumset of SourceDataLine (audio output) channels
* (MONO and/or STEREO) supported by the mixer, or null if the mixer doesn't
* have any source data lines.
*/
private EnumSet<MixerChannel> getSupportedSourceChannels( Mixer mixer )
{
DataLine.Info stereoInfo=
new DataLine.Info(SourceDataLine.class,
AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO );
boolean stereoSupported = mixer.isLineSupported( stereoInfo );
DataLine.Info monoInfo=
new DataLine.Info(SourceDataLine.class,
AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO );
boolean monoSupported = mixer.isLineSupported( monoInfo );
if( stereoSupported && monoSupported )
{
return EnumSet.of( MixerChannel.MONO,
MixerChannel.STEREO );
}
else if( stereoSupported )
{
return EnumSet.of( MixerChannel.STEREO );
}
else if( monoSupported )
{
return EnumSet.of( MixerChannel.MONO );
}
return null;
}
@@ -354,17 +392,24 @@ public class MixerManager
"\n lineclass:" + lineInfo.getLineClass() +
"\n" );
}
Line.Info portInfo = new Line.Info( Port.class );
if( mixer.isLineSupported( portInfo ) )
{
sb.append( "**PORT LINE IS SUPPORTED BY THIS MIXER***\n" );
}
}
return sb.toString();
}
public class DiscoveredMixer
public class InputMixerConfiguration
{
private Mixer mMixer;
private EnumSet<MixerChannel> mChannels;
public DiscoveredMixer( Mixer mixer, EnumSet<MixerChannel> channels )
public InputMixerConfiguration( Mixer mixer, EnumSet<MixerChannel> channels )
{
mMixer = mixer;
mChannels = channels;
@@ -389,10 +434,14 @@ public class MixerManager
{
return mChannels.contains( channel );
}
}
public static void main( String[] args )
{
mLog.debug( "Starting ..." );
public String toString()
{
return getMixerName();
}
MixerManager.getInstance();
mLog.debug( "Finished!" );
}
}