Skip to content

Commit 08eab96

Browse files
authored
Merge pull request microsoft#185268 from microsoft/merogge/loop-cue
add audio cues for AI panel chat
2 parents bda8ac3 + eae6a36 commit 08eab96

File tree

13 files changed

+150
-14
lines changed

13 files changed

+150
-14
lines changed

src/vs/editor/standalone/browser/standaloneServices.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
8787
import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage';
8888
import { DefaultConfiguration } from 'vs/platform/configuration/common/configurations';
8989
import { WorkspaceEdit } from 'vs/editor/common/languages';
90-
import { AudioCue, IAudioCueService, Sound } from 'vs/platform/audioCues/browser/audioCueService';
90+
import { AudioCue, AudioCueGroupId, IAudioCueService, Sound } from 'vs/platform/audioCues/browser/audioCueService';
9191
import { LogService } from 'vs/platform/log/common/logService';
9292
import { getEditorFeatures } from 'vs/editor/common/editorFeatures';
9393
import { onUnexpectedError } from 'vs/base/common/errors';
@@ -1055,6 +1055,11 @@ class StandaloneAudioService implements IAudioCueService {
10551055

10561056
async playSound(cue: Sound, allowManyInParallel?: boolean | undefined): Promise<void> {
10571057
}
1058+
playAudioCueLoop(cue: AudioCue): IDisposable {
1059+
return toDisposable(() => { });
1060+
}
1061+
playRandomAudioCue(groupId: AudioCueGroupId, allowManyInParallel?: boolean): void {
1062+
}
10581063
}
10591064

