!7 import static x30_pkg.x30_util.VF1; module ReceiveAudio > DynPrintLog { bool enabled; float gain = 0.5f; transient L chunks = synchroLinkedList(); transient int minChunksToPlay = 2; start { vmBus_onMessage('gotFromPrivateChat, voidfunc(S sender, S msg) { if (enabled && startsWith(msg, "audio:")) chunks.add(shortArrayFromBytes_littleEndian(base64decode(substring(msg, 6)))); }); dm_addSoundSource(new VF1() { int sampleIndex; short[] samples; public void get(double[] pair) { pair[0] = pair[1] = 0; if (!enabled) ret; if (samples == null) { if (l(chunks) < minChunksToPlay) { minChunksToPlay = 2; ret; } minChunksToPlay = 1; samples = syncPopFirst(chunks); sampleIndex = 0; } pair[0] = samples[sampleIndex]*gain; pair[1] = samples[sampleIndex+1]*gain; sampleIndex += 2; if (sampleIndex >= l(samples)) samples = null; } }); } visualize { ret centerAndSouth(super.visualize(), centerAndEast( withLabel("Volume (0-200%):", liveSliderZeroToOne(gain/2, voidfunc(float f) { setField(gain := f*2) })), main.onChange(dm_fieldCheckBox('enabled), r { if (!enabled) chunks.clear() }))); } }