Skip to content

Commit

Permalink
Merge pull request #90 from dszakallas/persistent-jumpset
Browse files Browse the repository at this point in the history
feat(LaunchPad): Save state of presets when switching
  • Loading branch information
dszakallas authored Apr 6, 2024
2 parents 84c5d2a + 1d3e9a5 commit ed8b3ff
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 22 deletions.
25 changes: 20 additions & 5 deletions packages/launchpad-common/src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Action } from '@mixxx-launch/mixxx/src/util'
import { ControlContext } from './Control'
import PlaylistSidebar from './PlaylistSidebar'
import { posMod } from '@mixxx-launch/common'
import { Preset, PresetConf, makePresetTemplate } from './Preset'
import { Preset, PresetConf, makePresetTemplate, PresetState } from './Preset'

export type PresetSize = 'short' | 'tall' | 'grande'

Expand Down Expand Up @@ -66,11 +66,21 @@ const onMidi = (layout: App, channel: number, modifier: Modifier) =>

const buttons = ['up', 'down', 'left', 'right', 'session', 'user1', 'user2', 'mixer'] as const

// We need an identifier for each block to save/restore its state
// We want each deck to get a separate identifier. Also, the preset templates should be unique for each deck.
// We can use the PresetSize and index to identify the preset template, and the channel to identify the deck.
// Therefore SavedPresetStateKey = {PresetSize}_{index}_{channel}
type SavedPresetStateKey = `${PresetSize}_${number}_${number}`

const makePresetStateKey = (presetSize: PresetSize, index: number, channel: number): SavedPresetStateKey =>
`${presetSize}_${index}_${channel}`

