npm.io
5.1.1 • Published 1 month ago

@uttori/data-tools

Licence
MIT
Version
5.1.1
Deps
1
Size
3.2 MB
Vulns
0
Weekly
0

view on npm npm module downloads Coverage Status Tree-Shaking Support Dependency Count Minified + GZip Minified

Uttori Data Tools

Tools for working with binary data.

  • CRC32 - CRC-32/ISO-HDLC (of hex, compute unsigned) and CRC-32C/Castagnoli (crc32c).
  • DataBuffer - Cursor-based reader and writer for binary formats.
  • DataBitstream - Read a DataBuffer as a stream of bits.
  • Diff / Myers - Myers diff. edits, diff, and hunks compare sequences.
  • ImagePNG - Decode a PNG and encode exact RGBA8, with no quantization.
  • ImageGIF - Decode GIF87a/GIF89a and encode an exact indexed image.
  • GIFLZW - Compress and decompress the GIF LZW variant.
  • RgbaSurface - Mutable straight RGBA8 surface: fill, blit, crop, and bitmap text.
  • BitmapText - 5×7 caption glyphs. measure and drawBitmapText.
  • AudioWAV - Read and write RIFF WAVE and AIFF.
  • AudioMIDI - Read and write Standard MIDI files.
  • SP404PadInfo - Read and encode SP-404/SX/A PAD_INFO.BIN pad records.
  • SP404Pattern - Read SP-404/SX/A patterns and convert between patterns and MIDI.
  • IPS - Build and apply IPS patches, including the truncate extension.

Install

npm install --save @uttori/data-tools

Examples

import { CRC32 } from '@uttori/data-tools';

CRC32.of('The quick brown fox jumps over the lazy dog');
➜ '414FA339'
CRC32.compute('123456789');
➜ 0xCBF43926
CRC32.crc32c('123456789');
➜ 0xE3069283
import { DataBuffer } from '@uttori/data-tools';

const buffer = new DataBuffer([0x34, 0x12]);
buffer.readUInt16(true);
➜ 0x1234
buffer.compare([0x34, 0x12]);
➜ true
import { DataBitstream, DataBuffer } from '@uttori/data-tools';

const bits = new DataBitstream(new DataBuffer([0xfc, 0x08]));
bits.readLSB(4);
➜ 12
import { edits } from '@uttori/data-tools';

// Op: 0 match, 1 delete, 2 insert
edits(['a', 'b', 'c'], ['a', 'x', 'c']).map((edit) => [edit.op, edit.x]);
➜ [[0, 'a'], [1, 'b'], [2, 'x'], [0, 'c']]
import { ImagePNG } from '@uttori/data-tools';

const png = new ImagePNG(ImagePNG.encodeRGBA({
  width: 1,
  height: 1,
  rgba: new Uint8Array([255, 0, 0, 255]),
}));
png.getPixel(0, 0);
➜ [255, 0, 0, 255]
import { ImageGIF } from '@uttori/data-tools';

const gif = new ImageGIF(ImageGIF.encodeIndexed({
  width: 1,
  height: 1,
  indexes: new Uint8Array([0]),
  palette: [[255, 0, 0, 255]],
}));
gif.header;
➜ 'GIF89a'
gif.getPixel(0, 0);
➜ [255, 0, 0, 255]
import { GIFLZW } from '@uttori/data-tools';

const packed = new GIFLZW([1, 2, 2, 3]).compressBytes(2);
[...new GIFLZW(packed).decompressBytes(2)];
➜ [1, 2, 2, 3]
import { BitmapText, RgbaSurface } from '@uttori/data-tools';

const surface = new RgbaSurface(6, 7);
surface.fill([0, 0, 0, 255]).drawText('A', 0, 0, [255, 255, 255, 255]);
surface.rgba[4];
➜ 255
BitmapText.measure('A').width;
➜ 5
import { AudioWAV } from '@uttori/data-tools';

const fmt = AudioWAV.encodeFMT({ channels: 1, sampleRate: 8000, bitsPerSample: 8 });
const data = Buffer.alloc(9);
data.write('data', 0, 4, 'ascii');
data.writeUInt32LE(1, 4);
data[8] = 128;
const body = Buffer.concat([fmt, data]);
const wav = AudioWAV.fromFile(Buffer.concat([
  AudioWAV.encodeHeader({ size: body.length }),
  body,
]));
wav.chunks.find((chunk) => chunk.type === 'format').value.sampleRate;
➜ 8000
import { AudioMIDI } from '@uttori/data-tools';

const midi = AudioMIDI.convertToMidi({
  bpm: 120,
  tracks: [{ notes: [{ midiNote: 60, ticks: 0, velocity: 100, length: 480 }] }],
});
midi.chunks[0].events[1].data;
➜ { note: 60, velocity: 100, length: 480 }
import { DataBuffer, IPS } from '@uttori/data-tools';

const original = new DataBuffer([1, 2, 3, 4]);
const patch = IPS.createIPSFromDataBuffers(original, new DataBuffer([1, 9, 3, 4]));
[...patch.apply(original).data];
➜ [1, 9, 3, 4]

SP-404 Utilities

MIDI note values are numeric, fromMidi() returns a committed DataBuffer with bytes ready to read or save.

It imports PPQ MIDI into 4/4 patterns of at most 64 bars, rejecting unsupported timing, invalid pad mappings, and values that would overflow the device fields.

For the original device, pass { og: true } when parsing and 96, true to fromMidi(), the MKII uses 480 PPQ by default.

import { AudioMIDI, SP404PadInfo, SP404Pattern } from '@uttori/data-tools';

const pad = new SP404PadInfo(SP404PadInfo.encodePad({ volume: 100 })).pads[0];
pad.label;
➜ 'A1'

const midi = AudioMIDI.convertToMidi({
  tracks: [{ notes: [{ midiNote: 60, ticks: 0, velocity: 100, length: 480 }] }],
});
const bytes = SP404Pattern.fromMidi(midi, { 60: 'A1' }, 480);
const pattern = new SP404Pattern(bytes);
pattern.getUsedPads();
➜ ['A1']

See the pad-info API and pattern API for examples and options.

Tree Shaking with ESM Modules

To enable tree-shaking with RollUp, you will likely want to use replace() of @rollup/plugin-replace like the following example to get a clean output:

rollup({
  input: './you-entry-file.js',
  plugins: [
    replace({
      'process.env.UTTORI_DATA_DEBUG': 'false',
    }),
  ],
});

Tests

To run the test suite, first install the dependencies, then run npm test:

npm install
npm test
DEBUG=Uttori* npm test

Contributors

License

Keywords