// 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