chord importing

This commit is contained in:
2023-07-05 17:30:05 +02:00
parent 1f99835a4e
commit c1c0193dae
16 changed files with 94 additions and 454 deletions

View File

@@ -1,3 +1,3 @@
{
" ": "␣"
"SPA": "␣"
}

View File

@@ -5,7 +5,7 @@
</script>
<nav>
<h1>dot i/o</h1>
<a href="/" class="title">dot i/o</a>
<div class="steps">
<a href={base} title="CPM - characters per minute" class="icon train cpm">music_note</a>
@@ -45,9 +45,13 @@
width: 100%;
}
h1 {
.title {
margin-block: 0;
font-size: 1.5rem;
font-weight: bold;
color: var(--md-sys-color-primary);
text-decoration: none;
}
.icon {

View File

@@ -1,5 +1,6 @@
import {LineBreakTransformer} from "$lib/serial/webserial/util/line-break-transformer.js"
import {LineBreakTransformer} from "$lib/serial/line-break-transformer.js"
import {serialLog} from "$lib/serial/connection.js"
import {ACTION_MAP} from "$lib/serial/webserial/constants/action-map.js"
export class CharaDevice {
/** @type {Promise<SerialPort>} */
@@ -127,7 +128,43 @@ export class CharaDevice {
async send(...command) {
return this.runWith(async (send, read) => {
await send(...command)
return read()
const commandString = command.join(" ").replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")
return read().then(it => it.replace(new RegExp(`^${commandString} `), ""))
})
}
/**
* @returns {Promise<number>}
*/
async getChordCount() {
return Number.parseInt(await this.send("CML C0"))
}
/**
* @param index {number}
* @returns {Promise<{actions: number[]; phrase: string, unk: number}>}
*/
async getChord(index) {
const chord = await this.send(`CML C1 ${index}`)
const [keys, rawPhrase, b] = chord.split(" ")
let phrase = []
for (let i = 0; i < rawPhrase.length; i += 2) {
phrase.push(Number.parseInt(rawPhrase.substring(i, i + 2), 16))
}
let bigKeys = BigInt(`0x${keys}`)
let actions = []
for (let i = 0; i < 12; i++) {
const action = Number(bigKeys & BigInt(0b1111111111))
if (action !== 0) {
actions.push(ACTION_MAP[action])
}
bigKeys >>= BigInt(10)
}
return {
actions,
phrase: String.fromCodePoint(...phrase),
unk: Number(b),
}
}
}

View File

@@ -1,132 +0,0 @@
import {LineBreakTransformer} from "$lib/serial/webserial/util/line-break-transformer.js"
import {CONFIG_ID} from "$lib/serial/webserial/constants/config-id.js"
let _chordMaps = []
let serialPort = null
let portReader = null
let lineReader = null
let lineReaderDone = null
let abortController1 = new AbortController()
let abortController2 = new AbortController()
async function disconnectSerialConnection() {
console.log("disconnectSerialConnection()")
if (serialPort) {
console.log("closing serial port")
lineReader.releaseLock()
console.log(serialPort.readable)
await abortController1.abort()
await lineReaderDone.catch(() => {
/* Ingore the error */
})
await serialPort.close()
console.log("serial port is closed")
document.getElementById("statusDiv").innerHTML = "status: closed serial port"
} else {
console.log("there is no serial connection open to close")
}
}
//TODO not sure this actually works
async function cancelReader() {
if (serialPort) {
if (lineReader) {
// if(lineReader.locked){
await lineReader.cancel().then(() => {
console.log("cleared line reader")
})
// await serialPort.readable.releaseLock();
console.log(abortController1)
await abortController1.abort()
console.log(serialPort.readable)
await lineReaderDone.catch(() => {
/* Ingore the error */
}) //this frees up the serialPort.readable after the abortControl1.abort() signal
// await serialPort.readable.cancel();
// }
}
}
}
async function resetReader() {
console.log("resetting lineReader")
if (serialPort) {
if (lineReader) {
if (lineReader.locked) {
await lineReader.releaseLock()
}
await lineReader.cancel().then(() => {
console.log("cleared line reader")
})
await lineReaderDone.catch(() => {
/* Ingore the error */
})
}
await setupLineReader()
}
console.log("reset lineReader")
}
async function getCount() {
await sendCommandString("SELECT BASE")
await readGetChordmapCount()
document.getElementById("countDiv").innerHTML = "count: " + _chordmapCountOnDevice
}
async function getGetAll1() {
await selectBase() //select BASE
await sendCommandString("GETALL")
await readGetAllChordmaps()
}
async function getGetAll2() {
await selectBase() //select BASE
await sendCommandString("GETSOME 0 " + _chordmapCountOnDevice)
await readGetSomeChordmaps(_chordmapCountOnDevice)
}
async function getGetAll() {
await selectBase() //select BASE
for (let i = 0; i < _chordmapCountOnDevice; i++) {
await sendCommandString("GETSOME " + (i + 0).toString() + " " + (i + 1).toString())
await readGetOneChordmap()
}
}
let _chordmapId = "DEFAULT"
let _chordmapCountOnDevice = 50 //TODO set this to zero by default
let _firmwareVersion = "0"
async function readGetChordmapCount() {
const {value, done} = await lineReader.read()
if (value) {
_chordmapCountOnDevice = parseInt(value)
console.log(_chordmapCountOnDevice)
}
}
async function readGetAll() {
readGetSomeChordmaps(_chordmapCountOnDevice)
}
function commitAll() {
console.log("commitAll()")
const dataTable = document.getElementById("dataTable")
//iterate through table from bottom to top to see if there's a commit enabled
//TODO check if we need to skip the header row
for (let i = dataTable.rows.length - 1; i >= 1; i--) {
//iterate through rows
let row = dataTable.rows[i]
// console.log(row);
// console.log(row.cells);
// console.log(row.cells[0]);
// console.log(row.cells[0].innerHTML);
let virtualId = parseInt(row.cells[0].innerHTML)
console.log("table row " + i + " has virtualId of " + virtualId)
// document.getElementById(virtualId.toString()+"-commit")
setTimeout(pressCommitButton, i * 100, virtualId)
//rows would be accessed using the "row" variable assigned in the for loop
}
}

View File

@@ -1,11 +0,0 @@
export async function bootLoader() {
//Sends the bootloader command to the charachorder via the serial API
await sendCommandString("BOOTLOADER")
await readGetNone()
}
export async function reboot() {
//Sends the restart command to the charachorder via the serial API
await sendCommandString("RESTART")
await readGetNone()
}

View File

@@ -31,7 +31,7 @@
* `38,655,229,952`
* `0x00000000900080000`
*/
export const _actionMap = [
export const ACTION_MAP = [
"NUL", //0x00 0
"CCFunc", //0x01
"STX", //0x02 2

View File

@@ -1,27 +0,0 @@
import {readGetOneAndToss} from "$lib/serial/webserial/noop.js"
/**
* Unused?
* @param lineReader {ReadableStreamDefaultReader<string>}
* @returns {Promise<string>}
*/
export async function readGetHexChord(lineReader) {
let hexChordString = ""
await readGetOneAndToss(lineReader) //this is added for the latest firmware with customers, where decimal version
const {value, done} = await lineReader.read()
if (done) {
console.log("reader is done")
} else {
console.log(["value", value])
if (value) {
let arrValue = [...value]
const strValue = String(arrValue.join(""))
console.log(strValue)
hexChordString = strValue.substr(0, 16)
await readGetOneAndToss(lineReader) // the "processing chord:" decimal output
}
}
return hexChordString
}

View File

@@ -1,5 +0,0 @@
/**
* @param lineReader {ReadableStreamDefaultReader<string>}
* @returns {Promise<void>}
*/
export async function readGetOneAndToss(lineReader) {}

View File

@@ -1,19 +0,0 @@
/**
* JavaScript handles up to 53-bit bs it uses float64 to operate with integers
*/
function bitwiseAndLarge(val1, val2) {
let shift = 0,
result = 0
const mask = ~(~0 << 30) // Gives us a bit mask like 01111..1 (30 ones)
const divisor = 1 << 30 // To work with the bit mask, we need to clear bits at a time
while (val1 !== 0 && val2 !== 0) {
let rs = mask & val1 & (mask & val2)
val1 = Math.floor(val1 / divisor) // val1 >>> 30
val2 = Math.floor(val2 / divisor) // val2 >>> 30
for (let i = shift++; i--; ) {
rs *= divisor // rs << 30
}
result += rs
}
return result
}

View File

@@ -1,208 +0,0 @@
import {_actionMap} from "$lib/serial/webserial/constants/action-map.js"
import {_keyMap} from "$lib/serial/webserial/constants/key-map.js"
import {_keyMapDefaults} from "$lib/serial/webserial/constants/key-map-defaults.js"
/**
* @param hexString {string}
* @param chordMapId {keyof typeof import('$lib/serial/webserial/constants/key-map-defaults.js')._keyMapDefaults}
* @returns {string}
*/
export function convertHexadecimalChordToHumanString(hexString, chordMapId) {
let humanString = ""
console.log(hexString)
if (hexString.length <= 0) {
hexString = "00"
}
let bigNum = BigInt("0x" + hexString)
if (chordMapId === "CHARACHORDER") {
// CharaChorder original uses different key map structure
let decString = String(bigNum).split("") //no left zeros; that's ok
console.log(decString)
for (let i = 0; i < decString.length; i++) {
if (decString[i] !== "0") {
if (humanString.length > 0) {
humanString += " + "
}
console.log({
"i": i,
"decString[i]": decString[i],
"decString.length": decString.length,
"decString": decString,
"10exp": decString.length - i - 1,
"decChordComp": decString[i] * 10 ** (decString.length - i - 1),
// 'decChordCompBigInt':BigInt(decString[i])*BigInt((BigInt(10)**(decString.length-i-1))),
"noteId": chord_to_noteId(decString[i] * 10 ** (decString.length - i - 1)),
})
let noteId
let actionId
if (decString[i] % 2 === 1) {
//if it is odd, then it is simple
noteId = chord_to_noteId(decString[i] * 10 ** (decString.length - i - 1))
actionId = _keyMapDefaults["CHARACHORDER"][noteId]
if (actionId === 0) {
actionId = 0x0200 + noteId
}
humanString += _actionMap[actionId]
} else {
//value is even, odd plus a 1
noteId = chord_to_noteId((decString[i] - 1) * 10 ** (decString.length - i - 1))
actionId = _keyMapDefaults["CHARACHORDER"][noteId]
if (actionId === 0) {
actionId = 0x0200 + noteId
}
humanString += _actionMap[actionId]
humanString += " + "
noteId = chord_to_noteId(10 ** (decString.length - i - 1))
actionId = _keyMapDefaults["CHARACHORDER"][noteId]
if (actionId === 0) {
actionId = 0x0200 + noteId
}
humanString += _actionMap[actionId]
}
}
}
} else {
let binString = bigNum.toString(2) //no left zeros; that's ok
console.log(binString)
for (let i = 0; i < binString.length; i++) {
if (binString[i] === "1") {
if (humanString.length > 0) {
humanString += " + "
}
humanString += _keyMap[64 - binString.length + i]
//console.log(i);
//humanString+=_keyMap[(64-binString.length+i)];
}
}
}
console.log(humanString)
return humanString
}
/**
* @param humanString {string}
* @returns {string}
*/
export function convertHumanStringToHexadecimalPhrase(humanString) {
let hexString = ""
for (let i = 0; i < humanString.length; i++) {
let hex = Number(humanString.charCodeAt(i)).toString(16)
hexString += hex
}
hexString = hexString.toUpperCase()
console.log(hexString)
return hexString
}
/**
* @param hexString {string}
* @returns {string}
*/
function convertHexadecimalPhraseToAsciiString(hexString) {
let asciiString = ""
console.log("convertHexadecimalPhraseToAsciiString()")
//assume 2x size
//get every 2 characters
//TODO covert to byte array and account for non-ascii inputs like mouse moves
for (let i = 0; i < hexString.length; i += 2) {
asciiString += String.fromCharCode(parseInt(hexString.substr(i, 2), 16))
//console.log("0x"+hexString.substr(i, 2));
//asciiString += String.fromCharCode("0x"+hexString.substr(i, 2));
}
console.log(asciiString)
return asciiString
}
function noteId_to_chord(note) {
return BigInt(2 * ((note - 1) % 5) + 1) * BigInt(10) ** BigInt(Math.floor((note - 1) / 5))
}
function chord_to_noteId(chord) {
const part1 = 5 * Math.floor(Math.log10(chord))
const part2 = Math.floor(chord / 10 ** Math.floor(Math.log10(chord)) + 1) / 2
const part3 = Math.log10(chord)
const full = Math.floor(
5 * Math.floor(Math.log10(chord)) + Math.floor(chord / 10 ** Math.floor(Math.log10(chord)) + 1) / 2,
)
console.log([chord, part1, part2, part3, full])
return full
}
/**
* @param humanString {string}
* @param chordMapId {keyof typeof import('$lib/serial/webserial/constants/key-map-defaults.js')._keyMapDefaults}
* @returns {Promise<bigint>}
*/
export async function convertHumanStringToBigNum(humanString, chordMapId) {
console.log("convertHumanStringToBigNum")
let bigNum = BigInt(0)
//parse the pieces with _+_
let humanStringParts = humanString.split(" + ") //assumes plus isn't being used; bc default is = for the +/= key
console.log(humanStringParts)
for (const part of humanStringParts) {
let actionId = _actionMap.indexOf(part)
console.log(actionId)
if (chordMapId === "CHARACHORDER") {
//charachorder original uses different key map structure
let keyId
if (actionId < 0x0200) {
keyId = _keyMapDefaults["CHARACHORDER"].indexOf(actionId)
} else {
keyId = actionId - 0x0200 //using the physical key position
}
console.log(keyId)
bigNum = BigInt(noteId_to_chord(keyId))
// bigNum+= BigInt(noteId_to_chord(keyId));
console.log(bigNum)
} else {
//use other keymap
}
}
console.log(bigNum)
return bigNum
}
/**
* @param humanString {string}
* @param chordMapId {keyof typeof import('$lib/serial/webserial/constants/key-map-defaults.js')._keyMapDefaults}
* @returns {string}
*/
export function convertHumanStringToHexadecimalChord(humanString, chordMapId) {
let bigNum = BigInt(0)
//parse the pieces with _+_
let humanStringParts = humanString.split(" + ") //assumes plus isn't being used; bc default is = for the +/= key
console.log(humanStringParts)
humanStringParts.forEach(async part => {
let actionId = _actionMap.indexOf(part)
console.log(actionId)
if (chordMapId === "CHARACHORDER") {
//charachorder original uses different key map structure
let keyId
if (actionId < 0x0200) {
keyId = _keyMapDefaults["CHARACHORDER"].indexOf(actionId)
} else {
keyId = actionId - 0x0200 //using the physical key position
}
console.log(keyId)
bigNum += BigInt(noteId_to_chord(keyId))
console.log(bigNum)
} else {
//use other keymap
}
})
console.log(bigNum)
let hexString = bigNum.toString(16).toUpperCase()
hexString = "0".repeat(16 - hexString.length) + hexString //add leading zeros up to 16 characters
console.log(hexString)
return hexString
}

View File

@@ -1,17 +0,0 @@
import {readGetOneAndToss} from "$lib/serial/webserial/noop.js"
export let FIRMWARE_VERSION = "0"
/**
* @param lineReader {ReadableStreamDefaultReader<string>}
* @returns {Promise<void>}
*/
export async function readVersion(lineReader) {
await readGetOneAndToss(lineReader) //electronics board version
const {value, done} = await lineReader.read()
if (value) {
FIRMWARE_VERSION = value
console.log("Firmware Version:", FIRMWARE_VERSION)
}
await readGetOneAndToss(lineReader) //serial api version
}

View File

@@ -1 +1,2 @@
export const prerender = true
export const trailingSlash = "always"

View File

@@ -1,6 +1,52 @@
<script>
import {serialPort} from "$lib/serial/connection.js"
import keySymbols from "$lib/assets/key-symbols.json"
</script>
<svelte:head>
<title>dot i/o</title>
</svelte:head>
<h1>dot i/o V2</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
<h2>Chords</h2>
{#if $serialPort}
{#await $serialPort.getChordCount() then chordCount}
<p>You have {chordCount} chords</p>
<table>
{#each Array.from({length: chordCount}) as _, i}
{#await $serialPort.getChord(i) then { phrase, actions }}
<tr>
<th>{phrase}</th>
<td>
{#each actions as action}
<i>{keySymbols[action] || action}</i>
{/each}
</td>
</tr>
{/await}
{/each}
</table>
{/await}
{/if}
<style lang="scss">
table i {
display: block;
aspect-ratio: 1;
padding-block: 4px;
padding-inline: 8px;
font-style: normal;
border: 1px solid var(--md-sys-color-outline);
border-radius: 8px;
}
td {
display: flex;
gap: 4px;
}
</style>

View File

@@ -39,17 +39,6 @@
</div>
</section>
<section>
<h2>Chords</h2>
<div class="icon bg">piano</div>
<table>
<tr>
<th>three</th>
<td><i></i><i>3</i></td>
</tr>
</table>
</section>
<section>
<h2>Serial Terminal</h2>
<div class="icon bg">terminal</div>
@@ -130,24 +119,6 @@
}
}
table td {
display: flex;
gap: 4px;
}
table i {
display: block;
aspect-ratio: 1;
padding-block: 4px;
padding-inline: 8px;
font-style: normal;
border: 1px solid var(--md-sys-color-outline);
border-radius: 8px;
}
.device-grid {
display: grid;
grid-template-rows: 1fr 1fr;

0
static/.nojekyll Normal file
View File