-
Notifications
You must be signed in to change notification settings - Fork 305
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
ソング: ループ範囲を設定できるようにする #2281
Open
romot-co
wants to merge
28
commits into
VOICEVOX:main
Choose a base branch
from
romot-co:feature/2224_add_loop
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
ソング: ループ範囲を設定できるようにする #2281
Changes from 5 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
89695e8
ループを試行する
romot-co 728b3b8
ループエリアの基本的な動作
romot-co c954ae0
ルーラーのズーム修正
romot-co f03c07f
ループ範囲が0の場合はループ無効にする
romot-co 1e001bc
ループ範囲を任意のところから開始
romot-co c93b1ba
singing.tsとaudioRendering.tsを変更
sigprogramming 7903a0a
フィールドを移動
sigprogramming b346832
Merge pull request #1 from sigprogramming/loop
romot-co 7193bc7
ループ有効・無効切り替え
romot-co eb14753
ループボタンを調性
romot-co ddeeaf2
ループ無効時も範囲は設定できるようにする
romot-co 3af7ab2
クリックでON/OFFするおよびループ設定後に再生ヘッドを開始位置に移動する
romot-co cdd4bea
ループマスクエリアの表示問題を修正する
romot-co f594378
動作および表示調整
romot-co e9a9800
動作およびCSS調整
romot-co 950d115
ループエリアの見た目調整
romot-co a357cea
ループはデフォルトでオフ
romot-co 5266acb
ループ削除と追加メニュー(仕掛かり)
romot-co aa36df8
Merge remote-tracking branch 'origin/main' into feature/2224_add_loop
romot-co b1bfd40
ループボタンで初期ループを作成
romot-co 88f2762
(スナップショットを更新)
github-actions[bot] 334908e
リファクタリングおよび処理の最適化(RequestAnimationFrameの利用など)
romot-co 35340f4
コンフリクト解消
romot-co 82b2557
Merge branch 'feature/2224_add_loop' of https://github.com/romot-co/v…
romot-co 918eb32
クリック時の初期位置の誤りを修正
romot-co dad6414
サイズ調整
romot-co 3e86177
ループ作成の動作を適切にする
romot-co c0b9e63
サイズ調整
romot-co File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
<template> | ||
<div class="sequencer-loop-control" :class="cursorClass"> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
:width | ||
:height="24" | ||
shape-rendering="geometricPrecision" | ||
@mousedown.stop="addLoop($event)" | ||
> | ||
<!-- ループ範囲 --> | ||
<rect | ||
v-if="isLoopEnabled" | ||
:x="loopStartX - offset" | ||
y="0" | ||
:width="loopEndX - loopStartX" | ||
:height="4" | ||
class="loop-range" | ||
/> | ||
<!-- ループ開始ハンドル --> | ||
<path | ||
v-if="isLoopEnabled" | ||
:d="`M${loopStartX - offset},0 L${loopStartX - offset},15 L${loopStartX - offset + 12},0 Z`" | ||
class="loop-handle loop-start-handle" | ||
:class="{ 'loop-handle-disabled': loopStartTick === loopEndTick }" | ||
/> | ||
<!-- ループ終了ハンドル --> | ||
<path | ||
v-if="isLoopEnabled" | ||
:d="`M${loopEndX - offset},0 L${loopEndX - offset},15 L${loopEndX - offset - 12},0 Z`" | ||
class="loop-handle loop-end-handle" | ||
:class="{ 'loop-handle-disabled': loopStartTick === loopEndTick }" | ||
/> | ||
<!-- ループ開始ドラッグ領域 --> | ||
<rect | ||
v-if="isLoopEnabled" | ||
:x="loopStartX - offset - 4" | ||
y="0" | ||
width="16" | ||
height="24" | ||
class="loop-drag-area" | ||
@mousedown.stop="startDragging('start', $event)" | ||
/> | ||
<!-- ループ終了ドラッグ領域 --> | ||
<rect | ||
v-if="isLoopEnabled" | ||
:x="loopEndX - offset - 4" | ||
y="0" | ||
width="16" | ||
height="24" | ||
class="loop-drag-area" | ||
@mousedown.stop="startDragging('end', $event)" | ||
/> | ||
</svg> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import { ref, computed, onMounted, onUnmounted } from "vue"; | ||
import { useStore } from "@/store"; | ||
import { useLoopControl } from "@/composables/useLoopControl"; | ||
import { useCursorState, CursorState } from "@/composables/useCursorState"; | ||
import { tickToBaseX, baseXToTick } from "@/sing/viewHelper"; | ||
import { getNoteDuration } from "@/sing/domain"; | ||
|
||
const props = defineProps<{ | ||
width: number; | ||
offset: number; | ||
}>(); | ||
|
||
const store = useStore(); | ||
const { isLoopEnabled, loopStartTick, loopEndTick, setLoopRange } = | ||
useLoopControl(); | ||
const { setCursorState, cursorClass } = useCursorState(); | ||
|
||
const tpqn = computed(() => store.state.tpqn); | ||
const sequencerZoomX = computed(() => store.state.sequencerZoomX); | ||
const sequencerSnapType = computed(() => store.state.sequencerSnapType); | ||
|
||
const loopStartX = computed( | ||
() => tickToBaseX(loopStartTick.value, tpqn.value) * sequencerZoomX.value, | ||
); | ||
const loopEndX = computed( | ||
() => tickToBaseX(loopEndTick.value, tpqn.value) * sequencerZoomX.value, | ||
); | ||
|
||
const isDragging = ref(false); | ||
const dragTarget = ref<"start" | "end" | null>(null); | ||
const dragStartX = ref(0); | ||
const dragStartHandleX = ref(0); // ドラッグ開始時のハンドル位置 | ||
|
||
const addLoop = (event: MouseEvent) => { | ||
const target = event.currentTarget as HTMLElement; | ||
const rect = target.getBoundingClientRect(); | ||
const x = event.clientX - rect.left + props.offset; | ||
const tick = snapToGrid(baseXToTick(x / sequencerZoomX.value, tpqn.value)); | ||
setLoopRange(tick, tick); | ||
startDragging("end", event); | ||
}; | ||
|
||
const snapToGrid = (tick: number): number => { | ||
const snapInterval = getNoteDuration(sequencerSnapType.value, tpqn.value); | ||
return Math.round(tick / snapInterval) * snapInterval; | ||
}; | ||
|
||
const startDragging = (target: "start" | "end", event: MouseEvent) => { | ||
event.preventDefault(); | ||
isDragging.value = true; | ||
dragTarget.value = target; | ||
dragStartX.value = event.clientX; | ||
dragStartHandleX.value = | ||
target === "start" ? loopStartX.value : loopEndX.value; | ||
setCursorState(CursorState.EW_RESIZE); | ||
}; | ||
|
||
const onDrag = (event: MouseEvent) => { | ||
if (!isDragging.value || !dragTarget.value) return; | ||
|
||
const dx = event.clientX - dragStartX.value; | ||
const newX = dragStartHandleX.value + dx; | ||
const baseTick = baseXToTick(newX / sequencerZoomX.value, tpqn.value); | ||
|
||
// スナップ処理 | ||
const newTick = Math.max(0, snapToGrid(baseTick)); | ||
|
||
try { | ||
if (dragTarget.value === "start") { | ||
if (newTick <= loopEndTick.value) { | ||
setLoopRange(newTick, loopEndTick.value); | ||
} else { | ||
// 開始ハンドルが終了ハンドルを超えた場合、開始と終了を入れ替える | ||
setLoopRange(loopEndTick.value, newTick); | ||
// ドラッグ対象を終了ハンドルに切り替え | ||
dragTarget.value = "end"; | ||
// ドラッグ開始点を現在のカーソル位置に再設定 | ||
dragStartX.value = event.clientX; | ||
// 新しいドラッグ開始ハンドル位置を再計算 | ||
dragStartHandleX.value = | ||
tickToBaseX(loopEndTick.value, tpqn.value) * sequencerZoomX.value; | ||
} | ||
} else { | ||
if (newTick >= loopStartTick.value) { | ||
setLoopRange(loopStartTick.value, newTick); | ||
} else { | ||
// 終了ハンドルが開始ハンドルを下回った場合、開始と終了を入れ替える | ||
setLoopRange(newTick, loopStartTick.value); | ||
// ドラッグ対象を開始ハンドルに切り替え | ||
dragTarget.value = "start"; | ||
// ドラッグ開始点を現在のカーソル位置に再設定 | ||
dragStartX.value = event.clientX; | ||
// 新しいドラッグ開始ハンドル位置を再計算 | ||
dragStartHandleX.value = | ||
tickToBaseX(loopStartTick.value, tpqn.value) * sequencerZoomX.value; | ||
} | ||
} | ||
} catch (error) { | ||
throw new Error("Could not set loop range"); | ||
} | ||
}; | ||
|
||
const stopDragging = () => { | ||
isDragging.value = false; | ||
dragTarget.value = null; | ||
setCursorState(CursorState.UNSET); | ||
}; | ||
|
||
onMounted(() => { | ||
window.addEventListener("mousemove", onDrag); | ||
window.addEventListener("mouseup", stopDragging); | ||
}); | ||
|
||
onUnmounted(() => { | ||
window.removeEventListener("mousemove", onDrag); | ||
window.removeEventListener("mouseup", stopDragging); | ||
setCursorState(CursorState.UNSET); | ||
}); | ||
</script> | ||
|
||
<style scoped lang="scss"> | ||
.sequencer-loop-control { | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
width: 100%; | ||
height: 24px; | ||
pointer-events: auto; | ||
cursor: pointer; | ||
} | ||
|
||
.loop-range { | ||
fill: var(--scheme-color-primary); | ||
} | ||
|
||
.loop-handle { | ||
fill: var(--scheme-color-primary); | ||
pointer-events: none; | ||
stroke: var(--scheme-color-primary); | ||
stroke-width: 2; | ||
stroke-linejoin: round; | ||
|
||
&.loop-handle-disabled { | ||
fill: var(--scheme-color-secondary); | ||
stroke: var(--scheme-color-secondary); | ||
} | ||
} | ||
|
||
.loop-drag-area { | ||
height: 28px; | ||
fill: transparent; | ||
cursor: ew-resize; | ||
pointer-events: all; | ||
} | ||
</style> |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mouseup
時にルーラーのonClick
も実行されちゃっているかも?