import javax.sound.sampled.*;
import java.util.Calendar;
import java.io.File;
import java.io.IOException;
import java.math.*;
import java.io.IOException;
import java.util.Scanner;
import java.util.Date;
class FilePlayer { //Following code is all declarations and initializations of static audio clips.
private static File theTimeIsFile, timeForFile, repeatFile, dexFile, hydFile, gabFile, andFile;
private static AudioInputStream theTimeIsStream, timeForStream, repeatStream, dexStream, hydStream, gabStream, andStream;
private static Clip theTimeIsClip, timeForClip, repeatClip, dexClip, hydClip, gabClip, andClip;
private static AudioFormat theTimeIsFormat, timeForFormat, repeatFormat, dexFormat, hydFormat, gabFormat, andFormat;
private static DataLine.Info theTimeIsInfo, timeForInfo, repeatInfo, dexInfo, hydInfo, gabInfo, andInfo;
private int time;
static {
theTimeIsFile = new File("C:/PillMonitor/ProgramData/Audio/TheTimeIs.wav");
timeForFile = new File("C:/PillMonitor/ProgramData/Audio/TimeFor.wav");
repeatFile = new File("C:/PillMonitor/ProgramData/Audio/Repeat.wav");
dexFile = new File("C:/PillMonitor/ProgramData/Audio/dex.wav");
hydFile = new File("C:/PillMonitor/ProgramData/Audio/hyd.wav");
gabFile = new File("C:/PillMonitor/ProgramData/Audio/gab.wav");
andFile = new File("C:/PillMonitor/ProgramData/Audio/and.wav");
try {
theTimeIsStream = AudioSystem.getAudioInputStream(theTimeIsFile);
timeForStream = AudioSystem.getAudioInputStream(timeForFile);
repeatStream = AudioSystem.getAudioInputStream(repeatFile);
dexStream = AudioSystem.getAudioInputStream(dexFile);
hydStream = AudioSystem.getAudioInputStream(hydFile);
gabStream = AudioSystem.getAudioInputStream(gabFile);
andStream = AudioSystem.getAudioInputStream(andFile);
}
catch(UnsupportedAudioFileException ex) {
System.out.println("Audio files are unsupported.");
System.exit(1);
} catch(IOException ex) {
System.out.println("IO Exception in FilePlayer SIB, AudioStream initialization.");
System.exit(1);
}
theTimeIsFormat = theTimeIsStream.getFormat();
timeForFormat = timeForStream.getFormat();
repeatFormat = repeatStream.getFormat();
dexFormat = dexStream.getFormat();
hydFormat = hydStream.getFormat();
gabFormat = gabStream.getFormat();
andFormat = andStream.getFormat();
theTimeIsInfo = new DataLine.Info(Clip.class, theTimeIsFormat);
timeForInfo = new DataLine.Info(Clip.class, timeForFormat);
repeatInfo = new DataLine.Info(Clip.class, repeatFormat);
dexInfo = new DataLine.Info(Clip.class, dexFormat);
hydInfo = new DataLine.Info(Clip.class, hydFormat);
gabInfo = new DataLine.Info(Clip.class, gabFormat);
andInfo = new DataLine.Info(Clip.class, andFormat);
try {
theTimeIsClip = (Clip) AudioSystem.getLine(theTimeIsInfo);
timeForClip = (Clip) AudioSystem.getLine(timeForInfo);
repeatClip = (Clip) AudioSystem.getLine(repeatInfo);
dexClip = (Clip) AudioSystem.getLine(dexInfo);
hydClip = (Clip) AudioSystem.getLine(hydInfo);
gabClip = (Clip) AudioSystem.getLine(gabInfo);
andClip = (Clip) AudioSystem.getLine(andInfo);
}
catch(LineUnavailableException ex) {
System.out.println("Audio line unavailable exception in FilePlayer static initialization block.");
System.exit(1);
}
try {
theTimeIsClip.open(theTimeIsStream);
timeForClip.open(timeForStream);
repeatClip.open(repeatStream);
dexClip.open(dexStream);
hydClip.open(hydStream);
gabClip.open(gabStream);
andClip.open(andStream);
}
catch(LineUnavailableException ex) {
System.out.println("LU-Exception in FilePlayer static-init-block opening methods.");
System.exit(1);
}
catch(IOException ex) {
System.out.println("IOException in FilePlayer static-init-block opening methods.");
System.exit(1);
}
}
//Parsing data to store the length of each audio clip in a static variable.
private static double theTimeIsTimeVar, timeForTimeVar, repeatTimeVar, dexTimeVar, hydTimeVar, gabTimeVar, andTimeVar;
private static long theTimeIsTime, timeForTime, repeatTime, dexTime, hydTime, gabTime, andTime;
static {
//Here we get the lengths in milliseconds
theTimeIsTimeVar = theTimeIsClip.getMicrosecondLength() * 0.001;
timeForTimeVar = timeForClip.getMicrosecondLength() * 0.001;
repeatTimeVar = repeatClip.getMicrosecondLength() * 0.001;
dexTimeVar = dexClip.getMicrosecondLength() * 0.001;
hydTimeVar = hydClip.getMicrosecondLength() * 0.001;
gabTimeVar = gabClip.getMicrosecondLength() * 0.001;
andTimeVar = andClip.getMicrosecondLength() * 0.001;
//Now transfer the milliseconds to BigDecimal objects
BigDecimal theTimeIsBD = new BigDecimal(theTimeIsTimeVar);
BigDecimal timeForBD = new BigDecimal(timeForTimeVar);
BigDecimal repeatBD = new BigDecimal(repeatTimeVar);
BigDecimal dexBD = new BigDecimal(dexTimeVar);
BigDecimal hydBD = new BigDecimal(hydTimeVar);
BigDecimal gabBD = new BigDecimal(gabTimeVar);
BigDecimal andBD = new BigDecimal(andTimeVar);
//Now we return the data to the final variables which will be used, converted into long data type.
theTimeIsTime = theTimeIsBD.longValue();
timeForTime = timeForBD.longValue();
repeatTime = repeatBD.longValue();
dexTime = dexBD.longValue();
hydTime = hydBD.longValue();
gabTime = gabBD.longValue();
andTime = andBD.longValue();
}
public FilePlayer(int time) { //The FilePlayer must be instantiated with the current HOUR_OF_DAY, transferred here to the
//private instance variable "time."
this.time = time;
}
public void playAudio() { //Choose which audio sequence to play based on the time variable.
switch(time) {
case 8: try {play08();}
catch(IOException ex) {System.out.println("IOEx in FP switchblock (method play08())."); System.exit(1);}
catch(UnsupportedAudioFileException ex) {System.out.println("USAF-Exception in FP switch method 08."); System.exit(1);}
catch(LineUnavailableException ex) {System.out.println("LU-Exception FP SB m-8."); System.exit(1);}
catch(InterruptedException ex) {System.out.println("INT-Exception FP SB m-8."); System.exit(1);}
break;
case 12: try {play12();}
catch(IOException ex) {System.out.println("IOEx in FP switchblock (method play12())."); System.exit(1);}
catch(UnsupportedAudioFileException ex) {System.out.println("USAF-Exception in FP switch method 12."); System.exit(1);}
catch(LineUnavailableException ex) {System.out.println("LU-Exception FP SB m-12."); System.exit(1);}
catch(InterruptedException ex) {System.out.println("INT-Exception FP SB m-12."); System.exit(1);}
break;
case 16: try {play16();}
catch(IOException ex) {System.out.println("IOEx in FP switchblock (method play16())."); System.exit(1);}
catch(UnsupportedAudioFileException ex) {System.out.println("USAF-Exception in FP switch method 16."); System.exit(1);}
catch(LineUnavailableException ex) {System.out.println("LU-Exception FP SB m-16."); System.exit(1);}
catch(InterruptedException ex) {System.out.println("INT-Exception FP SB m-16."); System.exit(1);}
break;
case 20: try {play20();}
catch(IOException ex) {System.out.println("IOEx in FP switchblock (method play20())."); System.exit(1);}
catch(UnsupportedAudioFileException ex) {System.out.println("USAF-Exception in FP switch method 20."); System.exit(1);}
catch(LineUnavailableException ex) {System.out.println("LU-Exception FP SB m-20."); System.exit(1);}
catch(InterruptedException ex) {System.out.println("INT-Exception FP SB m-20."); System.exit(1);}
break;
}
}
private void play08() throws UnsupportedAudioFileException, LineUnavailableException, InterruptedException, IOException {
//for() loop to play the audio files twice
for(int i = 0; i < 2; i++) {
theTimeIsClip.start();
Thread.currentThread().sleep(theTimeIsTime); //Sleep for the length of the clip - this is why we had the
theTimeIsClip.stop(); //clip length vars earlier.
theTimeIsClip.setFramePosition(0); //Rewind clip because the audio is going to repeat.
//Now initialize and set up a clip for the appropriate time (this could have just as well been done statically
//earlier, but it wasn't worth changing that).
Clip timeClip;
File timeFile = new File("C:/PillMonitor/ProgramData/Audio/08.wav");
AudioInputStream stream = AudioSystem.getAudioInputStream(timeFile);
AudioFormat format = stream.getFormat();
DataLine.Info infos = new DataLine.Info(Clip.class, format);
timeClip = (Clip) AudioSystem.getLine(infos);
timeClip.open(stream);
double timeTimeVar = timeClip.getMicrosecondLength() * 0.001;
BigDecimal timeTimeBD = new BigDecimal(timeTimeVar);
long timeTime = timeTimeBD.longValue();
timeClip.start();
Thread.currentThread().sleep(timeTime);
timeClip.stop();
timeClip.setFramePosition(0);
//Play rest of the clips.
timeForClip.start();
Thread.currentThread().sleep(timeForTime);
timeForClip.stop();
timeForClip.setFramePosition(0);
dexClip.start();
Thread.currentThread().sleep(dexTime);
dexClip.stop();
dexClip.setFramePosition(0);
andClip.start();
Thread.currentThread().sleep(dexTime);
andClip.stop();
andClip.setFramePosition(0);
hydClip.start();
Thread.currentThread().sleep(dexTime);
hydClip.stop();
hydClip.setFramePosition(0);
if(i != 0) { //Unless it's the first iteration of the for() loop, break out of the loop here.
break;
}
repeatClip.start(); //Otherwise, play the "repeat" audio clip.
Thread.currentThread().sleep(repeatTime);
repeatClip.stop();
repeatClip.setFramePosition(0);
}
} //Subsequent play##() methods are alike.
private void play12() throws UnsupportedAudioFileException, LineUnavailableException, InterruptedException, IOException {
for(int i = 0; i < 2; i++) {
theTimeIsClip.start();
Thread.currentThread().sleep(theTimeIsTime);
theTimeIsClip.stop();
theTimeIsClip.setFramePosition(0);
Clip timeClip;
File timeFile = new File("C:/PillMonitor/ProgramData/Audio/12.wav");
AudioInputStream stream = AudioSystem.getAudioInputStream(timeFile);
AudioFormat format = stream.getFormat();
DataLine.Info infos = new DataLine.Info(Clip.class, format);
timeClip = (Clip) AudioSystem.getLine(infos);
timeClip.open(stream);
double timeTimeVar = timeClip.getMicrosecondLength() * 0.001;
BigDecimal timeTimeBD = new BigDecimal(timeTimeVar);
long timeTime = timeTimeBD.longValue();
timeClip.start();
Thread.currentThread().sleep(timeTime);
timeClip.stop();
timeClip.setFramePosition(0);
timeForClip.start();
Thread.currentThread().sleep(timeForTime);
timeForClip.stop();
timeForClip.setFramePosition(0);
hydClip.start();
Thread.currentThread().sleep(hydTime);
hydClip.stop();
hydClip.setFramePosition(0);
if(i != 0)
break;
repeatClip.start();
Thread.currentThread().sleep(repeatTime);
repeatClip.stop();
repeatClip.setFramePosition(0);
}
}
private void play16() throws UnsupportedAudioFileException, LineUnavailableException, InterruptedException, IOException {
for(int i = 0; i < 2; i++) {
theTimeIsClip.start();
Thread.currentThread().sleep(theTimeIsTime);
theTimeIsClip.stop();
theTimeIsClip.setFramePosition(0);
Clip timeClip;
File timeFile = new File("C:/PillMonitor/ProgramData/Audio/16.wav");
AudioInputStream stream = AudioSystem.getAudioInputStream(timeFile);
AudioFormat format = stream.getFormat();
DataLine.Info infos = new DataLine.Info(Clip.class, format);
timeClip = (Clip) AudioSystem.getLine(infos);
timeClip.open(stream);
double timeTimeVar = timeClip.getMicrosecondLength() * 0.001;
BigDecimal timeTimeBD = new BigDecimal(timeTimeVar);
long timeTime = timeTimeBD.longValue();
timeClip.start();
Thread.currentThread().sleep(timeTime);
timeClip.stop();
timeClip.setFramePosition(0);
timeForClip.start();
Thread.currentThread().sleep(timeForTime);
timeForClip.stop();
timeForClip.setFramePosition(0);
hydClip.start();
Thread.currentThread().sleep(hydTime);
hydClip.stop();
hydClip.setFramePosition(0);
if(i != 0)
break;
repeatClip.start();
Thread.currentThread().sleep(repeatTime);
repeatClip.stop();
repeatClip.setFramePosition(0);
}
}
private void play20() throws UnsupportedAudioFileException, LineUnavailableException, InterruptedException, IOException {
for(int i = 0; i < 2; i++) {
theTimeIsClip.start();
Thread.currentThread().sleep(theTimeIsTime);
theTimeIsClip.stop();
theTimeIsClip.setFramePosition(0);
Clip timeClip;
File timeFile = new File("C:/PillMonitor/ProgramData/Audio/20.wav");
AudioInputStream stream = AudioSystem.getAudioInputStream(timeFile);
AudioFormat format = stream.getFormat();
DataLine.Info infos = new DataLine.Info(Clip.class, format);
timeClip = (Clip) AudioSystem.getLine(infos);
timeClip.open(stream);
double timeTimeVar = timeClip.getMicrosecondLength() * 0.001;
BigDecimal timeTimeBD = new BigDecimal(timeTimeVar);
long timeTime = timeTimeBD.longValue();
timeClip.start();
Thread.currentThread().sleep(timeTime);
timeClip.stop();
timeClip.setFramePosition(0);
timeForClip.start();
Thread.currentThread().sleep(timeForTime);
timeForClip.stop();
timeForClip.setFramePosition(0);
hydClip.start();
Thread.currentThread().sleep(hydTime);
hydClip.stop();
hydClip.setFramePosition(0);
andClip.start();
Thread.currentThread().sleep(andTime);
andClip.stop();
andClip.setFramePosition(0);
gabClip.start();
Thread.currentThread().sleep(gabTime);
gabClip.stop();
gabClip.setFramePosition(0);
if(i != 0)
break;
repeatClip.start();
Thread.currentThread().sleep(repeatTime);
repeatClip.stop();
repeatClip.setFramePosition(0);
}
}
}
class WaitingMachine {
private long target8, target12, target16, target20;
private long midnight, nextMorning;
{ //All the code in this initialization block parses the epoch milliseconds into variables.
Date target8Date, target12Date, target16Date, target20Date; //Calendar returns a Date object for its epoch millisecond method,
//so we have to parse the data through those.
Calendar target8Cal = Calendar.getInstance();
Calendar target12Cal = Calendar.getInstance();
Calendar target16Cal = Calendar.getInstance();
Calendar target20Cal = Calendar.getInstance();
Calendar[] calAr = new Calendar[4]; //Sort the calendars into an array, then use the for() loop to set all their minute, second
calAr[0] = target8Cal; //and millisecond values to 0.
calAr[1] = target12Cal;
calAr[2] = target16Cal;
calAr[3] = target20Cal;
for(int i = 0; i < calAr.length; i++) {
calAr[i].set(calAr[i].MINUTE, 0);
calAr[i].set(calAr[i].SECOND, 0);
calAr[i].set(calAr[i].MILLISECOND, 0);
}
calAr[0].set(calAr[0].HOUR_OF_DAY, 8); //Set these calendars' hours to the target times.
calAr[1].set(calAr[1].HOUR_OF_DAY, 12);
calAr[2].set(calAr[2].HOUR_OF_DAY, 16);
calAr[3].set(calAr[3].HOUR_OF_DAY, 20);
Calendar morning = calAr[0]; //Create a target time to represent the 8 AM execution of the next day.
//We go by DAY_OF_YEAR to avoid throwing exceptions at the end of each month
//because of the +1 incrementation of the day.
if(Calendar.getInstance().get(Calendar.DAY_OF_YEAR) != 365)
morning.set(morning.DAY_OF_YEAR, (morning.get(morning.DAY_OF_YEAR) + 1));
else
morning.set(morning.DAY_OF_YEAR, 1);
Date mornDate = morning.getTime();
nextMorning = mornDate.getTime();
target8Date = calAr[0].getTime(); //Parsing target times into Date objects and then out of them.
target12Date = calAr[1].getTime();
target16Date = calAr[2].getTime();
target20Date = calAr[3].getTime();
target8 = target8Date.getTime();
target12 = target12Date.getTime();
target16 = target16Date.getTime();
target20 = target20Date.getTime();
Calendar midNight = Calendar.getInstance(); //Setting up tomorrow's midnight, much like for tomorrow's 8 AM above.
if(Calendar.getInstance().get(Calendar.DAY_OF_YEAR) != 365)
midNight.set(midNight.DAY_OF_YEAR, (Calendar.getInstance().get(Calendar.DAY_OF_YEAR) + 1));
else
midNight.set(midNight.DAY_OF_YEAR, 1);
midNight.set(midNight.HOUR_OF_DAY, 0);
midNight.set(midNight.MINUTE, 0);
midNight.set(midNight.SECOND, 0);
midNight.set(midNight.MILLISECOND, 0);
Date nightDate = midNight.getTime();
midnight = nightDate.getTime();
}
private long currentTime;
private long currentTarget;
private boolean nightFlag = false; //This is used to show that the final execution of the day has run and the special night sleep
//must execute.
public WaitingMachine() {
currentTime = System.currentTimeMillis(); //Check what the current time in epoch milllis is, and set the target time
//to be the next target hour, whatever that may be.
if(midnight <= currentTime && currentTime <= target8)
currentTarget = target8;
else if(target8 < currentTime && currentTime <= target12)
currentTarget = target12;
else if(target12 < currentTime && currentTime <= target16)
currentTarget = target16;
else if(target16 < currentTime && currentTime <= target20)
currentTarget = target20;
else
nightFlag = true; //Set night flag if it's after 8 PM.
}
public int waitFor() {
if(nightFlag) //If it's after 8 PM, execute special night sleep code.
nightWatch();
try {
Thread.currentThread().sleep((currentTarget - currentTime) + 100); //Sleep until target time. 100 millis added to avert
//returning the previous hour instead of current.
} catch(InterruptedException ex) {
System.out.println("Interrupt exception in waitFor() sleep() method.");
System.exit(1);
}
return Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
}
private void nightWatch() { //This method sleeps until midnight, then resumes normal execution from midnight til 8 AM.
try {
Thread.currentThread().sleep((midnight - currentTime) + 100);
} catch(InterruptedException ex) {
System.out.println("Interrupt exception in nightWatch() sleep() method.");
System.exit(1);
}
nightFlag = false;
currentTime = System.currentTimeMillis();
currentTarget = nextMorning;
}
}
public class PillMonitor {
public static void main(String[] args) {
System.out.println("Pill Monitor v1.2. Do not close this window.");
for(;;) { //Get time from WaitingMachine object, create and execute Watcher and FilePlayer with that data.
WaitingMachine waiter = new WaitingMachine();
int pillTimer = waiter.waitFor();
new Watcher(pillTimer).start();
FilePlayer player = new FilePlayer(pillTimer);
player.playAudio();
}
}
}
class Watcher extends Thread{
private int hour;
private static boolean isWritten = false; //Meaning, is an (unacknowledged) output line currently written to the screen.
private static Scanner scanner = new Scanner(System.in); //Scanner must be static or else it will re-parse the first character in
//System.in every time the object's run() method executes.
public Watcher(int hour) {
this.hour = hour;
}
@Override
public void run() {
int localHour = hour; //Buffer variable; allows us to work with the HOUR_OF_DAY yet print an AM/PM time to output.
if(hour == 16)
localHour = 4;
else if(hour == 20)
localHour = 8;
if(isWritten) { //If there is an unacknowledged pill alert still on screen, print an alert for it before printing this object's pill alert.
System.out.println("\n\t-ALERT: Pill not taken or registered.");
}
System.out.printf("\nLast pill: %s Time: %d", getPill(hour), localHour);
System.out.print(":00"); //This makes the output look like a normal time. We know it's always going to be on the hour,
//so we can just print ":00" safely.
if(hour == 8)
System.out.print(" AM");
else
System.out.print(" PM");
if(!isWritten) { //If the last pill was checked (or this is the first alert), we block on input and set the isWritten flag to true.
isWritten = true;
long skip = 0; //Following two try blocks are scanning ahead and clearing the input stream
//If this is not done, any accidental non-enter key presses someone may have hit
//Will be stuck there and get read as valid data to justify continuing execution
try {
skip = System.in.available();
} catch(IOException ex) {
System.out.println("\nIOException in Watcher thread, System.in.available() method.");
System.exit(1);
}
if(skip != 0) {
try {
System.in.skip(skip);
} catch(IOException ex) {
System.out.println("\nIOException in Watcher thread, System.in.skip() method.");
System.exit(1);
}
}
scanner.nextLine(); //Scan in a line of input (this will block until the enter key is pressed).
System.out.print("\t-Pill taken\n");
isWritten = false;
}
}
private static String getPill(int h) { //Returns the name of the appropriate pill(s) to be taken for screen output based on the hour.
String s = "";
switch(h) {
case 8: s = "Dexa and Hydro";
break;
case 12:
case 16:
s = "Hydro";
break;
case 20: s = "Hydro and Gaba";
break;
}
return s;
}
}