Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

wasm-spectrogram

A Rust library for generating Aegisub-like spectrogram images from audio data, optimized for WebAssembly.

This library's processing logic and visual output are a direct port of the spectrogram implementation found in Aegisub.

Example Output

A spectrogram screenshot

(The timeruler is not generated by this library)

Usage

1. WebAssembly

The following functions are exposed to JavaScript via wasm-bindgen:

#[wasm_bindgen]
pub fn generate_spectrogram_image(
    audio_data: &[f32],
    sample_rate: u32,
    fft_size: usize,
    hop_length: usize,
    img_width: usize,
    img_height: usize,
    gain: f32,
) -> Result<Vec<u8>, JsValue>

// If using the 'parallel' feature:
#[wasm_bindgen]
pub fn init_thread_pool(num_threads: usize) -> js_sys::Promise

JavaScript Example (with parallel support)

import init, {
	generate_spectrogram_image,
	initThreadPool,
} from "./pkg/wasm_spectrogram.js";

async function run() {
	await init();

	// This step is only needed if you compiled with the 'parallel' feature.
	await initThreadPool(navigator.hardwareConcurrency);

	// --- Your audio loading logic ---
	// const audioData = getYourF32AudioData();
	// const sampleRate = 48000;
	// --------------------------------

	const fftSize = 1024;
	const hopLength = 128;
	const imgWidth = 800;
	const imgHeight = 600;
	const gain = 9.0; // Adjust gain as needed

	try {
		const pixelData = generate_spectrogram_image(
			audioData,
			sampleRate,
			fftSize,
			hopLength,
			imgWidth,
			imgHeight,
			gain,
		);

		// 4. `pixelData` is a Uint8Array of [R, G, B, A, R, G, B, A, ...]
		//    You can now render this to a <canvas>
		const canvas = document.getElementById("my-canvas");
		const ctx = canvas.getContext("2d");
		canvas.width = imgWidth;
		canvas.height = imgHeight;

		const imgData = new ImageData(
			new Uint8ClampedArray(pixelData.buffer),
			imgWidth,
			imgHeight,
		);

		// The image is rendered from bottom-up, but canvas putImageData
		// works top-down, so it will appear correctly oriented.
		ctx.putImageData(imgData, 0, 0);
	} catch (e) {
		console.error("Error generating spectrogram:", e);
	}
}

run();

2. Native Rust

You can also use this library in a native Rust application by enabling the native_api module.

use wasm_spectrogram::native_api::generate_spectrogram_image_native;

// --- Your audio loading logic ---
// let audio_data: Vec<f32> = ...;
// let sample_rate = 48000;
// --------------------------------

let pixels = generate_spectrogram_image_native(
    &audio_data,
    sample_rate,
    1024,
    128,
    800,
    600,
    9.0
).unwrap();

// `pixels` is a Vec<u8> of RGBA data
// You can now save this with a crate like `image`

By default, native processing is single-threaded. If you want to enable multi-threading via Rayon, add the parallel feature to your Cargo.toml:

wasm-spectrogram = { version = "0.1", features = ["parallel"] }

Building

  1. Install the Toolchain: The required toolchain is specified in rust-toolchain.toml (nightly-2025-11-04). You can change it to latest version if you like.

    rustup toolchain install nightly-2025-11-04
    rustup target add wasm32-unknown-unknown --toolchain nightly-2025-11-04
  2. Install wasm-pack:

    cargo install wasm-pack
  3. Build (Serial - No Threads): This builds the standard, single-threaded Wasm module. We use a Python build script to handle cross-platform environments automatically.

    python build.py
  4. Build (Parallel - With Threads): To enable multi-threading, simply pass the --parallel flag to the script. This will automatically inject the necessary RUSTFLAGS (for Atomics and Shared Memory) and compile the wasm.

    python build.py --parallel

    ⚠️ Important: To use the multi-threaded build, your web server must set the following headers to enable Cross-Origin Isolation. This is required for SharedArrayBuffer (which powers Wasm threads) to work.

    Cross-Origin-Opener-Policy: same-origin
    Cross-Origin-Embedder-Policy: require-corp
    

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages