async function visual(sound) {
const audio_context = new window.AudioContext({ sampleRate: 44100 }),
audio_buffer = await new Promise((resolve) =>
sound.arrayBuffer().then((d) => audio_context.decodeAudioData(d, resolve))
);
const pcmf32_buffer = audio_buffer.getChannelData(0);
const length_in_seconds = pcmf32_buffer.length / audio_context.sampleRate;
const minimum_frequency = 16.34 * length_in_seconds;
const maximum_frequency = 20000.0 * length_in_seconds;
const deviation = 1.0;
const height = 500;
const frequency_basis_linear = 0;
const frequency_range_linear = maximum_frequency - minimum_frequency;
const frequency_offset_linear = minimum_frequency;
const frequency_basis_log = 2.0;
const minimum_octave =
Math.log(minimum_frequency) / Math.log(frequency_basis_log);
const maximum_octave =
Math.log(maximum_frequency) / Math.log(frequency_basis_log);
const frequency_range_log = maximum_octave - minimum_octave;
const frequency_offset_log = minimum_octave;
const frequencies = CCWT.frequencyBand(
height,
frequency_range_log,
frequency_offset_log,
frequency_basis_log,
deviation
);
const padding = 1;
const gain_factor = 30;
const fourier_transformed_signal = CCWT.fft1d(
pcmf32_buffer,
padding,
gain_factor
);
const pixels_per_second = 60;
const output_width = Math.floor(length_in_seconds * pixels_per_second);
let percent_complete = 0;
const width = output_width;
const canvas_ctx = DOM.context2d(width, height, 1);
canvas_ctx.fillStyle = "#000000";
canvas_ctx.fillRect(0, 0, width, height);
const row_callback = function (y, row_data, output_padding) {
const spectrogram = canvas_ctx.createImageData(output_width, 1);
const spectro_data = spectrogram.data;
const percent_complete_new = Math.round((y / height) * 100);
if (percent_complete_new != percent_complete) {
console.log(percent_complete_new + "%");
percent_complete = percent_complete_new;
}
let x = 0;
for (x = 0; x < output_width; ++x) {
const r = row_data[output_padding * 2 + x * 2];
const i = row_data[output_padding * 2 + x * 2 + 1];
const amplitude_raw = Math.hypot(r, i);
const value_sign = (0 < amplitude_raw) - (amplitude_raw < 0);
const amplitude = Math.min(amplitude_raw * value_sign, 1.0) * value_sign;
const rgb = d3.rgb(color(amplitude));
const index = x * 4;
spectro_data[index] = rgb.r;
spectro_data[index + 1] = rgb.g;
spectro_data[index + 2] = rgb.b;
spectro_data[index + 3] = 255;
}
canvas_ctx.putImageData(spectrogram, 0, y);
};
CCWT.numericOutput(
fourier_transformed_signal,
padding,
frequencies,
0,
frequencies.length / 2,
output_width,
row_callback
);
return canvas_ctx.canvas;
}