MoreRSS

site iconMumulhl修改

00后,分享编程、技巧、生活
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Mumulhl的 RSS 预览

没麦怎么办,把手机当作麦克风!(Scrcpy 篇)

2025-04-30 22:31:56

很久很久之前写了一篇在 Linux 上如何把 Android 作为麦克风的教程,不久前因为要用 Windows 上的一些软件,装上了 Windows,顺便折腾个在 Windows 上的把 Android作为麦克风的方案。

找了很久,没有找到既方便又开源的方案,一些免费的方案(如 WO Mic)音质不太行,自己摸索了一方,最后用 ScrcpyVB-CABLE 实现了。关键是 不需要在 Android 上安装任何软件!

使用 Scrcpy 把 Android 作为麦克风理论上在 Linux 和 macOS 上也可以,在音频驱动那里会有点不同。

确保 Android 版本 >= 11

安装

Scrcpy

首先是 Scrcpy,它将用于从 Android 转发音频,可以在 Github Release 页面 下载对应的压缩包再解压,32 位下载 scrcpy-win32-vX.X.zip,64 位下载 scrcpy-win64-vX.X.zip

最好的办法是直接通过 Windows 的包管理器 winget 安装:

1
winget install --id=Genymobile.scrcpy

VB-CABLE

VB-CABLE 的用途是将音频由输出转到输入。

前往 VB-CABLE官网,点击 VBCABLE_Driver_Pack45.zip 下载压缩包,解压后,双击执行 VBCABLE_Setup_x64.exe(64 位)或 VBCABLE_Setup.exe(32 位)安装。然后就好了很么都不用动。系统 > 声音 > 输入 应该会自己变成 CABLE Output。

正式步骤

Android 上开启 USB 调试

具体步骤见 Android Developer 文档

运行 Scrcpy 命令

如果通过压缩包获取 Scrcpy 点击解压后目录里的 open_a_terminal_here.bat 在该目录下打开终端。通过 winget 安装 Scrcpy 直接打开终端即可。

接着运行:

1
scrcpy --no-video --no-control --audio-source=mic-unprocessed

第一次运行 Android 上会要求授权,授权即可。

参数解释:

  • --no-video 不要画面
  • --no-control 不要控制
  • --audio-source=mic-unprocessed 将声音源指定为未处理的麦克风音频

声音源还有很多选项,完整选项见 Audio 文档,我觉得 mic-unprocessed 音质是最好的,也可以试试别的,简单列举几个:

  • mic: 捕获麦克风
  • mic-unprocessed: 捕获未处理(原始)的麦克风声音
  • mic-camcorder: 捕获为录制视频调优的麦克风声音,如果可用,其方向与摄像头相同
  • mic-voice-recognition: 捕获为语音识别调优的麦克风声音
  • mic-voice-communication: 捕获为语音通信调优的麦克风声音(例如,如果可用,它会利用回声消除或自动增益控制)

改变音谱输出设备

系统 > 声音 > 音量合成器 > 应用 将 Scrcpy 创建的窗口的 输出设备 更改为 CABLE Input,现在通过 Android 麦克风输入的音频就不会从扬声器里输出了,会从输入设备 CABLE Output 里输出。

Imagen 3: 免费文生图工具保持人物一致性

2025-02-16 11:08:28

Featured image of post Imagen 3: 免费文生图工具保持人物一致性

Imagen 3

Imagen 3 是 Google DeepMind 最新的文生图模型,可以在 ImageFx 免费使用。Imagen 3 在 Gemini 内也是可以使用的,Gemini 生成图片有时会不听话,不生成图片,所以本文采用 ImageFx。

如何实现人物一致性?

主要有两种方式:

  1. 对现有的人物图像进行“引用”,如 Midjourney
  2. 对人物描述相当详细的 Prompt,基本适用于各种文生图模型

第一种方式 Imagen 3 不支持,本文就采用第二种。

创造人物形象

