Skip to content

Commit

Permalink
Merge pull request #78 from dszakallas/release-dec
Browse files Browse the repository at this point in the history
  • Loading branch information
dszakallas authored Dec 29, 2023
2 parents 1c22160 + 636524c commit 84ed9ee
Show file tree
Hide file tree
Showing 54 changed files with 5,392 additions and 3,833 deletions.
6,049 changes: 3,497 additions & 2,552 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"workspaces": [
"packages/common",
"packages/mixxx",
"packages/launch-common",
"packages/launchpad-common",
"packages/launchpad-mk1",
"packages/launchpad-mk2",
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const posMod = (x: number, n: number): number => ((x % n) + n) % n

export const hexFormat = (n: number, d: number) => '0x' + n.toString(16).toUpperCase().padStart(d, '0')
export const hexFormat = (n: number, d: number) => n.toString(16).toUpperCase().padStart(d, '0')

export const range = function * (n: number): Generator<number> {
for (let i = 0; i < n; i++) {
Expand Down
11 changes: 11 additions & 0 deletions packages/launch-common/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "@mixxx-launch/launch-common",
"author": "Midiparse",
"license": "MIT",
"private": true,
"version": "1.0.0",
"main": "src/index.ts",
"dependencies": {
"@mixxx-launch/mixxx": "~1.0.0"
}
}
80 changes: 80 additions & 0 deletions packages/launch-common/src/Control.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {
Component,
} from '@mixxx-launch/mixxx'

export type BindingTemplate = {
type: new (...args: unknown[]) => Component,
target: unknown,
listeners?: {
[_: string]: (control: any) => (...args: any[]) => void
}
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export type Phantom<_Ctx> = never

export type ControlType < Ctx > = {
type: string
bindings: { [k: string | number | symbol]: BindingTemplate }
params: Params
state?: State
context?: Phantom<Ctx>
}

export type State = { [k: string]: unknown }
export type Params = { [k: string]: unknown }

export type ControlTemplate<C extends ControlType<unknown>> = {
bindings: C['bindings']
state?: C['state']
}

export type MakeControlTemplate<C extends ControlType<unknown>> = (
params: C['params']
) => ControlTemplate<C>

export type MakeBindings<Ctx, C extends ControlType<Ctx>> = (ctx: Ctx, template: C["bindings"]) => Bindings<C>

export type Bindings<C extends ControlType<unknown>> = {
[K in keyof C["bindings"]]: InstanceType<C["bindings"][K]["type"]>
}

export class Control<Ctx, C extends ControlType<Ctx>> extends Component {
templates: C['bindings']
bindings: Bindings<C>
state: C['state']
context: Ctx

constructor(makeBindings: MakeBindings<Ctx, C>, templates: C['bindings'], state: C['state'], context: Ctx) {
super()
this.bindings = makeBindings(context, templates)
this.templates = templates
this.state = state

this.context = context
}

onMount() {
super.onMount()

Object.keys(this.bindings).forEach((k) => {
const b = this.bindings[k]
const listeners = this.templates[k].listeners ?? {}
Object.keys(listeners).forEach((event) => {
const listener = listeners[event]
if (listener != null) {
b.addListener(event, listener(this))
}
})
})

Object.values(this.bindings).forEach((b) => b.mount())
}

onUnmount() {
const bs = Object.values(this.bindings)
bs.forEach((b) => b.unmount())
bs.forEach((b) => b.removeAllListeners())
super.onUnmount()
}
}
Empty file.
15 changes: 15 additions & 0 deletions packages/launch-common/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"composite": true,
"outDir": "dist"
},
"include": [
"src"
],
"references": [
{
"path": "../mixxx"
}
]
}
Empty file.
3 changes: 2 additions & 1 deletion packages/launchcontrol-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"version": "1.0.0",
"main": "src/index.ts",
"dependencies": {
"@mixxx-launch/mixxx": "~1.0.0"
"@mixxx-launch/mixxx": "~1.0.0",
"@mixxx-launch/launch-common": "~1.0.0"
}
}
54 changes: 54 additions & 0 deletions packages/launchcontrol-common/src/Control.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ControlComponent, ControlDef, ControlMessage, MidiMessage } from "@mixxx-launch/mixxx"
import { LCMidiComponent, LaunchControlDevice, OnOff } from "./device"
import { ControlType as BaseControlType, Control as BaseControl, Bindings } from '@mixxx-launch/launch-common/src/Control'

export type ControlContext = {
device: LaunchControlDevice
}

export type ControlType = BaseControlType<ControlContext>
export type Control<C extends ControlType> = BaseControl<ControlContext, C>

export type ControlBindingTemplate<C extends ControlType> = {
type: new (...args: any[]) => ControlComponent
target: ControlDef
softTakeover?: boolean
listeners?: {
update?: (c: Control<C>) => (message: ControlMessage) => void
mount?: (c: Control<C>) => () => void
unmount?: (c: Control<C>) => () => void
}
}

export type ButtonKey = readonly [number, number]

export type MidiTarget = [number, string, OnOff?]

export type MidiBindingTemplate<C extends ControlType> = {
type: new (...args: any[]) => LCMidiComponent
target: MidiTarget
listeners: {
midi?: (c: Control<C>) => (message: MidiMessage) => void
mount?: (c: Control<C>) => () => void
unmount?: (c: Control<C>) => () => void
}
}

