Skip to content

Commit 43afb8b

Browse files
committed
The arrangement player now takes over what the singleton event engine did
Also removed the read only version of ITimeParams (use the Readonly type instead).
1 parent 8f67811 commit 43afb8b

File tree

15 files changed

+445
-458
lines changed

15 files changed

+445
-458
lines changed

src/App.tsx

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import { ProgressIndicator } from "./components/ui/framework/ProgressIndicator.j
1717
import { ChildAlignment, Orientation } from "./components/ui/framework/ui-types.js";
1818
import { UIComponent } from "./components/ui/framework/UIComponent.js";
1919

20+
import { ArrangementPlayControls } from "./components/ui/Arrangement/ArrangementPlayControls.js";
21+
import { ArrangementTitle } from "./components/ui/Arrangement/ArrangementTitle.js";
2022
import { ArrangementViewer } from "./components/ui/Arrangement/ArrangementViewer.js";
2123
import { ValueEditorEntryType, type IValueEditorValueEntry } from "./components/ui/composites/ValueDialog.js";
2224
import { Codicon } from "./components/ui/framework/Codicon.js";
@@ -36,17 +38,13 @@ import type { ISerialisedArrangement } from "./core/types/snapshots.js";
3638
import { UndoManager } from "./core/UndoManager.js";
3739
import { convertErrorToString } from "./core/utils.js";
3840
import { ArrangementPlayer } from "./player/ArrangementPlayer.js";
39-
import { EventEngine } from "./player/EventEngine.js";
40-
import { AnimationEngine } from "./ui/AnimationEngine.js";
41+
import type { ScoreBookUiServices } from "./player/types.js";
4142
import { DialogHost } from "./ui/DialogHost.js";
4243
import { emptySongString } from "./ui/index.js";
4344
import { ModeManager } from "./ui/ModeManager.js";
4445
import { MouseHandler } from "./ui/MouseHandler.js";
4546
import { ScoreLibrary } from "./ui/ScoreLibrary.js";
4647
import { SelectionManager } from "./ui/SelectionManager.js";
47-
import { ArrangementTitle } from "./components/ui/Arrangement/ArrangementTitle.js";
48-
import { ArrangementPlayControls } from "./components/ui/Arrangement/ArrangementPlayControls.js";
49-
import type { ScoreBookUiServices } from "./player/types.js";
5048

5149
interface IAppState {
5250
ready: boolean;
@@ -84,7 +82,6 @@ export class App extends UIComponent<{}, IAppState> {
8482

8583
const selectionManager = new SelectionManager();
8684
this.services = {
87-
animationEngine: new AnimationEngine(EventEngine.instance),
8885
selectionManager,
8986
modeManager: new ModeManager(selectionManager),
9087
};
@@ -539,10 +536,12 @@ export class App extends UIComponent<{}, IAppState> {
539536
}
540537

541538
case " ": {
542-
if (EventEngine.instance.state === "stopped") {
543-
void EventEngine.instance.play();
544-
} else {
545-
EventEngine.instance.stop();
539+
if (this.arrangementPlayer) {
540+
if (this.arrangementPlayer.currentTiming === null) {
541+
this.arrangementPlayer.play();
542+
} else {
543+
this.arrangementPlayer.stop();
544+
}
546545
}
547546
event.preventDefault(); // This is to prevent spaces getting written in number inputs
548547

src/components/ui/Arrangement/ArrangementPlayControls.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import type { ComponentChild } from "preact";
77

88
import type { UndoManager } from "../../../core/UndoManager.js";
99
import type { ArrangementPlayer } from "../../../player/ArrangementPlayer.js";
10-
import { EventEngine } from "../../../player/EventEngine.js";
1110
import type { ScoreBookUiServices } from "../../../player/types.js";
1211
import { Button } from "../framework/Button.js";
1312
import { Codicon } from "../framework/Codicon.js";
@@ -37,7 +36,7 @@ export class ArrangementPlayControls extends UIComponent<IArrangementControlsTop
3736
super(props);
3837

3938
this.state = {
40-
playing: EventEngine.instance.state === "playing",
39+
playing: props.arrangementPlayer.currentTiming !== null,
4140
editingTitle: false,
4241
title: props.arrangementPlayer.arrangementView.title,
4342
};
@@ -46,7 +45,7 @@ export class ArrangementPlayControls extends UIComponent<IArrangementControlsTop
4645
public override componentDidMount(): void {
4746
const { arrangementPlayer } = this.props;
4847

49-
this.addSubscription(EventEngine.instance, this.onPlaybackStateChange);
48+
this.addSubscription(arrangementPlayer, this.onPlaybackStateChange);
5049
const arrangement = arrangementPlayer.arrangementView;
5150
this.addSubscription(arrangement, this.titleChangeSubscription);
5251
}
@@ -64,7 +63,7 @@ export class ArrangementPlayControls extends UIComponent<IArrangementControlsTop
6463
imageOnly
6564
id="playbackButton"
6665
onClick={() => {
67-
EventEngine.instance.stop();
66+
arrangementPlayer.stop();
6867
}}>
6968
<Icon src={Codicon.DebugPause} />
7069
</Button>
@@ -75,7 +74,7 @@ export class ArrangementPlayControls extends UIComponent<IArrangementControlsTop
7574
imageOnly
7675
id="playbackButton"
7776
onClick={() => {
78-
void EventEngine.instance.play();
77+
arrangementPlayer.play();
7978
}}>
8079
<Icon src={Codicon.DebugStart} />
8180
</Button>
@@ -139,7 +138,8 @@ export class ArrangementPlayControls extends UIComponent<IArrangementControlsTop
139138
};
140139

141140
private onPlaybackStateChange = () => {
142-
this.setState({ playing: EventEngine.instance.state === "playing" });
141+
const { arrangementPlayer } = this.props;
142+
this.setState({ playing: arrangementPlayer.currentTiming !== null });
143143
};
144144

145145
private startRecording = () => {

src/components/ui/Arrangement/ArrangementViewer.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { createRef, type JSX } from "preact";
77

88
import { Publisher } from "../../../core/Publisher.js";
99
import type { RealTime } from "../../../core/ScoreBookDataModel.js";
10-
import type { ITimeParamsView } from "../../../core/types/general.js";
10+
import type { ITimeParams } from "../../../core/types/general.js";
1111
import type { UndoManager } from "../../../core/UndoManager.js";
1212
import type { ArrangementPlayer } from "../../../player/ArrangementPlayer.js";
1313
import type { ScoreBookUiServices } from "../../../player/types.js";
@@ -74,7 +74,7 @@ export class ArrangementViewer extends UIComponent<IArrangementViewerProps, IArr
7474
}
7575

7676
public override componentDidMount(): void {
77-
const { arrangementPlayer, services } = this.props;
77+
const { arrangementPlayer } = this.props;
7878
const { autoFollowIsOn } = this.state;
7979

8080
setTimeout(this.handleResize, 0);
@@ -85,10 +85,10 @@ export class ArrangementViewer extends UIComponent<IArrangementViewerProps, IArr
8585

8686
// If desired, turn on auto-follow like so.
8787
if (autoFollowIsOn) {
88-
services.animationEngine.connect(this.autoFollow);
88+
arrangementPlayer.animationEngine.connect(this.autoFollow);
8989
} else {
9090
// Otherwise, set up the subscription which will turn it on again.
91-
this.addSubscription(services.animationEngine, this.animationEngineSubscription);
91+
this.addSubscription(arrangementPlayer.animationEngine, this.animationEngineSubscription);
9292
}
9393

9494
this.updateScrollShadows();
@@ -102,10 +102,10 @@ export class ArrangementViewer extends UIComponent<IArrangementViewerProps, IArr
102102
public override componentWillUnmount(): void {
103103
super.componentWillUnmount();
104104

105-
const { services } = this.props;
105+
const { arrangementPlayer } = this.props;
106106

107107
this.resizeObserver.disconnect();
108-
services.animationEngine.disconnect(this.autoFollow);
108+
arrangementPlayer.animationEngine.disconnect(this.autoFollow);
109109
}
110110

111111
public override render(): JSX.Element {
@@ -195,7 +195,7 @@ export class ArrangementViewer extends UIComponent<IArrangementViewerProps, IArr
195195
* @param timeParams The time params of the arrangement.
196196
* @returns The width of the entire note line in pt.
197197
*/
198-
private getNoteLineMinWidth = (timeParams: ITimeParamsView): number => {
198+
private getNoteLineMinWidth = (timeParams: Readonly<ITimeParams>): number => {
199199
const widthFromNotes = baseNoteWidth * timeParams.timings.length;
200200
const extraWidthBetweenBars = (timeParams.length - 1) * 4;
201201

@@ -304,8 +304,9 @@ export class ArrangementViewer extends UIComponent<IArrangementViewerProps, IArr
304304
};
305305

306306
private animationEngineSubscription = () => {
307-
const { services } = this.props;
308-
if (services.animationEngine.state === "playing") {
307+
const { arrangementPlayer } = this.props;
308+
309+
if (arrangementPlayer.animationEngine.state === "playing") {
309310
this.setState({ autoFollowIsOn: true });
310311
}
311312
};

src/components/ui/GuideRail/GuideRail.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { type ComponentChild } from "preact";
77

8-
import type { IArrangement, ITimeParamsView } from "../../../core/types/general.js";
8+
import type { IArrangement, ITimeParams } from "../../../core/types/general.js";
99
import { UIComponent, type ICommonUIProperties } from "../framework/UIComponent.js";
1010
import { TimingViewer } from "./TimingViewer.js";
1111
import { Container } from "../framework/Container.js";
@@ -55,7 +55,7 @@ export class GuideRail extends UIComponent<IGuideRailProps, IGuideRailState> {
5555
);
5656
}
5757

58-
private getBarDivisibility(timeParams: ITimeParamsView): BarDivisibility {
58+
private getBarDivisibility(timeParams: Readonly<ITimeParams>): BarDivisibility {
5959
const beatsPerBar = Number(timeParams.timeSignature.split("/")[0]);
6060
if (beatsPerBar % 4 === 0) {
6161
return 4;

src/components/ui/GuideRail/TimingViewer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import type { ComponentChild } from "preact";
77

88
import type { ITiming } from "../../../core/ScoreBookDataModel.js";
9-
import type { ITimeParams, ITimeParamsView } from "../../../core/types/general.js";
9+
import type { ITimeParams } from "../../../core/types/general.js";
1010
import { UIComponent, type ICommonUIProperties } from "../framework/UIComponent.js";
1111
import { NoteViewer } from "../Note/NoteViewer.js";
1212
import { type BarDivisibility } from "./GuideRail.js";
@@ -40,7 +40,7 @@ export class TimingViewer extends UIComponent<ITimingViewerProperties> {
4040
);
4141
}
4242

43-
private getTimingText(timeParams: ITimeParamsView, barDivisibility: BarDivisibility, timing: ITiming,
43+
private getTimingText(timeParams: Readonly<ITimeParams>, barDivisibility: BarDivisibility, timing: ITiming,
4444
isStartOfBar: boolean): string {
4545
const { timeSignature, stepResolution } = timeParams;
4646

src/components/ui/Track/TrackViewer.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ export class TrackViewer extends UIComponent<ITrackViewerProperties, ITrackViewe
108108
};
109109

110110
private audibleChanged = () => {
111+
// XXX: this is called way too often. Need a callback only when the audible state of this track changes.
111112
const { trackPlayer, arrangementPlayer } = this.props;
112113

113114
this.setState({

src/core/TimeParams.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export class TimeParams extends Publisher implements ISbDmTimeParams {
7676
if (!this.validateTimeSignature(newTimeSignature)) {
7777
throw new Error("Invalid time signature");
7878
}
79+
7980
if (newTimeSignature !== this.signature) {
8081
this.signature = newTimeSignature;
8182
this.regenerateTimings();
@@ -101,6 +102,7 @@ export class TimeParams extends Publisher implements ISbDmTimeParams {
101102
if (!this.validateTempo(newTempo)) {
102103
throw new Error("Invalid tempo");
103104
}
105+
104106
if (newTempo !== this.usedTempo) {
105107
this.usedTempo = newTempo;
106108
this.regenerateTimings();

src/core/serialisation/serialisers.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ const serialiseNotes = (trackSnapshot: ITrackSnapshot): string => {
4141
const notesStylesAsNumbers = trackSnapshot.notes.map((noteChar) => {
4242
return urlCharacterToNumber[noteChar];
4343
});
44+
45+
throw new Error("Move serialization to the data model.");
4446
const base = 2n; // XXX BigInt(getNoteStyleCount(trackSnapshot.instrumentId));
4547
const notesAsNumber = interpretAsBaseN(notesStylesAsNumbers, base);
4648

src/core/types/edit_commands.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import type { ISbDmInstrument, ISbDmNote, ISbDmTrack } from "../ScoreBookDataModel.js";
7-
import type { IArrangement, INoteStyle, IPolyrhythm, ITimeParamsView } from "./general.js";
7+
import type { IArrangement, INoteStyle, IPolyrhythm, ITimeParams } from "./general.js";
88

99
/* eslint-disable @typescript-eslint/naming-convention */
1010

@@ -67,21 +67,21 @@ export interface EditCommand_Note {
6767

6868
export interface EditCommand_TimeParamsTimeSignature {
6969
type: "EditCommand_TimeParamsTimeSignature";
70-
timeParams: ITimeParamsView;
70+
timeParams: Readonly<ITimeParams>;
7171
timeSignature: string;
7272
pulse: string;
7373
stepResolution: number;
7474
}
7575

7676
export interface EditCommand_TimeParamsTempo {
7777
type: "EditCommand_TimeParamsTempo";
78-
timeParams: ITimeParamsView;
78+
timeParams: Readonly<ITimeParams>;
7979
tempo: number;
8080
}
8181

8282
export interface EditCommand_TimeParamsLength {
8383
type: "EditCommand_TimeParamsLength";
84-
timeParams: ITimeParamsView;
84+
timeParams: Readonly<ITimeParams>;
8585
length: number;
8686
}
8787

src/core/types/general.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,23 +72,15 @@ export interface IArrangement extends ISubscribable {
7272
removeTrack(track: ISbDmTrack): void;
7373
}
7474

75-
export interface ITimeParamsView extends ISubscribable {
76-
readonly timeSignature: string;
77-
readonly tempo: number;
78-
readonly length: number;
79-
readonly pulse: string;
80-
readonly stepResolution: number;
81-
readonly timings: ITiming[];
82-
83-
isValid(timing: ITiming): boolean;
84-
}
85-
86-
export interface ITimeParams extends ITimeParamsView {
75+
export interface ITimeParams extends ISubscribable {
8776
timeSignature: string;
8877
tempo: number;
8978
length: number;
9079
pulse: string;
9180
stepResolution: number;
81+
timings: ITiming[];
82+
83+
isValid(timing: ITiming): boolean;
9284
}
9385

9486
export interface IPolyrhythm {

0 commit comments

Comments
 (0)