想要有一个对人物描述相当详细的 Prompt,由人来写就太繁琐了,可以提供一些人物的特征让大语言模型帮我们写,这里采用 Gemini 2.0 Flash Thinking 模型。给出的特征越详细越能生成符合自己想要的人物。

示范 Prompt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
你是一名文生图prompt专家,请写一个英文prompt去生成一个人物,将prompt放入代码块中。

prompt要求:

1. 无背景
2. 全身图

人物:

1. 白色头发
2. 穿白里带红的和服
3. 20岁左右的女性
4. 日本动漫风格

注:不要想着生成loli什么的,生成图片时会被判定为“儿童”而无法生成!

先用大语言模型写的 Prompt 生成一下,如果效果不错,把种子锁定下,最好把种子保存下,方便以后生成尽可能相似的人物。

Example 1

根据人物创造详细的 Prompt

下载一张图片,发给支持图像的大语言模型,并让它根据图像创造出详细的 Prompt。

示范 Prompt:

1
你是一名文生图prompt专家,请写一个英文prompt去描述图像人物,将prompt放入代码块中。

把人物嵌入图像中

首先先生成一个生成人物坐在桌前写作业的图片的Prompt。

示范 Prompt:

1
2
3
4
5
6
7
你是一名文生图prompt专家,请写一个英文prompt去生成一张图片,将prompt放入代码块中。

要求:

1. 用 [CHARACTER] 代替人物
2. 人物坐在一张桌子前写作业
3. 氛围温馨

我们将使用 [CHARACTER] 代指人物,然后把上一个步骤生成的 Prompt 拼接进去,如:

1
2
3
[CHARACTER] sitting at a wooden desk, writing homework in a softly lit room, warm atmosphere, cozy, desk lamp, books, study, home, peaceful, detailed, realistic.

[CHARACTER]: 1girl, full body, Tomoe Gozen, white hair, long hair, white kimono, red and gold floral pattern, standing, arms outstretched, black background

最终效果图:

2024年度总结:AI原来能这么好用

2025-01-01 10:07:26

Featured image of post 2024年度总结:AI原来能这么好用

(封面来自 Unsplash,作者 BoliviaInteligente)

2024年结束了,2025年来了,转眼又是一年了。2024给我最大的震撼就是AI的辅助带来的便捷,过去稍有接触AI,但是没有太深入地使用,导致也用不好。

真正对 AI 的接触

看到 X 上有很多大佬用 Cursor 开发出了一些项目,就有点手痒痒了。过去我使用的编辑器是 Helix,一个全键盘编辑器,感觉效率会高一些。如果要用 Cursor 这样的 AI 编辑器就要脱离全键盘的操作了,但是 AI 能提高我的开发效率的话,没有了全键盘操作也不算亏。于是 Cursor 就取代 Helix 在我的电脑上的地位。

Cursor 让我震撼的是,在编辑一块代码的时候,居然可以按 Tab 键去补全其他部分相关的代码,以前我以为 AI 代码补全就只能像补全变量名、函数名那样补全,这完全打破了我的认知啊!

最早使用 AI 我都倾向于想用一句话让 AI 完成我想要做的有点复杂的事,但在理论上是不可行的,事实上也是不行的。只用一句话去表述一件较为复杂的事,信息量很少,难以去描述细节,AI 自然无法做到和自己所想的相符。和人说件事,说话只说一句,得让对方多疯狂,更别说是 AI 了。在我的使用体验来说,AI 更适合去做小任务,而非是复杂的大任务,如果要让 AI 去做大任务,将它分解为多个小任务效果会更好。

AI 的使用体验总结起来就是一个字——“爽”,每周最多一天的时间里我可以开发得更高效。

开源让我学到了很多

2024年新开发的项目主要实在暑假里开发的,好几个我都觉得不太行,所以只挑出我最经常维护的词悦来说吧。

词悦是一个开源的支持 mdict 格式的词典,是我觉得 Android 上没有简洁、好用的开源的 mdict 词典而开发的。虽然最初发布的时候很简陋,不过现在经过几个月的开发,已经完善了许多了。

