Skip to content

Commit

Permalink
Added "volume" control
Browse files Browse the repository at this point in the history
  • Loading branch information
devgianlu committed Oct 5, 2018
1 parent f31669d commit d1764a9
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 4 deletions.
17 changes: 17 additions & 0 deletions src/main/java/org/librespot/spotify/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,23 @@ public void frame(@NotNull Spirc.Frame frame) {
state.setShuffle(frame.getState().getShuffle());
handleShuffle();
break;
case kMessageTypeVolume:
spirc.deviceState().setVolume(frame.getVolume());
if (playerRunner != null) playerRunner.controller().setVolume(frame.getVolume());
stateUpdated();
break;
case kMessageTypeVolumeDown:
if (playerRunner != null) {
spirc.deviceState().setVolume(playerRunner.controller().volumeDown());
stateUpdated();
}
break;
case kMessageTypeVolumeUp:
if (playerRunner != null) {
spirc.deviceState().setVolume(playerRunner.controller().volumeUp());
stateUpdated();
}
break;
}
}

Expand Down
84 changes: 81 additions & 3 deletions src/main/java/org/librespot/spotify/player/PlayerRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
* @author Gianlu
*/
public class PlayerRunner implements Runnable {
public static final int VOLUME_STEPS = 64;
private static final int VOLUME_STEP = 65536 / VOLUME_STEPS;
private static final int BUFFER_SIZE = 2048;
private static final int CONVERTED_BUFFER_SIZE = BUFFER_SIZE * 2;
private static final Logger LOGGER = Logger.getLogger(PlayerRunner.class);
Expand All @@ -33,6 +35,8 @@ public class PlayerRunner implements Runnable {
private final Packet joggPacket = new Packet();
private final Page joggPage = new Page();
private final float normalizationFactor;
private final Mixer mixer;
private final Controller controller;
private byte[] buffer;
private int count;
private int index;
Expand All @@ -47,13 +51,15 @@ public PlayerRunner(@NotNull AudioFileStreaming audioFile, @NotNull Normalizatio
this.audioIn = audioFile.stream();
this.listener = listener;
this.normalizationFactor = normalizationData.getFactor(configuration);
this.mixer = AudioSystem.getMixer(AudioSystem.getMixerInfo()[0]);

this.joggSyncState.init();
this.joggSyncState.buffer(BUFFER_SIZE);
this.buffer = joggSyncState.data;

readHeader();
initializeSound();
this.controller = new Controller(outputLine);

LOGGER.trace(String.format("Player ready for playback, fileId: %s", audioFile.getFileIdHex()));
}
Expand Down Expand Up @@ -117,13 +123,13 @@ private void initializeSound() throws PlayerException {
int rate = jorbisInfo.rate;

AudioFormat audioFormat = new AudioFormat((float) rate, 16, channels, true, false);
DataLine.Info datalineInfo = new DataLine.Info(SourceDataLine.class, audioFormat, AudioSystem.NOT_SPECIFIED);
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat, AudioSystem.NOT_SPECIFIED);

if (!AudioSystem.isLineSupported(datalineInfo))
if (!mixer.isLineSupported(dataLineInfo))
throw new PlayerException();

try {
outputLine = (SourceDataLine) AudioSystem.getLine(datalineInfo);
outputLine = (SourceDataLine) mixer.getLine(dataLineInfo);
outputLine.open(audioFormat);
} catch (LineUnavailableException | IllegalStateException | SecurityException ex) {
throw new PlayerException(ex);
Expand Down Expand Up @@ -253,12 +259,84 @@ public void stop() {
stopped = true;
}

@NotNull
public Controller controller() {
return controller;
}

public interface Listener {
void endOfTrack();

void playbackError(@NotNull Exception ex);
}

public static class Controller {
private final FloatControl masterGain;
private final DynamicRange dynamicRange;
private int volume = 0;

Controller(@NotNull Line line) {
if (line.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
masterGain = (FloatControl) line.getControl(FloatControl.Type.MASTER_GAIN);
dynamicRange = DynamicRange.get(masterGain);
} else {
masterGain = null;
dynamicRange = null;
}
}

private static double map(double x, double in_min, double in_max, double out_min, double out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

private float calcLogarithmic(int val) {
float normalized = (float) val / 65536f;
return (float) map(Math.exp(normalized * dynamicRange.b) * dynamicRange.a,
0, 1, masterGain.getMinimum(), masterGain.getMaximum());
}

public void setVolume(int val) {
this.volume = val;

if (masterGain != null)
masterGain.setValue(calcLogarithmic(val));
}

public int volumeDown() {
setVolume(volume - VOLUME_STEP);
return volume;
}

public int volumeUp() {
setVolume(volume + VOLUME_STEP);
return volume;
}

private enum DynamicRange {
DB_50(0.0031623f, 5.757f), DB_60(0.001f, 6.908f), DB_70(0.00031623f, 8.059f),
DB_80(0.0001f, 9.210f), DB_90(0.000031623f, 10.36f), DB_100(0.00001f, 11.51f);

private final float a;
private final float b;

DynamicRange(float a, float b) {
this.a = a;
this.b = b;
}

@NotNull
public static DynamicRange get(@NotNull FloatControl gain) {
int range = (int) (gain.getMaximum() - gain.getMinimum());
if (range <= 50) return DB_50;
else if (range <= 60) return DB_60;
else if (range <= 70) return DB_70;
else if (range <= 80) return DB_80;
else if (range <= 90) return DB_90;
else return DB_100;
}
}
}

private static class NotVorbisException extends PlayerException {
}

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/librespot/spotify/spirc/SpotifyIrc.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.librespot.spotify.core.Session;
import org.librespot.spotify.mercury.MercuryClient;
import org.librespot.spotify.mercury.SubListener;
import org.librespot.spotify.player.PlayerRunner;
import org.librespot.spotify.proto.Spirc;

import java.io.IOException;
Expand Down Expand Up @@ -66,7 +67,7 @@ private Spirc.DeviceState.Builder initializeDeviceState() {
.build())
.addCapabilities(Spirc.Capability.newBuilder()
.setTyp(Spirc.CapabilityType.kVolumeSteps)
.addIntValue(64)
.addIntValue(PlayerRunner.VOLUME_STEPS)
.build())
.addCapabilities(Spirc.Capability.newBuilder()
.setTyp(Spirc.CapabilityType.kSupportedContexts)
Expand Down

0 comments on commit d1764a9

Please sign in to comment.