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:
@@ -4,10 +4,21 @@ import java.awt.Color;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
|
||||
import javax.sound.sampled.BooleanControl;
|
||||
import javax.sound.sampled.FloatControl;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
@@ -16,68 +27,329 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import settings.SettingsManager;
|
||||
import source.mixer.MixerChannelConfiguration;
|
||||
import source.mixer.MixerManager;
|
||||
import audio.output.AudioOutput;
|
||||
|
||||
public class AudioPanel extends JPanel implements Listener<AudioEvent>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Logger mLog = LoggerFactory.getLogger( AudioPanel.class );
|
||||
|
||||
private static ImageIcon MUTED_ICON = SettingsManager.getScaledIcon(
|
||||
new ImageIcon( "images/audio_muted.png" ), 38 );
|
||||
private static ImageIcon UNMUTED_ICON = SettingsManager.getScaledIcon(
|
||||
new ImageIcon( "images/audio_unmuted.png" ), 38 );
|
||||
|
||||
private SettingsManager mSettingsManager;
|
||||
private IAudioController mController;
|
||||
private MuteButton mMuteButton;
|
||||
|
||||
private JButton mMuteButton;
|
||||
private AudioChannelsPanel mAudioChannelsPanel;
|
||||
|
||||
public AudioPanel( SettingsManager settingsManager, IAudioController controller )
|
||||
{
|
||||
mSettingsManager = settingsManager;
|
||||
mController = controller;
|
||||
|
||||
mMuteButton = new MuteButton( mController );
|
||||
mController.addControllerListener( this );
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]") );
|
||||
setLayout( new MigLayout( "insets 0 0 0 0", "[]0[grow,fill]", "[fill]0[]") );
|
||||
setBackground( Color.BLACK );
|
||||
|
||||
add( mMuteButton, "span" );
|
||||
mMuteButton = new MuteButton();
|
||||
mMuteButton.setBackground( getBackground() );
|
||||
add( mMuteButton );
|
||||
|
||||
for( String channel: mController.getAudioChannels() )
|
||||
{
|
||||
add( new AudioChannelPanel( mSettingsManager, mController, channel ), "span" );
|
||||
}
|
||||
mAudioChannelsPanel = new AudioChannelsPanel( mSettingsManager, mController );
|
||||
|
||||
add( mAudioChannelsPanel );
|
||||
|
||||
addMouseListener( new MouseSelectionListener() );
|
||||
}
|
||||
|
||||
public int getPreferredHeight()
|
||||
{
|
||||
return mMuteButton.getHeight() +
|
||||
( mController.getAudioChannels().size() * 20 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( AudioEvent event )
|
||||
{
|
||||
if( event.getType() == AudioEvent.Type.AUDIO_CONFIGURATION_CHANGED )
|
||||
switch( event.getType() )
|
||||
{
|
||||
mLog.debug( "Audio configuration change detected" );
|
||||
case AUDIO_CONFIGURATION_CHANGE_STARTED:
|
||||
break;
|
||||
case AUDIO_CONFIGURATION_CHANGE_COMPLETE:
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
remove( mAudioChannelsPanel );
|
||||
|
||||
mAudioChannelsPanel = new AudioChannelsPanel(
|
||||
mSettingsManager, mController );
|
||||
|
||||
add( mAudioChannelsPanel );
|
||||
|
||||
mAudioChannelsPanel.repaint();
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
} );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio output mute control menu item.
|
||||
*/
|
||||
public class AudioOutputMuteItem extends JMenuItem
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private AudioOutput mAudioOutput;
|
||||
|
||||
public AudioOutputMuteItem( AudioOutput audioOutput )
|
||||
{
|
||||
super( audioOutput.isMuted() ? "Unmute" : "Mute" );
|
||||
|
||||
mAudioOutput = audioOutput;
|
||||
|
||||
addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
mAudioOutput.setMuted( !mAudioOutput.isMuted() );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse listener
|
||||
*/
|
||||
public class MouseSelectionListener implements MouseListener
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent event )
|
||||
{
|
||||
if( SwingUtilities.isRightMouseButton( event ) )
|
||||
{
|
||||
JPopupMenu popup = new JPopupMenu();
|
||||
|
||||
/* Audio mixer/output selection menus */
|
||||
JMenu outputMenu = new JMenu( "Audio Output" );
|
||||
|
||||
MixerChannelConfiguration[] mixerConfigurations =
|
||||
MixerManager.getInstance().getOutputMixers();
|
||||
|
||||
for( MixerChannelConfiguration mixerConfig: mixerConfigurations )
|
||||
{
|
||||
MixerSelectionItem mixerItem = new MixerSelectionItem( mixerConfig );
|
||||
|
||||
try
|
||||
{
|
||||
MixerChannelConfiguration current = mController.getMixerChannelConfiguration();
|
||||
|
||||
if( current != null && current.equals( mixerConfig ) )
|
||||
{
|
||||
mixerItem.setSelected( true );
|
||||
}
|
||||
}
|
||||
catch( AudioException e )
|
||||
{
|
||||
mLog.debug( "Error while detecting current mixer "
|
||||
+ "channel configuration", e );
|
||||
}
|
||||
|
||||
outputMenu.add( mixerItem );
|
||||
}
|
||||
|
||||
popup.add( outputMenu );
|
||||
|
||||
/* Audio output mute and volume control */
|
||||
for( AudioOutput output: mController.getAudioOutputs() )
|
||||
{
|
||||
JMenu menu = new JMenu( "Channel: " + output.getChannelName() );
|
||||
|
||||
menu.add( new AudioOutputMuteItem( output ) );
|
||||
|
||||
if( output.hasGainControl() )
|
||||
{
|
||||
JMenu volume = new JMenu( "Volume" );
|
||||
volume.add( new VolumeSlider( output.getGainControl() ) );
|
||||
menu.add( volume );
|
||||
}
|
||||
|
||||
popup.add( menu );
|
||||
}
|
||||
|
||||
popup.show( event.getComponent(), event.getX(), event.getY() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed( MouseEvent e ) {}
|
||||
@Override
|
||||
public void mouseReleased( MouseEvent e ) {}
|
||||
@Override
|
||||
public void mouseEntered( MouseEvent e ) {}
|
||||
@Override
|
||||
public void mouseExited( MouseEvent e ) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio volume (gain) adjustment slider control
|
||||
*/
|
||||
public class VolumeSlider extends JSlider
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private FloatControl mFloatControl;
|
||||
|
||||
public VolumeSlider( FloatControl control )
|
||||
{
|
||||
super( 0, 100, 0 );
|
||||
|
||||
setMajorTickSpacing( 25 );
|
||||
setMinorTickSpacing( 5 );
|
||||
setPaintTicks( true );
|
||||
setPaintLabels( true );
|
||||
|
||||
mFloatControl = control;
|
||||
|
||||
setValue( getIntegerValue( mFloatControl.getValue() ) );
|
||||
|
||||
addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent event )
|
||||
{
|
||||
mFloatControl.shift( mFloatControl.getValue(),
|
||||
getFloatValue( VolumeSlider.this.getValue() ),
|
||||
1000 );
|
||||
}
|
||||
} );
|
||||
|
||||
addMouseListener( new MouseListener()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent event )
|
||||
{
|
||||
if( event.getClickCount() == 2 )
|
||||
{
|
||||
VolumeSlider.this.setValue( 50 );
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseReleased( MouseEvent arg0 ) {}
|
||||
public void mousePressed( MouseEvent arg0 ) {}
|
||||
public void mouseExited( MouseEvent arg0 ) {}
|
||||
public void mouseEntered( MouseEvent arg0 ) {}
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the integer value to a floating point value to use in the
|
||||
* float control. Assumes an integer value of 50 is the 0.0 dB mid
|
||||
* point (ie no gain ) value.
|
||||
*/
|
||||
private int getIntegerValue( float value )
|
||||
{
|
||||
if( value == 0.0f )
|
||||
{
|
||||
return 50;
|
||||
}
|
||||
else if( value < 0.0f )
|
||||
{
|
||||
float ratio = value / mFloatControl.getMinimum();
|
||||
|
||||
return 50 - (int)( ratio * 50.0f );
|
||||
}
|
||||
else
|
||||
{
|
||||
float ratio = value / mFloatControl.getMaximum();
|
||||
|
||||
return 50 + (int)( ratio * 50.0f );
|
||||
}
|
||||
}
|
||||
|
||||
private float getFloatValue( int value )
|
||||
{
|
||||
if( value == 50 )
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
else if( value < 50 )
|
||||
{
|
||||
return (float)( 50 - value ) / 50.0f * mFloatControl.getMinimum();
|
||||
}
|
||||
else
|
||||
{
|
||||
return (float)( value - 50 ) / 50.0f * mFloatControl.getMaximum();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixer/Channel configuration selection item
|
||||
*/
|
||||
public class MixerSelectionItem extends JMenuItem
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private MixerChannelConfiguration mConfiguration;
|
||||
|
||||
public MixerSelectionItem( MixerChannelConfiguration config )
|
||||
{
|
||||
super( config.toString() );
|
||||
|
||||
mConfiguration = config;
|
||||
|
||||
addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
try
|
||||
{
|
||||
mController.setMixerChannelConfiguration( mConfiguration );
|
||||
}
|
||||
catch ( AudioException e1 )
|
||||
{
|
||||
mLog.error( "Couldn't set mixer channel configuration "
|
||||
+ "to: " + mConfiguration.toString() );
|
||||
|
||||
JOptionPane.showMessageDialog( MixerSelectionItem.this,
|
||||
"Couldn't set [" + mConfiguration.toString() +
|
||||
"] as the audio output device" );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mute button to mute all audio output channels exposed by the audio
|
||||
* controller
|
||||
*/
|
||||
public class MuteButton extends JButton
|
||||
{
|
||||
private IAudioController mController;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private boolean mMuted = false;
|
||||
|
||||
public MuteButton( IAudioController controller )
|
||||
public MuteButton()
|
||||
{
|
||||
super( "Mute Audio" );
|
||||
|
||||
setBackground( Color.GREEN );
|
||||
|
||||
mController = controller;
|
||||
setIcon( UNMUTED_ICON );
|
||||
setBorderPainted( false );
|
||||
|
||||
addActionListener( new ActionListener()
|
||||
{
|
||||
@@ -86,31 +358,17 @@ public class AudioPanel extends JPanel implements Listener<AudioEvent>
|
||||
{
|
||||
mMuted = !mMuted;
|
||||
|
||||
for( String channel: mController.getAudioChannels() )
|
||||
for( AudioOutput output: mController.getAudioOutputs() )
|
||||
{
|
||||
try
|
||||
{
|
||||
BooleanControl control = mController.getMuteControl( channel );
|
||||
|
||||
if( control != null )
|
||||
{
|
||||
control.setValue( mMuted );
|
||||
}
|
||||
}
|
||||
catch ( AudioException e1 )
|
||||
{
|
||||
mLog.error( "Couldn't obtain mute control for "
|
||||
+ "channel [" + channel + "] from audio manager", e1 );
|
||||
}
|
||||
output.setMuted( mMuted );
|
||||
}
|
||||
|
||||
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
MuteButton.this.setText( ( mMuted ? "Unmute Audio" : "Mute Audio" ) );
|
||||
MuteButton.this.setBackground( ( mMuted ? Color.RED : Color.GREEN ) );
|
||||
setIcon( mMuted ? MUTED_ICON : UNMUTED_ICON );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user