Soup - film development site
From Create52 Cookbook
Source code for timer applet
Version 1.01:
import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.TimerTask;
/**
* Quick film development timer using a bog standard Applet.
* Version 1.01.
*
* History:
* v1.00: initial release
* v1.01: moved timing logic and sound generation out of paint() method -
* sounds weren't being generated if applet wasn't visible (e.g. you
* were looking at another table in firefox, or minimised the browser)
*
* @author Alex Hunsley Jan 2008
*
*/
public class SoupTimer extends Applet {
private static final String APPLET_PARAM_TIME = "time";
private boolean loadingSounds = true;
private static final String[] soundNames = {"tick.au", "a440.au", "a880alarm.au"};
private static final int NUM_SOUNDS = soundNames.length;
private static final int SOUND_TICK = 0;
private static final int SOUND_BEEP = 1;
private static final int SOUND_BEEP_ALARM = 2;
private static final int MODE_WAITING_TO_START = 1;
private static final int MODE_COUNTING_DOWN = 2;
private static final int MODE_FINISHED_COUNTING_DOWN = 3;
private static final int MODE_COUNTING_UP = 4;
private int mode = MODE_WAITING_TO_START;
private static final int PAINT_UPDATE_PERIOD = 100; //msecs
private AudioClip[] clip = new AudioClip[NUM_SOUNDS];
private static GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
private static String[] fonts = ge.getAvailableFontFamilyNames();
private int fontStyle = Font.PLAIN;
private Font font = new Font("Helvetica", fontStyle, 72);
private long startTime = 0;
private int minutes = 1;
private int seconds = 30;
private int totalSeconds = 0;
private Timer timer;
private String lastMsg = null;
private String msg;
private int AGITATE_EVERY = 60;
private static final int NUM_BEEPS_WARNING = 4;
private String timeParam = "";
public void init() {
timeParam = getParameter(APPLET_PARAM_TIME);
if (timeParam == null || timeParam.length() == 0 || timeParam.indexOf(':') < 0) {
minutes = 4;
seconds = 0;
timeParam = "4:00";
}
else {
StringTokenizer strTok = new StringTokenizer(timeParam, ":");
String minsStr = strTok.nextToken();
String secsStr = "0";
if (strTok.hasMoreTokens()) {
secsStr = strTok.nextToken();
}
minutes = Integer.parseInt(minsStr);
seconds = Integer.parseInt(secsStr);
}
timer = new Timer();
this.addMouseListener(clickyListen);
totalSeconds = minutes * 60 + seconds;
msg = secsToClock(totalSeconds);
new Thread() {
public void run() {
for (int i=0; i < NUM_SOUNDS; i++) {
clip[i] = getAudioClip(getCodeBase(), soundNames[i]);
}
}
}.start();
}
MouseAdapter clickyListen = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (mode == MODE_WAITING_TO_START) {
mode = MODE_COUNTING_DOWN;
startTime = System.currentTimeMillis();
timer.schedule(tickTask,
0, // delay
PAINT_UPDATE_PERIOD); // period
clip[SOUND_TICK].play();
}
else if (mode == MODE_FINISHED_COUNTING_DOWN) {
mode = MODE_COUNTING_UP;
startTime = System.currentTimeMillis();
clip[SOUND_TICK].play();
}
repaint();
}
};
public void stop() {
if (tickTask != null) {
tickTask.cancel();
}
}
private static final String secsToClock(int totalSecs) {
int secs = totalSecs % 60;
int min = (totalSecs - secs) / 60;
StringBuffer sBuff = new StringBuffer();
if (min < 10) {
sBuff.append("0");
}
sBuff.append(Integer.toString(min));
sBuff.append(":");
if (secs < 10) {
sBuff.append("0");
}
sBuff.append(Integer.toString(secs));
return sBuff.toString();
}
private TimerTask tickTask = new TimerTask() {
public void run() {
int elapsedSecs = 0;
//if (mode != MODE_FINISHED_COUNTING_DOWN) {
long currentTime = System.currentTimeMillis();
elapsedSecs = (int)((currentTime - startTime) / 1000);
if (mode == MODE_WAITING_TO_START) {
elapsedSecs = 0;
}
//}
//else {
// elapsedSecs = 0;
//}
int absSecs;
if (mode != MODE_COUNTING_UP) {
absSecs = totalSeconds - elapsedSecs;
}
else {
absSecs = elapsedSecs;
}
int clockSecs = absSecs;
if (absSecs == 0 && mode == MODE_COUNTING_DOWN) {
mode = MODE_FINISHED_COUNTING_DOWN;
//tickTask.cancel();
playFinalAlarm();
}
if (clockSecs < 0) {
clockSecs = 0;
}
msg = secsToClock(clockSecs);
if (lastMsg == null) {
lastMsg = msg;
}
if (! msg.equals(lastMsg)) {
lastMsg = msg;
if (mode != MODE_COUNTING_UP) {
int modSecs = elapsedSecs % AGITATE_EVERY;
if (absSecs <= NUM_BEEPS_WARNING) {
clip[SOUND_BEEP].play();
}
else if (modSecs == 0 || modSecs >= (AGITATE_EVERY - NUM_BEEPS_WARNING)) {
clip[SOUND_TICK].play();
}
}
repaint();
}
}
};
protected void playFinalAlarm() {
new Thread() {
public void run() {
for (int i = 0; i < 10; i++) {
clip[SOUND_BEEP_ALARM].play();
try {
sleep(500);
}
catch (Exception e) {
// nowt
}
}
}
}.start();
}
public void paint(Graphics g) {
if (msg == null) {
return;
}
g.setColor(Color.black);
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
g.drawRect(1, 1, getWidth() - 3, getHeight() - 3);
//URL docBase = getCodeBase();
if (mode == MODE_WAITING_TO_START) {
g.drawString("Please click to start timer", 20, 20);
}
g.setFont(font);
FontMetrics fm = g.getFontMetrics ();
int msg_width = fm.stringWidth (msg);
int ascent = fm.getMaxAscent ();
int descent= fm.getMaxDescent ();
int msg_x = getSize().width/2 - msg_width/2;
int msg_y = getSize().height/2 - descent/2 + ascent/2;
if (mode == MODE_FINISHED_COUNTING_DOWN) {
g.setColor(Color.red);
}
else if (mode == MODE_COUNTING_UP) {
g.setColor(Color.blue);
} else if (mode == MODE_WAITING_TO_START) {
g.setColor(Color.gray);
}
g.drawString (msg, msg_x, msg_y);
}
}
Version 1.0, daddy:
import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.URL;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.TimerTask;
/**
* Quick film development timer using a bog standard Applet.
* Version 1.0.
*
* @author Alex Hunsley Jan 2008
*
*/
public class SoupTimer extends Applet {
private static final String APPLET_PARAM_TIME = "time";
private boolean loadingSounds = true;
private static final String[] soundNames = {"tick.au", "a440.au", "a880alarm.au"};
private static final int NUM_SOUNDS = soundNames.length;
private static final int SOUND_TICK = 0;
private static final int SOUND_BEEP = 1;
private static final int SOUND_BEEP_ALARM = 2;
private static final int MODE_WAITING_TO_START = 1;
private static final int MODE_COUNTING_DOWN = 2;
private static final int MODE_FINISHED_COUNTING_DOWN = 3;
private static final int MODE_COUNTING_UP = 4;
private int mode = MODE_WAITING_TO_START;
private static final int PAINT_UPDATE_PERIOD = 100; //msecs
private AudioClip[] clip = new AudioClip[NUM_SOUNDS];
private static GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
private static String[] fonts = ge.getAvailableFontFamilyNames();
private int fontStyle = Font.PLAIN;
private Font font = new Font("Helvetica", fontStyle, 72);
private long startTime = 0;
private int minutes = 1;
private int seconds = 30;
private int totalSeconds = 0;
private Timer timer;
private String lastMsg = null;
private int AGITATE_EVERY = 60;
private static final int NUM_BEEPS_WARNING = 4;
private String timeParam = "";
public void init() {
timeParam = getParameter(APPLET_PARAM_TIME);
if (timeParam == null || timeParam.length() == 0 || timeParam.indexOf(':') < 0) {
minutes = 4;
seconds = 0;
}
else {
StringTokenizer strTok = new StringTokenizer(timeParam, ":");
String minsStr = strTok.nextToken();
String secsStr = "0";
if (strTok.hasMoreTokens()) {
secsStr = strTok.nextToken();
}
minutes = Integer.parseInt(minsStr);
seconds = Integer.parseInt(secsStr);
}
timer = new Timer();
this.addMouseListener(clickyListen);
totalSeconds = minutes * 60 + seconds;
new Thread() {
public void run() {
for (int i=0; i < NUM_SOUNDS; i++) {
clip[i] = getAudioClip(getCodeBase(), soundNames[i]);
}
}
}.start();
}
MouseAdapter clickyListen = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (mode == MODE_WAITING_TO_START) {
mode = MODE_COUNTING_DOWN;
startTime = System.currentTimeMillis();
timer.schedule(tickTask,
0, // delay
PAINT_UPDATE_PERIOD); // period
clip[SOUND_TICK].play();
}
else if (mode == MODE_FINISHED_COUNTING_DOWN) {
mode = MODE_COUNTING_UP;
startTime = System.currentTimeMillis();
clip[SOUND_TICK].play();
}
repaint();
}
};
public void stop() {
if (tickTask != null) {
tickTask.cancel();
}
}
private static final String secsToClock(int totalSecs) {
int secs = totalSecs % 60;
int min = (totalSecs - secs) / 60;
StringBuffer sBuff = new StringBuffer();
if (min < 10) {
sBuff.append("0");
}
sBuff.append(Integer.toString(min));
sBuff.append(":");
if (secs < 10) {
sBuff.append("0");
}
sBuff.append(Integer.toString(secs));
return sBuff.toString();
}
private TimerTask tickTask = new TimerTask() {
public void run() {
repaint();
}
};
protected void playFinalAlarm() {
new Thread() {
public void run() {
for (int i = 0; i < 10; i++) {
clip[SOUND_BEEP_ALARM].play();
try {
sleep(500);
}
catch (Exception e) {
// nowt
}
}
}
}.start();
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
g.drawRect(1, 1, getWidth() - 3, getHeight() - 3);
//URL docBase = getCodeBase();
if (mode == MODE_WAITING_TO_START) {
g.drawString("Please click to start timer", 20, 20);
}
g.setFont(font);
FontMetrics fm = g.getFontMetrics ();
String msg = timeParam;
int elapsedSecs = 0;
//if (mode != MODE_FINISHED_COUNTING_DOWN) {
long currentTime = System.currentTimeMillis();
elapsedSecs = (int)((currentTime - startTime) / 1000);
if (mode == MODE_WAITING_TO_START) {
elapsedSecs = 0;
}
//}
//else {
// elapsedSecs = 0;
//}
int absSecs;
if (mode != MODE_COUNTING_UP) {
absSecs = totalSeconds - elapsedSecs;
}
else {
absSecs = elapsedSecs;
}
int clockSecs = absSecs;
if (absSecs == 0 && mode == MODE_COUNTING_DOWN) {
mode = MODE_FINISHED_COUNTING_DOWN;
//tickTask.cancel();
playFinalAlarm();
}
if (clockSecs < 0) {
clockSecs = 0;
}
msg = secsToClock(clockSecs);
if (lastMsg == null) {
lastMsg = msg;
}
if (mode != MODE_COUNTING_UP) {
if (! msg.equals(lastMsg)) {
lastMsg = msg;
int modSecs = elapsedSecs % AGITATE_EVERY;
if (absSecs <= NUM_BEEPS_WARNING) {
clip[SOUND_BEEP].play();
}
else if (modSecs == 0 || modSecs >= (AGITATE_EVERY - NUM_BEEPS_WARNING)) {
clip[SOUND_TICK].play();
}
}
}
int msg_width = fm.stringWidth (msg);
int ascent = fm.getMaxAscent ();
int descent= fm.getMaxDescent ();
int msg_x = getSize().width/2 - msg_width/2;
int msg_y = getSize().height/2 - descent/2 + ascent/2;
if (mode == MODE_FINISHED_COUNTING_DOWN) {
g.setColor(Color.red);
}
else if (mode == MODE_COUNTING_UP) {
g.setColor(Color.blue);
} else if (mode == MODE_WAITING_TO_START) {
g.setColor(Color.gray);
}
g.drawString (msg, msg_x, msg_y);
}
}
Database update 2008-01-07
Records 359-439 created in souptimes table as so:
,4,14,5,11,4:00,,0,1,0 ,4,14,5,4,5:00,,0,1,0 ,4,14,4,6,5:00,,0,1,0 ,4,14,4,7,7:30,,0,1,0 ,4,14,4,8,11:00,,0,1,0 ,4,14,2,16,4:00,,0,1,0 ,4,14,2,6,5:30,,0,1,0 ,4,14,2,7,7:30,,0,1,0 ,4,14,2,8,13:00,,0,1,0 ,4,14,1,7,5:00,,0,1,0 ,4,14,1,8,8:00,,0,1,0 ,4,14,1,9,13:00,,0,1,0 ,4,14,7,4,5:00,,0,1,0 ,4,14,7,5,7:00,,0,1,0 ,4,14,7,6,10:30,,0,1,0 ,4,15,6,2,4:00,,0,1,0 ,4,15,5,2,6:00,,0,1,0 ,4,15,5,11,8:00,,0,1,0 ,4,15,5,4,9:00,,0,1,0 ,4,15,4,5,6:30,,0,1,0 ,4,15,4,6,9:30,,0,1,0 ,4,15,4,7,14:00,,0,1,0 ,4,15,3,2,5:00,,0,1,0 ,4,15,3,2,6:00,,0,1,0 ,4,15,3,2,8:00,,0,1,0 ,4,15,2,4,5:00,,0,1,0 ,4,15,2,5,7:30,,0,1,0 ,4,15,2,6,10:00,,0,1,0 ,4,15,2,7,13:30,,0,1,0 ,4,15,1,5,6:00,,0,1,0 ,4,15,1,6,7:30,,0,1,0 ,4,15,1,7,9:00,,0,1,0 ,4,15,1,8,14:30,,0,1,0 ,4,15,7,4,9:00,,0,1,0 ,4,15,7,5,13:00,,0,1,0 ,4,15,7,6,19:00,,0,1,0 ,5,1,6,2,4:30,,0,1,0 ,5,1,5,11,8:00,,0,1,0 ,5,1,5,11,9:00,,0,1,0 ,5,1,4,5,6:30,,0,1,0 ,5,1,4,6,8:00,,0,1,0 ,5,1,4,7,11:00,,0,1,0 ,5,1,4,8,16:00,,0,1,0 ,5,1,3,3,6:30,,0,1,0 ,5,1,3,4,8:00,,0,1,0 ,5,1,2,4,5:00,,0,1,0 ,5,1,2,5,6:30,,0,1,0 ,5,1,2,15,7:30,,0,1,0 ,5,1,2,6,8:30,,0,1,0 ,5,1,2,7,10:30,,0,1,0 ,5,1,2,8,14:00,,0,1,0 ,5,1,1,5,6:00,,0,1,0 ,5,1,1,6,7:00,,0,1,0 ,5,1,1,7,8:00,,0,1,0 ,5,1,1,8,9:00,,0,1,0 ,5,1,1,9,12:00,,0,1,0 ,5,1,1,14,16:30,,0,1,0 ,5,1,7,4,8:30,,0,1,0 ,5,1,7,5,10:30,,0,1,0 ,5,1,7,6,14:30,,0,1,0 ,5,2,6,2,6:00,,0,1,0 ,5,2,5,11,10:00,,0,1,0 ,5,2,5,4,14:00,,0,1,0 ,5,2,4,5,12:00,,0,1,0 ,5,2,4,6,15:00,,0,1,0 ,5,2,3,3,10:00,,0,1,0 ,5,2,3,4,14:00,,0,1,0 ,5,2,2,4,8:30,,0,1,0 ,5,2,2,5,11:30,,0,1,0 ,5,2,2,15,13:30,,0,1,0 ,5,2,2,6,15:30,,0,1,0 ,5,2,2,7,19:00,,0,1,0 ,5,2,7,4,15:30,,0,1,0 ,5,2,7,5,19:00,,0,1,0 ,5,2,6,2,11:00,,0,1,0 ,5,2,5,11,14:00,,0,1,0 ,5,2,5,4,18:00,,0,1,0 ,5,2,4,5,23:00,,0,1,0 ,5,2,3,3,14:00,,0,1,0 ,5,2,3,4,20:00,,0,1,0 ,5,2,2,4,16:00,,0,1,0
This data was from the document linked to at http://www.ilfordphoto.com/applications/page.asp?n=87&t=Developing+Black+and+white+film (direct link to file: http://www.ilfordphoto.com/applications/download.asp?n=429)
The data imported was for developers HC and Microphen, all listed dilutions.

