Skip to content

Commit

Permalink
Refactored Decoder into librespot-decoder-api
Browse files Browse the repository at this point in the history
  • Loading branch information
devgianlu committed Apr 29, 2021
1 parent 2f1cda4 commit 77a5584
Show file tree
Hide file tree
Showing 20 changed files with 182 additions and 81 deletions.
47 changes: 47 additions & 0 deletions decoder-api/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!--
~ Copyright 2021 devgianlu
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>xyz.gianlu.librespot</groupId>
<artifactId>librespot-java</artifactId>
<version>1.5.6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>librespot-decoder-api</artifactId>
<packaging>jar</packaging>

<name>librespot-java decoder API</name>

<dependencies>
<dependency>
<groupId>xyz.gianlu.librespot</groupId>
<artifactId>librespot-sink-api</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,27 @@
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.gianlu.librespot.audio.AbsChunkedInputStream;
import xyz.gianlu.librespot.audio.GeneralAudioStream;
import xyz.gianlu.librespot.player.mixing.output.OutputAudioFormat;

import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;

/**
* @author Gianlu
* @author devgianlu
*/
public abstract class Decoder implements Closeable {
public static final int BUFFER_SIZE = 2048;
private static final Logger LOGGER = LoggerFactory.getLogger(Decoder.class);
protected final AbsChunkedInputStream audioIn;
protected final SeekableInputStream audioIn;
protected final float normalizationFactor;
protected final int duration;
private final GeneralAudioStream audioFile;
protected volatile boolean closed = false;
protected int seekZero = 0;
private OutputAudioFormat format;

public Decoder(@NotNull GeneralAudioStream audioFile, float normalizationFactor, int duration) {
this.audioIn = audioFile.stream();
this.audioFile = audioFile;
public Decoder(@NotNull SeekableInputStream audioIn, float normalizationFactor, int duration) {
this.audioIn = audioIn;
this.duration = duration;
this.normalizationFactor = normalizationFactor;
}
Expand Down Expand Up @@ -106,14 +102,6 @@ public final int size() {
return audioIn.size();
}

public final int decodedLength() {
return audioIn.decodedLength();
}

public final int decryptTimeMs() {
return audioFile.decryptTimeMs();
}

public static class CannotGetTimeException extends Exception {
public CannotGetTimeException(String message) {
super(message);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2021 devgianlu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package xyz.gianlu.librespot.player.decoders;

import java.io.IOException;
import java.io.InputStream;

/**
* @author devgianlu
*/
public abstract class SeekableInputStream extends InputStream {
public abstract int size();

public abstract int position();

public abstract void seek(int seekZero) throws IOException;

public abstract long skip(long skip) throws IOException;

public abstract int read(byte[] buffer, int index, int length) throws IOException;

public abstract void close();

public abstract int decodedLength();
}
18 changes: 18 additions & 0 deletions lib/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,24 @@
</build>

<dependencies>
<dependency>
<groupId>xyz.gianlu.librespot</groupId>
<artifactId>librespot-decoder-api</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Audio -->
<dependency>
<groupId>org.jcraft</groupId>
<artifactId>jorbis</artifactId>
<version>0.0.17</version>
</dependency>
<dependency>
<groupId>com.badlogicgames.jlayer</groupId>
<artifactId>jlayer</artifactId>
<version>1.0.2-gdx</version>
</dependency>

<!-- Data -->
<dependency>
<groupId>com.google.protobuf</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@
package xyz.gianlu.librespot.audio;

import org.jetbrains.annotations.NotNull;
import xyz.gianlu.librespot.player.decoders.SeekableInputStream;

import java.io.IOException;
import java.io.InputStream;

import static xyz.gianlu.librespot.audio.storage.ChannelManager.CHUNK_SIZE;

/**
* @author Gianlu
* @author devgianlu
*/
public abstract class AbsChunkedInputStream extends InputStream implements HaltListener {
public abstract class AbsChunkedInputStream extends SeekableInputStream implements HaltListener {
private static final int PRELOAD_AHEAD = 3;
private static final int PRELOAD_CHUNK_RETRIES = 2;
private static final int MAX_CHUNK_TRIES = 128;
Expand Down Expand Up @@ -82,10 +82,12 @@ public final synchronized void reset() {
pos = mark;
}

public final synchronized int pos() {
@Override
public final synchronized int position() {
return pos;
}

@Override
public final synchronized void seek(int where) throws IOException {
if (where < 0) throw new IllegalArgumentException();
if (closed) throw new IOException("Stream is closed!");
Expand Down Expand Up @@ -260,6 +262,7 @@ public final void notifyChunkError(int index, @NotNull ChunkException ex) {
}
}

@Override
public int decodedLength() {
return decodedLength;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@


/**
* @author Gianlu
* @author devgianlu
*/
public interface GeneralAudioStream {
public interface DecodedAudioStream {
@NotNull
AbsChunkedInputStream stream();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ private LoadedStream loadEpisode(@NotNull EpisodeId id, @NotNull AudioQualityPic
}
}

private static class FileAudioStream implements GeneralAudioStream {
private static class FileAudioStream implements DecodedAudioStream {
private static final Logger LOGGER = LoggerFactory.getLogger(FileAudioStream.class);
private final File file;
private final RandomAccessFile raf;
Expand Down Expand Up @@ -276,25 +276,25 @@ public int decryptTimeMs() {

public static class LoadedStream {
public final MetadataWrapper metadata;
public final GeneralAudioStream in;
public final DecodedAudioStream in;
public final NormalizationData normalizationData;
public final Metrics metrics;

public LoadedStream(@NotNull Metadata.Track track, @NotNull GeneralAudioStream in, @Nullable NormalizationData normalizationData, @NotNull Metrics metrics) {
public LoadedStream(@NotNull Metadata.Track track, @NotNull DecodedAudioStream in, @Nullable NormalizationData normalizationData, @NotNull Metrics metrics) {
this.metadata = new MetadataWrapper(track, null, null);
this.in = in;
this.normalizationData = normalizationData;
this.metrics = metrics;
}

public LoadedStream(@NotNull Metadata.Episode episode, @NotNull GeneralAudioStream in, @Nullable NormalizationData normalizationData, @NotNull Metrics metrics) {
public LoadedStream(@NotNull Metadata.Episode episode, @NotNull DecodedAudioStream in, @Nullable NormalizationData normalizationData, @NotNull Metrics metrics) {
this.metadata = new MetadataWrapper(null, episode, null);
this.in = in;
this.normalizationData = normalizationData;
this.metrics = metrics;
}

private LoadedStream(@NotNull LocalId id, @NotNull GeneralAudioStream in) {
private LoadedStream(@NotNull LocalId id, @NotNull DecodedAudioStream in) {
this.metadata = new MetadataWrapper(null, null, id);
this.in = in;
this.normalizationData = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ void setUrl(@NotNull HttpUrl url) {
}
}

public class Streamer implements GeneralAudioStream, GeneralWritableStream {
public class Streamer implements DecodedAudioStream, GeneralWritableStream {
private final StreamId streamId;
private final ExecutorService executorService = Executors.newCachedThreadPool(new NameThreadFactory((r) -> "cdn-async-" + r.hashCode()));
private final SuperAudioFormat format;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package xyz.gianlu.librespot.player.decoders;
package xyz.gianlu.librespot.audio.decoders;

import com.spotify.metadata.Metadata.AudioFile;
import org.jetbrains.annotations.NotNull;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
* limitations under the License.
*/

package xyz.gianlu.librespot.player.decoders;
package xyz.gianlu.librespot.audio.decoders;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.gianlu.librespot.audio.GeneralAudioStream;
import xyz.gianlu.librespot.audio.format.SuperAudioFormat;
import xyz.gianlu.librespot.player.decoders.Decoder;
import xyz.gianlu.librespot.player.decoders.SeekableInputStream;

import java.util.*;

Expand All @@ -41,7 +42,7 @@ private Decoders() {
}

@Nullable
public static Decoder initDecoder(@NotNull SuperAudioFormat format, @NotNull GeneralAudioStream audioFile, float normalizationFactor, int duration) {
public static Decoder initDecoder(@NotNull SuperAudioFormat format, @NotNull SeekableInputStream audioIn, float normalizationFactor, int duration) {
Set<Class<? extends Decoder>> set = decoders.get(format);
if (set == null) return null;

Expand All @@ -50,7 +51,7 @@ public static Decoder initDecoder(@NotNull SuperAudioFormat format, @NotNull Gen

try {
Class<? extends Decoder> clazz = opt.get();
return clazz.getConstructor(GeneralAudioStream.class, float.class, int.class).newInstance(audioFile, normalizationFactor, duration);
return clazz.getConstructor(SeekableInputStream.class, float.class, int.class).newInstance(audioIn, normalizationFactor, duration);
} catch (ReflectiveOperationException ex) {
LOGGER.error("Failed initializing Codec instance for {}", format, ex);
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
* limitations under the License.
*/

package xyz.gianlu.librespot.player.decoders;
package xyz.gianlu.librespot.audio.decoders;

import javazoom.jl.decoder.*;
import org.jetbrains.annotations.NotNull;
import xyz.gianlu.librespot.audio.GeneralAudioStream;
import xyz.gianlu.librespot.player.decoders.Decoder;
import xyz.gianlu.librespot.player.decoders.SeekableInputStream;
import xyz.gianlu.librespot.player.mixing.output.OutputAudioFormat;

import java.io.IOException;
Expand All @@ -31,17 +32,16 @@
* @author Gianlu
*/
public final class Mp3Decoder extends Decoder {
private final byte[] buffer = new byte[2 * BUFFER_SIZE];
private final byte[] buffer = new byte[2 * Decoder.BUFFER_SIZE];
private final Mp3InputStream in;

public Mp3Decoder(@NotNull GeneralAudioStream audioFile, float normalizationFactor, int duration) throws IOException, BitstreamException {
super(audioFile, normalizationFactor, duration);
public Mp3Decoder(@NotNull SeekableInputStream audioIn, float normalizationFactor, int duration) throws IOException, BitstreamException {
super(audioIn, normalizationFactor, duration);

skipMp3Tags(audioIn);
this.in = new Mp3InputStream(audioIn, normalizationFactor);

audioIn.mark(-1);

setAudioFormat(new OutputAudioFormat(in.getSampleRate(), 16, in.getChannels(), true, false));
}

Expand Down
Loading

0 comments on commit 77a5584

Please sign in to comment.