From 282427cfecea793c8087e6f6f548f741fc3c8b6b Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Thu, 11 Jul 2024 19:45:50 +0100 Subject: [PATCH] wip --- .../core/src/imageWriter/headerGenerator.ts | 70 +++++++++++-------- packages/core/src/imageWriter/imageWriter.ts | 12 ++-- packages/core/src/models/base.ts | 2 +- packages/core/src/models/neo.ts | 6 +- packages/core/src/models/plus.ts | 2 +- packages/core/src/proxy.ts | 2 +- packages/core/src/types.ts | 2 +- packages/core/src/util.ts | 11 ++- packages/node/src/device.ts | 4 +- packages/webhid-demo/src/demo/chase.ts | 2 +- packages/webhid/src/device.ts | 6 +- 11 files changed, 66 insertions(+), 53 deletions(-) diff --git a/packages/core/src/imageWriter/headerGenerator.ts b/packages/core/src/imageWriter/headerGenerator.ts index d795f31..d7b2854 100644 --- a/packages/core/src/imageWriter/headerGenerator.ts +++ b/packages/core/src/imageWriter/headerGenerator.ts @@ -1,3 +1,4 @@ +import { uint8ArrayToDataView } from '../util' import type { StreamdeckImageHeaderGenerator, StreamdeckImageWriterProps } from './types' export class StreamdeckGen1ImageHeaderGenerator implements StreamdeckImageHeaderGenerator { @@ -6,18 +7,19 @@ export class StreamdeckGen1ImageHeaderGenerator implements StreamdeckImageHeader } writeFillImageCommandHeader( - buffer: Buffer, + buffer: Uint8Array, props: StreamdeckImageWriterProps, partIndex: number, isLast: boolean, _bodyLength: number ): void { - buffer.writeUInt8(0x02, 0) - buffer.writeUInt8(0x01, 1) - buffer.writeUInt16LE(partIndex, 2) - // 3 = 0x00 - buffer.writeUInt8(isLast ? 1 : 0, 4) - buffer.writeUInt8(props.keyIndex + 1, 5) + const bufferView = uint8ArrayToDataView(buffer) + + bufferView.setUint8(0, 0x02) + bufferView.setUint8(2, 0x01) + bufferView.setUint16(2, partIndex, true) + bufferView.setUint8(4, isLast ? 1 : 0) + bufferView.setUint8(5, props.keyIndex + 1) } } @@ -27,18 +29,20 @@ export class StreamdeckGen2ImageHeaderGenerator implements StreamdeckImageHeader } writeFillImageCommandHeader( - buffer: Buffer, + buffer: Uint8Array, props: StreamdeckImageWriterProps, partIndex: number, isLast: boolean, bodyLength: number ): void { - buffer.writeUInt8(0x02, 0) - buffer.writeUInt8(0x07, 1) - buffer.writeUInt8(props.keyIndex, 2) - buffer.writeUInt8(isLast ? 1 : 0, 3) - buffer.writeUInt16LE(bodyLength, 4) - buffer.writeUInt16LE(partIndex++, 6) + const bufferView = uint8ArrayToDataView(buffer) + + bufferView.setUint8(0, 0x02) + bufferView.setUint8(1, 0x07) + bufferView.setUint8(2, props.keyIndex) + bufferView.setUint8(3, isLast ? 1 : 0) + bufferView.setUint16(4, bodyLength, true) + bufferView.setUint16(4, partIndex++, true) } } @@ -56,21 +60,23 @@ export class StreamdeckPlusLcdImageHeaderGenerator } writeFillImageCommandHeader( - buffer: Buffer, + buffer: Uint8Array, props: StreamdeckPlusHeaderProps, partIndex: number, isLast: boolean, bodyLength: number ): void { - buffer.writeUInt8(0x02, 0) - buffer.writeUInt8(0x0c, 1) - buffer.writeUInt16LE(props.x, 2) - buffer.writeUInt16LE(props.y, 4) - buffer.writeUInt16LE(props.width, 6) - buffer.writeUInt16LE(props.height, 8) - buffer.writeUInt8(isLast ? 1 : 0, 10) // Is last - buffer.writeUInt16LE(partIndex, 11) - buffer.writeUInt16LE(bodyLength, 13) + const bufferView = uint8ArrayToDataView(buffer) + + bufferView.setUint8(0, 0x02) + bufferView.setUint8(1, 0x0c) + bufferView.setUint16(2, props.x, true) + bufferView.setUint16(4, props.y, true) + bufferView.setUint16(6, props.width, true) + bufferView.setUint16(8, props.height, true) + bufferView.setUint8(10, isLast ? 1 : 0) + bufferView.setUint16(11, partIndex, true) + bufferView.setUint16(13, bodyLength, true) } } @@ -80,17 +86,19 @@ export class StreamdeckNeoLcdImageHeaderGenerator implements StreamdeckImageHead } writeFillImageCommandHeader( - buffer: Buffer, + buffer: Uint8Array, _props: never, partIndex: number, isLast: boolean, bodyLength: number ): void { - buffer.writeUInt8(0x02, 0) - buffer.writeUInt8(0x0b, 1) - buffer.writeUInt8(0, 2) - buffer.writeUInt8(isLast ? 1 : 0, 3) - buffer.writeUInt16LE(bodyLength, 4) - buffer.writeUInt16LE(partIndex++, 6) + const bufferView = uint8ArrayToDataView(buffer) + + bufferView.setUint8(0, 0x02) + bufferView.setUint8(1, 0x0b) + bufferView.setUint8(2, 0) + bufferView.setUint8(3, isLast ? 1 : 0) + bufferView.setUint16(4, bodyLength, true) + bufferView.setUint16(6, partIndex, true) } } diff --git a/packages/core/src/imageWriter/imageWriter.ts b/packages/core/src/imageWriter/imageWriter.ts index 1225654..bfe934d 100644 --- a/packages/core/src/imageWriter/imageWriter.ts +++ b/packages/core/src/imageWriter/imageWriter.ts @@ -12,13 +12,13 @@ export class StreamdeckOriginalImageWriter implements StreamdeckImageWriter { const packet1Bytes = byteBuffer.length / 2 - const packet1 = Buffer.alloc(MAX_PACKET_SIZE) + const packet1 = new Uint8Array(MAX_PACKET_SIZE) this.headerGenerator.writeFillImageCommandHeader(packet1, props, 0x01, false, packet1Bytes) - byteBuffer.copy(packet1, PACKET_HEADER_LENGTH, 0, packet1Bytes) + packet1.set(byteBuffer.subarray(0, packet1Bytes), PACKET_HEADER_LENGTH) - const packet2 = Buffer.alloc(MAX_PACKET_SIZE) + const packet2 = new Uint8Array(MAX_PACKET_SIZE) this.headerGenerator.writeFillImageCommandHeader(packet2, props, 0x02, true, packet1Bytes) - byteBuffer.copy(packet2, PACKET_HEADER_LENGTH, packet1Bytes) + packet2.set(byteBuffer.subarray(packet1Bytes), PACKET_HEADER_LENGTH) return [packet1, packet2] } @@ -42,7 +42,7 @@ export class StreamdeckDefaultImageWriter let remainingBytes = byteBuffer.length for (let part = 0; remainingBytes > 0; part++) { - const packet = Buffer.alloc(MAX_PACKET_SIZE) + const packet = new Uint8Array(MAX_PACKET_SIZE) const byteCount = Math.min(remainingBytes, MAX_PAYLOAD_SIZE) this.headerGenerator.writeFillImageCommandHeader( @@ -55,7 +55,7 @@ export class StreamdeckDefaultImageWriter const byteOffset = byteBuffer.length - remainingBytes remainingBytes -= byteCount - byteBuffer.copy(packet, PACKET_HEADER_LENGTH, byteOffset, byteOffset + byteCount) + packet.set(byteBuffer.subarray(byteOffset, byteOffset + byteCount), PACKET_HEADER_LENGTH) result.push(packet) } diff --git a/packages/core/src/models/base.ts b/packages/core/src/models/base.ts index e5faccf..9cf9047 100644 --- a/packages/core/src/models/base.ts +++ b/packages/core/src/models/base.ts @@ -160,7 +160,7 @@ export abstract class StreamDeckInputBase extends EventEmitter ): Promise public abstract fillPanelBuffer(imageBuffer: Uint8Array, options?: FillPanelOptions): Promise - public async fillLcd(_imageBuffer: Buffer, _sourceOptions: FillImageOptions): Promise { + public async fillLcd(_imageBuffer: Uint8Array, _sourceOptions: FillImageOptions): Promise { throw new Error('Not supported for this model') } public async fillEncoderLcd( diff --git a/packages/core/src/models/neo.ts b/packages/core/src/models/neo.ts index 41f9c75..761cdfb 100644 --- a/packages/core/src/models/neo.ts +++ b/packages/core/src/models/neo.ts @@ -34,7 +34,7 @@ export class StreamDeckNeo extends StreamDeckGen2Base { } } - public override async fillLcd(imageBuffer: Buffer, sourceOptions: FillImageOptions): Promise { + public override async fillLcd(imageBuffer: Uint8Array, sourceOptions: FillImageOptions): Promise { const size = this.LCD_STRIP_SIZE if (!size) throw new Error(`There is no lcd to fill`) @@ -51,10 +51,10 @@ export class StreamDeckNeo extends StreamDeckGen2Base { } private async convertFillLcdBuffer( - sourceBuffer: Buffer, + sourceBuffer: Uint8Array, size: LcdSegmentSize, sourceOptions: FillImageOptions - ): Promise { + ): Promise { const sourceOptions2: InternalFillImageOptions = { format: sourceOptions.format, offset: 0, diff --git a/packages/core/src/models/plus.ts b/packages/core/src/models/plus.ts index 8dbabaa..9e15c6a 100644 --- a/packages/core/src/models/plus.ts +++ b/packages/core/src/models/plus.ts @@ -122,7 +122,7 @@ export class StreamDeckPlus extends StreamDeckGen2Base { } } - public override async fillLcd(buffer: Buffer, sourceOptions: FillImageOptions): Promise { + public override async fillLcd(buffer: Uint8Array, sourceOptions: FillImageOptions): Promise { const size = this.LCD_STRIP_SIZE if (!size) throw new Error(`There is no lcd to fill`) diff --git a/packages/core/src/proxy.ts b/packages/core/src/proxy.ts index c68719b..f04b3f8 100644 --- a/packages/core/src/proxy.ts +++ b/packages/core/src/proxy.ts @@ -103,7 +103,7 @@ export class StreamDeckProxy implements StreamDeck { return this.device.getSerialNumber() } - public async fillLcd(imageBuffer: Buffer, sourceOptions: FillImageOptions): Promise { + public async fillLcd(imageBuffer: Uint8Array, sourceOptions: FillImageOptions): Promise { return this.device.fillLcd(imageBuffer, sourceOptions) } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index e3c6a7d..31b9949 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -117,7 +117,7 @@ export interface StreamDeck extends EventEmitter { * @param {Buffer} imageBuffer The image to write * @param {Object} sourceOptions Options to control the write */ - fillLcd(imageBuffer: Buffer, sourceOptions: FillImageOptions): Promise + fillLcd(imageBuffer: Uint8Array, sourceOptions: FillImageOptions): Promise /** * Fills the lcd strip above an encoder diff --git a/packages/core/src/util.ts b/packages/core/src/util.ts index 086a41f..d4dad33 100644 --- a/packages/core/src/util.ts +++ b/packages/core/src/util.ts @@ -1,3 +1,4 @@ +import { uint8ArrayToDataView } from './util' import { InternalFillImageOptions } from './models/base' export interface FillImageTargetOptions { @@ -17,10 +18,10 @@ export function transformImageBuffer( ): Uint8Array { if (!imageHeight) imageHeight = imageWidth - const imageBufferView = new DataView(imageBuffer) + const imageBufferView = uint8ArrayToDataView(imageBuffer) const byteBuffer = new Uint8Array(destPadding + imageWidth * imageHeight * targetOptions.colorMode.length) - const byteBufferView = new DataView(byteBuffer) + const byteBufferView = uint8ArrayToDataView(byteBuffer) const flipColours = sourceOptions.format.substring(0, 3) !== targetOptions.colorMode.substring(0, 3) @@ -65,7 +66,7 @@ export function transformImageBuffer( export const BMP_HEADER_LENGTH = 54 export function writeBMPHeader(buf: Uint8Array, iconSize: number, iconBytes: number, imagePPM: number): void { - const bufView = new DataView(buf) + const bufView = uint8ArrayToDataView(buf) // Uses header format BITMAPINFOHEADER https://en.wikipedia.org/wiki/BMP_file_format // Bitmap file header @@ -89,3 +90,7 @@ export function writeBMPHeader(buf: Uint8Array, iconSize: number, iconBytes: num bufView.setInt32(46, 0, true) // Colour pallette size bufView.setInt32(50, 0, true) // 'Important' Colour count } + +export function uint8ArrayToDataView(buffer: Uint8Array): DataView { + return new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) +} diff --git a/packages/node/src/device.ts b/packages/node/src/device.ts index 3aee1fb..7e47bc4 100644 --- a/packages/node/src/device.ts +++ b/packages/node/src/device.ts @@ -91,14 +91,14 @@ export class NodeHIDSyncDevice extends EventEmitter implements HIDDevice { } public async sendFeatureReport(data: Uint8Array): Promise { - this.device.sendFeatureReport(data) + this.device.sendFeatureReport(Buffer.from(data)) } public async getFeatureReport(reportId: number, reportLength: number): Promise { return Buffer.from(this.device.getFeatureReport(reportId, reportLength)) } public async sendReports(buffers: Uint8Array[]): Promise { for (const data of buffers) { - this.device.write(data) + this.device.write(Buffer.from(data)) } } diff --git a/packages/webhid-demo/src/demo/chase.ts b/packages/webhid-demo/src/demo/chase.ts index 760e3af..f0d6379 100644 --- a/packages/webhid-demo/src/demo/chase.ts +++ b/packages/webhid-demo/src/demo/chase.ts @@ -28,7 +28,7 @@ export class ChaseDemo implements Demo { ctx.fillText(n.toString(), 8, canvas.height * 0.9, canvas.width * 0.8) const id = ctx.getImageData(0, 0, canvas.width, canvas.height) - ps.push(device.fillKeyBuffer(i, Buffer.from(id.data), { format: 'rgba' })) + ps.push(device.fillKeyBuffer(i, id.data, { format: 'rgba' })) ctx.restore() } } diff --git a/packages/webhid/src/device.ts b/packages/webhid/src/device.ts index 7d5b967..f0ec728 100644 --- a/packages/webhid/src/device.ts +++ b/packages/webhid/src/device.ts @@ -34,8 +34,8 @@ export class WebHIDDevice extends EventEmitter implements CoreHIDDevice { return this.device.forget() } - public async sendFeatureReport(data: Buffer): Promise { - return this.device.sendFeatureReport(data[0], new Uint8Array(data.subarray(1))) + public async sendFeatureReport(data: Uint8Array): Promise { + return this.device.sendFeatureReport(data[0], data.subarray(1)) } public async getFeatureReport(reportId: number, _reportLength: number): Promise { const view = await this.device.receiveFeatureReport(reportId) @@ -44,7 +44,7 @@ export class WebHIDDevice extends EventEmitter implements CoreHIDDevice { public async sendReports(buffers: Uint8Array[]): Promise { return this.reportQueue.add(async () => { for (const data of buffers) { - await this.device.sendReport(data[0], new Uint8Array(data.subarray(1))) + await this.device.sendReport(data[0], data.subarray(1)) } }) }