Skip to content

Commit

Permalink
[project-s] スナップを設定する機能を追加 (VOICEVOX#1609)
Browse files Browse the repository at this point in the history
  • Loading branch information
sigprogramming authored Oct 15, 2023
1 parent 1aa007d commit d7a28d3
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 6 deletions.
5 changes: 3 additions & 2 deletions src/components/Sing/ScoreSequencer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,9 @@ export default defineComponent({
if (noteNumber < 0) {
return;
}
// NOTE: ノートの追加は1/8をベース
const duration = getNoteDuration(8, tpqn.value);
// NOTE: ノートの長さはスナップをベース(最小の長さは1/8)
const noteType = Math.min(8, state.sequencerSnapType);
const duration = getNoteDuration(noteType, tpqn.value);
const lyric = getDoremiFromNoteNumber(noteNumber);
// NOTE: 仮ID
const id = uuidv4();
Expand Down
51 changes: 47 additions & 4 deletions src/components/Sing/ToolBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,26 @@
</div>
<div class="sing-setting">
<q-slider v-model.number="volume" class="sing-volume" />
<select class="sing-snap">
<option>1/16</option>
</select>
<q-select
v-model="snapTypeSelectModel"
:options="snapTypeSelectOptions"
color="primary"
text-color="display-on-primary"
outlined
dense
options-dense
transition-show="none"
transition-hide="none"
class="sing-snap"
/>
</div>
</div>
</template>

<script lang="ts">
import { defineComponent, computed, watch, ref } from "vue";
import { useStore } from "@/store";
import { getSnapTypes, isTriplet } from "@/helpers/singHelper";
export default defineComponent({
name: "SingToolBar",
Expand Down Expand Up @@ -238,6 +248,32 @@ export default defineComponent({
},
});
const snapTypeSelectOptions = computed(() => {
const tpqn = store.state.score?.resolution ?? 480;
return getSnapTypes(tpqn).map((snapType) => {
if (isTriplet(snapType)) {
return { snapType, label: `1/${(snapType / 3) * 2}(三連符)` };
} else {
return { snapType, label: `1/${snapType}` };
}
});
});
const snapTypeSelectModel = computed({
get() {
const snapType = store.state.sequencerSnapType;
const selectOptions = snapTypeSelectOptions.value;
return (
selectOptions.find((value) => value.snapType === snapType) ??
selectOptions[selectOptions.length - 1]
);
},
set(value) {
store.dispatch("SET_SNAP_TYPE", {
snapType: value.snapType,
});
},
});
return {
isShowSinger,
toggleShowSinger,
Expand All @@ -256,6 +292,8 @@ export default defineComponent({
stop,
seek,
volume,
snapTypeSelectOptions,
snapTypeSelectModel,
};
},
});
Expand Down Expand Up @@ -336,7 +374,12 @@ export default defineComponent({
}
.sing-volume {
margin-right: 10px;
margin-right: 16px;
width: 72px;
}
.sing-snap {
margin-right: 2px;
min-width: 160px;
}
</style>
32 changes: 32 additions & 0 deletions src/helpers/singHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export function getMeasureDuration(timeSignature: TimeSignature, tpqn: number) {
return tpqn * quarterNotesPerMeasure;
}

// TODO: getNumOfMeasuresに変更する
export function getMeasureNum(notes: Note[], measureDuration: number) {
if (notes.length === 0) {
return 0;
Expand All @@ -29,6 +30,29 @@ export function getNoteDuration(noteType: number, tpqn: number) {
return (tpqn * 4) / noteType;
}

export function getNoteTypes(tpqn: number) {
const maxNoteType = 128;
const wholeNoteDuration = tpqn * 4;
const noteTypes = [1];
for (let noteType = 2; noteType <= maxNoteType; noteType *= 2) {
if (wholeNoteDuration % noteType !== 0) {
break;
}
noteTypes.push(noteType);
}
for (let noteType = 3; noteType <= maxNoteType; noteType *= 2) {
if (wholeNoteDuration % noteType !== 0) {
break;
}
noteTypes.push(noteType);
}
return noteTypes;
}

export function isTriplet(noteType: number) {
return noteType % 3 === 0;
}

export function getKeyBaseHeight() {
return BASE_Y_PER_NOTE_NUMBER;
}
Expand All @@ -54,6 +78,14 @@ export function baseYToNoteNumber(baseY: number, integer = true) {
: 127.5 - baseY / BASE_Y_PER_NOTE_NUMBER;
}

export function getSnapTypes(tpqn: number) {
return getNoteTypes(tpqn).filter((value) => value <= 64);
}

export function isValidSnapType(snapType: number, tpqn: number) {
return getSnapTypes(tpqn).some((value) => value === snapType);
}

export function getPitchFromNoteNumber(noteNumber: number) {
const mapPitches = [
"C",
Expand Down
14 changes: 14 additions & 0 deletions src/store/singing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
import { EngineId, StyleId } from "@/type/preload";
import {
getDoremiFromNoteNumber,
isValidSnapType,
noteNumberToFrequency,
round,
} from "@/helpers/singHelper";
Expand Down Expand Up @@ -673,6 +674,19 @@ export const singingStore = createPartialStore<SingingStoreTypes>({
},
},

SET_SNAP_TYPE: {
mutation(state, { snapType }) {
state.sequencerSnapType = snapType;
},
async action({ state, commit }, { snapType }) {
const tpqn = state.score?.resolution ?? 480;
if (!isValidSnapType(snapType, tpqn)) {
throw new Error("The snap type is invalid.");
}
commit("SET_SNAP_TYPE", { snapType });
},
},

SET_ZOOM_X: {
mutation(state, { zoomX }: { zoomX: number }) {
state.sequencerZoomX = zoomX;
Expand Down
5 changes: 5 additions & 0 deletions src/store/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,11 @@ export type SingingStoreTypes = {
action(): void;
};

SET_SNAP_TYPE: {
mutation: { snapType: number };
action(payload: { snapType: number }): void;
};

SET_ZOOM_X: {
mutation: { zoomX: number };
action(payload: { zoomX: number }): void;
Expand Down

0 comments on commit d7a28d3

Please sign in to comment.