AsioChannel class represents an input or output channel available form the
+ * ASIO driver. It provides information such as current state and sample type. This class also
+ * encapsulates and makes available native audio buffers if it is active. Convenience methods
+ * are also available to facilitate consuming audio.
+ */
+public class AsioChannel {
+
+ private final int index;
+ private final boolean isInput;
+ private volatile boolean isActive;
+ private final int channelGroup;
+ private final AsioSampleType sampleType;
+ private final String name;
+ private final ByteBuffer[] nativeBuffers;
+ private volatile int bufferIndex;
+
+ private static final float MAX_INT16 = (float) 0x00007FFF;
+ private static final float MAX_INT18 = (float) 0x0001FFFF;
+ private static final float MAX_INT20 = (float) 0x0007FFFF;
+ private static final float MAX_INT24 = (float) 0x007FFFFF;
+ private static final float MAX_INT32 = (float) 0x7FFFFFFF; // Integer.MAX_VALUE
+
+ private AsioChannel(int index, boolean isInput, boolean isActive, int channelGroup, AsioSampleType sampleType, String name) {
+ this.index = index;
+ this.isInput = isInput;
+ this.isActive = isActive;
+ this.channelGroup = channelGroup;
+ this.sampleType = sampleType;
+ this.name = name;
+ nativeBuffers = new ByteBuffer[2];
+ }
+
+ public int getChannelIndex() {
+ return index;
+ }
+
+ public boolean isInput() {
+ return isInput;
+ }
+
+ public boolean isActive() {
+ return isActive;
+ }
+
+ public int getChannelGroup() {
+ return channelGroup;
+ }
+
+ public AsioSampleType getSampleType() {
+ return sampleType;
+ }
+
+ public String getChannelName() {
+ return name;
+ }
+
+ /**
+ * Returns the current buffer to read or write from, with the position reset to zero. The endian-ness
+ * of the buffer and of the underlying system has been accounted for. Note that input buffers
+ * isInput() are read-only.
+ */
+ public ByteBuffer getByteBuffer() {
+ return nativeBuffers[bufferIndex];
+ }
+
+ protected void setBufferIndex(int bufferIndex) {
+ this.bufferIndex = bufferIndex;
+ // guard against drivers which fire a straggling bufferSwitch callback while
+ // or after their buffers have been disposed of (the buffers are cleared to
+ // null by setByteBuffers() in that case)
+ if (nativeBuffers[bufferIndex] != null) {
+ nativeBuffers[bufferIndex].rewind(); // reset position to start of buffer
+ }
+ }
+
+ protected void setByteBuffers(ByteBuffer buffer0, ByteBuffer buffer1) {
+ if (buffer0 == null || buffer1 == null) {
+ // the ByteBuffer references are cleared
+ isActive = false;
+ nativeBuffers[0] = null;
+ nativeBuffers[1] = null;
+ } else {
+ nativeBuffers[0] = isInput ? buffer0.asReadOnlyBuffer() : buffer0;
+ nativeBuffers[1] = isInput ? buffer1.asReadOnlyBuffer() : buffer1;
+ if (sampleType.name().contains("MSB")) {
+ nativeBuffers[0].order(ByteOrder.BIG_ENDIAN); // set the endian-ness of the buffers
+ nativeBuffers[1].order(ByteOrder.BIG_ENDIAN); // according to the sample type
+ } else {
+ nativeBuffers[0].order(ByteOrder.LITTLE_ENDIAN);
+ nativeBuffers[1].order(ByteOrder.LITTLE_ENDIAN);
+ }
+ isActive = true;
+ }
+ }
+
+ /**
+ * A convenience method to write a float array of samples to the output. The array
+ * values are expected to be bounded to the range of [-1,1]. The need to convert to the correct
+ * sample type is abstracted. The output array should be same size as the buffer.
+ * If it is larger, then a BufferOverflowException will be thrown. If it is smaller,
+ * the buffer will be incompletely filled.
+ *
+ * If the ASIO host does not use floats to represent samples, then the AsioChannel's
+ * ByteBuffer should be directly manipulated. Use getByteBuffer to access the buffer.
+ * @param output A float array to write to the output.
+ */
+ public void write(float[] output) {
+ if (isInput) {
+ throw new IllegalStateException("Only output channels can be written to.");
+ }
+ if (!isActive) {
+ throw new IllegalStateException("This channel is not active: " + toString());
+ }
+ ByteBuffer outputBuffer = getByteBuffer();
+ switch (sampleType) {
+ case ASIOSTFloat64MSB:
+ case ASIOSTFloat64LSB: {
+ for (float sampleValue : output) {
+ outputBuffer.putDouble((double) sampleValue);
+ }
+ break;
+ }
+ case ASIOSTFloat32MSB:
+ case ASIOSTFloat32LSB: {
+ for (float sampleValue : output) {
+ outputBuffer.putFloat(sampleValue);
+ }
+ break;
+ }
+ case ASIOSTInt32MSB:
+ case ASIOSTInt32LSB: {
+ for (float sampleValue : output) {
+ outputBuffer.putInt((int) (sampleValue * MAX_INT32));
+ }
+ break;
+ }
+ case ASIOSTInt32MSB16:
+ case ASIOSTInt32LSB16: {
+ for (float sampleValue : output) {
+ outputBuffer.putInt((int) (sampleValue * MAX_INT16));
+ }
+ break;
+ }
+ case ASIOSTInt32MSB18:
+ case ASIOSTInt32LSB18: {
+ for (float sampleValue : output) {
+ outputBuffer.putInt((int) (sampleValue * MAX_INT18));
+ }
+ break;
+ }
+ case ASIOSTInt32MSB20:
+ case ASIOSTInt32LSB20: {
+ for (float sampleValue : output) {
+ outputBuffer.putInt((int) (sampleValue * MAX_INT20));
+ }
+ break;
+ }
+ case ASIOSTInt32MSB24:
+ case ASIOSTInt32LSB24: {
+ for (float sampleValue : output) {
+ outputBuffer.putInt((int) (sampleValue * MAX_INT24));
+ }
+ break;
+ }
+ case ASIOSTInt16MSB:
+ case ASIOSTInt16LSB: {
+ for (float sampleValue : output) {
+ outputBuffer.putShort((short) (sampleValue * MAX_INT16));
+ }
+ break;
+ }
+ case ASIOSTInt24MSB: {
+ for (float sampleValue : output) {
+ int sampleValueInt = (int) (sampleValue * MAX_INT24);
+ outputBuffer.put((byte) ((sampleValueInt >> 16) & 0xFF));
+ outputBuffer.put((byte) ((sampleValueInt >> 8) & 0xFF));
+ outputBuffer.put((byte) (sampleValueInt & 0xFF));
+ }
+ break;
+ }
+ case ASIOSTInt24LSB: {
+ for (float sampleValue : output) {
+ int sampleValueInt = (int) (sampleValue * MAX_INT24);
+ outputBuffer.put((byte) (sampleValueInt & 0xFF));
+ outputBuffer.put((byte) ((sampleValueInt >> 8) & 0xFF));
+ outputBuffer.put((byte) ((sampleValueInt >> 16) & 0xFF));
+ }
+ break;
+ }
+ case ASIOSTDSDInt8MSB1:
+ case ASIOSTDSDInt8LSB1:
+ case ASIOSTDSDInt8NER8: {
+ throw new IllegalStateException(
+ "The sample types ASIOSTDSDInt8MSB1, ASIOSTDSDInt8LSB1, and ASIOSTDSDInt8NER8 are not supported.");
+ }
+ }
+ }
+
+ /**
+ * A convenience method to read samples from the input buffer to a float array.
+ * The argument array must have the same length as the configured buffer size. The returned samples
+ * are bounded to within [-1,1]. The input array should be the same size as the input
+ * array. If it larger, then a BufferUnderflowException will be thrown. If it is smaller,
+ * then the buffer will be incompletely read.
+ * @param input A float array to read into.
+ */
+ public void read(float[] input) {
+ if (!isInput) {
+ throw new IllegalStateException("Only input channels can be read from.");
+ }
+ if (!isActive) {
+ throw new IllegalStateException("This channel is not active: " + toString());
+ }
+ ByteBuffer inputBuffer = getByteBuffer();
+ switch (sampleType) {
+ case ASIOSTFloat64MSB:
+ case ASIOSTFloat64LSB: {
+ for (int i = 0; i < input.length; i++) {
+ input[i] = (float) inputBuffer.getDouble();
+ }
+ break;
+ }
+ case ASIOSTFloat32MSB:
+ case ASIOSTFloat32LSB: {
+ for (int i = 0; i < input.length; i++) {
+ input[i] = inputBuffer.getFloat();
+ }
+ break;
+ }
+ case ASIOSTInt32MSB:
+ case ASIOSTInt32LSB: {
+ for (int i = 0; i < input.length; i++) {
+ input[i] = ((float) inputBuffer.getInt()) / MAX_INT32;
+ }
+ break;
+ }
+ case ASIOSTInt32MSB16:
+ case ASIOSTInt32LSB16: {
+ for (int i = 0; i < input.length; i++) {
+ input[i] = ((float) inputBuffer.getInt()) / MAX_INT16;
+ }
+ break;
+ }
+ case ASIOSTInt32MSB18:
+ case ASIOSTInt32LSB18: {
+ for (int i = 0; i < input.length; i++) {
+ input[i] = ((float) inputBuffer.getInt()) / MAX_INT18;
+ }
+ break;
+ }
+ case ASIOSTInt32MSB20:
+ case ASIOSTInt32LSB20: {
+ for (int i = 0; i < input.length; i++) {
+ input[i] = ((float) inputBuffer.getInt()) / MAX_INT20;
+ }
+ break;
+ }
+ case ASIOSTInt32MSB24:
+ case ASIOSTInt32LSB24: {
+ for (int i = 0; i < input.length; i++) {
+ input[i] = ((float) inputBuffer.getInt()) / MAX_INT24;
+ }
+ break;
+ }
+ case ASIOSTInt16MSB:
+ case ASIOSTInt16LSB: {
+ for (int i = 0; i < input.length; i++) {
+ input[i] = ((float) inputBuffer.getShort()) / MAX_INT16;
+ }
+ break;
+ }
+ case ASIOSTInt24MSB: {
+ for (int i = 0; i < input.length; i++) {
+ int sampleValueInt = ((int) inputBuffer.get()) & 0xFFFF; sampleValueInt <<= 8;
+ sampleValueInt |= ((int) inputBuffer.get()) & 0xFF; sampleValueInt <<= 8;
+ sampleValueInt |= ((int) inputBuffer.get()) & 0xFF;
+ input[i] = ((float) sampleValueInt) / MAX_INT24;
+ }
+ break;
+ }
+ case ASIOSTInt24LSB: {
+ for (int i = 0; i < input.length; i++) {
+ int sampleValueInt = ((int) inputBuffer.get()) & 0xFF;
+ sampleValueInt |= (((int) inputBuffer.get()) & 0xFF) << 8;
+ sampleValueInt |= (((int) inputBuffer.get()) & 0xFFFF) << 16;
+ input[i] = ((float) sampleValueInt) / MAX_INT24;
+ }
+ break;
+ }
+ case ASIOSTDSDInt8MSB1:
+ case ASIOSTDSDInt8LSB1:
+ case ASIOSTDSDInt8NER8: {
+ throw new IllegalStateException(
+ "The sample types ASIOSTDSDInt8MSB1, ASIOSTDSDInt8LSB1, and ASIOSTDSDInt8NER8 are not supported.");
+ }
+ }
+ }
+
+ /*
+ * equals() is overridden such that it may be used in a Set
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof AsioChannel)) {
+ return false;
+ } else {
+ AsioChannel channelInfo = (AsioChannel) o;
+ return (channelInfo.getChannelIndex() == index && channelInfo.isInput() == isInput);
+ }
+ }
+
+ /*
+ * hashCode() overridden in order to accompany equals()
+ */
+ @Override
+ public int hashCode() {
+ return isInput ? index : ~index + 1; // : 2's complement
+ }
+
+ /**
+ * Returns a string description of the channel in the format,
+ * "Output Channel 0: Analog Out 1/2 Delta-AP [1], ASIOSTInt32LSB, group 0, inactive"
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(isInput ? "Input" : "Output");
+ sb.append(" Channel "); sb.append(Integer.toString(index));
+ sb.append(": "); sb.append(name);
+ sb.append(", "); sb.append(sampleType.toString());
+ sb.append(", group "); sb.append(Integer.toString(channelGroup));
+ sb.append(", "); sb.append(isActive ? "active" : "inactive");
+ return sb.toString();
+ }
+}
diff --git a/jasiohost-master/src/com/synthbot/jasiohost/AsioDriver.java b/jasiohost-master/src/com/synthbot/jasiohost/AsioDriver.java
new file mode 100644
index 0000000..309378a
--- /dev/null
+++ b/jasiohost-master/src/com/synthbot/jasiohost/AsioDriver.java
@@ -0,0 +1,668 @@
+/*
+ * Copyright 2009,2010,2012 Martin Roth (mhroth@gmail.com)
+ *
+ * This file is part of JAsioHost.
+ *
+ * JAsioHost 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * JAsioHost 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 JAsioHost. If not, see AsioDriver class represents an ASIO driver. Only one may be loaded at any
+ * time. A new driver is instantiated with getDriver(), supplied with the name of the
+ * driver to load, as derived from getDriverNames().AsioException, which is a RuntimeException
+ * (i.e., it is not required to be caught). The error message will have some information about
+ * what has gone wrong. Generally, it is not possible to recover from such errors, except for
+ * shutting down the driver.IllegalStateException if the driver is not
+ * in the appropriate state for that method to be called. For instance, if start()
+ * is called before createBuffers(), an IllegalStateException will be
+ * thrown.getDriver(), the sequence of setup calls
+ * are: createBuffers() and then start(). Once the driver has been started,
+ * bufferSwitch() will be called on any AsioDriverListeners and audio can be
+ * sent or received from the driver. In order to shut down the driver from the RUNNING
+ * state, call: stop(), disposeBuffers(), exit(), and
+ * finally unloadDriver() (if the driver should be fully unloaded from memory). Alternatively
+ * returnToState() can also be used in order to return the AsioDriver to
+ * a particular AsioDriverState.
+ */
+public class AsioDriver {
+
+ private AsioDriverState currentState;
+ private final ListINITIALIZED state. If a driver is
+ * already loaded and it is the named driver, then that driver object is returned. If the named
+ * driver is different from the one currently loaded, then the currently loaded driver is shut
+ * down and a new one is instantiated. There is only ever one loaded driver.
+ *
+ * @param driverName The name of the driver to load, as returned by getDriverNames().
+ * @return The named AsioDriver object.
+ * @throws AsioException Thrown if the driver could not be loaded into memory.
+ * @throws NullPointerException Thrown if the input is null.
+ */
+ public static AsioDriver getDriver(String driverName) {
+ if (driverName == null) {
+ throw new NullPointerException("The driver name cannot be null.");
+ }
+ if (isDriverLoaded()) {
+ if (driverName.equals(asioDriver.getName())) {
+ return asioDriver;
+ } else {
+ asioDriver.shutdownAndUnloadDriver();
+ }
+ }
+ if (getDriverNames().contains(driverName)) {
+ asioDriver = new AsioDriver(driverName);
+ return asioDriver;
+ } else {
+ throw new IllegalArgumentException(
+ "The given driver name does not exist in the system registry. " +
+ "Check AsioDriver.getDriverNames().");
+ }
+ }
+
+ private static void registerThreadIfNecessary() {
+ if (registeredThreads.isEmpty()) {
+ // the first thread to access native code does not have to be registered
+ // ...it does so implicitly by accessing the ASIO code for the first time
+ registeredThreads.add(Thread.currentThread());
+ } else if (!registeredThreads.contains(Thread.currentThread())) {
+ registeredThreads.add(Thread.currentThread());
+ registerThread();
+ }
+ }
+
+ /**
+ * Due to the way in which ASIO drivers are integrated into Windows (as COM objects),
+ * it is necessary to register each thread which accesses any ASIO methods. Such
+ * registration is done automatically for the thread which loads the AsioDriver
+ * class (usually the first thread to access any AsioDriver method). Failure to register
+ * additional threads may cause exceptions, non-function of methods, or other arbitrary behaviour.
+ * A thread may register itself more than once, but this is not necessary. Registration is lightweight.
+ */
+ private static native void registerThread();
+
+ /**
+ * Returns the currently loaded AsioDriver. If no driver is loaded, null
+ * is returned. Check to see if a driver is currently loaded with isDriverLoaded().
+ */
+ public static AsioDriver getCurrentDriver() {
+ return asioDriver;
+ }
+
+ static {
+ // load jasiohost32.dll or jasiohost64.dll depending on the the bit-depth of the JVM
+ System.loadLibrary("jasiohost" + System.getProperty("sun.arch.data.model"));
+ registeredThreads = new HashSettrue if a driver is currently loaded, false otherwise.
+ */
+ public static boolean isDriverLoaded() {
+ return asioDriver != null;
+ }
+
+ /**
+ * A list of all (maximum 32) ASIO drivers registered with the system is returned.
+ */
+ public static ListINITIALIZED state to the
+ * LOADED state.
+ */
+ public synchronized void exit() {
+ if (!AsioDriverState.INITIALIZED.equals(currentState)) {
+ throw new IllegalStateException("AsioDriver must be in AsioDriverState.INITIALIZED state " +
+ "in order to be initialised. The current state is: " + currentState.toString());
+ }
+ registerThreadIfNecessary();
+ ASIOExit();
+ currentState = AsioDriverState.LOADED;
+ }
+ private native void ASIOExit();
+
+ /**
+ * Open the native control panel, allowing the user to adjust the ASIO settings. A control panel
+ * may not be provided by all drivers on all platforms.
+ */
+ public synchronized void openControlPanel() {
+ if (currentState.ordinal() < AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be at least in the INITIALIZED state: " +
+ currentState.toString());
+ }
+ registerThreadIfNecessary();
+ ASIOControlPanel();
+ }
+ private static native void ASIOControlPanel();
+
+ /**
+ * Returns the number of available input channels. -1 is returned if there is an error.
+ * @return The number of available input channels.
+ */
+ public int getNumChannelsInput() {
+ if (currentState.ordinal() < AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be at least in the INITIALIZED state: " +
+ currentState.toString());
+ }
+ return inputChannels.length;
+ }
+
+ /**
+ * Returns the number of available output channels. -1 is return if there is an error.
+ * @return The number of available output channels.
+ */
+ public int getNumChannelsOutput() {
+ if (currentState.ordinal() < AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be at least in the INITIALIZED state: " +
+ currentState.toString());
+ }
+ return outputChannels.length;
+ }
+ private static native int ASIOGetChannels(boolean isInput);
+
+ /**
+ * Returns the current sample rate to which the host is set.
+ * @return The current sample rate in Hz.
+ */
+ public synchronized double getSampleRate() {
+ if (currentState.ordinal() < AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be at least in the INITIALIZED state: " +
+ currentState.toString());
+ }
+ registerThreadIfNecessary();
+ return ASIOGetSampleRate();
+ }
+ private static native double ASIOGetSampleRate();
+
+ /**
+ * Inquires of the hardware if a specific available sample rate is available.
+ * @param sampleRate The sample rate in Hz.
+ * @return True if the sample rate is supported. False otherwise.
+ */
+ public synchronized boolean canSampleRate(double sampleRate) {
+ if (currentState.ordinal() < AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be at least in the INITIALIZED state: " +
+ currentState.toString());
+ }
+ registerThreadIfNecessary();
+ return ASIOCanSampleRate(sampleRate);
+ }
+ private static native boolean ASIOCanSampleRate(double sampleRate);
+
+ /**
+ * Set the hardware to the requested sample Rate. If sampleRate == 0, enable external sync.
+ * The driver must be in the INITIALIZED state.
+ * @param sampleRate The sample rate in Hz.
+ */
+ public synchronized void setSampleRate(double sampleRate) {
+ if (currentState.ordinal() != AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be in the INITIALIZED state: " +
+ currentState.toString());
+ }
+ registerThreadIfNecessary();
+ ASIOSetSampleRate(sampleRate);
+ }
+ private static native void ASIOSetSampleRate(double sampleRate);
+
+ /**
+ * Returns the minimum supported buffer size.
+ */
+ public synchronized int getBufferMinSize() {
+ if (currentState.ordinal() < AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be at least in the INITIALIZED state: " +
+ currentState.toString());
+ }
+ registerThreadIfNecessary();
+ return ASIOGetBufferSize(0);
+ }
+
+ /**
+ * Returns the maximum supported buffer size.
+ */
+ public synchronized int getBufferMaxSize() {
+ if (currentState.ordinal() < AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be at least in the INITIALIZED state: " + currentState.toString());
+ }
+ registerThreadIfNecessary();
+ return ASIOGetBufferSize(1);
+ }
+
+ /**
+ * Returns the preferred buffer size. The host should attempt to use this buffer size.
+ */
+ public synchronized int getBufferPreferredSize() {
+ if (currentState.ordinal() < AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be at least in the INITIALIZED state: " + currentState.toString());
+ }
+ registerThreadIfNecessary();
+ return ASIOGetBufferSize(2);
+ }
+
+ /**
+ * Returns the granularity at which buffer sizes may differ. Usually, the buffer size will be
+ * a power of 2; in this case, granularity will be reported as -1, signaling possible
+ * buffer sizes starting from the minimum size, increased in powers of 2 up to maximum size.
+ */
+ public synchronized int getBufferGranularity() {
+ if (currentState.ordinal() < AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be at least in the INITIALIZED state: " +
+ currentState.toString());
+ }
+ registerThreadIfNecessary();
+ return ASIOGetBufferSize(3);
+ }
+
+ private static native int ASIOGetBufferSize(int index);
+
+ /**
+ * Note: As getLatencyInput() will also have to include the audio buffer size of the
+ * createBuffers() call, the application should call this function after the buffer creation.
+ * In the case that the call occurs beforehand the driver should assume preferred buffer size.
+ * @return The input latency in samples.
+ */
+ public synchronized int getLatencyInput() {
+ if (currentState.ordinal() < AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be at least in the INITIALIZED state: " +
+ currentState.toString());
+ }
+ registerThreadIfNecessary();
+ return ASIOGetLatencies(true);
+ }
+
+ /**
+ * Note: As getLatencyOutput() will also have to include the audio buffer size of the
+ * createBuffers() call, the application should call this function after the buffer creation.
+ * In the case that the call occurs beforehand the driver should assume preferred buffer size.
+ * @return The output latency in samples.
+ */
+ public synchronized int getLatencyOutput() {
+ if (currentState.ordinal() < AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be at least in the INITIALIZED state: " +
+ currentState.toString());
+ }
+ registerThreadIfNecessary();
+ return ASIOGetLatencies(false);
+ }
+ private static native int ASIOGetLatencies(boolean isInput);
+
+ /**
+ * Get information about an input channel. The returned AsioChannel object
+ * is persistent for as long as the driver is INITIALIZED, and can be retrieved
+ * in any context.
+ * @param index The input channel index to get information about.
+ * @return An AsioChannel object representing the requested input channel.
+ * @throws IndexOutOfBoundsException Thrown if the requested channel index is out of bounds. The
+ * channel does not exist.
+ */
+ public synchronized AsioChannel getChannelInput(int index) {
+ if (currentState.ordinal() < AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be at least in the INITIALIZED state: " +
+ currentState.toString());
+ }
+ if (index < 0 || index >= inputChannels.length) {
+ throw new IndexOutOfBoundsException("The input index must be in [0," +
+ Integer.toString(inputChannels.length) + "): " + Integer.toString(index));
+ }
+ return inputChannels[index];
+ }
+
+ /**
+ * Get information about an output channel. The returned AsioChannel object
+ * is persistent for as long as the driver is INITIALIZED, and can be retrieved
+ * in any context.
+ * @param index The output channel index to get information about.
+ * @return An AsioChannel object representing the requested output channel.
+ * @throws IndexOutOfBoundsException Thrown if the requested channel index is out of bounds. The
+ * channel does not exist.
+ */
+ public synchronized AsioChannel getChannelOutput(int index) {
+ if (currentState.ordinal() < AsioDriverState.INITIALIZED.ordinal()) {
+ throw new IllegalStateException("The AsioDriver must be at least in the INITIALIZED state: " +
+ currentState.toString());
+ }
+ if (index < 0 || index >= outputChannels.length) {
+ throw new IndexOutOfBoundsException("The output index must be in [0," +
+ Integer.toString(outputChannels.length) + "): " + Integer.toString(index));
+ }
+ return outputChannels[index];
+ }
+ private static native AsioChannel ASIOGetChannelInfo(int index, boolean isInput);
+
+ /**
+ * Creates audio buffers for the set of designated channels. The buffer size is that as returned by
+ * getBufferPreferredSize().
+ * @param channelsToInit A Set of AsioChannelInfo objects designating the
+ * input and output channels to initialise and create audio buffers for.
+ */
+ public synchronized void createBuffers(SetcreateBuffers()). The active
+ * channels are reset; all channels become inactive.
+ */
+ public synchronized void disposeBuffers() {
+ if (!AsioDriverState.PREPARED.equals(currentState)) {
+ throw new IllegalStateException("The ASIO driver must be in the PREPARED state " +
+ "in order to dispose of the audio buffers.");
+ }
+ for (AsioChannel channelInfo : activeChannels) {
+ channelInfo.setByteBuffers(null, null); // clear the ByteBuffer references
+ }
+ activeChannels.clear();
+ registerThreadIfNecessary();
+ ASIODisposeBuffers();
+ currentState = AsioDriverState.INITIALIZED;
+ }
+ private static native void ASIODisposeBuffers();
+
+ /**
+ * Start the driver. Input buffers are presented and output buffers consumed via calls to
+ * bufferSwitch(). Puts the AsioDriver into the RUNNING state.
+ */
+ public synchronized void start() {
+ if (!AsioDriverState.PREPARED.equals(currentState)) {
+ throw new IllegalStateException();
+ }
+ registerThreadIfNecessary();
+ ASIOStart();
+ currentState = AsioDriverState.RUNNING;
+ }
+ private static native void ASIOStart();
+
+ /**
+ * Stop the driver. Calls to bufferSwitch() will cease. The AsioDriver
+ * returns to the PREPARED state.
+ */
+ public synchronized void stop() {
+ if (!AsioDriverState.RUNNING.equals(currentState)) {
+ throw new IllegalStateException();
+ }
+ registerThreadIfNecessary();
+ ASIOStop();
+ currentState = AsioDriverState.PREPARED;
+ }
+ private static native void ASIOStop();
+
+ /**
+ * Loads the named driver into memory.
+ * @param driverName The ASIO driver to load.
+ * @return True if the driver was successfully loaded. False otherwise.
+ */
+ private static native boolean loadDriver(String driverName);
+
+ private synchronized void unloadDriver() {
+ if (!AsioDriverState.LOADED.equals(currentState)) {
+ throw new IllegalStateException();
+ }
+ registerThreadIfNecessary();
+ removeCurrentDriver();
+ currentState = AsioDriverState.UNLOADED;
+ asioDriver = null;
+ }
+ private static native void removeCurrentDriver();
+
+ /**
+ * Shutdown the ASIO driver, regardless of what state it is in. Unload it from memory. This is a
+ * convenience method for returnToState(AsioDriverState.UNLOADED).
+ */
+ public synchronized void shutdownAndUnloadDriver() {
+ returnToState(AsioDriverState.UNLOADED);
+ }
+
+ /**
+ * Return the driver to a given state. If the target state is ahead or equal to the current state
+ * then this method has no effect.
+ * @param targetState The state to which the driver should return.
+ */
+ public synchronized void returnToState(AsioDriverState targetState) {
+ if (targetState == null) {
+ throw new NullPointerException("Target state may not be null.");
+ }
+ if (targetState.ordinal() < currentState.ordinal()) {
+ switch (currentState) {
+ case RUNNING: {
+ stop();
+ if (currentState.equals(targetState)) {
+ break;
+ }
+ // allow fall-throughs
+ }
+ case PREPARED: {
+ disposeBuffers();
+ if (currentState.equals(targetState)) {
+ break;
+ }
+ }
+ case INITIALIZED: {
+ exit();
+ if (currentState.equals(targetState)) {
+ break;
+ }
+ }
+ case LOADED: {
+ unloadDriver();
+ break;
+ }
+ default: {
+ // do nothing
+ }
+ }
+ }
+ }
+
+ /**
+ * Add a new AsioDriverListener. Listeners can only be updated while the driver
+ * is in the LOADED or INITIALIZED state.
+ * @param listener A new AsioDriverListener which should be sent callbacks.
+ */
+ public synchronized void addAsioDriverListener(AsioDriverListener listener) {
+ if (currentState.ordinal() < AsioDriverState.PREPARED.ordinal()) {
+ if (!listeners.contains(listener)) {
+ listeners.add(listener);
+ }
+ } else {
+ throw new IllegalStateException("AsioDriverListeners can only be updated while the " +
+ "AsioDriver is in the LOADED or INITIALIZED state.");
+ }
+ }
+
+ /**
+ * Unregister an AsioDriverListener. Listeners can only be updated while the driver
+ * is in the LOADED or INITIALIZED state.
+ * @param listener A previously registered AsioDriverListener.
+ */
+ public synchronized void removeAsioDriverListener(AsioDriverListener listener) {
+ if (currentState.ordinal() < AsioDriverState.PREPARED.ordinal()) {
+ listeners.remove(listener);
+ } else {
+ throw new IllegalStateException("AsioDriverListeners can only be updated while the " +
+ "AsioDriver is in the LOADED or INITIALIZED state.");
+ }
+ }
+
+
+ /*
+ * Callbacks
+ */
+
+ private void fireSampleRateDidChange(double sampleRate) {
+ for (AsioDriverListener listener : listeners) {
+ listener.sampleRateDidChange(sampleRate);
+ }
+ }
+
+ private void fireResetRequest() {
+ for (AsioDriverListener listener : listeners) {
+ listener.resetRequest();
+ }
+ }
+
+ private void fireResyncRequest() {
+ for (AsioDriverListener listener : listeners) {
+ listener.resyncRequest();
+ }
+ }
+
+ private void fireBufferSizeChanged(int bufferSize) {
+ for (AsioDriverListener listener : listeners) {
+ listener.bufferSizeChanged(bufferSize);
+ }
+ }
+
+ private void fireLatenciesChanged(int inputLatency, int outputLatency) {
+ for (AsioDriverListener listener : listeners) {
+ listener.latenciesChanged(inputLatency, outputLatency);
+ }
+ }
+
+ private void fireBufferSwitch(long systemTime, long samplePosition, int bufferIndex) {
+ for (AsioChannel channel : inputChannels) {
+ if (channel.isActive()) channel.setBufferIndex(bufferIndex);
+ }
+ for (AsioChannel channel : outputChannels) {
+ if (channel.isActive()) channel.setBufferIndex(bufferIndex);
+ }
+ // NOTE(mhroth): use a standard for loop in order to avoid implicitly creating iterator objects
+ // as this function is called very often
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).bufferSwitch(systemTime, samplePosition, activeChannels);
+ }
+ }
+}
diff --git a/jasiohost-master/src/com/synthbot/jasiohost/AsioDriverInfo.java b/jasiohost-master/src/com/synthbot/jasiohost/AsioDriverInfo.java
new file mode 100644
index 0000000..09b45e2
--- /dev/null
+++ b/jasiohost-master/src/com/synthbot/jasiohost/AsioDriverInfo.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2009 Martin Roth (mhroth@gmail.com)
+ *
+ * This file is part of JAsioHost.
+ *
+ * JAsioHost 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * JAsioHost 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 JAsioHost. If not, see AsioDriver.ASIOInit().
+ */
+public class AsioDriverInfo {
+
+ private final int asioVersion;
+ private final int driverVersion;
+ private final String driverName;
+ private final String errorMessage;
+
+ protected AsioDriverInfo(int asioVersion, int driverVersion, String driverName, String errorMessage) {
+ this.asioVersion = asioVersion;
+ this.driverVersion = driverVersion;
+ this.driverName = driverName;
+ this.errorMessage = errorMessage;
+ }
+
+ public int getAsioVersion() {
+ return asioVersion;
+ }
+
+ public int getDriverVersion() {
+ return driverVersion;
+ }
+
+ public String getDriverName() {
+ return driverName;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("=== ASIO Driver Information ===\n");
+ sb.append("ASIO Version: "); sb.append(Integer.toString(asioVersion)); sb.append("\n");
+ sb.append("Driver Version: "); sb.append(Integer.toString(driverVersion)); sb.append("\n");
+ sb.append("Driver Name: "); sb.append(driverName); sb.append("\n");
+ sb.append("Error Message: "); sb.append(errorMessage);
+ return sb.toString();
+ }
+
+}
diff --git a/jasiohost-master/src/com/synthbot/jasiohost/AsioDriverListener.java b/jasiohost-master/src/com/synthbot/jasiohost/AsioDriverListener.java
new file mode 100644
index 0000000..d0ae360
--- /dev/null
+++ b/jasiohost-master/src/com/synthbot/jasiohost/AsioDriverListener.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2009 Martin Roth (mhroth@gmail.com)
+ *
+ * This file is part of JAsioHost.
+ *
+ * JAsioHost 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * JAsioHost 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 JAsioHost. If not, see
+ * public void resetRequest() {
+ * new Thread() {
+ * @Override
+ * public void run() {
+ * AsioDriver.getDriver().returnToState(AsioDriverState.INITIALIZED);
+ * }
+ * }.start();
+ * }
+ *
+ * Because all methods are synchronized, this approach will safely return the driver
+ * to the INITIALIZED state as soon as possible. The buffers must then be recreated
+ * and the driver restarted.
+ */
+ public void resetRequest();
+
+ /**
+ * The driver detected audio buffer underruns and requires a resynchronization.
+ */
+ public void resyncRequest();
+
+ /**
+ * The driver has a new preferred buffer size. The host should make an effort to
+ * accommodate the driver by returning to the INITIALIZED state and calling
+ * AsioDriver.createBuffers().
+ * @param bufferSize The new preferred buffer size.
+ */
+ public void bufferSizeChanged(int bufferSize);
+
+ /**
+ * The input or output latencies have changed. The host is updated with the new values.
+ * @param inputLatency The new input latency in milliseconds.
+ * @param outputLatency The new output latency in milliseconds.
+ */
+ public void latenciesChanged(int inputLatency, int outputLatency);
+
+ /**
+ * The next block of samples is ready. Input buffers are filled with new input,
+ * and output buffers should be filled at the end of this method.
+ * @param sampleTime System time related to sample position, in nanoseconds.
+ * @param samplePosition Sample position since start() was called.
+ * @param activeChannels The set of channels which are active and have allocated buffers. Retrieve
+ * the buffers with AsioChannel.getBuffer(), or use AsioChannel.read()
+ * and AsioDriver.write() in order to easily work with float arrays.
+ */
+ public void bufferSwitch(long sampleTime, long samplePosition, SetUNLOADED state, it can not be changed. Use AsioDriver.getDriver(String)
+ * in order to instantiate a new AsioDriver.
+ */
+ UNLOADED,
+
+ /**
+ * The driver is loaded into memory.
+ */
+ LOADED,
+
+ /**
+ * The driver it initialised and is ready to accept commands.
+ */
+ INITIALIZED,
+
+ /**
+ * The audio buffers have been created and configured.
+ */
+ PREPARED,
+
+ /**
+ * The driver is running and making calls to AsioDriver.bufferSwitch().
+ */
+ RUNNING;
+}
diff --git a/jasiohost-master/src/com/synthbot/jasiohost/AsioException.java b/jasiohost-master/src/com/synthbot/jasiohost/AsioException.java
new file mode 100644
index 0000000..b7a3b63
--- /dev/null
+++ b/jasiohost-master/src/com/synthbot/jasiohost/AsioException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2009 Martin Roth (mhroth@gmail.com)
+ *
+ * This file is part of JAsioHost.
+ *
+ * JAsioHost 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * JAsioHost 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 JAsioHost. If not, see MSB) and
+ * Little-Endian (LSB) encodings, which are meaningless in Java, as all
+ * numbers are Big-Endian encoded. AsioSampleTypes are exposed for informative
+ * purposes, however they are most useful for determining the Java native type to use.
+ * The endianness of the sample is resolved in the native library.
+ */
+public enum AsioSampleType {
+
+ /**
+ * 16-bit data word
+ */
+ ASIOSTInt16MSB(0),
+
+ /**
+ * This is the packed 24 bit format. 2 data words will spawn consecutive 6 bytes in memory.
+ * (Used for 18 and 20 bits as well, if they use this packed format). In Java it is represented
+ * the same as any of the ASIOSTInt32MSB16 formats.
+ */
+ ASIOSTInt24MSB(1),
+
+ /**
+ * 32-bit data word.
+ */
+ ASIOSTInt32MSB(2),
+
+ /**
+ * IEEE 754 32 bit float, as found on Intel x86 architecture.
+ */
+ ASIOSTFloat32MSB(3),
+
+ /**
+ * IEEE 754 64 bit double float, as found on Intel x86 architecture
+ */
+ ASIOSTFloat64MSB(4),
+
+ /**
+ * Sample data fills the least significant 16 bits, the other bits are sign extended.
+ */
+ ASIOSTInt32MSB16(8),
+
+ /**
+ * Sample data fills the least significant 18 bits, the other bits are sign extended.
+ */
+ ASIOSTInt32MSB18(9),
+
+ /**
+ * Sample data fills the least significant 20 bits, the other bits are sign extended.
+ */
+ ASIOSTInt32MSB20(10),
+
+ /**
+ * Sample data fills the least significant 24 bits, the other bits are sign extended.
+ */
+ ASIOSTInt32MSB24(11),
+ ASIOSTInt16LSB(16),
+ ASIOSTInt24LSB(17),
+ ASIOSTInt32LSB(18),
+ ASIOSTFloat32LSB(19),
+ ASIOSTFloat64LSB(20),
+ ASIOSTInt32LSB16(24),
+ ASIOSTInt32LSB18(25),
+ ASIOSTInt32LSB20(26),
+ ASIOSTInt32LSB24(27),
+ ASIOSTDSDInt8LSB1(32),
+ ASIOSTDSDInt8MSB1(33),
+ ASIOSTDSDInt8NER8(40);
+
+ private int nativeEnum; // the native enum representing this type
+
+ private AsioSampleType(int nativeEnum) {
+ this.nativeEnum = nativeEnum;
+ }
+
+ public static AsioSampleType getSampleType(int nativeEnum) {
+ for (AsioSampleType sampleType : values()) {
+ if (sampleType.nativeEnum == nativeEnum) {
+ return sampleType;
+ }
+ }
+ return null;
+ }
+}
diff --git a/jasiohost-master/src/com/synthbot/jasiohost/ExampleHost.java b/jasiohost-master/src/com/synthbot/jasiohost/ExampleHost.java
new file mode 100644
index 0000000..6812d1e
--- /dev/null
+++ b/jasiohost-master/src/com/synthbot/jasiohost/ExampleHost.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2009,2010 Martin Roth (mhroth@gmail.com)
+ *
+ * This file is part of JAsioHost.
+ *
+ * JAsioHost 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * JAsioHost 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 JAsioHost. If not, see ExampleHost demonstrates how to use an AsioDriver in order to read
+ * and write audio from a loaded ASIO driver. A small GUI is presented, allowing the user to select
+ * any of the available ASIO drivers on the system. The Start button loads the driver and
+ * plays a 440Hz tone. The Stop button stops this process and unloads the driver. The
+ * Control Panel button opens the driver's control panel for any additional configuration.
+ */
+public class ExampleHost extends JFrame implements AsioDriverListener {
+
+ private static final long serialVersionUID = 1L;
+
+ private AsioDriver asioDriver;
+ private Set+ * Each ASIO driver registered in the system registry is exposed as one audio + * device. Since the ASIO API only allows a single driver to be loaded at any + * time, driver information that requires the driver to be loaded (such as the + * number of channels) is probed by briefly loading and unloading the driver, + * and then cached. + * + *
+ * ASIO pushes audio data to the host in bufferSwitch() callbacks,
+ * while JSyn's synthesis thread blocks on {@link AudioDeviceOutputStream#write}
+ * to pace itself. This class therefore bridges the two sides with ring buffers:
+ * the synthesis thread writes interleaved samples into a ring buffer which is
+ * drained by the ASIO callback thread.
+ */
+class JAsioAudioDeviceManager implements AudioDeviceManager {
+
+ private final Listfalse.
+ *
+ * Call it at the very top of your setup(), before any other Sound
+ * library object is created or any other device is selected, for example:
+ *
+ * MultiChannel.useJASIO();
+ * Sound.outputDevice("ASIO4ALL v2");
+ *
+ * After the switch, Sound.list() lists all ASIO drivers
+ * registered in the system. Select the one you want to use with
+ * Sound.outputDevice(). Note that the ASIO API only allows one
+ * driver to be in use at a time.
+ *
+ * Returns true if ASIO support was successfully loaded.
+ *
+ * @webref I/O:MultiChannel
+ * @webBrief Force using native ASIO drivers instead of JavaSound or PortAudio.
+ * @see MultiChannel#usePortAudio()
+ * @see Sound#list()
+ */
+ public static boolean useJASIO() {
+ Engine engine;
+ try {
+ // do not select an output device yet: ASIO only allows one driver to
+ // be loaded at a time, and Sound.list() needs to probe each driver
+ // to report its channel counts
+ engine = Engine.getEngine(null, Engine.DRIVER_JASIO, false);
+ } catch (RuntimeException e) {
+ Engine.printError(e.getMessage());
+ return false;
+ }
+ try {
+ engine.useJAsio();
+ return engine.isUsingJAsio();
+ } catch (RuntimeException e) {
+ Engine.printError(e.getMessage());
+ }
+ return engine.isUsingJAsio();
+ }
+
}