How do i get byte[] pcmData (just like in https://github.com/goxr3plus/Java-Spectrum-Analyser-Tutorials) but on xt audio? i wanted to draw the osc (spectrum analyzer) of the internal audio using wasapi (output speakers) of the computer real time. e.g. analyzes youtube audio output real time, internal audio of games, etc.
edit: how do i capture internal audio wasapi pcmdata (internal sound, not mic sound) using xt audio to analyze it on a visualizer? i need byte[]
See below for a complete example. It records 1 second of audio data for each loopback device, converts it to a byte array, then dumps that to a file with the name of the device. I hope it's sufficiently self-explanatory.
package sample;
import com.sun.jna.Pointer;
import java.io.FileOutputStream;
import java.util.EnumSet;
import xt.audio.Enums.XtDeviceCaps;
import xt.audio.Enums.XtEnumFlags;
import xt.audio.Enums.XtSample;
import xt.audio.Enums.XtSystem;
import xt.audio.Structs.XtBuffer;
import xt.audio.Structs.XtBufferSize;
import xt.audio.Structs.XtChannels;
import xt.audio.Structs.XtDeviceStreamParams;
import xt.audio.Structs.XtFormat;
import xt.audio.Structs.XtMix;
import xt.audio.Structs.XtStreamParams;
import xt.audio.XtAudio;
import xt.audio.XtDevice;
import xt.audio.XtDeviceList;
import xt.audio.XtPlatform;
import xt.audio.XtSafeBuffer;
import xt.audio.XtService;
import xt.audio.XtStream;
public class Sample {
// intermediate buffer
static byte[] BYTES;
// dump to file (never do this, see below)
static FileOutputStream fos;
// audio streaming callback
static int onBuffer(XtStream stream, XtBuffer buffer, Object user) throws Exception {
XtSafeBuffer safe = XtSafeBuffer.get(stream);
if(safe == null) return 0;
// lock buffer from native into java
safe.lock(buffer);
// short[] because we specified INT16 below
// this is the captured audio data
short[] audio = (short[])safe.getInput();
// you want a spectrum analyzer, i dump to a file
// but actually never dump to a file in any serious app
// see http://www.rossbencina.com/code/real-time-audio-programming-101-time-waits-for-nothing
processAudio(audio, buffer.frames);
// unlock buffer from java into native
safe.unlock(buffer);
return 0;
}
static void processAudio(short[] audio, int frames) throws Exception {
// convert from short[] to byte[]
for(int frame = 0; frame < frames; frame++) {
// for 2 channels
for(int channel = 0; channel < 2; channel++) {
// 2 = channels again
int sampleIndex = frame * 2 + channel;
// 2 = 2 bytes for each short
int byteIndex0 = sampleIndex * 2;
int byteIndex1 = sampleIndex * 2 + 1;
// probably some library method for this, somewhere
BYTES[byteIndex0] = (byte)(audio[sampleIndex] & 0x000000FF);
BYTES[byteIndex1] = (byte)((audio[sampleIndex] & 0x0000FF00) >> 8);
}
}
// by now BYTES contains the data you want,
// but be sure to account for frame count
// (i.e. not all off BYTES may contain useful data,
// might be some unused garbage at the end)
// compute total bytes this round
// = frame count * 2 channels * 2 bytes per short (INT16)
int byteCount = frames * 2 * 2;
// write to file - again, never do this in a real app
fos.write(BYTES, 0, byteCount);
}
public static void main(String[] args) throws Exception {
// this initializes platform dependent stuff like COM
try(XtPlatform platform = XtAudio.init(null, Pointer.NULL, null)) {
// works on windows only, obviously
XtService service = platform.getService(XtSystem.WASAPI);
// list input devices (this includes loopback)
try(XtDeviceList list = service.openDeviceList(EnumSet.of( XtEnumFlags.INPUT))) {
for(int i = 0; i < list.getCount(); i++) {
String deviceId = list.getId(i);
EnumSet<XtDeviceCaps> caps = list.getCapabilities(deviceId);
// filter loopback devices
if(caps.contains(XtDeviceCaps.LOOPBACK)) {
String deviceName = list.getName(deviceId);
// just to check what output we're recording
System.out.println(deviceName);
// open device
try(XtDevice device = service.openDevice(deviceId)) {
// 16 bit 48khz
XtMix mix = new XtMix(48000, XtSample.INT16);
// 2 channels input, no masking
XtChannels channels = new XtChannels(2, 0, 0, 0);
// final audio format
XtFormat format = new XtFormat(mix, channels);
// query min/max/default buffer sizes
XtBufferSize bufferSize = device.getBufferSize(format);
// true->interleaved, onBuffer->audio stream callback
XtStreamParams streamParams = new XtStreamParams(true, Sample::onBuffer, null, null);
// final initialization params with default buffer size
XtDeviceStreamParams deviceParams = new XtDeviceStreamParams(streamParams, format, bufferSize.current);
// run stream
// safe buffer allows you to get java short[] instead on jna Pointer in the callback
try(XtStream stream = device.openStream(deviceParams, null);
var safeBuffer = XtSafeBuffer.register(stream, true)) {
// max frames to enter onBuffer * channels * bytes per sample
BYTES = new byte[stream.getFrames() * 2 * 2];
// make filename valid
String fileName = deviceName.replaceAll("[\\\\/:*?\"<>|]", "");
try(FileOutputStream fos0 = new FileOutputStream(fileName + ".raw")) {
// make filestream accessible to the callback
// could also be done by passsing as userdata to openStream
fos = fos0;
// run for 1 second
stream.start();
Thread.sleep(1000);
stream.stop();
}
}
}
}
}
}
}
}
}