From abdd921dc3e596ca69c70c89411496ca4b6fd66a Mon Sep 17 00:00:00 2001 From: sevenc-nanashi Date: Wed, 24 Jul 2024 06:21:27 +0900 Subject: [PATCH 01/34] =?UTF-8?q?Change:=20shouldPlayTracks=E3=81=8CSet?= =?UTF-8?q?=E3=82=92=E8=BF=94=E3=81=99=E3=82=88=E3=81=86=E3=81=AB=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Sing/SideBar/TrackItem.vue | 3 +- src/sing/domain.ts | 16 +++--- src/store/singing.ts | 4 +- .../unit/domain/sing/shouldPlayTracks.spec.ts | 54 +++++++++++++++++++ 4 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 tests/unit/domain/sing/shouldPlayTracks.spec.ts diff --git a/src/components/Sing/SideBar/TrackItem.vue b/src/components/Sing/SideBar/TrackItem.vue index ad8600f723..a893683268 100644 --- a/src/components/Sing/SideBar/TrackItem.vue +++ b/src/components/Sing/SideBar/TrackItem.vue @@ -158,7 +158,6 @@ import { useStore } from "@/store"; import ContextMenu from "@/components/Menu/ContextMenu.vue"; import { shouldPlayTracks } from "@/sing/domain"; import { CharacterInfo, StyleInfo, TrackId } from "@/type/preload"; -import { getOrThrow } from "@/helpers/mapHelper"; const props = defineProps<{ trackId: TrackId; @@ -179,7 +178,7 @@ const isThereSoloTrack = computed(() => [...tracks.value.values()].some((track) => track.solo), ); const shouldPlayTrack = computed(() => - getOrThrow(shouldPlayTracks(store.state.tracks), props.trackId), + shouldPlayTracks(store.state.tracks).has(props.trackId), ); const setTrackPan = (pan: number) => { diff --git a/src/sing/domain.ts b/src/sing/domain.ts index f7d6b44e6e..40b16b965b 100644 --- a/src/sing/domain.ts +++ b/src/sing/domain.ts @@ -573,14 +573,18 @@ export const splitLyricsByMoras = ( * ソロのトラックが存在する場合は、ソロのトラックのみ再生する。(ミュートは無視される) * ソロのトラックが存在しない場合は、ミュートされていないトラックを再生する。 */ -export const shouldPlayTracks = ( - tracks: Map, -): Map => { +export const shouldPlayTracks = (tracks: Map): Set => { const soloTrackExists = [...tracks.values()].some((track) => track.solo); - const shouldPlayMap = new Map(); + const shouldPlaySet = new Set(); for (const [trackKey, track] of tracks) { - shouldPlayMap.set(trackKey, soloTrackExists ? track.solo : !track.mute); + if (soloTrackExists) { + if (track.solo) { + shouldPlaySet.add(trackKey); + } + } else if (!track.mute) { + shouldPlaySet.add(trackKey); + } } - return shouldPlayMap; + return shouldPlaySet; }; diff --git a/src/store/singing.ts b/src/store/singing.ts index a90dd38ebd..0c64a41a40 100644 --- a/src/store/singing.ts +++ b/src/store/singing.ts @@ -174,7 +174,7 @@ const offlineRenderTracks = async ( const channelStrip = new ChannelStrip(offlineAudioContext); channelStrip.volume = track.gain; channelStrip.pan = track.pan; - channelStrip.mute = !getOrThrow(shouldPlays, trackId); + channelStrip.mute = !shouldPlays.has(trackId); channelStrip.output.connect(mainChannelStrip.input); trackChannelStrips.set(trackId, channelStrip); @@ -276,7 +276,7 @@ export const singingStorePlugin: WatchStoreStatePlugin = (store) => { const channelStrip = getOrThrow(trackChannelStrips, trackId); channelStrip.volume = track.gain; channelStrip.pan = track.pan; - channelStrip.mute = !getOrThrow(shouldPlays, trackId); + channelStrip.mute = !shouldPlays.has(trackId); } const channelStripTrackIds = [...trackChannelStrips.keys()]; for (const trackId of channelStripTrackIds) { diff --git a/tests/unit/domain/sing/shouldPlayTracks.spec.ts b/tests/unit/domain/sing/shouldPlayTracks.spec.ts new file mode 100644 index 0000000000..4e14794e14 --- /dev/null +++ b/tests/unit/domain/sing/shouldPlayTracks.spec.ts @@ -0,0 +1,54 @@ +import { createDefaultTrack, shouldPlayTracks } from "@/sing/domain"; +import { Track } from "@/store/type"; +import { TrackId } from "@/type/preload"; + +const createTrack = ({ solo, mute }: { solo: boolean; mute: boolean }) => { + const track = createDefaultTrack(); + + if (solo) { + track.solo = true; + } + if (mute) { + track.mute = true; + } + + return track; +}; +const toTracksMap = (tracks: Track[]) => { + return tracks.reduce((acc, track) => { + acc.set(TrackId(crypto.randomUUID()), track); + return acc; + }, new Map()); +}; +const findIndices = (tracks: Map, trackIds: Set) => + [...trackIds].map((trackId) => { + return Array.from(tracks.keys()).findIndex((id) => id === trackId); + }); + +describe("shouldPlayTracks", () => { + it("ソロのトラックが存在する場合はソロのトラックのみ再生する(ミュートは無視される)", () => { + const tracks = toTracksMap([ + createTrack({ solo: false, mute: false }), + createTrack({ solo: false, mute: false }), + createTrack({ solo: true, mute: false }), + createTrack({ solo: true, mute: false }), + createTrack({ solo: false, mute: true }), + createTrack({ solo: false, mute: true }), + ]); + + const result = shouldPlayTracks(tracks); + expect(findIndices(tracks, result).sort()).toEqual([2, 3]); + }); + + it("ソロのトラックが存在しない場合はミュートされていないトラックを再生する", () => { + const tracks = toTracksMap([ + createTrack({ solo: false, mute: false }), + createTrack({ solo: false, mute: false }), + createTrack({ solo: false, mute: true }), + createTrack({ solo: false, mute: true }), + ]); + + const result = shouldPlayTracks(tracks); + expect(findIndices(tracks, result).sort()).toEqual([0, 1]); + }); +}); From 009b89a4bc4a8b2460daa7435e09b700961047cc Mon Sep 17 00:00:00 2001 From: sevenc-nanashi Date: Wed, 24 Jul 2024 06:48:49 +0900 Subject: [PATCH 02/34] =?UTF-8?q?Change:=20=E5=AE=9F=E9=A8=93=E7=9A=84?= =?UTF-8?q?=E6=A9=9F=E8=83=BD=E3=81=AB=E9=9A=94=E9=9B=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dialog/ImportSongProjectDialog.vue | 28 +++++++++ .../Dialog/SettingDialog/SettingDialog.vue | 18 ++++++ .../Dialog/SettingDialog/ToggleCell.vue | 27 ++++++--- src/components/Sing/SingEditor.vue | 6 +- src/components/Sing/ToolBar/ToolBar.vue | 5 ++ src/store/project.ts | 16 +++++- src/store/setting.ts | 1 + src/store/singing.ts | 57 ++++++++++++------- src/type/preload.ts | 1 + 9 files changed, 129 insertions(+), 30 deletions(-) diff --git a/src/components/Dialog/ImportSongProjectDialog.vue b/src/components/Dialog/ImportSongProjectDialog.vue index a4d2eadd57..b7e8691217 100644 --- a/src/components/Dialog/ImportSongProjectDialog.vue +++ b/src/components/Dialog/ImportSongProjectDialog.vue @@ -40,10 +40,17 @@ > + @@ -110,6 +117,10 @@ const { dialogRef, onDialogOK, onDialogCancel } = useDialogPluginComponent(); const store = useStore(); const log = createLogger("ImportExternalProjectDialog"); +const multiTrackEnabled = computed( + () => store.state.experimentalSetting.enableMultiTrack, +); + // 受け入れる拡張子 const acceptExtensions = computed( () => supportedExtensions.map((ext) => `.${ext}`).join(",") + ",.vvproj", @@ -232,6 +243,23 @@ const trackOptions = computed(() => { }); // 選択中のトラック const selectedTrackIndexes = ref(null); +const selectedTrackIndex = computed({ + get: () => { + if (selectedTrackIndexes.value == null) { + return -1; + } + if (selectedTrackIndexes.value.length === 0) { + return -1; + } + return selectedTrackIndexes.value[0]; + }, + set: (index: number) => { + if (selectedTrackIndexes.value == null) { + return; + } + selectedTrackIndexes.value = [index]; + }, +}); // データ初期化 const initializeValues = () => { diff --git a/src/components/Dialog/SettingDialog/SettingDialog.vue b/src/components/Dialog/SettingDialog/SettingDialog.vue index 4b1677c0a4..3dfd5180a0 100644 --- a/src/components/Dialog/SettingDialog/SettingDialog.vue +++ b/src/components/Dialog/SettingDialog/SettingDialog.vue @@ -230,6 +230,7 @@ @@ -524,6 +525,19 @@ ) " /> + + + 現在のプロジェクトがマルチトラック機能を利用しているため、無効化できません。 + + @@ -896,6 +910,10 @@ const selectedEngineId = computed({ const renderEngineNameLabel = (engineId: EngineId) => { return engineInfos.value[engineId].name; }; + +const canDisableMultiTrack = computed(() => { + return store.state.tracks.size <= 1; +});