// Copyright 2014-2025 Jesper Larsson
//
// This file is part of Klipspringer, <https://klipspringer.avadeaux.net/>
//
// Klipspringer is free software: you can redistribute it and/or modify it under the terms of the
// GNU General Public License as published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// Klipspringer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with Klipspringer. If
// not, see <https://www.gnu.org/licenses/>.
package net.avadeaux.klipspringer.split;
import java.io.IOException;
import net.avadeaux.klipspringer.*;
import net.avadeaux.klipspringer.codec.*;
public class PlayProcess implements TrackKeyInterface.Control, PlayMonitor, Runnable {
public static class Info implements PlayMonitor.Info {
private String trackName;
private int trackIndex;
private PcmFormat format;
private String quality;
private double time;
private long timestamp = 0;
public String trackName() { return trackName; }
public int trackIndex() { return trackIndex; }
public PcmFormat format() { return format; }
public double time() { return time; }
public String quality() { return quality; }
}
private final Track track;
private final TrackSplitter.Segment[] segs;
private final TrackSplitter.Hop[] hops;
private final long endFrame; // upper bound for playing
private final double flushSilenceSecs;
private SplitPlayer play = null;
private long seekFrame;
private long changeNo = 1;
private boolean fading;
public void run() {
while (true) {
SplitPlayer p;
long f;
synchronized (this) {
if (play == null) { break; }
p = play;
f = seekFrame;
}
try {
track.decode(f, p);
p.drain();
} catch (Exception ex) {
if (System.getProperty("klipspringer.debug") != null) { ex.printStackTrace(); }
} finally {
synchronized (this) {
if (p == play) {
play = null;
changeNo++;
notifyAll();
} else {
p = null;
}
}
try {
if (p != null) { p.close(); }
} catch (Exception ex) {
System.err.println(ex);
if (System.getProperty("klipspringer.debug") != null) { ex.printStackTrace(); }
}
}
}
}
public PlayProcess(Track track,
TrackSplitter.Segment[] segs,
TrackSplitter.Hop[] hops,
long endFrame,
boolean fading,
double flushSilenceSecs)
{
this.track = track;
this.segs = segs;
this.hops = hops;
this.endFrame = endFrame;
this.fading = fading;
this.flushSilenceSecs = flushSilenceSecs;
}
public synchronized void seek(int trackIx, double time, Device.Player.Factory outFact, Object... extra) throws IOException {
Query.Onoff setFading = extra.length > 0 ? (Query.Onoff) extra[0] : Query.Onoff.DETECT;
if (setFading != Query.Onoff.DETECT) { fading = setFading == Query.Onoff.ON; }
seek(trackIx < 0 ? 0 : segs[trackIx].lo(0) + (long) (time*track.format().rate()), outFact);
}
public synchronized void seek(long fromFrame, Device.Player.Factory outFact) throws IOException {
if (play != null) { play.close(); }
seekFrame = fromFrame;
PcmFormat inFmt = track.format();
PcmFormat outFmt = outFact.selectFormat(inFmt);
Device.Player outPlay = outFact.open(outFmt);
Device.BitLayout lout = outFact.layout(outFmt);
play = SplitPlayer.create(segs, hops, inFmt, outPlay, lout, seekFrame, endFrame, fading, flushSilenceSecs);
changeNo++;
notifyAll();
}
public Info getInfoObject() { return new Info(); }
public synchronized void getStatus(PlayMonitor.Info istatus, long timeoutMillis) {
Info status = (Info) istatus;
for (long now = System.currentTimeMillis(), end = now+timeoutMillis;
status.timestamp == changeNo && now < end;
now = System.currentTimeMillis())
{ try { wait(end-now); } catch (InterruptedException e) { } }
status.timestamp = changeNo;
if (play != null) {
long frame = -1;
try {
frame = play.currentFrame();
} catch (IOException ex) {
if (System.getProperty("klipspringer.debug") != null) { ex.printStackTrace(); }
}
if (frame > -1) {
int i = TrackSplitter.segmentIndex(segs, frame);
status.trackName = "Track "+(i+1);
status.trackIndex = i;
status.format = track.format();
status.quality = track.quality();
status.time = (double) (frame-segs[i].lo(0))/status.format.rate();
return;
}
}
status.trackName = null;
status.format = null;
status.quality = null;
status.time = Double.NaN;
}
public synchronized void quit() {
try {
if (play != null) {
SplitPlayer p = play;
play = null;
p.close();
changeNo++;
}
} catch (IOException ex) {
if (System.getProperty("klipspringer.debug") != null) { ex.printStackTrace(); }
} finally {
notifyAll();
}
}
public synchronized int playingTrackIndex() {
if (play == null) { return -1; }
long frame;
try {
frame = play.currentFrame();
} catch (IOException ex) {
if (System.getProperty("klipspringer.debug") != null) { ex.printStackTrace(); }
return -1;
}
return TrackSplitter.segmentIndex(segs, frame);
}
public synchronized double playingTime() {
if (play == null) { return Double.NaN; }
long frame;
try {
frame = play.currentFrame();
} catch (IOException ex) {
if (System.getProperty("klipspringer.debug") != null) { ex.printStackTrace(); }
return Double.NaN;
}
return (double) (frame-segs[0].lo(0)) / play.format().rate();
}
public synchronized Query.Onoff fading() { return fading ? Query.Onoff.ON : Query.Onoff.OFF; }
}
Version: v4.3.2.2 (2026-05-16T17:03:34+02:00)
Raw file
Source code overview
Klipspringer home