/* JWildfire - an image and animation processor written in Java Copyright (C) 1995-2011 Andreas Maschke This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jwildfire.create.tina.dance; import org.jwildfire.base.Prefs; import org.jwildfire.create.tina.audio.JLayerInterface; import org.jwildfire.create.tina.audio.RecordedFFT; import org.jwildfire.create.tina.base.Flame; import org.jwildfire.create.tina.dance.motion.DanceFlameTransformer; import org.jwildfire.create.tina.swing.FlameHolder; import org.jwildfire.image.SimpleImage; import org.jwildfire.swing.ImagePanel; public class RealtimeAnimRenderThread implements Runnable, FlameHolder { private final DancingFractalsController controller; private final DancingFlameStack flameStack; private boolean forceAbort; private boolean running; private RecordedFFT fftData; private JLayerInterface musicPlayer; private ImagePanel fftPanel; private int framesPerSecond = 12; private long timeRenderStarted = 0; private final DanceFlameTransformer transformer; private Flame currFlame; private boolean drawTriangles = true; private boolean drawFFT = true; private boolean drawFPS = true; public RealtimeAnimRenderThread(DancingFractalsController pController, DancingFlameProject pProject) { controller = pController; transformer = new DanceFlameTransformer(pProject); flameStack = new DancingFlameStack(Prefs.getPrefs()); } @Override public void run() { setRunning(forceAbort = false); try { long fpsMeasureMentFrameCount = 0; long startFPSMeasurement = System.currentTimeMillis(); long nextFrame = timeRenderStarted = startFPSMeasurement; double fps = 0.0; setRunning(true); while (!forceAbort) { long time = System.currentTimeMillis(); while (time < nextFrame) { try { Thread.sleep(1); } catch (Exception ex) { ex.printStackTrace(); } time = System.currentTimeMillis(); } nextFrame = (long) (time + 1000.0 / (double) getFramesPerSecond() + 0.5); DancingFlame dancingFlame = flameStack.getFlame(); currFlame = null; short currFFT[]; if (fftData != null) { currFFT = fftData.getData(musicPlayer.getPosition()); if (drawFFT && fftPanel != null) { SimpleImage img = fftPanel.getImage(); drawFFT(img, currFFT); fftPanel.repaint(); } } else { currFFT = null; } long elapsedTime = time - timeRenderStarted; if (dancingFlame != null) { currFlame = transformer.createTransformedFlame(dancingFlame, currFFT, elapsedTime, getFramesPerSecond()); } fpsMeasureMentFrameCount++; long dt = (System.currentTimeMillis() - startFPSMeasurement); if (dt > 500) { fps = (double) (fpsMeasureMentFrameCount * 1000.0) / (double) dt; fpsMeasureMentFrameCount = 0; startFPSMeasurement = System.currentTimeMillis(); } if (currFlame != null) { try { controller.refreshFlameImage(currFlame, drawTriangles, fps, elapsedTime, drawFPS); } catch (Exception ex) { ex.printStackTrace(); } } } } finally { setRunning(false); } } public boolean isDone() { return !isRunning(); } public void setForceAbort(boolean forceAbort) { this.forceAbort = forceAbort; } public void setFFTData(RecordedFFT pFFTData) { fftData = pFFTData; } public void setMusicPlayer(JLayerInterface pMusicPlayer) { musicPlayer = pMusicPlayer; } public void setFFTPanel(ImagePanel pFFTPanel) { fftPanel = pFFTPanel; } private void drawFFT(SimpleImage img, short[] buffer) { final double hScale = 1.75; final int imgWidth = img.getImageWidth(); final int imgHeight = img.getImageHeight(); int blockSize = imgWidth / (buffer.length + 1); img.fillBackground(0, 0, 0); for (int i = 0; i < buffer.length; i++) { short val = buffer[i]; int iVal = (int) ((double) val / (double) Short.MAX_VALUE * (double) imgHeight * hScale + 0.5); if (iVal < 0) iVal = 0; else if (iVal >= imgHeight) iVal = imgHeight - 1; for (int y = 0; y < iVal; y++) { img.setARGB(i * blockSize, imgHeight - 1 - y, 255, 255, 0, 0); img.setARGB((i + 1) * blockSize, imgHeight - 1 - y, 255, 255, 0, 0); } for (int x = i * blockSize; x < (i + 1) * blockSize; x++) { img.setARGB(x, imgHeight - 1 - iVal, 255, 255, 0, 0); } } } public void setFramesPerSecond(int pFPS) { framesPerSecond = pFPS; } public boolean isRunning() { return running; } public void setRunning(boolean running) { this.running = running; } public long getTimeRenderStarted() { return timeRenderStarted; } public int getFramesPerSecond() { return framesPerSecond; } @Override public Flame getFlame() { return currFlame; } public void setDrawFFT(boolean pDrawFFT) { drawFFT = pDrawFFT; } public void setDrawFPS(boolean pDrawFPS) { drawFPS = pDrawFPS; } public void setDrawTriangles(boolean pDrawTriangles) { drawTriangles = pDrawTriangles; } public DancingFlameStack getFlameStack() { return flameStack; } }