Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/sound #166

Merged
merged 3 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@ fn save_base64_to_image(base64_str: &str, file_name: &str) -> Result<(), String>
}
}

/// 读取 MP3 文件并返回其 Base64 编码字符串
#[tauri::command]
fn read_mp3_file(path: String) -> Result<String, String> {
let mut file = File::open(&path).map_err(|e| format!("无法打开文件: {}", e))?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).map_err(|e| format!("读取文件时出错: {}", e))?;

// 将文件内容编码为 Base64
let base64_str = general_purpose::STANDARD.encode(&buffer);
Ok(base64_str)
}



#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
println!("程序运行了!");
Expand All @@ -95,7 +109,8 @@ pub fn run() {
save_file_by_path,
convert_image_to_base64,
save_base64_to_image,
check_json_exist
check_json_exist,
read_mp3_file
])
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_fs::init())
Expand Down
14 changes: 12 additions & 2 deletions src/core/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ export namespace Settings {
moveAmplitude: number;
moveFriction: number;
gamepadDeadzone: number;

// 音效相关
soundEnabled: boolean;
cuttingLineStartSoundFile: string;
connectLineStartSoundFile: string;
connectFindTargetSoundFile: string;
cuttingLineReleaseSoundFile: string;
// github 相关
githubToken: string;
githubUser: string;
Expand Down Expand Up @@ -73,7 +78,12 @@ export namespace Settings {
moveAmplitude: 2,
moveFriction: 0.1,
gamepadDeadzone: 0.1,

// 音效相关
soundEnabled: true,
cuttingLineStartSoundFile: "",
connectLineStartSoundFile: "",
connectFindTargetSoundFile: "",
cuttingLineReleaseSoundFile: "",
// github 相关
githubToken: "",
githubUser: "",
Expand Down
115 changes: 115 additions & 0 deletions src/core/SoundService.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// 实测发现 不可行:
// @tauri-apps/plugin-fs 只能读取文本文件,不能强行读取流文件并强转为ArrayBuffer
// import { readTextFile } from "@tauri-apps/plugin-fs";

import { invoke } from "@tauri-apps/api/core";
import { StringDict } from "./dataStruct/StringDict";
import { Settings } from "./Settings";

/**
* 播放音效的服务
* 这个音效播放服务是用户自定义的
*/
export namespace SoundService {

let cuttingLineStartSoundFile = "";
let connectLineStartSoundFile = "";
let connectFindTargetSoundFile = "";
let cuttingLineReleaseSoundFile = "";

export function init() {
Settings.watch("cuttingLineStartSoundFile", (value) => {
cuttingLineStartSoundFile = value;
});
Settings.watch("connectLineStartSoundFile", (value) => {
connectLineStartSoundFile = value;
});
Settings.watch("connectFindTargetSoundFile", (value) => {
connectFindTargetSoundFile = value;
});
Settings.watch("cuttingLineReleaseSoundFile", (value) => {
cuttingLineReleaseSoundFile = value;
});
}

export namespace play {
// 开始切断
export function cuttingLineStart() {
loadAndPlaySound(cuttingLineStartSoundFile);
}

// 开始连接
export function connectLineStart() {
loadAndPlaySound(connectLineStartSoundFile);
}

// 连接吸附到目标点
export function connectFindTarget() {
loadAndPlaySound(connectFindTargetSoundFile);
}

// 自动保存执行特效
// 自动备份执行特效

// 框选增加物体音效

// 切断特效声音
export function cuttingLineRelease() {
loadAndPlaySound(cuttingLineReleaseSoundFile);
}
// 连接成功
}

const audioContext = new window.AudioContext();

async function loadAndPlaySound(filePath: string) {
if (filePath.trim() === "") {
console.log("filePath is empty");
return;
}

// 解码音频数据
const audioBuffer = await getAudioBufferByFilePath(filePath);
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioContext.destination);
source.start(0);
}

const pathAudioBufferMap = new StringDict<AudioBuffer>();

async function getAudioBufferByFilePath(filePath: string) {
// 先从缓存中获取音频数据
if (pathAudioBufferMap.hasId(filePath)) {
const result = pathAudioBufferMap.getById(filePath);
if (result) {
return result;
}
}

// 缓存中没有

// 读取文件为字符串
const base64Data: string = await invoke<string>("read_mp3_file", {
path: filePath,
});
// 解码 Base64 字符串
const byteCharacters = atob(base64Data); // 使用 atob 解码 Base64 字符串
const byteNumbers = new Uint8Array(byteCharacters.length);

for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i); // 转换为字节数组
}

// 创建 ArrayBuffer
const arrayBuffer = byteNumbers.buffer;

// 解码音频数据
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);

// 加入缓存
pathAudioBufferMap.setById(filePath, audioBuffer);

