fix: voice call, select audio input device (#7922)

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou 2024-05-07 16:18:48 +08:00 committed by GitHub
parent f08933f93c
commit 2c1595d0d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 305 additions and 79 deletions

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" class="icon" viewBox="0 0 1024 1024"><path d="M608 160c141.16 0 256 114.84 256 256 0 17.67 14.33 32 32 32s32-14.33 32-32c0-85.48-33.29-165.83-93.73-226.27C773.83 129.29 693.47 96 608 96c-17.67 0-32 14.33-32 32s14.33 32 32 32zm-24 168c61.76 0 112 50.24 112 112 0 17.67 14.33 32 32 32s32-14.33 32-32c0-97.05-78.95-176-176-176-17.67 0-32 14.33-32 32s14.33 32 32 32z"/><path d="M808.3 561.21c-12.76-3.83-25.7-6.2-38.46-7.03-60.3-4.5-116.45 18.9-146.55 61.08-22.6 31.67-45.66 50.01-68.52 54.5-17.71 3.48-33.12-1.7-45.49-5.85-2.66-.9-5.18-1.74-7.68-2.49-93.84-28.17-156.49-108.42-155.9-199.7.16-24.14 16.38-45.98 42.34-56.99 43.75-18.56 77.35-54 92.17-97.22 7.02-20.48 9.65-41.57 7.8-62.68-2.66-31.78-15.1-61.85-35.96-86.96-21.1-25.39-49.51-44-82.16-53.8-4.07-1.22-8.22-2.31-12.35-3.23-30.63-6.87-62.7-4.49-92.73 6.88-29.24 11.07-54.56 29.86-73.23 54.33a476.073 476.073 0 0 0-36.42 55.34 477.675 477.675 0 0 0-17.24 33.81C109.84 312.17 95.73 376.76 96 443.15c.26 63.78 13.7 126.26 39.95 185.7 27.55 62.39 69.3 119.84 120.74 166.11 54.14 48.71 117.6 84.85 188.63 107.4C499.02 919.41 554.33 928 610.21 928c10.99 0 22.01-.33 33.03-1 17.64-1.07 31.08-16.23 30.01-33.87-1.07-17.64-16.22-31.08-33.87-30.01-59.19 3.57-117.96-3.75-174.69-21.76C342.78 802.66 244.31 715.78 194.5 603c-46.76-105.9-46.21-221.33 1.55-325.03 4.55-9.87 9.57-19.72 14.92-29.26 9.29-16.54 19.89-32.64 31.5-47.86 23.47-30.77 64.09-45.87 101.07-37.58 2.66.6 5.33 1.3 7.95 2.08 40.93 12.29 69.48 45.6 72.75 84.86 0 .05.01.1.01.15 1.07 12.15-.47 24.39-4.58 36.37-8.94 26.06-29.58 47.59-56.63 59.07-23.58 10.01-43.63 25.72-57.99 45.45-15.12 20.78-23.2 45-23.36 70.05-.37 57.15 19 114.29 54.53 160.91 36.46 47.83 87.28 82.58 146.96 100.49 1.5.45 3.44 1.1 5.69 1.86 29.79 10.01 108.9 36.59 186.49-72.13 16.95-23.75 52.2-37.26 89.81-34.42l.36.03c7.97.51 16.17 2.02 24.34 4.47 22.12 6.64 42.04 25.38 56.11 52.77 16.97 33.04 21.71 72.53 12.1 100.56l-.16.47c-5.54 16.05-17.78 29.48-34.47 37.8-15.82 7.89-22.24 27.1-14.36 42.92s27.1 22.24 42.92 14.36c31.78-15.85 55.36-42.19 66.41-74.2l.18-.53c15.23-44.4 9.22-102.11-15.68-150.61-22.07-43.02-55.68-73.15-94.62-84.84z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" class="icon" viewBox="-186 -186 1365 1365"><path d="M608 160c141.16 0 256 114.84 256 256 0 17.67 14.33 32 32 32s32-14.33 32-32c0-85.48-33.29-165.83-93.73-226.27C773.83 129.29 693.47 96 608 96c-17.67 0-32 14.33-32 32s14.33 32 32 32zm-24 168c61.76 0 112 50.24 112 112 0 17.67 14.33 32 32 32s32-14.33 32-32c0-97.05-78.95-176-176-176-17.67 0-32 14.33-32 32s14.33 32 32 32z"/><path d="M808.3 561.21c-12.76-3.83-25.7-6.2-38.46-7.03-60.3-4.5-116.45 18.9-146.55 61.08-22.6 31.67-45.66 50.01-68.52 54.5-17.71 3.48-33.12-1.7-45.49-5.85-2.66-.9-5.18-1.74-7.68-2.49-93.84-28.17-156.49-108.42-155.9-199.7.16-24.14 16.38-45.98 42.34-56.99 43.75-18.56 77.35-54 92.17-97.22 7.02-20.48 9.65-41.57 7.8-62.68-2.66-31.78-15.1-61.85-35.96-86.96-21.1-25.39-49.51-44-82.16-53.8-4.07-1.22-8.22-2.31-12.35-3.23-30.63-6.87-62.7-4.49-92.73 6.88-29.24 11.07-54.56 29.86-73.23 54.33a476.073 476.073 0 0 0-36.42 55.34 477.675 477.675 0 0 0-17.24 33.81C109.84 312.17 95.73 376.76 96 443.15c.26 63.78 13.7 126.26 39.95 185.7 27.55 62.39 69.3 119.84 120.74 166.11 54.14 48.71 117.6 84.85 188.63 107.4C499.02 919.41 554.33 928 610.21 928c10.99 0 22.01-.33 33.03-1 17.64-1.07 31.08-16.23 30.01-33.87-1.07-17.64-16.22-31.08-33.87-30.01-59.19 3.57-117.96-3.75-174.69-21.76C342.78 802.66 244.31 715.78 194.5 603c-46.76-105.9-46.21-221.33 1.55-325.03 4.55-9.87 9.57-19.72 14.92-29.26 9.29-16.54 19.89-32.64 31.5-47.86 23.47-30.77 64.09-45.87 101.07-37.58 2.66.6 5.33 1.3 7.95 2.08 40.93 12.29 69.48 45.6 72.75 84.86 0 .05.01.1.01.15 1.07 12.15-.47 24.39-4.58 36.37-8.94 26.06-29.58 47.59-56.63 59.07-23.58 10.01-43.63 25.72-57.99 45.45-15.12 20.78-23.2 45-23.36 70.05-.37 57.15 19 114.29 54.53 160.91 36.46 47.83 87.28 82.58 146.96 100.49 1.5.45 3.44 1.1 5.69 1.86 29.79 10.01 108.9 36.59 186.49-72.13 16.95-23.75 52.2-37.26 89.81-34.42l.36.03c7.97.51 16.17 2.02 24.34 4.47 22.12 6.64 42.04 25.38 56.11 52.77 16.97 33.04 21.71 72.53 12.1 100.56l-.16.47c-5.54 16.05-17.78 29.48-34.47 37.8-15.82 7.89-22.24 27.1-14.36 42.92s27.1 22.24 42.92 14.36c31.78-15.85 55.36-42.19 66.41-74.2l.18-.53c15.23-44.4 9.22-102.11-15.68-150.61-22.07-43.02-55.68-73.15-94.62-84.84z"/></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/models/platform_model.dart';
typedef AudioINputSetDevice = void Function(String device);
typedef AudioInputBuilder = Widget Function(
List<String> devices, String currentDevice, AudioINputSetDevice setDevice);
class AudioInput extends StatelessWidget {
final AudioInputBuilder builder;
const AudioInput({Key? key, required this.builder}) : super(key: key);
static String getDefault() {
if (isWindows) return translate('System Sound');
return '';
}
static Future<String> getValue() async {
String device = await bind.mainGetOption(key: 'audio-input');
if (device.isNotEmpty) {
return device;
} else {
return getDefault();
}
}
static Future<void> setDevice(String device) async {
if (device == getDefault()) device = '';
await bind.mainSetOption(key: 'audio-input', value: device);
}
static Future<Map<String, Object>> getDevicesInfo() async {
List<String> devices = (await bind.mainGetSoundInputs()).toList();
if (isWindows) {
devices.insert(0, translate('System Sound'));
}
String current = await getValue();
return {'devices': devices, 'current': current};
}
@override
Widget build(BuildContext context) {
return futureBuilder(
future: getDevicesInfo(),
hasData: (data) {
String currentDevice = data['current'];
List<String> devices = data['devices'] as List<String>;
if (devices.isEmpty) {
return const Offstage();
}
return builder(devices, currentDevice, setDevice);
},
);
}
}

View File

@ -6,6 +6,7 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/common/widgets/audio_input.dart';
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
@ -469,38 +470,7 @@ class _GeneralState extends State<_General> {
return const Offstage();
}
String getDefault() {
if (isWindows) return translate('System Sound');
return '';
}
Future<String> getValue() async {
String device = await bind.mainGetOption(key: 'audio-input');
if (device.isNotEmpty) {
return device;
} else {
return getDefault();
}
}
setDevice(String device) {
if (device == getDefault()) device = '';
bind.mainSetOption(key: 'audio-input', value: device);
}
return futureBuilder(future: () async {
List<String> devices = (await bind.mainGetSoundInputs()).toList();
if (isWindows) {
devices.insert(0, translate('System Sound'));
}
String current = await getValue();
return {'devices': devices, 'current': current};
}(), hasData: (data) {
String currentDevice = data['current'];
List<String> devices = data['devices'] as List<String>;
if (devices.isEmpty) {
return const Offstage();
}
return AudioInput(builder: (devices, currentDevice, setDevice) {
return _Card(title: 'Audio Input Device', children: [
...devices.map((device) => _Radio<String>(context,
value: device,

View File

@ -4,6 +4,7 @@ import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/audio_input.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:flutter_hbb/models/chat_model.dart';
@ -701,17 +702,86 @@ class _CmControlPanel extends StatelessWidget {
children: [
Offstage(
offstage: !client.inVoiceCall,
child: buildButton(
context,
color: Colors.red,
onClick: () => closeVoiceCall(),
icon: Icon(
Icons.call_end_rounded,
color: Colors.white,
size: 14,
),
text: "Stop voice call",
textColor: Colors.white,
child: Row(
children: [
Expanded(
child: buildButton(context,
color: MyTheme.accent,
onClick: null, onTapDown: (details) async {
final devicesInfo = await AudioInput.getDevicesInfo();
List<String> devices = devicesInfo['devices'] as List<String>;
if (devices.isEmpty) {
msgBox(
gFFI.sessionId,
'custom-nocancel-info',
'Prompt',
'no_audio_input_device_tip',
'',
gFFI.dialogManager,
);
return;
}
String currentDevice = devicesInfo['current'] as String;
final x = details.globalPosition.dx;
final y = details.globalPosition.dy;
final position = RelativeRect.fromLTRB(x, y, x, y);
showMenu(
context: context,
position: position,
items: devices
.map((d) => PopupMenuItem<String>(
value: d,
height: 18,
padding: EdgeInsets.zero,
onTap: () => AudioInput.setDevice(d),
child: IgnorePointer(
child: RadioMenuButton(
value: d,
groupValue: currentDevice,
onChanged: (v) {
if (v != null) AudioInput.setDevice(v);
},
child: Container(
child: Text(
d,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
constraints: BoxConstraints(
maxWidth:
kConnectionManagerWindowSizeClosedChat
.width -
80),
),
)),
))
.toList(),
);
},
icon: Icon(
Icons.call_rounded,
color: Colors.white,
size: 14,
),
text: "Audio input",
textColor: Colors.white),
),
Expanded(
child: buildButton(
context,
color: Colors.red,
onClick: () => closeVoiceCall(),
icon: Icon(
Icons.call_end_rounded,
color: Colors.white,
size: 14,
),
text: "Stop voice call",
textColor: Colors.white,
),
)
],
),
),
Offstage(
@ -872,12 +942,14 @@ class _CmControlPanel extends StatelessWidget {
Widget buildButton(BuildContext context,
{required Color? color,
required Function() onClick,
Icon? icon,
GestureTapCallback? onClick,
Widget? icon,
BoxBorder? border,
required String text,
required Color? textColor,
String? tooltip}) {
String? tooltip,
GestureTapDownCallback? onTapDown}) {
assert(!(onClick == null && onTapDown == null));
Widget textWidget;
if (icon != null) {
textWidget = Text(
@ -901,7 +973,16 @@ class _CmControlPanel extends StatelessWidget {
color: color, borderRadius: borderRadius, border: border),
child: InkWell(
borderRadius: borderRadius,
onTap: () => checkClickTime(client.id, onClick),
onTap: () {
if (onClick == null) return;
checkClickTime(client.id, onClick);
},
onTapDown: (details) {
if (onTapDown == null) return;
checkClickTime(client.id, () {
onTapDown.call(details);
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [

View File

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/common/widgets/audio_input.dart';
import 'package:flutter_hbb/common/widgets/toolbar.dart';
import 'package:flutter_hbb/models/chat_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
@ -1953,34 +1954,71 @@ class _VoiceCallMenu extends StatelessWidget {
@override
Widget build(BuildContext context) {
menuChildrenGetter() {
final audioInput =
AudioInput(builder: (devices, currentDevice, setDevice) {
return Column(
children: devices
.map((d) => RdoMenuButton<String>(
child: Container(
child: Text(
d,
overflow: TextOverflow.ellipsis,
),
constraints: BoxConstraints(maxWidth: 250),
),
value: d,
groupValue: currentDevice,
onChanged: (v) {
if (v != null) setDevice(v);
},
ffi: ffi,
))
.toList(),
);
});
return [
audioInput,
Divider(),
MenuButton(
child: Text(translate('End call')),
onPressed: () => bind.sessionCloseVoiceCall(sessionId: ffi.sessionId),
ffi: ffi,
),
];
}
return Obx(
() {
final String tooltip;
final String icon;
switch (ffi.chatModel.voiceCallStatus.value) {
case VoiceCallStatus.waitingForResponse:
tooltip = "Waiting";
icon = "assets/call_wait.svg";
break;
return buildCallWaiting(context);
case VoiceCallStatus.connected:
tooltip = "Disconnect";
icon = "assets/call_end.svg";
break;
return _IconSubmenuButton(
tooltip: 'Voice call',
svg: 'assets/voice_call.svg',
color: _ToolbarTheme.blueColor,
hoverColor: _ToolbarTheme.hoverBlueColor,
menuChildrenGetter: menuChildrenGetter,
ffi: ffi,
);
default:
return Offstage();
}
return _IconMenuButton(
assetName: icon,
tooltip: tooltip,
onPressed: () =>
bind.sessionCloseVoiceCall(sessionId: ffi.sessionId),
color: _ToolbarTheme.redColor,
hoverColor: _ToolbarTheme.hoverRedColor);
},
);
}
}
Widget buildCallWaiting(BuildContext context) {
return _IconMenuButton(
assetName: "assets/call_wait.svg",
tooltip: "Waiting",
onPressed: () => bind.sessionCloseVoiceCall(sessionId: ffi.sessionId),
color: _ToolbarTheme.redColor,
hoverColor: _ToolbarTheme.hoverRedColor,
);
}
}
class _RecordMenu extends StatelessWidget {
const _RecordMenu({Key? key}) : super(key: key);
@ -2115,7 +2153,7 @@ class _IconSubmenuButton extends StatefulWidget {
final Color hoverColor;
final List<Widget> Function() menuChildrenGetter;
final MenuStyle? menuStyle;
final FFI ffi;
final FFI? ffi;
final double? width;
_IconSubmenuButton({
@ -2126,7 +2164,7 @@ class _IconSubmenuButton extends StatefulWidget {
required this.color,
required this.hoverColor,
required this.menuChildrenGetter,
required this.ffi,
this.ffi,
this.menuStyle,
this.width,
}) : super(key: key);
@ -2208,13 +2246,13 @@ class MenuButton extends StatelessWidget {
final VoidCallback? onPressed;
final Widget? trailingIcon;
final Widget? child;
final FFI ffi;
final FFI? ffi;
MenuButton(
{Key? key,
this.onPressed,
this.trailingIcon,
required this.child,
required this.ffi})
this.ffi})
: super(key: key);
@override
@ -2223,7 +2261,9 @@ class MenuButton extends StatelessWidget {
key: key,
onPressed: onPressed != null
? () {
_menuDismissCallback(ffi);
if (ffi != null) {
_menuDismissCallback(ffi!);
}
onPressed?.call();
}
: null,
@ -2236,13 +2276,13 @@ class CkbMenuButton extends StatelessWidget {
final bool? value;
final ValueChanged<bool?>? onChanged;
final Widget? child;
final FFI ffi;
final FFI? ffi;
const CkbMenuButton(
{Key? key,
required this.value,
required this.onChanged,
required this.child,
required this.ffi})
this.ffi})
: super(key: key);
@override
@ -2253,7 +2293,9 @@ class CkbMenuButton extends StatelessWidget {
child: child,
onChanged: onChanged != null
? (bool? value) {
_menuDismissCallback(ffi);
if (ffi != null) {
_menuDismissCallback(ffi!);
}
onChanged?.call(value);
}
: null,
@ -2266,13 +2308,13 @@ class RdoMenuButton<T> extends StatelessWidget {
final T? groupValue;
final ValueChanged<T?>? onChanged;
final Widget? child;
final FFI ffi;
final FFI? ffi;
const RdoMenuButton({
Key? key,
required this.value,
required this.groupValue,
required this.child,
required this.ffi,
this.ffi,
this.onChanged,
}) : super(key: key);
@ -2284,7 +2326,9 @@ class RdoMenuButton<T> extends StatelessWidget {
child: child,
onChanged: onChanged != null
? (T? value) {
_menuDismissCallback(ffi);
if (ffi != null) {
_menuDismissCallback(ffi!);
}
onChanged?.call(value);
}
: null,
@ -2471,10 +2515,11 @@ class InputModeMenu {
_menuDismissCallback(FFI ffi) => ffi.inputModel.refreshMousePos();
Widget _buildPointerTrackWidget(Widget child, FFI ffi) {
Widget _buildPointerTrackWidget(Widget child, FFI? ffi) {
return Listener(
onPointerHover: (PointerHoverEvent e) =>
ffi.inputModel.lastMousePos = e.position,
onPointerHover: (PointerHoverEvent e) => {
if (ffi != null) {ffi.inputModel.lastMousePos = e.position}
},
child: MouseRegion(
child: child,
),

View File

@ -454,7 +454,12 @@ async fn handle(data: Data, stream: &mut Connection) {
if let Some(v) = value.get("privacy-mode-impl-key") {
crate::privacy_mode::switch(v);
}
let pre_opts = Config::get_options();
let new_audio_input = pre_opts.get("audio-input");
Config::set_options(value);
if new_audio_input != pre_opts.get("audio-input") {
crate::audio_service::restart();
}
allow_err!(stream.send(&Data::Options(None)).await);
}
},

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", "跟随远程光标"),
("Follow remote window focus", "跟随远程窗口焦点"),
("default_proxy_tip", "默认代理协议及端口为 Socks5 和 1080"),
("no_audio_input_device_tip", "未找到音频输入设备"),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", "Sledovat dálkový kurzor"),
("Follow remote window focus", "Sledovat zaměření vzdáleného okna"),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", "Dem entfernten Cursor folgen"),
("Follow remote window focus", "Dem Fokus des entfernten Fensters folgen"),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -224,5 +224,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", "Default protocol and port are Socks5 and 1080"),
("no_audio_input_device_tip", "No audio input device found."),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", "Seguir cursor remoto"),
("Follow remote window focus", "Seguir ventana remota activa"),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", "Segui cursore remoto"),
("Follow remote window focus", "Segui focus finestra remota"),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", "Sekot attālajam kursoram"),
("Follow remote window focus", "Sekot attālā loga fokusam"),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", "Volg de cursor op afstand"),
("Follow remote window focus", "Volg de focus van het venster op afstand"),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", "Podążaj za zdalnym kursorem"),
("Follow remote window focus", "Podążaj za aktywnością zdalnych okien"),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", "Следовать за удалённым курсором"),
("Follow remote window focus", "Следовать за фокусом удалённого окна"),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", "Nasledovať vzdialený kurzor"),
("Follow remote window focus", "Nasledovať vzdialené zameranie okna"),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", "跟隨遠端游標"),
("Follow remote window focus", "跟隨遠端視窗焦點"),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Follow remote cursor", ""),
("Follow remote window focus", ""),
("default_proxy_tip", ""),
("no_audio_input_device_tip", ""),
].iter().cloned().collect();
}

View File

@ -103,6 +103,7 @@ mod pa_impl {
#[cfg(not(any(target_os = "linux", target_os = "android")))]
mod cpal_impl {
use self::service::{Reset, ServiceSwap};
use super::*;
use cpal::{
traits::{DeviceTrait, HostTrait, StreamTrait},
@ -125,7 +126,23 @@ mod cpal_impl {
}
}
pub fn run(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> {
fn run_restart(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> {
state.reset();
sp.snapshot(|_sps: ServiceSwap<_>| Ok(()))?;
match &state.stream {
None => {
state.stream = Some(play(&sp)?);
}
_ => {}
}
if let Some((_, format)) = &state.stream {
sp.send_shared(format.clone());
}
RESTARTING.store(false, Ordering::SeqCst);
Ok(())
}
fn run_serv_snapshot(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> {
sp.snapshot(|sps| {
match &state.stream {
None => {
@ -141,6 +158,14 @@ mod cpal_impl {
Ok(())
}
pub fn run(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> {
if !RESTARTING.load(Ordering::SeqCst) {
run_serv_snapshot(sp, state)
} else {
run_restart(sp, state)
}
}
fn send(
data: Vec<f32>,
sample_rate0: u32,

View File

@ -369,6 +369,8 @@ pub fn set_option(key: String, value: String) {
return;
}
}
} else if &key == "audio-input" {
crate::audio_service::restart();
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{