Documentation/Source example
Example source
samples/vis/JS - Waveform.js
Referenced by:vis.Create
include('docs/Flags.js');
// JSplitter vis sample: Waveform | JavaScript callback.
// Source: recent PCM waveform; render options affect only the presentation.
// JSplitter vis: Waveform rendered in JavaScript (works with either GDI or Direct2D).
vis.UpdateIntervalMs = 16; // Default: 16 ms; allowed: unsigned integer 1..4294967295 ms.
const textColor = window.InstanceType === 1
? window.GetColourDUI(0) : window.GetColourCUI(0);
const backgroundColor = window.InstanceType === 1
? window.GetColourDUI(1) : window.GetColourCUI(1);
const envelopeColor = (textColor & 0x00ffffff) | 0x60000000;
let envelopePoints = [];
// Callback data (three equally sized buffers of PCM amplitudes, -1..1):
// data.line: Float32Array - resampled waveform for drawing a continuous line.
// data.min: Float32Array - minimum PCM value within each time interval.
// data.max: Float32Array - maximum PCM value within each time interval.
// min/max form an envelope that preserves short peaks missed by resampling.
// JSplitter owns these arrays and resizes them automatically with Points.
const waveform = vis.Create(VisSourceType.Waveform, {
channel: "mix", // Default: "mix"; also accepts a vis.AudioFormat channel name or unsigned PCM index.
points: "auto", // Default: "auto" (~2 points per 3 px); allowed: integer 16..4096 or "auto".
durationMs: 300, // Default: 300 ms; allowed: integer 20..2000 ms.
smoothing: 0 // Default: 0; allowed: 0..1.
}, (gr, x, y, w, h, data) => {
const { line, min, max } = data;
const n = line.length;
if (n < 2) return;
const mid = y + h * 0.5;
const radius = h * 0.5;
const step = w / (n - 1);
// Ordinary drawing is not antialiased by default in either GDI or D2D.
gr.SetSmoothingMode(SmoothingMode.AntiAlias);
// Draw the full min/max envelope as a translucent filled region. The
// central waveform below remains visible and demonstrates data.line too.
envelopePoints.length = n * 4;
for (let i = 0; i < n; ++i) {
const px = x + i * step;
envelopePoints[i * 2] = px;
envelopePoints[i * 2 + 1] = mid - Math.max(-1, Math.min(1, max[i])) * radius;
const reverse = n * 2 + (n - 1 - i) * 2;
envelopePoints[reverse] = px;
envelopePoints[reverse + 1] = mid - Math.max(-1, Math.min(1, min[i])) * radius;
}
gr.FillPolygon(envelopeColor, 1, envelopePoints);
// The central waveform line is independent of the envelope.
for (let i = 1; i < n; ++i) {
const x0 = x + (i - 1) * step, x1 = x + i * step;
const y0 = mid - Math.max(-1, Math.min(1, line[i - 1])) * radius;
const y1 = mid - Math.max(-1, Math.min(1, line[i])) * radius;
gr.DrawLine(x0, y0, x1, y1, 1, textColor);
}
gr.SetSmoothingMode(SmoothingMode.Default);
});
function on_paint(gr) {
const w = window.Width, h = window.Height;
gr.FillSolidRect(0, 0, w, h, backgroundColor);
if (w > 0 && h > 0) waveform.Draw(gr, 0, 0, w, h);
}
setInterval(() => window.Repaint(), 33);