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