Skip to content

Commit

Permalink
Merge pull request #388 from ryohey/fix-discord-reported
Browse files Browse the repository at this point in the history
Fix problems reported in Discord
  • Loading branch information
ryohey authored Jul 28, 2024
2 parents 712300c + 0fcc345 commit b77361e
Show file tree
Hide file tree
Showing 14 changed files with 443 additions and 407 deletions.
49 changes: 11 additions & 38 deletions app/src/actions/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,42 +82,6 @@ export const transposeSelection =
})
}

export const moveSelection = (rootStore: RootStore) => (point: NotePoint) => {
const {
pianoRollStore: { selectedTrack, selection, quantizer },
} = rootStore

if (selectedTrack === undefined || selection === null) {
return
}

// ノートと選択範囲を移動
// Move notes and selection
const quantized = NotePoint.clamp({
tick: quantizer.round(point.tick),
noteNumber: Math.round(point.noteNumber),
})

const dt = quantized.tick - selection.from.tick
const dn = quantized.noteNumber - selection.from.noteNumber

const to = {
tick: selection.to.tick + dt,
noteNumber: selection.to.noteNumber + dn,
}

const clampedTo = NotePoint.clamp(to)
const limit = {
tick: to.tick - clampedTo.tick,
noteNumber: to.noteNumber - clampedTo.noteNumber,
}

moveSelectionBy(rootStore)({
tick: dt - limit.tick,
noteNumber: dn - limit.noteNumber,
})
}

export const moveSelectionBy =
({
pianoRollStore,
Expand All @@ -133,8 +97,17 @@ export const moveSelectionBy =
}

if (selection !== null) {
const s = Selection.moved(selection, delta.tick, delta.noteNumber)
pianoRollStore.selection = s
const movedSelection = Selection.moved(
selection,
delta.tick,
delta.noteNumber,
)
const clampedSelection = Selection.clamp(movedSelection)
if (!Selection.equals(movedSelection, clampedSelection)) {
// Do not move the selection range or notes when it tries to go out of the screen
return
}
pianoRollStore.selection = clampedSelection
}

selectedTrack.updateEvents(
Expand Down
107 changes: 66 additions & 41 deletions app/src/components/EventEditor/EventController.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import { clamp, flow } from "lodash"
import { bpmToUSecPerBeat, uSecPerBeatToBPM } from "../../helpers/bpm"
import { controllerTypeString } from "../../helpers/noteNumberString"
import { TrackEvent } from "../../track"

export type EventInputProp =
| {
type: "number"
value: number
minValue: number
maxValue: number
}
| {
type: "text"
value: string
}
export interface EventInputProp {
type: "text" | "number"
value: string
}

export type EventValueUpdator = {
update: (value: number | string) => any
// null means no update
update: (value: string) => any | null
}

// Abstraction Layer for manipulating TrackEvent on EventList
Expand All @@ -36,50 +32,40 @@ export function getEventController<T extends TrackEvent>(
controllerTypeString(e.controllerType) ?? `CC${e.controllerType}`,
value: {
type: "number",
value: e.value,
minValue: 0,
maxValue: 127,
update: (value) => ({ value }),
value: e.value.toFixed(0),
update: intConverter(0, 127, (value) => ({ value })),
},
}
case "note":
return {
name: e.subtype,
name: "Note",
value: {
type: "number",
value: e.velocity,
minValue: 0,
maxValue: 127,
update: (velocity) => ({ velocity }),
value: e.velocity.toFixed(0),
update: intConverter(0, 127, (velocity) => ({ velocity })),
},
gate: {
type: "number",
value: e.duration,
minValue: 0,
maxValue: Infinity,
update: (duration) => ({ duration }),
value: e.duration.toFixed(0),
update: intConverter(0, Infinity, (duration) => ({ duration })),
},
}
case "programChange":
return {
name: e.subtype,
name: "Program Change",
value: {
type: "number",
value: e.value,
minValue: 0,
maxValue: 127,
update: (value) => ({ value }),
value: e.value.toFixed(0),
update: intConverter(0, 127, (value) => ({ value })),
},
}
case "pitchBend":
return {
name: e.subtype,
name: "Pitch Bend",
value: {
type: "number",
value: e.value,
minValue: 0,
maxValue: 16384,
update: (value) => ({ value }),
value: e.value.toFixed(0),
update: intConverter(0, 16384, (value) => ({ value })),
},
}
default:
Expand All @@ -89,7 +75,7 @@ export function getEventController<T extends TrackEvent>(
switch (e.subtype) {
case "trackName":
return {
name: e.subtype,
name: "Track Name",
value: {
type: "text",
value: e.text,
Expand All @@ -98,13 +84,27 @@ export function getEventController<T extends TrackEvent>(
}
case "midiChannelPrefix":
return {
name: e.subtype,
name: "MIDI Channel Prefix",
value: {
type: "number",
value: e.value.toFixed(0),
update: intConverter(0, 127, (channel) => ({ channel })),
},
}
case "setTempo":
return {
name: "Tempo",
value: {
type: "number",
value: e.value,
minValue: 0,
maxValue: 127,
update: (channel) => ({ channel }),
value: uSecPerBeatToBPM(e.microsecondsPerBeat).toFixed(3),
update: flow(
parseInt,
createClamp(1, 512),
bpmToUSecPerBeat,
Math.floor,
nanToNull,
optional((microsecondsPerBeat) => ({ microsecondsPerBeat })),
),
},
}
default:
Expand All @@ -115,3 +115,28 @@ export function getEventController<T extends TrackEvent>(
return { name: e.type }
}
}

const nanToNull = (value: number) => {
if (Number.isNaN(value)) {
return null
}
return value
}

const createClamp = (min: number, max: number) => (value: number) =>
clamp(value, min, max)

const optional =
<T, S>(fn: (value: T) => S) =>
(value: T | null) => {
if (value === null) {
return null
}
return fn(value)
}

const intConverter = <T>(
minValue: number,
maxValue: number,
fn: (value: number) => T,
) => flow(parseInt, createClamp(minValue, maxValue), nanToNull, optional(fn))
Loading

0 comments on commit b77361e

Please sign in to comment.