Skip to content

Commit

Permalink
feat: razer stream controller x
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian committed Apr 17, 2023
1 parent c84d2ec commit 2b777ba
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 8 deletions.
1 change: 1 addition & 0 deletions packages/core/src/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export enum LoupedeckModelId {
LoupedeckLive = 'loupedeck-live',
LoupedeckLiveS = 'loupedeck-live-s',
RazerStreamController = 'razer-stream-controller',
RazerStreamControllerX = 'razer-stream-controller-x',
}

/**
Expand Down
24 changes: 18 additions & 6 deletions packages/core/src/models/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export interface LoupedeckDisplayDefinition {
height: number
encoded: Buffer
xPadding: number
columnGap?: number // TODO make required
rowGap?: number // TODO make required
}

export interface LoupedeckDeviceOptions {
Expand Down Expand Up @@ -136,11 +138,14 @@ export abstract class LoupedeckDeviceBase extends EventEmitter<LoupedeckDeviceEv
await this.#connection.close()
}

protected convertKeyIndexToCoordinates(index: number): [x: number, y: number] {
const width = this.lcdKeySize
const height = this.lcdKeySize
const x = (index % 4) * width
const y = Math.floor(index / 4) * height
private convertKeyIndexToCoordinates(index: number, display: LoupedeckDisplayDefinition): [x: number, y: number] {
const cols = this.lcdKeyColumns

const width = this.lcdKeySize + (display.columnGap ?? 0)
const height = this.lcdKeySize + (display.rowGap ?? 0)

const x = (index % cols) * width
const y = Math.floor(index / cols) * height

return [x, y]
}
Expand All @@ -158,6 +163,8 @@ export abstract class LoupedeckDeviceBase extends EventEmitter<LoupedeckDeviceEv
): [buffer: Buffer, offset: number] {
const padding = 10 // header + id

console.log('drawing', x, y)

const pixelCount = width * height
const encoded = Buffer.alloc(pixelCount * 2 + padding)

Expand Down Expand Up @@ -190,6 +197,8 @@ export abstract class LoupedeckDeviceBase extends EventEmitter<LoupedeckDeviceEv
if (y < 0 || y + height > display.height) throw new Error('x is not valid')

const [encoded, padding] = this.createBufferWithHeader(display, width, height, x + display.xPadding, y)

// TODO - blank dead zones in encoded buffer
encodeBuffer(buffer, encoded, format, padding, width * height)

await this.#runInQueueIfEnabled(async () => {
Expand All @@ -199,7 +208,10 @@ export abstract class LoupedeckDeviceBase extends EventEmitter<LoupedeckDeviceEv
}

public async drawKeyBuffer(index: number, buffer: Buffer, format: LoupedeckBufferFormat): Promise<void> {
const [x, y] = this.convertKeyIndexToCoordinates(index)
const display = this.displays.find((d) => d.id === LoupedeckDisplayId.Center)
if (!display) throw new Error('Invalid DisplayId')

const [x, y] = this.convertKeyIndexToCoordinates(index, display)

const size = this.lcdKeySize
return this.drawBuffer(LoupedeckDisplayId.Center, buffer, format, size, size, x, y)
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/models/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { LoupedeckSerialConnection } from '../serial'
import { RazerStreamControllerDevice } from './razer-stream-controller'
import { LoupedeckDevice } from './interface'
import { LoupedeckLiveSDevice } from './live-s'
import { RazerStreamControllerDeviceX } from './razer-stream-controller-x'

export interface DeviceModelSpec {
id: LoupedeckModelId
Expand Down Expand Up @@ -34,4 +35,10 @@ export const DEVICE_MODELS: DeviceModelSpec[] = [
productId: 0x0d06,
class: RazerStreamControllerDevice,
},
{
id: LoupedeckModelId.RazerStreamControllerX,
vendorId: VendorIdRazer,
productId: 0x0d09,
class: RazerStreamControllerDeviceX,
},
]
68 changes: 68 additions & 0 deletions packages/core/src/models/razer-stream-controller-x.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { LoupedeckControlType, LoupedeckDisplayId } from '../constants'
import { LoupedeckSerialConnection } from '../serial'
import { LoupedeckDisplayDefinition, LoupedeckDeviceBase, LoupedeckDeviceOptions } from './base'
import { LoupedeckModelId } from '../info'
import { LoupedeckControlDefinition } from './interface'

const DisplayCenter: LoupedeckDisplayDefinition = {
id: LoupedeckDisplayId.Center,
width: 480,
height: 270,
encoded: Buffer.from([0x00, 0x4d]),
xPadding: 5,
columnGap: 20,
rowGap: 18,
}
const Displays: LoupedeckDisplayDefinition[] = [DisplayCenter]

const Controls: LoupedeckControlDefinition[] = []
for (let i = 0; i < 15; i++) {
Controls.push({
type: LoupedeckControlType.Button,
index: i,
encoded: 0x1b + i,
})
}
export class RazerStreamControllerDeviceX extends LoupedeckDeviceBase {
constructor(connection: LoupedeckSerialConnection, options: LoupedeckDeviceOptions) {
super(connection, options, Displays, Controls)
}

public get modelId(): LoupedeckModelId {
return LoupedeckModelId.RazerStreamControllerX
}
public get modelName(): string {
return 'Razer Stream Controller X'
}

public override get lcdKeySize(): number {
return 78
}

public get lcdKeyColumns(): number {
return 5
}
public get lcdKeyRows(): number {
return 3
}

protected override createBufferWithHeader(
display: LoupedeckDisplayDefinition,
width: number,
height: number,
x: number,
y: number
): [buffer: Buffer, offset: number] {
// The Razer Stream Controller X only has one screen object

if (display.id !== DisplayCenter.id) {
throw new Error('Unknown DisplayId')
}

return super.createBufferWithHeader(display, width, height, x, y)
}

protected override onTouch(_event: 'touchmove' | 'touchend' | 'touchstart', _buff: Buffer): void {
// Not supported by device
}
}
16 changes: 14 additions & 2 deletions packages/web-demo/src/demo/fill-when-pressed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
LoupedeckControlInfo,
LoupedeckDevice,
LoupedeckDisplayId,
LoupedeckModelId,
LoupedeckTouchEventData,
RGBColor,
} from '@loupedeck/web'
Expand All @@ -15,6 +16,9 @@ function stringifyInfo(info: LoupedeckControlInfo): string {
const colorRed: RGBColor = { red: 255, green: 0, blue: 0 }
const colorBlack: RGBColor = { red: 0, green: 0, blue: 0 }

const bufferRed = Buffer.alloc(80 * 80 * 3, Buffer.from([255, 0, 0]))
const bufferBlack = Buffer.alloc(80 * 80 * 3)

export class FillWhenPressedDemo implements Demo {
private pressed: string[] = []
private touchBoxes = new Set<number>()
Expand All @@ -30,7 +34,11 @@ export class FillWhenPressedDemo implements Demo {
if (this.pressed.indexOf(id) === -1) {
this.pressed.push(id)

await device.setButtonColor({ id: info.index, ...colorRed })
if (device.modelId === LoupedeckModelId.RazerStreamControllerX) {
await device.drawKeyBuffer(info.index, bufferRed, LoupedeckBufferFormat.RGB)
} else {
await device.setButtonColor({ id: info.index, ...colorRed })
}
}
}
public async controlUp(device: LoupedeckDevice, info: LoupedeckControlInfo): Promise<void> {
Expand All @@ -39,7 +47,11 @@ export class FillWhenPressedDemo implements Demo {
if (index !== -1) {
this.pressed.splice(index, 1)

await device.setButtonColor({ id: info.index, ...colorBlack })
if (device.modelId === LoupedeckModelId.RazerStreamControllerX) {
await device.drawKeyBuffer(info.index, bufferBlack, LoupedeckBufferFormat.RGB)
} else {
await device.setButtonColor({ id: info.index, ...colorBlack })
}
}
}

Expand Down

0 comments on commit 2b777ba

Please sign in to comment.