How to repack Kontakt libraries

Discussion in 'Kontakt' started by pyrolysis, Nov 15, 2024 at 2:46 PM.

Tags:
  1. pyrolysis

    pyrolysis Newbie

    Joined:
    Jan 10, 2018
    Messages:
    3
    Likes Received:
    1
    This is a wall of text write-up for my future self and anybody else who's interested in details. I came up with a routine that works when repacking several handy libs that were too bulky for own use. It benefits my workflow to lighten them up several times without tradeoffs.

    It's not rocket science but a hit and miss. I convert 96k and 24-bit samples where justified and guaranteed to not be perceptible. In some multimic libs I keep the mics I will need in future, including usable "mix", and remove far mics I never needed. Getting rid of wet mics because you can reproduce them in theory with "close" and ext verb can ruin the soundstage and leave you with less usable lib. Messing with a library compulsively just destroys it and wastes your precious time. It's a good hobby to keep you busy, but you could make music instead, you know.

    This breaks some EULA and requires patched/unlocked Kontakt to process and use a lib. The ethical side is up to you and depends on whether you own software you use. Avoid this if this may cause problems to you.


    WHY THE FUSS

    The samples inside protected libs that are made for Kontakt Player cannot be freely accessed. If you want to remove or modify them, you have to dig into encrypted NKX containers that contain compressed NCW samples. This is still possible with full patched K version that can open this lib, or to some extent with INNKX.

    Real NKX files can't be created by regular users and non-Player libs. This often results in tons of samples in Samples/ folder that may be slow to handle. The workaround is to create NKR containers and optionally rename them to NKX (aka faux NKX).

    The goal is that a library can be repacked to lower RAM and disk footprint or changed somehow:

    - Fix the samples or the way they are used

    - Remove mics or instruments you never use

    - Convert sample rate and bit depth

    - Compress and pack samples to container files

    The estimates are that 24-bit WAV->NCW results in a 30% reduction on average. WAV 24->16 conversion results in 33% size reduction. But 16-bit NCW compresses better, and NCW 24->16 is roughly 50%, due to the lost audiophilic warmth.


    REQUIREMENTS

    Windows, because macOS K patches are less universal on average

    Node.js LTS version (https://nodejs.org/en/download) to run helper utils.

    File manager with mask selection, batch renaming, advanced search and file list features. Total Commander will work.

    Any FM that can create multiple hardlinks (not to be confused with other link types). Explorer++, Total Commander with hardlink plugin, etc. This allows creating a full backup of a folder immediately that cannot be damaged by most (not all) file operations. This can save many hours and disk space.

    (Optional) Total Commander with INNKX plugin. Can easily extract samples and other resources from NKX and NKR containers.

    (Optional) Any editor that does batch sample conversions decently, e.g Izotope RX.

    K 4, used to batch convert between WAV and NCW, but it can't create NCR/NCX. Any 4.x 64-bit version will probably work. It isn't really need to be installed for this purpose, as long as the registration is ok, Kontakt.exe can be just extracted from an installer.

    K 5, 6, ... where the repacked lib is supposed to be used. If you do this not just for yourself, try the lowest compatible version to not increase the requirements unreasonably.


    WORKFLOWS

    Choose a method depending on whether you may need unused samples, could be a bug in a lib you may want to fix later by remapping them.


    Without encrypted NKX:

    1. Make sure samples have unique names and won't interfere with flat folder structure, rename them and batch resave if there are dupe names until all are unique, move all samples to one folder

    2. Convert NCW to WAV and back if samples need to be processed

    3. Batch resave and resolve WAV/NCW extension change if there was one

    4. Split NCWs among faux NKX containers in 2GB groups

    5. Remove original NCW samples s and place faux NCXs to Samples/

    6. Batch resave insts and multis and resolve NCW inside faux NKX


    With encrypted NKX:

    1. Extract NCW/WAV/AIF files from NKX with INNKX if possible

    (OR TO DITCH UNUSED)

    1a. Use check-filenames util to check that filenames inside NKX are unique, make a list of dupe names, ditch the job if there aretoo many

    1b. Create a multi and merge all lib multis and insts to it, skip to the next method if you hit the limit of 64 insts per multi or K doesn't have enough resources to handle them

    1c. Save merged multi with collected samples, choose (de)compress them to be the same as inside NKX

    1d. Check against a list of filenames, if you used a merged multi to save samples, dupe names could be autorenamed

    (OR TO KEEP UNUSED)

    1a. Use check-filenames util to check that filenames inside NKX are unique, make a list of dupe names, ditch the job if there are too many

    1b. Generate dummy samples with the same names and NCW/WAV/AIF format as the ones inside NKX

    1c. Create and save dummy multi that contains all dummy samples

    1d. Remove dummy samples, batch resave dummy multi and resolve missing samples with the ones from NKX

    1e. Save dummy multi with collected samples, (de)compress them to be the same as inside NKX

    2. Convert NCW to WAV and back if samples need to be processed

    4. Split NCWs among faux NKX containers in 2GB groups

    5. Remove original NCX containers and place faux NCXs to Samples/

    6. Batch resave insts and multis and resolve NCWs inside faux NKXs


    EXTRACT FROM NKX WITH INNKX

    NKX is processed as an archive. If it fails to work you can use it to see filenames but can't extract them.


    EXTRACT FROM NKX WITH KONTAKT

    "Samples Missing" dialog should appear, choose "Resolve Manually - Browse for folder", make sure "resolve all possible" is selected, don't select anything else to not complicate the process.

    "# files could not be found" count decreases while the samples are found. That it disappears itself means that all of them are found.

    Then "Skip missing" for deleted mic files.


    FAUX NKX

    Containers can be named arbitrarily and have NCR extension - or NKX if you want them to look classy. If original ones are called lib_1.nkx, etc repacked ones can be lib_repacked_01.nkx. A lib that has samples in NKX that are used by scripts may require putting the repacked samples to the exact location so they can be found.

    NCR/NKX can be any size up to 2GB (2147483648 B, 1048576 KB), target it around 2B bytes to make it safe and round, sort NCW to 01, ... folders beforehand.

    "Edit Mode", open "Instrument Options", "Create" for Resource Container. It will create Resources/ at the specified path and dummy NCR with the specified name.

    You can put the samples in Resources/ir_samples/ (not only IR) and ignore the rest. There should be no subfolders in ir_samples, it's important to keep filenames unique if you want to put the samples back into containers. To create multiple NCR files, clear the contents of ir_samples, enter a new name and "Create".


    BATCH CONVERT WAV/NCW

    In order to do this, add WAV or NCW to a new instrument or several ones and save as NKI/NKM + samples.

    This is possible in K 4, it's fully compatible with compressed NCW. Higher versions don't allow adding unlimited samples simultaneously.

    Add a new instrument to a multi. Enter "Edit Mode", switch to "Mapping Editor", select all or a batch of samples in the browser and drag them to the area, it should show grey placeholders before becoming unresponsive, which become yellow when completed

    10000+ samples can be processed at once this way. If you find some threshold above which K freezes or runs out of RAM, try 2, 3, etc batches.

    "Save multi as", select "Patch + samples", check "Compress samples" for WAV to NCW conversion, uncheck for the opposite. Names and paths don't matter but make sure they won't accidentally mix with lib's own files.


    SAMPLE RATE AND BIT DEPTH

    Use common sense and quality tools to not spoil a lib. Izotope RX is capable of batch processing and does both things well enough. Avoid processing IR samples.

    Downsampling from 48k won't do any good but 192k or 96k likely will, beware of aliasing because sample rate converters aren't equal.

    The usual settings for bitdepth reduction would be medium to light dithering, normal noiseshaping. Avoid it for samples that require heavy dynamic processing like electric guitars. Also when the samples are not normalized, always check this first; start with different low velocity samples.


    SAMPLE REMOVAL

    A properly designed lib has unique sample filenames with folder structure that can be flattened. This happens when samples are extracted using helper utils, and there's no info on folder names. Most times, samples are named in a unified way, and it's possible to isolate the things you don't need with file masks, like *_F_* for far mics.

    After that, the presets can be updated with batch resave and "Skip missing".


    PRO TIPS

    Use a fast SSD, this will prevent disk from being a bottleneck. Keep eye on free disk space and RAM. There's no such thing as enough RAM, it can be occupied during K routines, at least to the size of decompressed samples in a batch.

    Avoid "Write absolute paths" in "Load/import" K options, otherwise you'll need to resave a lib on another machine. Try moving a repack and temporary files to new locations before testing to make sure it's not dependent on file paths.

    Keep original instruments and multis in an archive or separate folder to protect them from "Batch resave".

    Make hardlink backups with meaningful names at every step if you don't want to redo it when things go wrong.

    Use standalone K instead of the plugin to exclude DAW from the equation. VST minihost with the plugin can be fine too.

    Use a different folder for temporary files so they're not affected by "Batch Resave" in the lib folder.

    Instruments and multis can be backward compatible between K "patch" versions (5.5.0, 5.5.1, ...), in case you don't have the exact version at hand.

    K version that a lib requires can be seen in info panel when selecting an instrument, scroll it. It's possible that parts of a lib were saved with different versions but it's rare.

    If you're doing multiple jobs at once and need to run several copies of the same K version, make physical copies of K standalone folder to make them run cleaner.

    Have as many standalone K versions as practical if a version or the way it's patched makes a difference.

    Use K's file browser to drag files inside it, unless that doesn't work for some reason. OS drag'n'drop can cause issues that didn't exist. The hotkeys to select multiple files (Shift/Ctrl+click, etc) are universal.

    If something's taking time, wait, then wait more. Some operations can take up to an hour even on a very fast PC and still be successful, especially with old K versions. Once you know how long it usually takes and it's still too long, try interacting with a window, it can incorrectly show as unresponsive. Otherwise kill K process, rinse and repeat.

    If a protected lib was added with an installer, this can affect how it works on other machines and K versions that didn't go through the same process. If you want it to work for you or anyone else, test it on K installations that weren't affected by the installer. Portable versions are super helpful.


    HELPER SCRIPTS

    Need Node.js to be installed. Put them on the desktop or elsewhere. Drag and drop NKC files to .cmd to run.

    check-filenames.cmd
    Code:
    rem check-filenames.cmd
    @echo off
    cd /d "%~dp0"
    node check-filenames.js %*
    pause
    check-filenames.js
    Code:
    const fs = require('fs');
    
    const args = process.argv.slice(2);
    const resourceFilenames = args;
    
    if (!resourceFilenames.length) {
        console.log('check-filenames resource-name.nkr|resource-name.nkc [resource-name2.nkr|resource-name2.nkc]');
        process.exit();
    }
    
    const allWaveFiles = [];
    
    for (const resourceFilename of resourceFilenames) {
        let resource = fs.readFileSync(resourceFilename, { encoding: 'utf8' });
        resource = resource.replace(/([\w\-_ ~!@#$%^&*()+=[\]{}',.])\x00/g, '$1');
        const waveFilenames = resource.match(/[\w\-_ ~!@#$%^&*()+=[\]{}',.]+\.(?:wav|aiff?|ncw)/ig);
        allWaveFiles.push(...waveFilenames.map(file => ({ file, resource: resourceFilename })));
    }
    
    const sortedWaveFiles = allWaveFiles.sort(({ file: aFile }, { file: bFile }) => aFile.localeCompare(bFile));
    
    console.log(sortedWaveFiles.map(({ file }) => file).join('\n'));
    
    console.log();
    console.log();
    
    const nonUniqueWaveFiles = allWaveFiles
    .map(({ file, resource }, index)  => ({ file: file.toLowerCase(), resource, index }))
    .filter(({ file }, index, arr) => arr.filter(({ file: dupeFile }) => dupeFile === file).length > 1);
    
    for (const { index } of nonUniqueWaveFiles) {
        const { file, resource } = allWaveFiles[index];
        console.log('!  ' + file + '  (' + resource + ')');
    }
    extract-dummy-samples.cmd
    Code:
    rem extract-dummy-samples.cmd
    @echo off
    cd /d "%~dp0"
    node extract-dummy-samples.js %*
    pause
    extract-dummy-samples.js
    Code:
    const fs = require('fs');
    
    const args = process.argv.slice(2);
    const resourceFilenames = args;
    
    if (!resourceFilenames.length) {
        console.log('check-filenames resource-name.nkr|resource-name.nkc [resource-name2.nkr|resource-name2.nkc]');
        process.exit();
    }
    
    const allWaveFiles = [];
    
    for (const resourceFilename of resourceFilenames) {
        let resource = fs.readFileSync(resourceFilename, { encoding: 'utf8' });
        resource = resource.replace(/([\w\-_ ~!@#$%^&*()+=[\]{}',.])\x00/g, '$1');
        const waveFilenames = resource.match(/[\w\-_ ~!@#$%^&*()+=[\]{}',.]+\.(?:wav|aiff?|ncw)/ig);
        allWaveFiles.push(...waveFilenames.map(file => ({ file, resource: resourceFilename })));
    }
    
    const sortedWaveFiles = allWaveFiles.sort(({ file: aFile }, { file: bFile }) => aFile.localeCompare(bFile));
    
    console.log(sortedWaveFiles.map(({ file }) => file).join('\n'));
    
    console.log();
    console.log();
    
    const nonUniqueWaveFiles = allWaveFiles
    .map(({ file, resource }, index)  => ({ file: file.toLowerCase(), resource, index }))
    .filter(({ file }, index, arr) => arr.filter(({ file: dupeFile }) => dupeFile === file).length > 1);
    
    for (const { index } of nonUniqueWaveFiles) {
        const { file, resource } = allWaveFiles[index];
        console.log('!  ' + file + '  (' + resource + ')');
    }
     
    Last edited: Nov 15, 2024 at 3:40 PM
  2.  
Loading...
Loading...