extracted first audio data and calc movement speed, replaced audioPlayer with vlc

This commit is contained in:
Raybz@Raybz 2013-06-13 11:51:29 +02:00
parent a9ffb6b044
commit 6eb81fa959
10 changed files with 240 additions and 183 deletions

View File

@ -21,6 +21,8 @@ import org.wyrez.audio.decoder.DecoderFactory;
import org.wyrez.audio.util.Band;
import org.wyrez.audio.util.SampleBuffer;
import org.wyrez.audio.util.SampleHelper;
import org.wyrez.shootingstars.game.GameSettings;
import uk.co.caprica.vlcj.player.MediaPlayer;
/**
*
@ -36,7 +38,14 @@ public class AudioDataManager {
private AudioProcessor audioProcessorLowBand;
private AudioProcessor audioProcessorMidBand;
private AudioProcessor audioProcessorHighBand;
private AudioPlayer player;
private MediaPlayer player;
private int bufferSize;
private float samplingRate;
private float[] speedData;
private float[] movementData;
private float[] flashData;
private float peakAverage;
private float indexFactor;
private float bpm;
private float currentTime;
private float lastTimeSync;
@ -44,10 +53,12 @@ public class AudioDataManager {
public void AudioDataManager() {
}
public boolean initialize(String file, AudioPlayer player) {
public boolean initialize(GameSettings settings, MediaPlayer player) {
try {
this.player = player;
SampleBuffer samples = SampleHelper.createSampleBuffer(DecoderFactory.create(file));
SampleBuffer samples = SampleHelper.createSampleBuffer(
DecoderFactory.create(settings.getAudioFile()));
this.samplingRate = samples.samplingRate;
this.audioProcessorLowBand = new AudioProcessor(samples, LOW_BAND);
this.audioProcessorMidBand = new AudioProcessor(samples, MID_BAND);
this.audioProcessorHighBand = new AudioProcessor(samples, HIGH_BAND);
@ -58,42 +69,104 @@ public class AudioDataManager {
}
public void update(float tpf) {
if (lastTimeSync != player.getElapsedTime()) {
currentTime = player.getElapsedTime();
if (lastTimeSync != player.getTime()) {
currentTime = player.getTime();
lastTimeSync = currentTime;
} else {
currentTime += tpf;
currentTime += tpf * 1000f;
}
}
public void analyseLowBand() {
audioProcessorLowBand.calculate();
bufferSize = audioProcessorLowBand.getBufferSize();
indexFactor = (samplingRate / bufferSize) / 1000f;
speedData = calcWave(audioProcessorLowBand.getSpectrum(), 4, 24, 3);
SampleHelper.normalize(speedData, 0f, 1f);
movementData = calcWave(audioProcessorLowBand.getPeaks(), 2, 4, 16);
SampleHelper.normalize(movementData, 0f, 1f);
audioProcessorLowBand.cutFastPeaks(MIN_PEAK_DIFF);
flashData = audioProcessorLowBand.getPeaks();
peakAverage = audioProcessorLowBand.getPeakAverage();
bpm = audioProcessorLowBand.getBpm();
while (bpm > 200f) {
bpm /= 2f;
}
audioProcessorLowBand.clean();
audioProcessorLowBand = null;
}
public void analyseMidBand() {
audioProcessorMidBand.calculate();
audioProcessorMidBand.cutFastPeaks(MIN_PEAK_DIFF);
audioProcessorMidBand.clean();
audioProcessorMidBand = null;
}
public void analyseHighBand() {
audioProcessorHighBand.calculate();
audioProcessorHighBand.clean();
audioProcessorHighBand = null;
}
private float[] calcWave(float[] data, int actualWeight, int calcSize, int smooth) {
float[] result = new float[data.length];
float average;
int count;
for (int s = 0; s < smooth; s++) {
for (int i = 0; i < data.length; i++) {
count = actualWeight;
average = data[i] * actualWeight;
for (int j = 0; j < calcSize; j++) {
if (i > j) {
average += data[i - j];
count++;
}
if (i < data.length - j - 1) {
average += data[i + j];
count++;
}
}
average /= count;
result[i] = average;
}
}
return result;
}
public float getBPM() {
return bpm;
}
public boolean isLowPeak() {
int index = (int) (currentTime * (audioProcessorLowBand.getSamplingRate() / audioProcessorLowBand.getBufferSize()));
if (index > audioProcessorLowBand.getPeaks().length) {
index = audioProcessorLowBand.getPeaks().length - 1;
private int calcIndex() {
int index = (int) (currentTime * indexFactor);
if (index > flashData.length) {
index = flashData.length - 1;
}
return audioProcessorLowBand.getPeaks()[index] > audioProcessorLowBand.getPeakAverage()*0.5f;
return index;
}
public boolean isFlash() {
return flashData[calcIndex()] > peakAverage * 0.5f;
}
public float getMovementData() {
return 0.5f;
//TODO argf
//System.out.println(currentTime + " * " + indexFactor + " = " + calcIndex() + " --> " + movementData[calcIndex()]);
//return movementData[calcIndex()];
}
public float getSpeedData() {
return speedData[calcIndex()];
}
public float getMidData() {

View File

@ -1,65 +0,0 @@
/*
* Copyright (C) 2013 Darth Affe <http://wyrez.org> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.wyrez.shootingstars.audiodata;
import org.wyrez.audio.AudioDevice;
import org.wyrez.audio.decoder.Decoder;
/**
*
* @author Darth Affe
*/
public abstract class AudioPlayer extends Thread {
private static final int BUFFER_SIZE = 128;
/**/
private Decoder decoder;
private AudioDevice device;
private float elapsedTime;
private boolean cleanup = false;
public AudioPlayer(Decoder decoder) throws Exception {
this.decoder = decoder;
device = new AudioDevice(decoder.getSamplingRate(), decoder.getChannelCount(),
BUFFER_SIZE * decoder.getChannelCount());
}
@Override
public void run() {
elapsedTime = 0;
float[] samples = new float[BUFFER_SIZE * decoder.getChannelCount()];
long startTime = System.nanoTime();
while (decoder.readSamplesStereo(samples) > 0) {
if (cleanup) {
break;
}
device.writeSamples(samples);
elapsedTime = (System.nanoTime() - startTime) / 1000000000.0f;
}
finished(cleanup);
}
public float getElapsedTime() {
return elapsedTime;
}
public void cleanup() {
this.cleanup = true;
}
public abstract void finished(boolean cleanup);
}

