diff --git a/src/components/MainContent.test.tsx b/src/components/MainContent.test.tsx
index dd038f4..d278d14 100644
--- a/src/components/MainContent.test.tsx
+++ b/src/components/MainContent.test.tsx
@@ -58,6 +58,10 @@ const storeState = {
addAudioTrack: vi.fn(),
projectName: 'Test Project',
savedProjectName: 'Test Project',
+ showGlobalTracks: false,
+ setShowGlobalTracks: vi.fn((show: boolean) => {
+ storeState.showGlobalTracks = show;
+ }),
requestPianoRollScroll: vi.fn(),
mainContentScrollRequest: null,
activeTrackAutomationTrackId: null,
@@ -82,6 +86,11 @@ const finishTrackCreateDialogClose = () => {
}
};
+const toggleGlobalTracksAndRerender = (rerender: (ui: React.ReactNode) => void) => {
+ fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ rerender();
+};
+
vi.mock('../stores/projectStore', () => ({
useProjectStore: (selector?: StoreSelector) => (
selector ? selector(storeState) : storeState
@@ -160,6 +169,7 @@ describe('MainContent', () => {
storeState.selectedRegionIds = [];
storeState.activeRegionId = null;
storeState.showPianoRoll = false;
+ storeState.showGlobalTracks = false;
storeState.playheadPosition = 0;
storeState.timeSignature = { numerator: 4, denominator: 4 };
storeState.clearAllSelections.mockClear();
@@ -171,6 +181,7 @@ describe('MainContent', () => {
storeState.openSpectrogramViewer.mockClear();
storeState.addTrack.mockClear();
storeState.addAudioTrack.mockClear();
+ storeState.setShowGlobalTracks.mockClear();
executeCommandMock.mockClear();
});
@@ -385,29 +396,35 @@ describe('MainContent', () => {
expect(screen.queryByRole('dialog', { name: 'Create New Track' })).not.toBeInTheDocument();
});
- it('toggles the mock global tracks button active state locally', () => {
- render();
+ it('toggles the persisted global tracks button active state through the store', () => {
+ const { rerender } = render();
- const globalTracksButton = screen.getByRole('button', { name: 'Show global tracks' });
+ let globalTracksButton = screen.getByRole('button', { name: 'Show global tracks' });
expect(globalTracksButton.className).not.toContain('active');
fireEvent.click(globalTracksButton);
+ expect(storeState.setShowGlobalTracks).toHaveBeenCalledWith(true);
+ rerender();
+ globalTracksButton = screen.getByRole('button', { name: 'Show global tracks' });
expect(globalTracksButton.className).toContain('active');
fireEvent.click(globalTracksButton);
+ expect(storeState.setShowGlobalTracks).toHaveBeenCalledWith(false);
+ rerender();
+ globalTracksButton = screen.getByRole('button', { name: 'Show global tracks' });
expect(globalTracksButton.className).not.toContain('active');
});
- it('renders the four mock global tracks only when toggled on', () => {
- render();
+ it('renders the four global tracks only when the persisted toggle is on', () => {
+ const { rerender } = render();
expect(screen.queryByText('Marker')).not.toBeInTheDocument();
expect(screen.queryByText('Tempo')).not.toBeInTheDocument();
expect(screen.queryByText('Key Signature')).not.toBeInTheDocument();
expect(screen.queryByText('Chord')).not.toBeInTheDocument();
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
expect(screen.getByText('Marker')).toBeInTheDocument();
expect(screen.getByText('Tempo')).toBeInTheDocument();
@@ -416,7 +433,7 @@ describe('MainContent', () => {
const globalTracksInfoShell = screen.getByRole('button', { name: 'Add Marker global track item' }).closest('.global-tracks-info-shell') as HTMLElement;
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
fireEvent.transitionEnd(globalTracksInfoShell);
expect(screen.queryByText('Marker')).not.toBeInTheDocument();
@@ -427,9 +444,9 @@ describe('MainContent', () => {
it('routes the tempo and chord global track add buttons through commands', () => {
storeState.playheadPosition = 5;
- render();
+ const { rerender } = render();
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
fireEvent.click(screen.getByRole('button', { name: 'Add Tempo global track item' }));
expect(executeCommandMock).toHaveBeenCalledTimes(1);
@@ -450,10 +467,10 @@ describe('MainContent', () => {
]);
storeState.globalTracks = globalTracks;
- render();
+ const { rerender } = render();
fireEvent.click(screen.getByRole('button', { name: 'select-midi-region' }));
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
fireEvent.click(screen.getByText('Am'));
expect(storeState.selectedRegionIds).toEqual(['chord-1']);
@@ -471,9 +488,9 @@ describe('MainContent', () => {
]);
storeState.globalTracks = globalTracks;
- render();
+ const { rerender } = render();
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
fireEvent.click(screen.getByText('Am'));
fireEvent.click(screen.getByText('G'), { shiftKey: true });
@@ -492,9 +509,9 @@ describe('MainContent', () => {
]);
storeState.globalTracks = globalTracks;
- render();
+ const { rerender } = render();
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
const firstChord = screen.getByText('Am');
const lastChord = screen.getByText('G');
@@ -524,9 +541,9 @@ describe('MainContent', () => {
]);
storeState.globalTracks = globalTracks;
- render();
+ const { rerender } = render();
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
fireEvent.click(screen.getByText('Am'));
fireEvent.click(screen.getByText('128 BPM'), { metaKey: true });
@@ -548,9 +565,9 @@ describe('MainContent', () => {
]);
storeState.globalTracks = globalTracks;
- render();
+ const { rerender } = render();
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
fireEvent.click(screen.getByText('Am'));
fireEvent.click(screen.getByText('140 BPM'), { shiftKey: true });
@@ -566,9 +583,9 @@ describe('MainContent', () => {
storeState.globalTracks = globalTracks;
storeState.playheadPosition = 3;
- render();
+ const { rerender } = render();
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
fireEvent.click(screen.getByRole('button', { name: 'Add Chord global track item' }));
expect(executeCommandMock).toHaveBeenCalledTimes(1);
@@ -586,9 +603,9 @@ describe('MainContent', () => {
]);
storeState.globalTracks = globalTracks;
- render();
+ const { rerender } = render();
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
fireEvent.doubleClick(screen.getByText('Am'));
fireEvent.keyDown(screen.getByRole('textbox'), { key: 'Tab' });
@@ -605,9 +622,9 @@ describe('MainContent', () => {
]);
storeState.globalTracks = globalTracks;
- render();
+ const { rerender } = render();
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
fireEvent.doubleClick(screen.getByText('Am'));
fireEvent.keyDown(screen.getByRole('textbox'), { key: 'Tab' });
@@ -626,9 +643,9 @@ describe('MainContent', () => {
]);
storeState.globalTracks = globalTracks;
- render();
+ const { rerender } = render();
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
fireEvent.doubleClick(screen.getByText('G'));
fireEvent.keyDown(screen.getByRole('textbox'), { key: 'Tab', shiftKey: true });
@@ -639,18 +656,18 @@ describe('MainContent', () => {
});
it('routes the signature global track add button through a command', () => {
- render();
+ const { rerender } = render();
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
fireEvent.click(screen.getByRole('button', { name: 'Add Key Signature global track item' }));
expect(executeCommandMock).toHaveBeenCalledTimes(1);
});
it('creates new marker regions with a one-bar default length', () => {
- render();
+ const { rerender } = render();
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
fireEvent.click(screen.getByRole('button', { name: 'Add Marker global track item' }));
expect(executeCommandMock).toHaveBeenCalledTimes(1);
@@ -672,13 +689,13 @@ describe('MainContent', () => {
return 1;
});
- const { container } = render();
+ const { container, rerender } = render();
const mainContent = container.querySelector('.main-content') as HTMLDivElement;
Object.defineProperty(mainContent, 'scrollWidth', { configurable: true, value: 4000 });
Object.defineProperty(mainContent, 'clientWidth', { configurable: true, value: 900 });
mainContent.scrollLeft = 500;
- fireEvent.click(screen.getByRole('button', { name: 'Show global tracks' }));
+ toggleGlobalTracksAndRerender(rerender);
fireEvent.doubleClick(screen.getByText('Am'));
await waitFor(() => {
diff --git a/src/components/MainContent.tsx b/src/components/MainContent.tsx
index e697776..5496747 100644
--- a/src/components/MainContent.tsx
+++ b/src/components/MainContent.tsx
@@ -68,6 +68,8 @@ const MainContent: React.FC = ({
addAudioTrack,
projectName,
savedProjectName,
+ showGlobalTracks,
+ setShowGlobalTracks,
requestPianoRollScroll,
mainContentScrollRequest,
activeTrackAutomationTrackId,
@@ -84,7 +86,6 @@ const MainContent: React.FC = ({
const [draggedTrackIndex, setDraggedTrackIndex] = useState(null);
const [dragOverTrackIndex, setDragOverTrackIndex] = useState(null);
const [showCreateTrackModal, setShowCreateTrackModal] = useState(false);
- const [showGlobalTracksMock, setShowGlobalTracksMock] = useState(false);
const mainContentRegions = useMainContentRegions({
tracks,
@@ -252,10 +253,10 @@ const MainContent: React.FC = ({
setShowCreateTrackModal(true);
}, []);
- const handleToggleGlobalTracksMock = useCallback((event: React.MouseEvent) => {
+ const handleToggleGlobalTracks = useCallback((event: React.MouseEvent) => {
event.stopPropagation();
- setShowGlobalTracksMock(previous => !previous);
- }, []);
+ setShowGlobalTracks(!showGlobalTracks);
+ }, [setShowGlobalTracks, showGlobalTracks]);
const handleGlobalChordDropToTrack = useCallback(async (draggedRegionId: string, trackIndex: number) => {
const targetTrack = tracks[trackIndex];
@@ -348,10 +349,10 @@ const MainContent: React.FC = ({
@@ -385,7 +386,7 @@ const MainContent: React.FC = ({
KGTrack, {
@@ -89,7 +97,7 @@ export class KGProject {
private globalTracks: KGGlobalTrack[] = createDefaultGlobalTracks();
// Constructor
- constructor(name: string = RESERVED_PROJECT_NAME, maxBars: number = 32, currentBars: number = 0, bpm: number = TIME_CONSTANTS.DEFAULT_BPM, timeSignature: TimeSignature = { numerator: 4, denominator: 4 }, keySignature: KeySignature = "C major", selectedMode: string = "ionian", isLooping: boolean = false, loopingRange: [number, number] = [0, 0], barWidthMultiplier: number = 2, tracks: KGTrack[] = [], projectStructureVersion: number = KGProject.CURRENT_PROJECT_STRUCTURE_VERSION, pianoRollZoom: number = 1, globalTracks: KGGlobalTrack[] = createDefaultGlobalTracks()) {
+ constructor(name: string = RESERVED_PROJECT_NAME, maxBars: number = 32, currentBars: number = 0, bpm: number = TIME_CONSTANTS.DEFAULT_BPM, timeSignature: TimeSignature = { numerator: 4, denominator: 4 }, keySignature: KeySignature = "C major", selectedMode: string = "ionian", isLooping: boolean = false, loopingRange: [number, number] = [0, 0], barWidthMultiplier: number = 2, tracks: KGTrack[] = [], projectStructureVersion: number = KGProject.CURRENT_PROJECT_STRUCTURE_VERSION, pianoRollZoom: number = 1, globalTracks: KGGlobalTrack[] = createDefaultGlobalTracks(), showGlobalTracks: boolean = false, isMetronomeEnabled: boolean = false) {
this.name = name;
this.maxBars = maxBars;
this.currentBars = currentBars;
@@ -104,6 +112,8 @@ export class KGProject {
this.projectStructureVersion = projectStructureVersion;
this.pianoRollZoom = pianoRollZoom;
this.globalTracks = globalTracks;
+ this.showGlobalTracks = showGlobalTracks;
+ this.isMetronomeEnabled = isMetronomeEnabled;
}
// Getters
@@ -188,6 +198,22 @@ export class KGProject {
return this.projectStructureVersion;
}
+ public getShowGlobalTracks(): boolean {
+ return this.showGlobalTracks;
+ }
+
+ public setShowGlobalTracks(showGlobalTracks: boolean): void {
+ this.showGlobalTracks = showGlobalTracks;
+ }
+
+ public getIsMetronomeEnabled(): boolean {
+ return this.isMetronomeEnabled;
+ }
+
+ public setIsMetronomeEnabled(isMetronomeEnabled: boolean): void {
+ this.isMetronomeEnabled = isMetronomeEnabled;
+ }
+
public getIsLooping(): boolean {
return this.isLooping;
}
diff --git a/src/core/io/KGProjectStorage.test.ts b/src/core/io/KGProjectStorage.test.ts
index 385a5ff..9a29ada 100644
--- a/src/core/io/KGProjectStorage.test.ts
+++ b/src/core/io/KGProjectStorage.test.ts
@@ -163,6 +163,20 @@ describe('KGProjectStorage', () => {
expect(loaded!.getPianoRollZoom()).toBe(5);
});
+ it('preserves persisted global-track visibility and metronome state when saving and loading', async () => {
+ const project = createTestProject('Toggle Song');
+ project.setShowGlobalTracks(true);
+ project.setIsMetronomeEnabled(true);
+
+ await storage.save('Toggle Song', project);
+
+ const loaded = await storage.load('Toggle Song');
+
+ expect(loaded).not.toBeNull();
+ expect(loaded!.getShowGlobalTracks()).toBe(true);
+ expect(loaded!.getIsMetronomeEnabled()).toBe(true);
+ });
+
it('preserves global tracks and marker regions when saving and loading', async () => {
const project = createTestProject('Marker Song');
const markerTrack = project.getGlobalTracks().find(track => track.getType() === GlobalTrackType.Marker);
diff --git a/src/core/project-upgrader/KGProjectUpgrader.ts b/src/core/project-upgrader/KGProjectUpgrader.ts
index 09e4fad..ec62425 100644
--- a/src/core/project-upgrader/KGProjectUpgrader.ts
+++ b/src/core/project-upgrader/KGProjectUpgrader.ts
@@ -14,6 +14,7 @@ import { upgradeToV12 } from './upgradeToV12';
import { upgradeToV13 } from './upgradeToV13';
import { upgradeToV14 } from './upgradeToV14';
import { upgradeToV15 } from './upgradeToV15';
+import { upgradeToV16 } from './upgradeToV16';
/**
* Upgrade the given project to the latest structure version, one version at a time.
@@ -93,6 +94,10 @@ export function upgradeProjectToLatest(project: KGProject): KGProject {
workingProject = upgradeToV15(workingProject);
break;
}
+ case 16: {
+ workingProject = upgradeToV16(workingProject);
+ break;
+ }
default: {
// If an upgrader is missing, throw to prevent loading incompatible structures
throw new Error(`No upgrader found for project structure version ${nextVersion}`);
diff --git a/src/core/project-upgrader/upgradeToV16.test.ts b/src/core/project-upgrader/upgradeToV16.test.ts
new file mode 100644
index 0000000..21bf400
--- /dev/null
+++ b/src/core/project-upgrader/upgradeToV16.test.ts
@@ -0,0 +1,26 @@
+import { describe, expect, it } from 'vitest';
+import { KGProject } from '../KGProject';
+import { upgradeProjectToLatest } from './KGProjectUpgrader';
+import { upgradeToV16 } from './upgradeToV16';
+
+describe('upgradeToV16', () => {
+ it('defaults persisted UI/audio toggles to false for legacy projects', () => {
+ const project = new KGProject('Legacy', 32, 0, 125, undefined, undefined, undefined, undefined, [0, 0], 1, [], 15, 1, []);
+
+ upgradeToV16(project);
+
+ expect(project.getProjectStructureVersion()).toBe(16);
+ expect(project.getShowGlobalTracks()).toBe(false);
+ expect(project.getIsMetronomeEnabled()).toBe(false);
+ });
+
+ it('preserves existing persisted toggle values through the main upgrader path', () => {
+ const project = new KGProject('Legacy', 32, 0, 125, undefined, undefined, undefined, undefined, [0, 0], 1, [], 15, 1, [], true, true);
+
+ const upgraded = upgradeProjectToLatest(project);
+
+ expect(upgraded.getProjectStructureVersion()).toBe(KGProject.CURRENT_PROJECT_STRUCTURE_VERSION);
+ expect(upgraded.getShowGlobalTracks()).toBe(true);
+ expect(upgraded.getIsMetronomeEnabled()).toBe(true);
+ });
+});
diff --git a/src/core/project-upgrader/upgradeToV16.ts b/src/core/project-upgrader/upgradeToV16.ts
new file mode 100644
index 0000000..c227b8e
--- /dev/null
+++ b/src/core/project-upgrader/upgradeToV16.ts
@@ -0,0 +1,17 @@
+import { KGProject } from '../KGProject';
+
+export function upgradeToV16(project: KGProject): KGProject {
+ try {
+ if (typeof project.getShowGlobalTracks() !== 'boolean') {
+ project.setShowGlobalTracks(false);
+ }
+
+ if (typeof project.getIsMetronomeEnabled() !== 'boolean') {
+ project.setIsMetronomeEnabled(false);
+ }
+ } finally {
+ project.setProjectStructureVersion(16);
+ }
+
+ return project;
+}
diff --git a/src/stores/projectStore.test.ts b/src/stores/projectStore.test.ts
index c4dd175..711c71d 100644
--- a/src/stores/projectStore.test.ts
+++ b/src/stores/projectStore.test.ts
@@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
import { act } from '@testing-library/react';
import { KGTrack } from '../core/track/KGTrack';
import { KGMidiTrack } from '../core/track/KGMidiTrack';
+import { createDefaultGlobalTracks } from '../core/global-track';
const pianoRollStateMocks = vi.hoisted(() => ({
setSheetMusicViewEnabled: vi.fn(),
@@ -9,16 +10,27 @@ const pianoRollStateMocks = vi.hoisted(() => ({
}));
let mockTracks: KGTrack[] = [new KGMidiTrack('Track 1', 0, 'acoustic_grand_piano')];
+let mockIsMetronomeEnabled = false;
+let mockShowGlobalTracks = false;
const mockProject = {
getTimeSignature: () => ({ numerator: 4, denominator: 4 }),
getMaxBars: () => 32,
getBarWidthMultiplier: () => 1,
getTracks: () => mockTracks,
+ getGlobalTracks: () => createDefaultGlobalTracks(),
getBpm: () => 120,
getKeySignature: () => 'C major',
getName: () => 'Test Project',
getSelectedMode: () => 'major',
getIsLooping: () => false,
+ getIsMetronomeEnabled: () => mockIsMetronomeEnabled,
+ setIsMetronomeEnabled: vi.fn((value: boolean) => {
+ mockIsMetronomeEnabled = value;
+ }),
+ getShowGlobalTracks: () => mockShowGlobalTracks,
+ setShowGlobalTracks: vi.fn((value: boolean) => {
+ mockShowGlobalTracks = value;
+ }),
getLoopingRange: () => [0, 0] as [number, number],
getPianoRollZoom: () => 1,
};
@@ -37,6 +49,7 @@ const mockAudioInterface = {
setTrackVolume: vi.fn(),
setTrackMute: vi.fn(),
setTrackSolo: vi.fn(),
+ setMetronomeEnabled: vi.fn(),
};
const configValues = new Map([
@@ -126,6 +139,11 @@ describe('projectStore piano roll state', () => {
mockAudioInterface.cancelAudioRecording.mockResolvedValue(undefined);
mockAudioInterface.getTransportPosition.mockReset();
mockAudioInterface.getTransportPosition.mockReturnValue(8);
+ mockAudioInterface.setMetronomeEnabled.mockReset();
+ mockIsMetronomeEnabled = false;
+ mockShowGlobalTracks = false;
+ mockProject.setIsMetronomeEnabled.mockClear();
+ mockProject.setShowGlobalTracks.mockClear();
configValues.set('audio.input_device_id', 'default');
});
@@ -195,6 +213,42 @@ describe('projectStore piano roll state', () => {
expect(state.requestedSheetMusicViewEnabled).toBe(false);
});
+ it('initializes persisted metronome and global-track visibility state from the project', async () => {
+ mockIsMetronomeEnabled = true;
+ mockShowGlobalTracks = true;
+
+ const { useProjectStore } = await import('./projectStore');
+
+ const state = useProjectStore.getState();
+ expect(state.isMetronomeEnabled).toBe(true);
+ expect(state.showGlobalTracks).toBe(true);
+ });
+
+ it('persists metronome toggles to the project and audio interface', async () => {
+ const { useProjectStore } = await import('./projectStore');
+
+ act(() => {
+ useProjectStore.getState().toggleMetronome();
+ });
+
+ const state = useProjectStore.getState();
+ expect(mockProject.setIsMetronomeEnabled).toHaveBeenCalledWith(true);
+ expect(mockAudioInterface.setMetronomeEnabled).toHaveBeenCalledWith(true);
+ expect(state.isMetronomeEnabled).toBe(true);
+ });
+
+ it('persists global-track visibility changes to the project', async () => {
+ const { useProjectStore } = await import('./projectStore');
+
+ act(() => {
+ useProjectStore.getState().setShowGlobalTracks(true);
+ });
+
+ const state = useProjectStore.getState();
+ expect(mockProject.setShowGlobalTracks).toHaveBeenCalledWith(true);
+ expect(state.showGlobalTracks).toBe(true);
+ });
+
it('tracks playback preparation around startPlaying success', async () => {
let resolveStart: (() => void) | null = null;
mockCore.startPlaying.mockImplementationOnce(() => new Promise((resolve) => {
diff --git a/src/stores/projectStore.ts b/src/stores/projectStore.ts
index a0d8179..7b7662e 100644
--- a/src/stores/projectStore.ts
+++ b/src/stores/projectStore.ts
@@ -92,6 +92,7 @@ interface ProjectState {
selectedMode: string;
isLooping: boolean;
isMetronomeEnabled: boolean;
+ showGlobalTracks: boolean;
loopingRange: [number, number]; // [startBar, endBar] - bar indices (0-based)
playheadPosition: number; // in beats
isPlaying: boolean;
@@ -194,6 +195,7 @@ interface ProjectState {
stopTransport: () => Promise;
toggleLoop: () => void;
toggleMetronome: () => void;
+ setShowGlobalTracks: (show: boolean) => void;
setBpm: (bpm: number) => void;
setMaxBars: (maxBars: number) => void;
setBarWidthMultiplier: (multiplier: number) => void;
@@ -432,7 +434,8 @@ export const useProjectStore = create((set, get) => {
keySignature: currentProject.getKeySignature(),
selectedMode: currentProject.getSelectedMode(),
isLooping: currentProject.getIsLooping(),
- isMetronomeEnabled: false,
+ isMetronomeEnabled: currentProject.getIsMetronomeEnabled(),
+ showGlobalTracks: currentProject.getShowGlobalTracks(),
loopingRange: currentProject.getLoopingRange(),
playheadPosition: KGCore.instance().getPlayheadPosition(),
isPlaying: KGCore.instance().getIsPlaying(),
@@ -870,6 +873,7 @@ export const useProjectStore = create((set, get) => {
// Setup audio synths for all tracks
const audioInterface = KGAudioInterface.instance();
+ audioInterface.setMetronomeEnabled(projectToLoad.getIsMetronomeEnabled());
// Clear any existing synths/buses first
tracks.forEach(track => {
@@ -949,6 +953,8 @@ export const useProjectStore = create((set, get) => {
keySignature,
selectedMode: projectToLoad.getSelectedMode(),
isLooping: projectToLoad.getIsLooping(),
+ isMetronomeEnabled: projectToLoad.getIsMetronomeEnabled(),
+ showGlobalTracks: projectToLoad.getShowGlobalTracks(),
loopingRange: projectToLoad.getLoopingRange(),
activeTrackAutomationTrackId: null,
activeTrackAutomationType: null,
@@ -1455,6 +1461,8 @@ export const useProjectStore = create((set, get) => {
toggleMetronome: () => {
const { isMetronomeEnabled, isPlaying, timeSignature } = get();
const newValue = !isMetronomeEnabled;
+ const project = KGCore.instance().getCurrentProject();
+ project.setIsMetronomeEnabled(newValue);
set({ isMetronomeEnabled: newValue });
const audio = KGAudioInterface.instance();
@@ -1470,6 +1478,12 @@ export const useProjectStore = create((set, get) => {
}
},
+ setShowGlobalTracks: (show: boolean) => {
+ const project = KGCore.instance().getCurrentProject();
+ project.setShowGlobalTracks(show);
+ set({ showGlobalTracks: show });
+ },
+
setBpm: (bpm: number) => {
try {
// Create and execute the change project property command
@@ -1886,6 +1900,8 @@ export const useProjectStore = create((set, get) => {
bpm: project.getBpm(),
keySignature: project.getKeySignature(),
selectedMode: project.getSelectedMode(),
+ isMetronomeEnabled: project.getIsMetronomeEnabled(),
+ showGlobalTracks: project.getShowGlobalTracks(),
currentTime: formatCurrentTime(project, core.getPlayheadPosition()),
});