Export audio to WAV
Bake a slowed-down study track a student can loop in any audio player. Generate audio previews for every song in a catalogue so browsers can hear before they load the notation. Render backing tracks a live performer can trigger from a phone. AlphaTabApi.exportAudio(options) runs the alphaTab synthesizer offline and hands you the raw PCM samples โ alphaTab produces the samples, your code encodes them into whatever container you want. This page walks the full pipeline: configure the export, pull chunks, wrap them in a WAV header, download the result.
async function generateWav() {
try {
// setup options
const options = new alphaTab.synth.AudioExportOptions();
options.masterVolume = 0.8;
options.metronomeVolume = 1;
options.trackVolume.set(0, 0.5);
options.sampleRate = 44100;
// start exporter
const exporter = await api!.exportAudio(options);
// collect all chunks in 500ms steps
const chunks: Float32Array[] = [];
try {
while (true) {
const chunk = await exporter.render(500);
if (chunk === undefined) {
break;
}
chunks.push(chunk.samples);
}
} finally {
exporter.destroy();
}
// use the samples.
// in this example: create a wav file and set it as source of the audio element
const wav = convertSamplesToWavBlobUrl(chunks, options.sampleRate);
// <audio id="wav" controls="true" />
document.querySelector<HTMLAudioElement>('#wav')!.src = wav;
}
catch (e) {
console.error('Error during export', e);
alert('Something went wrong during export, check the browser logs for details');
}
}
function convertSamplesToWavBlobUrl(chunks: Float32Array[], sampleRate: number): string {
const samples = chunks.reduce((p, c) => p + c.length, 0);
const wavHeaderSize = 44;
const fileSize = wavHeaderSize + samples * 4;
const buffer = alphaTab.io.ByteBuffer.withCapacity(fileSize);
//
// write wav header
// RIFF chunk
buffer.write(new Uint8Array([0x52, 0x49, 0x46, 0x46]), 0, 4); // RIFF
alphaTab.io.IOHelper.writeInt32LE(buffer, fileSize - 8); // file size
buffer.write(new Uint8Array([0x57, 0x41, 0x56, 0x45]), 0, 4); // WAVE
// format chunk
buffer.write(new Uint8Array([0x66, 0x6D, 0x74, 0x20]), 0, 4); // fmtโฃ
alphaTab.io.IOHelper.writeInt32LE(buffer, 16); // block size
alphaTab.io.IOHelper.writeInt16LE(buffer, 3); // audio format (1=WAVE_FORMAT_IEEE_FLOAT)
const channels = 2;
alphaTab.io.IOHelper.writeInt16LE(buffer, channels); // number of channels
alphaTab.io.IOHelper.writeInt32LE(buffer, sampleRate); // sample rate
alphaTab.io.IOHelper.writeInt32LE(buffer, Float32Array.BYTES_PER_ELEMENT * channels * sampleRate); // bytes/second
const bitsPerSample = Float32Array.BYTES_PER_ELEMENT * 8;
alphaTab.io.IOHelper.writeInt16LE(buffer, channels * Math.floor((bitsPerSample + 7) / 8)); // block align
alphaTab.io.IOHelper.writeInt16LE(buffer, bitsPerSample); // bits per sample
// data chunk
buffer.write(new Uint8Array([0x64, 0x61, 0x74, 0x61]), 0, 4); // data
alphaTab.io.IOHelper.writeInt32LE(buffer, samples * 4);
for (const c of chunks) {
const bytes = new Uint8Array(c.buffer, c.byteOffset, c.byteLength);
buffer.write(bytes, 0, bytes.length);
}
const blob: Blob = new Blob([
buffer.toArray()
], {
type: 'audio/wav'
});
return URL.createObjectURL(blob);
}
The demo synthesizes the full score, wraps the samples in a WAV file, and pipes the resulting blob URL into an <audio> element. Read the source under src/components/AudioExportSample/ for the complete implementation.
Pull-based renderingโ
exportAudio returns an IAudioExporter. The exporter follows an asynchronous pull pattern โ you call render(milliseconds) in a loop and receive one AudioExportChunk per call. Each chunk carries a stereo interleaved Float32Array at the sample rate you configured. When the song ends, render returns undefined.
import * as alphaTab from '@coderline/alphatab';
const container = getAlphaTabContainer();
const api = new alphaTab.AlphaTabApi(container, {
core: { file: '/scores/song.gp' },
player: { playerMode: alphaTab.PlayerMode.EnabledSynthesizer },
});
api.scoreLoaded.on(async () => {
const options = new alphaTab.synth.AudioExportOptions();
options.sampleRate = 44100;
options.masterVolume = 1;
const exporter = await api.exportAudio(options);
const chunks = [];
try {
while (true) {
const chunk = await exporter.render(500);
if (chunk === undefined) break;
chunks.push(chunk.samples);
}
} finally {
exporter.destroy();
}
const wavUrl = encodeWav(chunks, options.sampleRate);
triggerDownload(wavUrl, `${api.score.title || 'Untitled'}.wav`);
});
getAlphaTabContainer() is the platform-specific render target. encodeWav and triggerDownload are your code โ the samples are format-agnostic.
Options that shape the exportโ
AudioExportOptions mirrors the runtime mixer, but with export-specific defaults:
soundFontsโ SF2 buffers to load into the offline synth. If omitted, the exporter reuses whatever the current synthesizer already has loaded.sampleRateโ output rate in Hz. Defaults to44100.masterVolume,metronomeVolumeโ mix knobs applied to the offline synth. Metronome defaults to0; enable it to bake the click into the render.playbackRangeโ limit the export to a tick range. Omit for the whole song.trackVolume,trackTranspositionPitchesโ per-track mix and pitch shift, keyed by track index. Applied on top of the score's own settings.useSyncPointsโ whentrue, sync-point timing is respected. Turn off for the score's original tempo map.
Encode the chunks to WAVโ
WAV wants a 44-byte header followed by the raw samples. alphaTab emits IEEE-float samples in stereo interleaved order, so the format tag is 3 (WAVE_FORMAT_IEEE_FLOAT) and each sample stays 32 bits. The <AudioExportSample> component above lists the full encoder โ copy the createWavSampleUrl function into your project.
Hand the chunks to a codec library (LAME, OggEncoder, Opus) instead when you want MP3, OGG, or Opus output. A 4-minute stereo song at 44.1 kHz is roughly 84 MB of float PCM, so encode as chunks arrive rather than buffering the whole song.
Where to look nextโ
- Play, pause, stop โ the live playback counterpart to offline export.
- Backing-track audio โ combine exported audio with sync points for hybrid playback.
AlphaTabApi.exportAudioand theplayer.playerModethat has to be enabled for it.AudioExportOptions,AudioExportChunk, andIAudioExporterin the API reference.