最初词悦用 git-cliff 生成版本发布时的更新日志,后来我才慢慢在词悦中把 Github 的 label、milestones 功能好好用上,现在词悦的版本更新日志是由 Github 生成的,这样可以把这个版本贡献者直接显示在更新日志中。

词悦还提交到了 F-Droid 上,我还提交过几个 MR,但是水平不太够,没办法,犯了很多低级错误,都由 linsui 大佬纠正了,感谢 linsui 大佬不知疲倦地修改我的 MR!

在开源中,我学会了许多以前不知道的技巧、技术,开源不仅利他,还能利己。

重启博客

2024,我重启了我的博客,一年的文章就是2023的十几倍了(2023只有一篇文章)。2024我写了15篇博文,其中大部分都是在暑假写的。暑假闲来无事重启了博客,还把博客主题换成了 Stack。我还把博客提交到 V2EX VXNA、博友圈等平台上,我的博客也终于不再是我自娱自乐的地方了

对2025的展望

希望我可以在2025把 AI 使用得更加得心应手吧;希望我能在2025凭借自己的能力去赚到人生第一桶金,哪怕没几块也行哪。

给Flutter Android App支持全局上下文菜单

2024-11-30 23:16:10

最近在开发词悦(一个 mdict 词典)的时候,需要支持全局上下文菜单,查了很多资料都没有找到合适的方法,问了下 cursor,得到了初步方案,经过稍微的改动就有了这篇教程。

本文开发环境在 Linux 下。

初始化项目

1
2
flutter create example
cd example

写代码

Manifest

编辑 android/app/src/main/AndroidManifest.xml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 省略... -->
 <meta-data
 android:name="io.flutter.embedding.android.NormalTheme"
 android:resource="@style/NormalTheme"
 />
 <intent-filter>
 <action android:name="android.intent.action.MAIN"/>
 <category android:name="android.intent.category.LAUNCHER"/>
 </intent-filter>
</activity>
<!-- 新增的部分 -->
<!-- android:label 是上下文菜单中显示的名称 -->
<activity
 android:name=".ProcessTextActivity"
 android:label="example"
 android:exported="true">
 <intent-filter>
 <action android:name="android.intent.action.PROCESS_TEXT" />
 <data android:mimeType="text/plain" />
 <category android:name="android.intent.category.DEFAULT" />
 </intent-filter>
</activity>

原生 Android

创建 android/app/src/main/kotlin/com/example/example/ProcessTextActivity.kt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.example.example

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity

class ProcessTextActivity : Activity() {
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)

 val text = intent?.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT)?.toString() ?: ""

 val intent = Intent(this, MainActivity::class.java).apply {
 action = Intent.ACTION_PROCESS_TEXT
 putExtra(Intent.EXTRA_PROCESS_TEXT, text)
 addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
 addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) // 如果不加这个 flag,app 在后台运行时无法把选中的文本传给 Flutter
 }

 startActivity(intent)
 finish()
 }
}

编辑 android/app/src/main/kotlin/com/example/example/MainActivity.kt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.example.example

import android.content.Intent
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
 private val CHANNEL = "com.example.example/process_text"
 private var methodChannel: MethodChannel? = null

 override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
 super.configureFlutterEngine(flutterEngine)

 methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
 }

 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)

 if (intent?.action == Intent.ACTION_PROCESS_TEXT) {
 val text = intent.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT)?.toString() ?: ""
 methodChannel?.invokeMethod("processText", text)
 }
 }
}

Flutter 部分

编辑 lib/main.dart:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
 WidgetsFlutterBinding.ensureInitialized();

 platform.setMethodCallHandler((call) async {
 if (call.method == "processText") {
 final text = call.arguments as String; // call.arguments 里就是选中的文本了
 print(text);
 }
 });

 runApp(const MyApp());
}

const platform = MethodChannel("com.example.example/process_text");

结尾

接下来怎么样就靠你的想象力了 :)

nginx 搭建极简直播服务

