Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-assignment of audioInputStream removed #57

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
### [![AlexKent](https://user-images.githubusercontent.com/20374208/75432997-f5422100-5957-11ea-87a2-164eb98d83ef.png)](https://www.minepi.com/AlexKent) Support me joining PI Network app with invitation code [AlexKent](https://www.minepi.com/AlexKent) [![AlexKent](https://user-images.githubusercontent.com/20374208/75432997-f5422100-5957-11ea-87a2-164eb98d83ef.png)](https://www.minepi.com/AlexKent)

---

<h3 align="center" > Java Stream Player ( Library )</h3>
@@ -27,7 +29,7 @@
- FLAC
- MONKEY's AUDIO
- SPEEX
- **Not Supported Yet ❌**
- **Not Supported Yet ❌**
- AAC
- THEORA
- ... all the others
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

<groupId>com.github.goxr3plus</groupId>
<artifactId>java-stream-player</artifactId>
<version>9.0.5</version>
<version>10.0.0</version>


<properties>
108 changes: 54 additions & 54 deletions src/main/java/com/goxr3plus/streamplayer/stream/StreamPlayer.java
Original file line number Diff line number Diff line change
@@ -35,11 +35,7 @@
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -56,34 +52,32 @@
*/
public class StreamPlayer implements StreamPlayerInterface, Callable<Void> {

/**
* Class logger
*/
/** Class logger */
private Logger logger;

// -------------------AUDIO----------------,-----

private volatile Status status = Status.NOT_SPECIFIED;

/**
* The data source
*/
/** The data source */
private DataSource source;

/** The audio input stream. */
private volatile AudioInputStream audioInputStream;

/** The encoded audio input stream. */
/** The encoded audio input stream. mp3, ogg or similar */
private AudioInputStream encodedAudioInputStream;

/** Copy of the encoded audio input stream. */
private AudioInputStream encodedAudioInputStreamCopy;

/** The decoded audio input stream. Linear format */
private AudioInputStream decodedAudioInputStream;


/** The audio file format. */
private AudioFileFormat audioFileFormat;

// -------------------LOCKS---------------------

/**
* It is used for synchronization in place of audioInputStream
*/
/** Used for synchronization in place of audioInputStream */
private final Object audioLock = new Object();

// -------------------VARIABLES---------------------
@@ -187,9 +181,8 @@ public void reset() {
outlet.flushAndFreeDataLine();

// AudioFile
audioInputStream = null;
audioFileFormat = null;
encodedAudioInputStream = null;
encodedAudioInputStreamCopy = null;
encodedAudioLength = -1;

// Controls
@@ -328,7 +321,7 @@ private void initAudioInputStream() throws StreamPlayerException {
generateEvent(Status.OPENING, getEncodedStreamPosition(), source);

// Audio resources from file||URL||inputStream.
audioInputStream = source.getAudioInputStream();
encodedAudioInputStream = source.getAudioInputStream();

// Audio resources from file||URL||inputStream.
audioFileFormat = source.getAudioFileFormat();
@@ -425,13 +418,13 @@ private void initLine() throws LineUnavailableException, StreamPlayerException {
createLine();
if (!outlet.getSourceDataLine().isOpen()) {
currentLineBufferSize = lineBufferSize >= 0 ? lineBufferSize : outlet.getSourceDataLine().getBufferSize();
openLine(audioInputStream.getFormat(), currentLineBufferSize);
openLine(decodedAudioInputStream.getFormat(), currentLineBufferSize);
} else {
AudioFormat format = audioInputStream == null ? null : audioInputStream.getFormat();
AudioFormat format = decodedAudioInputStream == null ? null : decodedAudioInputStream.getFormat();
if (!outlet.getSourceDataLine().getFormat().equals(format)) { // TODO: Check if bug, does equals work as intended?
outlet.getSourceDataLine().close();
currentLineBufferSize = lineBufferSize >= 0 ? lineBufferSize : outlet.getSourceDataLine().getBufferSize();
openLine(audioInputStream.getFormat(), currentLineBufferSize);
openLine(decodedAudioInputStream.getFormat(), currentLineBufferSize);
}
}
}
@@ -474,7 +467,7 @@ private void createLine() throws LineUnavailableException, StreamPlayerException
if (outlet.getSourceDataLine() != null)
logger.warning("Warning Source DataLine is not null!\n");
else {
final AudioFormat sourceFormat = audioInputStream.getFormat();
final AudioFormat sourceFormat = encodedAudioInputStream.getFormat();

logger.info(() -> "Create Line : Source format : " + sourceFormat + "\n");

@@ -495,17 +488,18 @@ private void createLine() throws LineUnavailableException, StreamPlayerException
+ "Target format: " + targetFormat + "\n");

// Keep a reference on encoded stream to progress notification.
encodedAudioInputStream = audioInputStream;
encodedAudioInputStreamCopy = encodedAudioInputStream;
try {
// Get total length in bytes of the encoded stream.
encodedAudioLength = encodedAudioInputStream.available();
encodedAudioLength = encodedAudioInputStreamCopy.available();
} catch (final IOException e) {
logger.warning("Cannot get m_encodedaudioInputStream.available()\n" + e);
}

// Create decoded Stream
audioInputStream = AudioSystem.getAudioInputStream(targetFormat, audioInputStream);
final DataLine.Info lineInfo = new DataLine.Info(SourceDataLine.class, audioInputStream.getFormat(),
// audioInputStream = AudioSystem.getAudioInputStream(targetFormat, audioInputStream); // TODO: Remove re-assignment
decodedAudioInputStream = AudioSystem.getAudioInputStream(targetFormat, encodedAudioInputStream);
final DataLine.Info lineInfo = new DataLine.Info(SourceDataLine.class, decodedAudioInputStream.getFormat(),
AudioSystem.NOT_SPECIFIED);
if (!AudioSystem.isLineSupported(lineInfo))
throw new StreamPlayerException(PlayerException.LINE_NOT_SUPPORTED);
@@ -708,12 +702,12 @@ public long seekBytes(final long bytes) throws StreamPlayerException {
synchronized (audioLock) {
generateEvent(Status.SEEKING, AudioSystem.NOT_SPECIFIED, null);
initAudioInputStream();
if (audioInputStream != null) {
if (decodedAudioInputStream != null) {

long skipped;
// Loop until bytes are really skipped.
while (totalSkipped < bytes) { // totalSkipped < (bytes-SKIP_INACCURACY_SIZE)))
skipped = audioInputStream.skip(bytes - totalSkipped);
skipped = decodedAudioInputStream.skip(bytes - totalSkipped);
if (skipped == 0)
break;
totalSkipped += skipped;
@@ -843,7 +837,7 @@ public Void call() {

// Reads up a specified maximum number of bytes from audio stream
// wtf i have written here omg //to fix! cause it is complicated
for (; toRead > 0 && (nBytesRead = audioInputStream.read(audioDataBuffer.array(), totalRead,
for (; toRead > 0 && (nBytesRead = decodedAudioInputStream.read(audioDataBuffer.array(), totalRead,
toRead)) != -1; toRead -= nBytesRead, totalRead += nBytesRead)

// Check for under run
@@ -870,11 +864,11 @@ public Void call() {

// Notify all registered Listeners
listeners.forEach(listener -> {
if (audioInputStream instanceof PropertiesContainer) {
if (decodedAudioInputStream instanceof PropertiesContainer) {
// Pass audio parameters such as instant
// bit rate, ...
listener.progress(nEncodedBytes, outlet.getSourceDataLine().getMicrosecondPosition(),
trimBuffer, ((PropertiesContainer) audioInputStream).properties());
trimBuffer, ((PropertiesContainer) decodedAudioInputStream).properties());
} else
// Pass audio parameters
listener.progress(nEncodedBytes, outlet.getSourceDataLine().getMicrosecondPosition(),
@@ -937,9 +931,9 @@ private void goOutOfPause() {
@Override
public int getEncodedStreamPosition() {
int position = -1;
if (source.isFile() && encodedAudioInputStream != null)
if (source.isFile() && encodedAudioInputStreamCopy != null)
try {
position = encodedAudioLength - encodedAudioInputStream.available();
position = encodedAudioLength - encodedAudioInputStreamCopy.available();
} catch (final IOException ex) {
logger.log(Level.WARNING, "Cannot get m_encodedaudioInputStream.available()", ex);
stop();
@@ -952,8 +946,9 @@ public int getEncodedStreamPosition() {
*/
private void closeStream() {
try {
if (audioInputStream != null) {
audioInputStream.close();
AudioInputStream stream1 = this.decodedAudioInputStream;
if (decodedAudioInputStream != null) { // TODO: Find out which audioInputStream or audioInputStream1 should be closed.
decodedAudioInputStream.close();
logger.info("Stream closed");
}
} catch (final IOException ex) {
@@ -994,17 +989,16 @@ public List<String> getMixers() {
// audio mixers that are currently installed on the system.
final Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();

if (mixerInfos != null)
Arrays.stream(mixerInfos).forEach(mInfo -> {
// line info
final Line.Info lineInfo = new Line.Info(SourceDataLine.class);
final Mixer mixer = AudioSystem.getMixer(mInfo);
Arrays.stream(mixerInfos).forEach(mInfo -> {
// line info
final Line.Info lineInfo = new Line.Info(SourceDataLine.class);
final Mixer mixer = AudioSystem.getMixer(mInfo);

// if line supported
if (mixer.isLineSupported(lineInfo))
mixers.add(mInfo.getName());
// if line supported
if (mixer.isLineSupported(lineInfo))
mixers.add(mInfo.getName());

});
});

return mixers;
}
@@ -1023,7 +1017,7 @@ private Mixer getMixer(final String name) {
// audio mixers that are currently installed on the system.
final Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();

if (name != null && mixerInfos != null)
if (name != null)
for (Mixer.Info mixerInfo : mixerInfos)
if (mixerInfo.getName().equals(name)) {
mixer = AudioSystem.getMixer(mixerInfo);
@@ -1067,12 +1061,18 @@ public float getMinimumGain() {

/**
* Returns Pan precision.
* <p>
* Obtains the resolution or granularity of the control, in the units that the control measures.
* The precision is the size of the increment between discrete valid values for this control,
* over the set of supported floating-point values.
*
* @return The Precision Value
* @return The Precision Value for the pan control, if it exists, otherwise 0.0.
*/
@Override
public float getPrecision() {
return !outlet.hasControl(FloatControl.Type.PAN, outlet.getPanControl()) ? 0.0F : outlet.getPanControl().getPrecision();
return !outlet.hasControl(FloatControl.Type.PAN, outlet.getPanControl())
? 0
: outlet.getPanControl().getPrecision();

}

@@ -1248,10 +1248,10 @@ public void setBalance(final float fBalance) {
*/
@Override
public void setEqualizer(final float[] array, final int stop) {
if (!isPausedOrPlaying() || !(audioInputStream instanceof PropertiesContainer))
if (!isPausedOrPlaying() || !(decodedAudioInputStream instanceof PropertiesContainer))
return;
// Map<?, ?> map = ((PropertiesContainer) audioInputStream).properties()
final float[] equalizer = (float[]) ((PropertiesContainer) audioInputStream).properties().get("mp3.equalizer");
final float[] equalizer = (float[]) ((PropertiesContainer) decodedAudioInputStream).properties().get("mp3.equalizer");
if (stop >= 0) System.arraycopy(array, 0, equalizer, 0, stop);

}
@@ -1264,10 +1264,10 @@ public void setEqualizer(final float[] array, final int stop) {
*/
@Override
public void setEqualizerKey(final float value, final int key) {
if (!isPausedOrPlaying() || !(audioInputStream instanceof PropertiesContainer))
if (!isPausedOrPlaying() || !(decodedAudioInputStream instanceof PropertiesContainer))
return;
// Map<?, ?> map = ((PropertiesContainer) audioInputStream).properties()
final float[] equalizer = (float[]) ((PropertiesContainer) audioInputStream).properties().get("mp3.equalizer");
final float[] equalizer = (float[]) ((PropertiesContainer) decodedAudioInputStream).properties().get("mp3.equalizer");
equalizer[key] = value;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.goxr3plus.streamplayer.stream;

import com.goxr3plus.streamplayer.enums.Status;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.logging.Logger;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;

/**
* Tests of all or most of the public methods of StreamPlayer.
* These unit tests are written primarily as documentation of the behavior and as example use case,
* not as a part of test driven development.
*/
public class StreamPlayerFutureImprovementTest {
StreamPlayer player;
private File audioFile;

@BeforeEach
void setup() {
final Logger logger = mock(Logger.class);
player = new StreamPlayer(logger);
audioFile = new File("Logic - Ballin [Bass Boosted].mp3");
}

/**
* This test fails if it's permitted to add a null to the StreamPlayer listener list.
*/
@Test
void addStreamPlayerListener_dontAcceptNull() {
// Currently, we can add a null to the list of stream player listeners.
// Should that really be allowed?
assertThrows(Exception.class, () -> player.addStreamPlayerListener(null));

fail("Test not done");
}


@Test
@DisplayName("When play() is called without first calling open(), an exception is thrown")
void playingUnopenedSourceThrowsException() {

assertThrows(Exception.class, () -> player.play());
}

@Test
void seekBytes() throws StreamPlayerException {
player.open(audioFile);
player.play();
int positionByte1 = player.getPositionByte();

player.seekBytes(100);
int positionByte2 = player.getPositionByte();

assertTrue( positionByte2 > positionByte1);

// TODO: It seems that getPositionByte doesn't work.
// It isn't called from within this project, except for in this test.
// It is however called by XR3Player. If XR3Player needs this method, it must be tested
// within this project. The method relies on a map, which doesn't seem to be updated by play()
}

}
Loading