npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@karaplay/file-coder

v1.5.6

Published

A comprehensive library for encoding/decoding karaoke files (.emk, .kar, MIDI) with Next.js support. Convert EMK to KAR, read/write karaoke files, full browser and server support.

Readme

@karaplay/file-coder

A comprehensive library for encoding/decoding karaoke files (.emk, .kar, MIDI) with Next.js support.

✅ Thai Support: Thai lyrics are fully supported with proper TIS-620 encoding. All Thai characters are preserved and readable throughout the EMK→KAR conversion process.

Features

  • 🎵 Convert NCN format (.mid + .lyr + .cur) to .kar (MIDI karaoke)
  • 🔓 Decode .emk (Extreme Karaoke) files
  • 📖 Read and validate .kar files
  • NEW: Complete EMK → KAR workflow (one-step conversion)
  • 🌐 Full TypeScript support
  • ⚛️ Next.js compatible (both client and server side)
  • 🌐 NEW: Browser-compatible API for client-side processing
  • 🇹🇭 Thai language support (TIS-620 encoding)
  • 🎯 v1.5.0: Accurate tempo handling for ZXIO format (1.24x ratio)
  • ⏱️ NEW: Tempo validation and duration accuracy
  • FIXED v1.3.2: Client-side EMK decoder now works correctly!
  • ✅ 131 tests - 100% pass rate (including integration tests with real files)

Installation

npm install @karaplay/file-coder

Quick Start

Browser/Client-Side (Next.js) 🌐 NEW

For client-side processing in Next.js or browser environments:

'use client';

import { convertEmkFileToKar } from '@karaplay/file-coder/client';

function MyComponent() {
  const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;
    
    const result = await convertEmkFileToKar(file, { autoDownload: true });
    console.log('Converted:', result.metadata.title);
  };
  
  return <input type="file" accept=".emk" onChange={handleFileUpload} />;
}

📚 Full Browser API Documentation →

Server-Side (Node.js)

Usage

EMK to KAR Workflow (Complete Pipeline) ⭐ NEW

Convert EMK files directly to KAR in one step:

import { convertEmkToKar } from '@karaplay/file-coder';

const result = convertEmkToKar({
  inputEmk: 'songs/song.emk',
  outputKar: 'output/song.kar'
});

console.log(`Title: ${result.metadata.title}`);
console.log(`Artist: ${result.metadata.artist}`);
console.log(`Success: ${result.success}`);
// Thai text is readable! ✅

Batch conversion:

import { convertEmkToKarBatch } from '@karaplay/file-coder';

const results = convertEmkToKarBatch(
  ['song1.emk', 'song2.emk', 'song3.emk'],
  'output/kar'
);

See EMK_TO_KAR_WORKFLOW.md for complete documentation.


⏱️ Tempo Handling & Duration Accuracy

Understanding EMK Tempo Conversion

EMK files use non-standard MIDI timing that requires tempo adjustment during conversion. This library automatically handles tempo correction based on the EMK format:

ZXIO Format (v1.5.0+)

Original EMK:  64 BPM  →  Converted KAR: 79 BPM
Tempo Ratio:   1.24x   (ticksPerBeat / 77.42)
Duration:      4:43    ✅ Accurate!

MThd Format

Tempo Ratio:   4x, 8x, or 20x  (auto-detected)

Validating Conversion Accuracy

Use @tonejs/midi to verify tempo and duration:

import { Midi } from '@tonejs/midi';
import { convertEmkToKar } from '@karaplay/file-coder';
import * as fs from 'fs';

// Convert EMK to KAR
const result = convertEmkToKar({
  inputEmk: 'song.emk',
  outputKar: 'song.kar'
});

// Validate with Tone.js
const karBuffer = fs.readFileSync('song.kar');
const midi = new Midi(karBuffer);

console.log('✅ Validation Results:');
console.log(`   Tempo: ${midi.header.tempos[0].bpm.toFixed(2)} BPM`);
console.log(`   Duration: ${(midi.duration / 60).toFixed(2)} minutes`);
console.log(`   PPQ: ${midi.header.ppq}`);
console.log(`   Tracks: ${midi.tracks.length}`);

🔧 Troubleshooting Tempo Issues

