
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
karaoke-player
Advanced tools
A Node.js library for reading and playing MIDI/KAR karaoke files with multi-encoding support (UTF-8, TIS-620, Windows-874). Includes browser-only MIDI player with Web Audio API support.
A Node.js library for reading and extracting lyrics from MIDI/KAR karaoke files. This library allows you to easily read MIDI and KAR files, extract lyrics, and access MIDI event data.
npm start to launch the web interfaceNote: This library focuses on reading and extracting data from MIDI/KAR files. For MIDI playback, you'll need to integrate with a MIDI output library (like easymidi or midi) or a software synthesizer. The extracted MIDI events can be used to drive any MIDI playback system.
The library automatically detects and handles multiple text encodings commonly used in MIDI/KAR files:
For lyrics extraction, the library uses a specific fallback chain to ensure maximum compatibility:
This fallback mechanism ensures that lyrics are correctly decoded regardless of the encoding used in the MIDI/KAR file. The order prioritizes Thai encodings since many karaoke files use them, while still supporting UTF-8 for international content.
npm install
Before using the library in a browser, you need to build the browser-compatible files:
npm run build
This will create browser-compatible files in the dist/ directory:
UTF8.jsMIDIEvents.jsMIDIFileHeader.jsMIDIFileTrack.jsTextEncoding.jsMIDIFile.jskarfiletis.jsThese files can be included directly in your HTML using <script> tags.
To start the web-based karaoke player interface:
npm start
This will start a local HTTP server at http://localhost:3000/ where you can:
You can change the port by setting the PORT environment variable:
PORT=8080 npm start
const karaoke = require('./lib');
// Read a KAR file and get lyrics
karaoke.readFile('path/to/file.kar', (err, karFile) => {
if (err) {
console.error('Error:', err);
return;
}
// Get lyrics with timing
const lyrics = karFile.getLyrics();
lyrics.forEach(line => {
console.log(`[${Math.round(line.time / 1000)}s] ${line.text}`);
});
});
const fs = require('fs');
const karaoke = require('./lib');
const buffer = fs.readFileSync('path/to/file.kar');
const karFile = karaoke.readBuffer(buffer, 'filename.kar');
const lyrics = karFile.getLyrics();
console.log(lyrics);
karaoke.getInfo('path/to/file.kar', (err, info) => {
if (err) {
console.error('Error:', err);
return;
}
console.log('Format:', info.format);
console.log('Tracks:', info.trackCount);
console.log('Time Division:', info.timeDivision);
});
karaoke.getText('path/to/file.kar', (err, text) => {
if (err) {
console.error('Error:', err);
return;
}
// text is an object with track numbers as keys
for (const track in text) {
console.log(`Track ${track}:`, text[track]);
}
});
karaoke.getEvents('path/to/file.kar', (err, events) => {
if (err) {
console.error('Error:', err);
return;
}
// events is an object with track numbers as keys
for (const track in events) {
console.log(`Track ${track} has ${events[track].length} events`);
}
});
To use the Player in a browser environment, you need to include the required scripts in your HTML file.
First, include all the necessary library files. You can use the built files from dist/ directory or use a bundler:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Karaoke Player</title>
</head>
<body>
<!-- Include library scripts (order matters!) -->
<script src="dist/UTF8.js"></script>
<script src="dist/MIDIEvents.js"></script>
<script src="dist/MIDIFileHeader.js"></script>
<script src="dist/MIDIFileTrack.js"></script>
<script src="dist/TextEncoding.js"></script>
<script src="dist/MIDIFile.js"></script>
<script src="assets/js/midiplayer/MIDIPlayer.js"></script>
<!-- Optional: Include SpessaSynth for better sound quality -->
<script type="importmap">
{
"imports": {
"spessasynth_lib": "https://cdn.jsdelivr.net/npm/spessasynth_lib@latest/dist/index.js",
"spessasynth_core": "https://cdn.jsdelivr.net/npm/spessasynth_core@latest/dist/index.js"
}
}
</script>
<script src="assets/js/midiplayer/SpessaSynthPlayer.js"></script>
</body>
</html>
<body>
<!-- File input for selecting MIDI/KAR files -->
<input type="file" id="fileInput" accept=".mid,.midi,.kar">
<!-- Playback controls -->
<button id="playBtn">Play</button>
<button id="pauseBtn">Pause</button>
<button id="stopBtn">Stop</button>
<!-- Progress bar -->
<input type="range" id="seekBar" min="0" max="100" value="0">
<!-- Time display -->
<span id="currentTime">0:00</span> / <span id="totalTime">0:00</span>
<!-- Lyric display container -->
<div id="lyricContainer"></div>
</body>
<script>
// Create player instance
const player = new MIDIPlayer('fileInput', function(song) {
console.log('Song loaded:', song);
console.log('Duration:', song.duration);
// Update UI when song is loaded
document.getElementById('seekBar').max = song.duration;
document.getElementById('totalTime').textContent = formatTime(song.duration);
// Auto-play (optional)
// player.play();
});
// Update position during playback
player.ontick = function(song, position) {
document.getElementById('seekBar').value = position;
document.getElementById('currentTime').textContent = formatTime(position);
};
// Playback controls
document.getElementById('playBtn').addEventListener('click', () => {
player.play();
});
document.getElementById('pauseBtn').addEventListener('click', () => {
player.pause();
});
document.getElementById('stopBtn').addEventListener('click', () => {
player.stop();
});
// Seek functionality
document.getElementById('seekBar').addEventListener('input', (e) => {
const position = parseFloat(e.target.value);
player.setPosition(position);
});
// Helper function to format time
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
</script>
Here's a complete working example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Karaoke Player</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
#lyricContainer {
margin-top: 20px;
padding: 20px;
background: #f0f0f0;
border-radius: 8px;
min-height: 200px;
text-align: center;
font-size: 24px;
}
.controls {
display: flex;
gap: 10px;
margin: 20px 0;
align-items: center;
}
button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
#seekBar {
flex: 1;
}
</style>
</head>
<body>
<h1>Karaoke Player</h1>
<input type="file" id="fileInput" accept=".mid,.midi,.kar">
<div class="controls">
<button id="playBtn">▶ Play</button>
<button id="pauseBtn">⏸ Pause</button>
<button id="stopBtn">⏹ Stop</button>
<input type="range" id="seekBar" min="0" max="100" value="0">
<span id="timeDisplay">0:00 / 0:00</span>
</div>
<div id="lyricContainer">Select a MIDI/KAR file to start</div>
<!-- Include library scripts -->
<script src="dist/UTF8.js"></script>
<script src="dist/MIDIEvents.js"></script>
<script src="dist/MIDIFileHeader.js"></script>
<script src="dist/MIDIFileTrack.js"></script>
<script src="dist/TextEncoding.js"></script>
<script src="dist/MIDIFile.js"></script>
<script src="assets/js/midiplayer/MIDIPlayer.js"></script>
<script>
// Initialize player
const player = new MIDIPlayer('fileInput', function(song) {
console.log('Song loaded:', song);
document.getElementById('seekBar').max = song.duration;
updateTimeDisplay(0, song.duration);
});
// Update position
player.ontick = function(song, position) {
document.getElementById('seekBar').value = position;
updateTimeDisplay(position, song.duration);
};
// Controls
document.getElementById('playBtn').onclick = () => player.play();
document.getElementById('pauseBtn').onclick = () => player.pause();
document.getElementById('stopBtn').onclick = () => player.stop();
document.getElementById('seekBar').addEventListener('input', (e) => {
player.setPosition(parseFloat(e.target.value));
});
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
function updateTimeDisplay(current, total) {
document.getElementById('timeDisplay').textContent =
`${formatTime(current)} / ${formatTime(total)}`;
}
</script>
</body>
</html>
For better sound quality, you can use SpessaSynth with a SoundFont file:
<!-- Include SpessaSynth -->
<script type="importmap">
{
"imports": {
"spessasynth_lib": "https://cdn.jsdelivr.net/npm/spessasynth_lib@latest/dist/index.js",
"spessasynth_core": "https://cdn.jsdelivr.net/npm/spessasynth_core@latest/dist/index.js"
}
}
</script>
<script src="assets/js/midiplayer/SpessaSynthPlayer.js"></script>
<script>
// Initialize SpessaSynth
const AudioContextFunc = window.AudioContext || window.webkitAudioContext;
const audioContext = new AudioContextFunc();
const spessaPlayer = new SpessaSynthPlayer(audioContext);
// Load SoundFont (SF2/SF3/DLS format)
const soundfontFile = await fetch('path/to/soundfont.sf3').then(r => r.arrayBuffer());
await spessaPlayer.initialize('path/to/spessasynth_processor.min.js');
await spessaPlayer.loadSoundFont(soundfontFile, 'main');
// Create player and set soundfont engine
const player = new MIDIPlayer('fileInput', function(song) {
console.log('Song loaded');
});
player.setSoundfontEngine('spessasynth', spessaPlayer);
</script>
Note:
If you're using a modern bundler, you can import the library directly:
// Using ES modules
import { KarFile, MIDIFile, Player } from 'karaoke-player';
// Or using CommonJS
const { KarFile, MIDIFile, Player } = require('karaoke-player');
Important: The Player class is browser-only and requires the MIDIPlayer script to be available globally. You'll need to:
assets/js/midiplayer/MIDIPlayer.js to your projectwindow.MIDIPlayer is available before using PlayerExample with Vite:
// main.js
import { Player } from 'karaoke-player';
import './assets/js/midiplayer/MIDIPlayer.js'; // Make MIDIPlayer available globally
// Now you can use Player
const player = new Player('fileInput', (song) => {
console.log('Song loaded:', song);
});
Or with webpack:
// webpack.config.js
module.exports = {
// ... other config
plugins: [
new webpack.ProvidePlugin({
MIDIPlayer: path.resolve(__dirname, 'assets/js/midiplayer/MIDIPlayer.js')
})
]
};
Player (Browser-only)Browser-only MIDI player class that uses Web Audio API for playback. Requires browser environment.
Note: This class is browser-only and will throw an error if used in Node.js. For Node.js, use KarFile and MIDIFile classes directly.
// Browser usage only
const { Player } = require('karaoke-player');
// Create player instance
const player = new Player('fileInputId', (song) => {
console.log('Song loaded:', song);
console.log('Duration:', song.duration);
});
// Play the loaded song
player.play();
// Pause playback
player.pause();
// Stop playback
player.stop();
// Get current position
const position = player.getPosition();
// Set position (seek)
player.setPosition(5.5); // Seek to 5.5 seconds
// Set soundfont engine (SpessaSynth)
player.setSoundfontEngine('spessasynth', spessaSynthInstance);
Methods:
play() - Start or resume playbackpause() - Pause playbackstop() - Stop playbackgetPosition() - Get current playback position in secondssetPosition(position) - Seek to specific positionsetSoundfontEngine(engine, instance) - Set soundfont engine (SpessaSynth)openFile(fileObj) - Open MIDI file from ArrayBufferhandleFileSelect(event) - Handle file input change eventProperties:
currentPosition - Current playback positionduration - Song durationstate - Current state: 'stopped', 'playing', or 'paused'onload - Callback when song is loadedontick - Callback for position updatesKarFileMain class for reading and parsing KAR/MIDI files.
Methods:
readFile(filePath, callback) - Read a file from diskreadBuffer(buffer) - Read from a buffergetLyrics() - Get formatted lyrics with timing (auto-detects encoding)getText() - Get raw text from all tracks (auto-detects encoding)readEvents() - Get all MIDI events organized by trackMIDIFileLow-level MIDI file parser.
Methods:
getLyrics() - Get lyrics from meta events (auto-detects encoding)getMidiEvents() - Get all MIDI eventsgetEvents(type, subtype) - Get filtered eventsgetTrackEvents(index) - Get events from a specific trackTextEncodingText encoding utilities for detecting and decoding various encodings.
Methods:
detectEncoding(bytes, byteOffset, byteLength) - Detect encoding from bytesdecodeString(bytes, byteOffset, byteLength, encoding) - Decode bytes with specified encodingautoDecode(bytes, byteOffset, byteLength) - Auto-detect and decodedecodeWithFallback(bytes, byteOffset, byteLength) - Decode with fallback chain (TIS-620 → Windows-874 → UTF-8)decodeTIS620(buffer) - Decode TIS-620 encodingdecodeWindows874(buffer) - Decode Windows-874 encodingSupported encodings: 'utf8', 'tis620', 'windows874', 'cp874', 'latin1'
Note: The decodeWithFallback() method is used automatically for lyrics decoding in MIDI/KAR files.
readFile(filePath, callback) - Read a KAR/MIDI filereadBuffer(buffer, fileName) - Read from a buffergetLyrics(filePath, callback) - Get lyrics directlygetText(filePath, callback) - Get raw text directlygetEvents(filePath, callback) - Get events directlygetInfo(filePath, callback) - Get file informationSee the examples/ directory for more usage examples:
basic.js - Basic lyrics reading exampleread-buffer.js - Reading from buffer exampleget-info.js - Getting file information exampleRun examples:
node examples/basic.js path/to/file.kar
The getLyrics() method returns an array of lyric lines with the following structure:
[
{
time: 0, // Time in milliseconds
text: "Hello", // Lyric text
track: 0, // Track number
parts: [ // Word-by-word breakdown
{ time: 0, text: "Hello" }
]
},
// ...
]
MIT
This library is based on the web-based karaoke player project. The core MIDI parsing code has been adapted for Node.js use.
FAQs
A Node.js library for reading and playing MIDI/KAR karaoke files with multi-encoding support (UTF-8, TIS-620, Windows-874). Includes browser-only MIDI player with Web Audio API support.
The npm package karaoke-player receives a total of 4 weekly downloads. As such, karaoke-player popularity was classified as not popular.
We found that karaoke-player 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
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.