From f3f674c3294987bfe69aae7b646d18727b65facc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20Stenstr=C3=B6m?= Date: Thu, 29 Aug 2019 21:31:58 +0200 Subject: [PATCH 1/7] Another main, application and listener implementation This implementation doesn't subclass StreamPlayer, and it doesn't implement the StreamPlayerListener interface. Instead, the application has an instance of the regular StreamPlayer, and there is a separate implementation of the StreamPlayerListener interface. One thing I dislike, is that the StreamPlayerListener depends on the StreamPlayer; it imports it, and has an instance of it, because that is how it gets the total number of bytes. I have also added an alternative progress logging message. I would have prefered to create the listener i main(), but it needs information that only the application has (the filename), so it has to be created in the application. Besides that, it works as usual. --- .../application/AnotherDemoApplication.java | 74 ++++++++++++++++ .../streamplayer/application/AnotherMain.java | 15 ++++ .../AnotherStreamPlayerListener.java | 88 +++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 src/main/java/com/goxr3plus/streamplayer/application/AnotherDemoApplication.java create mode 100644 src/main/java/com/goxr3plus/streamplayer/application/AnotherMain.java create mode 100644 src/main/java/com/goxr3plus/streamplayer/application/AnotherStreamPlayerListener.java 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]; + } + +} From bb2a8b582b43accedd38366c9bd317ffccff8e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20Stenstr=C3=B6m?= Date: Sun, 1 Sep 2019 18:51:18 +0200 Subject: [PATCH 2/7] More specific exception Generally, it's not recommended to throw Exception. See for example https://rules.sonarsource.com/java/RSPEC-112 In class StreamPlayer, new Exception is changed into new UnsupportedOperationException As a consequence, StreamPlayerEventLauncher.call() no longer throws a checked exception. UnsupportedOperationException is an unchecked exception. --- .../goxr3plus/streamplayer/stream/StreamPlayer.java | 10 +++++----- .../streamplayer/stream/StreamPlayerEventLauncher.java | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) 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 From eafe607fc93f72ac229404f33e6e43591d7520c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20Stenstr=C3=B6m?= Date: Sun, 1 Sep 2019 20:28:36 +0200 Subject: [PATCH 3/7] Mockito + demo Mockito is a mocking library used to create faked instances of classes. The class doesn't need to exist yet; we can create a mock from an interface or a parent class. Mockito can also create spies, which intercept existing methods and check if and how they are called. The demonstration unit test uses spies. Mockito has JUnit as a dependency. To avaoid JUnit version number conflict, we back the Junit version from 5.5.1 to 5.1.1, which is what this verison of Mockito wants. --- pom.xml | 13 ++++-- .../streamplayer/stream/StreamPlayerTest.java | 44 +++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/goxr3plus/streamplayer/stream/StreamPlayerTest.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/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 From b59f43956122ec58942fb59c10855214f8dff249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20Stenstr=C3=B6m?= Date: Sun, 1 Sep 2019 20:28:36 +0200 Subject: [PATCH 4/7] Mockito + demo Mockito is a mocking library used to create faked instances of classes. The class doesn't need to exist yet; we can create a mock from an interface or a parent class. Mockito can also create spies, which intercept existing methods and check if and how they are called. The demonstration unit test uses spies. Mockito has JUnit as a dependency. To avaoid JUnit version number conflict, we back the Junit version from 5.5.1 to 5.1.1, which is what this verison of Mockito wants. --- pom.xml | 13 ++++-- .../streamplayer/stream/StreamPlayerTest.java | 44 +++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/goxr3plus/streamplayer/stream/StreamPlayerTest.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/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 From 17e4da3bbc30436f176a64d230d62490f10e911b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20Stenstr=C3=B6m?= Date: Thu, 29 Aug 2019 21:31:58 +0200 Subject: [PATCH 5/7] Another main, application and listener implementation This implementation doesn't subclass StreamPlayer, and it doesn't implement the StreamPlayerListener interface. Instead, the application has an instance of the regular StreamPlayer, and there is a separate implementation of the StreamPlayerListener interface. One thing I dislike, is that the StreamPlayerListener depends on the StreamPlayer; it imports it, and has an instance of it, because that is how it gets the total number of bytes. I have also added an alternative progress logging message. I would have prefered to create the listener i main(), but it needs information that only the application has (the filename), so it has to be created in the application. Besides that, it works as usual. --- .../application/AnotherDemoApplication.java | 74 ++++++++++++++++ .../streamplayer/application/AnotherMain.java | 15 ++++ .../AnotherStreamPlayerListener.java | 88 +++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 src/main/java/com/goxr3plus/streamplayer/application/AnotherDemoApplication.java create mode 100644 src/main/java/com/goxr3plus/streamplayer/application/AnotherMain.java create mode 100644 src/main/java/com/goxr3plus/streamplayer/application/AnotherStreamPlayerListener.java 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]; + } + +} From a1991f24fc93b7ae878ab6a3bbcaf860e98bfcd3 Mon Sep 17 00:00:00 2001 From: GOXR3PLUS STUDIO Date: Mon, 2 Sep 2019 11:34:27 +0300 Subject: [PATCH 6/7] Update README.md --- README.md | 205 +++++++++++++++++++++++++++--------------------------- 1 file changed, 103 insertions(+), 102 deletions(-) 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 ? From a9900324d0f7a8b80ebad96b68dce87bd54c74a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20Stenstr=C3=B6m?= Date: Thu, 29 Aug 2019 21:31:58 +0200 Subject: [PATCH 7/7] Another main, application and listener implementation This implementation doesn't subclass StreamPlayer, and it doesn't implement the StreamPlayerListener interface. Instead, the application has an instance of the regular StreamPlayer, and there is a separate implementation of the StreamPlayerListener interface. One thing I dislike, is that the StreamPlayerListener depends on the StreamPlayer; it imports it, and has an instance of it, because that is how it gets the total number of bytes. I have also added an alternative progress logging message. I would have prefered to create the listener i main(), but it needs information that only the application has (the filename), so it has to be created in the application. Besides that, it works as usual. --- .../application/AnotherDemoApplication.java | 74 ++++++++++++++++ .../streamplayer/application/AnotherMain.java | 15 ++++ .../AnotherStreamPlayerListener.java | 88 +++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 src/main/java/com/goxr3plus/streamplayer/application/AnotherDemoApplication.java create mode 100644 src/main/java/com/goxr3plus/streamplayer/application/AnotherMain.java create mode 100644 src/main/java/com/goxr3plus/streamplayer/application/AnotherStreamPlayerListener.java 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]; + } + +}