/*********************************************************************** * 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 tag * ******** December 7, 2002 ********************************************* * - Enabled font size control through the 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 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