
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Read and write multi-track M4A Stems files with karaoke extensions.
Perfect for karaoke applications - Store backing tracks and synchronized lyrics in a single file format compatible with DJ software like Traktor and Mixxx.
npm install m4a-stems
Requirements:
This library works with .stem.mp4 or .stem.m4a files that follow the NI Stems specification with karaoke extensions.
Note: The official NI spec uses
.stem.mp4, but.stem.m4ais equally valid since both are MP4 containers. Use.stem.mp4for maximum DJ software compatibility (Traktor, etc.), or.stem.m4aif targeting audio applications.
| Track | Content | Purpose |
|---|---|---|
| 0 | Master | Full mix (plays in normal audio players) |
| 1 | Drums | Rhythm, percussion |
| 2 | Bass | Low-end, basslines |
| 3 | Other | Melody, instruments, synths |
| 4 | Vocals | Vocals (mute for karaoke) |
The format uses two metadata locations:
stem atom (moov/udta/stem) - NI Stems metadata for DJ software compatibilitykara atom (moov/udta/meta/ilst/----:com.stems:kara) - Karaoke lyrics and timingThis dual approach means files work in both DJ software and karaoke applications.
The Extractor works with binary data - you handle the I/O, the library handles the extraction.
Accepted input types:
Uint8Array - works everywhereArrayBuffer - works everywhere (e.g., from fetch)Buffer - works in Node.js onlyimport * as Extractor from 'm4a-stems/extractor';
import fs from 'fs/promises';
// Read the file yourself
const fileData = await fs.readFile('song.stem.m4a');
// Extract tracks (synchronous, returns Uint8Array)
const trackBuffer = Extractor.extractTrack(fileData, 0);
const allTracks = Extractor.extractAllTracks(fileData);
const info = Extractor.getTrackInfo(fileData);
const count = Extractor.getTrackCount(fileData);
import * as Extractor from 'm4a-stems/extractor';
// Fetch the file yourself
const response = await fetch('song.stem.m4a');
const arrayBuffer = await response.arrayBuffer();
// Extract tracks (synchronous, returns Uint8Array)
const trackBuffer = Extractor.extractTrack(arrayBuffer, 0);
const allTracks = Extractor.extractAllTracks(arrayBuffer);
import * as Extractor from 'm4a-stems/extractor';
// Fetch stems file
const response = await fetch('song.stem.m4a');
const arrayBuffer = await response.arrayBuffer();
// Extract all tracks as separate M4A buffers
const tracks = Extractor.extractAllTracks(arrayBuffer);
// Decode each track with Web Audio API
const audioContext = new AudioContext();
const audioBuffers = await Promise.all(
tracks.map(track => audioContext.decodeAudioData(track.buffer))
);
// Now you have 5 AudioBuffers: master, drums, bass, other, vocals
import { M4AStemsReader, Atoms } from 'm4a-stems';
// Full file load
const data = await M4AStemsReader.load('song.stem.m4a');
console.log(data.metadata.title); // "Song Title"
console.log(data.metadata.artist); // "Artist Name"
console.log(data.metadata.key); // "Am"
console.log(data.metadata.duration); // 180.5 (seconds)
// Access lyrics with timing
console.log(data.lyrics);
// [
// { start: 0.5, end: 2.0, text: 'First line', words: { timings: [[0, 0.3], [0.4, 0.8]] } },
// { start: 2.5, end: 4.0, text: 'Second line' }
// ]
// Or read atoms directly
const stems = await Atoms.readNiStemsMetadata('song.stem.m4a');
// { version: 1, mastering_dsp: {...}, stems: [{name: 'drums', color: '#FF0000'}, ...] }
const kara = await Atoms.readKaraAtom('song.stem.m4a');
// { timing: {...}, lines: [...], singers: {...} }
import { Atoms } from 'm4a-stems';
// Add NI Stems metadata (for DJ software)
await Atoms.addNiStemsMetadata('song.stem.m4a', ['drums', 'bass', 'other', 'vocals']);
// Add karaoke data
await Atoms.writeKaraAtom('song.stem.m4a', {
timing: { offset_sec: 0 },
lines: [
{
start: 0.5,
end: 2.0,
text: 'Hello world',
words: { timings: [[0, 0.4], [0.5, 1.0]] } // Word-level timing
}
]
});
// Add standard metadata
await Atoms.addStandardMetadata('song.stem.m4a', {
title: 'Song Title',
artist: 'Artist Name',
album: 'Album Name',
year: 2024,
genre: 'Rock',
tempo: 120
});
// Add musical key
await Atoms.addMusicalKey('song.stem.m4a', 'Am');
The Writer requires FFmpeg to encode WAV files to AAC and mux the multi-track container.
import { M4AStemsWriter } from 'm4a-stems';
await M4AStemsWriter.write({
outputPath: 'output.stem.m4a',
// Audio stems (WAV files to be encoded to AAC)
stemsWavFiles: {
vocals: 'tracks/vocals.wav',
drums: 'tracks/drums.wav',
bass: 'tracks/bass.wav',
other: 'tracks/other.wav',
},
// Full mix for backward compatibility
mixdownWav: 'tracks/mixdown.wav',
// Metadata
metadata: {
title: 'Song Title',
artist: 'Artist Name',
key: 'Am',
tempo: 120,
},
// Karaoke lyrics
lyricsData: {
lines: [
{ start: 0.5, end: 2.0, text: 'First line of lyrics' },
{ start: 2.5, end: 4.0, text: 'Second line of lyrics' },
],
},
// AAC is the NI Stems standard (default if omitted)
codec: 'aac',
});
# Inspect file structure
npx m4a-stems song.stem.m4a
# Show only metadata
npx m4a-stems song.stem.m4a --metadata
# Show only lyrics
npx m4a-stems song.stem.m4a --lyrics
# Show MP4 atom tree
npx m4a-stems song.stem.m4a --atoms
import * as Extractor from 'm4a-stems/extractor';
All functions are synchronous. Input accepts Uint8Array, ArrayBuffer, or Node.js Buffer:
Extractor.extractTrack(data, trackIndex) → Uint8Array
Extractor.extractAllTracks(data) → Uint8Array[]
Extractor.getTrackCount(data) → number
Extractor.getTrackInfo(data) → TrackInfo[]
import { Atoms } from 'm4a-stems';
// NI Stems metadata
await Atoms.readNiStemsMetadata(filePath) → Object
await Atoms.addNiStemsMetadata(filePath, stemNames) → void
// Karaoke data
await Atoms.readKaraAtom(filePath) → Object
await Atoms.writeKaraAtom(filePath, karaData) → void
// Standard metadata
await Atoms.addStandardMetadata(filePath, metadata) → void
await Atoms.addMusicalKey(filePath, key) → void
// Advanced features
await Atoms.writeVpchAtom(filePath, pitchData) → void // Vocal pitch
await Atoms.writeKonsAtom(filePath, onsetsArray) → void // Beat onsets
await Atoms.dumpAtomTree(filePath) → Object[]
import { M4AStemsReader } from 'm4a-stems';
const data = await M4AStemsReader.load(filePath);
// {
// metadata: { title, artist, album, duration, key, tempo, genre, year },
// lyrics: [{ start, end, text, words? }],
// features: { vocalPitch, onsets },
// audio: { sources, timing, profile }
// }
import { M4AStemsWriter } from 'm4a-stems';
await M4AStemsWriter.write({
outputPath,
stemsWavFiles: { vocals, drums, bass, other }, // WAV files to encode
mixdownWav,
metadata: { title, artist, album, key, tempo, genre, year },
lyricsData: { lines },
codec: 'aac', // 'aac' (default, NI Stems standard) or 'alac' (lossless)
});
DJ Software (Full Stem Support):
Audio Players (Master Track Only):
Karaoke Applications:
npm test # Run tests
npm run test:coverage # With coverage
npm run lint # Linting
MIT License - see LICENSE for details.
Created by Luis Montes as part of the Loukai karaoke project.
See the Loukai M4A Format Specification for complete format details.
FAQs
Read and write multi-track M4A Stems files with karaoke extensions
We found that m4a-stems demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.