Skip to content

Add proper ASIO support for sound library. - #113

Open
trackme518 wants to merge 6 commits into
processing:mainfrom
trackme518:jasio-support
Open

trackme518 wants to merge 6 commits into
processing:mainfrom
trackme518:jasio-support

Conversation

@trackme518

@trackme518 trackme518 commented Oct 26, 2026 •

Copy link
Copy Markdown

Summary

This adds a native ASIO output backend to the Sound library on Windows, built on
JAsioHost, and fixes the driver enumeration
problems that make ASIO unreliable through PortAudio.

The existing PortAudio ASIO path was previously documented in #87. My test reports
there show it failing or stalling with real multichannel interfaces:

  • Voicemeeter Virtual ASIO: WaitForSingleObject() timed out in WriteStream() /
    RuntimeException: Wait timed out
  • TASCAM US-16x08 ASIO: the same write timeouts
  • WDM-KS devices: Blocking API not supported yet
  • MME: only stereo output despite an 8-channel interface
  • the same failure occurs with SoundFile, so it is not specific to the JSyn synth

This PR talks to the ASIO drivers directly through JAsioHost instead.

What changed

  • Added MultiChannel.useJASIO() and JAsioAudioDeviceManager, a JAsioHost-backed
    AudioDeviceManager with interleaved ring-buffered input/output streams.

  • useJASIO() switches the backend without loading a driver, so Sound.list() can
    enumerate all registered ASIO drivers. Select one afterwards:

    MultiChannel.useJASIO();
    Sound.list();
    Sound.outputDevice("Focusrite USB ASIO");
  • Rewrote the Windows ASIO driver enumerator in the bundled native DLL:

    • scans HKLM\SOFTWARE\ASIO in both the 64-bit and 32-bit registry views
    • scans HKCU\Software\ASIO
    • expands environment variables in InprocServer32 paths
    • accepts CLSIDs with or without braces
    • verifies that the DLL exists, matches the host bitness and is an in-process
      COM server before advertising it
    • deduplicates by CLSID and name and never aborts on a broken registry entry
    • JASIOHOST_DEBUG=1 prints a scan report and rejection reasons to stderr
  • Engine.selectOutputDevice() now stops the current synth before probing the
    next device, and the ASIO callback path is synchronized against driver
    teardown. ASIO drivers can therefore be switched at runtime without leaving a
    driver loaded.

  • Bundled the JAsioHost Java sources; the new jasio-deps target in build.xml
    compiles JAsioHost.jar. Prebuilt native DLLs are included so no ASIO SDK
    build is required.

  • Updated examples/IO/AsioOutput to list all registered ASIO drivers and play a
    different note on every reported output channel.

  • Added jasiohost-master/PATCHES.md documenting the native changes and the MSVC
    build command.

Testing

Tested on Windows 11 with Processing 4.5.7:

  • all 11 drivers registered on the machine are listed
  • Focusrite Scarlett 16i16 (ASIO driver name Focusrite USB ASIO): opens and plays
  • ASIO4ALL v2 (also tested with the Scarlett 16i16): 72 inputs / 64 outputs, opens and plays
  • VB-Matrix VASIO-8: buffer creation and start/stop work
  • runtime switching between ASIO4ALL v2, Focusrite Scarlett 16i16
    (Focusrite USB ASIO) and VB-Matrix VASIO-8 in a single JVM: 15 switches with
    audio callbacks running plus 30 rapid switches, no JVM crash; switching back
    to PortAudio unloads the last ASIO driver cleanly
  • the updated AsioOutput example compiles with Processing's preprocessor and
    is configured to play one different note per output channel

C++ runtime compatibility

ASIO driver DLLs are C++ COM servers and therefore depend on the Microsoft C++
runtime (msvcp140.dll / vcruntime140.dll). Which runtime version an ASIO
driver requires depends on the toolchain it was compiled with.

Windows loads native DLL dependencies from the host executable's directory
first. Processing 4.3.4 bundles an old msvcp140.dll (14.27) in
processing-4.3.4/java/bin, so the Focusrite Scarlett 16i16 ASIO driver binds
to that old runtime and crashes inside its own ASIOInit (crash log:
focusriteusbasio.dll -> msvcp140.dll). The same driver and library work in
Processing 4.5.7, whose JDK uses the newer system runtime. This is not a
problem in the Sound library and cannot be fixed from inside the JVM, because
the host's private runtime DLL is already loaded before the ASIO driver is.

The Microsoft 14.x C++ runtime is backward compatible: a newer runtime can
serve components built against older ones, but not the reverse. In practice
this means older ASIO drivers should be fine with an older bundled runtime,
while the very newest ASIO drivers may require a newer runtime than an old
Processing release provides. Using a current Processing release (4.5.x at the
time of writing) avoids the issue.

Native DLLs / building

The prebuilt 64-bit jasiohost64.dll is included in library/windows-amd64/
and jasiohost-master/; the maintainer does not need to build it. ASIO SDK
sources are not committed because of Steinberg's license. PATCHES.md
documents the exact changes and the MSVC build command. The 32-bit DLL is
unchanged; Processing 4 uses a 64-bit JVM.

Notes

  • ASIO only allows one driver to be loaded at a time; this is an ASIO API
    limitation.
  • ASIO drivers are enumerated when the JAsioHost native library is loaded.
  • JAsioHost is LGPL; its license text is included.

Refs #87

The PortAudio bindings' ASIO host fails to open many ASIO drivers in
practice, so this adds a second ASIO path based on JAsioHost, which
talks to the drivers directly. It is enabled with MultiChannel.useJASIO()
and keeps all existing functionality (JavaSound, PortAudio, multichannel,
audio input) untouched. JAsioHost (LGPL) is bundled as sources under
jasiohost-master/ and built into library/JAsioHost.jar by the new
jasio-deps ant target; the native dlls ship in library/windows-amd64/.
GitHub now auto-fails workflows using the retired upload-artifact v3
backend, and the old action versions run on deprecated Node runtimes.
Bump all actions to their current majors and check out the repository
in pr.yml before referencing the local composite setup action.
Realtek's ASIO driver (and teardown of some others) can report channels
that never get an audio buffer, or fire bufferSwitch callbacks while the
buffers are being disposed of. Check that every opened channel really has
a buffer before starting the driver so such drivers fail with a clear
error message, null-guard AsioChannel.setBufferIndex in JAsioHost, and
swallow RuntimeExceptions from callbacks that race with shutdown.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant