// 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;
import net.avadeaux.klipspringer.codec.PcmFormat;
/** Runnable that continuously updates terminal display with current status. Terminates and updates
* the display with a shutdown hook, and should therefore not be run in a daemon thread.
*/
public class DeviceTerminalDisplay implements Runnable {
private final PcmFormat format;
private DeviceFeeder.Play playState = null, recState = null;
private boolean terminate = false;
private volatile String backup = "";
public DeviceTerminalDisplay(PcmFormat format) {
this.format = format;
}
public synchronized void startPlay(DeviceFeeder.Play playState) {
this.playState = playState;
notifyAll();
}
public synchronized void stopPlay() {
this.playState = null;
notifyAll();
}
public synchronized void startRecord(DeviceFeeder.Play recState) {
this.recState = recState;
notifyAll();
}
public synchronized void stopRecord() {
this.recState = null;
notifyAll();
}
/** Gets display into a state where output (for instance an error message) can be printed
* without being erased. The behavior is not thread safe, but it should work in the vast
* majority of cases.
*/
public void nobackup() {
System.out.print(backup);
backup = "";
}
public void run() {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
synchronized (DeviceTerminalDisplay.this) {
terminate = true;
DeviceTerminalDisplay.this.notifyAll();
}
}
});
System.out.print("\n");
backup = "\u001b[A\u001b[K";
while (true) {
boolean rec = false;
double ti = Double.NaN;
boolean quit = false;
synchronized (this) {
try { wait(1000); } catch (InterruptedException e) { }
if (terminate) {
quit = true;
} else if (recState != null) {
rec = true;
try { ti = recState.time(); } catch (Exception ex) { }
} else {
try { ti = playState == null ? Double.NaN : playState.time(); } catch (Exception ex) { }
}
}
System.out.print(backup);
if (quit) { break; }
if (rec) { System.out.print("\u001b[31m\u001b[5mREC\u001b[25m\u001b[0m "); }
if (!Double.isNaN(ti)) {
long s = Math.round(ti);
if (s >= 3600) {
System.out.print(String.format("%d:%02d:%02d", s/3600, (s%3600)/60, s%60));
} else {
System.out.print(String.format("%d:%02d", s/60, s%60));
}
System.out.print(" ");
}
int ch = format.channels();
System.out.print(format.significantBips()+"-bit "+format.rate()+" Hz "+ (ch == 2 ? "stereo" : (ch == 1 ? "mono" : ch+"-channel")));
System.out.print("\n");
}
}
}
Version: v4.3.2.2 (2026-05-16T17:03:34+02:00)
Raw file
Source code overview
Klipspringer home