/***********************************************************************
* JazzMachine.java - Source Code (requires java 1.1) *
* Version release date : June 24, 2003 *
* Copyright (C) 2000-2003 Neural Semantics sprl, Belgium *
* Author : Michel Petre mpetre@neuralsemantics.com *
* *
* http://www.neuralsemantics.com/ *
* *
* This code is released under GNU GPL license, version 2 or later, *
* with the following modifications : *
* *
* Commercial use - you are free to use any part of this source *
* code in any commercial software, provided that : *
* 1. If you distribute the source code, this copyright notice *
* must remain unchanged. *
* 2. A credit must be added to your software and documentation, *
* on every place where your own copyright notices appear. *
* 3. A free licensed copy of your software must be provided *
* to Neural Semantics. *
* More questions ? report@neuralsemantics.com *
* *
* Non commercial software - A credit must be added to your software *
* and documentation. For applets, a link to our web page is enough. *
* *
* Use and modifications of the software : parts of this code can *
* only be used in software that are - conceptually - clearly *
* different from this one. This means that just making cosmetic *
* changes to the code in order to remove/change the displayed *
* copyright notice or the link to our web page is expressly *
* forbidden. *
* *
* This notice must remain intact in all copies of this code. *
* This code is distributed WITHOUT ANY WARRANTY OF ANY KIND. *
* The GNU GPL license can be found at : *
* http://www.gnu.org/copyleft/gpl.html *
* *
* Please feel free to use any portion of this code for your best *
* purposes ! *
***********************************************************************/
/* History & changes **************************************************
* *
******** August 18, 2000 **********************************************
* - First release *
******** November 14, 2000 ********************************************
* - Added setForeground(Color c) to the labels, to overcome a *
* minor color inheritance bug in some IE JVM's *
******** May 21, 2000 *************************************************
* - Added standard hyperlink panel *
* - Changed static variable names to uppercase *
* - Display output sound frequency instead of requested frequency *
******** January 30, 2002 *********************************************
* - Lowered volume range and default volume level *
* - Enabled applet color control through the <APPLET> tag *
******** December 7, 2002 *********************************************
* - Enabled font size control through the <APPLET> tag *
******** June 24, 2003 ************************************************
* - Changed hyperlink panel *
***********************************************************************/
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class JazzMachine extends Applet
implements Runnable, AdjustmentListener, MouseListener {
// program name
final static String TITLE = "The jazz machine";
// Line separator char
final static String LSEP = System.getProperty("line.separator");
// Value range
final static int MIN_FREQ = 1; // min value for barFreq
final static int MAX_FREQ = 200; // max value for barFreq
final static int MIN_AMPL = 0; // min value for barVolume
final static int MAX_AMPL = 100; // max value for barVolume
// Sun's mu-law audio rate = 8KHz
private double rate = 8000d;
private boolean audioOn = false; // state of audio switch (on/off)
private boolean changed = true; // change flag
private int freqValue = 1; // def value of frequency scrollbar
private int amplValue = 70; // def value of volume scrollbar
private int amplMultiplier = 100; // volume multiplier coeff
private int frequency, amplitude; // the requested values
private int soundFrequency; // the actual output frequency
// the mu-law encoded sound samples
private byte[] mu;
// the audio stream
private java.io.InputStream soundStream;
// graphic components
private Scrollbar barVolume, barFreq;
private Label labelValueFreq;
private Canvas canvas;
// flag for frequency value display
private boolean showFreq = true;
// width and height of canvas area
private int cw, ch;
// offscreen Image and Graphics objects
private Image img;
private Graphics graph;
// dimensions of the graphic ball
private int ovalX, ovalY, ovalW, ovalH;
// color of the graphic ball
private Color ovalColor;
// default font size
private int fontSize = 12;
// hyperlink objects
private Panel linkPanel;
private Label labelNS;
private Color inactiveLinkColor = new Color(255,255,128);
private Color activeLinkColor = Color.white;
private Font linkFont = new Font("Dialog", Font.PLAIN, fontSize);
// standard font for the labels
private Font ctrlFont;
// standard foreground color for the labels
private Color fgColor = Color.white;
// standard background color for the control area
private Color ctrlColor = Color.darkGray;
// standard background color for the graphic ball area
private Color bgColor = Color.black;
// start value for the time counter
private long startTime;
// maximum life time for an unchanged sound (10 seconds)
private long fixedTime = 10000;
// this applet's context
private AppletContext appletContext;
// animation thread
Thread runner;
//**********************************************************************
// Constructors
//**********************************************************************
public JazzMachine() {
}
//**********************************************************************
// Methods
//**********************************************************************
public void init() {
// read applet <PARAM> tags
try {
appletContext = getAppletContext();
this.setAppletParams();
}
catch(Exception e) {
appletContext = null;
}
// font for the labels
ctrlFont = new Font("Dialog", Font.PLAIN, fontSize);
// convert scrollbar values to real values (see below for details)
amplitude = (MAX_AMPL - amplValue) * amplMultiplier;
frequency = (int)Math.pow(1.2d, (double)(freqValue + 250) / 10.0);
setLayout(new BorderLayout());
setBackground(ctrlColor);
setForeground(fgColor);
Label labelVolume = new Label(" Volume ");
labelVolume.setForeground(fgColor);
labelVolume.setAlignment(Label.CENTER);
labelVolume.setFont(ctrlFont);
barVolume = new Scrollbar(Scrollbar.VERTICAL, amplValue, 1,
MIN_AMPL, MAX_AMPL + 1);
barVolume.addAdjustmentListener(this);
// assign fixed size to the scrollbar
Panel pVolume = new Panel();
pVolume.setLayout(null);
pVolume.add(barVolume);
barVolume.setSize(16, 90);
pVolume.setSize(16, 90);
Label labelFreq = new Label("Frequency");
labelFreq.setForeground(fgColor);
labelFreq.setAlignment(Label.RIGHT);
labelFreq.setFont(ctrlFont);
barFreq = new Scrollbar(Scrollbar.HORIZONTAL, freqValue, 1,
MIN_FREQ, MAX_FREQ);
barFreq.addAdjustmentListener(this);
// assign fixed size to the scrollbar
Panel pFreq = new Panel();
pFreq.setLayout(null);
pFreq.add(barFreq);
barFreq.setSize(140, 18);
pFreq.setSize(140, 18);
// show initial frequency value
labelValueFreq = new Label();
if (showFreq) {
labelValueFreq.setText("0000000 Hz");
labelValueFreq.setForeground(fgColor);
labelValueFreq.setAlignment(Label.LEFT);
labelValueFreq.setFont(ctrlFont);
}
Panel east = new Panel();
east.setLayout(new BorderLayout(10, 10));
east.add("North", labelVolume);
Panel pEast = new Panel();
pEast.add(pVolume);
east.add("Center", pEast);
Panel south = new Panel();
Panel pSouth = new Panel();
pSouth.setLayout(new FlowLayout(FlowLayout.CENTER));
pSouth.add(labelFreq);
pSouth.add(pFreq);
pSouth.add(labelValueFreq);
south.add("South", pSouth);
this.composeLink();
Panel west = new Panel();
// dummy label to enlarge the panel
west.add(new Label(" "));
add("North", linkPanel);
add("South", south);
add("East", east);
add("West", west);
add("Center", canvas = new Canvas());
}
//------------------------------------------------------------------------
private void composeLink() {
linkPanel = new Panel();
linkPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 5));
linkPanel.setFont(linkFont);
linkPanel.setForeground(Color.yellow);
Label labelName = new Label(TITLE + " \u00a9");
labelName.setForeground(inactiveLinkColor);
labelName.setFont(linkFont);
labelName.setAlignment(Label.RIGHT);
labelNS = new Label(" Neural Semantics ");
labelNS.setForeground(inactiveLinkColor);
labelNS.setFont(linkFont);
labelNS.setAlignment(Label.LEFT);
Panel pNS = new Panel();
pNS.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
pNS.add(labelNS);
linkPanel.add(labelName);
linkPanel.add(pNS);
// link to Neural Semantics website
if (appletContext != null) {
String h = getDocumentBase().getHost();
if ((h.length() > 4) && (h.substring(0, 4).equals("www.")))
h = h.substring(4);
if ((h != null) && (! h.startsWith("neuralsemantics.com"))) {
// create a hand cursor for the hyperlink area
Cursor linkCursor = new Cursor(Cursor.HAND_CURSOR);
pNS.setCursor(linkCursor);
labelName.addMouseListener(this);
labelNS.addMouseListener(this);
}
}
}
//------------------------------------------------------------------------
private void switchAudio(boolean b) {
// switch audio to ON if b=true and audio is OFF
if ((b) && (! audioOn)) {
try {
sun.audio.AudioPlayer.player.start(soundStream);
}
catch(Exception e) { }
audioOn = true;
}
// switch audio to OFF if b=false and audio is ON
if ((! b) && (audioOn)) {
try {
sun.audio.AudioPlayer.player.stop(soundStream);
}
catch(Exception e) { }
audioOn = false;
}
}
//------------------------------------------------------------------------
private void getChanges() {
// create new sound wave
mu = getWave(frequency, amplitude);
// show new frequency value
if (showFreq)
labelValueFreq.setText((new Integer(soundFrequency)).toString() + " Hz");
// shut up !
switchAudio(false);
// switch audio stream to new sound sample
try {
soundStream = new sun.audio.ContinuousAudioDataStream(new
sun.audio.AudioData(mu));
}
catch(Exception e) { }
// listen
switchAudio(true);
// Adapt animation settings
double prop = (double)freqValue / (double)MAX_FREQ;
ovalW = (int)(prop * cw);
ovalH = (int)(prop * ch);
ovalX = (int)((cw - ovalW) / 2);
ovalY = (int)((ch - ovalH) / 2);
int r = (int)(255 * prop);
int b = (int)(255 * (1.0 - prop));
int g = (int)(511 * (.5d - Math.abs(.5d - prop)));
ovalColor = new Color(r, g, b);
// start the timer
startTime = System.currentTimeMillis();
// things are fixed
changed = false;
}
//**********************************************************************
// Thread
//**********************************************************************
public void start() {
// create thread
if (runner == null) {
runner = new Thread(this);
runner.start();
}
}
//------------------------------------------------------------------------
public void run() {
// infinite loop
while (true) {
// Volume or Frequency has changed ?
if (changed)
this.getChanges();
// a touch of hallucination
repaint();
// let the children sleep. Shut up if inactive during more
// than the fixed time.
if (System.currentTimeMillis() - startTime > fixedTime)
switchAudio(false);
// let the computer breath
try { Thread.sleep(100); }
catch (InterruptedException e) { }
}
}
//------------------------------------------------------------------------
public void stop() {
this.cleanup();
}
//------------------------------------------------------------------------
public void destroy() {
this.cleanup();
}
//------------------------------------------------------------------------
private synchronized void cleanup() {
// shut up !
switchAudio(false);
// kill the runner thread
if (runner != null) {
try {
runner.stop();
runner.join();
runner = null;
}
catch(Exception e) { }
}
}
//**********************************************************************
// AdjustmentListener Interface
//**********************************************************************
public void adjustmentValueChanged(AdjustmentEvent e) {
Object source = e.getSource();
// Volume range : 0 - 10000
// ! Scrollbar value range is inverted.
// ! 100 = multiplier coefficient.
if (source == barVolume) {
amplitude = (MAX_AMPL - barVolume.getValue()) * amplMultiplier;
changed = true;
}
// Frequency range : 97 - 3591 Hz
// ! Scrollbar value range represents a logarithmic function.
// The purpose is to assign more room for low frequency values.
else if (source == barFreq) {
freqValue = barFreq.getValue();
frequency = (int)Math.pow(1.2d, (double)(freqValue + 250) / 10.0);
changed = true;
}
}
//**********************************************************************
// MouseListener Interface
//**********************************************************************
public void mouseClicked(MouseEvent e) {
}
//-------------------------------------------
public void mouseEntered(MouseEvent e) {
// text color rollover
labelNS.setForeground(activeLinkColor);
if (appletContext != null)
showStatus("Visit Neural Semantics");
}
//-------------------------------------------
public void mouseExited(MouseEvent e) {
// text color rollover
labelNS.setForeground(inactiveLinkColor);
if (appletContext != null)
showStatus("");
}
//-------------------------------------------
public void mousePressed(MouseEvent e) {
// link to Neural Semantics website
if (appletContext != null) {
String h = getDocumentBase().getHost();
if ((h.length() > 4) && (h.substring(0, 4).equals("www.")))
h = h.substring(4);
if ((h != null) && (! h.startsWith("neuralsemantics.com"))) {
try {
java.net.URL url =
new java.net.URL("http://www.neuralsemantics.com/");
appletContext.showDocument(url);
}
catch(Exception ex){ }
}
}
}
//-------------------------------------------
public void mouseReleased(MouseEvent e) {
}
//**********************************************************************
// Painting
//**********************************************************************
public void update(Graphics g) {
Graphics canvasGraph = canvas.getGraphics();
if (img == null) {
// get canvas dimensions
cw = canvas.getSize().width;
ch = canvas.getSize().height;
// initialize offscreen image
img = createImage(cw, ch);
graph = img.getGraphics();
}
// offscreen painting
graph.setColor(bgColor);
graph.fillRect(0, 0, cw, ch);
graph.setColor(ovalColor);
graph.fillOval(ovalX, ovalY, ovalW, ovalH);
// canvas painting
if (canvasGraph != null) {
canvasGraph.drawImage(img, 0, 0, canvas);
canvasGraph.dispose();
}
}
//**********************************************************************
// Sound processing
//**********************************************************************
// Creates a sound wave from scratch, using predefined frequency
// and amplitude.
private byte[] getWave(int freq, int ampl) {
int lin;
// calculate the number of samples in one sinewave period
// !! change this to multiple periods if you need more precision !!
int nSample = (int)(rate / freq);
// calculate output wave frequency
soundFrequency = (int)(rate / nSample);
// create array of samples
byte[] wave = new byte[nSample];
// pre-calculate time interval & constant stuff
double timebase = 2.0 * Math.PI * freq / rate;
// Calculate samples for a single period of the sinewave.
// Using a single period is no big precision, but enough
// for this applet anyway !
for (int i=0; i<nSample; i++) {
// calculate PCM sample value
lin = (int)(Math.sin(timebase * i) * ampl);
// convert it to mu-law
wave[i] = linToMu(lin);
}
return wave;
}
/************************************************************************/
/* linToMu : performs PCM to mu-law conversion */
/* originally for Sparcstation 1. */
/* */
/* Original C code : */
/* */
/* Copyright 1989 by Rich Gopstein and Harris Corporation */
/* */
/* Permission to use, copy, modify, and distribute this software */
/* and its documentation for any purpose and without fee is */
/* hereby granted, provided that the above copyright notice */
/* appears in all copies and that both that copyright notice and */
/* this permission notice appear in supporting documentation, and */
/* that the name of Rich Gopstein and Harris Corporation not be */
/* used in advertising or publicity pertaining to distribution */
/* of the software without specific, written prior permission. */
/* Rich Gopstein and Harris Corporation make no representations */
/* about the suitability of this software for any purpose. It */
/* provided "as is" without express or implied warranty. */
/* */
/* Translated to Java by Neural Semantics sprl */
/* http://www.neuralsemantics.com/ */
/************************************************************************/
private static byte linToMu(int lin) {
int mask;
if (lin < 0) {
lin = -lin;
mask = 0x7F;
}
else {
mask = 0xFF;
}
if (lin < 32)
lin = 0xF0 | 15 - (lin / 2);
else if (lin < 96)
lin = 0xE0 | 15 - (lin-32) / 4;
else if (lin < 224)
lin = 0xD0 | 15 - (lin-96) / 8;
else if (lin < 480)
lin = 0xC0 | 15 - (lin-224) / 16;
else if (lin < 992)
lin = 0xB0 | 15 - (lin-480) / 32;
else if (lin < 2016)
lin = 0xA0 | 15 - (lin-992) / 64;
else if (lin < 4064)
lin = 0x90 | 15 - (lin-2016) / 128;
else if (lin < 8160)
lin = 0x80 | 15 - (lin-4064) / 256;
else
lin = 0x80;
return (byte)(mask & lin);
}
//**********************************************************************
// Applet info
//**********************************************************************
public String getAppletInfo() {
String s = "The jazz machine" + LSEP + LSEP +
"A music synthetizer applet" + LSEP +
"Copyright (c) Neural Semantics, 2000-2003" + LSEP + LSEP +
"Home page : http://www.neuralsemantics.com/";
return s;
}
//------------------------------------------------------------------------
private void setAppletParams() {
// read the HTML showfreq parameter
String param = getParameter("showfreq");
if (param != null)
if (param.toUpperCase().equals("OFF"))
showFreq = false;
// read the HTML backcolor parameter
bgColor = changeColor(bgColor, getParameter("backcolor"));
// read the HTML controlcolor parameter
ctrlColor = changeColor(ctrlColor, getParameter("controlcolor"));
// read the HTML textcolor parameter
fgColor = changeColor(fgColor, getParameter("textcolor"));
// read the HTML fontsize parameter
param = getParameter("fontsize");
if (param != null) {
try {
fontSize = Integer.valueOf(param).intValue();
}
catch(NumberFormatException e) { }
}
}
//------------------------------------------------------------------------
private Color changeColor(Color c, String s) {
if (s != null) {
try {
if (s.charAt(0) == '#')
c = new Color(Integer.valueOf(s.substring(1), 16).intValue());
else
c = new Color(Integer.valueOf(s).intValue());
}
catch (NumberFormatException e) { e.printStackTrace(); }
}
return c;
}
//**********************************************************************
// Interface
//**********************************************************************
public Dimension getPreferredSize() {
return new Dimension(getSize().width, getSize().height);
}
}
|