Skip to content

Commit f10b154

Browse files
committed
Add support for stream commands
1 parent df02683 commit f10b154

File tree

5 files changed

+54
-23
lines changed

5 files changed

+54
-23
lines changed

index.html

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,8 @@
5656
border-radius: 3px;
5757
font-family: monospace;
5858
}
59-
.clear-logs {
59+
.actions {
6060
margin-top: 10px;
61-
padding: 5px 10px;
62-
font-size: 12px;
63-
background-color: #f0f0f0;
64-
border: 1px solid #ccc;
65-
border-radius: 3px;
66-
cursor: pointer;
6761
}
6862
</style>
6963
</head>
@@ -95,9 +89,12 @@ <h1>Improv Audio Player</h1>
9589
<div id="connection-status">Disconnected</div>
9690
<div id="available-groups"></div>
9791
<div id="status-display" class="status"></div>
98-
<button id="join-group-btn">Join Group</button>
99-
<button id="leave-group-btn">Leave Group</button>
100-
<button class="clear-logs" id="clear-logs-btn">Clear Logs</button>
92+
<div class="actions">
93+
<button id="stop-btn">Stop</button>
94+
<button id="join-group-btn">Join Group</button>
95+
<button id="leave-group-btn">Leave Group</button>
96+
<button class="clear-logs" id="clear-logs-btn">Clear Logs</button>
97+
</div>
10198
</div>
10299
<div id="metadata" class="metadata"></div>
103100
<div class="artwork">
@@ -118,6 +115,7 @@ <h1>Improv Audio Player</h1>
118115
const playerIdInput = document.getElementById("player-id-input");
119116
const urlInput = document.getElementById("url-input");
120117
const connectButton = document.getElementById("connect-button");
118+
const stopButton = document.getElementById("stop-btn");
121119
const availableGroups = document.getElementById("available-groups");
122120
const joinGroupButton = document.getElementById("join-group-btn");
123121
const leaveGroupButton = document.getElementById("leave-group-btn");
@@ -329,6 +327,17 @@ <h1>Improv Audio Player</h1>
329327
.map((group) => `<code>${group.groupId}</code>`)
330328
.join(", ")}`;
331329
});
330+
stopButton.addEventListener("click", () => {
331+
if (!client) {
332+
logger.error("No client connected to stop playback");
333+
return;
334+
}
335+
if (!client.sessionInfo) {
336+
logger.error("No active session to stop");
337+
return;
338+
}
339+
client.sendStreamCommand("stop");
340+
});
332341
</script>
333342
</body>
334343
</html>

server.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,20 +105,27 @@ async function main() {
105105
const playAudio = async () => {
106106
logger.log("");
107107
logger.log("Sending WAV audio data to connected clients");
108+
let endRequested = false;
108109
const session = group.startSession(
109110
"pcm",
110111
wavData.sampleRate,
111112
wavData.channels,
112113
wavData.bitDepth,
113114
);
115+
session.on("stream-command", (command) => {
116+
if (command.command === "stop") {
117+
endRequested = true;
118+
logger.log("Stop command received, stopping audio playback.");
119+
}
120+
});
114121
session.sendMetadata({
115122
title: "Sample Audio",
116123
artist: "Someone on the internet",
117124
album: null,
118125
year: null,
119126
track: null,
120127
group_members: [],
121-
support_commands: ["play", "pause"],
128+
support_commands: ["play", "stop"],
122129
repeat: "off",
123130
shuffle: false,
124131
});
@@ -129,6 +136,9 @@ async function main() {
129136
(timeSlice / 1000) * wavData.sampleRate * wavData.channels;
130137

131138
for (let i = 0; i < wavData.audioData.length; i += bytesPerSlice) {
139+
if (endRequested) {
140+
break;
141+
}
132142
// Mimick metadata update
133143
if (i % (bytesPerSlice * 10) === 0) {
134144
session.sendMetadata({
@@ -138,7 +148,7 @@ async function main() {
138148
year: null,
139149
track: null,
140150
group_members: [],
141-
support_commands: ["play", "pause"],
151+
support_commands: ["play", "stop"],
142152
repeat: "off",
143153
shuffle: false,
144154
});

src/client/client.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
PlayerTimeMessage,
1010
GroupListMessage,
1111
ClientMessages,
12+
MediaCommand,
1213
} from "../messages.js";
1314
import type { Logger } from "../logging.js";
1415
import { EventEmitter } from "../util/event-emitter.js";
@@ -421,6 +422,21 @@ export class Client extends EventEmitter<Events> {
421422
);
422423
}
423424

425+
public sendStreamCommand(command: MediaCommand) {
426+
if (!this.sessionInfo || !this.metadata) {
427+
throw new Error("Cannot send command: no active session");
428+
}
429+
if (!this.metadata.support_commands.includes(command)) {
430+
throw new Error(`Command ${command} not supported by session`);
431+
}
432+
this.send({
433+
type: "stream/command",
434+
payload: {
435+
command,
436+
},
437+
});
438+
}
439+
424440
public joinGroup(groupId: string) {
425441
this.send({
426442
type: "group/join",

src/server/server-client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type { Logger } from "../logging.js";
1111
import { generateUniqueId } from "../util/unique-id.js";
1212
import { EventEmitter } from "../util/event-emitter.js";
1313

14-
interface ServerClientEvents {
14+
export interface ServerClientEvents {
1515
close: void;
1616
"player-state": PlayerState | null;
1717
"stream-command": StreamCommandMessage["payload"];

src/server/server-session.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import {
88
import type { Logger } from "../logging.js";
99
import { ServerGroup } from "./server-group.js";
1010
import { EventEmitter } from "../util/event-emitter.js";
11-
import { ServerClient } from "./server-client.js";
11+
import { ServerClient, ServerClientEvents } from "./server-client.js";
1212
import { arraysEqual } from "../util/array-equal.js";
1313

1414
const HEADER_SIZE = 13;
1515
const METADATA_ARRAY_FIELDS = ["group_members", "support_commands"];
1616

1717
interface ServerSessionEvents {
1818
"session-end": ServerSession;
19+
"stream-command": ServerClientEvents["stream-command"];
1920
}
2021

2122
export class ServerSession extends EventEmitter<ServerSessionEvents> {
@@ -134,18 +135,13 @@ export class ServerSession extends EventEmitter<ServerSessionEvents> {
134135
if (this._lastReportedArt) {
135136
client.sendBinary(this._lastReportedArt);
136137
}
137-
// TODO Commented out because we don't unlisten yet
138-
// Any mutation of sessionActive need to take care of it.
138+
client.on("stream-command", (command) => {
139+
this.fire("stream-command", command);
140+
});
141+
// TODO Commented out because we don't unlisten yet and we should pass client along
139142
// client.on("player-state", (state) => {
140143
// console.log(`Unhandled player state from ${client.clientId}:`, state);
141144
// });
142-
// TODO Commented out because we don't unlisten yet
143-
// client.on("stream-command", (command) => {
144-
// console.log(
145-
// `Unhandled stream command from ${client.clientId}:`,
146-
// command,
147-
// );
148-
// });
149145

150146
this.sessionActive.add(client.clientId);
151147
yield client;

0 commit comments

Comments
 (0)