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;
12 import java.rmi.Remote;
13 import java.rmi.RemoteException;
15 // Checker annotations
16 //import iotchecker.qual.*;
18 // Standard Java Packages
19 import java.util.concurrent.atomic.AtomicBoolean;
21 import java.util.HashMap;
22 import java.util.HashSet;
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;
35 import java.util.Random;
38 public class IHome implements Speaker {
41 /*******************************************************************************************************************************************
43 *******************************************************************************************************************************************/
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);
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;
57 public static final String MUSIC_FILE_DIRECTORY = "./music/"; // file that the music files are in
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;
65 private IoTTCP iHomeTCPConnection = null;
67 private AtomicBoolean driverIsShuttingDown = new AtomicBoolean();
68 private boolean didClose = false;
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();
76 private AtomicBoolean didInit = new AtomicBoolean();
77 private AtomicBoolean playbackAboutToStart = new AtomicBoolean();
78 private AtomicBoolean settingVolume = new AtomicBoolean();
79 private AtomicBoolean playbackAboutToStop = new AtomicBoolean();
83 private long sequenceNumber = SEQUENCE_NUMBER_INITIAL_VALUE;
84 private long rtpTimestamp = RTP_TIMESTAMP_INITIAL_VALUE;
86 private long currentPlaybackTime = 0;
87 static Semaphore currentPlaybackTimeMutex = new Semaphore(1);
89 private long desiredPlaybackTime = 0;
90 static Semaphore desiredPlaybackTimeMutex = new Semaphore(1);
93 private String connectionURL = "";
94 private float currentVolume = DEFAULT_VOLUME;
95 private LinkedList audioLinkedList = new LinkedList();
97 private List < SpeakerSmartCallback > callbackList = new CopyOnWriteArrayList< SpeakerSmartCallback > ();
99 /*******************************************************************************************************************************************
101 *******************************************************************************************************************************************/
102 private Thread timingThread = null;
103 private Thread audioThread = null;
104 private Thread controlThread = null;
105 private Thread monitorThread = null;
108 @config private IoTSet<IoTDeviceAddress> speakerAddresses;
110 public IHome(IoTSet<IoTDeviceAddress> devAddresses) {
112 speakerAddresses = devAddresses;
117 playbackAboutToStart.set(false);
118 settingVolume.set(false);
121 /*******************************************************************************************************************************************
123 ** Speaker Interface Methods
125 *******************************************************************************************************************************************/
129 if (didInit.compareAndSet(false, true) == false) {
130 return; // already init
134 isDoneEnding.set(true);
135 playbackFileIsDone.set(false);
136 Map<String, Integer> addrCount = new HashMap<String, Integer>();
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);
144 addrCount.put(devAdrr.getAddress(), 1);
148 for (IoTDeviceAddress devAdrr : speakerAddresses.values()) {
149 if (addrCount.get(devAdrr.getAddress()) <= 1) {
152 if (devAdrr.getIsDstPortWildcard()) {
153 if (controlAddress == null) {
154 controlAddress = devAdrr;
155 } else if (timingAddress == null) {
156 timingAddress = devAdrr;
158 serverAddress = devAdrr;
161 tcpAddress = devAdrr;
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());
177 // Launch the worker function in a separate thread.
178 monitorThread = new Thread(new Runnable() {
180 monitorThreadWorker();
183 monitorThread.start();
188 public boolean startPlayback() {
191 if (playbackAboutToStart.compareAndSet(false, true) == false) {
195 if (playbackStarted.get()) {
199 if (isDoneEnding.get() == false) {
203 // Reset all Parameters
205 playbackFileIsDone.set(false);
206 playbackState.set(true);
209 currentPlaybackTimeMutex.acquire();
210 currentPlaybackTime = 0;
211 } catch (Exception e) {
214 currentPlaybackTimeMutex.release();
219 desiredPlaybackTimeMutex.acquire();
220 desiredPlaybackTime = 0;
221 } catch (Exception e) {
224 desiredPlaybackTimeMutex.release();
226 sequenceNumber = SEQUENCE_NUMBER_INITIAL_VALUE;
227 rtpTimestamp = RTP_TIMESTAMP_INITIAL_VALUE;
230 // start TCP connection
231 iHomeTCPConnection = new IoTTCP(tcpAddress);
232 iHomeTCPConnection.setReuseAddress(true);
234 // Get in and out communication
235 PrintWriter tcpOut = new PrintWriter(iHomeTCPConnection.getOutputStream(), true);
236 BufferedReader tcpIn = new BufferedReader(new InputStreamReader(iHomeTCPConnection.getInputStream()));
239 String session = String.valueOf(SOURCE_ID);
240 connectionURL = "rtsp://" + myAddress.getAddress() + "/" + session;
242 // Construct The commands
243 String optionsCommand = "OPTIONS * RTSP/1.0\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";
249 String announceCommandBody = "v=0\r\n" +
250 "o=iTunes " + session + " 0 IN IP4 " + myAddress.getAddress() + "\r\n" +
252 "c=IN IP4 " + tcpAddress.getAddress() + "\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";
258 String announceCommand = "ANNOUNCE " + connectionURL + " RTSP/1.0\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" +
266 // get the ports that we are going to tell the iHome to use
267 int ourControlPort = controlAddress.getSourcePortNumber();
268 int ourTimingPort = timingAddress.getSourcePortNumber();
270 String setupCommand = "SETUP " + connectionURL + " RTSP/1.0\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";
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";
279 tcpOut.print(optionsCommand);
281 while (!tcpIn.ready()) {
283 while (tcpIn.ready()) {
284 String answer = tcpIn.readLine();
285 System.out.println(answer);
289 tcpOut.print(announceCommand);
292 while (!tcpIn.ready()) {
294 while (tcpIn.ready()) {
295 String answer = tcpIn.readLine();
296 System.out.println(answer);
300 tcpOut.print(setupCommand);
302 while (!tcpIn.ready()) {
305 // ports that the speaker told us to communicate over
307 int controlPort = -1;
310 while (tcpIn.ready()) {
311 String answer = tcpIn.readLine();
312 System.out.println(answer);
314 if (answer.contains("Transport")) {
316 String[] splitString = answer.split(";");
318 for (String str : splitString) {
319 String[] keyValue = str.split("=");
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);
326 } else if (keyValue[0].equals("control_port")) {
327 controlPort = Integer.parseInt(keyValue[1]);
328 System.out.println("DEBUG: Control port: " + controlPort);
330 } else if (keyValue[0].equals("timing_port")) {
331 timingPort = Integer.parseInt(keyValue[1]);
332 System.out.println("DEBUG: Timing port: " + timingPort);
341 serverAddress.setDstPort(serverPort);
342 controlAddress.setDstPort(controlPort);
343 timingAddress.setDstPort(timingPort);
345 // Launch the worker function in a separate thread.
346 // Must launch timing thread before record message since record message
348 timingThread = new Thread(new Runnable() {
350 timingWorkerFunction();
353 timingThread.start();
356 // give the timing thread some time to set itself up
359 tcpOut.print(recordCommand);
361 while (!tcpIn.ready()) {
363 while (tcpIn.ready()) {
364 String answer = tcpIn.readLine();
365 System.out.println(answer);
371 // Launch the worker function in a separate thread.
372 controlThread = new Thread(new Runnable() {
374 controlWorkerFunction();
377 controlThread.start();
380 playbackFileIsDone.set(false);
382 // wait for audio Data
385 // Launch the worker function in a separate thread.
386 audioThread = new Thread(new Runnable() {
388 audioWorkerFunction();
396 // playback has officially Started
397 playbackStarted.set(true);
400 playbackAboutToStart.set(true);
402 // Set the volume to the current volume
403 setVolume(currentVolume);
405 } catch (Exception e) {
413 public boolean stopPlayback() {
415 if (playbackAboutToStop.compareAndSet(false, true) == false) {
419 isDoneEnding.set(false);
420 playbackState.set(false);
421 if (playbackStarted.get() == false) {
425 playbackStarted.set(false);
431 controlThread.join();
432 } catch (Exception e) {
436 isDoneEnding.set(true);
439 String teardownCommand = "TEARDOWN " + connectionURL + " RTSP/1.0\r\n" +
442 "User-Agent: iTunes/11.0.4 (Windows; N)\r\n\r\n";
447 // Get in and out communication
448 PrintWriter tcpOut = new PrintWriter(iHomeTCPConnection.getOutputStream(), true);
449 BufferedReader tcpIn = new BufferedReader(new InputStreamReader(iHomeTCPConnection.getInputStream()));
451 tcpOut.print(teardownCommand);
453 while (!tcpIn.ready()) {
455 while (tcpIn.ready()) {
456 String answer = tcpIn.readLine();
457 System.out.println(answer);
460 // close the connection
461 iHomeTCPConnection.close();
463 } catch (Exception e) {
467 playbackAboutToStop.set(false);
473 public boolean getPlaybackState() {
474 return playbackState.get();
477 public boolean setVolume(float _percent) {
479 if (settingVolume.compareAndSet(false, true) == false) {
483 // keep in range of percentage
486 } else if (_percent > 100) {
490 // cant set the volume if there is no playback
491 if (playbackStarted.get() == false) {
495 // convert the volume from a percentage to a db
499 dbVolume = ((float)(_percent / 100.0) * (float)(VOLUME_MAX_VALUE_DB - VOLUME_MIN_VALUE_DB)) + (float)VOLUME_MIN_VALUE_DB;
501 // cap the volume to a level that the speaker supports
502 if (dbVolume > VOLUME_MAX_VALUE_DB) {
503 dbVolume = VOLUME_MAX_VALUE_DB;
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;
514 // Get in and out communication
515 PrintWriter tcpOut = new PrintWriter(iHomeTCPConnection.getOutputStream(), true);
516 BufferedReader tcpIn = new BufferedReader(new InputStreamReader(iHomeTCPConnection.getInputStream()));
519 tcpOut.print(volumeCommand);
522 // Wait for data to come back
523 while (!tcpIn.ready()) {
526 // read the data from the iHome
527 while (tcpIn.ready()) {
528 String answer = tcpIn.readLine();
529 System.out.println(answer);
532 // update the current volume parameter
533 currentVolume = _percent;
534 } catch (Exception e) {
538 settingVolume.set(false);
543 public float getVolume() {
545 while (settingVolume.get()) {
546 // block until volume set is done
548 return currentVolume;
551 public void loadData(short[] _samples, int _offs, int _len) {
553 short[] sample = new short[_len];
555 for (int i = 0; i < _len; i++, j++) {
556 sample[i] = _samples[j];
558 synchronized (audioLinkedList) {
559 audioLinkedList.addLast(sample);
563 public void clearData() {
564 synchronized (audioLinkedList) {
565 audioLinkedList.clear();
569 public int getPosition() {
572 currentPlaybackTimeMutex.acquire();
573 pTime = currentPlaybackTime;
574 } catch (Exception e) {
577 currentPlaybackTimeMutex.release();
579 int mSecPos = (int)((pTime * 1000) / 44100);
583 public void setPosition(int _mSec) {
584 int sampleNumber = (_mSec * 44100) / 1000;
587 desiredPlaybackTimeMutex.acquire();
588 desiredPlaybackTime = sampleNumber;
589 } catch (Exception e) {
592 desiredPlaybackTimeMutex.release();
596 public void registerCallback(SpeakerSmartCallback _cb) {
597 callbackList.add(_cb);
601 /*******************************************************************************************************************************************
605 *******************************************************************************************************************************************/
608 private void timingWorkerFunction() {
610 IoTUDP timingUDP = new IoTUDP(timingAddress);
612 byte[] receiveData = new byte[1024];
613 byte[] sendData = new byte[32];
615 while (didEnd.get() == false) {
617 receiveData = timingUDP.recieveData(receiveData.length);
619 long nanotime = nanoTime();
620 int seconds = (int)((nanotime / 1000000000) & 0xffffffff);
621 long fractions = ((( nanotime % 1000000000) * (0xffffffffL)) / 1000000000);
623 sendData[0] = (byte)0x80; // Header bit field
624 sendData[1] = (byte)0xd3; // mark bit and message payload number
626 sendData[2] = (byte) 0x00;
627 sendData[3] = (byte) 0x07;
629 sendData[4] = (byte) 0x00;
630 sendData[5] = (byte) 0x00;
631 sendData[6] = (byte) 0x00;
632 sendData[7] = (byte) 0x00;
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];
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);
655 nanotime = nanoTime();
656 seconds = (int)( nanotime / 1000000000);
657 fractions = ((( nanotime % 1000000000) * (0xffffffffL)) / 1000000000);
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);
670 timingUDP.sendData(sendData);
672 } catch (Exception e) {
677 private void controlWorkerFunction() {
681 IoTUDP controlUDP = new IoTUDP(controlAddress);
682 controlUDP.setSoTimeout(1);
683 byte[] sendData = new byte[20];
684 boolean first = true;
687 while (didEnd.get() == false) {
690 byte[] receiveData = new byte[24];
691 receiveData = controlUDP.recieveData(receiveData.length);
693 // System.out.println("Control Packet Arrived");
694 // String packetData = bytesToHex(receiveData);
695 // System.out.println(packetData);
697 } catch (Exception e) {
698 // e.printStackTrace();
702 long rtpTimestampCopy = rtpTimestamp;
703 long nanotime = nanoTime();
704 int seconds = (int)( nanotime / 1000000000);
705 long fractions = (( nanotime % 1000000000) * (0xffffffffL)) / 1000000000;
709 sendData[0] = (byte)0x90; // Header bit field
712 sendData[0] = (byte)0x80; // Header bit field
716 sendData[1] = (byte)0xd4; // mark bit and message payload number
717 sendData[2] = (byte)0x00;
718 sendData[3] = (byte)0x07;
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);
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);
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);
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);
744 controlUDP.sendData(sendData);
746 // System.out.println("---------------------------------------------");
747 // System.out.println("Sending Control Sync");
748 // System.out.println("---------------------------------------------");
752 } catch (Exception e) {
757 private void audioWorkerFunction() {
760 IoTUDP serverUDP = new IoTUDP(serverAddress);
762 // current frame being played
763 long frameCounter = 0;
765 // used for bit packing for audio stream
766 short[] array = null;
769 int noAudioCount = 0;
771 while (didEnd.get() == false) {
773 byte[] sendData = new byte[352 * 4 + 19];
775 sendData[0] = (byte)0x80;
777 if (frameCounter == 0) {
778 sendData[1] = (byte)0xe0;
781 sendData[1] = (byte)0x60;
784 sendData[2] = (byte)((sequenceNumber >> 8) & 0xFF);
785 sendData[3] = (byte)((sequenceNumber >> 0) & 0xFF);
787 long rtpTmp = rtpTimestamp;
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);
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);
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;
807 for (int i = 19; i < sendData.length; i += 4) {
808 if (array != null && (offset + 1) >= array.length) {
815 synchronized (audioLinkedList) {
816 array = (short[])audioLinkedList.poll();
826 desiredPlaybackTimeMutex.acquire();
827 time1 = desiredPlaybackTime;
828 } catch (Exception e) {
832 desiredPlaybackTimeMutex.release();
836 currentPlaybackTimeMutex.acquire();
837 time2 = currentPlaybackTime;
838 } catch (Exception e) {
841 currentPlaybackTimeMutex.release();
844 while ((time2 < time1)) {
848 currentPlaybackTimeMutex.acquire();
849 currentPlaybackTime++;
850 } catch (Exception e) {
853 currentPlaybackTimeMutex.release();
856 if ((offset + 1) >= array.length) {
858 synchronized (audioLinkedList) {
859 array = (short[])audioLinkedList.poll();
877 currentPlaybackTimeMutex.acquire();
878 currentPlaybackTime++;
879 } catch (Exception e) {
882 currentPlaybackTimeMutex.release();
886 desiredPlaybackTimeMutex.acquire();
887 desiredPlaybackTime++;
888 } catch (Exception e) {
892 desiredPlaybackTimeMutex.release();
898 if (noAudioCount > 10) {
900 if (playbackFileIsDone.get() == false) {
901 playbackFileIsDone.set(true);
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);
915 sequenceNumber = sequenceNumber % SEQUENCE_NUMBER_WRAP_AROUND;
916 rtpTimestamp += RTP_TIMESTAMP_INCREMENT_VALUE;
919 serverUDP.sendData(sendData);
922 // need to sleep for a bit
923 if ((frameCounter % 2) == 0) {
930 } catch (Exception e) {
935 private void monitorThreadWorker() {
936 while (driverIsShuttingDown.get() == false) {
937 if (playbackFileIsDone.get()) {
939 playbackFileIsDone.set(false);
941 for (SpeakerSmartCallback c : callbackList) {
943 //c.speakerDone(this);
945 } catch (Exception e) {
953 private void endDriver() {
956 driverIsShuttingDown.set(true);
958 monitorThread.join();
959 } catch (Exception e) {
967 * close() called by the garbage collector right before trashing object
969 public void finalize() {
975 private static long nanoTime() {
976 long nanotime = System.nanoTime();
983 * Prepare the speakers for a new song to start playing
985 private void prepareNextSong(IHome ih) {
986 System.out.println("Starting Music Prep");
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
993 if (ih.getPlaybackState()) {
997 } catch (Exception e) {
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>();
1006 // put all names in a list
1007 for (int i = 0; i < audioFiles.length; i++) {
1008 if (audioFiles[i].isFile()) {
1010 audioFileNames.add(audioFiles[i].getCanonicalPath());
1011 } catch (Exception ex) {
1012 ex.printStackTrace();
1017 // pick a random file to play
1018 Random rand = new Random(System.nanoTime());
1019 String audioFilename = audioFileNames.get(rand.nextInt(audioFileNames.size()));
1021 System.out.println("Going to load audio file");
1022 System.out.println(audioFilename);
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");
1030 // count the number of samples
1032 for (short[] d : dat) {
1036 // make into a single large buffer for 1 large RMI call
1037 short[] compressedArray = new short[count];
1039 for (short[] d : dat) {
1041 compressedArray[count] = s;
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");
1052 // Attack the speaker
1053 System.out.println("Trying to play the song on the speaker");
1058 /* TODO: Uncomment this part to do speaker test
1059 public static void main(String[] args) throws Exception {
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);
1081 ih.prepareNextSong(ih);