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: add different keyboard sound effects #620

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
Binary file added apps/client/assets/sounds/cherry/1.wav
Binary file not shown.
Binary file added apps/client/assets/sounds/cherry/2.wav
Binary file not shown.
Binary file added apps/client/assets/sounds/cherry/3.wav
Binary file not shown.
Binary file added apps/client/assets/sounds/cherry/4.wav
Binary file not shown.
Binary file added apps/client/assets/sounds/cherry/5.wav
Binary file not shown.
7 changes: 7 additions & 0 deletions apps/client/assets/sounds/cherry/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import cherryFirst from "./1.wav";
import cherrySecond from "./2.wav";
import cherryThird from "./3.wav";
import cherryFourth from "./4.wav";
import cherryFifth from "./5.wav";

export { cherryFirst, cherrySecond, cherryThird, cherryFourth, cherryFifth };
Binary file added apps/client/assets/sounds/durm/1.wav
Binary file not shown.
Binary file added apps/client/assets/sounds/durm/2.wav
Binary file not shown.
Binary file added apps/client/assets/sounds/durm/3.wav
Binary file not shown.
Binary file added apps/client/assets/sounds/durm/4.wav
Binary file not shown.
Binary file added apps/client/assets/sounds/durm/5.wav
Binary file not shown.
Binary file not shown.
43 changes: 43 additions & 0 deletions apps/client/assets/sounds/durm/_readme_and_license.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Sound pack downloaded from Freesound
----------------------------------------

"DW drum kit"