export type BindingTemplates<C extends ControlType> = {
[K: string]: MidiBindingTemplate<C> | ControlBindingTemplate<C>
}

export const makeBindings = <C extends ControlType>(ctx: ControlContext, t: BindingTemplates<C>): Bindings<C> => {
const ret: { [_: string]: any } = {}
for (const k in t) {
if (t[k].type === ControlComponent) {
const c = t[k] as ControlBindingTemplate<C>
const softTakeover = c.softTakeover || false
ret[k] = new ControlComponent(c.target, softTakeover)
} else {
const c = t[k] as MidiBindingTemplate<C>
ret[k] = new LCMidiComponent(ctx.device, ...c.target)
}
}
return ret as Bindings<C>
}
Empty file.
142 changes: 142 additions & 0 deletions packages/launchcontrol-common/src/controls/deck.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { MakeControlTemplate } from '@mixxx-launch/launch-common/src/Control'
import { MidiMessage, absoluteNonLin } from "@mixxx-launch/mixxx"
import { ControlComponent, ControlMessage, getValue, root, setValue } from "@mixxx-launch/mixxx/src/Control"
import { Control, ControlBindingTemplate, MidiBindingTemplate } from '../Control'
import { LCMidiComponent } from "../device"
import { channelColorPalette } from '../util'

const eq3Channel = ['low', 'mid', 'hi']

export type Eq3Type = {
type: 'eq3'
bindings: {
[ch in typeof eq3Channel[number] as `knob.${ch}`]: MidiBindingTemplate<Eq3Type>
} & {
[ch in typeof eq3Channel[number] as `kill.${ch}`]: ControlBindingTemplate<Eq3Type>
} & {
[ch in typeof eq3Channel[number] as `val.${ch}`]: ControlBindingTemplate<Eq3Type>
}
params: {
template: number
column: number
deck: number
}
}

export const makeEq3: MakeControlTemplate<Eq3Type> = ({ template, column, deck }) => {
const bindings: Eq3Type['bindings'] = {}
const fxParams = root.equalizerRacks[0].effect_units[deck].effects[0].parameters
eq3Channel.forEach((v, i) => {
bindings[`knob.${v}`] = {
type: LCMidiComponent,
target: [template, `knob.${2-i}.${column}`],
listeners: {
midi: ({ bindings }: Control<Eq3Type>) => ({ value }: MidiMessage) => {
setValue(bindings[`val.${v}`].control, absoluteNonLin(value, 0, 1, 4))
}
}
}

bindings[`kill.${v}`] = {
type: ControlComponent,
target: fxParams[i].button_value,
listeners: {
update: ({ context: { device }, bindings }: Control<Eq3Type>) => ({ value }: ControlMessage) => {
device.sendColor(template, bindings[`knob.${v}`].led, device.colors[channelColorPalette[deck % 4][value ? 1 : 0]])
}
}
}

bindings[`val.${v}`] = {
type: ControlComponent,
target: fxParams[i].value,
softTakeover: true
}
})

return { bindings }
}


export type Eq3KillType = {
type: 'eq3Kill'
bindings: {
[ch in typeof eq3Channel[number] | "qfx" as `pad.${ch}`]: MidiBindingTemplate<Eq3KillType>
} & {
[ch in typeof eq3Channel[number] | "qfx" as `kill.${ch}`]: ControlBindingTemplate<Eq3KillType>
}
params: {
template: number
row: number
column: number
deck: number
}
}

export const makeEq3Kill: MakeControlTemplate<Eq3KillType> = ({ template, row, column, deck }) => {
const bindings: Eq3KillType['bindings'] = {}
const fxParams = root.equalizerRacks[0].effect_units[deck].effects[0].parameters

const eq3KillChannel = [
...['low', 'mid', 'hi'].map((v, i) => [v, fxParams[2-i].button_value] as const),
['qfx', root.quickEffectRacks[0].effect_units[deck].enabled] as const
] as const

eq3KillChannel.forEach(([v, c], i) => {
bindings[`pad.${v}`] = {
type: LCMidiComponent,
target: [template, `pad.${row}.${column + i}`, 'on'],
listeners: {
midi: ({ bindings }: Control<Eq3KillType>) => ({ value }: MidiMessage) => {
if (value) {
const ctrl = bindings[`kill.${v}`].control
setValue(ctrl, 1 - getValue(ctrl))
}
}
}
}
bindings[`kill.${v}`] = {
type: ControlComponent,
target: c,
listeners: {
update: ({ context: { device }, bindings }: Control<Eq3KillType>) => ({ value }: ControlMessage) => {
device.sendColor(template, bindings[`pad.${v}`].led, value ? device.colors.hi_red : device.colors.black)
}
}
}
})

return { bindings }
}

export type GainType = {
type: 'gain'
bindings: {
fader: MidiBindingTemplate<GainType>
ctrl:ControlBindingTemplate<GainType>
}
params: {
template: number
column: number
deck: number
}
}

export const makeGain: MakeControlTemplate<GainType> = ({ template, column, deck }) => ({
bindings: {
fader: {
type: LCMidiComponent,
target: [template, `fader.0.${column}`],
listeners: {
midi: ({ bindings }: Control<GainType>) => ({ value }: MidiMessage) => {
setValue(bindings.ctrl.control, value / 127)
}
}
},
ctrl: {
type: ControlComponent,
target: root.channels[deck].volume,
softTakeover: true
}
}
})
Loading

0 comments on commit 84ed9ee

Please sign in to comment.