Gvc, the backend used for audio uses pulseaudio, so make sure pipewire-pulse
is installed if using pipewire
speaker-changed default speaker's state changedmicrophone-changed default microphone' state changedstream-added: (id: number) new stream appearedstream-removed: (id: number) stream disappearedcontrol:
Gvc.MixerControlspeaker: Stream writablemicrophone: Stream writableapps: Stream[] list of streams filtered by sink inputsrecorders: Stream[] list of streams filtered by source outputsspeakers: Stream[] list of streams filtered by sinksmicrophones: Stream[] list of streams filtered by sourcesgetStream: (id: number) => Streamstream
Gvc.MixerStream the
wrapped stream objectname: stringapplication_id: string|nulldescription: string|nullicon-name: stringid: numberis-muted: boolean this returns if volume is 0, setting this to true sets
volume to 0. If you want the actual is-muted, you can use
Stream.stream.isMutedvolume: number: writable, between 0 and 1const audio = await Service.import("audio");
/** @param {'speaker' | 'microphone'} type */
const VolumeSlider = (type = "speaker") =>
Widget.Slider({
hexpand: true,
drawValue: false,
onChange: ({ value }) => audio[type].volume = value,
value: audio[type].bind("volume"),
});
const speakerSlider = VolumeSlider("speaker");
const micSlider = VolumeSlider("microphone");
const audio = await Service.import("audio");
const volumeIndicator = Widget.Button({
on_clicked: () => audio.speaker.is_muted = !audio.speaker.is_muted,
child: Widget.Icon().hook(audio.speaker, (self) => {
const vol = audio.speaker.volume * 100;
const icon = [
[101, "overamplified"],
[67, "high"],
[34, "medium"],
[1, "low"],
[0, "muted"],
].find(([threshold]) => threshold <= vol)?.[1];
self.icon = `audio-volume-${icon}-symbolic`;
self.tooltip_text = `Volume ${Math.floor(vol)}%`;
}),
});