2024-09-30 20:31:22

当我们像在仅需要在局域网内进行直播这种情况时,就可以自己动手搭建极简的直播服务。

预备知识

会写一点点 nginx 配置。

概念

RTMP

用于流媒体传输的协议,最初用于 Flash 播放器,也可以用于直播。

本教程中,我们将用该协议将直播流推送至 nginx 服务器。

HLS

基于 HTTP 流媒体传输协议,它将流分割成多个文件传输,对于直播流,以 m3u8 文件为播放列表,以 ts 文件为视频。

本教程中,该协议用于向浏览器传输直播流。

编译 nginx

nginx 本身并不支持 RTMP 协议,需要把 nginx-rtmp-module 模块编译进 nginx。

nginx.org 下载 nginx 源码,解压后进入目录。

然后克隆 nginx-rtmp-module 源码:

1
git clone https://github.com/arut/nginx-rtmp-module --depth=1

编译并安装 nginx:

1
2
3
./configure --add-module=nginx-rtmp-module
make
make install

最后 /usr/local/nginx/sbin 添加到环境变量 PATH 中。

前端

一般浏览器不能直接播放 HLS,这里采用 DPlayer + hls.js 用于播放 (因为好看)

这里 还有 这里 下载 DPlayer.min.jshls.min.js/usr/local/nginx/html/

/usr/local/nginx/html/index.html 改为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">

<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <title>直播</title>
</head>

<body>
 <div id="dplayer"></div>
 <script src="hls.min.js"></script>
 <script src="DPlayer.min.js"></script>
 <script>
 const dp = new DPlayer({
 container: document.getElementById('dplayer'),
 autoplay: true, // 自动播放
 live: true, // 直播
 video: {
 url: '/live/example.m3u8', // 等下 example 改成自己的推流码
 type: 'hls',
 },
 });
 </script>
</body>

</html>

打开首页就是播放器。

配置

/usr/local/nginx/html/conf/nginx.conf 改为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
worker_processes 1;


events {
 worker_connections 1024;
}

rtmp {
 server {
 listen 1935;

 chunk_size 4000;

 application live { # /live 是推流地址
 live on;
 hls on;
 hls_path html/live;
 }
 }
}

http {
 server {
 listen 8080;

 location / {
 root html;
 add_header Cache-Control no-cache;
 }
 }
}

关于 nginx-rtmp-module 更多配置见 wiki

不用担心 HLS 的 ts 文件会无限增加下去,nginx-rtmp-module 会删除老的 ts 文件。

启动 nginx:

1
nginx

推流

这里推流使用 OBS Studio

在“设置”-“直播”中填写“服务器”为 http://localhost:8080/live,“推流码”填自己想要的,但 index.html 中的 example 要改成这个推流码,不然没法播放直播。

配置好来源就可以开始直播啦~

试试看

打开 http://localhost:8080 就可以看到直播了,大概会有 10s 左右的延迟。

正式使用时,请根据自己的需求更改。

结语

过去,学校里的大型表演,能到现场看的人数有限,不是所有人都能到现场看,而且没有直播。再过不久有一个歌唱比赛(我当然去参加海选了,只不过没选上),我想搭建一个直播服务去直播这些表演,于是就有了这篇教程。因为能否直播还要征得校方同意,所以不一定能用得上…

给 Android 换个配色

2024-08-15 15:25:12

介绍

Material You 加入到 Android 12 中,其中包括了动态配色。

用户可以直接在系统层面上很方便地改变系统配色和支持动态配色的软件的配色。开发者也不需要再开发配色功能。

设置

(因为不同厂商把选项名改得不一样,所以很难明确写出选项的名称)

  1. 打开设置
  2. 点击一个名称大致包含了 桌面壁纸 的选项
  3. 点击名称像 系统风格 的选项,就可以选择颜色了。

可以单独选择配色,也可以从壁纸上提取出颜色。

结语

用了 Android 13 一整年了,都不知道有这个功能 😂 最近用 Flutter 开发词典的时候才了解到。