This pack of sounds contains sounds by the following user:
- Logicogonist ( https://freesound.org/people/Logicogonist/ )

You can find this pack online at: https://freesound.org/people/Logicogonist/packs/16053/


Licenses in this pack (see below for individual sound licenses)
---------------------------------------------------------------

Creative Commons 0: http://creativecommons.org/publicdomain/zero/1.0/


Sounds in this pack
-------------------

* 261413__veiler__crash.wav
* url: https://freesound.org/s/261413/
* license: Creative Commons 0
* 261412__veiler__tom-2.wav
* url: https://freesound.org/s/261412/
* license: Creative Commons 0
* 261411__veiler__tom-3.wav
* url: https://freesound.org/s/261411/
* license: Creative Commons 0
* 261410__veiler__tom-4.wav
* url: https://freesound.org/s/261410/
* license: Creative Commons 0
* 261409__veiler__kick-bass-win.wav
* url: https://freesound.org/s/261409/
* license: Creative Commons 0
* 261408__veiler__snare-3.wav
* url: https://freesound.org/s/261408/
* license: Creative Commons 0
* 261407__veiler__tom-1.wav
* url: https://freesound.org/s/261407/
* license: Creative Commons 0


Binary file added apps/client/assets/sounds/durm/backspace.wav
Binary file not shown.
9 changes: 9 additions & 0 deletions apps/client/assets/sounds/durm/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import durmFirst from "./1.wav";
import durmSecond from "./2.wav";
import dburmThird from "./3.wav";
import durmFourth from "./4.wav";
import durmFifth from "./5.wav";
import durmBackspace from "./backspace.wav";
import durmSpace from "./space.wav";

export { durmFirst, durmSecond, dburmThird, durmFourth, durmFifth, durmSpace, durmBackspace };
Binary file added apps/client/assets/sounds/durm/space.wav
Binary file not shown.
5 changes: 5 additions & 0 deletions apps/client/assets/sounds/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as CherrySounds from "./cherry";
import * as DurmSounds from "./durm";
import * as VintageKeyboardSounds from "./vintageKeyboard";

export { CherrySounds, DurmSounds, VintageKeyboardSounds };
37 changes: 37 additions & 0 deletions apps/client/assets/sounds/vintageKeyboard/_readme_and_license.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Sound pack downloaded from Freesound
----------------------------------------

"vintage keyboard"

This pack of sounds contains sounds by the following user:
- jim-ph ( https://freesound.org/people/jim-ph/ )

You can find this pack online at: https://freesound.org/people/jim-ph/packs/12363/


Licenses in this pack (see below for individual sound licenses)
---------------------------------------------------------------

Creative Commons 0: http://creativecommons.org/publicdomain/zero/1.0/


Sounds in this pack
-------------------

* 194799__jim-ph__keyboard5.wav
* url: https://freesound.org/s/194799/
* license: Creative Commons 0
* 194798__jim-ph__vintage-keyboard-4.wav
* url: https://freesound.org/s/194798/
* license: Creative Commons 0
* 194797__jim-ph__vintage-keyboard-3.wav
* url: https://freesound.org/s/194797/
* license: Creative Commons 0
* 194796__jim-ph__vintage-keyboard-2.wav
* url: https://freesound.org/s/194796/
* license: Creative Commons 0
* 194795__jim-ph__vintage-keyboard-1.wav
* url: https://freesound.org/s/194795/
* license: Creative Commons 0


13 changes: 13 additions & 0 deletions apps/client/assets/sounds/vintageKeyboard/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import vintageKeyboardFirst from "./keyboard1.wav";
import vintageKeyboardSecond from "./keyboard2.wav";
import vintageKeyboardThird from "./keyboard3.wav";
import vintageKeyboardFourth from "./keyboard4.wav";
import vintageKeyboardFifth from "./keyboard5.wav";

export {
vintageKeyboardFirst,
vintageKeyboardSecond,
vintageKeyboardThird,
vintageKeyboardFourth,
vintageKeyboardFifth,
};
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
8 changes: 4 additions & 4 deletions apps/client/components/main/QuestionInput/QuestionInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ const { showAnswer } = useGameMode();
const { showSummary } = useSummary();
const { isShowWordsWidth } = useShowWordsWidth();
const { isUseSpaceSubmitAnswer } = useSpaceSubmitAnswer();
const { isKeyboardSoundEnabled } = useKeyboardSound();
const { checkPlayTypingSound, playTypingSound } = useTypingSound();
const { keyboardSound } = useKeyboardSound();
const { checkPlayTypingSound, playTypingSound } = useTypingSound(keyboardSound.value);
const { playRightSound, playErrorSound } = usePlayTipSound();
const { handleAnswerError, resetCloseTip } = answerError();
const { isAutoNextQuestion } = useAutoNextQuestion();
Expand Down Expand Up @@ -127,8 +127,8 @@ function getWordsClassNames(index: number) {
}

function inputChangedCallback(e: KeyboardEvent) {
if (isKeyboardSoundEnabled() && checkPlayTypingSound(e)) {
playTypingSound();
if (keyboardSound.value !== "off" && checkPlayTypingSound(e)) {
playTypingSound(e);
}
}

Expand Down
75 changes: 42 additions & 33 deletions apps/client/components/main/QuestionInput/useTypingSound.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
// useTypingSound.ts
import { ref } from "vue";

import { CherrySounds, DurmSounds, VintageKeyboardSounds } from "~/assets/sounds";
import errorSoundPath from "~/assets/sounds/error.mp3";
import rightSoundPath from "~/assets/sounds/right.mp3";
import typingSoundPath from "~/assets/sounds/typing.mp3";

const drumSounds = Object.entries(DurmSounds)
.filter(([key, value]) => key !== "durmSpace" && key !== "durmBackspace") // 过滤掉不需要的键
.map(([key, value]) => value);
const cherrySounds = Object.values(CherrySounds);
const vintageKeyboardSounds = Object.values(VintageKeyboardSounds);

function randomItem(arr: string | string[]) {
return arr[Math.floor(Math.random() * arr.length)];
}

export function usePlayTipSound() {
// 正确提示音
const rightAudio = new Audio(rightSoundPath);
Expand All @@ -26,50 +37,48 @@ export function usePlayTipSound() {
}

const PLAY_INTERVAL_TIME = 60;
export function useTypingSound() {
let audioCtxRef: AudioContext | null = null;
let audioBuffer: AudioBuffer | null = null;
const lastPlayTime = ref(0); // 与上一次播放时间间隔

// 不需要等页面渲染就可以加载了(提前)
loadAudioContext();
export function useTypingSound(soundsType: string) {
const audioCtxRef = ref<AudioContext | null>(null); // 将 AudioContext 初始化为空
const lastPlayTime = ref(0);

async function loadAudioContext() {
audioCtxRef = new AudioContext();
await loadAudioBuffer(typingSoundPath);
}
async function loadAndPlayAudio(url: string) {
if (!audioCtxRef.value) {
audioCtxRef.value = new AudioContext(); // 在首次使用时创建 AudioContext
if (audioCtxRef.value.state === "suspended") {
await audioCtxRef.value.resume(); // 确保 AudioContext 是激活状态
}
}

async function loadAudioBuffer(url: string) {
const response = await fetch(url);
const arrayBuffer = await response.arrayBuffer();
// 使用 decodeAudioData 方法处理 arrayBuffer
const audioContext = audioCtxRef;
if (!audioContext) return;

const decodedAudioData = await audioContext.decodeAudioData(arrayBuffer);
if (decodedAudioData) {
audioBuffer = decodedAudioData;
} else {
throw new Error("Audio decoding failed.");
}
}

function playTypingSound() {
const now = Date.now();
if (now - lastPlayTime.value < PLAY_INTERVAL_TIME) return;
if (!audioCtxRef || !audioBuffer) return;
const decodedAudioData = await audioCtxRef.value.decodeAudioData(arrayBuffer);

const source = audioCtxRef.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioCtxRef.destination);
const source = audioCtxRef.value.createBufferSource();
source.buffer = decodedAudioData;
source.connect(audioCtxRef.value.destination);
source.start();
lastPlayTime.value = now;
// 当音频播放结束,手动释放资源

// 音频播放结束后,手动释放资源
source.onended = () => {
source.disconnect();
};
}

function playTypingSound(e: KeyboardEvent) {
if (soundsType === "off" || Date.now() - lastPlayTime.value < PLAY_INTERVAL_TIME) return;

let soundPath;
const isSpecialKey = e.code === "Space" || e.code === "Backspace";
const soundTypeToSounds = {
drumSound: isSpecialKey ? DurmSounds[`durm${e.code}`] : randomItem(drumSounds),
cherrySound: randomItem(cherrySounds),
vintageKeyboardSound: randomItem(vintageKeyboardSounds),
};

soundPath = soundTypeToSounds[soundsType as keyof typeof soundTypeToSounds] || typingSoundPath;
loadAndPlayAudio(soundPath);
lastPlayTime.value = Date.now();
}
function checkPlayTypingSound(e: KeyboardEvent) {
if (e.altKey || e.ctrlKey || e.metaKey) return false;

Expand Down
49 changes: 42 additions & 7 deletions apps/client/components/user/Setting.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,23 @@
<table class="table">
<tbody>
<tr class="hover">
<td class="label-text">开启键盘打字音效</td>
<td class="label-text">选择键盘打字音效</td>
<td class="w-[300px] text-center">
<input
type="checkbox"
class="toggle toggle-secondary"
:checked="keyboardSound"
@change="toggleKeyboardSound"
/>
<div class="join mr-12">
<select
v-model="keyboardSound"
@change="toggleKeyboardSound"
class="btn btn-outline btn-secondary select-sm"
>
<option
v-for="(sound, index) in soundsType"
:key="index"
:value="sound.value"
>
{{ sound.label }}
</option>
</select>
</div>
</td>
</tr>
<tr class="hover">
Expand Down Expand Up @@ -247,6 +256,29 @@ const {

const { getGameModeOptions, currentGameMode, toggleGameMode } = useGameMode();

const soundsType = [
{
label: "关闭",
value: "off",
},
{
label: "默认音效",
value: "defaultSound",
},
{
label: "架子鼓",
value: "drumSound",
},
{
label: "樱桃轴",
value: "cherrySound",
},
{
label: "老式键盘",
value: "vintageKeyboardSound",
},
];

const shortcutKeyBindList = [
{
label: "播放发音",
Expand Down Expand Up @@ -284,4 +316,7 @@ onUnmounted(() => {
.btn-outline.btn-secondary {
@apply text-fuchsia-500 outline-fuchsia-500;
}
.select-sm:focus {
outline: none;
}
</style>
2 changes: 1 addition & 1 deletion apps/client/composables/user/sound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function useKeyboardSound() {
isTrue: isKeyboardSoundEnabled,
toggle: toggleKeyboardSound,
remove: removeKeyboardSound,
} = useLocalStorageBoolean(KEYBOARD_SOUND_KEY, true); // 默认开启
} = useLocalStorageBoolean(KEYBOARD_SOUND_KEY, "defaultSound"); // 默认开启

return {
keyboardSound,
Expand Down
28 changes: 21 additions & 7 deletions apps/client/utils/localStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,48 @@ import { ref } from "vue";
export function useLocalStorageBoolean(
key: string,
// 默认开启
defaultValue: boolean = true,
defaultValue: any,
) {
const valueRef = ref(defaultValue);

function loadCache() {
const storedValue = localStorage.getItem(key);
// 如果 localStorage 中有值才进行校验,则使用该值
// 如果 localStorage 中有值,则使用该值
if (storedValue !== null) {
valueRef.value = storedValue === "true";
// 检测值是否为 'true' 或 'false'
// 若是,则转为 boolean 类型,否则按字符串处理
if (storedValue === "true" || storedValue === "false") {
valueRef.value = storedValue === "true";
} else {
valueRef.value = storedValue;
}
}
update(valueRef.value);
}

function update(value: boolean) {
function update(value: string | boolean) {
valueRef.value = value;
localStorage.setItem(key, String(value));
if (typeof value === "boolean") {
localStorage.setItem(key, String(value));
} else {
localStorage.setItem(key, value);
}
}

function remove() {
localStorage.removeItem(key);
}

function toggle() {
update(!valueRef.value);
if (typeof valueRef.value === "boolean") {
update(!valueRef.value);
} else {
update(valueRef.value);
}
}

function isTrue(): boolean {
return valueRef.value;
return valueRef.value === true || valueRef.value === "true";
}

loadCache();
Expand Down