Adding config file for sharing.
[iot2.git] / benchmarks / drivers / Java / IHome / IHome.java
1 package iotcode.IHome;
2
3 // IoT Packages
4 import iotcode.interfaces.*;
5 import iotcode.annotation.*;
6 import iotruntime.IoTUDP;
7 import iotruntime.IoTTCP;
8 import iotruntime.slave.IoTSet;
9 import iotruntime.slave.IoTDeviceAddress;
10
11 // RMI Packages
12 import java.rmi.Remote;
13 import java.rmi.RemoteException;
14
15 // Checker annotations
16 //import iotchecker.qual.*;
17
18 // Standard Java Packages
19 import java.util.concurrent.atomic.AtomicBoolean;
20 import java.util.Map;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Set;
24 import java.util.List;
25 import java.util.ArrayList;
26 import java.io.InputStreamReader;
27 import java.io.BufferedReader;
28 import java.io.PrintWriter;
29 import java.io.ByteArrayInputStream;
30 import java.util.LinkedList;
31 import java.util.concurrent.Semaphore;
32 import java.util.concurrent.CopyOnWriteArrayList;
33
34 // TODO: REMOVE THIS
35 import java.util.Random;
36 import java.io.File;
37
38 public class IHome implements Speaker {
39
40
41     /*******************************************************************************************************************************************
42     **  Constants
43     *******************************************************************************************************************************************/
44
45     public static final float VOLUME_MUTED_VALUE_DB = (float) (-144.0);
46     public static final float VOLUME_MIN_VALUE_DB = (float) (-30.0);
47     public static final float VOLUME_MAX_VALUE_DB = (float) (-0.0);
48     public static final float DEFAULT_VOLUME = (float) (30.0);
49
50     public static final long SEQUENCE_NUMBER_INITIAL_VALUE = 18086;
51     public static final long SEQUENCE_NUMBER_WRAP_AROUND = 32768L;
52     public static final long RTP_TIMESTAMP_INITIAL_VALUE = 3132223670L;
53     public static final long RTP_TIMESTAMP_INCREMENT_VALUE = 352L;
54     public static final long SOURCE_ID = 1326796157;
55     public static final long SEQUENCE_ID = 0x86b27741;
56         // TODO: REMOVE THIS
57         public static final String MUSIC_FILE_DIRECTORY = "./music/";   // file that the music files are in
58
59     private IoTDeviceAddress tcpAddress = null;
60     private IoTDeviceAddress myAddress = null;
61     private IoTDeviceAddress controlAddress = null;
62     private IoTDeviceAddress timingAddress = null;
63     private IoTDeviceAddress serverAddress = null;
64
65     private IoTTCP iHomeTCPConnection = null;
66
67     private AtomicBoolean driverIsShuttingDown = new AtomicBoolean();
68     private boolean didClose = false;
69
70     private AtomicBoolean didEnd = new AtomicBoolean();
71     private AtomicBoolean playbackStarted = new AtomicBoolean();
72     private AtomicBoolean playbackFileIsDone = new AtomicBoolean();
73     private AtomicBoolean isDoneEnding = new AtomicBoolean();
74     private AtomicBoolean playbackState = new AtomicBoolean();
75
76     private AtomicBoolean didInit = new AtomicBoolean();
77     private AtomicBoolean playbackAboutToStart = new AtomicBoolean();
78     private AtomicBoolean settingVolume = new AtomicBoolean();
79     private AtomicBoolean playbackAboutToStop = new AtomicBoolean();
80
81
82
83     private long sequenceNumber = SEQUENCE_NUMBER_INITIAL_VALUE;
84     private long rtpTimestamp = RTP_TIMESTAMP_INITIAL_VALUE;
85
86     private long currentPlaybackTime = 0;
87     static Semaphore currentPlaybackTimeMutex = new Semaphore(1);
88
89     private long desiredPlaybackTime = 0;
90     static Semaphore desiredPlaybackTimeMutex = new Semaphore(1);
91
92
93     private String connectionURL = "";
94     private float currentVolume = DEFAULT_VOLUME;
95     private LinkedList audioLinkedList = new LinkedList();
96
97     private List < SpeakerSmartCallback > callbackList = new CopyOnWriteArrayList< SpeakerSmartCallback > ();
98
99     /*******************************************************************************************************************************************
100     **  Threads
101     *******************************************************************************************************************************************/
102     private Thread timingThread = null;
103     private Thread audioThread = null;
104     private Thread controlThread = null;
105     private Thread monitorThread = null;
106
107
108     @config private IoTSet<IoTDeviceAddress> speakerAddresses;
109
110         public IHome(IoTSet<IoTDeviceAddress> devAddresses) {
111         this();
112                 speakerAddresses = devAddresses;
113     }
114         
115     public IHome() {
116         didInit.set(false);
117         playbackAboutToStart.set(false);
118         settingVolume.set(false);
119     }
120
121     /*******************************************************************************************************************************************
122     **
123     **  Speaker Interface Methods
124     **
125     *******************************************************************************************************************************************/
126
127     public void init() {
128
129         if (didInit.compareAndSet(false, true) == false) {
130             return; // already init
131         }
132
133         didEnd.set(false);
134         isDoneEnding.set(true);
135         playbackFileIsDone.set(false);
136         Map<String, Integer> addrCount = new HashMap<String, Integer>();
137
138
139         // get correct addresses
140         for (IoTDeviceAddress devAdrr : speakerAddresses.values()) {
141             if (addrCount.containsKey(devAdrr.getAddress())) {
142                 addrCount.put(devAdrr.getAddress(), addrCount.get(devAdrr.getAddress()) + 1);
143             } else {
144                 addrCount.put(devAdrr.getAddress(), 1);
145             }
146         }
147
148         for (IoTDeviceAddress devAdrr : speakerAddresses.values()) {
149             if (addrCount.get(devAdrr.getAddress()) <= 1) {
150                 myAddress = devAdrr;
151             } else {
152                 if (devAdrr.getIsDstPortWildcard()) {
153                     if (controlAddress == null) {
154                         controlAddress = devAdrr;
155                     } else if (timingAddress == null) {
156                         timingAddress = devAdrr;
157                     } else {
158                         serverAddress = devAdrr;
159                     }
160                 } else {
161                     tcpAddress = devAdrr;
162                 }
163             }
164         }
165
166         System.out.println("tcpAddress: " + tcpAddress.getAddress() + ":" + tcpAddress.getSourcePortNumber() +
167                            ":" + tcpAddress.getDestinationPortNumber());
168         System.out.println("myAddress: " + myAddress.getAddress() + ":" + myAddress.getSourcePortNumber() +
169                            ":" + myAddress.getDestinationPortNumber());
170         System.out.println("controlAddress: " + controlAddress.getAddress() + ":" + controlAddress.getSourcePortNumber() +
171                            ":" + controlAddress.getDestinationPortNumber());
172         System.out.println("timingAddress: " + timingAddress.getAddress() + ":" + timingAddress.getSourcePortNumber() +
173                            ":" + timingAddress.getDestinationPortNumber());
174         System.out.println("serverAddress: " + serverAddress.getAddress() + ":" + serverAddress.getSourcePortNumber() +
175                            ":" + serverAddress.getDestinationPortNumber());
176
177         // Launch the worker function in a separate thread.
178         monitorThread = new Thread(new Runnable() {
179             public void run() {
180                 monitorThreadWorker();
181             }
182         });
183         monitorThread.start();
184     }
185
186
187
188     public boolean startPlayback() {
189
190
191         if (playbackAboutToStart.compareAndSet(false, true) == false) {
192             return false;
193         }
194
195         if (playbackStarted.get()) {
196             return true;
197         }
198
199         if (isDoneEnding.get() == false) {
200             return false;
201         }
202
203         // Reset all Parameters
204         didEnd.set(false);
205         playbackFileIsDone.set(false);
206         playbackState.set(true);
207
208         try {
209             currentPlaybackTimeMutex.acquire();
210             currentPlaybackTime = 0;
211         } catch (Exception e) {
212             e.printStackTrace();
213         }
214         currentPlaybackTimeMutex.release();
215
216
217
218         try {
219             desiredPlaybackTimeMutex.acquire();
220             desiredPlaybackTime = 0;
221         } catch (Exception e) {
222             e.printStackTrace();
223         }
224         desiredPlaybackTimeMutex.release();
225
226         sequenceNumber = SEQUENCE_NUMBER_INITIAL_VALUE;
227         rtpTimestamp = RTP_TIMESTAMP_INITIAL_VALUE;
228
229         try {
230             // start TCP connection
231             iHomeTCPConnection = new IoTTCP(tcpAddress);
232             iHomeTCPConnection.setReuseAddress(true);
233
234             // Get in and out communication
235             PrintWriter tcpOut = new PrintWriter(iHomeTCPConnection.getOutputStream(), true);
236             BufferedReader tcpIn = new BufferedReader(new InputStreamReader(iHomeTCPConnection.getInputStream()));
237
238
239             String session = String.valueOf(SOURCE_ID);
240             connectionURL = "rtsp://" + myAddress.getAddress() + "/" + session;
241
242             // Construct The commands
243             String optionsCommand = "OPTIONS * RTSP/1.0\r\n" +
244                                     "CSeq: 1\r\n" +
245                                     "User-Agent: iTunes/11.0.4 (Windows; N)\r\n" +
246                                     "Client-Instance: c0cb804fd20e80f6\r\n" +
247                                     "Apple-Challenge: i8j36XRYVmSZs9nZ7Kf0Cg\r\n\r\n";
248
249             String announceCommandBody = "v=0\r\n" +
250                                          "o=iTunes " + session + " 0 IN IP4 " + myAddress.getAddress() + "\r\n" +
251                                          "s=iTunes\r\n" +
252                                          "c=IN IP4 " + tcpAddress.getAddress() + "\r\n" +
253                                          "t=0 0\r\n" +
254                                          "m=audio 0 RTP/AVP 96\r\n" +
255                                          "a=rtpmap:96 AppleLossless\r\n" +
256                                          "a=fmtp:96 352 0 16 40 10 14 2 255 0 0 44100\r\n";
257
258             String announceCommand = "ANNOUNCE " + connectionURL + " RTSP/1.0\r\n" +
259                                      "CSeq: 1\r\n" +
260                                      "Content-Type: application/sdp\r\n" +
261                                      "Content-Length: " + announceCommandBody.length() + "\r\n" +
262                                      "User-Agent: iTunes/11.0.4 (Windows; N)\r\n\r\n" +
263                                      announceCommandBody;
264
265
266             // get the ports that we are going to tell the iHome to use
267             int ourControlPort = controlAddress.getSourcePortNumber();
268             int ourTimingPort = timingAddress.getSourcePortNumber();
269
270             String setupCommand = "SETUP " + connectionURL + " RTSP/1.0\r\n" +
271                                   "CSeq: 2\r\n" +
272                                   "Transport: RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=" + Integer.toString(ourControlPort) + ";timing_port=" + Integer.toString(ourTimingPort) + "\r\n" +
273                                   "User-Agent: iTunes/11.0.4 (Windows; N)\r\n\r\n";
274
275             String recordCommand = "RECORD " + connectionURL + " RTSP/1.0\r\nCSeq: 3\r\nSession: 1\r\nRange: npt=0-\r\nRTP-Info: seq=" + sequenceNumber + ";rtptime=" + rtpTimestamp + "\r\nUser-Agent: iTunes/11.0.4 (Windows; N)\r\n\r\n";
276
277
278             Thread.sleep(100);
279             tcpOut.print(optionsCommand);
280             tcpOut.flush();
281             while (!tcpIn.ready()) {
282             }
283             while (tcpIn.ready()) {
284                 String answer = tcpIn.readLine();
285                 System.out.println(answer);
286             }
287
288             Thread.sleep(100);
289             tcpOut.print(announceCommand);
290             tcpOut.flush();
291
292             while (!tcpIn.ready()) {
293             }
294             while (tcpIn.ready()) {
295                 String answer = tcpIn.readLine();
296                 System.out.println(answer);
297             }
298
299             Thread.sleep(100);
300             tcpOut.print(setupCommand);
301             tcpOut.flush();
302             while (!tcpIn.ready()) {
303             }
304
305             // ports that the speaker told us to communicate over
306             int serverPort = -1;
307             int controlPort = -1;
308             int timingPort = -1;
309
310             while (tcpIn.ready()) {
311                 String answer = tcpIn.readLine();
312                 System.out.println(answer);
313
314                 if (answer.contains("Transport")) {
315
316                     String[] splitString = answer.split(";");
317
318                     for (String str : splitString) {
319                         String[] keyValue = str.split("=");
320
321                         if (keyValue.length == 2) {
322                             if (keyValue[0].equals("server_port")) {
323                                 serverPort = Integer.parseInt(keyValue[1]);
324                                                                 System.out.println("DEBUG: Server port: " + serverPort);
325
326                             } else if (keyValue[0].equals("control_port")) {
327                                 controlPort = Integer.parseInt(keyValue[1]);
328                                                                 System.out.println("DEBUG: Control port: " + controlPort);
329
330                             } else if (keyValue[0].equals("timing_port")) {
331                                 timingPort = Integer.parseInt(keyValue[1]);
332                                                                 System.out.println("DEBUG: Timing port: " + timingPort);
333                             }
334
335                         }
336                     }
337
338                 }
339             }
340
341             serverAddress.setDstPort(serverPort);
342             controlAddress.setDstPort(controlPort);
343             timingAddress.setDstPort(timingPort);
344
345             // Launch the worker function in a separate thread.
346             // Must launch timing thread before record message since record message
347             // syncs with timing
348             timingThread = new Thread(new Runnable() {
349                 public void run() {
350                     timingWorkerFunction();
351                 }
352             });
353             timingThread.start();
354
355
356             // give the timing thread some time to set itself up
357             Thread.sleep(100);
358
359             tcpOut.print(recordCommand);
360             tcpOut.flush();
361             while (!tcpIn.ready()) {
362             }
363             while (tcpIn.ready()) {
364                 String answer = tcpIn.readLine();
365                 System.out.println(answer);
366             }
367
368
369
370
371             // Launch the worker function in a separate thread.
372             controlThread = new Thread(new Runnable() {
373                 public void run() {
374                     controlWorkerFunction();
375                 }
376             });
377             controlThread.start();
378
379
380             playbackFileIsDone.set(false);
381
382             // wait for audio Data
383             Thread.sleep(1000);
384
385             // Launch the worker function in a separate thread.
386             audioThread = new Thread(new Runnable() {
387                 public void run() {
388                     audioWorkerFunction();
389                 }
390             });
391             audioThread.start();
392
393
394
395
396             // playback has officially Started
397             playbackStarted.set(true);
398
399             // playback started
400             playbackAboutToStart.set(true);
401
402             // Set the volume to the current volume
403             setVolume(currentVolume);
404
405         } catch (Exception e) {
406             e.printStackTrace();
407             return false;
408         }
409
410         return true;
411     }
412
413     public boolean stopPlayback() {
414
415         if (playbackAboutToStop.compareAndSet(false, true) == false) {
416             return false;
417         }
418
419         isDoneEnding.set(false);
420         playbackState.set(false);
421         if (playbackStarted.get() == false) {
422             return false;
423         }
424
425         playbackStarted.set(false);
426         didEnd.set(true);
427
428         try {
429             timingThread.join();
430             audioThread.join();
431             controlThread.join();
432         } catch (Exception e) {
433             e.printStackTrace();
434         }
435
436         isDoneEnding.set(true);
437
438
439         String teardownCommand = "TEARDOWN " + connectionURL + " RTSP/1.0\r\n" +
440                                  "CSeq: 32\r\n" +
441                                  "Session: 1\r\n" +
442                                  "User-Agent: iTunes/11.0.4 (Windows; N)\r\n\r\n";
443
444
445
446         try {
447             // Get in and out communication
448             PrintWriter tcpOut = new PrintWriter(iHomeTCPConnection.getOutputStream(), true);
449             BufferedReader tcpIn = new BufferedReader(new InputStreamReader(iHomeTCPConnection.getInputStream()));
450
451             tcpOut.print(teardownCommand);
452             tcpOut.flush();
453             while (!tcpIn.ready()) {
454             }
455             while (tcpIn.ready()) {
456                 String answer = tcpIn.readLine();
457                 System.out.println(answer);
458             }
459
460             // close the connection
461             iHomeTCPConnection.close();
462
463         } catch (Exception e) {
464             e.printStackTrace();
465         }
466
467         playbackAboutToStop.set(false);
468
469         return true;
470     }
471
472
473     public boolean getPlaybackState() {
474         return playbackState.get();
475     }
476
477     public boolean setVolume(float _percent) {
478
479         if (settingVolume.compareAndSet(false, true) == false) {
480             return false;
481         }
482
483         // keep in range of percentage
484         if (_percent < 0) {
485             _percent = 0;
486         } else if (_percent > 100) {
487             _percent = 100;
488         }
489
490         // cant set the volume if there is no playback
491         if (playbackStarted.get() == false) {
492             return false;
493         }
494
495         // convert the volume from a percentage to a db
496         float dbVolume = 0;
497         if (_percent > 0) {
498
499             dbVolume = ((float)(_percent / 100.0) * (float)(VOLUME_MAX_VALUE_DB - VOLUME_MIN_VALUE_DB)) + (float)VOLUME_MIN_VALUE_DB;
500
501             // cap the volume to a level that the speaker supports
502             if (dbVolume > VOLUME_MAX_VALUE_DB) {
503                 dbVolume = VOLUME_MAX_VALUE_DB;
504             }
505         }
506
507         // construct the command
508         String body = "volume: " + String.format("%f", dbVolume) + "\r\n";
509         String volumeCommand = "SET_PARAMETER " + connectionURL + " RTSP/1.0\r\nCSeq: 4\r\nSession: 1\r\nContent-Type: text/parameters\r\nContent-Length: " + body.length() + "\r\nUser-Agent: iTunes/11.0.4 (Windows; N)\r\n\r\n" + body;
510
511
512
513         try {
514             // Get in and out communication
515             PrintWriter tcpOut = new PrintWriter(iHomeTCPConnection.getOutputStream(), true);
516             BufferedReader tcpIn = new BufferedReader(new InputStreamReader(iHomeTCPConnection.getInputStream()));
517
518             // send and flush
519             tcpOut.print(volumeCommand);
520             tcpOut.flush();
521
522             // Wait for data to come back
523             while (!tcpIn.ready()) {
524             }
525
526             // read the data from the iHome
527             while (tcpIn.ready()) {
528                 String answer = tcpIn.readLine();
529                 System.out.println(answer);
530             }
531
532             // update the current volume parameter
533             currentVolume = _percent;
534         } catch (Exception e) {
535             e.printStackTrace();
536         }
537
538         settingVolume.set(false);
539
540         return true;
541     }
542
543     public float getVolume() {
544
545         while (settingVolume.get()) {
546             // block until volume set is done
547         }
548         return currentVolume;
549     }
550
551     public void loadData(short[] _samples, int _offs, int _len) {
552
553         short[] sample = new short[_len];
554         int j = _offs;
555         for (int i = 0; i < _len; i++, j++) {
556             sample[i] = _samples[j];
557         }
558         synchronized (audioLinkedList) {
559             audioLinkedList.addLast(sample);
560         }
561     }
562
563     public void clearData() {
564         synchronized (audioLinkedList) {
565             audioLinkedList.clear();
566         }
567     }
568
569     public int getPosition() {
570         long pTime = 0;
571         try {
572             currentPlaybackTimeMutex.acquire();
573             pTime = currentPlaybackTime;
574         } catch (Exception e) {
575             e.printStackTrace();
576         }
577         currentPlaybackTimeMutex.release();
578
579         int mSecPos = (int)((pTime * 1000) / 44100);
580         return mSecPos;
581     }
582
583     public void setPosition(int _mSec) {
584         int sampleNumber = (_mSec * 44100) / 1000;
585
586         try {
587             desiredPlaybackTimeMutex.acquire();
588             desiredPlaybackTime = sampleNumber;
589         } catch (Exception e) {
590             e.printStackTrace();
591         }
592         desiredPlaybackTimeMutex.release();
593     }
594
595
596     public void registerCallback(SpeakerSmartCallback _cb) {
597         callbackList.add(_cb);
598     }
599
600
601     /*******************************************************************************************************************************************
602     **
603     **  Helper Methods
604     **
605     *******************************************************************************************************************************************/
606
607
608     private void timingWorkerFunction() {
609         try {
610             IoTUDP timingUDP = new IoTUDP(timingAddress);
611
612             byte[] receiveData = new byte[1024];
613             byte[] sendData = new byte[32];
614
615             while (didEnd.get() == false) {
616
617                 receiveData = timingUDP.recieveData(receiveData.length);
618
619                 long nanotime = nanoTime();
620                 int seconds = (int)((nanotime / 1000000000) & 0xffffffff);
621                 long fractions = ((( nanotime % 1000000000) * (0xffffffffL)) / 1000000000);
622
623                 sendData[0] = (byte)0x80;                   // Header bit field
624                 sendData[1] = (byte)0xd3;                   // mark bit and message payload number
625
626                 sendData[2] = (byte) 0x00;
627                 sendData[3] = (byte) 0x07;
628
629                 sendData[4] = (byte) 0x00;
630                 sendData[5] = (byte) 0x00;
631                 sendData[6] = (byte) 0x00;
632                 sendData[7] = (byte) 0x00;
633
634                 // origin time-stamp
635                 sendData[8] = receiveData[24];
636                 sendData[9] = receiveData[25];
637                 sendData[10] = receiveData[26];
638                 sendData[11] = receiveData[27];
639                 sendData[12] = receiveData[28];
640                 sendData[13] = receiveData[29];
641                 sendData[14] = receiveData[30];
642                 sendData[15] = receiveData[31];
643
644                 // arrival time-stamp
645                 sendData[16] = (byte)((seconds >> 24) & 0xff);
646                 sendData[17] = (byte)((seconds >> 16) & 0xff);
647                 sendData[18] = (byte)((seconds >> 8) & 0xff);
648                 sendData[19] = (byte)((seconds >> 0) & 0xff);
649                 sendData[20] = (byte)((fractions >> 24) & 0xff);
650                 sendData[21] = (byte)((fractions >> 16) & 0xff);
651                 sendData[22] = (byte)((fractions >> 8) & 0xff);
652                 sendData[23] = (byte)((fractions >> 0) & 0xff);
653
654
655                 nanotime = nanoTime();
656                 seconds = (int)( nanotime / 1000000000);
657                 fractions = ((( nanotime % 1000000000) * (0xffffffffL)) / 1000000000);
658
659                 // transmit time-stamp
660                 sendData[24] = (byte)((seconds >> 24) & 0xff);
661                 sendData[25] = (byte)((seconds >> 16) & 0xff);
662                 sendData[26] = (byte)((seconds >> 8) & 0xff);
663                 sendData[27] = (byte)((seconds >> 0) & 0xff);
664                 sendData[28] = (byte)((fractions >> 24) & 0xff);
665                 sendData[29] = (byte)((fractions >> 16) & 0xff);
666                 sendData[30] = (byte)((fractions >> 8) & 0xff);
667                 sendData[31] = (byte)((fractions >> 0) & 0xff);
668
669                 // Send the Data
670                 timingUDP.sendData(sendData);
671             }
672         } catch (Exception e) {
673             e.printStackTrace();
674         }
675     }
676
677     private void controlWorkerFunction() {
678
679         try {
680
681             IoTUDP controlUDP = new IoTUDP(controlAddress);
682             controlUDP.setSoTimeout(1);
683             byte[] sendData = new byte[20];
684             boolean first = true;
685
686
687             while (didEnd.get() == false) {
688
689                 try {
690                     byte[] receiveData = new byte[24];
691                     receiveData = controlUDP.recieveData(receiveData.length);
692
693                     // System.out.println("Control Packet Arrived");
694                     // String packetData = bytesToHex(receiveData);
695                     // System.out.println(packetData);
696
697                 } catch (Exception e) {
698                     e.printStackTrace();
699                 }
700
701
702                 long rtpTimestampCopy = rtpTimestamp;
703                 long nanotime = nanoTime();
704                 int seconds = (int)( nanotime / 1000000000);
705                 long fractions = (( nanotime % 1000000000) * (0xffffffffL)) / 1000000000;
706
707
708                 if (first) {
709                     sendData[0] = (byte)0x90;               // Header bit field
710                     first = false;
711                 } else {
712                     sendData[0] = (byte)0x80;               // Header bit field
713                 }
714
715
716                 sendData[1] = (byte)0xd4;                   // mark bit and message payload number
717                 sendData[2] = (byte)0x00;
718                 sendData[3] = (byte)0x07;
719
720                 // time-stamp of packet
721                 sendData[4] = (byte)((rtpTimestampCopy >> 24) & 0xFF);
722                 sendData[5] = (byte)((rtpTimestampCopy >> 16) & 0xFF);
723                 sendData[6] = (byte)((rtpTimestampCopy >> 8) & 0xFF);
724                 sendData[7] = (byte)((rtpTimestampCopy >> 0) & 0xFF);
725
726                 // ntp time-stamp
727                 sendData[8] = (byte)((seconds >> 24) & 0xff);
728                 sendData[9] = (byte)((seconds >> 16) & 0xff);
729                 sendData[10] = (byte)((seconds >> 8) & 0xff);
730                 sendData[11] = (byte)((seconds >> 0) & 0xff);
731
732                 sendData[12] = (byte)((fractions >> 24) & 0xff);
733                 sendData[13] = (byte)((fractions >> 16) & 0xff);
734                 sendData[14] = (byte)((fractions >> 8) & 0xff);
735                 sendData[15] = (byte)((fractions >> 0) & 0xff);
736
737                 rtpTimestampCopy += 88200;
738                 sendData[16] = (byte)((rtpTimestampCopy >> 24) & 0xFF);
739                 sendData[17] = (byte)((rtpTimestampCopy >> 16) & 0xFF);
740                 sendData[18] = (byte)((rtpTimestampCopy >> 8) & 0xFF);
741                 sendData[19] = (byte)((rtpTimestampCopy >> 0) & 0xFF);
742
743                 // send the data
744                 controlUDP.sendData(sendData);
745
746                 // System.out.println("---------------------------------------------");
747                 // System.out.println("Sending Control Sync");
748                 // System.out.println("---------------------------------------------");
749
750                 Thread.sleep(1000);
751             }
752         } catch (Exception e) {
753             e.printStackTrace();
754         }
755     }
756
757     private void audioWorkerFunction() {
758         try {
759
760             IoTUDP serverUDP = new IoTUDP(serverAddress);
761
762             // current frame being played
763             long frameCounter = 0;
764
765             // used for bit packing for audio stream
766             short[] array = null;
767             int offset = 0;
768
769             int noAudioCount = 0;
770
771             while (didEnd.get() == false) {
772
773                 byte[] sendData = new byte[352 * 4 + 19];
774
775                 sendData[0] = (byte)0x80;
776
777                 if (frameCounter == 0) {
778                     sendData[1] = (byte)0xe0;
779                     // frameCounter = 1;
780                 } else {
781                     sendData[1] = (byte)0x60;
782                 }
783
784                 sendData[2] = (byte)((sequenceNumber >> 8) & 0xFF);
785                 sendData[3] = (byte)((sequenceNumber >> 0) & 0xFF);
786
787                 long rtpTmp = rtpTimestamp;
788
789                 sendData[4] = (byte)((rtpTmp >> 24) & 0xFF);
790                 sendData[5] = (byte)((rtpTmp >> 16) & 0xFF);
791                 sendData[6] = (byte)((rtpTmp >> 8) & 0xFF);
792                 sendData[7] = (byte)((rtpTmp >> 0) & 0xFF);
793
794                 sendData[8] = (byte)((SEQUENCE_ID >> 24) & 0xFF);
795                 sendData[9] = (byte)((SEQUENCE_ID >> 16) & 0xFF);
796                 sendData[10] = (byte)((SEQUENCE_ID >> 8) & 0xFF);
797                 sendData[11] = (byte)((SEQUENCE_ID >> 0) & 0xFF);
798
799                 sendData[12] = (byte) 0x20;
800                 sendData[13] = (byte) 0x00;
801                 sendData[14] = (byte) 0x12;
802                 sendData[15] = (byte) 0x00;
803                 sendData[16] = (byte) 0x00;
804                 sendData[17] = (byte) 0x02;
805                 sendData[18] = (byte) 0xc0;
806
807                 for (int i = 19; i < sendData.length; i += 4) {
808                     if (array != null && (offset + 1) >= array.length) {
809                         array = null;
810                     }
811
812                     if (array == null) {
813                         offset = 0;
814
815                         synchronized (audioLinkedList) {
816                             array = (short[])audioLinkedList.poll();
817                         }
818                     }
819
820                     if (array != null) {
821
822                         long time1 = 0;
823                         long time2 = 0;
824
825                         try {
826                             desiredPlaybackTimeMutex.acquire();
827                             time1 = desiredPlaybackTime;
828                         } catch (Exception e) {
829                             e.printStackTrace();
830                         }
831
832                         desiredPlaybackTimeMutex.release();
833
834
835                         try {
836                             currentPlaybackTimeMutex.acquire();
837                             time2 = currentPlaybackTime;
838                         } catch (Exception e) {
839                             e.printStackTrace();
840                         }
841                         currentPlaybackTimeMutex.release();
842
843
844                         while ((time2 < time1)) {
845                             offset++;
846
847                             try {
848                                 currentPlaybackTimeMutex.acquire();
849                                 currentPlaybackTime++;
850                             } catch (Exception e) {
851                                 e.printStackTrace();
852                             }
853                             currentPlaybackTimeMutex.release();
854
855
856                             if ((offset + 1) >= array.length) {
857                                 offset = 0;
858                                 synchronized (audioLinkedList) {
859                                     array = (short[])audioLinkedList.poll();
860                                 }
861
862                                 if (array == null) {
863                                     break;
864                                 }
865                             }
866                         }
867                     }
868
869                     short l = 0;
870                     short r = 0;
871
872                     if (array != null) {
873                         l = array[offset++];
874                         r = array[offset++];
875
876                         try {
877                             currentPlaybackTimeMutex.acquire();
878                             currentPlaybackTime++;
879                         } catch (Exception e) {
880                             e.printStackTrace();
881                         }
882                         currentPlaybackTimeMutex.release();
883
884
885                         try {
886                             desiredPlaybackTimeMutex.acquire();
887                             desiredPlaybackTime++;
888                         } catch (Exception e) {
889                             e.printStackTrace();
890                         }
891
892                         desiredPlaybackTimeMutex.release();
893
894                         noAudioCount = 0;
895                     } else {
896                         noAudioCount++;
897
898                         if (noAudioCount > 10) {
899                             noAudioCount = 0;
900                             if (playbackFileIsDone.get() == false) {
901                                 playbackFileIsDone.set(true);
902                             }
903                         }
904                     }
905
906                     sendData[i - 1] |= (byte)((l >> 15) & 1);
907                     sendData[i] = (byte)((l >> 7) & 0xff);
908                     sendData[i + 1] = (byte)(((l << 1) & 0xfe) | ((r >> 15) & 1));
909                     sendData[i + 2] = (byte)((r >> 7) & 0xff);
910                     sendData[i + 3] = (byte)((r << 1) & 0xfe);
911                 }
912
913
914                 sequenceNumber++;
915                 sequenceNumber = sequenceNumber % SEQUENCE_NUMBER_WRAP_AROUND;
916                 rtpTimestamp += RTP_TIMESTAMP_INCREMENT_VALUE;
917
918                 frameCounter++;
919                 serverUDP.sendData(sendData);
920
921
922                 // need to sleep for a bit
923                 if ((frameCounter % 2) == 0) {
924                     Thread.sleep(7);
925                 } else {
926                     Thread.sleep(6);
927                 }
928
929             }
930         } catch (Exception e) {
931             e.printStackTrace();
932         }
933     }
934
935     private void monitorThreadWorker() {
936         while (driverIsShuttingDown.get() == false) {
937             if (playbackFileIsDone.get()) {
938                 stopPlayback();
939                 playbackFileIsDone.set(false);
940
941                 for (SpeakerSmartCallback c : callbackList) {
942                     try {
943                         //c.speakerDone(this);
944                                                 c.speakerDone();
945                     } catch (Exception e) {
946                         e.printStackTrace();
947                     }
948                 }
949             }
950         }
951     }
952
953     private void endDriver() {
954         stopPlayback();
955
956         driverIsShuttingDown.set(true);
957         try {
958             monitorThread.join();
959         } catch (Exception e) {
960             e.printStackTrace();
961         }
962
963         didClose = true;
964     }
965
966     /**
967      * close() called by the garbage collector right before trashing object
968      */
969     public void finalize() {
970         if (!didClose) {
971             endDriver();
972         }
973     }
974
975     private static long nanoTime() {
976         long nanotime = System.nanoTime();
977         return nanotime;
978     }
979         
980         
981         // TODO: The following is only for test
982         /**
983          * Prepare the speakers for a new song to start playing
984          */
985         private void prepareNextSong(IHome ih) {
986                 System.out.println("Starting Music Prep");
987
988                 System.out.println("Stopping all device playback");
989                 // stop all devices that are still playing and clear their buffers
990                 // they are about to end playback anyways
991                 try {
992
993                         if (ih.getPlaybackState()) {
994                                 ih.stopPlayback();
995                         }
996                         ih.clearData();
997                 } catch (Exception e) {
998                         e.printStackTrace();
999                 }
1000
1001                 // get the music file names that are in the music files directory
1002                 File musicFolder = new File(MUSIC_FILE_DIRECTORY);
1003                 File[] audioFiles = musicFolder.listFiles();
1004                 List<String> audioFileNames = new ArrayList<String>();
1005
1006                 // put all names in a list
1007                 for (int i = 0; i < audioFiles.length; i++) {
1008                         if (audioFiles[i].isFile()) {
1009                                 try {
1010                                         audioFileNames.add(audioFiles[i].getCanonicalPath());
1011                                 } catch (Exception ex) {
1012                                         ex.printStackTrace();
1013                                 }
1014                         }
1015                 }
1016
1017                 // pick a random file to play
1018                 Random rand = new Random(System.nanoTime());
1019                 String audioFilename = audioFileNames.get(rand.nextInt(audioFileNames.size()));
1020
1021                 System.out.println("Going to load audio file");
1022                 System.out.println(audioFilename);
1023
1024                 // decode the mp3 file
1025                 System.out.println("Starting Decode");
1026                 MP3Decoder dec = new MP3Decoder(audioFilename);
1027                 List<short[]> dat = dec.getDecodedFrames();
1028                 System.out.println("Ending Decode");
1029
1030                 // count the number of samples
1031                 int count = 0;
1032                 for (short[] d : dat) {
1033                         count += d.length;
1034                 }
1035
1036                 // make into a single large buffer for 1 large RMI call
1037                 short[] compressedArray = new short[count];
1038                 count = 0;
1039                 for (short[] d : dat) {
1040                         for (short s : d) {
1041                                 compressedArray[count] = s;
1042                                 count++;
1043                         }
1044                 }
1045
1046                 System.out.println("Loading Speakers");
1047                 // send the new data to all the speakers
1048                 ih.loadData(compressedArray, 0, compressedArray.length);
1049                 System.out.println("Done loading a single speaker with data");
1050                 System.out.println("All Speakers done loading");
1051                 
1052                 // Attack the speaker
1053                 System.out.println("Trying to play the song on the speaker");
1054                 ih.startPlayback();
1055                 ih.setPosition(0);
1056         }
1057         
1058         /* TODO: Uncomment this part to do speaker test
1059         public static void main(String[] args) throws Exception {
1060
1061                 System.out.println("Executing main function!");
1062                 //IoTDeviceAddress iotDevAdd1 = new IoTDeviceAddress("192.168.0.93", 60000, 1024, false, false);
1063                 //IoTDeviceAddress iotDevAdd2 = new IoTDeviceAddress("192.168.0.90", 60001, -1, false, false);
1064                 //IoTDeviceAddress iotDevAdd3 = new IoTDeviceAddress("192.168.0.93", 60010, -1, false, true);
1065                 //IoTDeviceAddress iotDevAdd4 = new IoTDeviceAddress("192.168.0.93", 60011, -1, false, true);
1066                 //IoTDeviceAddress iotDevAdd5 = new IoTDeviceAddress("192.168.0.93", 60012, -1, false, true);
1067                 IoTDeviceAddress iotDevAdd1 = new IoTDeviceAddress(args[0], 60000, 1024, false, false);
1068                 IoTDeviceAddress iotDevAdd2 = new IoTDeviceAddress(args[1], 60001, -1, false, false);
1069                 IoTDeviceAddress iotDevAdd3 = new IoTDeviceAddress(args[0], 60010, -1, false, true);
1070                 IoTDeviceAddress iotDevAdd4 = new IoTDeviceAddress(args[0], 60011, -1, false, true);
1071                 IoTDeviceAddress iotDevAdd5 = new IoTDeviceAddress(args[0], 60012, -1, false, true);
1072                 Set<IoTDeviceAddress> set = new HashSet<IoTDeviceAddress>();
1073                 set.add(iotDevAdd1);
1074                 set.add(iotDevAdd2);
1075                 set.add(iotDevAdd3);
1076                 set.add(iotDevAdd4);
1077                 set.add(iotDevAdd5);
1078                 IoTSet<IoTDeviceAddress> iotset = new IoTSet<IoTDeviceAddress>(set);
1079                 IHome ih = new IHome(iotset);
1080                 ih.init();
1081                 ih.prepareNextSong(ih);
1082         }*/
1083 }
1084
1085
1086
1087