Implement JavaFX mediaplayer

main
Joshua Sigona 3 years ago
parent 5e6d626f6e
commit e87126e851
  1. 24
      LLSIG/pom.xml
  2. 4
      LLSIG/src/main/java/LLSIG/BeatTiming.java
  3. 3
      LLSIG/src/main/java/LLSIG/Canvas.java
  4. 339
      LLSIG/src/main/java/LLSIG/JLayerPlayerPausable.java
  5. 61
      LLSIG/src/main/java/LLSIG/LLSIG.java
  6. 4
      LLSIG/src/main/java/LLSIG/Lane.java
  7. 14
      LLSIG/src/main/java/LLSIG/Note.java
  8. 48
      LLSIG/src/main/java/LLSIG/Player.java

@ -25,12 +25,24 @@
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javazoom/jlayer -->
<dependency>
<groupId>javazoom</groupId>
<artifactId>jlayer</artifactId>
<version>1.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javazoom/jlayer -->
<dependency>
<groupId>javazoom</groupId>
<artifactId>jlayer</artifactId>
<version>1.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.openjfx/javafx-media -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>18-ea+4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.openjfx/javafx-swing -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>11-ea+24</version>
</dependency>
</dependencies>
<build>

@ -1,10 +1,10 @@
package main.java.LLSIG;
public class BeatTiming {
long offset;
double offset;
int bpm;
boolean active;
BeatTiming(long offset,int bpm) {
BeatTiming(double offset,int bpm) {
this.offset=offset;
this.bpm=bpm;
this.active=true;

@ -37,8 +37,7 @@ public class Canvas extends JPanel{
g.setColor(Color.BLACK);
g.fillRect(0,0,this.getWidth(),this.getHeight());
g.setColor(Color.WHITE);
g.drawString(Integer.toString(LLSIG.game.musicPlayer.getPlayPosition()),0,32);
g.drawString(Integer.toString(LLSIG.game.musicPlayer.getFrameIndex()),0,64);
g.drawString(Double.toString(LLSIG.game.musicPlayer.getPlayPosition()),0,32);
if (LLSIG.game.BPM_MEASURE) {
g.drawString("Average BPM: "+LLSIG.approximateBPM(),MIDDLE_X-128,MIDDLE_Y+64);
} else

@ -1,339 +0,0 @@
package main.java.LLSIG;
/* *-----------------------------------------------------------------------
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as published
* by the Free Software Foundation; either version 2 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Original by: http://thiscouldbebetter.wordpress.com/2011/07/04/pausing-an-mp3-file-using-jlayer/
* Last modified: 21-jul-2012 by Arthur Assuncao
*----------------------------------------------------------------------
*/
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javazoom.jl.decoder.Bitstream;
import javazoom.jl.decoder.Decoder;
import javazoom.jl.decoder.Header;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.decoder.SampleBuffer;
import javazoom.jl.player.AudioDevice;
import javazoom.jl.player.FactoryRegistry;
//use with JLayerPausableTest
public class JLayerPlayerPausable{
// This class is loosely based on javazoom.jl.player.AdvancedPlayer.
private java.net.URL urlToStreamFrom;
private String audioPath;
private Bitstream bitstream;
private Decoder decoder;
private AudioDevice audioDevice;
private boolean closed;
private boolean complete;
private boolean paused;
private boolean stopped;
private PlaybackListener listener;
private int frameIndexCurrent;
private int resumePoint;
private final int lostFrames = 20; //some fraction of a second of the sound gets "lost" after every pause. 52 in original code
public JLayerPlayerPausable(URL urlToStreamFrom) throws JavaLayerException{
this.urlToStreamFrom = urlToStreamFrom;
this.listener = new PlaybackAdapter();
}
public JLayerPlayerPausable(String audioPath) throws JavaLayerException{
this.audioPath = audioPath;
this.listener = new PlaybackAdapter();
}
public void setPlaybackListener(PlaybackListener newPlaybackListener){
if(newPlaybackListener != null){
this.listener = newPlaybackListener;
}
else{
throw new NullPointerException("PlaybackListener is null");
}
}
public int getPosition() {
return this.audioDevice!=null?this.audioDevice.getPosition()+resumePoint:resumePoint;
}
private InputStream getAudioInputStream() throws IOException{
if(this.audioPath != null){
return new FileInputStream(this.audioPath);
}
else if(this.urlToStreamFrom != null){
this.urlToStreamFrom.openStream();
}
return null;
}
public boolean play() throws JavaLayerException{
return this.play(0);
}
public boolean play(long frameIndexStart) throws JavaLayerException {
//return this.play(frameIndexStart, -1, 52); //original, mas voltava num ponto anterior ao do pause. 52 Sao os frames perdidos ao dar pause
return this.play(frameIndexStart, -1, lostFrames);
}
public boolean play(long frameIndexStart, int frameIndexFinal, int correctionFactorInFrames) throws JavaLayerException{
try {
this.bitstream = new Bitstream(this.getAudioInputStream());
}
catch (IOException e) {
e.printStackTrace();
}
this.audioDevice = FactoryRegistry.systemRegistry().createAudioDevice();
this.decoder = new Decoder();
this.audioDevice.open(this.decoder);
boolean shouldContinueReadingFrames = true;
this.paused = false;
this.stopped = false;
this.frameIndexCurrent = 0;
while (shouldContinueReadingFrames == true && this.frameIndexCurrent < frameIndexStart - correctionFactorInFrames){
shouldContinueReadingFrames = this.skipFrame();
this.frameIndexCurrent++;
}
if (this.listener != null) {
this.listener.playbackStarted(new PlaybackEvent(this, PlaybackEvent.EventType.Instances.Started, this.audioDevice.getPosition()));
}
if (frameIndexFinal < 0){
frameIndexFinal = Integer.MAX_VALUE;
}
while (shouldContinueReadingFrames == true && this.frameIndexCurrent < frameIndexFinal){
if (this.paused || this.stopped){
shouldContinueReadingFrames = false;
try{
Thread.sleep(1);
}
catch (Exception ex){
ex.printStackTrace();
}
}
else{
shouldContinueReadingFrames = this.decodeFrame();
this.frameIndexCurrent++;
}
}
// last frame, ensure all data flushed to the audio device.
if (this.audioDevice != null && !this.paused){
this.audioDevice.flush();
synchronized (this){
this.complete = (this.closed == false);
this.close();
}
// report to listener
if (this.listener != null) {
int audioDevicePosition = -1;
if(this.audioDevice != null){
audioDevicePosition = this.audioDevice.getPosition();
}
else{
//throw new NullPointerException("attribute audioDevice in " + this.getClass() + " is NULL");
}
PlaybackEvent playbackEvent = new PlaybackEvent(this, PlaybackEvent.EventType.Instances.Stopped, audioDevicePosition);
this.listener.playbackFinished(playbackEvent);
}
}
return shouldContinueReadingFrames;
}
public int getFrameIndex() {
return this.frameIndexCurrent;
}
public boolean resume() throws JavaLayerException{
return this.play(this.frameIndexCurrent);
}
public synchronized void close(){
if (this.audioDevice != null){
this.closed = true;
this.audioDevice.close();
this.audioDevice = null;
try{
this.bitstream.close();
}
catch (Exception ex){
ex.printStackTrace();
}
}
}
protected boolean decodeFrame() throws JavaLayerException{
boolean returnValue = false;
if(this.stopped){ //nothing for decode
return false;
}
try{
if (this.audioDevice != null){
Header header = this.bitstream.readFrame();
if (header != null){
// sample buffer set when decoder constructed
SampleBuffer output = (SampleBuffer) this.decoder.decodeFrame(header, this.bitstream);
synchronized (this){
if (this.audioDevice != null){
this.audioDevice.write(output.getBuffer(), 0, output.getBufferLength());
}
}
this.bitstream.closeFrame();
returnValue = true;
}
else{
System.out.println("End of file"); //end of file
//this.stop();
returnValue = false;
}
}
}
catch (RuntimeException ex){
throw new JavaLayerException("Exception decoding audio frame", ex);
}
return returnValue;
}
public void pause(){
if(!this.stopped){
this.paused = true;
if (this.listener != null) {
this.resumePoint = getPosition();
System.out.println("Resume Point: "+resumePoint);
this.listener.playbackPaused(new PlaybackEvent(this, PlaybackEvent.EventType.Instances.Paused, this.audioDevice.getPosition()));
}
this.close();
}
}
protected boolean skipFrame() throws JavaLayerException{
boolean returnValue = false;
Header header = this.bitstream.readFrame();
if (header != null) {
this.bitstream.closeFrame();
returnValue = true;
}
return returnValue;
}
public void stop(){
if(!this.stopped){
if(!this.closed){
this.listener.playbackFinished(new PlaybackEvent(this, PlaybackEvent.EventType.Instances.Stopped, this.audioDevice.getPosition()));
this.close();
}
else if(this.paused){
int audioDevicePosition = -1; //this.audioDevice.getPosition(), audioDevice is null
this.listener.playbackFinished(new PlaybackEvent(this, PlaybackEvent.EventType.Instances.Stopped, audioDevicePosition));
}
this.stopped = true;
}
}
/**
* @return the closed
*/
public boolean isClosed() {
return closed;
}
/**
* @return the complete
*/
public boolean isComplete() {
return complete;
}
/**
* @return the paused
*/
public boolean isPaused() {
return paused;
}
/**
* @return the stopped
*/
public boolean isStopped() {
return stopped;
}
// inner classes
public static class PlaybackEvent{
public JLayerPlayerPausable source;
public EventType eventType;
public int frameIndex;
public PlaybackEvent(JLayerPlayerPausable source, EventType eventType, int frameIndex) {
this.source = source;
this.eventType = eventType;
this.frameIndex = frameIndex;
}
public static class EventType{
public String name;
public EventType(String name){
this.name = name;
}
public static class Instances{
public static EventType Started = new EventType("Started");
public static EventType Paused = new EventType("Paused");
public static EventType Stopped = new EventType("Stopped");
}
}
}
public static class PlaybackAdapter implements PlaybackListener{
@Override
public void playbackStarted(PlaybackEvent event){
System.err.println("Playback started");
}
@Override
public void playbackPaused(PlaybackEvent event){
System.err.println("Playback paused");
}
@Override
public void playbackFinished(PlaybackEvent event){
System.err.println("Playback stopped");
}
}
public static interface PlaybackListener{
public void playbackStarted(PlaybackEvent event);
public void playbackPaused(PlaybackEvent event);
public void playbackFinished(PlaybackEvent event);
}
}

@ -6,12 +6,13 @@ import java.awt.event.KeyListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.nio.file.Paths;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
@ -21,6 +22,8 @@ import javax.swing.JFrame;
import main.java.sig.utils.FileUtils;
import javafx.application.Platform;
public class LLSIG implements KeyListener{
Player musicPlayer;
JFrame window;
@ -30,8 +33,8 @@ public class LLSIG implements KeyListener{
public static LLSIG game;
public static Font gameFont = new Font("Century Gothic",Font.BOLD,32);
public static int bpm = 120;
public static long offset = 0;
public static long testOffset = 0;
public static double offset = 0;
public static double testOffset = 0;
public static double beatDelay = ((1/((double)bpm/60))*1000);
public static List<Long> beats = new ArrayList<Long>();
@ -39,15 +42,13 @@ public class LLSIG implements KeyListener{
int NOTE_SPEED = 750; //The note speed determines how early you see the note. So lowering this number increases the speed.
List<Lane> lanes = new ArrayList<Lane>();
List<BeatTiming> timings = new ArrayList<BeatTiming>();
HashMap<Long,Long> frameLookup = new HashMap<>();
String song = "MiChi - ONE-315959669";
String song = "MiChi-ONE";
final static Dimension WINDOW_SIZE = new Dimension(1280,1050);
public boolean EDITMODE = false;
public boolean ANALYSIS = true;
public boolean METRONOME = ANALYSIS||true;
public boolean METRONOME = true;
public boolean BPM_MEASURE = false;
public boolean PLAYING = true; //Whether or not a song is loaded and playing.
public static int beatNumber = 0;
@ -66,17 +67,23 @@ public class LLSIG implements KeyListener{
public static int EARLY_COUNT = 0;
public static int LATE_COUNT = 0;
public static int MISS_COUNT = 0;
public static int LAST_PERFECT = 0;
public static int LAST_EXCELLENT = 0;
public static int LAST_GREAT = 0;
public static int LAST_EARLY = 0;
public static int LAST_LATE = 0;
public static int LAST_MISS = 0;
public static double LAST_PERFECT = 0;
public static double LAST_EXCELLENT = 0;
public static double LAST_GREAT = 0;
public static double LAST_EARLY = 0;
public static double LAST_LATE = 0;
public static double LAST_MISS = 0;
public static int COMBO = 0;
public static Clip metronome_click1,metronome_click2;
LLSIG(JFrame f) {
Platform.startup(() ->
{
// This block will be executed on JavaFX Thread
});
this.window = f;
AudioInputStream audioInputStream;
@ -103,10 +110,10 @@ public class LLSIG implements KeyListener{
PLAYING = new File("music/"+song+".mp3").exists();
if (PLAYING) {
this.musicPlayer = new Player("music/"+song+".mp3");
musicPlayer.play();
this.musicPlayer = new Player(Paths.get("music/"+song+".mp3").toUri().toString());
musicPlayer.play(148900l);
LoadSongData("MiChi - ONE-315959669",lanes);
LoadSongData(song,lanes);
}
Canvas canvas = new Canvas(f.getSize());
window.add(canvas);
@ -128,11 +135,6 @@ public class LLSIG implements KeyListener{
if (METRONOME) {
if (beatNumber*beatDelay+offset<musicPlayer.getPlayPosition()) {
beatNumber++;
if (ANALYSIS) {
Long lookup = (long)musicPlayer.getPlayPosition();
frameLookup.put(lookup,(long)musicPlayer.getFrameIndex());
System.out.println("Mapped Position "+lookup+" to "+frameLookup.get(lookup));
}
if (beatNumber%4==0) {
metronome_click1.setFramePosition(0);
metronome_click1.start();
@ -164,18 +166,12 @@ public class LLSIG implements KeyListener{
lanes.add(new Lane(new ArrayList<Note>()));
}
timings.clear();
frameLookup.clear();
try {
String[] data = FileUtils.readFromFile("music/"+song+".sig");
for (String line : data) {
String[] split = line.split(Pattern.quote(","));
if (split[0].equals("F")) {
long position = Long.parseLong(split[1]);
long frameIndex = Long.parseLong(split[2]);
frameLookup.put(position,frameIndex);
} else
if (split[0].equals("B")) {
offset=Integer.parseInt(split[1]);
offset=Double.parseDouble(split[1]);
bpm=Integer.parseInt(split[2]);
beatDelay = ((1/((double)bpm/60))*1000);
timings.add(new BeatTiming(offset,bpm));
@ -209,13 +205,6 @@ public class LLSIG implements KeyListener{
.append(bt.bpm)
.toString());
}
for (Long key : frameLookup.keySet()) {
Long frameIndex = frameLookup.get(key);
data.add(new StringBuilder().append("F").append(",")
.append(key).append(",")
.append(frameIndex)
.toString());
}
for (int lane=0;lane<lanes.size();lane++) {
Lane l = lanes.get(lane);
int noteCount=0;
@ -298,7 +287,7 @@ public class LLSIG implements KeyListener{
Lane l = lanes.get(lane);
if (l.noteExists()) {
Note n = l.getNote();
int diff = n.getStartFrame()-LLSIG.game.musicPlayer.getPlayPosition();
double diff = n.getStartFrame()-LLSIG.game.musicPlayer.getPlayPosition();
if (diff<=BAD_TIMING_WINDOW) {
if (Math.abs(diff)<=PERFECT_TIMING_WINDOW) {l.lastRating=TimingRating.PERFECT;COMBO++;PERFECT_COUNT++;LAST_PERFECT=LLSIG.game.musicPlayer.getPlayPosition();} else
if (Math.abs(diff)<=EXCELLENT_TIMING_WINDOW) {l.lastRating=TimingRating.EXCELLENT;COMBO++;EXCELLENT_COUNT++;LAST_EXCELLENT=LLSIG.game.musicPlayer.getPlayPosition();} else

@ -5,7 +5,7 @@ public class Lane{
List<Note> noteChart;
int currentNoteIndex = 0;
TimingRating lastRating = TimingRating.MISS;
int lastNote = -1;
double lastNote = -1;
public Lane(List<Note> noteChart) {
super();
this.noteChart = noteChart;
@ -63,7 +63,7 @@ public class Lane{
public void markMissedNotes() {
if (LLSIG.game.PLAYING) {
noteChart.forEach((note)->{
int diff = note.getStartFrame()-LLSIG.game.musicPlayer.getPlayPosition();
double diff = note.getStartFrame()-LLSIG.game.musicPlayer.getPlayPosition();
if (diff<-LLSIG.BAD_TIMING_WINDOW) {
note.active=false;
lastRating = TimingRating.MISS;

@ -2,15 +2,15 @@ package main.java.LLSIG;
public class Note {
NoteType type;
int start,end;
double start,end;
boolean active=true; //Set to false when the note has been scored.
double beatSnapStart,beatSnapEnd = -1;
public Note(NoteType type,int start,int end) {
public Note(NoteType type,double start,double end) {
this.type=type;
this.start=start;
this.end=end;
}
public Note(NoteType type,int start) {
public Note(NoteType type,double start) {
this(type,start,-1);
}
public NoteType getNoteType() {
@ -19,16 +19,16 @@ public class Note {
public void setNoteType(NoteType type) {
this.type = type;
}
public int getStartFrame() {
public double getStartFrame() {
return start;
}
public void setStartFrame(int start) {
public void setStartFrame(double start) {
this.start = start;
}
public int getEndFrame() {
public double getEndFrame() {
return end;
}
public void setEndFrame(int end) {
public void setEndFrame(double end) {
this.end = end;
}
public void setBeatSnap(double value) {

@ -1,35 +1,30 @@
package main.java.LLSIG;
import javazoom.jl.decoder.JavaLayerException;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaPlayer.Status;
import javafx.util.Duration;
public class Player {
JLayerPlayerPausable jlpp;
String song;
MediaPlayer jlpp;
Media song;
public Player(String song) {
this.song=song;
this.song=new Media(song);
jlpp = new MediaPlayer(this.song);
}
public void play() {
new Thread() {
public void run() {
try {
if (jlpp!=null) {jlpp.close();}
jlpp = new JLayerPlayerPausable(song);
jlpp.play();
} catch (JavaLayerException e) {
e.printStackTrace();
}
jlpp.stop();
jlpp.play();
}
}.start();
}
public void play(long frame) {
new Thread() {
public void run() {
try {
if (jlpp!=null) {jlpp.close();}
jlpp = new JLayerPlayerPausable(song);
jlpp.play(frame);
} catch (JavaLayerException e) {
e.printStackTrace();
}
jlpp.stop();
jlpp.play();
jlpp.seek(new Duration(frame));
}
}.start();
}
@ -37,26 +32,19 @@ public class Player {
jlpp.pause();
}
public boolean isPaused() {
return jlpp.isPaused();
return jlpp.getStatus()==Status.PAUSED;
}
public void resume() {
new Thread() {
public void run() {
try {
jlpp.resume();
} catch (JavaLayerException e) {
e.printStackTrace();
}
jlpp.play();
}
}.start();
}
public void kill() {
jlpp.close();
jlpp.dispose();
}
public int getPlayPosition() {
return jlpp.getPosition();
}
public int getFrameIndex() {
return jlpp.getFrameIndex();
public double getPlayPosition() {
return jlpp.getCurrentTime().toMillis();
}
}

Loading…
Cancel
Save