import processing.net.*; import java.util.*; // for ArrayList import netscape.javascript.JSObject; import controlP5.*; // GUI public class SimpleRhythm extends PApplet implements Clocker { // Constants final float CLOCK_INTERVAL = 10; // in ms float _prev_trigger = 0; // in ms float _interval = 0; // in ms // Timing Clock _clock; Timer _timer; float _prev_timing; // for timing calculation float _bpm = 120.0; float _beatNum = 4.0; float _real_bpm; // current actual bpm // Frame & Line float _frameX, _frameY, _frameW, _frameH; float _beatR = 8.0; // Colors and Fonts color _bgColor; color _frameColor, _lineColor, _beatColor; color _bpmColor; PFont _font; // Beats ArrayList _beats; // beat object // GUI ControlP5 controlP5; // for GUI // For Application Export static public void main(String args[]) { PApplet.main(new String[] { "--present", "SimpleRhythm" }); // fullscreen "Present" mode } void setup(){ // Set Drawing Region and Drawing Options size(500, 300); // default size frameRate(60); ellipseMode(CENTER); // smooth(); // Color & Font Settings _bgColor = color(20, 20, 20); _frameColor = color(100, 100, 100); _lineColor = color(255, 0, 0); _beatColor = color(255, 255, 255); _bpmColor = color(60, 60, 60); _font = loadFont("Standard0757-8.vlw"); textFont(_font); textAlign(LEFT); // Beats Object _beats = new ArrayList(); // Timing Object _clock = new Clock(this, 10); _timer = new Timer(); _clock.start(); // start Clock thread // Setup GUI controlP5 = new ControlP5(this); controlP5.setAutoInitialization(false); controlP5.addSlider("speedSlider", 60, 280, _bpm, 10, 40, 10, 120); controlP5.controller("speedSlider").setLabel("BPM"); controlP5.addButton("clearButton", 1, width - 45, height - 50, 30, 16); controlP5.controller("clearButton").setLabel("CLEAR"); // Get to know when the browser is resized addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { updateLayout(); } }); } void trigger(){ float now = (float)_timer.totalElapsedTimeInMs(); _interval = now - _prev_timing; float interval = 60000.0 / _bpm * _beatNum; float timing = now /interval; for (int i=0; i<_beats.size(); i++){ ((Beat)_beats.get(i)).trigger(timing); } } void draw(){ background(_bgColor); strokeWeight(1); // frame sizes _frameX = width * 0.1; _frameY = height * 0.1; _frameW = width * 0.8; _frameH = height * 0.8; // frame rectMode(CORNER); stroke(_frameColor); noFill(); rect(_frameX, _frameY, _frameW, _frameH); // Beats for (int i=0; i<_beats.size(); i++){ Beat beat = (Beat)_beats.get(i); float x = beat._timing * _frameW + _frameX; float y = (1.0 - beat._volume) * _frameH + _frameY; // Draw beat rect rectMode(CENTER); stroke(_beatColor); fill(_beatColor); rect(x, y, _beatR * beat._volume, _beatR * beat._volume); } // line float interval = 60000.0 / _bpm * _beatNum; float lineX = (float)_timer.totalElapsedTimeInMs()/interval * _frameW + _frameX; if (lineX > _frameX + _frameW){ lineX = _frameX; _real_bpm = 60000.0 / ((float)_timer.totalElapsedTimeInMs() / (float)_beatNum); _timer.reset(); } stroke(_lineColor); strokeWeight(1.5); line(lineX, _frameY, lineX, _frameH + _frameY ); // display real bpm fill(_bpmColor); text(_real_bpm, 10, height - 20); } void mousePressed(){ if ( mouseX > _frameX && mouseX < _frameX + _frameW && mouseY > _frameY && mouseY < _frameY + _frameH){ // Create New Beats and add it to the Beat Array float timing = (mouseX - _frameX) / _frameW; float vol = (_frameH - (mouseY - _frameY)) / _frameH; _beats.add(new Beat("beep.aif", timing, vol)); } } void speedSlider(float value){ _bpm = value; // set new BPM value } void clearButton(int value){ _beats.clear(); // clear all beats } void updateLayout(){ } private class Beat{ public float _timing; // x axis public float _volume; // y axis private boolean _verbose = false; SoundPlayer _sound; private boolean _needsPlay = true; // flag to make sure the sound will be played only once in a cycle. public Beat(String filepath, float timing, float vol){ this._timing = timing; this._volume = vol; _sound = new SoundPlayer(filepath); // create new SoundPlayer instance _sound.setGain(_volume); // set volume if (_verbose && _sound._isReady) { println("Sound Loaded"); _sound.play(); } } public void trigger(float now){ if (now >= _timing && _needsPlay){ _sound.play(); _needsPlay = false; } if (now < _timing) _needsPlay = true; } } }