10601065
export interface IEditorOverrideServices {

src/vs/platform/audioCues/browser/audioCueService.ts

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { Disposable } from 'vs/base/common/lifecycle';
6+
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
77
import { FileAccess } from 'vs/base/common/network';
88
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
99
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -22,6 +22,8 @@ export interface IAudioCueService {
2222
onEnabledChanged(cue: AudioCue): Event<void>;
2323

2424
playSound(cue: Sound, allowManyInParallel?: boolean): Promise<void>;
25+
playAudioCueLoop(cue: AudioCue, milliseconds: number): IDisposable;
26+
playRandomAudioCue(groupId: AudioCueGroupId, allowManyInParallel?: boolean): void;
2527
}
2628

2729
export class AudioCueService extends Disposable implements IAudioCueService {
@@ -51,6 +53,12 @@ export class AudioCueService extends Disposable implements IAudioCueService {
5153
await Promise.all(Array.from(sounds).map(sound => this.playSound(sound, true)));
5254
}
5355

56+
public playRandomAudioCue(groupId: AudioCueGroupId, allowManyInParallel?: boolean): void {
57+
const cues = AudioCue.allAudioCues.filter(cue => cue.groupId === groupId);
58+
const index = Math.floor(Math.random() * cues.length);
59+
this.playAudioCue(cues[index], allowManyInParallel);
60+
}
61+
5462
private getVolumeInPercent(): number {
5563
const volume = this.configurationService.getValue<number>('audioCues.volume');
5664
if (typeof volume !== 'number') {
@@ -66,7 +74,6 @@ export class AudioCueService extends Disposable implements IAudioCueService {
6674
if (!allowManyInParallel && this.playingSounds.has(sound)) {
6775
return;
6876
}
69-
7077
this.playingSounds.add(sound);
7178
const url = FileAccess.asBrowserUri(`vs/platform/audioCues/browser/media/${sound.fileName}`).toString(true);
7279

@@ -87,6 +94,23 @@ export class AudioCueService extends Disposable implements IAudioCueService {
8794
}
8895
}
8996

97+
public playAudioCueLoop(cue: AudioCue, milliseconds: number): IDisposable {
98+
let playing = true;
99+
const playSound = () => {
100+
if (playing) {
101+
this.playAudioCue(cue, true).finally(() => {
102+
setTimeout(() => {
103+
if (playing) {
104+
playSound();
105+
}
106+
}, milliseconds);
107+
});
108+
}
109+
};
110+
playSound();
111+
return toDisposable(() => playing = false);
112+
}
113+
90114
private readonly obsoleteAudioCuesEnabled = observableFromEvent(
91115
Event.filter(this.configurationService.onDidChangeConfiguration, (e) =>
92116
e.affectsConfiguration('audioCues.enabled')
@@ -190,19 +214,30 @@ export class Sound {
190214
public static readonly diffLineInserted = Sound.register({ fileName: 'diffLineInserted.mp3' });
191215
public static readonly diffLineDeleted = Sound.register({ fileName: 'diffLineDeleted.mp3' });
192216
public static readonly diffLineModified = Sound.register({ fileName: 'diffLineModified.mp3' });
217+
public static readonly chatRequestSent = Sound.register({ fileName: 'chatRequestSent.mp3' });
218+
public static readonly chatResponsePending = Sound.register({ fileName: 'chatResponsePending.mp3' });
219+
public static readonly chatResponseReceived1 = Sound.register({ fileName: 'chatResponseReceived1.mp3' });
220+
public static readonly chatResponseReceived2 = Sound.register({ fileName: 'chatResponseReceived2.mp3' });
221+
public static readonly chatResponseReceived3 = Sound.register({ fileName: 'chatResponseReceived3.mp3' });
222+
public static readonly chatResponseReceived4 = Sound.register({ fileName: 'chatResponseReceived4.mp3' });
223+
public static readonly chatResponseReceived5 = Sound.register({ fileName: 'chatResponseReceived5.mp3' });
193224

194225
private constructor(public readonly fileName: string) { }
195226
}
196227

228+
export const enum AudioCueGroupId {
229+
chatResponseReceived = 'chatResponseReceived'
230+
}
231+
197232
export class AudioCue {
198233
private static _audioCues = new Set<AudioCue>();
199-
200234
private static register(options: {
201235
name: string;
202236
sound: Sound;
203237
settingsKey: string;
238+
groupId?: AudioCueGroupId;
204239
}): AudioCue {
205-
const audioCue = new AudioCue(options.sound, options.name, options.settingsKey);
240+
const audioCue = new AudioCue(options.sound, options.name, options.settingsKey, options.groupId);
206241
AudioCue._audioCues.add(audioCue);
207242
return audioCue;
208243
}
@@ -309,9 +344,53 @@ export class AudioCue {
309344
settingsKey: 'audioCues.diffLineModified'
310345
});
311346

347+
public static readonly chatRequestSent = AudioCue.register({
348+
name: localize('audioCues.chatRequestSent', 'Chat Request Sent'),
349+
sound: Sound.chatRequestSent,
350+
settingsKey: 'audioCues.chatRequestSent'
351+
});
352+
353+
public static readonly chatResponseReceived = {
354+
name: localize('audioCues.chatResponseReceived', 'Chat Response Received'),
355+
settingsKey: 'audioCues.chatResponseReceived',
356+
groupId: AudioCueGroupId.chatResponseReceived
357+
};
358+
359+
public static readonly chatResponseReceived1 = AudioCue.register({
360+
sound: Sound.chatResponseReceived1,
361+
...this.chatResponseReceived
362+
});
363+
364+
public static readonly chatResponseReceived2 = AudioCue.register({
365+
sound: Sound.chatResponseReceived2,
366+
...this.chatResponseReceived
367+
});
368+
369+
public static readonly chatResponseReceived3 = AudioCue.register({
370+
sound: Sound.chatResponseReceived3,
371+
...this.chatResponseReceived
372+
});
373+
374+
public static readonly chatResponseReceived4 = AudioCue.register({
375+
sound: Sound.chatResponseReceived4,
376+
...this.chatResponseReceived
377+
});
378+
379+
public static readonly chatResponseReceived5 = AudioCue.register({
380+
sound: Sound.chatResponseReceived5,
381+
...this.chatResponseReceived
382+
});
383+
384+
public static readonly chatResponsePending = AudioCue.register({
385+
name: localize('audioCues.chatResponsePending', 'Chat Response Pending'),
386+
sound: Sound.chatResponsePending,
387+
settingsKey: 'audioCues.chatResponsePending'
388+
});
389+
312390
private constructor(
313391
public readonly sound: Sound,
314392
public readonly name: string,
315393
public readonly settingsKey: string,
394+
public readonly groupId?: string
316395
) { }
317396
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

src/vs/workbench/contrib/audioCues/browser/audioCues.contribution.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,21 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).regis
117117
'description': localize('audioCues.notebookCellFailed', "Plays a sound when a notebook cell execution fails."),
118118
...audioCueFeatureBase,
119119
},
120+
'audioCues.chatRequestSent': {
121+
'description': localize('audioCues.chatRequestSent', "Plays a sound when a chat request is made."),
122+
...audioCueFeatureBase,
123+
default: 'off'
124+
},
125+
'audioCues.chatResponsePending': {
126+
'description': localize('audioCues.chatResponsePending', "Plays a sound on loop while the response is pending."),
127+
...audioCueFeatureBase,
128+
default: 'off'
129+
},
130+
'audioCues.chatResponseReceived': {
131+
'description': localize('audioCues.chatResponseReceived', "Plays a sound on loop while the response has been received."),
132+
...audioCueFeatureBase,
133+
default: 'off'
134+
}
120135
}
121136
});
122137

0 commit comments

Comments
 (0)