11 Replies Latest reply on Mar 17, 2009 8:08 AM by PhHein

    Example: Code to generate audio tone

    793415
      This code shows how to generate and play a simple sinusoidal audio tone using the javax.sound.sampled API (see the generateTone() method for the details).

      This can be particularly useful for checking a PCs sound system, as well as testing/debugging other sound related applications (such as an audio trace app.).

      The latest version should be available at..
      <http://www.physci.org/test/sound/Tone.java>

      You can launch it directly from..
      <http://www.physci.org/test/oscilloscope/tone.jar>

      Hoping it may be of use.
      package org.physci.sound;
      
      import javax.sound.sampled.AudioFormat;
      import javax.sound.sampled.AudioSystem;
      import javax.sound.sampled.SourceDataLine;
      import javax.sound.sampled.LineUnavailableException;
      
      import java.awt.BorderLayout;
      import java.awt.Toolkit;
      import java.awt.Image;
      
      import java.awt.event.ActionListener;
      import java.awt.event.ActionEvent;
      
      import javax.swing.JFrame;
      import javax.swing.JPanel;
      import javax.swing.JButton;
      import javax.swing.JSlider;
      import javax.swing.JCheckBox;
      import javax.swing.SwingUtilities;
      import javax.swing.UIManager;
      
      import javax.swing.border.TitledBorder;
      
      import java.net.URL;
      
      /**
      Audio tone generator, using the Java sampled sound API.
      @author andrew Thompson
      @version 2007/12/6
      */
      public class Tone extends JFrame {
      
        static AudioFormat af;
        static SourceDataLine sdl;
      
        public Tone() {
          super("Audio Tone");
          // Use current OS look and feel.
              try {
                  UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                  SwingUtilities.updateComponentTreeUI(this);
              } catch (Exception e) {
                  System.err.println("Internal Look And Feel Setting Error.");
                  System.err.println(e);
              }
      
          JPanel pMain=new JPanel(new BorderLayout());
      
          final JSlider sTone=new JSlider(JSlider.VERTICAL,200,2000,441);
          sTone.setPaintLabels(true);
          sTone.setPaintTicks(true);
          sTone.setMajorTickSpacing(200);
          sTone.setMinorTickSpacing(100);
          sTone.setToolTipText(
            "Tone (in Hertz or cycles per second - middle C is 441 Hz)");
          sTone.setBorder(new TitledBorder("Frequency"));
          pMain.add(sTone,BorderLayout.CENTER);
      
          final JSlider sDuration=new JSlider(JSlider.VERTICAL,0,2000,1000);
          sDuration.setPaintLabels(true);
          sDuration.setPaintTicks(true);
          sDuration.setMajorTickSpacing(200);
          sDuration.setMinorTickSpacing(100);
          sDuration.setToolTipText("Duration in milliseconds");
          sDuration.setBorder(new TitledBorder("Length"));
          pMain.add(sDuration,BorderLayout.EAST);
      
          final JSlider sVolume=new JSlider(JSlider.VERTICAL,0,100,20);
          sVolume.setPaintLabels(true);
          sVolume.setPaintTicks(true);
          sVolume.setSnapToTicks(false);
          sVolume.setMajorTickSpacing(20);
          sVolume.setMinorTickSpacing(10);
          sVolume.setToolTipText("Volume 0 - none, 100 - full");
          sVolume.setBorder(new TitledBorder("Volume"));
          pMain.add(sVolume,BorderLayout.WEST);
      
          final JCheckBox cbHarmonic  = new JCheckBox( "Add Harmonic", true );
          cbHarmonic.setToolTipText("..else pure sine tone");
      
          JButton bGenerate = new JButton("Generate Tone");
          bGenerate.addActionListener( new ActionListener() {
              public void actionPerformed(ActionEvent ae) {
                try{
                  generateTone(sTone.getValue(),
                    sDuration.getValue(),
                    (int)(sVolume.getValue()*1.28),
                    cbHarmonic.isSelected());
                }catch(LineUnavailableException lue){
                  System.out.println(lue);
                }
              }
            } );
      
          JPanel pNorth = new JPanel(new BorderLayout());
          pNorth.add(bGenerate,BorderLayout.WEST);
      
          pNorth.add( cbHarmonic, BorderLayout.EAST );
      
          pMain.add(pNorth, BorderLayout.NORTH);
          pMain.setBorder( new javax.swing.border.EmptyBorder(5,3,5,3) );
      
          getContentPane().add(pMain);
          pack();
          setLocation(0,20);
          setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          setLocationRelativeTo(null);
      
          String address = "/image/tone32x32.png";
          URL url = getClass().getResource(address);
      
          if (url!=null) {
            Image icon = Toolkit.getDefaultToolkit().getImage(url);
            setIconImage(icon);
          }
        }
      
        /** Generates a tone.
        @param hz Base frequency (neglecting harmonic) of the tone in cycles per second
        @param msecs The number of milliseconds to play the tone.
        @param volume Volume, form 0 (mute) to 100 (max).
        @param addHarmonic Whether to add an harmonic, one octave up. */
        public static void generateTone(int hz,int msecs, int volume, boolean addHarmonic)
          throws LineUnavailableException {
      
          float frequency = 44100;
          byte[] buf;
          AudioFormat af;
          if (addHarmonic) {
            buf = new byte[2];
            af = new AudioFormat(frequency,8,2,true,false);
          } else {
            buf = new byte[1];
            af = new AudioFormat(frequency,8,1,true,false);
          }
          SourceDataLine sdl = AudioSystem.getSourceDataLine(af);
          sdl = AudioSystem.getSourceDataLine(af);
          sdl.open(af);
          sdl.start();
          for(int i=0; i<msecs*frequency/1000; i++){
            double angle = i/(frequency/hz)*2.0*Math.PI;
            buf[0]=(byte)(Math.sin(angle)*volume);
      
            if(addHarmonic) {
              double angle2 = (i)/(frequency/hz)*2.0*Math.PI;
              buf[1]=(byte)(Math.sin(2*angle2)*volume*0.6);
              sdl.write(buf,0,2);
            } else {
              sdl.write(buf,0,1);
            }
          }
          sdl.drain();
          sdl.stop();
          sdl.close();
        }
      
        public static void main(String[] args){
          Runnable r = new Runnable() {
            public void run() {
              Tone t = new Tone();
              t.setVisible(true);
            }
          };
          SwingUtilities.invokeLater(r);
        }
      }
        • 1. Re: Example: Code to generate audio tone
          843802
          Any reason why you call getSourceDataLine() twice? This is quite handy, thank you.
          • 2. Re: Example: Code to generate audio tone
            793415
            JLudwig wrote:
            Any reason why you call getSourceDataLine() twice? ..
            Oh wait, I know this one (..snaps fingers. yes) it is because I am a mor0n, and forgot the class level (static) attribute declared earlier in the source when I (re)declared the local attribute and called getSourceDatLine (which was redundant, given the next line, which as you point out also called getSourceDataLine).

            There are (at least) two ways to correct this problem.
            1) Remove the class level attribute and the second call.
            2) Remove the local attribute as well as the first call (all on the same code line).

            Method 1 makes more sense, unless you intend to refactor the code to only instantiate a single SDL for however many times the user presses (what was it? Oh yeah..) 'Generate Tone'.

            My 'excuse' for my odd programming is that this was 'hacked down' from a longer program to form an SSCCE. I should have paid more attention to the fine details (and perhaps run a lint checker on it).

            Thanks for pointing that out. I guess from the fact you spotted it, that you have already corrected the problem. That you thought to report it, gives me confidence that you 'will go far (and be well thought of, besides)' in the open source community.
            ..This is quite handy, thank you.
            You're welcome. Thanks for letting us know about the error (OK - the pointless redundancy). Thanks to your report, other people who see this code later, will not have to wonder what (the heck) I was thinking when I did that.

            Another thing I noted, now I run the source on a 400MHz laptop (it's hard times, here) is that the logic could be improved. At the speeds that my laptop can feed data into the SDL, the sound that comes out the speakers approximates the sound of flatulence (with embedded static, as a free bonus!).

            Edit 1: Changed one descriptive word so that it might get by the net-nanny.

            Edited by: AndrewThompson64 on Mar 27, 2008 11:09 PM
            • 3. Re: Example: Code to generate audio tone
              843802
              Please help I'm trying to use your code as a guide to create DTMF tones.
              The DTMF requirements are to produce 2 tones at different frequencies. If you do a search on wikipedia they have a nice little table.
              I can get the first tone to produce the right note and I can get the second note to produce the right note, individually. However when I do:
                          
              double angle = i / (frequency / hz1)* ttpi;
              double angle2 = i / (frequency / hz2) * ttpi;
              buf[0] = (byte) (Math.sin(angle) * volume);
              buf[1] = (byte) (Math.sin(2*angle2) * (volume));
              sdl.write(buf, 0, 2);
              I dont get the two tones to be produced in the proper manner. Can you help me adjust this code to work?
              • 4. Re: Example: Code to generate audio tone
                793415
                The problem is most probably in the code you did not write.

                Post an SSCCE* to a new thread, add some dukes, and if I see it and have time, I'll look into it (assuming somebody else does not see the code and solve it first).

                * Google it.
                • 5. Re: Example: Code to generate audio tone
                  843802
                  Started a new thread

                  Edited by: karlmarxxx on Apr 16, 2008 1:43 AM
                  • 6. Re: Example: Code to generate audio tone
                    843802
                    AndrewThompson64 wrote:
                    sTone.setToolTipText(
                    "Tone (in Hertz or cycles per second - middle C is 441 Hz)");
                    middle c is certainly not 441 hz. : )
                    frequencies
                    • 7. Re: Example: Code to generate audio tone
                      793415
                      TuringPest wrote:
                      AndrewThompson64 wrote:
                      sTone.setToolTipText(
                      "Tone (in Hertz or cycles per second - middle C is 441 Hz)");
                      middle c is certainly not 441 hz. : )
                      frequencies
                      That's a pity. In that case, amend that to..
                          sTone.setToolTipText(
                            "Tone (in Hertz or cycles per second)");
                      ;-)
                      • 8. Re: Example: Code to generate audio tone
                        843802
                        First of all I thank you for posting this code. It is very much what I need.

                        Now, when I run the program I get hard sounds at both beginning and end of the tone. Is this just a symptom of a crappy sound system, poor quality headphones, or is it avoidable by coding? I am trying to convert a Morse code training program that I wrote quite a few years ago in C. The hard sounds in the headphones would get very tiring very quickly. Any help you can give me would be greatly appreciated.

                        I think that I might have to ramp up the volume at the beginning of the tone and ramp it down again at the end. First, is this possible and second, is there a better way to eliminate the hard sounds?

                        Thanks.
                        • 9. Re: Example: Code to generate audio tone
                          843802
                          To avoid static:

                          In the generateTone method, you have put sdl.start() just after sdl.open(af).
                          Instead put sdl.start() and put it just after the for loop and just before sdl.drain().

                          Hope that helps.

                          Ram
                          --
                          • 10. Re: Example: Code to generate audio tone
                            843802
                            you might want to look at JSyn:

                            [http://www.softsynth.com/jsyn/|http://www.softsynth.com/jsyn/]




                            or leave the world altogether and look at SuperCollider :

                            [http://supercollider.sourceforge.net/|http://supercollider.sourceforge.net/]

                            sc3*2
                            • 11. Re: Example: Code to generate audio tone
                              PhHein
                              Please stop posting to years old threads. Locking.