Problem: Converted KAR plays too fast or too slow
Solution: The library automatically applies correct tempo ratios:

| Format | Original BPM | Ratio | Converted BPM | Status | |--------|--------------|-------|---------------|--------| | ZXIO | 64 BPM | 1.24x | 79 BPM | ✅ v1.5.0+ | | MThd | 160 BPM | 4x | 640 BPM | ✅ Auto | | MThd | 142 BPM | 8x | 1136 BPM | ✅ Auto |

Important Notes:

  1. Tempo is automatically corrected - no manual adjustment needed
  2. Use @tonejs/midi for accurate duration - it handles tempo events correctly
  3. ⚠️ Don't use karaoke-player's getTickResolution() - it can give incorrect timing
  4. Duration decrease is normal - faster tempo = shorter playback time

Best Practices for MIDI Playback

When implementing a player for converted KAR files:

import { Midi } from '@tonejs/midi';

// ✅ CORRECT: Use Tone.js for timing
const midi = new Midi(karBuffer);
const duration = midi.duration; // Accurate!

midi.tracks.forEach(track => {
  track.notes.forEach(note => {
    const time = note.time;     // ✅ Correct time in seconds
    const noteNum = note.midi;  // MIDI note number
    const velocity = note.velocity;
  });
});

// ❌ INCORRECT: Don't manually calculate from ticks
// const time = ticks * tickResolution; // May be wrong!

NCN to KAR Conversion

import { convertNcnToKar } from 'file-coder';

const result = convertNcnToKar({
  inputMidi: 'path/to/song.mid',
  inputLyr: 'path/to/song.lyr',
  inputCur: 'path/to/song.cur',
  outputKar: 'path/to/output.kar',
  appendTitles: true
});

console.log(`Converted: ${result.metadata.title} by ${result.metadata.artist}`);

EMK Decoding (Server-side)

import { decodeEmkServer, parseSongInfoServer } from 'file-coder';
import * as fs from 'fs';

const fileBuffer = fs.readFileSync('path/to/song.emk');
const decoded = decodeEmkServer(fileBuffer);

// Access decoded parts
const midiData = decoded.midi;
const lyricData = decoded.lyric;
const cursorData = decoded.cursor;

// Parse song information
const songInfo = parseSongInfoServer(decoded.songInfo);
console.log(`Title: ${songInfo.TITLE}`);
console.log(`Artist: ${songInfo.ARTIST}`);

EMK Decoding (Client-side/Next.js)

'use client';

import { decodeEmkClient, parseSongInfoClient } from 'file-coder';

function MyComponent() {
  const handleFileUpload = async (file: File) => {
    const arrayBuffer = await file.arrayBuffer();
    const buffer = Buffer.from(arrayBuffer);
    
    const decoded = decodeEmkClient(buffer);
    const songInfo = parseSongInfoClient(decoded.songInfo);
    
    console.log(`Title: ${songInfo.TITLE}`);
  };
  
  return <input type="file" onChange={(e) => handleFileUpload(e.target.files[0])} />;
}

API Reference

NCN to KAR Functions

  • convertNcnToKar(options) - Main conversion function
  • parseLyricFile(filePath) - Parse .lyr file for metadata
  • buildKaraokeTrack(metadata, cursorBuffer, ticksPerBeat) - Build karaoke track with timing
  • buildMetadataTracks(metadata) - Build metadata tracks
  • CursorReader - Class for reading cursor timing data

EMK to KAR Workflow Functions ⭐ NEW

  • convertEmkToKar(options) - Complete pipeline: EMK → KAR (one step!)
  • convertEmkToKarBatch(emkFiles, outputDir, options?) - Batch convert multiple files
  • validateThaiLyricReadability(lyricBuffer) - Validate Thai text readability

KAR File Reader Functions

  • readKarFile(filePath) - Read and parse .kar file
  • validateKarFile(filePath) - Validate .kar file structure
  • extractLyricsFromKar(filePath) - Extract lyrics from .kar file

EMK Decoder Functions

Server-side

  • decodeEmkServer(fileBuffer) - Decode .emk file (Node.js)
  • parseSongInfoServer(songInfoBuffer) - Parse song metadata
  • xorDecryptServer(data) - XOR decryption utility
  • looksLikeTextServer(buffer) - Text detection utility

