
org.jaudiolibs.audioservers.jack.JackAudioServer Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2019 Neil C Smith.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This code 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this work; if not, see http://www.gnu.org/licenses/
*
*
* Please visit https://www.neilcsmith.net if you need additional information or
* have any questions.
*
*/
package org.jaudiolibs.audioservers.jack;
import java.nio.FloatBuffer;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jaudiolibs.audioservers.AudioClient;
import org.jaudiolibs.audioservers.AudioConfiguration;
import org.jaudiolibs.audioservers.AudioServer;
import org.jaudiolibs.audioservers.ext.ClientID;
import org.jaudiolibs.audioservers.ext.Connections;
import org.jaudiolibs.jnajack.Jack;
import org.jaudiolibs.jnajack.JackClient;
import org.jaudiolibs.jnajack.JackException;
import org.jaudiolibs.jnajack.JackOptions;
import org.jaudiolibs.jnajack.JackPort;
import org.jaudiolibs.jnajack.JackPortFlags;
import org.jaudiolibs.jnajack.JackPortType;
import org.jaudiolibs.jnajack.JackProcessCallback;
import org.jaudiolibs.jnajack.JackShutdownCallback;
import org.jaudiolibs.jnajack.JackStatus;
/**
* Implementation of AudioServer using Jack (via JNAJack)
*
* @author Neil C Smith
*/
// @TODO check thread safety of client shutdown.
public class JackAudioServer implements AudioServer {
private final static Logger LOG = Logger.getLogger(JackAudioServer.class.getName());
private enum State {
New, Initialising, Active, Closing, Terminated
};
private ClientID clientID;
private AudioConfiguration context;
private AudioClient client;
private Jack jack;
private JackClient jackclient;
private AtomicReference state;
private JackPort[] inputPorts;
private List inputBuffers;
private JackPort[] outputPorts;
private List outputBuffers;
private Callback callback;
private Connections connections;
private JackAudioServer(String id, AudioConfiguration ctxt,
boolean autoconnect, AudioClient client) {
this(new ClientID(id),
autoconnect ? Connections.ALL : Connections.NONE,
ctxt,
client);
}
JackAudioServer(
ClientID id,
Connections connections,
AudioConfiguration ctxt,
AudioClient client) {
this.clientID = id;
this.connections = connections;
this.context = ctxt;
this.client = client;
state = new AtomicReference(State.New);
}
public void run() throws Exception {
if (!state.compareAndSet(State.New, State.Initialising)) {
throw new IllegalStateException();
}
try {
initialise();
} catch (Exception ex) {
state.set(State.Terminated);
closeAll();
client.shutdown();
throw ex;
}
if (state.compareAndSet(State.Initialising, State.Active)) {
runImpl();
}
closeAll();
client.shutdown();
state.set(State.Terminated);
}
private void initialise() throws Exception {
jack = Jack.getInstance();
EnumSet options =
(connections.isConnectInputs() || connections.isConnectOutputs()) ?
EnumSet.noneOf(JackOptions.class) :
EnumSet.of(JackOptions.JackNoStartServer);
EnumSet status = EnumSet.noneOf(JackStatus.class);
try {
jackclient = jack.openClient(clientID.getIdentifier(), options, status);
} catch (JackException ex) {
LOG.log(Level.FINE, "Exception creating JACK client\nStatus set\n{0}", status);
throw ex;
}
LOG.log(Level.FINE, "JACK client created\nStatus set\n{0}", status);
int count = context.getInputChannelCount();
inputPorts = new JackPort[count];
inputBuffers = Arrays.asList(new FloatBuffer[count]);
for (int i = 0; i < count; i++) {
inputPorts[i] = jackclient.registerPort("Input_" + (i + 1),
JackPortType.AUDIO, JackPortFlags.JackPortIsInput);
}
count = context.getOutputChannelCount();
outputPorts = new JackPort[count];
outputBuffers = Arrays.asList(new FloatBuffer[count]);
for (int i = 0; i < count; i++) {
outputPorts[i] = jackclient.registerPort("Output_" + (i + 1),
JackPortType.AUDIO, JackPortFlags.JackPortIsOutput);
}
}
private void runImpl() {
try {
// make sure context is correct.
ClientID id = clientID;
String actualID = jackclient.getName();
if (!id.getIdentifier().equals(actualID)) {
id = new ClientID(actualID);
}
context = new AudioConfiguration(jackclient.getSampleRate(),
inputPorts.length,
outputPorts.length,
jackclient.getBufferSize(),
id,
connections,
jackclient);
LOG.log(Level.FINE, "Configuring AudioClient\n{0}", context);
client.configure(context);
jackclient.setProcessCallback(new Callback());
jackclient.onShutdown(new ShutDownHook());
jackclient.activate();
if (connections.isConnectInputs()) {
connectInputs();
}
if (connections.isConnectOutputs()) {
connectOutputs();
}
while (state.get() == State.Active) {
Thread.sleep(100); // @TODO switch to wait()
}
} catch (Exception ex) {
LOG.log(Level.FINE, "", ex);
shutdown();
}
}
private void connectInputs() {
try {
String[] ins = jack.getPorts(jackclient, null, JackPortType.AUDIO,
EnumSet.of(JackPortFlags.JackPortIsOutput, JackPortFlags.JackPortIsPhysical));
int inCount = Math.min(ins.length, inputPorts.length);
for (int i=0; i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy