Skip to content

Commit 92294f1

Browse files
committed
Clean up player interface
1 parent 7a5cc4d commit 92294f1

File tree

2 files changed

+73
-27
lines changed

2 files changed

+73
-27
lines changed

src/player/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Client JS
2+
3+
Client to connect to join a Resonate server.
4+
5+
## Methods
6+
7+
### `constructor(options)`
8+
9+
Instantiate a new Resonate client. The following options can be passed in:
10+
11+
- `playerId`, required, a unique identifier for the player.
12+
- `url`, required, the URL of the Resonate server to connect to.
13+
- `logger`, optional, a logger instance to use for logging messages. If not provided, the console will be used for logging.
14+
15+
### `connect(isReconnect: bool)`
16+
17+
Connect to the Resonate server.
18+
19+
- `isReconnect` is a boolean indicating whether this is a reconnection attempt. Trackin this allows the player to populate the `expected` field on the `close` event.
20+
21+
### `disconnect()`
22+
23+
Disconnect from the Resonate server.
24+
25+
## Events
26+
27+
type Events = {
28+
open: void;
29+
close: { expected: boolean };
30+
"server-update": ServerInfo | null;
31+
"session-update": SessionInfo | null;
32+
"metadata-update": Metadata | null;
33+
};
34+
35+
### `open`
36+
37+
Fired when the connection has been established with the Resonate server.
38+
39+
### `close`
40+
41+
Fired when the connection to the Resonate server has been closed.
42+
43+
Event data contains an `expected` boolean indicating whether the disconnection was expected (`disconnect()` called) or unexpected (e.g., due to a network issue).
44+
45+
### `server-update`
46+
47+
Fired when the server information has been updated. Event data is the server info.
48+
49+
### `session-update`
50+
51+
Fired when the session information has been updated. Event data is the session info.
52+
53+
### `metadata-update`
54+
55+
Fired when the metadata has been updated. Event data is the metadata.

src/player/player.ts

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class Player extends EventEmitter<Events> {
3737
private ws: WebSocket | null = null;
3838
private serverInfo: ServerInfo | null = null;
3939
private sessionInfo: SessionInfo | null = null;
40-
private audioContext: AudioContext | null = null;
40+
private audioContext = new AudioContextClass();
4141
private metadata: Metadata | null = null;
4242
private serverTimeDiff: number = 0; // Time difference between server and client
4343
private serverTimeDiffSamples: number[] = []; // Store last 50 samples for median
@@ -52,17 +52,10 @@ export class Player extends EventEmitter<Events> {
5252
}
5353

5454
// Establish a WebSocket connection
55-
connect(isReconnect: boolean = false) {
55+
public connect(isReconnect: boolean = false) {
5656
this.expectClose = !isReconnect;
5757
this.ws = new WebSocket(this.options.url);
5858

59-
// Reset audio output/syncing on new connection
60-
if (!isReconnect) {
61-
this.audioContext = new AudioContextClass();
62-
this.serverTimeDiff = 0;
63-
this.serverTimeDiffSamples = [];
64-
}
65-
6659
// Expect binary data as ArrayBuffer
6760
this.ws.binaryType = "arraybuffer";
6861

@@ -72,10 +65,10 @@ export class Player extends EventEmitter<Events> {
7265
this.logger.log("WebSocket connected");
7366
this.serverTimeDiffSamples = [];
7467
this.expectClose = false;
75-
this.sendHello();
76-
this.sendPlayerTime();
68+
this._sendHello();
69+
this._sendPlayerTime();
7770
timeSyncInterval = window.setInterval(() => {
78-
this.sendPlayerTime();
71+
this._sendPlayerTime();
7972
}, 5000);
8073
this.fire("open");
8174
};
@@ -85,15 +78,15 @@ export class Player extends EventEmitter<Events> {
8578
if (typeof event.data === "string") {
8679
try {
8780
const message = JSON.parse(event.data);
88-
this.handleTextMessage(
81+
this._handleTextMessage(
8982
message,
9083
this.audioContext!.currentTime * 1000000,
9184
);
9285
} catch (err) {
9386
this.logger.error("Error parsing message", err);
9487
}
9588
} else {
96-
this.handleBinaryMessage(event.data);
89+
this._handleBinaryMessage(event.data);
9790
}
9891
};
9992

@@ -105,16 +98,14 @@ export class Player extends EventEmitter<Events> {
10598
this.logger.log("WebSocket connection closed");
10699
clearTimeout(timeSyncInterval!);
107100
this.sessionInfo = null;
108-
this.audioContext!.close();
109-
this.audioContext = null;
110101
this.fire("close", {
111102
expected: this.expectClose,
112103
});
113104
};
114105
}
115106

116107
// Send a hello message to the server with player details.
117-
sendHello() {
108+
private _sendHello() {
118109
const helloMsg: PlayerHelloMessage = {
119110
type: "player/hello",
120111
payload: {
@@ -134,7 +125,7 @@ export class Player extends EventEmitter<Events> {
134125
this.ws!.send(JSON.stringify(helloMsg));
135126
}
136127

137-
sendPlayerTime() {
128+
private _sendPlayerTime() {
138129
const timeMsg: PlayerTimeMessage = {
139130
type: "player/time",
140131
payload: {
@@ -146,7 +137,7 @@ export class Player extends EventEmitter<Events> {
146137
}
147138

148139
// Handle text (JSON) messages from the server.
149-
handleTextMessage(message: ServerMessages, receivedAt: number) {
140+
private _handleTextMessage(message: ServerMessages, receivedAt: number) {
150141
this.logger.log("Received text message:", message);
151142
switch (message.type) {
152143
case "source/hello":
@@ -165,7 +156,7 @@ export class Player extends EventEmitter<Events> {
165156
this.sessionInfo = null;
166157
this.logger.log("Session ended");
167158
this.fire("session-update", null);
168-
this.sendPlayerTime();
159+
this._sendPlayerTime();
169160
break;
170161

171162
case "metadata/update":
@@ -177,7 +168,7 @@ export class Player extends EventEmitter<Events> {
177168

178169
case "source/time":
179170
// Pass player_received time to the handler
180-
this.handleServerTime(message.payload, receivedAt);
171+
this._handleServerTime(message.payload, receivedAt);
181172
break;
182173

183174
default:
@@ -187,7 +178,7 @@ export class Player extends EventEmitter<Events> {
187178
}
188179

189180
// Handle binary messages – here we assume binary messages are audio chunks.
190-
handleBinaryMessage(data: ArrayBuffer) {
181+
private _handleBinaryMessage(data: ArrayBuffer) {
191182
// Create a DataView for accessing binary data
192183
const dataView = new DataView(data);
193184

@@ -198,15 +189,15 @@ export class Player extends EventEmitter<Events> {
198189

199190
switch (messageType) {
200191
case BinaryMessageType.PlayAudioChunk:
201-
this.handleAudioChunk(data);
192+
this._handleAudioChunk(data);
202193
break;
203194
default:
204195
this.logger.error("Unknown binary message type:", messageType);
205196
}
206197
}
207198

208199
// Handle an audio chunk binary message.
209-
handleAudioChunk(data: ArrayBuffer) {
200+
private _handleAudioChunk(data: ArrayBuffer) {
210201
// Check if AudioContext is available
211202
if (!this.audioContext) {
212203
this.logger.error("Cannot play audio: AudioContext not initialized");
@@ -328,7 +319,7 @@ export class Player extends EventEmitter<Events> {
328319
}
329320
}
330321

331-
handleServerTime(payload: ServerTimeInfo, receivedAt: number) {
322+
private _handleServerTime(payload: ServerTimeInfo, receivedAt: number) {
332323
const { player_transmitted, source_received, source_transmitted } = payload;
333324

334325
// Calculate the raw offset from this message (in seconds)
@@ -345,7 +336,7 @@ export class Player extends EventEmitter<Events> {
345336
this.serverTimeDiffSamples.shift();
346337
} else if (this.serverTimeDiffSamples.length < 20) {
347338
// let's kick off another sample
348-
this.sendPlayerTime();
339+
this._sendPlayerTime();
349340
}
350341

351342
// Calculate the median of the samples for a stable offset
@@ -365,7 +356,7 @@ export class Player extends EventEmitter<Events> {
365356
}
366357

367358
// Close the WebSocket connection and clean up resources.
368-
disconnect() {
359+
public disconnect() {
369360
if (!this.ws) {
370361
return;
371362
}

0 commit comments

Comments
 (0)