export default class App extends Component {
conf: LayoutConf
bindings: [MidiComponent, Action<MidiMessage>][]
modifier: ModifierSidebar
presets: { [P in PresetSize]: readonly PresetConf[] }
savedPresetStates: { [key: SavedPresetStateKey]: PresetState }
playlistSidebar: PlaylistSidebar

// state variables
Expand All @@ -96,6 +106,7 @@ export default class App extends Component {
this.chord = []
this.layout = {}
this.mountedPresets = {}
this.savedPresetStates = {}
}

getLayout() {
Expand All @@ -107,14 +118,14 @@ export default class App extends Component {
}

updateLayout(diff: Diff) {
const removedChannels = diff[0].map((block) => block.channel)
removedChannels.forEach((ch) => {
diff[0].forEach((block) => {
const ch = block.channel
delete this.layout[ch]
this.device.clearColor(this.bindings[ch][0].control)
this.mountedPresets[ch].unmount()
this.savedPresetStates[makePresetStateKey(block.size, block.index, ch)] = this.mountedPresets[ch].state
})
const addedBlocks = diff[1]
addedBlocks.forEach((block) => {
diff[1].forEach((block) => {
this.layout[block.channel] = block
if (block.index) {
this.device.sendColor(this.bindings[block.channel][0].control, this.device.colors.hi_orange)
Expand All @@ -134,6 +145,10 @@ export default class App extends Component {
)

const preset = new Preset(ctx, presetTemplate)
const presetState = this.savedPresetStates[makePresetStateKey(block.size, block.index, block.channel)]
if (presetState) {
preset.state = presetState
}
this.mountedPresets[block.channel] = preset
this.mountedPresets[block.channel].mount()
})
Expand Down
18 changes: 17 additions & 1 deletion packages/launchpad-common/src/Preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,14 @@ export const makePresetTemplate = (
}
}

type PresetTemplate = {
export type PresetTemplate = {
controls: ControlTemplate<ControlContext, ControlType>[]
}

export type PresetState = {
controlStates: any[]
}

export class Preset extends Component {
controls: Control<ControlType>[]

Expand All @@ -95,6 +99,18 @@ export class Preset extends Component {
}
}

set state(presetState: PresetState) {
for (const i in this.controls) {
this.controls[i].state = presetState.controlStates[i]
}
}

get state(): PresetState {
return {
controlStates: this.controls.map((c) => c.state),
}
}

onUnmount() {
for (const control of this.controls) {
control.unmount()
Expand Down
17 changes: 9 additions & 8 deletions packages/launchpad-common/src/controls/beatjump.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ export type Type = {
gridPosition: [number, number]
jumps: readonly [number, number][]
vertical?: boolean
bounce?: boolean
}
state: {
mode: boolean
bounce: boolean
pressing: number | null
diff: number
set: number
Expand All @@ -23,7 +24,7 @@ export type Type = {

const colors = ['green', 'red']

const make: MakeDeckControlTemplate<Type> = ({ deck, gridPosition, jumps, vertical = false }) => {
const make: MakeDeckControlTemplate<Type> = ({ deck, gridPosition, jumps, vertical = false, bounce = false }) => {
const bindings: Type['bindings'] = {}
const spec = jumps.flatMap((j) => [
[j, -1],
Expand All @@ -37,7 +38,7 @@ const make: MakeDeckControlTemplate<Type> = ({ deck, gridPosition, jumps, vertic
modes(
mode,
() => {
if (!state.mode) {
if (!state.bounce) {
if (value) {
setValue(deck.beatjump, j[state.set] * d)
}
Expand All @@ -64,16 +65,16 @@ const make: MakeDeckControlTemplate<Type> = ({ deck, gridPosition, jumps, vertic
() => {
if (value) {
state.set = posMod(state.set + 1, 2)
const prefix = state.mode ? 'lo' : 'hi'
const prefix = state.bounce ? 'lo' : 'hi'
for (let b = 0; b < spec.length; ++b) {
device.sendColor(bindings[b].control, device.colors[`${prefix}_${colors[state.set]}`])
}
}
},
() => {
if (value) {
state.mode = !state.mode
const prefix = state.mode ? 'lo' : 'hi'
state.bounce = !state.bounce
const prefix = state.bounce ? 'lo' : 'hi'
for (let b = 0; b < spec.length; ++b) {
device.sendColor(bindings[b].control, device.colors[`${prefix}_${colors[state.set]}`])
}
Expand All @@ -85,7 +86,7 @@ const make: MakeDeckControlTemplate<Type> = ({ deck, gridPosition, jumps, vertic
(k: number) =>
({ bindings, state, context: { device } }: Control<Type>) =>
() => {
const prefix = state.mode ? 'lo' : 'hi'
const prefix = state.bounce ? 'lo' : 'hi'

device.sendColor(bindings[k].control, device.colors[`${prefix}_${colors[state.set]}`])
}
Expand All @@ -106,7 +107,7 @@ const make: MakeDeckControlTemplate<Type> = ({ deck, gridPosition, jumps, vertic
return {
bindings,
state: {
mode: false,
bounce: bounce,
pressing: null,
diff: 0,
set: 0,
Expand Down
17 changes: 9 additions & 8 deletions packages/launchpad-common/src/controls/loopjump.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,18 @@ export type Type = {
gridPosition: [number, number]
jumps: readonly [number, number][]
vertical?: boolean
bounce?: boolean
}
state: {
mode: boolean
bounce: boolean
pressing: number | null
diff: number
set: number
color: string[]
}
}

const make: MakeDeckControlTemplate<Type> = ({ gridPosition, deck, jumps, vertical = false }) => {
const make: MakeDeckControlTemplate<Type> = ({ gridPosition, deck, jumps, vertical = false, bounce = false }) => {
const bindings: Type['bindings'] = {}
const onMidi =
(k: number, j: [number, number], d: number) =>
Expand All @@ -33,7 +34,7 @@ const make: MakeDeckControlTemplate<Type> = ({ gridPosition, deck, jumps, vertic
modes(
mode,
() => {
if (!state.mode) {
if (!state.bounce) {
if (value) {
setValue(deck.loop_move, j[state.set] * d)
}
Expand Down Expand Up @@ -61,16 +62,16 @@ const make: MakeDeckControlTemplate<Type> = ({ gridPosition, deck, jumps, vertic
() => {
if (value) {
state.set = posMod(state.set + 1, 2)
const prefix = state.mode ? 'lo' : 'hi'
const prefix = state.bounce ? 'lo' : 'hi'
for (let b = 0; b < spec.length; ++b) {
device.sendColor(bindings[b].control, device.colors[`${prefix}_${state.color[state.set]}`])
}
}
},
() => {
if (value) {
state.mode = !state.mode
const prefix = state.mode ? 'lo' : 'hi'
state.bounce = !state.bounce
const prefix = state.bounce ? 'lo' : 'hi'
for (let b = 0; b < spec.length; ++b) {
device.sendColor(bindings[b].control, device.colors[`${prefix}_${state.color[state.set]}`])
}
Expand All @@ -82,7 +83,7 @@ const make: MakeDeckControlTemplate<Type> = ({ gridPosition, deck, jumps, vertic
(k: number) =>
({ context: { device }, bindings, state }: Control<Type>) =>
() => {
const prefix = state.mode ? 'lo' : 'hi'
const prefix = state.bounce ? 'lo' : 'hi'
device.sendColor(bindings[k].control, device.colors[`${prefix}_${state.color[state.set]}`])
}
const spec = jumps.flatMap((j) => [
Expand All @@ -106,7 +107,7 @@ const make: MakeDeckControlTemplate<Type> = ({ gridPosition, deck, jumps, vertic
return {
bindings,
state: {
mode: false,
bounce: bounce,
pressing: null,
diff: 0,
set: 0,
Expand Down

0 comments on commit ed8b3ff

Please sign in to comment.