mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-22 01:42:47 +00:00
fix: use proper phrase decompress algorithm
[deploy]
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
name: Action Codes
|
name: Action Codes
|
||||||
description: 10-bit action codes 0x00-0x1F
|
description: Invalid action codes
|
||||||
actions:
|
actions:
|
||||||
0x00:
|
0x00:
|
||||||
id: "0x00"
|
id: "0x00"
|
||||||
|
|||||||
@@ -28,11 +28,11 @@ describe("chords", function () {
|
|||||||
|
|
||||||
describe("phrase", function () {
|
describe("phrase", function () {
|
||||||
it("should stringify", function () {
|
it("should stringify", function () {
|
||||||
expect(stringifyPhrase([0x01, 0x68, 0x72, 0xd4, 0x65])).toEqual("016872D465")
|
expect(stringifyPhrase([0x20, 0x68, 0x72, 0xd4, 0x65, 0x1fff])).toEqual("206872D4651FFF")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should parse", function () {
|
it("should parse", function () {
|
||||||
expect(parsePhrase("016872D465")).toEqual([0x01, 0x68, 0x72, 0xd4, 0x65])
|
expect(parsePhrase("206872D4651FFF")).toEqual([0x20, 0x68, 0x72, 0xd4, 0x65, 0x1fff])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
|
import {compressActions, decompressActions} from "../serialization/actions"
|
||||||
|
|
||||||
export interface Chord {
|
export interface Chord {
|
||||||
actions: number[]
|
actions: number[]
|
||||||
phrase: number[]
|
phrase: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parsePhrase(phrase: string): number[] {
|
export function parsePhrase(phrase: string): number[] {
|
||||||
return Array.from({length: phrase.length / 2}).map((_, i) =>
|
return decompressActions(
|
||||||
Number.parseInt(phrase.slice(i * 2, i * 2 + 2), 16),
|
Uint8Array.from({length: phrase.length / 2}).map((_, i) =>
|
||||||
|
Number.parseInt(phrase.slice(i * 2, i * 2 + 2), 16),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stringifyPhrase(phrase: number[]): string {
|
export function stringifyPhrase(phrase: number[]): string {
|
||||||
return phrase
|
return [...compressActions(phrase)]
|
||||||
.map(it => it.toString(16).padStart(2, "0"))
|
.map(it => it.toString(16).padStart(2, "0"))
|
||||||
.join("")
|
.join("")
|
||||||
.toUpperCase()
|
.toUpperCase()
|
||||||
|
|||||||
@@ -2,27 +2,11 @@ import {describe, it, expect} from "vitest"
|
|||||||
import {compressActions, decompressActions} from "./actions"
|
import {compressActions, decompressActions} from "./actions"
|
||||||
|
|
||||||
describe("layout", function () {
|
describe("layout", function () {
|
||||||
const actions = [1, 5, 2, 1023, 42, 2, 4, 78]
|
const actions = [1023, 255, 256, 42, 32, 532, 8000]
|
||||||
|
|
||||||
describe("compression", function () {
|
describe("compression", function () {
|
||||||
it("should compress back and forth arrays divisible by 4", function () {
|
it("should compress back and forth arrays divisible by 4", function () {
|
||||||
expect(decompressActions(compressActions(actions))).toEqual(actions)
|
expect(decompressActions(compressActions(actions))).toEqual(actions)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should compress back and forth arrays divisible not divisible by 4", function () {
|
|
||||||
expect(decompressActions(compressActions([...actions, 1023, 512, 123]))).toEqual([
|
|
||||||
...actions,
|
|
||||||
1023,
|
|
||||||
512,
|
|
||||||
123,
|
|
||||||
])
|
|
||||||
expect(decompressActions(compressActions([...actions, 1023, 512]))).toEqual([...actions, 1023, 512])
|
|
||||||
expect(decompressActions(compressActions([...actions, 1023]))).toEqual([...actions, 1023])
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should compress alternating 0/1023", function () {
|
|
||||||
const array = Array.from({length: 128}).map((_, i) => (i % 2 === 0 ? 0 : 1023))
|
|
||||||
expect(decompressActions(compressActions(array))).toEqual(array)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
/**
|
/**
|
||||||
* Compresses an action list into a Uint8Array of 10-bit integers, supporting values of up to 1023
|
* Compresses an action list into a Uint8Array of variable-length 8/13-bit integers.
|
||||||
|
*
|
||||||
|
* Action codes <32 are invalid.
|
||||||
*/
|
*/
|
||||||
export function compressActions(actions: number[]): Uint8Array {
|
export function compressActions(actions: number[]): Uint8Array {
|
||||||
const overflow = actions.length % 4
|
const buffer = new Uint8Array(actions.length * 2)
|
||||||
const array = new Uint8Array(
|
let i = 0
|
||||||
Math.ceil((actions.length - overflow) * 1.25 + (overflow === 0 ? 0 : overflow + 1)),
|
for (const action of actions) {
|
||||||
)
|
if (action > 0xff) {
|
||||||
let arrayOffset = 0
|
buffer[i++] = action >>> 8
|
||||||
for (let i = 0; i < actions.length; i += 4) {
|
|
||||||
let final = 0
|
|
||||||
for (let j = 0; j < 4 && i + j < actions.length; j++) {
|
|
||||||
const action = actions[i + j]
|
|
||||||
array[arrayOffset++] = (action >>> 2) & 0xff
|
|
||||||
final |= (action & 0x03) << (j * 2)
|
|
||||||
}
|
}
|
||||||
array[arrayOffset++] = final
|
buffer[i++] = action & 0xff
|
||||||
}
|
}
|
||||||
console.assert(arrayOffset === array.length)
|
return buffer.slice(0, i)
|
||||||
return array
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,12 +22,12 @@ export function compressActions(actions: number[]): Uint8Array {
|
|||||||
*/
|
*/
|
||||||
export function decompressActions(raw: Uint8Array): number[] {
|
export function decompressActions(raw: Uint8Array): number[] {
|
||||||
const actions: number[] = []
|
const actions: number[] = []
|
||||||
for (let i = 0; i < raw.length + 4; i += 5) {
|
for (let i = 0; i < raw.length; i++) {
|
||||||
const overflow = raw[Math.min(i + 4, raw.length - 1)]
|
let action = raw[i]
|
||||||
|
if (action < 32) {
|
||||||
for (let j = 0; j < 4 && i + j < raw.length - 1; j++) {
|
action = (action << 8) | raw[++i]
|
||||||
actions.push((raw[i + j] << 2) | ((overflow >>> (j * 2)) & 0x3))
|
|
||||||
}
|
}
|
||||||
|
actions.push(action)
|
||||||
}
|
}
|
||||||
return actions
|
return actions
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import {page} from "$app/stores"
|
import {page} from "$app/stores"
|
||||||
import LL from "../../i18n/i18n-svelte"
|
import LL from "../../i18n/i18n-svelte"
|
||||||
|
|
||||||
const paths = [
|
$: paths = [
|
||||||
{href: "/config/chords/", title: $LL.configure.chords.TITLE(), icon: "piano"},
|
{href: "/config/chords/", title: $LL.configure.chords.TITLE(), icon: "piano"},
|
||||||
{href: "/config/layout/", title: $LL.configure.layout.TITLE(), icon: "keyboard"},
|
{href: "/config/layout/", title: $LL.configure.layout.TITLE(), icon: "keyboard"},
|
||||||
{href: "/config/settings/", title: $LL.configure.settings.TITLE(), icon: "settings"},
|
{href: "/config/settings/", title: $LL.configure.settings.TITLE(), icon: "settings"},
|
||||||
|
|||||||
Reference in New Issue
Block a user