Client-side

  • decodeEmkClient(fileBuffer) - Decode .emk file (Browser/Next.js)
  • parseSongInfoClient(songInfoBuffer) - Parse song metadata
  • xorDecryptClient(data) - XOR decryption utility
  • looksLikeTextClient(buffer) - Text detection utility

Development

Install Dependencies

npm install

Build

npm run build

Run Tests

npm test

Test Coverage

npm run test:coverage

Demo Server 🎤

A full-featured web demo with MIDI player and synchronized karaoke lyrics:

# Start demo server
npm run demo

# Stop demo server
npm run stop

# Restart demo server (kills old instance and starts new)
npm run restart

Open http://localhost:3000/demo-simple.html to access the demo.

Features:

  • 🎵 High-quality soundfont audio (GeneralUserGS)
  • 📝 Real-time synchronized karaoke lyrics
  • 🎨 Beautiful UI with lyric highlighting
  • 🇹🇭 Full Thai language support
  • 📊 EMK vs KAR comparison view
  • ⏱️ Accurate tempo and duration conversion

Requirements

  • Node.js >= 16
  • TypeScript >= 5.0
  • Next.js >= 13.0 (optional, for client-side features)

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Changelog

v1.3.2 (Latest)

🔧 Critical Fix: Client-Side EMK Decoder

  • Fixed: Client-side EMK decoder was failing with "Invalid EMK structure: expected at least 3 zlib blocks, found 0"
  • Root Cause: Missing ZLIB_SECOND_BYTES validation check in client-decoder (was only checking first byte 0x78)
  • Solution: Added proper zlib header validation (both bytes) and fallback to Node.js zlib when available
  • Added: 12 comprehensive client-side decoder tests
  • Result: Client-side EMK decoding now works in both Node.js (tests/SSR) and browser environments

Test results:

// All EMK files decode successfully on client-side ✅
// 131/131 tests passing (was 119 in v1.3.1) ✅
// Client and server decoders produce identical results ✅

v1.3.1

🔧 Critical Fix: Marker Lines Preservation

  • Fixed: Marker lines (e.g., "...... Intro ......", "....ดนตรี....") were being incorrectly filtered out
  • Improved: Now includes ALL lines that have timing data in cursor file
  • Smart Handling: Stops processing when cursor runs out of timing data
  • Result: Complete preservation of songs with intro/solo/outro markers

What was fixed: v1.3.0 introduced marker filtering that removed lines like "...... Intro ......" even when the cursor file had timing data for them. This caused songs like "Move On แบบใด" to be missing marker lines. v1.3.1 removes the filtering and trusts the cursor file - if timing exists, the line is included.

Test results:

// Z2510001: ✅ Has "....ดนตรี...." + complete lyrics
// Z2510006: ✅ Has "...... Intro ......" + complete lyrics  
// 119/119 tests passing ✅

v1.3.0

🔧 Critical Fix: Beginning Lyrics Preservation

  • Fixed: Beginning lyrics were being cut off during EMK to KAR and NCN to KAR conversion
  • Improved: Smart detection and skipping of instrumental intro markers (e.g., "....ดนตรี....")
  • Added: 6 comprehensive tests to verify beginning lyrics preservation
  • Verified: All 119 tests passing, ensuring complete lyric integrity from EMK decode to KAR output

What was fixed: Previously, the conversion process was double-skipping the first 4 lines of lyrics, causing the actual beginning of songs to be missing from KAR files. This version correctly preserves all lyrics from the beginning while intelligently filtering out non-lyrical markers.

Comparison test:

// EMK decoded lyrics vs KAR output: 100% match
// First lyric: "ท่องเที่ยวมาแล้วแทบทั่วเมืองไทย" ✅
// Last lyric: "กับยุพิน ที่เมืองพระรถ..." ✅

v1.2.0

  • Added Thai encoding tests (6 new tests)
  • Verified TIS-620 encoding preservation
  • Updated README with encoding clarification

v1.1.1

  • Documentation update for Thai encoding in KAR files
  • No code changes

v1.0.0

  • Initial release
  • Full browser and server support
  • EMK decoding and NCN to KAR conversion
  • Thai language support (TIS-620)