// 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 java.io.IOException;
import java.net.MalformedURLException;
import java.nio.ByteBuffer;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import java.util.regex.*;
import net.avadeaux.klipspringer.codec.*;
public class OutputSpec {
/** Synchronization mechanism to allow waiting until the first output player is ready. */
public static class Notify {
private boolean ready = false;
/** Waits until first output is successfully initialized. */
public synchronized void verifyReady() {
while (!ready) {
try { wait(); } catch (InterruptedException e) { }
}
}
/** Notifies that first output is successfully initialized. */
synchronized void setReady() {
ready = true;
notifyAll();
}
}
public static class LocalDeviceOutput implements Device.Player.Factory {
private final Device.Player.Factory fact;
private final Notify noti;
private LocalDeviceOutput(Device.Player.Factory fact, Notify noti) {
this.fact = fact;
this.noti = noti;
}
public Device.Player open(PcmFormat fmt) throws IOException {
Device.Player writer = fact.open(fmt);
if (noti != null) { noti.setReady(); }
return writer;
}
public PcmFormat selectFormat(AudioFormat fmt) throws UnsupportedFormatException {
return fact.selectFormat(fmt);
}
public Device.BitLayout layout(PcmFormat fmt) {
return fact.layout(fmt);
}
}
private final static Pattern urlP = Pattern.compile("([a-z+.\\-]+)://(.*)");
public static Device.Player.Factory file(String fileName, long totalFrames, Notify noti) {
return new Device.Player.Factory() {
boolean used = false;
public Device.Player open(PcmFormat fmt) throws IOException {
if (used) { throw new IllegalStateException("Output file reopened"); }
used = true;
PcmWriter out = EncodeFile.fileEncoder(fmt, totalFrames, fileName);
if (noti != null) { noti.setReady(); }
return new Device.Player() {
public synchronized boolean write(ByteBuffer data) throws IOException {
return out.write(data);
}
public PcmFormat format() { return fmt; }
public synchronized void close() throws IOException { out.close(); }
};
}
public PcmFormat selectFormat(AudioFormat fmt) throws UnsupportedFormatException {
return PcmFormat.of(fmt);
}
};
}
public static Device.Player.Factory process(String output, double obufsec, int urunreco, Notify noti) throws IOException {
if ("none".equals(output)) {
if (noti != null) { noti.setReady(); } // no output, no need to wait for open
return null;
}
Matcher m;
if (output == null || !(m = urlP.matcher(output)).matches()) { // not a URL, audio device
return new LocalDeviceOutput(DeviceList.outputFactory(output, obufsec, urunreco), noti);
}
if ("file".equals(m.group(1))) {
return file(m.group(2), AudioSystem.NOT_SPECIFIED, noti);
}
throw new MalformedURLException("Unrecognized protocol: "+m.group(1));
}
}
Version: v4.3.2.2 (2026-05-16T17:03:34+02:00)
Raw file
Source code overview
Klipspringer home