Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

bitwig-multisamplegen

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bitwig-multisamplegen - npm Package Compare versions

Comparing version 1.1.0 to 1.2.0

314

dist/index.js

@@ -227,3 +227,4 @@ #!/usr/bin/env node

const ALLOWED_EXTENSIONS = ['wav', 'aif', 'mp3', 'ogg'];
const COMPRESSION_TYPE = 'DEFLATE';
const DEFAULT_COMPRESSION_TYPE = 'DEFLATE';
const DEFAULT_SELECTION_VALUE = DEFAULT_VELOCITY;
const ALL_NOTES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];

@@ -239,2 +240,10 @@

const getSelection = parsed => {
if (parsed.selection === undefined) {
return DEFAULT_SELECTION_VALUE;
}
return parsed.selection;
};
const getNoteNumber = parsed => {

@@ -257,3 +266,3 @@ const note = parsed.note.toUpperCase();

const getFileNamesByNoteNumber = filenames => {
const getFileNamesByNoteNumber = (filenames, valueMode) => {
const result = {};

@@ -271,3 +280,3 @@ filenames.forEach(parsed => {

const n = Number(k);
result[n] = ramda.sortBy(x => getVelocity(x), result[n]);
result[n] = ramda.sortBy(x => Number(x.initialNumber), result[n]);
});

@@ -277,21 +286,66 @@ return result;

const getSampleFromParsed = parsed => {
const getSampleFromParsed = (allParsed, valueMode) => {
let selectionMin = 0;
let velocityMin = 0;
return parsed.reduce((acc, parsed) => {
const velocityMax = getVelocity(parsed);
const key = getNoteNumber(parsed);
const sample = {
name: parsed.fullname,
key,
lowKey: key,
highKey: key,
velocityMin,
velocityMax
};
velocityMin = velocityMax + 1;
return [...acc, sample];
const grouped = ramda.groupBy(filename => `${filename.note.toLowerCase()}${filename.octave}-${filename.initialNumber}`, allParsed);
const groupedKeys = Object.keys(grouped);
const maxIndex = groupedKeys.length - 1;
return groupedKeys.reduce((acc, k, index) => {
const parsedFileNames = grouped[k];
if (!parsedFileNames.length) {
return acc;
}
const firstParsed = parsedFileNames[0];
const velocityMax = getVelocity(firstParsed);
const selectionMax = getSelection(firstParsed);
let overrideVelocityMax = false;
let overrideSelectionMax = false;
if (valueMode === 'Velocity' && index === maxIndex) {
overrideVelocityMax = true;
} else if (valueMode === 'Selection' && index === maxIndex) {
overrideSelectionMax = true;
}
const samples = parsedFileNames.map(parsed => {
const key = getNoteNumber(parsed);
const sample = {
name: parsed.fullname,
key,
lowKey: key,
highKey: key,
velocityMin: valueMode === 'Velocity' ? velocityMin : 0,
velocityMax: valueMode === 'Velocity' ? velocityMax : 127,
selectionMin: valueMode === 'Selection' ? selectionMin : 0,
selectionMax: valueMode === 'Selection' ? selectionMax : 127,
prefix: parsed.prefix,
postfix: parsed.postfix,
extension: parsed.extension,
initialNumber: parsed.initialNumber
};
if (overrideSelectionMax) {
sample.selectionMax = 127;
}
if (overrideVelocityMax) {
sample.velocityMax = 127;
}
return sample;
});
if (valueMode === 'Velocity') {
velocityMin = velocityMax + 1;
} else if (valueMode === 'Selection') {
selectionMin = selectionMax + 1;
}
return [...acc, ...samples];
}, []);
};
const parseFileName = filename => {
const parseFileName = (filename, valueMode) => {
const res = filename.match(/^(.*)([abcdefgABCDEFG]#?)([0-9])-?(\d{1,3})?(.*)\.(.*)/);

@@ -308,5 +362,7 @@

octave: Number(res[3]),
velocity: Number(res[4]),
velocity: valueMode === 'Velocity' ? Number(res[4]) : 127,
selection: valueMode === 'Selection' ? Number(res[4]) : 127,
postfix: res[5],
extension: res[6]
extension: res[6],
initialNumber: res[4] || ''
};

@@ -337,4 +393,4 @@

const getAllSamples = filenames => {
const parsedFilenames = filenames.map(parseFileName).filter(parsed => !!parsed);
const getAllSamples = (filenames, valueMode) => {
const parsedFilenames = filenames.map(filename => parseFileName(filename, valueMode)).filter(parsed => !!parsed);
const filenamesByNote = getFileNamesByNoteNumber(parsedFilenames);

@@ -344,3 +400,3 @@ let result = [];

const n = Number(k);
result = [...result, ...getSampleFromParsed(filenamesByNote[n])];
result = [...result, ...getSampleFromParsed(filenamesByNote[n], valueMode)];
});

@@ -374,8 +430,21 @@ return result;

const generateSampleXml = sample => {
const generateSampleXml = (sample, keyfade, valueMode, valueFade, spreadSelectionsFadeValue, roundRobinEnabled) => {
const selectionFade = spreadSelectionsFadeValue === undefined ? valueFade : spreadSelectionsFadeValue;
const keyLowFade = sample.lowKey > 0 ? Math.min(keyfade, Math.abs(sample.key - sample.lowKey)) : 0;
const keyHighFade = sample.highKey < 127 ? Math.min(keyfade, Math.abs(sample.highKey - sample.key)) : 0;
const maximumFadeVelocity = Math.trunc(Math.abs(sample.velocityMax - sample.velocityMin) / 2);
const velocityLowFade = valueMode === 'Velocity' && sample.velocityMin > 0 ? Math.min(valueFade, maximumFadeVelocity) : 0;
const velocityHighFade = valueMode === 'Velocity' && sample.velocityMax < 127 ? Math.min(valueFade, maximumFadeVelocity) : 0;
const maximumFadeSelection = Math.trunc(Math.abs(sample.selectionMax - sample.selectionMin) / 2);
const selectLowFade = valueMode === 'Selection' && sample.selectionMin > 0 ? Math.min(selectionFade, maximumFadeSelection) : 0;
const selectHighFade = valueMode === 'Selection' && sample.selectionMax < 127 ? Math.min(selectionFade, maximumFadeSelection) : 0;
const param1 = sample.key;
const param2 = valueMode === 'Velocity' ? sample.velocityMax : sample.selectionMax;
const param3 = 0;
const zoneLogic = roundRobinEnabled ? 'round-robin' : 'always-play';
return `
<sample file="${sample.name}" gain="0.00" parameter-1="0.0000" parameter-2="0.0000" parameter-3="0.0000" reverse="false" sample-start="0.000" sample-stop="-1" zone-logic="always-play">
<key low="${sample.lowKey}" high="${sample.highKey}" root="${sample.key}" track="1.0000" tune="0.00"/>
<velocity low="${sample.velocityMin}" high="${sample.velocityMax}" />
<select low="0" high="127"/>
<sample file="${sample.name}" gain="0.00" parameter-1="${param1}" parameter-2="${param2}" parameter-3="${param3}" reverse="false" sample-start="0.000" sample-stop="-1" zone-logic="${zoneLogic}">
<key low-fade="${keyLowFade}" high-fade="${keyHighFade}" low="${sample.lowKey}" high="${sample.highKey}" root="${sample.key}" track="1.0000" tune="0.00"/>
<velocity low-fade="${velocityLowFade}" high-fade="${velocityHighFade}" low="${sample.velocityMin}" high="${sample.velocityMax}" />
<select low-fade="${selectLowFade}" high-fade="${selectHighFade}" low="${sample.selectionMin}" high="${sample.selectionMax}"/>
<loop fade="0.0000" mode="off" start="0.000" />

@@ -386,3 +455,3 @@ </sample>

const generateMultiSampleXml = (instrumentName, author, samples) => {
const generateMultiSampleXml = (instrumentName, author, samples, keyfade, valueMode, valueFade, spreadSelectionsFadeValue, roundRobinEnabled) => {
let xmlResult = `<?xml version="1.0" encoding="UTF-8"?>

@@ -397,3 +466,3 @@ <multisample name="${instrumentName}">

samples.forEach(sample => {
xmlResult = xmlResult + generateSampleXml(sample);
xmlResult = xmlResult + generateSampleXml(sample, keyfade, valueMode, valueFade, spreadSelectionsFadeValue, roundRobinEnabled);
});

@@ -434,9 +503,9 @@ xmlResult = xmlResult + '</multisample>';

const transformSamples = allSamples => {
const stretchNotes = (allSamples, valueMode) => {
let result = [];
const groupedSamples = ramda.groupBy(x => String(x.velocityMax), allSamples);
Object.keys(groupedSamples).forEach(velocity => {
groupedSamples[velocity] = ramda.sortBy(sample => sample.key, groupedSamples[velocity]);
const samples = groupedSamples[velocity];
const nextSamples = groupedSamples[velocity].map((sample, index) => {
const groupedSamples = ramda.groupBy(x => String(`${x.prefix}-${x.initialNumber}-${x.postfix}.${x.extension}`), allSamples);
Object.keys(groupedSamples).forEach(k => {
groupedSamples[k] = ramda.sortBy(sample => sample.key, groupedSamples[k]);
const samples = groupedSamples[k];
const nextSamples = groupedSamples[k].map((sample, index) => {
const computedSample = computeLowKey(samples, sample, index);

@@ -450,3 +519,49 @@ return computeHighKey(samples, computedSample, index);

const generateZipFile = function (pathdir, samples, givenPackageName) {
const transformSamples = (givenSamples, keyfade, valueMode, valueFade, spreadSelectionsFadeValue) => {
const allSamples = stretchNotes(givenSamples);
const transformedSamples = allSamples.map(sample => {
return { ...sample,
lowKey: Math.max(0, sample.lowKey - keyfade),
highKey: Math.min(127, sample.highKey + keyfade)
};
}).map(sample => {
if (valueMode === 'Velocity') {
return { ...sample,
velocityMin: Math.max(0, sample.velocityMin - valueFade),
velocityMax: Math.min(127, sample.velocityMax + valueFade)
};
} else if (valueMode === 'Selection') {
return { ...sample,
selectionMin: Math.max(0, sample.selectionMin - valueFade),
selectionMax: Math.min(127, sample.selectionMax + valueFade)
};
}
return sample;
});
if (spreadSelectionsFadeValue !== undefined) {
const resultSamples = [];
const grouped = ramda.groupBy(s => `${s.key}${s.initialNumber}`, transformedSamples);
Object.keys(grouped).forEach(k => {
const group = grouped[k];
if (group.length) {
const ratio = 127 / group.length;
group.forEach((sample, index) => {
const newSample = { ...sample,
selectionMin: Math.ceil(Math.max(0, Math.min(127, index * ratio - spreadSelectionsFadeValue))),
selectionMax: Math.floor(Math.max(0, Math.min(127, (index + 1) * ratio + spreadSelectionsFadeValue)))
};
resultSamples.push(newSample);
});
}
});
return resultSamples;
}
return transformedSamples;
};
const generateZipFile = function (pathdir, samples, givenPackageName, keyfade, compression, valueMode, valueFade, spreadSelectionsFadeValue, roundRobinEnabled) {
try {

@@ -475,4 +590,4 @@ let _exit2 = false;

const zip = new JSZip();
zip.file(MULTISAMPLE_FILE, generateMultiSampleXml(givenPackageName, AUTHOR_NAME, samples), {
compression: COMPRESSION_TYPE
zip.file(MULTISAMPLE_FILE, generateMultiSampleXml(givenPackageName, AUTHOR_NAME, samples, keyfade, valueMode, valueFade, spreadSelectionsFadeValue, roundRobinEnabled), {
compression
});

@@ -486,3 +601,3 @@ const spinner = ora('Copy sample files...').start();

zip.file(sample.name, buffer, {
compression: COMPRESSION_TYPE
compression
});

@@ -499,2 +614,13 @@ });

const getNbDuplicateNotes = samples => {
let counter = 0;
const grouped = ramda.groupBy(s => `${s.key}${s.initialNumber}`, samples);
Object.keys(grouped).forEach(k => {
if (grouped[k].length > 1) {
counter += grouped[k].length;
}
});
return counter;
};
const app = function () {

@@ -510,19 +636,97 @@ try {

const samples = getAllSamples(fileNames);
return Promise.resolve(inquirer.prompt([{
type: 'list',
name: 'valueMode',
message: 'Choose the desired value mode',
default: 'Velocity',
choices: ['Velocity', 'Selection', 'Round-Robin']
}])).then(function (valueModePayload) {
const valueMode = valueModePayload.valueMode;
const samples = getAllSamples(fileNames, valueMode);
if (samples.length === 0) {
ora(`Warning: no valid sample files found in directory`).fail();
return;
}
if (samples.length === 0) {
ora(`Warning: no valid sample files found in directory`).fail();
return;
}
return Promise.resolve(inquirer.prompt([{
type: 'input',
name: 'packageName',
message: 'Multisample Package name',
validate: input => {
return input && input.length > 0;
},
default: ''
}])).then(function (info) {
return Promise.resolve(generateZipFile(pathdir, transformSamples(samples), info.packageName)).then(function () {});
const nbDuplicateNotes = getNbDuplicateNotes(samples);
const inquirerQuestions = [{
type: 'input',
name: 'packageName',
message: 'Multisample Package name',
validate: input => {
return input && input.length > 0;
},
default: ''
}, valueMode !== 'Round-Robin' ? {
type: 'input',
name: 'keyfade',
message: 'Pitch fade value',
default: 0,
validate(value) {
const valid = !isNaN(parseFloat(value));
return valid || 'Please enter a number';
},
filter: value => {
const valid = !isNaN(parseFloat(value));
return valid ? Math.abs(Number(value)) : '';
}
} : null, valueMode !== 'Round-Robin' ? {
type: 'input',
name: 'valueFade',
message: `${valueMode} fade value`,
default: 0,
validate(value) {
const valid = !isNaN(parseFloat(value));
return valid || 'Please enter a number';
},
filter: value => {
const valid = !isNaN(parseFloat(value));
return valid ? Math.abs(Number(value)) : '';
}
} : null, valueMode !== 'Selection' && valueMode !== 'Round-Robin' && nbDuplicateNotes > 0 ? {
type: 'confirm',
name: 'spreadSelection',
message: `${nbDuplicateNotes} duplicate notes found, Would you want to spread samples selection ?`,
default: false
} : null];
return Promise.resolve(inquirer.prompt(inquirerQuestions.filter(x => Boolean(x)))).then(function (info) {
const inquirerQuestions2 = [info.spreadSelection === true && valueMode !== 'Round-Robin' ? {
type: 'input',
name: 'spreadSelectionFade',
message: 'spread selections fade value',
default: 0,
validate(value) {
const valid = !isNaN(parseFloat(value));
return valid || 'Please enter a number';
},
filter: value => {
const valid = !isNaN(parseFloat(value));
return valid ? Math.abs(Number(value)) : '';
}
} : null, {
type: 'confirm',
name: 'compression',
message: 'Enable compression ?',
default: true
}];
return Promise.resolve(inquirer.prompt(inquirerQuestions2.filter(x => Boolean(x)))).then(function (info2) {
const spreadSelectionsFadeValue = info2.spreadSelectionFade;
const compression = info2.compression ? DEFAULT_COMPRESSION_TYPE : undefined;
const roundRobinAutoEnabled = !info2.spreadSelectionFade && !info.keyfade && !info.valueFade;
if (roundRobinAutoEnabled && valueMode !== 'Round-Robin') {
ora('Round-Robin mode automatically enabled (becaause no fades are set)').warn();
}
const roundRobinEnabled = roundRobinAutoEnabled || valueMode === 'Round-Robin';
return Promise.resolve(generateZipFile(pathdir, transformSamples(samples, info.keyfade || 0, valueMode, info.valueFade, spreadSelectionsFadeValue), info.packageName, info.keyfade || 0, compression, valueMode, info.valueFade || 0, spreadSelectionsFadeValue, roundRobinEnabled)).then(function () {});
});
});
});

@@ -529,0 +733,0 @@ });

4

package.json
{
"name": "bitwig-multisamplegen",
"version": "1.1.0",
"version": "1.2.0",
"description": "Generate multisample xml file from samples ",

@@ -31,3 +31,3 @@ "author": "guillaumearm",

"lint:js": "eslint --fix --cache --ext .ts,.tsx ./src",
"lint:prettier": "prettier --check --write \"**/*\" --end-of-line auto",
"lint:prettier": "prettier --check --write \"**/*.{ts, js, json, tsx, jsx}\" --end-of-line auto",
"start": "microbundle-crl watch --no-compress --format cjs",

@@ -34,0 +34,0 @@ "build": "microbundle-crl --no-compress --format cjs",

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc