commit d0d1e497e78d3078073170ee6923f6186239e74b Author: Joshua Sigona Date: Mon Sep 6 14:02:26 2021 +0900 Music and render engine. diff --git a/LLSIG/.classpath b/LLSIG/.classpath new file mode 100644 index 0000000..2892fba --- /dev/null +++ b/LLSIG/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/LLSIG/.gitignore b/LLSIG/.gitignore new file mode 100644 index 0000000..e9fff3b --- /dev/null +++ b/LLSIG/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/music/ diff --git a/LLSIG/.project b/LLSIG/.project new file mode 100644 index 0000000..ec43181 --- /dev/null +++ b/LLSIG/.project @@ -0,0 +1,17 @@ + + + LLSIG + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/LLSIG/.settings/org.eclipse.jdt.core.prefs b/LLSIG/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..ec1937b --- /dev/null +++ b/LLSIG/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,12 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/LLSIG/lib/jlayer-1.0.1.jar b/LLSIG/lib/jlayer-1.0.1.jar new file mode 100644 index 0000000..382feff Binary files /dev/null and b/LLSIG/lib/jlayer-1.0.1.jar differ diff --git a/LLSIG/src/Canvas.java b/LLSIG/src/Canvas.java new file mode 100644 index 0000000..66d943a --- /dev/null +++ b/LLSIG/src/Canvas.java @@ -0,0 +1,42 @@ +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; + +import javax.swing.JPanel; + +public class Canvas extends JPanel{ + final Color NOTE_COLOR = new Color(196,116,116); + + public Canvas(Dimension size) { + super(); + this.setSize(size); + this.setMinimumSize(size); + } + public void paintComponent(Graphics g) { + + final int MIDDLE_X = this.getWidth()/2; + final int MIDDLE_Y = this.getHeight()/2; + final int JUDGEMENT_LINE_WIDTH = 64; + final int JUDGEMENT_LINE_HEIGHT = 4; + final int NOTE_SIZE = 16; + + super.paintComponent(g); + g.setColor(Color.BLACK); + g.fillRect(0,0,this.getWidth(),this.getHeight()); + g.setColor(Color.WHITE); + g.drawString(Integer.toString(LLSIG.game!=null?LLSIG.game.frameCount:0),0,16); + + g.setColor(Color.GRAY); + g.fillRect(MIDDLE_X-JUDGEMENT_LINE_WIDTH/2,MIDDLE_Y-JUDGEMENT_LINE_HEIGHT/2,JUDGEMENT_LINE_WIDTH,JUDGEMENT_LINE_HEIGHT); + + g.setColor(NOTE_COLOR); + int noteCounter = 0; + Lane lane1 = LLSIG.game.lanes.get(0); + while (lane1.noteExists(noteCounter)) { + Note n = lane1.getNote(noteCounter); + int NOTE_Y_OFFSET = (int)((((double)LLSIG.game.musicPlayer.getPlayPosition()-n.getStartFrame())/1000)*60*LLSIG.game.noteSpeed); + g.fillOval(MIDDLE_X-NOTE_SIZE/2,MIDDLE_Y-NOTE_SIZE/2+NOTE_Y_OFFSET,NOTE_SIZE,NOTE_SIZE); + noteCounter++; + } + } +} diff --git a/LLSIG/src/JLayerPlayerPausable.java b/LLSIG/src/JLayerPlayerPausable.java new file mode 100644 index 0000000..f84cb4c --- /dev/null +++ b/LLSIG/src/JLayerPlayerPausable.java @@ -0,0 +1,334 @@ +/* *----------------------------------------------------------------------- + * 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(int 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(int 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 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); + } +} \ No newline at end of file diff --git a/LLSIG/src/LLSIG.java b/LLSIG/src/LLSIG.java new file mode 100644 index 0000000..a172f60 --- /dev/null +++ b/LLSIG/src/LLSIG.java @@ -0,0 +1,74 @@ +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import javax.swing.JFrame; + +import javazoom.jl.decoder.JavaLayerException; + +public class LLSIG implements KeyListener{ + Player musicPlayer; + JFrame window; + Thread gameLoop; + ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(1); + int frameCount; + public static LLSIG game; + int noteSpeed = 4; + List lanes = new ArrayList(); + + LLSIG(JFrame f) { + this.window = f; + this.musicPlayer = new Player("music/MiChi - ONE-315959669.mp3"); + musicPlayer.play(); + + lanes.add(new Lane(Arrays.asList(new Note[] { + new Note(NoteType.NORMAL,1000), + new Note(NoteType.NORMAL,2000), + new Note(NoteType.NORMAL,3000), + new Note(NoteType.NORMAL,4000), + }))); + + Canvas canvas = new Canvas(f.getSize()); + window.add(canvas); + window.setVisible(true); + window.addKeyListener(this); + gameLoop = new Thread() { + public void run() { + frameCount++; + window.repaint(); + } + }; + stpe.scheduleAtFixedRate(gameLoop, 0, 16666666l, TimeUnit.NANOSECONDS); + } + + public static void main(String[] args) { + JFrame f = new JFrame(); + f.setSize(640, 640); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + game = new LLSIG(f); + } + + @Override + public void keyTyped(KeyEvent e) { + // TODO Auto-generated method stub + } + + @Override + public void keyPressed(KeyEvent e) { + System.out.println("Pressed "+e.getKeyChar()+" on frame "+musicPlayer.getPlayPosition()); + } + + @Override + public void keyReleased(KeyEvent e) { + // TODO Auto-generated method stub + + } +} diff --git a/LLSIG/src/Lane.java b/LLSIG/src/Lane.java new file mode 100644 index 0000000..76e6ba0 --- /dev/null +++ b/LLSIG/src/Lane.java @@ -0,0 +1,26 @@ +import java.util.ArrayList; +import java.util.List; + +public class Lane{ + List noteChart; + int currentNoteIndex = 0; + public Lane(List noteChart) { + super(); + this.noteChart = noteChart; + } + public boolean endOfChart() { + return currentNoteIndex==noteChart.size()-1; + } + public void consumeNote() { + currentNoteIndex = Math.min(currentNoteIndex+1,noteChart.size()-1); + } + public boolean noteExists(int noteOffset) { + return currentNoteIndex+noteOffset