Skip to content

RangeError: offset is out of bounds when using addBuffers() with 100ms chunks — and gaps when it works #22

@rafaelhovhannisyan24

Description

@rafaelhovhannisyan24
<!DOCTYPE html>
<html>

<head>
	<title>Signalsmith Stretch Web Audio Chunk Demo</title>
	<style>
		body {
			font-family: sans-serif;
			padding: 2rem;
		}

		button,
		input[type=file] {
			margin-top: 1rem;
		}
	</style>
</head>

<body>
	<h1>Audio Chunk Stretch Demo (100ms)</h1>
	<input type="file" id="upload" accept="audio/*">
	<button id="start">Start Playback</button>
	<script type="module">
		import SignalsmithStretch from '../release/SignalsmithStretch.mjs';

		const uploadInput = document.getElementById('upload');
		const startButton = document.getElementById('start');
		const audioContext = new AudioContext();

		let stretchNode;
		let chunkedBuffers = [];
		let chunkDuration = 0.1; // 100ms

		uploadInput.addEventListener('change', async (e) => {
			const file = e.target.files[0];
			if (!file) return;

			const arrayBuffer = await file.arrayBuffer();
			const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
			chunkedBuffers = splitIntoChunks(audioBuffer, chunkDuration);

			console.log(`Split into ${chunkedBuffers.length} chunks of ~${chunkDuration}s`);
		});

		startButton.addEventListener('click', async () => {
			await audioContext.resume(); // Ensure audio context is running

			if (!chunkedBuffers.length) return alert("Please upload an audio file first.");

			if (stretchNode) {
				stretchNode.stop();
				stretchNode.disconnect();
			}

			stretchNode = await SignalsmithStretch(audioContext);
			stretchNode.connect(audioContext.destination);

			stretchNode.configure({
				blockMs: 120,
				intervalMs: 30,
				splitComputation: true,
			});

			let chunkIndex = 0;
			const playNextChunk = async () => {
				if (chunkIndex >= chunkedBuffers.length) return;

				const chunk = chunkedBuffers[chunkIndex++];
				await stretchNode.addBuffers(chunk);
				stretchNode.schedule({
					active: true,
					rate: 1,
					semitones: 0,
					tonalityHz: 8000,
					formantCompensation: false,
					formantBaseHz: 200,
					output: audioContext.currentTime + 0.05 // slight offset to allow scheduling
				});

				setTimeout(playNextChunk, chunkDuration * 1000);
			};

			playNextChunk();
		});

		function splitIntoChunks(audioBuffer, durationSeconds) {
			const sampleRate = audioBuffer.sampleRate;
			const chunkSize = Math.floor(sampleRate * durationSeconds);
			const totalSamples = audioBuffer.length;
			const totalChunks = Math.ceil(totalSamples / chunkSize);
			const channels = audioBuffer.numberOfChannels;

			let chunks = [];
			for (let i = 0; i < totalChunks; i++) {
				let chunk = [];
				for (let c = 0; c < channels; c++) {
					const fullChannel = audioBuffer.getChannelData(c);
					const start = i * chunkSize;
					const end = Math.min(start + chunkSize, fullChannel.length);

					// Ensure we don't slice beyond the buffer
					if (start >= fullChannel.length) {
						// No more samples, push empty Float32Array to maintain channel count
						chunk.push(new Float32Array(0));
					} else {
						const length = end - start;
						if (length > 0) {
							// Copy the slice into a new Float32Array to avoid referencing the original buffer
							const sliced = new Float32Array(length);
							sliced.set(fullChannel.subarray(start, end));
							chunk.push(sliced);
						} else {
							chunk.push(new Float32Array(0));
						}
					}
				}
				chunks.push(chunk);
			}
			return chunks;
		}

	</script>

</body>

</html>

Hi,
I'm trying to stream audio into SignalsmithStretch in 100ms chunks using addBuffers() (see demo code below), but I'm encountering this error:

Uncaught RangeError: offset is out of bounds
It seems to occur inside buffer.subarray(blockSamples).set(...), suggesting the copied chunk length doesn't match internal expectations — possibly due to mismatches between chunk size and blockMs.

When I use the following configuration, the error doesn’t occur:

**stretchNode.configure({
  blockMs: 120,
  intervalMs: 30,
  splitComputation: true,
});**

However, even though playback starts, there are audible blips/gaps between chunks. It doesn’t sound seamless, especially on short samples.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions