fix: linter errors
This commit is contained in:
@@ -65,7 +65,7 @@ function createFallbackDescriptor(value: string): ChordDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeDescriptor(descriptor: ChordDescriptor): ChordDescriptor {
|
function normalizeDescriptor(descriptor: ChordDescriptor): ChordDescriptor {
|
||||||
let quality = descriptor.quality;
|
const quality = descriptor.quality;
|
||||||
let extensions = [...descriptor.extensions];
|
let extensions = [...descriptor.extensions];
|
||||||
|
|
||||||
const dedupe = (nextExtensions: ChordExtension[]) => Array.from(new Set(nextExtensions));
|
const dedupe = (nextExtensions: ChordExtension[]) => Array.from(new Set(nextExtensions));
|
||||||
|
|||||||
@@ -79,11 +79,11 @@ function formatKGOneTabLabel(tab: Tab): string {
|
|||||||
return 'Separator';
|
return 'Separator';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDefaultKGOneTab(mode: KGOneMode): Tab {
|
function getDefaultKGOneTab(mode: KGOneMode): Tab {
|
||||||
return mode === 'local-separator' ? 'separator' : 'fullsong';
|
return mode === 'local-separator' ? 'separator' : 'fullsong';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getKGOneMode(): KGOneMode {
|
function getKGOneMode(): KGOneMode {
|
||||||
const enabled = (ConfigManager.instance().get('general.kgone.enabled') as boolean | undefined) ?? false;
|
const enabled = (ConfigManager.instance().get('general.kgone.enabled') as boolean | undefined) ?? false;
|
||||||
return enabled ? 'server' : 'local-separator';
|
return enabled ? 'server' : 'local-separator';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1270,7 +1270,7 @@ const MainContent: React.FC<MainContentProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sortedRegions = [...chordRegions]
|
const sortedRegions = [...chordRegions]
|
||||||
.sort((left, right) => left.getStartFromBeat() - right.getStartFromBeat())
|
.sort((left, right) => left.getStartFromBeat() - right.getStartFromBeat());
|
||||||
const targetRegion = direction === 'forward'
|
const targetRegion = direction === 'forward'
|
||||||
? sortedRegions.find(region => region.getStartFromBeat() > currentStartBeat && region.getStartFromBeat() <= targetBarBeat)
|
? sortedRegions.find(region => region.getStartFromBeat() > currentStartBeat && region.getStartFromBeat() <= targetBarBeat)
|
||||||
: [...sortedRegions]
|
: [...sortedRegions]
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import {
|
|||||||
getRegionStartScrollLeft,
|
getRegionStartScrollLeft,
|
||||||
getRegionPlayheadRelation,
|
getRegionPlayheadRelation,
|
||||||
getScrollLeftForViewportRequest,
|
getScrollLeftForViewportRequest,
|
||||||
} from './PianoRoll';
|
} from './pianoRollViewport';
|
||||||
import type { SheetMeasureMetric } from './sheetNotationTypes';
|
import type { SheetMeasureMetric } from './sheetNotationTypes';
|
||||||
|
|
||||||
function createContainer({ clientWidth, scrollWidth }: { clientWidth: number; scrollWidth: number }): HTMLDivElement {
|
function createContainer({ clientWidth, scrollWidth }: { clientWidth: number; scrollWidth: number }): HTMLDivElement {
|
||||||
|
|||||||
@@ -25,37 +25,12 @@ import {
|
|||||||
import type { PianoRollAutomationType } from './pianoRollAutomation';
|
import type { PianoRollAutomationType } from './pianoRollAutomation';
|
||||||
import type { SheetMeasureMetric } from './sheetNotationTypes';
|
import type { SheetMeasureMetric } from './sheetNotationTypes';
|
||||||
import { getSheetPlayheadPixel, getSheetQuantizationOptions, parseSheetQuantization } from './sheetNotation';
|
import { getSheetPlayheadPixel, getSheetQuantizationOptions, parseSheetQuantization } from './sheetNotation';
|
||||||
|
import {
|
||||||
type RegionPlayheadRelation = 'before' | 'inside' | 'after';
|
createPendingModeSwitchRequest,
|
||||||
type ViewportSwitchAlignment = 'center' | 'region-start' | 'region-end';
|
getRegionStartScrollLeft,
|
||||||
type ViewportClampScope = 'region' | 'track';
|
getScrollLeftForViewportRequest,
|
||||||
|
type PendingModeSwitchRequest,
|
||||||
interface PendingModeSwitchRequest {
|
} from './pianoRollViewport';
|
||||||
sourceSheetMusicViewEnabled: boolean;
|
|
||||||
destinationSheetMusicViewEnabled: boolean;
|
|
||||||
destinationSheetMusicTrackScopeEnabled: boolean;
|
|
||||||
alignment: ViewportSwitchAlignment;
|
|
||||||
anchorBeat: number;
|
|
||||||
clampScope: ViewportClampScope;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ModeSwitchRequestOptions {
|
|
||||||
playheadBeat: number;
|
|
||||||
regionStartBeat: number;
|
|
||||||
regionEndBeat: number;
|
|
||||||
sourceSheetMusicViewEnabled: boolean;
|
|
||||||
destinationSheetMusicViewEnabled: boolean;
|
|
||||||
destinationSheetMusicTrackScopeEnabled: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ScrollLeftForViewportRequestOptions {
|
|
||||||
request: PendingModeSwitchRequest;
|
|
||||||
container: HTMLDivElement;
|
|
||||||
sheetMeasureMetrics: SheetMeasureMetric[];
|
|
||||||
activeRegionStartBeat: number;
|
|
||||||
activeRegionEndBeat: number;
|
|
||||||
songEndBeat: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PianoRollProps {
|
interface PianoRollProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -1389,233 +1364,3 @@ const PianoRoll: React.FC<PianoRollProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default PianoRoll;
|
export default PianoRoll;
|
||||||
|
|
||||||
export function getRegionPlayheadRelation(
|
|
||||||
playheadBeat: number,
|
|
||||||
regionStartBeat: number,
|
|
||||||
regionEndBeat: number
|
|
||||||
): RegionPlayheadRelation {
|
|
||||||
if (playheadBeat < regionStartBeat) {
|
|
||||||
return 'before';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (playheadBeat > regionEndBeat) {
|
|
||||||
return 'after';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'inside';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createPendingModeSwitchRequest({
|
|
||||||
playheadBeat,
|
|
||||||
regionStartBeat,
|
|
||||||
regionEndBeat,
|
|
||||||
sourceSheetMusicViewEnabled,
|
|
||||||
destinationSheetMusicViewEnabled,
|
|
||||||
destinationSheetMusicTrackScopeEnabled,
|
|
||||||
}: ModeSwitchRequestOptions): PendingModeSwitchRequest {
|
|
||||||
const relation = getRegionPlayheadRelation(playheadBeat, regionStartBeat, regionEndBeat);
|
|
||||||
const enteringTrackScopeSheet = (
|
|
||||||
!sourceSheetMusicViewEnabled &&
|
|
||||||
destinationSheetMusicViewEnabled &&
|
|
||||||
destinationSheetMusicTrackScopeEnabled
|
|
||||||
);
|
|
||||||
|
|
||||||
if (relation === 'inside') {
|
|
||||||
return {
|
|
||||||
sourceSheetMusicViewEnabled,
|
|
||||||
destinationSheetMusicViewEnabled,
|
|
||||||
destinationSheetMusicTrackScopeEnabled,
|
|
||||||
alignment: 'center',
|
|
||||||
anchorBeat: playheadBeat,
|
|
||||||
clampScope: destinationSheetMusicTrackScopeEnabled ? 'track' : 'region',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enteringTrackScopeSheet) {
|
|
||||||
return {
|
|
||||||
sourceSheetMusicViewEnabled,
|
|
||||||
destinationSheetMusicViewEnabled,
|
|
||||||
destinationSheetMusicTrackScopeEnabled,
|
|
||||||
alignment: 'center',
|
|
||||||
anchorBeat: playheadBeat,
|
|
||||||
clampScope: 'track',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
sourceSheetMusicViewEnabled,
|
|
||||||
destinationSheetMusicViewEnabled,
|
|
||||||
destinationSheetMusicTrackScopeEnabled,
|
|
||||||
alignment: relation === 'before' ? 'region-start' : 'region-end',
|
|
||||||
anchorBeat: relation === 'before' ? regionStartBeat : regionEndBeat,
|
|
||||||
clampScope: 'region',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHorizontalViewportMetrics(container: HTMLDivElement, sheetMusicViewEnabled: boolean): {
|
|
||||||
visibleWidth: number;
|
|
||||||
keysWidth: number;
|
|
||||||
} {
|
|
||||||
const keysWidth = sheetMusicViewEnabled
|
|
||||||
? 0
|
|
||||||
: (parseInt(
|
|
||||||
getComputedStyle(document.documentElement).getPropertyValue('--region-piano-key-width')
|
|
||||||
) || 60);
|
|
||||||
|
|
||||||
return {
|
|
||||||
visibleWidth: Math.max(0, container.clientWidth - keysWidth),
|
|
||||||
keysWidth,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPixelForAbsoluteBeat(
|
|
||||||
beat: number,
|
|
||||||
sheetMusicViewEnabled: boolean,
|
|
||||||
sheetMusicTrackScopeEnabled: boolean,
|
|
||||||
sheetMeasureMetrics: SheetMeasureMetric[],
|
|
||||||
activeRegionStartBeat: number
|
|
||||||
): number {
|
|
||||||
if (sheetMusicViewEnabled) {
|
|
||||||
return getSheetPlayheadPixel(
|
|
||||||
sheetMusicTrackScopeEnabled
|
|
||||||
? Math.max(0, beat)
|
|
||||||
: Math.max(0, beat - activeRegionStartBeat),
|
|
||||||
sheetMeasureMetrics
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const beatWidth = parseInt(
|
|
||||||
getComputedStyle(document.documentElement).getPropertyValue('--region-grid-beat-width')
|
|
||||||
) || 40;
|
|
||||||
return beat * beatWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getScopeBoundsInPixels(
|
|
||||||
request: PendingModeSwitchRequest,
|
|
||||||
sheetMeasureMetrics: SheetMeasureMetric[],
|
|
||||||
activeRegionStartBeat: number,
|
|
||||||
activeRegionEndBeat: number,
|
|
||||||
songEndBeat: number
|
|
||||||
): { startPx: number; endPx: number } {
|
|
||||||
if (request.destinationSheetMusicViewEnabled) {
|
|
||||||
if (request.clampScope === 'track') {
|
|
||||||
return {
|
|
||||||
startPx: getSheetPlayheadPixel(0, sheetMeasureMetrics),
|
|
||||||
endPx: getSheetPlayheadPixel(songEndBeat, sheetMeasureMetrics),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
startPx: getSheetPlayheadPixel(0, sheetMeasureMetrics),
|
|
||||||
endPx: getSheetPlayheadPixel(activeRegionEndBeat - activeRegionStartBeat, sheetMeasureMetrics),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const beatWidth = parseInt(
|
|
||||||
getComputedStyle(document.documentElement).getPropertyValue('--region-grid-beat-width')
|
|
||||||
) || 40;
|
|
||||||
|
|
||||||
if (request.clampScope === 'track') {
|
|
||||||
return {
|
|
||||||
startPx: 0,
|
|
||||||
endPx: songEndBeat * beatWidth,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
startPx: activeRegionStartBeat * beatWidth,
|
|
||||||
endPx: activeRegionEndBeat * beatWidth,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function clampScrollLeftToContainer(container: HTMLDivElement, scrollLeft: number): number {
|
|
||||||
return Math.max(0, Math.min(scrollLeft, container.scrollWidth - container.clientWidth));
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCenteredScrollLeft({
|
|
||||||
pixelPosition,
|
|
||||||
visibleWidth,
|
|
||||||
scopeStartPx,
|
|
||||||
scopeEndPx,
|
|
||||||
container,
|
|
||||||
}: {
|
|
||||||
pixelPosition: number;
|
|
||||||
visibleWidth: number;
|
|
||||||
scopeStartPx: number;
|
|
||||||
scopeEndPx: number;
|
|
||||||
container: HTMLDivElement;
|
|
||||||
}): number {
|
|
||||||
const unclamped = pixelPosition - visibleWidth / 2;
|
|
||||||
const maxScopeScrollLeft = Math.max(scopeStartPx, scopeEndPx - visibleWidth);
|
|
||||||
const clampedToScope = Math.max(scopeStartPx, Math.min(unclamped, maxScopeScrollLeft));
|
|
||||||
return clampScrollLeftToContainer(container, clampedToScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRegionEndAlignedScrollLeft({
|
|
||||||
visibleWidth,
|
|
||||||
scopeEndPx,
|
|
||||||
container,
|
|
||||||
}: {
|
|
||||||
visibleWidth: number;
|
|
||||||
scopeEndPx: number;
|
|
||||||
container: HTMLDivElement;
|
|
||||||
}): number {
|
|
||||||
return clampScrollLeftToContainer(container, Math.max(0, scopeEndPx - visibleWidth));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getScrollLeftForViewportRequest({
|
|
||||||
request,
|
|
||||||
container,
|
|
||||||
sheetMeasureMetrics,
|
|
||||||
activeRegionStartBeat,
|
|
||||||
activeRegionEndBeat,
|
|
||||||
songEndBeat,
|
|
||||||
}: ScrollLeftForViewportRequestOptions): number {
|
|
||||||
const { visibleWidth } = getHorizontalViewportMetrics(
|
|
||||||
container,
|
|
||||||
request.destinationSheetMusicViewEnabled
|
|
||||||
);
|
|
||||||
const pixelPosition = getPixelForAbsoluteBeat(
|
|
||||||
request.anchorBeat,
|
|
||||||
request.destinationSheetMusicViewEnabled,
|
|
||||||
request.destinationSheetMusicTrackScopeEnabled,
|
|
||||||
sheetMeasureMetrics,
|
|
||||||
activeRegionStartBeat
|
|
||||||
);
|
|
||||||
const { startPx, endPx } = getScopeBoundsInPixels(
|
|
||||||
request,
|
|
||||||
sheetMeasureMetrics,
|
|
||||||
activeRegionStartBeat,
|
|
||||||
activeRegionEndBeat,
|
|
||||||
songEndBeat
|
|
||||||
);
|
|
||||||
|
|
||||||
if (request.alignment === 'region-start') {
|
|
||||||
return clampScrollLeftToContainer(container, startPx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.alignment === 'region-end') {
|
|
||||||
return getRegionEndAlignedScrollLeft({
|
|
||||||
visibleWidth,
|
|
||||||
scopeEndPx: endPx,
|
|
||||||
container,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return getCenteredScrollLeft({
|
|
||||||
pixelPosition,
|
|
||||||
visibleWidth,
|
|
||||||
scopeStartPx: startPx,
|
|
||||||
scopeEndPx: endPx,
|
|
||||||
container,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRegionStartScrollLeft(startBeat: number): number {
|
|
||||||
const beatWidth = parseInt(
|
|
||||||
getComputedStyle(document.documentElement).getPropertyValue('--region-grid-beat-width')
|
|
||||||
) || TOOLBAR_CONSTANTS.BASE_BAR_WIDTH;
|
|
||||||
|
|
||||||
return Math.max(0, startBeat * beatWidth);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -613,7 +613,7 @@ const PianoRollAutomationLane: React.FC<PianoRollAutomationLaneProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let nextSelection = lassoShiftKeyRef.current ? new Set(selectedPointIdSet) : new Set<string>();
|
const nextSelection = lassoShiftKeyRef.current ? new Set(selectedPointIdSet) : new Set<string>();
|
||||||
|
|
||||||
renderedPointsSorted.forEach(point => {
|
renderedPointsSorted.forEach(point => {
|
||||||
const isIntersecting = (
|
const isIntersecting = (
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ vi.mock('vexflow', () => {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addKeySignature() {
|
addKeySignature(...args: unknown[]) {
|
||||||
vexflowMocks.addKeySignatureMock(...arguments);
|
vexflowMocks.addKeySignatureMock(...args);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,264 @@
|
|||||||
|
import { TOOLBAR_CONSTANTS } from '../../constants';
|
||||||
|
import type { SheetMeasureMetric } from './sheetNotationTypes';
|
||||||
|
import { getSheetPlayheadPixel } from './sheetNotation';
|
||||||
|
|
||||||
|
export type RegionPlayheadRelation = 'before' | 'inside' | 'after';
|
||||||
|
type ViewportSwitchAlignment = 'center' | 'region-start' | 'region-end';
|
||||||
|
type ViewportClampScope = 'region' | 'track';
|
||||||
|
|
||||||
|
export interface PendingModeSwitchRequest {
|
||||||
|
sourceSheetMusicViewEnabled: boolean;
|
||||||
|
destinationSheetMusicViewEnabled: boolean;
|
||||||
|
destinationSheetMusicTrackScopeEnabled: boolean;
|
||||||
|
alignment: ViewportSwitchAlignment;
|
||||||
|
anchorBeat: number;
|
||||||
|
clampScope: ViewportClampScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModeSwitchRequestOptions {
|
||||||
|
playheadBeat: number;
|
||||||
|
regionStartBeat: number;
|
||||||
|
regionEndBeat: number;
|
||||||
|
sourceSheetMusicViewEnabled: boolean;
|
||||||
|
destinationSheetMusicViewEnabled: boolean;
|
||||||
|
destinationSheetMusicTrackScopeEnabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScrollLeftForViewportRequestOptions {
|
||||||
|
request: PendingModeSwitchRequest;
|
||||||
|
container: HTMLDivElement;
|
||||||
|
sheetMeasureMetrics: SheetMeasureMetric[];
|
||||||
|
activeRegionStartBeat: number;
|
||||||
|
activeRegionEndBeat: number;
|
||||||
|
songEndBeat: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRegionPlayheadRelation(
|
||||||
|
playheadBeat: number,
|
||||||
|
regionStartBeat: number,
|
||||||
|
regionEndBeat: number
|
||||||
|
): RegionPlayheadRelation {
|
||||||
|
if (playheadBeat < regionStartBeat) {
|
||||||
|
return 'before';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playheadBeat > regionEndBeat) {
|
||||||
|
return 'after';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'inside';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPendingModeSwitchRequest({
|
||||||
|
playheadBeat,
|
||||||
|
regionStartBeat,
|
||||||
|
regionEndBeat,
|
||||||
|
sourceSheetMusicViewEnabled,
|
||||||
|
destinationSheetMusicViewEnabled,
|
||||||
|
destinationSheetMusicTrackScopeEnabled,
|
||||||
|
}: ModeSwitchRequestOptions): PendingModeSwitchRequest {
|
||||||
|
const relation = getRegionPlayheadRelation(playheadBeat, regionStartBeat, regionEndBeat);
|
||||||
|
const enteringTrackScopeSheet = (
|
||||||
|
!sourceSheetMusicViewEnabled &&
|
||||||
|
destinationSheetMusicViewEnabled &&
|
||||||
|
destinationSheetMusicTrackScopeEnabled
|
||||||
|
);
|
||||||
|
|
||||||
|
if (relation === 'inside') {
|
||||||
|
return {
|
||||||
|
sourceSheetMusicViewEnabled,
|
||||||
|
destinationSheetMusicViewEnabled,
|
||||||
|
destinationSheetMusicTrackScopeEnabled,
|
||||||
|
alignment: 'center',
|
||||||
|
anchorBeat: playheadBeat,
|
||||||
|
clampScope: destinationSheetMusicTrackScopeEnabled ? 'track' : 'region',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enteringTrackScopeSheet) {
|
||||||
|
return {
|
||||||
|
sourceSheetMusicViewEnabled,
|
||||||
|
destinationSheetMusicViewEnabled,
|
||||||
|
destinationSheetMusicTrackScopeEnabled,
|
||||||
|
alignment: 'center',
|
||||||
|
anchorBeat: playheadBeat,
|
||||||
|
clampScope: 'track',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceSheetMusicViewEnabled,
|
||||||
|
destinationSheetMusicViewEnabled,
|
||||||
|
destinationSheetMusicTrackScopeEnabled,
|
||||||
|
alignment: relation === 'before' ? 'region-start' : 'region-end',
|
||||||
|
anchorBeat: relation === 'before' ? regionStartBeat : regionEndBeat,
|
||||||
|
clampScope: 'region',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHorizontalViewportMetrics(container: HTMLDivElement, sheetMusicViewEnabled: boolean): {
|
||||||
|
visibleWidth: number;
|
||||||
|
keysWidth: number;
|
||||||
|
} {
|
||||||
|
const keysWidth = sheetMusicViewEnabled
|
||||||
|
? 0
|
||||||
|
: (parseInt(
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--region-piano-key-width')
|
||||||
|
) || 60);
|
||||||
|
|
||||||
|
return {
|
||||||
|
visibleWidth: Math.max(0, container.clientWidth - keysWidth),
|
||||||
|
keysWidth,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPixelForAbsoluteBeat(
|
||||||
|
beat: number,
|
||||||
|
sheetMusicViewEnabled: boolean,
|
||||||
|
sheetMusicTrackScopeEnabled: boolean,
|
||||||
|
sheetMeasureMetrics: SheetMeasureMetric[],
|
||||||
|
activeRegionStartBeat: number
|
||||||
|
): number {
|
||||||
|
if (sheetMusicViewEnabled) {
|
||||||
|
return getSheetPlayheadPixel(
|
||||||
|
sheetMusicTrackScopeEnabled
|
||||||
|
? Math.max(0, beat)
|
||||||
|
: Math.max(0, beat - activeRegionStartBeat),
|
||||||
|
sheetMeasureMetrics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const beatWidth = parseInt(
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--region-grid-beat-width')
|
||||||
|
) || 40;
|
||||||
|
return beat * beatWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getScopeBoundsInPixels(
|
||||||
|
request: PendingModeSwitchRequest,
|
||||||
|
sheetMeasureMetrics: SheetMeasureMetric[],
|
||||||
|
activeRegionStartBeat: number,
|
||||||
|
activeRegionEndBeat: number,
|
||||||
|
songEndBeat: number
|
||||||
|
): { startPx: number; endPx: number } {
|
||||||
|
if (request.destinationSheetMusicViewEnabled) {
|
||||||
|
if (request.clampScope === 'track') {
|
||||||
|
return {
|
||||||
|
startPx: getSheetPlayheadPixel(0, sheetMeasureMetrics),
|
||||||
|
endPx: getSheetPlayheadPixel(songEndBeat, sheetMeasureMetrics),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
startPx: getSheetPlayheadPixel(0, sheetMeasureMetrics),
|
||||||
|
endPx: getSheetPlayheadPixel(activeRegionEndBeat - activeRegionStartBeat, sheetMeasureMetrics),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const beatWidth = parseInt(
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--region-grid-beat-width')
|
||||||
|
) || 40;
|
||||||
|
|
||||||
|
if (request.clampScope === 'track') {
|
||||||
|
return {
|
||||||
|
startPx: 0,
|
||||||
|
endPx: songEndBeat * beatWidth,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
startPx: activeRegionStartBeat * beatWidth,
|
||||||
|
endPx: activeRegionEndBeat * beatWidth,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampScrollLeftToContainer(container: HTMLDivElement, scrollLeft: number): number {
|
||||||
|
return Math.max(0, Math.min(scrollLeft, container.scrollWidth - container.clientWidth));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCenteredScrollLeft({
|
||||||
|
pixelPosition,
|
||||||
|
visibleWidth,
|
||||||
|
scopeStartPx,
|
||||||
|
scopeEndPx,
|
||||||
|
container,
|
||||||
|
}: {
|
||||||
|
pixelPosition: number;
|
||||||
|
visibleWidth: number;
|
||||||
|
scopeStartPx: number;
|
||||||
|
scopeEndPx: number;
|
||||||
|
container: HTMLDivElement;
|
||||||
|
}): number {
|
||||||
|
const unclamped = pixelPosition - visibleWidth / 2;
|
||||||
|
const maxScopeScrollLeft = Math.max(scopeStartPx, scopeEndPx - visibleWidth);
|
||||||
|
const clampedToScope = Math.max(scopeStartPx, Math.min(unclamped, maxScopeScrollLeft));
|
||||||
|
return clampScrollLeftToContainer(container, clampedToScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRegionEndAlignedScrollLeft({
|
||||||
|
visibleWidth,
|
||||||
|
scopeEndPx,
|
||||||
|
container,
|
||||||
|
}: {
|
||||||
|
visibleWidth: number;
|
||||||
|
scopeEndPx: number;
|
||||||
|
container: HTMLDivElement;
|
||||||
|
}): number {
|
||||||
|
return clampScrollLeftToContainer(container, Math.max(0, scopeEndPx - visibleWidth));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getScrollLeftForViewportRequest({
|
||||||
|
request,
|
||||||
|
container,
|
||||||
|
sheetMeasureMetrics,
|
||||||
|
activeRegionStartBeat,
|
||||||
|
activeRegionEndBeat,
|
||||||
|
songEndBeat,
|
||||||
|
}: ScrollLeftForViewportRequestOptions): number {
|
||||||
|
const { visibleWidth } = getHorizontalViewportMetrics(
|
||||||
|
container,
|
||||||
|
request.destinationSheetMusicViewEnabled
|
||||||
|
);
|
||||||
|
const pixelPosition = getPixelForAbsoluteBeat(
|
||||||
|
request.anchorBeat,
|
||||||
|
request.destinationSheetMusicViewEnabled,
|
||||||
|
request.destinationSheetMusicTrackScopeEnabled,
|
||||||
|
sheetMeasureMetrics,
|
||||||
|
activeRegionStartBeat
|
||||||
|
);
|
||||||
|
const { startPx, endPx } = getScopeBoundsInPixels(
|
||||||
|
request,
|
||||||
|
sheetMeasureMetrics,
|
||||||
|
activeRegionStartBeat,
|
||||||
|
activeRegionEndBeat,
|
||||||
|
songEndBeat
|
||||||
|
);
|
||||||
|
|
||||||
|
if (request.alignment === 'region-start') {
|
||||||
|
return clampScrollLeftToContainer(container, startPx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.alignment === 'region-end') {
|
||||||
|
return getRegionEndAlignedScrollLeft({
|
||||||
|
visibleWidth,
|
||||||
|
scopeEndPx: endPx,
|
||||||
|
container,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return getCenteredScrollLeft({
|
||||||
|
pixelPosition,
|
||||||
|
visibleWidth,
|
||||||
|
scopeStartPx: startPx,
|
||||||
|
scopeEndPx: endPx,
|
||||||
|
container,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRegionStartScrollLeft(startBeat: number): number {
|
||||||
|
const beatWidth = parseInt(
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--region-grid-beat-width')
|
||||||
|
) || TOOLBAR_CONSTANTS.BASE_BAR_WIDTH;
|
||||||
|
|
||||||
|
return Math.max(0, startBeat * beatWidth);
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ vi.mock('../../stores/projectStore', () => ({
|
|||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { __trackAutomationTestUtils } = await import('./TrackAutomationLane');
|
const __trackAutomationTestUtils = await import('./trackAutomationLaneUtils');
|
||||||
|
|
||||||
describe('TrackAutomationLane mapping', () => {
|
describe('TrackAutomationLane mapping', () => {
|
||||||
it('maps volume 0.0dB to the visual midpoint and back', () => {
|
it('maps volume 0.0dB to the visual midpoint and back', () => {
|
||||||
|
|||||||
@@ -11,6 +11,14 @@ import { PIANO_ROLL_CONSTANTS } from '../../constants';
|
|||||||
import { AUDIO_INTERFACE_CONSTANTS } from '../../constants/coreConstants';
|
import { AUDIO_INTERFACE_CONSTANTS } from '../../constants/coreConstants';
|
||||||
import { isModifierKeyPressed } from '../../util/osUtil';
|
import { isModifierKeyPressed } from '../../util/osUtil';
|
||||||
import { KGMainContentState } from '../../core/state/KGMainContentState';
|
import { KGMainContentState } from '../../core/state/KGMainContentState';
|
||||||
|
import {
|
||||||
|
formatAutomationValue,
|
||||||
|
getLaneMetrics,
|
||||||
|
volumeToY,
|
||||||
|
yToVolume,
|
||||||
|
panToY,
|
||||||
|
yToPan,
|
||||||
|
} from './trackAutomationLaneUtils';
|
||||||
|
|
||||||
interface TrackAutomationLaneProps {
|
interface TrackAutomationLaneProps {
|
||||||
track: KGTrack;
|
track: KGTrack;
|
||||||
@@ -32,7 +40,6 @@ interface PreviewPoint {
|
|||||||
value: number;
|
value: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LANE_PADDING_Y = 12;
|
|
||||||
const POINT_RADIUS = 5;
|
const POINT_RADIUS = 5;
|
||||||
const SELECTED_POINT_COLOR = '#FFFFFF';
|
const SELECTED_POINT_COLOR = '#FFFFFF';
|
||||||
const TRACK_AUTOMATION_COLORS: Record<TrackAutomationType, string> = {
|
const TRACK_AUTOMATION_COLORS: Record<TrackAutomationType, string> = {
|
||||||
@@ -40,105 +47,6 @@ const TRACK_AUTOMATION_COLORS: Record<TrackAutomationType, string> = {
|
|||||||
pan: '#90EE90',
|
pan: '#90EE90',
|
||||||
};
|
};
|
||||||
|
|
||||||
function formatAutomationValue(automationType: TrackAutomationType, value: number): string {
|
|
||||||
if (automationType === 'volume') {
|
|
||||||
if (value <= AUDIO_INTERFACE_CONSTANTS.MIN_TRACK_VOLUME_DB) {
|
|
||||||
return '−∞';
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${value >= 0 ? '+' : ''}${value.toFixed(1)}dB`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const logicPanValue = value <= 0
|
|
||||||
? Math.round(value * 64)
|
|
||||||
: Math.round(value * 63);
|
|
||||||
return `${logicPanValue >= 0 ? '+' : ''}${logicPanValue}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLaneMetrics(laneHeight: number) {
|
|
||||||
const top = LANE_PADDING_Y;
|
|
||||||
const bottom = laneHeight - LANE_PADDING_Y;
|
|
||||||
const middle = Math.round((top + bottom) / 2);
|
|
||||||
return { top, middle, bottom };
|
|
||||||
}
|
|
||||||
|
|
||||||
function volumeToY(value: number, laneHeight: number): number {
|
|
||||||
const clamped = Math.max(
|
|
||||||
AUDIO_INTERFACE_CONSTANTS.MIN_TRACK_VOLUME_DB,
|
|
||||||
Math.min(AUDIO_INTERFACE_CONSTANTS.MAX_TRACK_VOLUME_DB, value)
|
|
||||||
);
|
|
||||||
const { top, middle, bottom } = getLaneMetrics(laneHeight);
|
|
||||||
|
|
||||||
if (clamped >= 0) {
|
|
||||||
const normalized = clamped / AUDIO_INTERFACE_CONSTANTS.MAX_TRACK_VOLUME_DB;
|
|
||||||
return middle - (middle - top) * normalized;
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalized = clamped / AUDIO_INTERFACE_CONSTANTS.MIN_TRACK_VOLUME_DB;
|
|
||||||
return middle + (bottom - middle) * normalized;
|
|
||||||
}
|
|
||||||
|
|
||||||
function yToVolume(y: number, laneHeight: number): number {
|
|
||||||
const { top, middle, bottom } = getLaneMetrics(laneHeight);
|
|
||||||
const clampedY = Math.min(bottom, Math.max(top, y));
|
|
||||||
|
|
||||||
if (clampedY <= middle) {
|
|
||||||
const normalized = middle === top ? 0 : (middle - clampedY) / (middle - top);
|
|
||||||
return Math.min(
|
|
||||||
AUDIO_INTERFACE_CONSTANTS.MAX_TRACK_VOLUME_DB,
|
|
||||||
Math.max(0, normalized * AUDIO_INTERFACE_CONSTANTS.MAX_TRACK_VOLUME_DB)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalized = bottom === middle ? 0 : (clampedY - middle) / (bottom - middle);
|
|
||||||
return Math.max(
|
|
||||||
AUDIO_INTERFACE_CONSTANTS.MIN_TRACK_VOLUME_DB,
|
|
||||||
Math.min(0, normalized * AUDIO_INTERFACE_CONSTANTS.MIN_TRACK_VOLUME_DB)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function panToY(value: number, laneHeight: number): number {
|
|
||||||
const clamped = Math.max(-1, Math.min(1, value));
|
|
||||||
const { top, middle, bottom } = getLaneMetrics(laneHeight);
|
|
||||||
|
|
||||||
if (clamped === 0) {
|
|
||||||
return middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clamped > 0) {
|
|
||||||
return middle - (middle - top) * clamped;
|
|
||||||
}
|
|
||||||
|
|
||||||
return middle + (bottom - middle) * Math.abs(clamped);
|
|
||||||
}
|
|
||||||
|
|
||||||
function yToPan(y: number, laneHeight: number): number {
|
|
||||||
const { top, middle, bottom } = getLaneMetrics(laneHeight);
|
|
||||||
const clampedY = Math.min(bottom, Math.max(top, y));
|
|
||||||
|
|
||||||
// Reserve a full pixel around the midpoint for exact center.
|
|
||||||
if (Math.abs(clampedY - middle) <= 0.5) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clampedY < middle) {
|
|
||||||
const normalized = middle === top ? 0 : (middle - clampedY) / (middle - top);
|
|
||||||
return Math.max(0, Math.min(1, normalized));
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalized = bottom === middle ? 0 : (clampedY - middle) / (bottom - middle);
|
|
||||||
return Math.max(-1, Math.min(0, -normalized));
|
|
||||||
}
|
|
||||||
|
|
||||||
export const __trackAutomationTestUtils = {
|
|
||||||
formatAutomationValue,
|
|
||||||
getLaneMetrics,
|
|
||||||
volumeToY,
|
|
||||||
yToVolume,
|
|
||||||
panToY,
|
|
||||||
yToPan,
|
|
||||||
};
|
|
||||||
|
|
||||||
const TrackAutomationLane: React.FC<TrackAutomationLaneProps> = ({
|
const TrackAutomationLane: React.FC<TrackAutomationLaneProps> = ({
|
||||||
track,
|
track,
|
||||||
automationType,
|
automationType,
|
||||||
@@ -467,7 +375,7 @@ const TrackAutomationLane: React.FC<TrackAutomationLaneProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let nextSelection = lassoShiftKeyRef.current ? new Set(selectedPointIdSet) : new Set<string>();
|
const nextSelection = lassoShiftKeyRef.current ? new Set(selectedPointIdSet) : new Set<string>();
|
||||||
renderedPoints.forEach(point => {
|
renderedPoints.forEach(point => {
|
||||||
const isIntersecting = (
|
const isIntersecting = (
|
||||||
point.x + POINT_RADIUS >= left &&
|
point.x + POINT_RADIUS >= left &&
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import { AUDIO_INTERFACE_CONSTANTS } from '../../constants/coreConstants';
|
||||||
|
import type { TrackAutomationType } from '../../core/track/KGTrackAutomationPoint';
|
||||||
|
|
||||||
|
const LANE_PADDING_Y = 12;
|
||||||
|
|
||||||
|
export function formatAutomationValue(automationType: TrackAutomationType, value: number): string {
|
||||||
|
if (automationType === 'volume') {
|
||||||
|
if (value <= AUDIO_INTERFACE_CONSTANTS.MIN_TRACK_VOLUME_DB) {
|
||||||
|
return '−∞';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${value >= 0 ? '+' : ''}${value.toFixed(1)}dB`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const logicPanValue = value <= 0
|
||||||
|
? Math.round(value * 64)
|
||||||
|
: Math.round(value * 63);
|
||||||
|
return `${logicPanValue >= 0 ? '+' : ''}${logicPanValue}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLaneMetrics(laneHeight: number) {
|
||||||
|
const top = LANE_PADDING_Y;
|
||||||
|
const bottom = laneHeight - LANE_PADDING_Y;
|
||||||
|
const middle = Math.round((top + bottom) / 2);
|
||||||
|
return { top, middle, bottom };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function volumeToY(value: number, laneHeight: number): number {
|
||||||
|
const clamped = Math.max(
|
||||||
|
AUDIO_INTERFACE_CONSTANTS.MIN_TRACK_VOLUME_DB,
|
||||||
|
Math.min(AUDIO_INTERFACE_CONSTANTS.MAX_TRACK_VOLUME_DB, value)
|
||||||
|
);
|
||||||
|
const { top, middle, bottom } = getLaneMetrics(laneHeight);
|
||||||
|
|
||||||
|
if (clamped >= 0) {
|
||||||
|
const normalized = clamped / AUDIO_INTERFACE_CONSTANTS.MAX_TRACK_VOLUME_DB;
|
||||||
|
return middle - (middle - top) * normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalized = clamped / AUDIO_INTERFACE_CONSTANTS.MIN_TRACK_VOLUME_DB;
|
||||||
|
return middle + (bottom - middle) * normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function yToVolume(y: number, laneHeight: number): number {
|
||||||
|
const { top, middle, bottom } = getLaneMetrics(laneHeight);
|
||||||
|
const clampedY = Math.min(bottom, Math.max(top, y));
|
||||||
|
|
||||||
|
if (clampedY <= middle) {
|
||||||
|
const normalized = middle === top ? 0 : (middle - clampedY) / (middle - top);
|
||||||
|
return Math.min(
|
||||||
|
AUDIO_INTERFACE_CONSTANTS.MAX_TRACK_VOLUME_DB,
|
||||||
|
Math.max(0, normalized * AUDIO_INTERFACE_CONSTANTS.MAX_TRACK_VOLUME_DB)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalized = bottom === middle ? 0 : (clampedY - middle) / (bottom - middle);
|
||||||
|
return Math.max(
|
||||||
|
AUDIO_INTERFACE_CONSTANTS.MIN_TRACK_VOLUME_DB,
|
||||||
|
Math.min(0, normalized * AUDIO_INTERFACE_CONSTANTS.MIN_TRACK_VOLUME_DB)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function panToY(value: number, laneHeight: number): number {
|
||||||
|
const clamped = Math.max(-1, Math.min(1, value));
|
||||||
|
const { top, middle, bottom } = getLaneMetrics(laneHeight);
|
||||||
|
|
||||||
|
if (clamped === 0) {
|
||||||
|
return middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clamped > 0) {
|
||||||
|
return middle - (middle - top) * clamped;
|
||||||
|
}
|
||||||
|
|
||||||
|
return middle + (bottom - middle) * Math.abs(clamped);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function yToPan(y: number, laneHeight: number): number {
|
||||||
|
const { top, middle, bottom } = getLaneMetrics(laneHeight);
|
||||||
|
const clampedY = Math.min(bottom, Math.max(top, y));
|
||||||
|
|
||||||
|
if (Math.abs(clampedY - middle) <= 0.5) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clampedY < middle) {
|
||||||
|
const normalized = middle === top ? 0 : (middle - clampedY) / (middle - top);
|
||||||
|
return Math.max(0, Math.min(1, normalized));
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalized = bottom === middle ? 0 : (clampedY - middle) / (bottom - middle);
|
||||||
|
return Math.max(-1, Math.min(0, -normalized));
|
||||||
|
}
|
||||||
@@ -10,12 +10,73 @@ function localSeparatorLog(message: string, payload?: unknown): void {
|
|||||||
console.log(`[localSeparator] ${message}`, payload);
|
console.log(`[localSeparator] ${message}`, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
type GPUDeviceLike = any;
|
interface GPUBufferLike {
|
||||||
type GPUBufferLike = any;
|
destroy(): void;
|
||||||
type GPUComputePipelineLike = any;
|
mapAsync(mode: number): Promise<void>;
|
||||||
|
getMappedRange(): ArrayBuffer;
|
||||||
|
unmap(): void;
|
||||||
|
}
|
||||||
|
|
||||||
declare const GPUBufferUsage: any;
|
interface GPUComputePassEncoderLike {
|
||||||
declare const GPUMapMode: any;
|
setPipeline(pipeline: GPUComputePipelineLike): void;
|
||||||
|
setBindGroup(index: number, bindGroup: unknown): void;
|
||||||
|
dispatchWorkgroups(x: number): void;
|
||||||
|
end(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GPUCommandEncoderLike {
|
||||||
|
copyBufferToBuffer(source: GPUBufferLike, sourceOffset: number, destination: GPUBufferLike, destinationOffset: number, size: number): void;
|
||||||
|
beginComputePass(): GPUComputePassEncoderLike;
|
||||||
|
finish(): unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GPUComputePipelineLike {
|
||||||
|
getBindGroupLayout(index: number): unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GPUDeviceLike {
|
||||||
|
queue: {
|
||||||
|
submit(commands: unknown[]): void;
|
||||||
|
writeBuffer(buffer: GPUBufferLike, bufferOffset: number, data: BufferSource): void;
|
||||||
|
};
|
||||||
|
createBuffer(descriptor: { size: number; usage: number }): GPUBufferLike;
|
||||||
|
createCommandEncoder(): GPUCommandEncoderLike;
|
||||||
|
createBindGroup(descriptor: { layout: unknown; entries: Array<{ binding: number; resource: { buffer: GPUBufferLike } }> }): unknown;
|
||||||
|
createComputePipeline(descriptor: {
|
||||||
|
layout: 'auto';
|
||||||
|
compute: {
|
||||||
|
module: unknown;
|
||||||
|
entryPoint: string;
|
||||||
|
};
|
||||||
|
}): GPUComputePipelineLike;
|
||||||
|
createShaderModule(descriptor: { code: string }): unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GPUAdapterLike {
|
||||||
|
features?: {
|
||||||
|
values?(): IterableIterator<unknown>;
|
||||||
|
};
|
||||||
|
limits?: unknown;
|
||||||
|
info?: unknown;
|
||||||
|
requestDevice(): Promise<GPUDeviceLike>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NavigatorWithGpu {
|
||||||
|
gpu?: {
|
||||||
|
requestAdapter(options: { powerPreference: string }): Promise<GPUAdapterLike | null>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const GPUBufferUsage: {
|
||||||
|
COPY_DST: number;
|
||||||
|
MAP_READ: number;
|
||||||
|
STORAGE: number;
|
||||||
|
COPY_SRC: number;
|
||||||
|
UNIFORM: number;
|
||||||
|
};
|
||||||
|
declare const GPUMapMode: {
|
||||||
|
READ: number;
|
||||||
|
};
|
||||||
|
|
||||||
const FRAMING_SHADER = `
|
const FRAMING_SHADER = `
|
||||||
struct Params {
|
struct Params {
|
||||||
@@ -85,7 +146,7 @@ export class LocalSeparatorGpuDsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
localSeparatorLog('Requesting WebGPU adapter for GPU DSP.');
|
localSeparatorLog('Requesting WebGPU adapter for GPU DSP.');
|
||||||
const adapter = await (navigator as { gpu?: { requestAdapter: (options: { powerPreference: string }) => Promise<any> } }).gpu?.requestAdapter({
|
const adapter = await (navigator as NavigatorWithGpu).gpu?.requestAdapter({
|
||||||
powerPreference: 'high-performance',
|
powerPreference: 'high-performance',
|
||||||
});
|
});
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
|
|||||||
Reference in New Issue
Block a user