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
}
35.5 KB
Binary file not shown.
35.5 KB
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)