mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
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:
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
68
src/source/mixer/MixerChannelConfiguration.java
Normal file
68
src/source/mixer/MixerChannelConfiguration.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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!" );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user