Drumkit.java

/*
 * Copyright © 2012 Nokia Corporation. All rights reserved.
 * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation.
 * Oracle and Java are trademarks or registered trademarks of Oracle and/or its
 * affiliates. Other product and company names mentioned herein may be trademarks
 * or trade names of their respective owners.
 * See LICENSE.TXT for license information.
 */

package com.nokia.example.drumkit.views;

import com.nokia.example.drumkit.components.Button;
import com.nokia.example.drumkit.components.Pad;
import com.nokia.example.drumkit.components.AnimatedButton;
import com.nokia.example.drumkit.components.WheelMenu;
import com.nokia.example.drumkit.components.Rectangle;
import com.nokia.example.drumkit.Main;
import com.nokia.example.drumkit.helpers.ImageLoader;
import com.nokia.example.drumkit.helpers.Sample;
import com.nokia.example.drumkit.SoundManager;
import java.util.Timer;
import java.util.TimerTask;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;
import java.util.Vector;

public class Drumkit
        extends GameCanvas
        implements Button.Listener, WheelMenu.Listener {

    private boolean playback = false;
    private boolean recording = false;
    private boolean pressed = false;
    private boolean kitEnabled = true;
    private int centerX = 0;
    private int centerY = 0;
    private int acivePad = -1;
    private final WheelMenu wheel;
    private final Image background;
    private final Image splash;
    private final Image appName;
    private final Pad pads[] = new Pad[4];
    private Button startPlaybackBtn;
    private Button stopPlaybackBtn;
    private Button playBtn;
    private AnimatedButton recordBtn;
    private Button exitBtn;
    private Timer timer;
    private Timer drummer = new Timer();
    private Timer longTapTimer;
    private Graphics g;
    private Vector tape;

    /**
     * Class containing the drumkit logic
     */
    public Drumkit() {
        super(false);
        setFullScreenMode(true);

        centerX = getWidth() / 2;
        centerY = getHeight() / 2;

        // Load image resources
        background = ImageLoader.loadImage("/pads.png");
        splash = ImageLoader.loadImage("/hit.png");
        appName = ImageLoader.loadImage("/app_name.png");

        createButtons();
        createPads();

        wheel = new WheelMenu(this);
    }

    /**
     * Create UI buttons
     */
    public final void createButtons() {
        int y = getHeight() - 56;

        startPlaybackBtn = new Button("/play.png", "/play_pressed.png", "/play_disabled.png", this);
        startPlaybackBtn.setPosition(59, y);
        stopPlaybackBtn = new Button("/stop.png", "/stop_pressed.png", "/play_disabled.png", this);
        stopPlaybackBtn.setPosition(59, y);

        recordBtn = new AnimatedButton("/record_off.png", "/recording.png", "/record_disabled.png", 50, this);
        recordBtn.setPosition(9, y);
        recordBtn.setSelectable(true);

        exitBtn = new Button("/exit.png", "/exit_pressed.png", "/exit.png", this);
        exitBtn.setPosition(getWidth() - 48, 4);

        playBtn = startPlaybackBtn;
        playBtn.disable();
    }

    /**
     * Define touch sensitive areas
     */
    public final void createPads() {
        pads[0] = new Pad(SoundManager.SAMPLE_KICK, centerX - 0, centerY + 36, 107, 112);
        pads[1] = new Pad(SoundManager.SAMPLE_SNARE, centerX - 111, centerY - 38, 113, 113);
        pads[2] = new Pad(SoundManager.SAMPLE_TOM2, centerX + 8, centerY - 95, 105, 104);
        pads[3] = new Pad(SoundManager.SAMPLE_CRASH, centerX - 95, centerY - 148, 93, 93);
    }

    /**
     * Handle show events
     */
    protected void showNotify() {
        timer = new Timer();
        g = getGraphics();

        /**
         * Start UI rendering loop
         */
        TimerTask ui = new TimerTask() {

            public void run() {
                render(g);
                flushGraphics();
            }
        };
        timer.schedule(ui, 0, 50);
    }

    /**
     * Handle hide events
     */
    protected void hideNotify() {
        timer.cancel();
    }

    /**
     * Render graphics
     * @param g
     */
    public void render(Graphics g) {
        int anchor = Graphics.HCENTER | Graphics.VCENTER;
        g.setColor(0xf0f0f0);
        g.fillRect(0, 0, getWidth(), getHeight());
        g.drawImage(background, centerX, centerY, anchor);
        playBtn.paint(g);
        recordBtn.paint(g);
        exitBtn.paint(g);
        g.drawImage(appName, centerX + 34, 28, anchor);
        for (int i = 0; i < pads.length; i++) {
            Pad pad = pads[i];
            if (pad != null && pad.isHit()) {
                Rectangle boundingBox = pad.getBoundingBox();
                g.drawImage(splash, boundingBox.getCenterX(), boundingBox.getCenterY(), anchor);
                pad.setHit(false);
            }
        }
        wheel.paint(g);
        g.setColor(0xff0000);
    }

    /**
     * Start playback
     */
    public void playTape() {
        if (tape != null && tape.size() > 0) {
            playback = true;
            recordBtn.disable();
            playBtn = stopPlaybackBtn;

            int length = tape.size();
            int i = 0;
            long startTime = ((Sample) tape.elementAt(0)).time;

            // Schedule recorded samples
            while (i < length) {
                final Sample sample = (Sample) tape.elementAt(i);
                drummer.schedule(new TimerTask() {

                    public void run() {
                        SoundManager.playSound(sample.sound);
                        sample.pad.setHit(true);
                    }
                }, sample.time - startTime);
                i++;
            }

            // Schedule task for stopping the playback
            TimerTask end = new TimerTask() {

                public void run() {
                    stopTape();
                }
            };
            drummer.schedule(end, ((Sample) tape.lastElement()).time - startTime + 100);
        }
    }

    /**
     * Stop playback
     */
    private void stopTape() {
        drummer.cancel();
        drummer = new Timer();
        playback = false;
        playBtn = startPlaybackBtn;
        recordBtn.enable();
    }

    /**
     * Start recording
     */
    private void startRecording() {
        playBtn.disable();
        recordBtn.select();
        tape = new Vector();
        recording = true;
    }

    /**
     * Stop recording
     */
    private void stopRecording() {
        playBtn.enable();
        recording = false;
    }

    /**
     * Deliver pressed events
     * @param x
     * @param y
     */
    protected void pointerPressed(int x, int y) {
        pressed = true;
        if (kitEnabled) {
            for (int i = 0; i < 4; i++) {
                Pad pad = pads[i];
                if (pad.contains(x, y)) {
                    if (recording) {
                        // Record sample
                        tape.addElement(new Sample(pad));
                    }
                    // Play sound assigned to the pad
                    SoundManager.playSound(pad.getSound());
                    pad.setHit(true);

                    // Set timer for monitoring long tap
                    if (!recording) {
                        acivePad = i;
                        monitorLongTap(x, y);
                    }
                    break;
                }
            }
        }

        playBtn.pointerPressed(x, y);
        recordBtn.pointerPressed(x, y);
        exitBtn.pointerPressed(x, y);
        wheel.pointerPressed(x, y);
    }

    /**
     * Deliver dragged events
     * @param x
     * @param y
     */
    protected void pointerDragged(int x, int y) {
        playBtn.pointerDragged(x, y);
        recordBtn.pointerDragged(x, y);
        exitBtn.pointerDragged(x, y);
        wheel.pointerDragged(x, y);
    }

    /**
     * Deliver released events
     * @param x
     * @param y
     */
    protected void pointerReleased(int x, int y) {
        playBtn.pointerReleased(x, y);
        recordBtn.pointerReleased(x, y);
        exitBtn.pointerReleased(x, y);
        wheel.pointerReleased(x, y);

        // Stop monitoring long taps
        if (longTapTimer != null) {
            longTapTimer.cancel();
            longTapTimer = null;
        }
        pressed = false;
    }

    /**
     * Custom implementation for long tap gesture
     * @param x
     * @param y
     */
    private void monitorLongTap(final int x, final int y) {
        // Reset long tap timer
        if (longTapTimer != null) {
            longTapTimer.cancel();
        }
        longTapTimer = new Timer();

        final int pad = acivePad;

        // Task that shows prepares wheel menu to show up in the right place
        longTapTimer.schedule(
                new TimerTask() {

                    public void run() {
                        if (pressed) {
                            wheel.setOrigin(x, y);

                            // Calculate destination coordinates for the wheel menu
                            centerX = getWidth() / 2;
                            centerY = getHeight() / 2;
                            int destinationX = centerX - (int) (0.2 * (centerX - x));
                            int destinationY = centerY - (int) (0.5 * (centerY - y));
                            wheel.setDestination(destinationX, destinationY);
                            stopTape();

                            // Disable buttons
                            exitBtn.disable();
                            recordBtn.disable();
                            playBtn.disable();

                            // Preselect a percussion and open the menu
                            wheel.select(pads[pad].getSound());
                            wheel.show();
                            kitEnabled = false;
                        }
                    }
                }, 1000);
    }

    /**
     * Handle button clicks
     * @param button
     */
    public void clicked(Button button) {

        if (button == playBtn) {
            if (playback) {
                stopTape();
            }
            else {
                playTape();
            }
        }
        else if (button == recordBtn) {
            if (recording) {
                stopRecording();
            }
            else {
                startRecording();
            }
        }
        else if (button == exitBtn) {
            Main.getInstance().destroyApp(true);
            Main.getInstance().notifyDestroyed();
        }
    }

    /**
     * Handle wheelOpened event
     */
    public void wheelOpened() {
    }

    /**
     * Handle wheelClosed event
     */
    public void wheelClosed() {
        exitBtn.enable();
        recordBtn.enable();
        if (tape != null) {
            playBtn.enable();
        }
        kitEnabled = true;
    }

    /**
     * Handle wheelItemSelected event
     */
    public void wheelItemSelected(Button button) {
        int percussion = wheel.getSelectedIndex();
        if (percussion > -1) {
            // Load and play the selected sample
            pads[acivePad].setSound(percussion);
            SoundManager.playSound(percussion);
        }
    }
}