View Javadoc
1   package fr.ifremer.tutti.ui.swing.util;
2   
3   /*
4    * #%L
5    * Tutti :: UI
6    * %%
7    * Copyright (C) 2012 - 2016 Ifremer
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/gpl-3.0.html>.
22   * #L%
23   */
24  
25  import com.google.common.base.Optional;
26  import fr.ifremer.tutti.util.BeepFrequency;
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  
30  import javax.sound.sampled.AudioFormat;
31  import javax.sound.sampled.AudioInputStream;
32  import javax.sound.sampled.AudioSystem;
33  import javax.sound.sampled.DataLine;
34  import javax.sound.sampled.LineUnavailableException;
35  import javax.sound.sampled.SourceDataLine;
36  import javax.sound.sampled.UnsupportedAudioFileException;
37  import java.io.IOException;
38  import java.util.ArrayList;
39  import java.util.List;
40  
41  /**
42   * @author Kevin Morin (Code Lutin)
43   * @since 4.3
44   */
45  public class SoundUtil {
46  
47      /** Logger. */
48      private static final Log log = LogFactory.getLog(SoundUtil.class);
49  
50      public static String SOUNDS_DIRECTORY = "/sounds";
51  
52      public static float SAMPLE_RATE = 8000f;
53  
54      public static void beep(BeepFrequency beepFrequency) {
55          beep(beepFrequency, 1);
56      }
57  
58      public static void beep(BeepFrequency beepFrequency, int number) {
59  
60          try {
61              for (int i = 0; i < number; i++) {
62                  tone(beepFrequency.getFrequency(), 500, 1.0);
63              }
64  
65          } catch (LineUnavailableException e) {
66              if (log.isErrorEnabled()) {
67                  log.error("Error while playing a beep", e);
68              }
69          }
70      }
71  
72      public static void tone(int hz, int msecs, double vol) throws LineUnavailableException {
73          byte[] buf = new byte[1];
74          AudioFormat af = new AudioFormat(SAMPLE_RATE, // sampleRate
75                  8,           // sampleSizeInBits
76                  1,           // channels
77                  true,        // signed
78                  false);      // bigEndian
79          try (SourceDataLine sdl = AudioSystem.getSourceDataLine(af)) {
80              sdl.open(af);
81              sdl.start();
82              for (int i = 0, end = msecs * 8; i < end; i++) {
83                  double angle = i / (SAMPLE_RATE / hz) * 2.0 * Math.PI;
84                  buf[0] = (byte) (Math.sin(angle) * 127.0 * vol);
85                  sdl.write(buf, 0, 1);
86              }
87              sdl.drain();
88              sdl.stop();
89          }
90      }
91  
92      public static void readNumber(double number, Optional<String> unit) {
93  
94          try {
95  
96              int thousands = (int) (number / 1000);
97              int hundreds = (int) (number % 1000) / 100;
98              int tensAndUnits = (int) number % 100;
99              int decimal = (int) (number * 10) % 10;
100 
101             if (log.isDebugEnabled()) {
102                 log.debug(thousands + " " + hundreds + " " + tensAndUnits + " " + decimal);
103             }
104 
105             List<AudioInputStream> audioInputStreams = new ArrayList<>();
106 
107             addSound(audioInputStreams, thousands, 1000);
108             addSound(audioInputStreams, hundreds, 100);
109             if (tensAndUnits != 0 || thousands == 0 && hundreds == 0) {
110                 addSound(audioInputStreams, tensAndUnits);
111             }
112             if (decimal > 0) {
113                 addSound(audioInputStreams, ",");
114                 addSound(audioInputStreams, decimal);
115             }
116             if (unit.isPresent()) {
117                 addSound(audioInputStreams, unit.get());
118             }
119 
120             if (!audioInputStreams.isEmpty()) {
121 
122                 AudioFormat format = audioInputStreams.get(0).getFormat();
123                 DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
124                 try (SourceDataLine audioLine = (SourceDataLine) AudioSystem.getLine(info)) {
125                     audioLine.open(format);
126                     audioLine.start();
127 
128                     byte[] bytesBuffer = new byte[4096];
129 
130                     while (!audioInputStreams.isEmpty()) {
131                         try (AudioInputStream audioInputStream = audioInputStreams.remove(0)) {
132                             int bytesRead;
133                             while ((bytesRead = audioInputStream.read(bytesBuffer)) != -1) {
134                                 audioLine.write(bytesBuffer, 0, bytesRead);
135                             }
136                         }
137                     }
138 
139                     audioLine.drain();
140 
141                 }
142             }
143 
144         } catch (Exception e) {
145             if (log.isErrorEnabled()) {
146                 log.error("Error while reading " + number + " " + unit, e);
147             }
148         }
149 
150     }
151 
152     protected static long addSound(List<AudioInputStream> inputStreams, int number) throws IOException, UnsupportedAudioFileException {
153         return addSound(inputStreams, number, 1);
154     }
155 
156     protected static long addSound(List<AudioInputStream> inputStreams, int number, int suffix) throws IOException, UnsupportedAudioFileException {
157         long length = 0;
158 
159         if (number > 1 || suffix == 1) {
160             if (log.isDebugEnabled()) {
161                 log.debug("-> add " + number + ".wav");
162             }
163             AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(SoundUtil.class.getResource(SOUNDS_DIRECTORY + "/" + number + ".wav"));
164             inputStreams.add(audioInputStream);
165             length += audioInputStream.getFrameLength();
166         }
167 
168         if (number >= 1 && suffix > 1) {
169             if (log.isDebugEnabled()) {
170                 log.debug("-> add " + suffix + ".wav");
171             }
172             AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(SoundUtil.class.getResource(SOUNDS_DIRECTORY + "/" + suffix + ".wav"));
173             inputStreams.add(audioInputStream);
174             length += audioInputStream.getFrameLength();
175         }
176 
177         return length;
178     }
179 
180     protected static long addSound(List<AudioInputStream> inputStreams, String name) throws IOException, UnsupportedAudioFileException {
181         if (log.isDebugEnabled()) {
182             log.debug("add " + name + ".wav");
183         }
184         AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(SoundUtil.class.getResource(SOUNDS_DIRECTORY + "/" + name + ".wav"));
185         inputStreams.add(audioInputStream);
186         return audioInputStream.getFrameLength();
187     }
188 
189 }