import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; // Additional imports for sound player import java.net.*; import java.io.*; import java.applet.*; import javax.sound.midi.*; import javax.sound.sampled.*; public class CoStarsComponent extends JComponent implements MouseListener, MouseMotionListener, KeyListener, ActionListener // for timer { ////////////////////////////////////// // PUBLIC INTERFACE ////////////////////////////////////// // Methods to override [ all may use width, height ] public void start() { } public void step() { } public void paint() { } // uses page public void mouse() { } // uses mouseX, mouseY public void press() { } public void drag() { } public void release() { } public void up() { } public void down() { } public void left() { } public void right() { } public void key() { } // uses key // For use, but not for override: public void beep() { doBeep(); } public void exit() { doExit(); } public void setStepDelay(int delay) { doSetStepDelay(delay); } public void play(String location) { doPlay(location, false); } public void loop(String location) { doPlay(location, true); } public void stopSounds() { doStopSounds(); } // Fields for use public int width, height, mouseX, mouseY, key; public int pressX, pressY, dragX, dragY, releaseX, releaseY; public int steps; public Graphics2D page; public static final Scanner scanner = new Scanner(System.in); // For backwards compatibility: public void onTimer() { } public void paint(Graphics2D page, int width, int height) { } ////////////////////////////////////// // PRIVATE IMPLEMENTATION ////////////////////////////////////// public final static synchronized CoStarsComponent newInstance(Class c, int w, int h) { CoStarsComponent result = null; if (CoStarsComponent.width0 != -1) throw new RuntimeException("width0 is not -1"); try { CoStarsComponent.width0 = w; CoStarsComponent.height0 = h; result = (CoStarsComponent)c.newInstance(); } catch (Exception e) {System.out.println("Err!");e.printStackTrace();} finally { CoStarsComponent.width0 = -1; CoStarsComponent.height0 = -1; } return result; } private static int width0 = -1, height0 = -1; public CoStarsComponent() { if (width0 == -1) return; this.width = CoStarsComponent.width0; this.height= CoStarsComponent.height0; setPreferredSize(new Dimension(this.width,this.height)); } // Methods to override [ all may use width, height ] private void doStart() { if (!started) { setFields(); start(); repaint(); started = true; } } private void doStep() { setFields(); steps++; step(); onTimer(); repaint(); } private void doPress() { setFields(); mouse(); press(); repaint(); } private void doDrag() { setFields(); drag(); repaint(); } private void doRelease() { setFields(); release(); repaint(); } private void doKey(int key) { setFields(); switch(key) { case KeyEvent.VK_UP: up(); break; case KeyEvent.VK_DOWN: down(); break; case KeyEvent.VK_LEFT: left(); break; case KeyEvent.VK_RIGHT: right(); break; default: this.key = key; key(); break; } repaint(); } private void doPaint(Graphics2D page) { if (!started) { repaint(); return; } setFields(page); paint(); paint(page,width,height); } private void setFields() { width = getWidth(); height = getHeight(); page = null; } public int getWidth() { int w = super.getWidth(); if (w > 0) this.width = w; return this.width; } public int getHeight() { int h = super.getHeight(); if (h > 0) this.height = h; return this.height; } private void setFields(Graphics2D page) { setFields(); this.page = page; } private boolean started = false; // set to true in start() private boolean isApplet = true; // set to false in launch() // to prevent paint from being inadvertently overrided public final void paint(Graphics page) { super.paint(page); } public final void paintComponent(Graphics page) { // if (this.timer == null) { timer = new Thread(this); timer.start(); } if (this.timer == null) { timer = new javax.swing.Timer(timerMillis, this); timer.start(); } page.setColor(Color.white); page.fillRect(0,0,getWidth(),getHeight()); page.setColor(Color.black); doPaint((Graphics2D)page); } private void doExit() { System.exit(0); } private void doBeep() { Toolkit.getDefaultToolkit().beep(); } private void doSetStepDelay(int millis) { timerMillis = Math.max(1,millis); if (timer != null) timer.setDelay(timerMillis); } private javax.swing.Timer timer; private int timerMillis = 1; private long lastTimerTime = 0; private void doOnTimer() { if (!started) return; if (System.currentTimeMillis() - lastTimerTime >= timerMillis) { doStep(); lastTimerTime = System.currentTimeMillis(); } } /* private Thread timer = null; private long lastTimerTime = 0; public void run() { while (true) { if (System.currentTimeMillis() - lastTimerTime >= timerMillis) { doStep(); lastTimerTime = System.currentTimeMillis(); } try { timer.sleep(timerMillis); } catch (Exception e) { } } } */ public void mousePressed(MouseEvent e) { mouseX = pressX = e.getX(); mouseY = pressY = e.getY(); doPress(); } public void mouseReleased(MouseEvent e) { mouseX = releaseX = e.getX(); mouseY = releaseY = e.getY(); doRelease(); } public void mouseDragged(MouseEvent e) { mouseX = dragX = e.getX(); mouseY = dragY = e.getY(); doDrag(); } public void mouseEntered(MouseEvent e) { } public void mouseExited (MouseEvent e) { } public void mouseClicked(MouseEvent e) { } public void mouseMoved(MouseEvent e) { } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); char keyChar = e.getKeyChar(); // System.out.println("keyPressed, code = " + keyCode + // ", char = '" + keyChar + "'"); if (keyCode < ' ') return; int key = ((keyChar < (char)65535) ? keyChar : keyCode); doKey(key); } public void actionPerformed(ActionEvent evt) { doOnTimer(); } private void addEventListeners() { addMouseListener(this); addMouseMotionListener(this); addKeyListener(this); } public static void launch() { launch(500,300); // default size } public static synchronized void launch(int width, int height) { try { Class c = null; StackTraceElement[] em = new Exception().getStackTrace(); for (StackTraceElement ste : em) { Class nextC = Class.forName(ste.getClassName()); if (CoStarsComponent.class.isAssignableFrom(nextC)) c = nextC; else break; } if (c == null) throw new RuntimeException("No main class"); CoStarsComponent comp = CoStarsComponent.newInstance(c,width,height); comp.isApplet = false; JFrame frame = new JFrame(comp.getClass().getName()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel cp = new JPanel(); cp.setLayout(new BorderLayout()); cp.add(comp); frame.setContentPane(cp); // comp.setPreferredSize(new Dimension(width,height)); frame.pack(); frame.setVisible(true); comp.launchApplet(); } catch (Exception e) {System.out.println("Err!");e.printStackTrace();} } public void launchApplet() { // because when we are the JComponent of an applet, "launch" is not called try { addEventListeners(); setFocusable(true); requestFocusInWindow(); // send key events to "comp" doStart(); } catch (Exception e) {System.out.println("Err!");e.printStackTrace();} } ////////////////////////// // MIDI support ////////////////////////// private URL toUrl(String location) throws Exception { if (location == null) return null; if (location .startsWith("http")) return new URL(location); return new File(location).toURI().toURL(); } /* private InputStream toInputStream(String location) throws Exception { InputStream is; if (location.startsWith("http")) is = new URL(location).openStream(); else is = new FileInputStream(new File(location)); return is; } */ private InputStream toInputStream(String location) throws Exception { byte[] data = fileCache.get(location); if (data == null) { //System.out.println("cache miss for " + location); InputStream is; if (location.startsWith("http")) is = new URL(location).openStream(); else is = new FileInputStream(new File(location)); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buf = new byte[1024]; int len; while ((len = is.read(buf)) > 0) bos.write(buf, 0, len); data = bos.toByteArray(); fileCache.put(location,data); } return new ByteArrayInputStream(data); } private HashMap fileCache = new HashMap(); public boolean inSandbox() { SecurityManager mgr = System.getSecurityManager(); if (mgr == null) return false; try { mgr.checkExit(-1); } catch (Exception e) { return true; } return false; } public boolean isApplet() { return inSandbox(); } private void doPlay(String location, boolean loop) { if (location == null) { return; } String err = null; if (!location.endsWith("wav") && !location.endsWith("mid") && !location.endsWith("midi")) err = "Unknown file type: " + location; else { try { InputStream is = toInputStream(location); if (location.endsWith("wav")) doPlayWav(is,loop); else if (location.endsWith("midi") || location.endsWith("mid")) doPlayMidi(is,loop); } catch (Exception e) { if (!isApplet) e.printStackTrace(); err = e.getMessage(); } } if (err != null) { if (isApplet) { System.out.println(err); beep(); } else throw new RuntimeException(err); } } private void doStopSounds() { doStopWavSounds(); doStopMidiSounds(); } /* private void doPlayUncachedMidiOrWav(String location, boolean loop) { try { AudioClip clip = Applet.newAudioClip(toUrl(location)); audioClips.add(clip); if (loop) clip.loop(); else clip.play(); } catch (Exception e) { throw new RuntimeException("" + e); } } private ArrayList audioClips = new ArrayList(); private void doStopUncachedMidiOrWav() { for (AudioClip clip : audioClips) clip.stop(); audioClips.clear(); } */ /////////////////// // For: midi files /////////////////// private Sequencer sequencer = null; private void doPlayMidi(InputStream is, boolean loop) { try { doStopMidiSounds(); sequencer = MidiSystem.getSequencer(); sequencer.setSequence(MidiSystem.getSequence(is)); if (loop) sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY); sequencer.open(); sequencer.start(); } catch (Exception e) { midiError("" + e); } } private void midiError(String msg) { System.err.println("Midi error: " + msg); sequencer = null; } private void doStopMidiSounds() { try { if ((sequencer == null) || (!sequencer.isRunning())) return; sequencer.stop(); sequencer.close(); } catch (Exception e) { midiError("" + e); } sequencer = null; } /////////////////////////////// // For: aif, au, and wav files /////////////////////////////// private void doPlayWav(InputStream is, boolean loop) { try { // From file // AudioInputStream stream = AudioSystem.getAudioInputStream(new File("audiofile")); // From URL // AudioInputStream stream = AudioSystem.getAudioInputStream(toUrl(location)); AudioInputStream stream = AudioSystem.getAudioInputStream(is); AudioFormat format = stream.getFormat(); /* // If ALAW and ULAW encodings must be converted // to PCM_SIGNED before it can be played... if (format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED) { format = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, format.getSampleRate(), format.getSampleSizeInBits()*2, format.getChannels(), format.getFrameSize()*2, format.getFrameRate(), true); // big endian stream = AudioSystem.getAudioInputStream(format, stream); } */ // Create the clip DataLine.Info info = new DataLine.Info( Clip.class, stream.getFormat(), ((int)stream.getFrameLength()*format.getFrameSize())); Clip clip = (Clip) AudioSystem.getLine(info); // This method does not return until the audio file is completely loaded clip.open(stream); // Start playing clip.start(); } catch (Exception e) { throw new RuntimeException("" + e); } } private ArrayList wavClips = new ArrayList(); private void doStopWavSounds() { for (Clip clip : wavClips) clip.stop(); wavClips.clear(); } }