diff --git a/README.md b/README.md index 99c3862..d852172 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ -# Java-stream-player -Java Audio Controller Library with (seek,start,stop,pause,play,restart features) - -This is a continuation and full improvement of [JavaZoom BasicPlayer](http://www.javazoom.net/jlgui/api.html) - [![Latest Version](https://img.shields.io/github/release/goxr3plus/java-stream-player.svg?style=flat-square)](https://github.com/goxr3plus/java-stream-player/releases) [![HitCount](http://hits.dwyl.io/goxr3plus/java-stream-player.svg)](http://hits.dwyl.io/goxr3plus/java-stream-player) Patreon donate button PayPal donate button +# Java-stream-player +Java Audio Controller Library with (skip,skipTo,start,stop,pause,play,restart features) +This is the next version of [JavaZoom BasicPlayer](http://www.javazoom.net/jlgui/api.html) + ### What audio formats it supports? It supports **WAV, AU, AIFF, MP3, OGG VORBIS, FLAC, MONKEY's AUDIO and SPEEX audio formats** , using some external libraries . Although more will be added in future releases. @@ -33,26 +32,6 @@ https://jitpack.io/private#goxr3plus/java-stream-player ``` -## Java Audio Tutorials and API's by GOXR3PLUS STUDIO - - **Spectrum Analyzers** - - [Java-Audio-Wave-Spectrum-API](https://github.com/goxr3plus/Java-Audio-Wave-Spectrum-API) - ![image](https://github.com/goxr3plus/Java-Audio-Wave-Spectrum-API/raw/master/images/Screenshot_2.jpg?raw=true) - - [Jave Spectrum Analyzers from Audio](https://github.com/goxr3plus/Java-Spectrum-Analyser-Tutorials) - - [Capture Audio from Microphone and make complex spectrum analyzers](https://github.com/goxr3plus/Java-Microphone-Audio-Spectrum-Analyzers-Tutorial) - - - **Java multiple audio formats player** - - [Java-stream-player](https://github.com/goxr3plus/java-stream-player) - - - **Speech Recognition/Translation/Synthenizers** - - [Java Speech Recognition/Translation/Synthesizer based on Google Cloud Services](https://github.com/goxr3plus/java-google-speech-api) - - [Java-Speech-Recognizer-Tutorial--Calculator](https://github.com/goxr3plus/Java-Speech-Recognizer-Tutorial--Calculator) - - [Java+MaryTTS=Java Text To Speech](https://github.com/goxr3plus/Java-Text-To-Speech-Tutorial) - - [Java Speech Recognition Program based on Google Cloud Services ](https://github.com/goxr3plus/Java-Google-Speech-Recognizer) - - [Java Google Text To Speech](https://github.com/goxr3plus/Java-Google-Text-To-Speech) - - [Full Google Translate Support using Java](https://github.com/goxr3plus/java-google-translator) - - [Professional Java Google Desktop Translator](https://github.com/goxr3plus/Java-Google-Desktop-Translator) - - Example usage : ``` JAVA @@ -71,123 +50,145 @@ import com.goxr3plus.streamplayer.stream.StreamPlayerException; */ public class Main extends StreamPlayer implements StreamPlayerListener { - private final String audioAbsolutePath = "Logic - Ballin [Bass Boosted].mp3"; - - /** - * Constructor - */ - public Main() { - - try { + private final String audioAbsolutePath = "Logic - Ballin [Bass Boosted].mp3"; - // Register to the Listeners - addStreamPlayerListener(this); + /** + * Constructor + */ + public Main() { - // Open a File - // open(new File("...")) //..Here must be the file absolute path - // open(INPUTSTREAM) - // open(AUDIOURL) + try { - // Example - open(new File(audioAbsolutePath)); + // Register to the Listeners + addStreamPlayerListener(this); - //Seek by bytes - //seekBytes(500000L); + // Open a File + // open(new File("...")) //..Here must be the file absolute path + // open(INPUTSTREAM) + // open(AUDIOURL) - //Seek +x seconds starting from the current position - seekSeconds(15); - seekSeconds(15); + // Example + open(new File(audioAbsolutePath)); - /* Seek starting from the begginning of the audio */ - //seekTo(200); + // Seek by bytes + // seekBytes(500000L); - // Play it - play(); - //pause(); + // Seek +x seconds starting from the current position + seekSeconds(15); + seekSeconds(15); - } catch (final Exception ex) { - ex.printStackTrace(); - } + /* Seek starting from the begginning of the audio */ + // seekTo(200); - } + // Play it + play(); + // pause(); - @Override - public void opened(final Object dataSource, final Map properties) { + } catch (final Exception ex) { + ex.printStackTrace(); + } - } + } - @Override - public void progress(final int nEncodedBytes, final long microsecondPosition, final byte[] pcmData,final Map properties) { + @Override + public void opened(final Object dataSource, final Map properties) { -// System.out.println("Encoded Bytes : " + nEncodedBytes); + } - // Current time position in seconds:) by GOXR3PLUS STUDIO - // This is not the more precise way ... - // in XR3Player i am using different techniques . - //https://github.com/goxr3plus/XR3Player - // Just for demostration purposes :) - // I will add more advanced techniques with milliseconds , microseconds , hours - // and minutes soon + @Override + public void progress(final int nEncodedBytes, final long microsecondPosition, final byte[] pcmData, + final Map properties) { - // .MP3 OR .WAV - final String extension = "mp3"; //THE SAMPLE Audio i am using is .MP3 SO ... :) + // System.out.println("Encoded Bytes : " + nEncodedBytes); - long totalBytes = getTotalBytes(); - if ("mp3".equals(extension) || "wav".equals(extension)) { + // Current time position in seconds:) by GOXR3PLUS STUDIO + // This is not the more precise way ... + // in XR3Player i am using different techniques . + // https://github.com/goxr3plus/XR3Player + // Just for demostration purposes :) + // I will add more advanced techniques with milliseconds , microseconds , hours + // and minutes soon - // Calculate the progress until now - double progress = (nEncodedBytes > 0 && totalBytes > 0) - ? (nEncodedBytes * 1.0f / totalBytes * 1.0f) - : -1.0f; -// System.out.println(progress*100+"%"); + // .MP3 OR .WAV + final String extension = "mp3"; // THE SAMPLE Audio i am using is .MP3 SO ... :) - System.out.println("Seconds : " + (int) (microsecondPosition / 1000000) + " s " + "Progress: [ " + progress * 100 + " ] %"); + long totalBytes = getTotalBytes(); + if ("mp3".equals(extension) || "wav".equals(extension)) { + // Calculate the progress until now + double progress = (nEncodedBytes > 0 && totalBytes > 0) ? (nEncodedBytes * 1.0f / totalBytes * 1.0f) + : -1.0f; + // System.out.println(progress*100+"%"); - // .WHATEVER MUSIC FILE* - } else { - //System.out.println("Current time is : " + (int) (microsecondPosition / 1000000) + " seconds"); - } + System.out.println("Seconds : " + (int) (microsecondPosition / 1000000) + " s " + "Progress: [ " + + progress * 100 + " ] %"); + // .WHATEVER MUSIC FILE* + } else { + // System.out.println("Current time is : " + (int) (microsecondPosition / + // 1000000) + " seconds"); + } - } + } - @Override - public void statusUpdated(final StreamPlayerEvent streamPlayerEvent) { + @Override + public void statusUpdated(final StreamPlayerEvent streamPlayerEvent) { - // Player status - final Status status = streamPlayerEvent.getPlayerStatus(); - //System.out.println(streamPlayerEvent.getPlayerStatus()); + // Player status + final Status status = streamPlayerEvent.getPlayerStatus(); + // System.out.println(streamPlayerEvent.getPlayerStatus()); - //Examples + // Examples - if (status == Status.OPENED) { + if (status == Status.OPENED) { - } else if (status == Status.OPENING) { + } else if (status == Status.OPENING) { - } else if (status == Status.RESUMED) { + } else if (status == Status.RESUMED) { - } else if (status == Status.PLAYING) { + } else if (status == Status.PLAYING) { - } else if (status == Status.STOPPED) { + } else if (status == Status.STOPPED) { - } else if (status == Status.SEEKING) { + } else if (status == Status.SEEKING) { - } else if (status == Status.SEEKED) { + } else if (status == Status.SEEKED) { - } + } - //etc... SEE XR3PLAYER https://github.com/goxr3plus/XR3Player for advanced examples - } + // etc... SEE XR3PLAYER https://github.com/goxr3plus/XR3Player for advanced + // examples + } - public static void main(final String[] args) { - new Main(); - } + public static void main(final String[] args) { + new Main(); + } } ``` +## Java Audio Tutorials and API's by GOXR3PLUS STUDIO + - **Spectrum Analyzers** + - [Java-Audio-Wave-Spectrum-API](https://github.com/goxr3plus/Java-Audio-Wave-Spectrum-API) + ![image](https://github.com/goxr3plus/Java-Audio-Wave-Spectrum-API/raw/master/images/Screenshot_2.jpg?raw=true) + - [Jave Spectrum Analyzers from Audio](https://github.com/goxr3plus/Java-Spectrum-Analyser-Tutorials) + - [Capture Audio from Microphone and make complex spectrum analyzers](https://github.com/goxr3plus/Java-Microphone-Audio-Spectrum-Analyzers-Tutorial) + + - **Java multiple audio formats player** + - [Java-stream-player](https://github.com/goxr3plus/java-stream-player) + + - **Speech Recognition/Translation/Synthenizers** + - [Java Speech Recognition/Translation/Synthesizer based on Google Cloud Services](https://github.com/goxr3plus/java-google-speech-api) + - [Java-Speech-Recognizer-Tutorial--Calculator](https://github.com/goxr3plus/Java-Speech-Recognizer-Tutorial--Calculator) + - [Java+MaryTTS=Java Text To Speech](https://github.com/goxr3plus/Java-Text-To-Speech-Tutorial) + - [Java Speech Recognition Program based on Google Cloud Services ](https://github.com/goxr3plus/Java-Google-Speech-Recognizer) + - [Java Google Text To Speech](https://github.com/goxr3plus/Java-Google-Text-To-Speech) + - [Full Google Translate Support using Java](https://github.com/goxr3plus/java-google-translator) + - [Professional Java Google Desktop Translator](https://github.com/goxr3plus/Java-Google-Desktop-Translator) + + + --- ### Looking for a ffmpeg wrapper in Java ? diff --git a/pom.xml b/pom.xml index c0f4f2a..fc849b0 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.github.goxr3plus @@ -13,7 +13,7 @@ 1.8 1.8 UTF-8 - 5.5.1 + 5.1.1 @@ -165,6 +165,13 @@ test + + + org.mockito + mockito-junit-jupiter + 3.0.0 + test + diff --git a/src/main/java/com/goxr3plus/streamplayer/application/AnotherDemoApplication.java b/src/main/java/com/goxr3plus/streamplayer/application/AnotherDemoApplication.java new file mode 100644 index 0000000..5bd49e8 --- /dev/null +++ b/src/main/java/com/goxr3plus/streamplayer/application/AnotherDemoApplication.java @@ -0,0 +1,74 @@ +package com.goxr3plus.streamplayer.application; + +import com.goxr3plus.streamplayer.enums.Status; +import com.goxr3plus.streamplayer.stream.StreamPlayer; +import com.goxr3plus.streamplayer.stream.StreamPlayerEvent; +import com.goxr3plus.streamplayer.stream.StreamPlayerListener; + +import java.io.File; +import java.util.Map; + +/** + * @author GOXR3PLUS + * + */ +public class AnotherDemoApplication { + + private final String audioFileName = "Logic - Ballin [Bass Boosted].mp3"; + + private StreamPlayer streamPlayer; + private StreamPlayerListener listener; + + public AnotherDemoApplication(StreamPlayer streamPlayer) { + this.streamPlayer = streamPlayer; + this.listener = new AnotherStreamPlayerListener(audioFileName, streamPlayer); + + } + + + void start() { + try { + + // Register to the Listeners + streamPlayer.addStreamPlayerListener(listener); + + // Open a File + // open(new File("...")) //..Here must be the file absolute path + // open(INPUTSTREAM) + // open(AUDIOURL) + + // Example + streamPlayer.open(new File(audioFileName)); + + //Seek by bytes + //seekBytes(500000L); + + //Seek +x seconds starting from the current position + streamPlayer.seekSeconds(15); // forward 15 seconds + streamPlayer.seekSeconds(15); // forward 15 seconds again + + /* Seek starting from the begginning of the audio */ + //seekTo(200); + + // Play it + streamPlayer.play(); + //pause(); + + } catch (final Exception ex) { + ex.printStackTrace(); + } + } + + + + + private String getExtension(String audioFileName) { + return audioFileName.split("\\.(?=[^.]+$)")[1]; + } + + +// public static void main(final String[] args) { +// new AnotherDemoApplication(); +// } + +} diff --git a/src/main/java/com/goxr3plus/streamplayer/application/AnotherMain.java b/src/main/java/com/goxr3plus/streamplayer/application/AnotherMain.java new file mode 100644 index 0000000..d8bfdb0 --- /dev/null +++ b/src/main/java/com/goxr3plus/streamplayer/application/AnotherMain.java @@ -0,0 +1,15 @@ +package com.goxr3plus.streamplayer.application; + +import com.goxr3plus.streamplayer.stream.StreamPlayer; +import com.goxr3plus.streamplayer.stream.StreamPlayerListener; + +public class AnotherMain { + public static void main(String[] args) { + + final StreamPlayer streamPlayer = new StreamPlayer(); + final AnotherDemoApplication application = new AnotherDemoApplication(streamPlayer); + application.start(); + + } + +} diff --git a/src/main/java/com/goxr3plus/streamplayer/application/AnotherStreamPlayerListener.java b/src/main/java/com/goxr3plus/streamplayer/application/AnotherStreamPlayerListener.java new file mode 100644 index 0000000..2b27805 --- /dev/null +++ b/src/main/java/com/goxr3plus/streamplayer/application/AnotherStreamPlayerListener.java @@ -0,0 +1,88 @@ +package com.goxr3plus.streamplayer.application; + +import com.goxr3plus.streamplayer.enums.Status; +import com.goxr3plus.streamplayer.stream.StreamPlayer; +import com.goxr3plus.streamplayer.stream.StreamPlayerEvent; +import com.goxr3plus.streamplayer.stream.StreamPlayerListener; + +import java.util.Map; + +class AnotherStreamPlayerListener implements StreamPlayerListener { + + private final String audioFileName; + private StreamPlayer streamPlayer; + + + public AnotherStreamPlayerListener(String audioFileName, StreamPlayer streamPlayer) { + this.audioFileName = audioFileName; + this.streamPlayer = streamPlayer; + } + + /** + * It is called when the StreamPlayer open(Object object) method is called. + * + * @param dataSource the data source + * @param properties the properties + */ + @Override + public void opened(Object dataSource, Map properties) { + System.out.println("The StreamPlayer was opened."); + } + + /** + * Is called several times per second when StreamPlayer run method is + * running. + * + * @param nEncodedBytes the n encoded bytes + * @param microsecondPosition the microsecond position + * @param pcmData the pcm data + * @param properties the properties + */ + @Override + public void progress(int nEncodedBytes, long microsecondPosition, byte[] pcmData, Map properties) { + + String extension = getExtension(audioFileName); + + + long totalBytes = streamPlayer.getTotalBytes(); + if ("mp3".equals(extension) || "wav".equals(extension)) { + + // Calculate the progress until now + double progress = (nEncodedBytes > 0 && totalBytes > 0) + ? ((double) nEncodedBytes / (double)totalBytes ) + : -1.0d; + + // TODO: Understand why the nEncodedBytes doesn't update each call of progress. + + System.out.println("Seconds : " + (int) (microsecondPosition / 1000000) + " s " + "Progress: [ " + progress * 100 + " ] %"); + final String message = String.format("Time: %.1f s, Progress: %.2f %%, encoded %d of %d bytes.", + microsecondPosition / 1000000d, + progress * 100d, + nEncodedBytes, + totalBytes); + System.out.println(message); + } + + + } + + /** + * Is called every time the status of the StreamPlayer changes. + * + * @param event the event + */ + @Override + public void statusUpdated(StreamPlayerEvent event) { + // Player status + final Status status = event.getPlayerStatus(); + + // Do different things depending on the status. + // See XR3PLAYER https://github.com/goxr3plus/XR3Player for advanced examples + + } + + private String getExtension(String audioFileName) { + return audioFileName.split("\\.(?=[^.]+$)")[1]; + } + +} diff --git a/src/main/java/com/goxr3plus/streamplayer/stream/StreamPlayer.java b/src/main/java/com/goxr3plus/streamplayer/stream/StreamPlayer.java index a2b644f..0bd6935 100644 --- a/src/main/java/com/goxr3plus/streamplayer/stream/StreamPlayer.java +++ b/src/main/java/com/goxr3plus/streamplayer/stream/StreamPlayer.java @@ -742,7 +742,7 @@ else if (previousStatus == Status.PAUSED) { * @param seconds Seconds to Skip */ //todo not finished needs more validations - public long seekSeconds(int seconds) throws Exception { + public long seekSeconds(int seconds) throws StreamPlayerException { int durationInSeconds = this.getDurationInSeconds(); //Validate @@ -774,7 +774,7 @@ public long seekSeconds(int seconds) throws Exception { * * @param seconds Seconds to Skip */ - public long seekTo(int seconds) throws Exception { + public long seekTo(int seconds) throws StreamPlayerException { int durationInSeconds = this.getDurationInSeconds(); //Validate @@ -800,11 +800,11 @@ public long seekTo(int seconds) throws Exception { // seek(bytes); // } - private void validateSeconds(int seconds, int durationInSeconds) throws Exception { + private void validateSeconds(int seconds, int durationInSeconds) { if (seconds < 0) { - throw new Exception("Trying to skip negative seconds "); + throw new UnsupportedOperationException("Trying to skip negative seconds "); } else if (seconds >= durationInSeconds) { - throw new Exception("Trying to skip with seconds {" + seconds + "} > maximum {" + durationInSeconds + "}"); + throw new UnsupportedOperationException("Trying to skip with seconds {" + seconds + "} > maximum {" + durationInSeconds + "}"); } } diff --git a/src/main/java/com/goxr3plus/streamplayer/stream/StreamPlayerEventLauncher.java b/src/main/java/com/goxr3plus/streamplayer/stream/StreamPlayerEventLauncher.java index 525c4bf..b6766c9 100644 --- a/src/main/java/com/goxr3plus/streamplayer/stream/StreamPlayerEventLauncher.java +++ b/src/main/java/com/goxr3plus/streamplayer/stream/StreamPlayerEventLauncher.java @@ -73,7 +73,7 @@ public StreamPlayerEventLauncher(Object source, Status playerStatus, int encoded } @Override - public String call() throws Exception { + public String call() { // Notify all the listeners that the state has been updated if (listeners != null) { listeners.forEach(listener -> listener diff --git a/src/test/java/com/goxr3plus/streamplayer/stream/StreamPlayerTest.java b/src/test/java/com/goxr3plus/streamplayer/stream/StreamPlayerTest.java new file mode 100644 index 0000000..815da0e --- /dev/null +++ b/src/test/java/com/goxr3plus/streamplayer/stream/StreamPlayerTest.java @@ -0,0 +1,44 @@ +package com.goxr3plus.streamplayer.stream; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class StreamPlayerTest { + + @Test + @DisplayName("Demonstration of spying") + void demonstrationOfSpyng() throws StreamPlayerException { + + final File audioFile = new File("Logic - Ballin [Bass Boosted].mp3"); + + // Setup the spy + final StreamPlayer streamPlayer = new StreamPlayer(); + final StreamPlayer spy = spy(streamPlayer); + + // Execute & verify + + // Call open, via the spy + spy.open(audioFile); + + // verify that getEncodedStreamPosition is called exactly two times + verify(spy, times(2)).getEncodedStreamPosition(); + + // Call play, via the spy + spy.play(); + + // Verify that getEncodedStreamPosition is now called 3 times (the 2 previous times + one more time) + verify(spy, times(3)).getEncodedStreamPosition(); + + spy.stop(); + // Verify that there are in total 4 calls of getEncodedStreamPosition after the player is stopped. + verify(spy, times(4)).getEncodedStreamPosition(); + + // We can only spy on public methods. + // TODO: Look into initAudioInputStream, and check if we really need to call getEncodedStreamPosition() twice. + } +} \ No newline at end of file