Skip to content

Commit

Permalink
feat: multiviewer box overlays
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian committed Sep 26, 2024
1 parent eb95d94 commit 313793d
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/commands/Settings/MultiViewerSourceCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class MultiViewerSourceUpdateCommand extends DeserializedCommand<MultiVie
const multiviewer = AtemStateUtil.getMultiViewer(state, this.multiViewerId)
const currentWindow = multiviewer.windows[this.properties.windowIndex]

// The Constellation HD range has a bug where it sends this command for every window on every frame
// The Constellation HD range had a bug where it sends this command for every window on every frame
// This hides that from library consumers by doing a deep diff, when we usually do not.
if (currentWindow && !isRunningInTests()) {
let isChanged = false
Expand Down
79 changes: 79 additions & 0 deletions src/commands/Settings/MultiViewerWindowOverlayPropertiesCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { DeserializedCommand, WritableCommand } from '../CommandBase'
import { MultiViewerWindowOverlayPropertiesState } from '../../state/settings'
import { AtemState, AtemStateUtil, InvalidIdError } from '../../state'

export class MultiViewerWindowOverlayPropertiesCommand extends WritableCommand<MultiViewerWindowOverlayPropertiesState> {
public static MaskFlags = {
labelVisible: 1 << 0,
borderVisible: 1 << 1,
}

public static readonly rawName = 'CMvO'

public readonly multiViewerId: number
public readonly windowIndex: number

constructor(multiviewerId: number, windowIndex: number) {
super()

this.multiViewerId = multiviewerId
this.windowIndex = windowIndex
}

public serialize(): Buffer {
const buffer = Buffer.alloc(8)
buffer.writeUInt8(this.multiViewerId, 0)
buffer.writeUInt8(this.windowIndex || 0, 1)

let value = 0
if (this.properties.labelVisible) value |= 0x01
if (this.properties.borderVisible) value |= 0x02

buffer.writeUInt8(value, 3)
buffer.writeUInt8(this.flag, 5)
return buffer
}
}

export class MultiViewerWindowOverlayPropertiesUpdateCommand extends DeserializedCommand<MultiViewerWindowOverlayPropertiesState> {
public static readonly rawName = 'MvOv'

public readonly multiViewerId: number
public readonly windowIndex: number

constructor(multiviewerId: number, windowIndex: number, props: MultiViewerWindowOverlayPropertiesState) {
super(props)

this.multiViewerId = multiviewerId
this.windowIndex = windowIndex
}

public static deserialize(rawCommand: Buffer): MultiViewerWindowOverlayPropertiesUpdateCommand {
const multiViewerId = rawCommand.readUInt8(0)
const windowIndex = rawCommand.readUInt8(1)

const values = rawCommand.readUInt8(3)

const props: MultiViewerWindowOverlayPropertiesState = {
labelVisible: (values & 0x01) > 0,
borderVisible: (values & 0x02) > 0,
}

return new MultiViewerWindowOverlayPropertiesUpdateCommand(multiViewerId, windowIndex, props)
}

public applyToState(state: AtemState): string {
if (!state.info.multiviewer || this.multiViewerId >= state.info.multiviewer.count) {
throw new InvalidIdError('MultiViewer', this.multiViewerId)
}

const multiviewer = AtemStateUtil.getMultiViewer(state, this.multiViewerId)
const window = multiviewer.windows[this.windowIndex]
if (!window) {
throw new InvalidIdError('MultiViewer Window', this.multiViewerId, this.windowIndex)
}
window.overlayProperties = this.properties

return `settings.multiViewers.${this.multiViewerId}.windows.${this.windowIndex}.overlayProperties`
}
}
51 changes: 51 additions & 0 deletions src/commands/Settings/MultiViewerWindowSafeAreaTypeCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { SymmetricalCommand } from '../CommandBase'
import { AtemState, AtemStateUtil, InvalidIdError } from '../../state'
import { SafeTitlePattern } from '../../enums'
import { combineComponents, getComponents } from '../../lib/atemUtil'

export class MultiViewerWindowOverlaySafeAreaPatternCommand extends SymmetricalCommand<{
safeTitlePattern: SafeTitlePattern[]
}> {
public static readonly rawName = 'StMw'

public readonly multiViewerId: number
public readonly windowIndex: number

constructor(multiviewerId: number, windowIndex: number, safeTitlePattern: SafeTitlePattern[]) {
super({ safeTitlePattern })

this.multiViewerId = multiviewerId
this.windowIndex = windowIndex
}

public serialize(): Buffer {
const buffer = Buffer.alloc(4)
buffer.writeUInt8(this.multiViewerId, 0)
buffer.writeUInt8(this.windowIndex, 1)
buffer.writeUInt8(combineComponents(this.properties.safeTitlePattern), 2)
return buffer
}

public static deserialize(rawCommand: Buffer): MultiViewerWindowOverlaySafeAreaPatternCommand {
const multiViewerId = rawCommand.readUInt8(0)
const windowIndex = rawCommand.readUInt8(1)
const safeTitlePattern = getComponents(rawCommand.readUInt8(2)) as SafeTitlePattern[]

return new MultiViewerWindowOverlaySafeAreaPatternCommand(multiViewerId, windowIndex, safeTitlePattern)
}

public applyToState(state: AtemState): string {
if (!state.info.multiviewer || this.multiViewerId >= state.info.multiviewer.count) {
throw new InvalidIdError('MultiViewer', this.multiViewerId)
}

const multiviewer = AtemStateUtil.getMultiViewer(state, this.multiViewerId)
const window = multiviewer.windows[this.windowIndex]
if (!window) {
throw new InvalidIdError('MultiViewer Window', this.multiViewerId, this.windowIndex)
}
window.safeTitlePattern = this.properties.safeTitlePattern

return `settings.multiViewers.${this.multiViewerId}.windows.${this.windowIndex}.safeTitlePattern`
}
}
4 changes: 3 additions & 1 deletion src/commands/Settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export * from './MultiViewerBorderColorCommand'
export * from './MultiViewerPropertiesCommand'
export * from './MultiViewerSourceCommand'
export * from './MultiViewerVuOpacityCommand'
export * from './MultiViewerWindowVuMeterCommand'
export * from './MultiViewerWindowOverlayPropertiesCommand'
export * from './MultiViewerWindowSafeAreaCommand'
export * from './MultiViewerWindowSafeAreaTypeCommand'
export * from './MultiViewerWindowVuMeterCommand'
export * from './VideoMode'
5 changes: 5 additions & 0 deletions src/enums/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,3 +419,8 @@ export enum TimeMode {
FreeRun = 0,
TimeOfDay = 1,
}

export enum SafeTitlePattern {
Horizontal = 1,
Vertical = 2,
}
10 changes: 9 additions & 1 deletion src/state/settings.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { VideoMode, MultiViewerLayout, TimeMode } from '../enums'
import { VideoMode, MultiViewerLayout, TimeMode, SafeTitlePattern } from '../enums'

export interface MultiViewerSourceState {
source: number
readonly windowIndex: number
readonly supportsVuMeter: boolean
readonly supportsSafeArea: boolean
// readonly supportsOverlayProperties: boolean
}

export interface MultiViewerWindowState extends MultiViewerSourceState {
safeTitle?: boolean
audioMeter?: boolean

safeTitlePattern?: SafeTitlePattern[]
overlayProperties?: MultiViewerWindowOverlayPropertiesState
}

export interface MultiViewerPropertiesState {
Expand All @@ -26,6 +30,10 @@ export interface MultiViewerBorderColorState {
/** Alpha component 0-1000 */
alpha: number
}
export interface MultiViewerWindowOverlayPropertiesState {
labelVisible: boolean
borderVisible: boolean
}

export interface MultiViewer {
readonly index: number
Expand Down

0 comments on commit 313793d

Please sign in to comment.