View File

@ -48,7 +48,7 @@ public class GroundGlowControl extends BaseControl {
@Override
public void update(float tpf) {
if (audioDataManager.isLowPeak()) {
if (audioDataManager.isFlash()) {
glowTimer = GLOW_DURATION;
}

View File

@ -30,15 +30,11 @@ import org.wyrez.shootingstars.helper.UserDataKeys;
public class PlayerMoveControl extends BaseControl {
private static final float BPM_TO_SPEED_FACTOR = 1f / 8f;
//private static final float DASH_MULTIPLIER = 4f;
//private static final float DASH_DURATION = 0.25f;
/**/
private AudioDataManager audioDataManager;
private float radius = 666f;
private float angle = 0f;
private float speed;
//TODO dashing isn't as nice at it should be -.- is it needed?
// private float dashTimer = 0f;
public PlayerMoveControl(AudioDataManager audioDataManager) {
this.audioDataManager = audioDataManager;
@ -61,20 +57,9 @@ public class PlayerMoveControl extends BaseControl {
angle -= FastMath.TWO_PI;
}
// if (audioDataManager.isLowPeak() && dashTimer <= 0f) {
// dashTimer = DASH_DURATION;
// System.out.println((System.nanoTime() - lastDash) / 1000000000.0);
// lastDash = System.nanoTime();
// }
spatial.setLocalTranslation(MathHelper.calcPointOnCircle(angle, radius, 250f)); //TODO set y-Position
// if (dashTimer > 0f) {
// angle += MathHelper.degreeToRadian(speed * (DASH_MULTIPLIER
// * (dashTimer / DASH_DURATION)) * tpf);
// dashTimer -= tpf;
// } else {
angle += MathHelper.degreeToRadian(speed * tpf);
// }
spatial.setLocalTranslation(MathHelper.calcPointOnCircle(angle, radius,
200f + (600f * audioDataManager.getMovementData())));
angle += MathHelper.degreeToRadian((speed / 2f + (speed * audioDataManager.getSpeedData())) * tpf);
}
}

View File

@ -19,7 +19,6 @@ package org.wyrez.shootingstars.game;
import com.jme3.material.Material;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.texture.Image;
import com.jme3.texture.Texture2D;
@ -27,7 +26,6 @@ import com.sun.jna.Memory;
import java.nio.ByteBuffer;
import org.wyrez.shootingstars.factories.Materials;
import org.wyrez.shootingstars.mesh.CinemaHex;
import uk.co.caprica.vlcj.player.MediaPlayerFactory;
import uk.co.caprica.vlcj.player.direct.BufferFormat;
import uk.co.caprica.vlcj.player.direct.DirectMediaPlayer;
import uk.co.caprica.vlcj.player.direct.RenderCallback;
@ -38,58 +36,30 @@ import uk.co.caprica.vlcj.player.direct.RenderCallback;
*/
public class Cinema extends Geometry implements RenderCallback {
private static final int WIDTH = 1280;
private static final int HEIGHT = 720;
private static final int DEPTH = 4;
private static final String VIDEO_FORMAT = "RGBA";
private static final Image.Format TEXTURE_FORMAT = Image.Format.RGBA8;
private final MediaPlayerFactory mediaPlayerFactory;
private final DirectMediaPlayer mediaPlayer;
private final Image videoImage;
private final Texture2D videoTexture;
private final Object bufferLock = new Object();
private ByteBuffer buffer;
private int videoBufferSize;
public Cinema(String file) {
this(file, 1000f);
public Cinema(GameSettings settings) {
this(settings, 1000f);
}
public Cinema(String file, float radius) {
this(file, radius, Vector3f.ZERO);
public Cinema(GameSettings settings, float radius) {
this(settings, radius, Vector3f.ZERO);
}
public Cinema(String file, float radius, Vector3f pos) {
super("Cinema: " + file, new CinemaHex(pos, radius,
radius * ((float) HEIGHT / (float) WIDTH)));
mediaPlayerFactory = new MediaPlayerFactory("--no-video-title-show", "--quiet");
mediaPlayer = mediaPlayerFactory.newDirectMediaPlayer(VIDEO_FORMAT, WIDTH, HEIGHT, WIDTH * DEPTH, this);
videoImage = new Image(TEXTURE_FORMAT, WIDTH, HEIGHT, null);
public Cinema(GameSettings settings, float radius, Vector3f pos) {
super("Cinema: " + settings.getVideoFile(), new CinemaHex(pos, radius,
radius * ((float) settings.getVideoHeight() / (float) settings.getVideoWidth())));
videoImage = new Image(settings.GetTextureFormat(), settings.getVideoWidth(), settings.getVideoHeight(), null);
videoTexture = new Texture2D(videoImage);
videoBufferSize = settings.getVideoWidth() * settings.getVideoHeight() * settings.getVideoDepth();
Material mat = Materials.UNSHADED.create();
mat.setTexture("ColorMap", videoTexture);
this.setMaterial(mat);
this.rotate(0f, FastMath.PI / 6f, 0f);
mediaPlayer.prepareMedia(file);
}
public void play() {
mediaPlayer.play();
}
public void pause() {
mediaPlayer.pause();
}
public void stop() {
mediaPlayer.stop();
}
public void cleanup() {
stop();
mediaPlayer.release();
mediaPlayerFactory.release();
}
public void render() {
@ -101,7 +71,7 @@ public class Cinema extends Geometry implements RenderCallback {
@Override
public void display(DirectMediaPlayer mediaPlayer, Memory[] nativeBuffers, BufferFormat bufferFormat) {
synchronized (bufferLock) {
buffer = nativeBuffers[0].getByteBuffer(0, WIDTH * HEIGHT * DEPTH);
buffer = nativeBuffers[0].getByteBuffer(0, videoBufferSize);
}
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2013 Darth Affe <http://wyrez.org> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.wyrez.shootingstars.game;
import com.jme3.texture.Image;
/**
*
* @author Darth Affe
*/
public class GameSettings {
private static final int WIDTH = 1280;
private static final int HEIGHT = 720;
private static final int DEPTH = 4;
private static final String VIDEO_FORMAT = "RGBA";
private static final Image.Format TEXTURE_FORMAT = Image.Format.RGBA8;
/**/
private String audioFile;
private String videoFile;
private boolean useVideo;
public GameSettings(String audioFile, String videoFile) {
this.audioFile = audioFile;
if (videoFile == null) {
videoFile = audioFile;
useVideo = false;
} else {
this.videoFile = videoFile;
useVideo = true;
}
}
public String getAudioFile() {
return audioFile;
}
public String getVideoFile() {
return videoFile;
}
public boolean useVideo(){
return useVideo;
}
public int getVideoWidth() {
return WIDTH;
}
public int getVideoHeight() {
return HEIGHT;
}
public int getVideoDepth() {
return DEPTH;
}
public String getVideoFormat() {
return VIDEO_FORMAT;
}
public Image.Format GetTextureFormat() {
return TEXTURE_FORMAT;
}
}

View File

@ -30,9 +30,9 @@ import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.texture.Image;
import org.wyrez.audio.decoder.DecoderFactory;
import org.wyrez.shootingstars.audiodata.AudioDataManager;
import org.wyrez.shootingstars.audiodata.AudioPlayer;
import org.wyrez.shootingstars.controls.GroundGlowControl;
import org.wyrez.shootingstars.controls.PlayerCamControl;
import org.wyrez.shootingstars.controls.PlayerMouseControl;
@ -40,32 +40,37 @@ import org.wyrez.shootingstars.controls.PlayerMoveControl;
import org.wyrez.shootingstars.controls.PlayerShootControl;
import org.wyrez.shootingstars.factories.Materials;
import org.wyrez.shootingstars.game.Cinema;
import org.wyrez.shootingstars.game.GameSettings;
import org.wyrez.shootingstars.game.Ground;
import org.wyrez.shootingstars.gui.GameGUI;
import org.wyrez.shootingstars.gui.listener.GameListener;
import org.wyrez.shootingstars.helper.UserDataKeys;
import tonegod.gui.core.Screen;
import uk.co.caprica.vlcj.player.MediaPlayerFactory;
import uk.co.caprica.vlcj.player.direct.DirectMediaPlayer;
/**
*
* @author Darth Affe
*/
public class GameState extends AbstractAppState implements GameListener {
private StateManager stateManager;
private AssetManager assetManager;
private InputManager inputManager;
private ViewPort viewPort;
private Camera camera;
private GameSettings settings;
private AudioDataManager audioDataManager;
private AudioPlayer audioPlayer;
private final MediaPlayerFactory mediaPlayerFactory;
private DirectMediaPlayer mediaPlayer;
private Node rootNode;
private GameGUI gui;
private Cinema cinema;
private Ground ground;
private Spatial player;
private boolean isRunning = false;
public GameState(Node rootNode, Screen screen, StateManager stateManager,
AssetManager assetManager, ViewPort viewPort,
InputManager inputManager, Camera camera, AudioDataManager audioDataManager) {
@ -77,39 +82,45 @@ public class GameState extends AbstractAppState implements GameListener {
this.audioDataManager = audioDataManager;
this.assetManager = assetManager;
this.viewPort = viewPort;
mediaPlayerFactory = new MediaPlayerFactory("--no-video-title-show", "--quiet");
this.settings = new GameSettings("Shades of AMV.mp3", "Shades of AMV.webm");
}
public void setSettings(GameSettings settings) {
this.settings = settings;
}
public void loadGround() {
ground = new Ground();
for (Spatial s : ground.getChildren()) {
s.addControl(new GroundGlowControl(audioDataManager));
}
// for (Spatial s : ground.getChildren()) {
// s.addControl(new GroundGlowControl(audioDataManager));
// }
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
fpp.addFilter(new BloomFilter(BloomFilter.GlowMode.Objects));
viewPort.addProcessor(fpp);
}
public boolean initAudioPlayer() {
public boolean initMediaPlayer() {
try {
this.audioPlayer = new AudioPlayer(DecoderFactory.create("Lost One no Goukoku.mp3")) {//TODO path
@Override
public void finished(boolean cleanup) {
System.out.println(">>> FINISHED!");
}
};
mediaPlayer = mediaPlayerFactory.newDirectMediaPlayer(settings.getVideoFormat(),
settings.getVideoWidth(), settings.getVideoHeight(),
settings.getVideoWidth() * settings.getVideoDepth(), cinema);
mediaPlayer.prepareMedia(settings.getVideoFile());
return true;
} catch (Exception ex) {
return false;
}
}
public void loadCinema() {
cinema = new Cinema("Broken.mp4"); //TODO settings?
cinema = new Cinema(settings);
cinema.move(0f, 160f, 0f);
}
public void loadPlayer() {
player = new Geometry("player", new Box(Vector3f.ZERO, 1f, 1f, 1f)); //TODO start location?
player.setMaterial(Materials.UNSHADED.create());
@ -119,16 +130,15 @@ public class GameState extends AbstractAppState implements GameListener {
player.addControl(new PlayerShootControl(inputManager));
player.addControl(new PlayerCamControl(camera));
}
public void start() {
gui.setStart();
inputManager.setCursorVisible(false);
player.setUserData(UserDataKeys.RUNNING, true);
cinema.play();
audioPlayer.start();
mediaPlayer.play();
isRunning = true;
}
@Override
public void stateAttached(AppStateManager stateManager) {
// stateManager.attach(new FlyCamAppState()); //TODO debug
@ -138,7 +148,7 @@ public class GameState extends AbstractAppState implements GameListener {
gui.setWait();
gui.attach();
}
@Override
public void stateDetached(AppStateManager stateManager) {
// stateManager.detach(new FlyCamAppState()); //TODO debug
@ -146,28 +156,33 @@ public class GameState extends AbstractAppState implements GameListener {
rootNode.detachChild(player);
rootNode.detachChild(ground);
rootNode.detachChild(cinema);
cinema.cleanup();
mediaPlayer.release();
mediaPlayerFactory.release();
inputManager.setCursorVisible(true);
}
@Override
public void render(RenderManager rm) {
cinema.render();
}
@Override
public void update(float tpf) {
audioDataManager.update(tpf);
}
@Override
public void cleanup() {
super.cleanup();
cinema.cleanup();
audioPlayer.cleanup();
mediaPlayer.release();
mediaPlayerFactory.release();
}
public AudioPlayer getAudioPlayer() {
return audioPlayer;
public GameSettings getSettings() {
return settings;
}
public DirectMediaPlayer getMediaPlayer() {
return mediaPlayer;
}
}

View File

@ -56,6 +56,10 @@ public class LoadingState extends AbstractAppState {
public void update(float tpf) {
switch (currentProgress) {
case START:
currentProgress = LoadingProgress.LOADING_CINEMA;
break;
case LOADING_CINEMA:
loadingCinema();
currentProgress = LoadingProgress.INIT_AUDIO_PLAYER;
break;
case INIT_AUDIO_PLAYER:
@ -76,10 +80,6 @@ public class LoadingState extends AbstractAppState {
break;
case ANALYSE_HIGH_BAND:
analyseHighBand();
currentProgress = LoadingProgress.LOADING_CINEMA;
break;
case LOADING_CINEMA:
loadingCinema();
currentProgress = LoadingProgress.GENERATING_SCENE;
break;
case GENERATING_SCENE:
@ -99,7 +99,7 @@ public class LoadingState extends AbstractAppState {
private void initAudioAnalysis() {
GameState gs = stateManager.getState(State.GAME);
audioDataManager.initialize("Lost One no Goukoku.mp3", gs.getAudioPlayer()); //TODO path
audioDataManager.initialize(gs.getSettings(), gs.getMediaPlayer()); //TODO path
}
private void analyseLowBand() {
@ -116,7 +116,7 @@ public class LoadingState extends AbstractAppState {
private void initAudioPlayer() {
GameState gs = stateManager.getState(State.GAME);
gs.initAudioPlayer();
gs.initMediaPlayer();
}
private void loadingCinema() {

View File

@ -23,12 +23,12 @@ package org.wyrez.shootingstars.states.util;
public enum LoadingProgress {
START("Loading...", 0),
INIT_AUDIO_PLAYER("Loading Audio-Player...", 1),
INIT_AUDIO_ANALYSIS("Creating Samples...", 2),
ANALYSE_LOW_BAND("Analyse Low-Band...", 3),
ANALYSE_MID_BAND("Analyse Mid-Band...", 4),
ANALYSE_HIGH_BAND("Analyse High-Band...", 5),
LOADING_CINEMA("Loading Cinema", 6),
LOADING_CINEMA("Loading Cinema", 1),
INIT_AUDIO_PLAYER("Loading Audio-Player...", 2),
INIT_AUDIO_ANALYSIS("Creating Samples...", 3),
ANALYSE_LOW_BAND("Analyse Low-Band...", 4),
ANALYSE_MID_BAND("Analyse Mid-Band...", 5),
ANALYSE_HIGH_BAND("Analyse High-Band...", 6),
GENERATING_SCENE("Generating Scene...", 7),
LOADING_PLAYER("Loading Player...", 8),
DONE("Done!", 8);