Полный пример см. ниже. Он записывает 1 секунду аудиоданных для каждого петлевого устройства, преобразует их в массив байтов, а затем выгружает их в файл с именем устройства. Я надеюсь, что это достаточно понятно.
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();
}
}
}
}
}
}
}
}
}
person
Sjoerd van Kreel
schedule
05.02.2021