AudioFormat fixed
[tu-mathvis-ws13:luisvera-tu-mathvis-ws13.git] / src / experiments / audio / Rooms.java
1 package experiments.audio;
2
3
4 import java.io.ByteArrayInputStream;
5 import java.io.ByteArrayOutputStream;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.awt.BorderLayout;
9 import java.awt.Color;
10 import java.awt.Container;
11 import java.awt.event.ActionEvent;
12 import java.awt.event.ActionListener;
13
14 import javax.sound.sampled.*;
15 import javax.swing.JButton;
16 import javax.swing.JFrame;
17
18 import experiments.audio.JRViewer;
19 import experiments.audio.JRViewer.ContentType;
20 import de.jtem.discretegroup.core.DiscreteGroupConstraint;
21 import de.jtem.discretegroup.core.DiscreteGroupSceneGraphRepresentation;
22 import de.jtem.discretegroup.core.DiscreteGroupSimpleConstraint;
23 import de.jtem.discretegroup.groups.*;
24 import de.jtem.discretegroup.util.TranslateTool;
25 import de.jreality.math.Matrix;
26 import de.jreality.math.MatrixBuilder;
27 import de.jreality.plugin.scene.Avatar;
28 import de.jreality.plugin.scene.Terrain;
29 import de.jreality.geometry.Primitives;
30 import de.jreality.scene.Appearance;
31 import de.jreality.scene.DirectionalLight;
32 import de.jreality.scene.Light;
33 import de.jreality.scene.Scene;
34 import de.jreality.scene.SceneGraphComponent;
35 import de.jreality.scene.SceneGraphPath;
36 import de.jreality.scene.SceneGraphPathObserver;
37 import de.jreality.scene.Transformation;
38 import de.jreality.shader.DefaultGeometryShader;
39 import de.jreality.shader.DefaultPolygonShader;
40 import de.jreality.shader.ShaderUtility;
41 import de.jreality.shader.ImageData;
42 import de.jreality.shader.TextureUtility;
43 import de.jreality.shader.TwoSidePolygonShader;
44 import de.jreality.tools.DraggingTool;
45 import de.jreality.tools.RotateTool;
46 import de.jreality.util.ArrayUtility;
47 import de.jreality.util.Input;
48 import de.jreality.util.SceneGraphUtility;
49 import de.jreality.audio.javasound.AudioInputStreamSource;
50 import de.jreality.audio.javasound.CachedAudioInputStreamSource;
51 import de.jreality.scene.AudioSource;
52 import de.jreality.scene.event.TransformationEvent;
53 import de.jreality.scene.event.TransformationListener;
54
55 public class Rooms  {
56         
57          static int whichGroup = 7; // 0, 5 = XX, 6 = **, 7 = *2222 (the solid room), 8 = 22*, 9 = 22X; 
58          static DiscreteGroupSceneGraphRepresentation dgsgr;
59          static SceneGraphComponent room;
60          static boolean roomIsVisible = false;
61          static SceneGraphComponent speaker = SceneGraphUtility.createFullSceneGraphComponent("speaker");
62          static boolean speakersAreVisible = false;
63          private static WallpaperGroup gr;
64          private static SceneGraphComponent base = new SceneGraphComponent();
65          
66          
67          private static String whichSource = "micro"; // "NYH1V5_SO.wav";
68          private static int count = 80;
69          private static double side = 0.999;
70          private static double ceiling = 1;
71          private static double vertical = 1.65;
72          private static double[] floor = {0,0,1.2}, vert = {-0.5,-0.5,-ceiling/2};
73          
74          static boolean running;
75          static private ByteArrayOutputStream out = new ByteArrayOutputStream();
76          static private int latencyParameter = 5; // the bigger the less latency (80 800 used to work)
77          static private int bufferParameter = 400; // sets the copies of the buffer to be stored into the buffer2; should be multiple of the latency
78         
79         
80          //TODO "map" of the sound, i.e., graphic of the amount of sound we hear depending on the position and the angle of vision
81          // -Xms512M -Xmx1024M as VM Argument for the run configuration is supposed to improve the use of memory
82          
83          public static SceneGraphComponent getRooms() throws Exception {
84                  
85                 /**SceneGraphComponent SGCList = SceneGraphUtility.createFullSceneGraphComponent("list");*/
86                 SceneGraphComponent world = SceneGraphUtility.createFullSceneGraphComponent("world");
87                 
88                 room = SceneGraphUtility.createFullSceneGraphComponent("basic room");
89                 room.setGeometry(Primitives.texturedBox(side, side, ceiling));
90                 MatrixBuilder.euclidean().translateFromTo(vert, floor).assignTo(room);
91                 room.setPickable(false);
92                 room.setVisible(roomIsVisible);
93                 //world.addChild(room);
94                 
95                 Appearance roomapp = room.getAppearance();
96                 DefaultGeometryShader dgs;
97             DefaultPolygonShader dps;   
98             dgs = ShaderUtility.createDefaultGeometryShader(roomapp, true);
99             dps = (DefaultPolygonShader) dgs.createPolygonShader("default");
100             dgs.setShowLines(false);
101             dgs.setShowPoints(false);
102             dps.setDiffuseColor(Color.WHITE);
103             dps.setTransparency(0.9);
104             ImageData id = ImageData.load(Input.getInput("alhambra.png"));
105             dps.createTexture2d().setImage(id);
106             Matrix tm = MatrixBuilder.euclidean().scale(side, side, ceiling).translateFromTo(vert, floor).rotateZ(Math.PI).getMatrix();
107             dps.getTexture2d().setTextureMatrix(tm);
108             // TODO fix the textures, distinguish between pairs of walls and even make the 3D whispering gallery
109             
110                 speaker.setGeometry(Primitives.box(0.1, 0.1, 0.1, true));
111                 speaker.setPickable(false);
112                 speaker.setVisible(speakersAreVisible);
113                 /**TranslateTool tt = new TranslateTool();
114                 speaker.addTool(tt);*/
115                 // the conical sound directions are the red AND white faces' directions, and the cardioid is the red face's direction
116                 world.addChild(speaker);
117                 
118                 // TODO 3D groups implementation
119                 gr = WallpaperGroup.instanceOfGroup(whichGroup);
120                 dgsgr = new DiscreteGroupSceneGraphRepresentation(gr);
121                 DiscreteGroupConstraint dgc = new DiscreteGroupSimpleConstraint(count);
122                 gr.setConstraint(dgc); 
123                 dgsgr.setWorldNode(world);
124                 dgsgr.update();
125                 MatrixBuilder.euclidean().rotateX(-Math.PI/2).assignTo(dgsgr.getSceneGraphRepn());
126                 dgsgr.getSceneGraphRepn().addChild(room);
127                 
128                 return dgsgr.getSceneGraphRepn();
129         }
130          
131          public static void updateGroup() throws Exception{
132                 base.removeAllChildren();
133                 base.addChild(getRooms());
134                 System.out.println(gr.getName());
135          }
136          
137          public static void updateVisibility() {
138                  room.setVisible(roomIsVisible);
139                  speaker.setVisible(speakersAreVisible);
140          }
141         
142         private static AudioFormat format() {
143             float sampleRate = 44100;
144             int sampleSizeInBits = 16;
145             int channels = 1;
146             boolean signed = true;
147             boolean bigEndian = false;
148             return new AudioFormat(sampleRate,sampleSizeInBits, channels, signed, bigEndian);
149           }
150         
151         public static void captureAudio() {
152                 try {
153                     DataLine.Info info = new DataLine.Info(TargetDataLine.class, format());
154                     final TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
155                     line.open(format());
156                     line.start();
157                     Runnable runner = new Runnable() { 
158                         public void run() { 
159                                 int bufferCount = 0;
160                                 int bufferSize = (int) format().getSampleRate()*format().getFrameSize() / latencyParameter;     
161                                 byte buffer[] = new byte[bufferSize];;
162                                         byte buffer2[] = new byte[bufferParameter*bufferSize];
163                                         InputStream input;
164                                         AudioInputStream stream;
165                                         AudioSource source;
166                                 running = true;
167                                         // TODO review the buffer system, because sometimes the application collapses
168                                 try {
169                                         while (running) {
170                                                         int count = line.read(buffer, 0, buffer.length);
171                                                         out.write(buffer, 0, count);
172                                                         for (int j=0; j < buffer.length; j++ ){
173                                                                 buffer2[j + bufferCount*buffer.length] = buffer[j];
174                                                         }
175                                                         
176                                                         bufferCount = (bufferCount + 1) % bufferParameter;
177                                                         //System.out.println(count);
178                                                         //System.out.println(bufferCount);
179                                                         if (bufferCount == 1) {
180                                                                 // sadly we have to do this, because AudioSource does not admit simultaneous reproductions
181                                                                 System.out.println("Buffer restarted");
182                                                                 input = new ByteArrayInputStream(buffer2);
183                                                                 stream = new AudioInputStream(input, format(), buffer2.length);
184                                                                 source = new AudioInputStreamSource("micro", stream);
185                                                                 speaker.setAudioSource(source); 
186                                                                 source.start();
187                                                         }
188                                                         out.reset();
189                                         }               
190                                         out.close();
191                                 } 
192                                 catch (IOException e) {
193                                         System.err.println("I/O problems: " + e);
194                                         System.exit(-1);
195                                 }
196                         }
197                     };
198                       
199                       Thread captureThread = new Thread(runner);
200                       captureThread.start();
201                     } catch (LineUnavailableException e) {
202                       System.err.println("Line unavailable: " + e);
203                       System.exit(-2);
204                     }
205                   }
206
207         
208         public static JFrame MicrophoneFrame() {
209                   
210                 JFrame frame = new JFrame("Microphone");
211                 Container content = frame.getContentPane();
212
213                 final JButton capture = new JButton("Capture audio");
214                 final JButton stop = new JButton("Stop capture");
215
216             capture.setEnabled(true);
217             stop.setEnabled(false);
218
219             ActionListener captureListener =   new ActionListener() {
220                 public void actionPerformed(ActionEvent e) {
221                         capture.setEnabled(false);
222                         stop.setEnabled(true);
223                         captureAudio();
224                 }
225             };
226                    
227             capture.addActionListener(captureListener);
228                 content.add(capture, BorderLayout.NORTH);
229
230                 ActionListener stopListener = new ActionListener() {
231                         public void actionPerformed(ActionEvent e) {
232                                 capture.setEnabled(true);
233                         stop.setEnabled(false);
234                         running = false;
235                         }
236                 };
237                     
238                 stop.addActionListener(stopListener);
239                 content.add(stop, BorderLayout.CENTER);
240                             
241                 return frame;
242         }
243         
244         public static void constructJRV() throws Exception{
245                 
246                 final JRViewer jrv = new JRViewer(true);
247                 jrv.addAudioSupport();
248                 jrv.registerPlugin(Avatar.class);
249                 jrv.registerPlugin(Terrain.class);
250                 //jrv.addContentUI();
251                 base.addChild(getRooms());
252                 jrv.setContent(base);
253                 jrv.registerPlugin(new OrbifoldOptions());
254                 jrv.startup();
255                 
256                 jrv.setShowPanelSlots(true, false, false, false);
257                 jrv.getPlugin(Avatar.class).setAvatarPosition(0, 0, 0);
258                 jrv.getPlugin(Avatar.class).setNavigationSpeed(0.08);
259                 jrv.getPlugin(Avatar.class).getAvatar().setLight(new DirectionalLight());
260                                         
261                 SceneGraphPath scp = new SceneGraphPath(jrv.getViewer().getSceneRoot(), jrv.getPlugin(Avatar.class).getAvatar());
262                 SceneGraphPathObserver scpo = new SceneGraphPathObserver(scp);
263                 TransformationListener listener = new TransformationListener() {
264                         public void transformationMatrixChanged(TransformationEvent ev) {
265                                 // optimize this mess, if possible
266                                 Matrix m = new Matrix(jrv.getPlugin(Avatar.class).getAvatar().getTransformation());
267                                 double[] aux = m.getColumn(1);
268                                 m.setColumn(1, m.getColumn(2));
269                                 m.setColumn(2, aux);
270                                 aux = m.getRow(1);
271                                 m.setRow(1, m.getRow(2));
272                                 m.setRow(2, aux);
273                                 m.setEntry(1, 3, -m.getEntry(1, 3));
274                                 m.setEntry(0, 1, m.getEntry(1, 0));
275                                 m.setEntry(1, 0, -m.getEntry(0, 1));
276                                 m.setEntry(2, 3, m.getEntry(2, 3) + vertical);
277                                 m.multiplyOnRight(MatrixBuilder.euclidean().rotateX(-Math.PI/2).getMatrix());
278                                 Transformation trans = new Transformation();
279                                 m.assignTo(trans);
280                                 //System.out.println(m);
281                                 speaker.setTransformation(trans);
282                         }
283                 };              
284                 scpo.addTransformationListener(listener);
285         }
286         
287         
288         public static void main(String[] args) throws Exception {
289                 
290                 Rooms.constructJRV();
291         
292                 if (whichSource.equals("micro")){
293                         
294                         JFrame frame = MicrophoneFrame();
295                         frame.pack();
296                         frame.setVisible(true);
297
298                  }
299                 
300                  else {
301                          InputStream wavFile = Rooms.class.getResourceAsStream(whichSource);
302                          AudioSource source = new CachedAudioInputStreamSource(whichSource, Input.getInput(whichSource, wavFile), true); 
303                          speaker.setAudioSource(source);
304                          source.start();
305                  }
306         }               
307 }