tweak
This commit is contained in:
parent
e1bea4e125
commit
d125cc3aac
|
@ -1194,6 +1194,7 @@ export interface Locale {
|
|||
"enableQuickAddMfmFunction": string;
|
||||
"bubbleGame": string;
|
||||
"sfx": string;
|
||||
"soundWillBePlayed": string;
|
||||
"_announcement": {
|
||||
"forExistingUsers": string;
|
||||
"forExistingUsersDescription": string;
|
||||
|
|
|
@ -1191,6 +1191,7 @@ addMfmFunction: "装飾を追加"
|
|||
enableQuickAddMfmFunction: "高度なMFMのピッカーを表示する"
|
||||
bubbleGame: "バブルゲーム"
|
||||
sfx: "効果音"
|
||||
soundWillBePlayed: "サウンドが再生されます"
|
||||
|
||||
_announcement:
|
||||
forExistingUsers: "既存ユーザーのみ"
|
||||
|
|
|
@ -271,7 +271,7 @@ export async function mainBoot() {
|
|||
|
||||
main.on('unreadAntenna', () => {
|
||||
updateAccount({ hasUnreadAntenna: true });
|
||||
sound.play('antenna');
|
||||
sound.playMisskeySfx('antenna');
|
||||
});
|
||||
|
||||
main.on('readAllAnnouncements', () => {
|
||||
|
|
|
@ -345,7 +345,7 @@ function react(viaKeyboard = false): void {
|
|||
pleaseLogin();
|
||||
showMovedDialog();
|
||||
if (appearNote.value.reactionAcceptance === 'likeOnly') {
|
||||
sound.play('reaction');
|
||||
sound.playMisskeySfx('reaction');
|
||||
|
||||
if (props.mock) {
|
||||
return;
|
||||
|
@ -365,7 +365,7 @@ function react(viaKeyboard = false): void {
|
|||
} else {
|
||||
blur();
|
||||
reactionPicker.show(reactButton.value, reaction => {
|
||||
sound.play('reaction');
|
||||
sound.playMisskeySfx('reaction');
|
||||
|
||||
if (props.mock) {
|
||||
emit('reaction', reaction);
|
||||
|
|
|
@ -370,7 +370,7 @@ function react(viaKeyboard = false): void {
|
|||
pleaseLogin();
|
||||
showMovedDialog();
|
||||
if (appearNote.value.reactionAcceptance === 'likeOnly') {
|
||||
sound.play('reaction');
|
||||
sound.playMisskeySfx('reaction');
|
||||
|
||||
misskeyApi('notes/reactions/create', {
|
||||
noteId: appearNote.value.id,
|
||||
|
@ -386,7 +386,7 @@ function react(viaKeyboard = false): void {
|
|||
} else {
|
||||
blur();
|
||||
reactionPicker.show(reactButton.value, reaction => {
|
||||
sound.play('reaction');
|
||||
sound.playMisskeySfx('reaction');
|
||||
|
||||
misskeyApi('notes/reactions/create', {
|
||||
noteId: appearNote.value.id,
|
||||
|
|
|
@ -62,7 +62,7 @@ async function toggleReaction() {
|
|||
if (confirm.canceled) return;
|
||||
|
||||
if (oldReaction !== props.reaction) {
|
||||
sound.play('reaction');
|
||||
sound.playMisskeySfx('reaction');
|
||||
}
|
||||
|
||||
if (mock) {
|
||||
|
@ -81,7 +81,7 @@ async function toggleReaction() {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
sound.play('reaction');
|
||||
sound.playMisskeySfx('reaction');
|
||||
|
||||
if (mock) {
|
||||
emit('reactionToggled', props.reaction, (props.count + 1));
|
||||
|
|
|
@ -81,7 +81,7 @@ function prepend(note) {
|
|||
emit('note');
|
||||
|
||||
if (props.sound) {
|
||||
sound.play($i && (note.userId === $i.id) ? 'noteMy' : 'note');
|
||||
sound.playMisskeySfx($i && (note.userId === $i.id) ? 'noteMy' : 'note');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ function onClick(ev: MouseEvent) {
|
|||
icon: 'ti ti-plus',
|
||||
action: () => {
|
||||
react(`:${props.name}:`);
|
||||
sound.play('reaction');
|
||||
sound.playMisskeySfx('reaction');
|
||||
},
|
||||
}] : [])], ev.currentTarget ?? ev.target);
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ function onClick(ev: MouseEvent) {
|
|||
icon: 'ti ti-plus',
|
||||
action: () => {
|
||||
react(props.emoji);
|
||||
sound.play('reaction');
|
||||
sound.playMisskeySfx('reaction');
|
||||
},
|
||||
}] : [])], ev.currentTarget ?? ev.target);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkButton primary gradate large rounded inline @click="start">{{ i18n.ts.start }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.frameInner">
|
||||
<div class="_gaps" style="padding: 16px;">
|
||||
<div style="font-size: 90%;"><i class="ti ti-music"></i> {{ i18n.ts.soundWillBePlayed }}</div>
|
||||
<MkSwitch v-model="mute">
|
||||
<template #label>{{ i18n.ts.mute }}</template>
|
||||
</MkSwitch>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -159,6 +167,7 @@ import { $i } from '@/account.js';
|
|||
import { DropAndFusionGame, Mono } from '@/scripts/drop-and-fusion-engine.js';
|
||||
import * as sound from '@/scripts/sound.js';
|
||||
import MkRange from '@/components/MkRange.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
|
||||
const NORMAL_BASE_SIZE = 30;
|
||||
const NORAML_MONOS: Mono[] = [{
|
||||
|
@ -409,6 +418,7 @@ const gameOver = ref(false);
|
|||
const gameStarted = ref(false);
|
||||
const highScore = ref<number | null>(null);
|
||||
const showConfig = ref(false);
|
||||
const mute = ref(false);
|
||||
const bgmVolume = ref(defaultStore.state.dropAndFusion.bgmVolume);
|
||||
const sfxVolume = ref(defaultStore.state.dropAndFusion.sfxVolume);
|
||||
|
||||
|
@ -545,7 +555,7 @@ async function start() {
|
|||
width: GAME_WIDTH,
|
||||
height: GAME_HEIGHT,
|
||||
canvas: canvasEl.value!,
|
||||
sfxVolume: sfxVolume.value,
|
||||
sfxVolume: mute.value ? 0 : sfxVolume.value,
|
||||
...(
|
||||
gameMode.value === 'normal' ? {
|
||||
monoDefinitions: NORAML_MONOS,
|
||||
|
@ -565,7 +575,9 @@ async function start() {
|
|||
}
|
||||
const bgmBuffer = await sound.loadAudio('/client-assets/drop-and-fusion/bgm_1.mp3');
|
||||
if (!bgmBuffer) return;
|
||||
bgmNodes = sound.createSourceNode(bgmBuffer, bgmVolume.value);
|
||||
bgmNodes = sound.createSourceNode(bgmBuffer, {
|
||||
volume: mute.value ? 0 : bgmVolume.value,
|
||||
});
|
||||
if (!bgmNodes) return;
|
||||
bgmNodes.soundSource.loop = true;
|
||||
bgmNodes.soundSource.start();
|
||||
|
@ -574,14 +586,14 @@ async function start() {
|
|||
|
||||
watch(bgmVolume, (newValue, oldValue) => {
|
||||
if (bgmNodes) {
|
||||
bgmNodes.gainNode.gain.value = newValue;
|
||||
bgmNodes.gainNode.gain.value = mute.value ? 0 : newValue;
|
||||
}
|
||||
});
|
||||
|
||||
watch(sfxVolume, (newValue, oldValue) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (game) {
|
||||
game.setSfxVolume(newValue);
|
||||
game.setSfxVolume(mute.value ? 0 : newValue);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ import MkRange from '@/components/MkRange.vue';
|
|||
import { i18n } from '@/i18n.js';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { playFile, soundsTypes, getSoundDuration } from '@/scripts/sound.js';
|
||||
import { playMisskeySfxFile, soundsTypes, getSoundDuration } from '@/scripts/sound.js';
|
||||
import { selectFile } from '@/scripts/select-file.js';
|
||||
|
||||
const props = defineProps<{
|
||||
|
@ -119,7 +119,7 @@ function listen() {
|
|||
return;
|
||||
}
|
||||
|
||||
playFile(type.value === '_driveFile_' ? {
|
||||
playMisskeySfxFile(type.value === '_driveFile_' ? {
|
||||
type: '_driveFile_',
|
||||
fileId: fileId.value as string,
|
||||
fileUrl: fileUrl.value as string,
|
||||
|
|
|
@ -233,7 +233,11 @@ export class DropAndFusionGame extends EventEmitter<{
|
|||
|
||||
// TODO: 効果音再生はコンポーネント側の責務なので移動する
|
||||
const pan = ((newX / this.gameWidth) - 0.5) * 2;
|
||||
sound.playUrl('/client-assets/drop-and-fusion/bubble2.mp3', this.sfxVolume, pan, nextMono.sfxPitch);
|
||||
sound.playUrl('/client-assets/drop-and-fusion/bubble2.mp3', {
|
||||
volume: this.sfxVolume,
|
||||
pan,
|
||||
playbackRate: nextMono.sfxPitch,
|
||||
});
|
||||
|
||||
this.emit('monoAdded', nextMono);
|
||||
this.emit('fusioned', newX, newY, additionalScore);
|
||||
|
@ -335,7 +339,11 @@ export class DropAndFusionGame extends EventEmitter<{
|
|||
const vol = ((Math.min(maxCollisionEnergyForSound, energy - minCollisionEnergyForSound) / maxCollisionEnergyForSound) / 4) * this.sfxVolume;
|
||||
const pan = ((((bodyA.position.x + bodyB.position.x) / 2) / this.gameWidth) - 0.5) * 2;
|
||||
const pitch = soundPitchMin + ((soundPitchMax - soundPitchMin) * (1 - (Math.min(10, energy) / 10)));
|
||||
sound.playUrl('/client-assets/drop-and-fusion/poi1.mp3', vol, pan, pitch);
|
||||
sound.playUrl('/client-assets/drop-and-fusion/poi1.mp3', {
|
||||
volume: vol,
|
||||
pan,
|
||||
playbackRate: pitch,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -400,7 +408,10 @@ export class DropAndFusionGame extends EventEmitter<{
|
|||
|
||||
// TODO: 効果音再生はコンポーネント側の責務なので移動する
|
||||
const pan = ((x / this.gameWidth) - 0.5) * 2;
|
||||
sound.playUrl('/client-assets/drop-and-fusion/poi2.mp3', this.sfxVolume, pan);
|
||||
sound.playUrl('/client-assets/drop-and-fusion/poi2.mp3', {
|
||||
volume: this.sfxVolume,
|
||||
pan,
|
||||
});
|
||||
}
|
||||
|
||||
public hold() {
|
||||
|
|
|
@ -126,13 +126,13 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; })
|
|||
* 既定のスプライトを再生する
|
||||
* @param type スプライトの種類を指定
|
||||
*/
|
||||
export function play(operationType: OperationType) {
|
||||
export function playMisskeySfx(operationType: OperationType) {
|
||||
const sound = defaultStore.state[`sound_${operationType}`];
|
||||
if (_DEV_) console.log('play', operationType, sound);
|
||||
if (sound.type == null || !canPlay) return;
|
||||
|
||||
canPlay = false;
|
||||
playFile(sound).finally(() => {
|
||||
playMisskeySfxFile(sound).finally(() => {
|
||||
// ごく短時間に音が重複しないように
|
||||
setTimeout(() => {
|
||||
canPlay = true;
|
||||
|
@ -144,7 +144,7 @@ export function play(operationType: OperationType) {
|
|||
* サウンド設定形式で指定された音声を再生する
|
||||
* @param soundStore サウンド設定
|
||||
*/
|
||||
export async function playFile(soundStore: SoundStore) {
|
||||
export async function playMisskeySfxFile(soundStore: SoundStore) {
|
||||
if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) {
|
||||
return;
|
||||
}
|
||||
|
@ -155,35 +155,42 @@ export async function playFile(soundStore: SoundStore) {
|
|||
const url = soundStore.type === '_driveFile_' ? soundStore.fileUrl : `/client-assets/sounds/${soundStore.type}.mp3`;
|
||||
const buffer = await loadAudio(url);
|
||||
if (!buffer) return;
|
||||
createSourceNode(buffer, soundStore.volume).soundSource.start();
|
||||
const volume = soundStore.volume * masterVolume;
|
||||
createSourceNode(buffer, { volume }).soundSource.start();
|
||||
}
|
||||
|
||||
export async function playUrl(url: string, volume = 1, pan = 0, playbackRate = 1) {
|
||||
const masterVolume = defaultStore.state.sound_masterVolume;
|
||||
if (isMute() || masterVolume === 0 || volume === 0) {
|
||||
export async function playUrl(url: string, opts: {
|
||||
volume?: number;
|
||||
pan?: number;
|
||||
playbackRate?: number;
|
||||
}) {
|
||||
if (opts.volume === 0) {
|
||||
return;
|
||||
}
|
||||
const buffer = await loadAudio(url);
|
||||
if (!buffer) return;
|
||||
createSourceNode(buffer, volume, pan, playbackRate).soundSource.start();
|
||||
createSourceNode(buffer, opts).soundSource.start();
|
||||
}
|
||||
|
||||
export function createSourceNode(buffer: AudioBuffer, volume: number, pan = 0, playbackRate = 1): {
|
||||
export function createSourceNode(buffer: AudioBuffer, opts: {
|
||||
volume?: number;
|
||||
pan?: number;
|
||||
playbackRate?: number;
|
||||
}): {
|
||||
soundSource: AudioBufferSourceNode;
|
||||
panNode: StereoPannerNode;
|
||||
gainNode: GainNode;
|
||||
} {
|
||||
const masterVolume = defaultStore.state.sound_masterVolume;
|
||||
|
||||
const panNode = ctx.createStereoPanner();
|
||||
panNode.pan.value = pan;
|
||||
panNode.pan.value = opts.pan ?? 0;
|
||||
|
||||
const gainNode = ctx.createGain();
|
||||
gainNode.gain.value = masterVolume * volume;
|
||||
|
||||
gainNode.gain.value = opts.volume ?? 1;
|
||||
|
||||
const soundSource = ctx.createBufferSource();
|
||||
soundSource.buffer = buffer;
|
||||
soundSource.playbackRate.value = playbackRate;
|
||||
soundSource.playbackRate.value = opts.playbackRate ?? 1;
|
||||
soundSource
|
||||
.connect(panNode)
|
||||
.connect(gainNode)
|
||||
|
|
|
@ -83,7 +83,7 @@ function onNotification(notification: Misskey.entities.Notification, isClient =
|
|||
}, 6000);
|
||||
}
|
||||
|
||||
sound.play('notification');
|
||||
sound.playMisskeySfx('notification');
|
||||
}
|
||||
|
||||
if ($i) {
|
||||
|
|
|
@ -123,7 +123,7 @@ const onStats = (stats) => {
|
|||
current[domain].delayed = stats[domain].delayed;
|
||||
|
||||
if (current[domain].waiting > 0 && widgetProps.sound && jammedAudioBuffer.value && !jammedSoundNodePlaying.value) {
|
||||
const soundNode = sound.createSourceNode(jammedAudioBuffer.value, 1)?.soundSource;
|
||||
const soundNode = sound.createSourceNode(jammedAudioBuffer.value, {}).soundSource;
|
||||
if (soundNode) {
|
||||
jammedSoundNodePlaying.value = true;
|
||||
soundNode.onended = () => jammedSoundNodePlaying.value = false;
|
||||
|
|
Loading…
Reference in New Issue