add output device switching

This commit is contained in:
Vidocq 2021-02-24 03:07:34 +08:00
parent bcc1b1e72e
commit 8a30558ac1
7 changed files with 95 additions and 41 deletions

View File

@ -21,51 +21,50 @@ export function createTray(win) {
tray.on("right-click", () => {
const contextMenu = Menu.buildFromTemplate([
{
label: "播放/暂停",
icon: "src/assets/icons/play.png",
click: () => {
win.webContents.send("play");
},
label: "播放/暂停",
icon: "src/assets/icons/play.png",
click: () => {
win.webContents.send("play");
},
{
label: "上一首",
icon: "src/assets/icons/left.png",
accelerator: "CmdOrCtrl+Left",
click: () => {
win.webContents.send("previous");
},
},
{
label: "上一首",
icon: "src/assets/icons/left.png",
accelerator: "CmdOrCtrl+Left",
click: () => {
win.webContents.send("previous");
},
{
label: "下一首",
icon: "src/assets/icons/right.png",
accelerator: "CmdOrCtrl+Right",
click: () => {
win.webContents.send("next");
},
},
{
label: "下一首",
icon: "src/assets/icons/right.png",
accelerator: "CmdOrCtrl+Right",
click: () => {
win.webContents.send("next");
},
{
label: "循环播放",
icon: "src/assets/icons/repeat.png",
accelerator: "Alt+R",
click: () => {
win.webContents.send("repeat");
},
},
{
label: "循环播放",
icon: "src/assets/icons/repeat.png",
accelerator: "Alt+R",
click: () => {
win.webContents.send("repeat");
},
{
label: "加入喜欢",
icon: "src/assets/icons/like.png",
accelerator: "CmdOrCtrl+L",
click: () => {
win.webContents.send("like");
},
},
{
label: "加入喜欢",
icon: "src/assets/icons/like.png",
accelerator: "CmdOrCtrl+L",
click: () => {
win.webContents.send("like");
},
{
label: "退出",
icon: "src/assets/icons/exit.png",
accelerator: "CmdOrCtrl+W",
click: () => {
app.exit();
},
},
{
label: "退出",
icon: "src/assets/icons/exit.png",
accelerator: "CmdOrCtrl+W",
click: () => {
app.exit();
},
},
]);

View File

@ -117,6 +117,7 @@ export default {
high: "High",
lossless: "Lossless",
},
deviceSelector: "Output Device",
appearance: {
text: "Appearance",
auto: "Auto",

View File

@ -118,6 +118,7 @@ export default {
high: "极高",
lossless: "无损",
},
deviceSelector: "音频输出设备",
appearance: {
text: "外观",
auto: "自动",

View File

@ -7,6 +7,7 @@ let localStorage = {
lang: null,
appearance: "auto",
musicQuality: 320000,
outputDevice: "default",
showGithubIcon: true,
showPlaylistsByAppleMusic: true,
showUnavailableSongInGreyStyle: true,

View File

@ -9,6 +9,9 @@ export default {
changeMusicQuality(state, value) {
state.settings.musicQuality = value;
},
changeOutputDevice(state, deviceId) {
state.settings.outputDevice = deviceId;
},
updateSettings(state, { key, value }) {
state.settings[key] = value;
},

View File

@ -102,6 +102,8 @@ export default class {
_init() {
Howler.autoUnlock = false;
Howler.usingWebAudio = true;
Howler.masterGain = true;
this._loadSelfFromLocalStorage();
this._replaceCurrentTrack(this._currentTrack.id, false).then(() => {
this._howler.seek(localStorage.getItem("playerCurrentTrackTime") ?? 0);
@ -150,11 +152,27 @@ export default class {
time,
});
}
_setupAudioNode() {
Howler.masterGain.disconnect();
const mediaStreamNode = Howler.ctx.createMediaStreamDestination();
Howler.masterGain.connect(mediaStreamNode);
let audio = '';
if (document.querySelector('audio') !== null) {
audio = document.querySelector('audio');
} else {
audio = document.createElement('audio');
document.body.append(audio);
}
audio.autoplay = true;
audio.srcObject = mediaStreamNode.stream;
audio.setSinkId(store.state.settings.outputDevice);
}
_playAudioSource(source, autoplay = true) {
Howler.unload();
this._setupAudioNode();
this._howler = new Howl({
src: [source],
html5: true,
html5: false,
format: ["mp3", "flac"],
});
if (autoplay) {

View File

@ -74,6 +74,18 @@
</select>
</div>
</div>
<div class="item">
<div class="left">
<div class="title"> {{ $t("settings.deviceSelector") }} </div>
</div>
<div class="right">
<select v-model="outputDevice">
<option v-for="device in allOutputDevices" :key="device.deviceId" :value="device.deviceId" :selected="device.deviceId == outputDevice">
{{ device.label }}
</option>
</select>
</div>
</div>
<div class="item">
<div class="left">
<div class="title">
@ -227,6 +239,7 @@ export default {
size: "0KB",
length: 0,
},
allOutputDevices: [],
};
},
computed: {
@ -270,6 +283,19 @@ export default {
this.clearCache("tracks");
},
},
outputDevice: {
get() {
if (this.allOutputDevices.length == 0) this.getAllOutputDevices(); // Ensure devices loaded before get
const isValidDevice = this.allOutputDevices.find(device => device.deviceId === this.settings.outputDevice);
if (this.settings.outputDevice === undefined || isValidDevice === undefined) return "default"; // Default deviceId
return this.settings.outputDevice;
},
set(deviceId) {
if (deviceId === this.settings.outputDevice || deviceId === undefined) return;
this.$store.commit("changeOutputDevice", deviceId);
document.querySelector("audio").setSinkId(deviceId); // Change output device
},
},
showGithubIcon: {
get() {
if (this.settings.showGithubIcon === undefined) return true;
@ -356,6 +382,11 @@ export default {
},
},
methods: {
getAllOutputDevices() {
return navigator.mediaDevices.enumerateDevices().then(
devices => this.allOutputDevices = devices.filter(device => device.kind == "audiooutput")
);
},
logout() {
doLogout();
this.$router.push({ name: "home" });