Search code examples
javamacosswingaudiosettings

What Java function can I use on macOS to play a proper alert sound upon the proper device, at the proper volume?


I want my Java application to respect the macOS settings at System Settings... › Sound › Sound effects when it plays alert sounds.

For example, the program will play the user's configured "alert sound" whenever someone presses backspace with the cursor in the leftmost position within a jTextField.

There are similar needs for playing the same alert sound within other features of my application. But I've not yet found a library that lets my application play the system's proper "alert sound", upon the proper device, at the proper volume.

I want for my application to respect the following macOS system settings:

  • Alert sound (sound name)
  • Play sound effects through (device name)
  • Alert volume (slider setting)

How can I do this?


Solution

  • I've experimented with Toolkit.beep on macOS and it seems to do what you'd want. I haven't verified that JTextField calls Toolkit.beep but it appears to be calling it. One could look at the openjdk source code and probably get a better idea.

    On Windows, JTextField seems to be using the Default Beep sound. Toolkit.beep as well.

    So long as the default Toolkit is ok to use (it is for me), you can place the following in your app where you need to alert the user.

    Toolkit tk = Toolkit.getDefaultToolkit();
    tk.beep();
    

    Here's my MCV example.

    import java.awt.event.*;
    import java.awt.BorderLayout;
    import java.awt.Toolkit;
    import javax.swing.*;
    
    public class Alert extends JFrame {
       private Toolkit tk = Toolkit.getDefaultToolkit();
       public Alert () {
          setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          JTextField tf = new JTextField();
          JButton bbeep = new JButton(new AbstractAction() {
             public void actionPerformed (ActionEvent e) {tk.beep();}
          });
          setLayout(new BorderLayout());
          getContentPane().add(tf, BorderLayout.NORTH);
          getContentPane().add(bbeep, BorderLayout.SOUTH);
          pack();
          setVisible(true);
       }
    
       public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
             public void run() { new Alert(); }
          });
       }
    }