Hi Daniel,
On 17 January 2015 at 21:05, Daniel Hams <
danie...@gmail.com> wrote:
> First comment - thank you very much for putting jnajack out there and making
> it available - it's much appreciated given the sorry state of regular Java
> audio!
Thanks! You may want to have a read of
https://praxisintermedia.wordpress.com/2013/11/06/jaudiolibs-audioservers-a-portaudio-esque-java-api/
I'm not that scathing of JavaSound myself, at least on Linux (Windows
is useless) - I'm able to get similar performance to JACK out of the
JAudioLibs JS backend, with a few little hacks in the background.
> I've been playing around a little using jnajack as the API for getting my
> floats and midi in and out of Java, but I've noticed a few things where I've
> perhaps not understood how things should work. If you have the time for a
> little discussion that'd be great.
Fire away! I'd recommend using the JAudioLibs AudioServer API for
audio - it's slightly simpler than using JNAJack directly, though
there's no access to MIDI without directly using JNAJack.
> (1) Achievable latencies seem a little high.
>
> Using jack with a period size of 1024 X 2 @ 44100hz is as low as I can go
> (46ms) with Java without getting overruns - this seems a little high.
...
> Command line as follows:
>
> java -XX:+UseG1GC -XX:InitiatingHeapOccupancyPercent=80
> -XX:MaxGCPauseMillis=10 -Xms512m -Xmx1024 -XX:-UsePerfData -jar cd.jar
OK, your achievable latency seems far too high, assuming otherwise
your system can achieve much better? My current laptop with stock
Ubuntu kernel (not even low latency kernel at the moment) and OpenJDK
7 can achieve a reliable 256 x 2 @ 48000hz with the internal soundcard
- the system won't go lower. I've achieved better in the past with a
realtime kernel.
First question - have you tried using
java -Xincgc -jar cd.jar
Does this give you better performance?
Generally, I've found the G1 garbage collector to be useless.
Unfortunately, I think that -Xincgc is deprecated in 8, though it
should still work - I really hope they change their mind! Likewise,
unless you need to be setting the heap size, don't! The smaller heap
you have, generally the better the performance because the GC has less
to do in one go - you want GC happening little and often.
> (2) Use of jna vs jni
>
> There was a mapped layer back in 2007 called jjack that (I think) used JNI -
> maybe you have previously tried with JNI too? The reason I mention this is
> the allocations I spotted above. In an ideal world we'd attempt to get to
> zero allocations during runtime use.
I did some benchmarking of JJack and JNAJack early on in the
development process. Generally, achievable latency was similar in my
experience, and this is back before JNA had direct native mapping.
CPU usage was initially much higher with JNAJack, until I helped get
the CallbackThreadInitializer API into JNA, since which it's generally
similar.
Offhand, I think JJack would have similar allocations to JNAJack - I
don't have the source to hand anymore, but I'm pretty sure it still
has to allocate the buffer reference in the JVM.
I'm happy to see a benchmark of JNI vs JNA using the JNAJack API -
I've just no intention of writing one! ;-)
> Of course if an application developer
> creates garbage that's something else, but it would be nice to have the
> jnajack platform not be a factor in it.
Not sure if you've seen Praxis LIVE (
www.praxislive.org), which is the
software I wrote JNAJack for. In the current v2 alpha it's possible
to run the audio pipeline in a separate VM - this is an approach I'd
recommend for a Java JACK application if you want to get best
performance.
> (3) CallbackThreadInitializer and application exit
>
> My application hangs attempting to shut down with the existing code base -
> and after I little investigation I discovered that changing the callback
> thread initialiser bits allows the shutdown.
>
> Jack.java:96 change from
>
> setCTIMethod.invoke(null, callback, ctiConstructor.newInstance(true,
> false, "JNAJack"));
> to
> setCTIMethod.invoke(null, callback, ctiConstructor.newInstance(false,
> false, "JNAJack"));
>
> So calling the constructor with daemon=false allows my app VM to shutdown.
> I've attempted to close and shutdown the jack connection appropriately but I
> still get this issue.
>
> Any pointers on what I'm probably missing? Maybe I should create a test case
> to show the issue ;-)
A test case would be good. Your experience would appear to be the
exact opposite of what you should be seeing! :-\
> (3a) Now that I'm writing about this, should the callback thread initializer
> be setup for the other callbacks too? (Currently only setup for
> ProcessCallbackWrapper, probably should be added for XRunCallbackWrapper and
> others too).
That shouldn't be necessary. When a native thread calls into the JVM,
it must be attached to the JVM before it can call any Java methods.
Prior to the changes I requested in JNA, every callback would attach
and detach, creating a Thread proxy object each time - hence the high
CPU load. The CallbackThreadInitializer keeps the thread attached to
the JVM between process calls. IIRC, if the other callbacks happen in
the same JACK thread, then the existing process thread settings should
cover them too. Even if they're not covered, the overhead will be
minimal for infrequent callbacks.
> Sorry for the rambling!
No problem, I can ramble with the best of them! ;-)
btw - your GitHub profile says you're UK based - don't suppose you're
anywhere near Oxford?
Best wishes,
Neil
--
Neil C Smith
Artist : Technologist : Adviser
http://neilcsmith.net
Praxis LIVE - open-source intermedia development -
www.praxislive.org
Digital Prisoners - interactive spaces and projections -
www.digitalprisoners.co.uk