return audioBuffer;
}
}
4 changes: 4 additions & 0 deletions src/core/controller/concrete/ControllerCutting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CircleFlameEffect } from "../../effect/concrete/CircleFlameEffect";
import { LineCuttingEffect } from "../../effect/concrete/LineCuttingEffect";
import { EdgeRenderer } from "../../render/canvas2d/entityRenderer/edge/EdgeRenderer";
import { Renderer } from "../../render/canvas2d/renderer";
import { SoundService } from "../../SoundService";
import { Stage } from "../../stage/Stage";
import { StageManager } from "../../stage/stageManager/StageManager";
import { Section } from "../../stageObject/entity/Section";
Expand Down Expand Up @@ -45,6 +46,8 @@ ControllerCutting.mousedown = (event: MouseEvent) => {
cuttingStartLocation,
cuttingStartLocation.clone(),
);
// 添加音效提示
SoundService.play.cuttingLineStart();
} else {
Stage.isCutting = false;
}
Expand Down Expand Up @@ -144,4 +147,5 @@ ControllerCutting.mouseup = (event: MouseEvent) => {
cuttingStartLocation.distance(ControllerCutting.lastMoveLocation) / 10,
),
);
SoundService.play.cuttingLineRelease();
};
6 changes: 5 additions & 1 deletion src/core/controller/concrete/ControllerNodeConnection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { EdgeRenderer } from "../../render/canvas2d/entityRenderer/edge/EdgeRend
import { ConnectableEntity } from "../../stageObject/StageObject";
import { ConnectPoint } from "../../stageObject/entity/ConnectPoint";
import { v4 } from "uuid";
import { SoundService } from "../../SoundService";

/**
* 右键连线功能 的控制器
Expand Down Expand Up @@ -109,6 +110,8 @@ ControllerNodeConnection.mousedown = (event: MouseEvent) => {
),
);
}
// 播放音效
SoundService.play.connectLineStart();
}
};

Expand All @@ -126,9 +129,10 @@ ControllerNodeConnection.mousemove = (event: MouseEvent) => {
let isFindConnectToNode = false;
for (const entity of StageManager.getConnectableEntity()) {
if (entity.collisionBox.isPointInCollisionBox(worldLocation)) {
// 找到了连接的节点,吸附上去
Stage.connectToEntity = entity;
isFindConnectToNode = true;

SoundService.play.connectFindTarget();
break;
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { StartFilesManager } from "./core/StartFilesManager";
import "./index.pcss";
import { DialogProvider } from "./utils/dialog";
import { PopupDialogProvider } from "./utils/popupDialog";
import { SoundService } from "./core/SoundService";

const router = createMemoryRouter(routes);
const Routes = () => <RouterProvider router={router} />;
Expand Down Expand Up @@ -66,6 +67,7 @@ async function loadSyncModules() {
Stage.init();
StageHistoryManager.init();
StageStyleManager.init();
SoundService.init();
}

/** 加载语言文件 */
Expand Down
1 change: 1 addition & 0 deletions src/pages/settings/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default function SettingsLayout() {
<NavLink to="/settings/control">{t("control")}</NavLink>
<NavLink to="/settings/ai">{t("ai")}</NavLink>
<NavLink to="/settings/github">{t("github")}</NavLink>
<NavLink to="/settings/sounds">sounds</NavLink>
</div>
<div className="container mx-auto flex-1 overflow-auto">
<Outlet />
Expand Down
37 changes: 37 additions & 0 deletions src/pages/settings/sounds.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Folder } from "lucide-react";
import { SettingField } from "./_field";

export default function Sounds() {
return (
<>
<SettingField
icon={<Folder />}
settingKey="cuttingLineStartSoundFile"
title={"切割线开始音效"}
details={"右键按下时刚开始创建切割线准备切割东西时播放的音效文件。"}
type="text"
/>
<SettingField
icon={<Folder />}
settingKey="connectLineStartSoundFile"
title={"连接线开始音效"}
details={"右键按下时刚开始创建连接时播放的音效文件。"}
type="text"
/>
<SettingField
icon={<Folder />}
settingKey="connectFindTargetSoundFile"
title={"连接线查找目标音效"}
details={"当"}
type="text"
/>
<SettingField
icon={<Folder />}
settingKey="cuttingLineReleaseSoundFile"
title={"切断线释放特效"}
details={"纯粹解压用的"}
type="text"
/>
</>
);
}
2 changes: 2 additions & 0 deletions src/pages/test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { LastLaunch } from "../core/LastLaunch";
import { StageDumper } from "../core/stage/StageDumper";
import { usePopupDialog } from "../utils/popupDialog";
import { XML } from "../utils/xml";
import { SoundService } from "../core/SoundService";

export default function TestPage() {
const [switchValue, setSwitchValue] = React.useState(false);
Expand Down Expand Up @@ -50,6 +51,7 @@ export default function TestPage() {
</div>
<Button onClick={handleTestHttp}>test http</Button>
<Button onClick={handleTestImageBase64}>getImageBase64</Button>
<Button onClick={SoundService.play.cuttingLineStart}>test sound</Button>
last launch: {LastLaunch.version}
</div>
);
Expand Down
1 change: 1 addition & 0 deletions src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type Path =
| `/settings/github`
| `/settings/performance`
| `/settings/physical`
| `/settings/sounds`
| `/settings/visual`
| `/test`
| `/welcome`;
Expand Down