Skip to content

Commit

Permalink
MIDI Input: add Launchpad S support. added new layer between UI-butto…
Browse files Browse the repository at this point in the history
…n-numbers and device-button-numbers
  • Loading branch information
YeonV committed Oct 4, 2024
1 parent 2bc1ee6 commit 5a6bb34
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 39 deletions.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ledfx",
"version": "2.0.103-b5",
"version": "2.0.104",
"description": "LedFx v2 - BladeMOD",
"author": "YeonV aka Blade",
"private": true,
Expand Down Expand Up @@ -98,7 +98,9 @@
"build:win32": "set GENERATE_SOURCEMAP=false && set CI=false && react-scripts build",
"build:default": "GENERATE_SOURCEMAP=false CI=false react-scripts build",
"prebuild": "node -e \"let pkg=require('./package.json'); pkg.homepage='/'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2));\"",
"postbuild": "node -e \"let pkg=require('./package.json'); pkg.homepage='.'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2));\" && rm -rf build/app && rm -rf build/preload.js && rm -rf build/renderer.js && rm -rf build/electron.js",
"postbuild": "run-script-os",
"postbuild:win32": "node -e \"let pkg=require('./package.json'); pkg.homepage='.'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2));\" && del /q build\\app && rm -rf build\\preload.js && rm -rf build\\renderer.js && rm -rf build\\electron.js",
"postbuild:default": "node -e \"let pkg=require('./package.json'); pkg.homepage='.'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2));\" && rm -rf build/app && rm -rf build/preload.js && rm -rf build/renderer.js && rm -rf build/electron.js",
"buildhass": "GENERATE_SOURCEMAP=false CI=false react-scripts build",
"prebuildhass": "node -e \"let pkg=require('./package.json'); pkg.homepage='./'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2));\"",
"postbuildhass": "node -e \"let pkg=require('./package.json'); pkg.homepage='.'; require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2));\" && rm -rf build/app && rm -rf build/preload.js && rm -rf build/renderer.js && rm -rf build/electron.js",
Expand Down
16 changes: 8 additions & 8 deletions public/app/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const coreFile = {
'instance1': ['-p', '8888', '--no-tray']
}
};
const coreParams = store.get('coreParams', defaultCoreParams);

const coreParams = store.get('coreParams', defaultCoreParams);

const corePath = (file) => path.join(path.dirname(__dirname), isDev ? '../extraResources' : '../../extraResources', file)
const runCore = (file, options) => require('child_process').spawn(`${corePath(file)}`, options).on('error', (err) => { console.error(`Failed to start subprocess. ${err}`); });
Expand All @@ -32,13 +32,13 @@ if (!fs.existsSync(path.join(app.getPath("userData"), '.ledfx-cc'))) {
console.log('Creating .ledfx-cc folder')
fs.mkdirSync(path.join(app.getPath("userData"), '.ledfx-cc'));
}
function startCore(wind, platform, instance = 'instance1', port = '8889') {
function startCore(wind, platform, instance = 'instance1', port = '8889') {
let subpy;

if (fs.existsSync(corePath(coreFile[platform]))) {
if (coreParams[platform] && instance && coreParams[platform][instance]) {
if (instance !== 'instance1') {

coreParams[platform][instance] = ['-p', port, '-c', path.join(app.getPath("userData"), '.ledfx-cc', instance)];
}
console.log('Starting core with params', platform, instance, coreParams[platform][instance])
Expand All @@ -60,17 +60,17 @@ function startCore(wind, platform, instance = 'instance1', port = '8889') {
subpy.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
wind.webContents.send('fromMain', ['snackbar', data.toString()]);
});
});
subpy.on('exit', (code, signal) => {
console.log(`Child process exited with code ${code} and signal ${signal}`);
});
subpy.on('error', (err) => {
console.error(`Failed to start subprocess. ${err}`);
});
}
}
}
return subpy;
}
}

const isCC = fs.existsSync(corePath(coreFile[process.platform]))

Expand Down
4 changes: 2 additions & 2 deletions public/app/handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const handlers = async (wind, subprocesses, event, parameters) => {
// path.join(app.getPath('appData'), '.ledfx', 'config.json')
// )

if (parameters.instance && parameters.instance !== 'instance1') {
if (parameters.instance && parameters.instance !== 'instance1') {
shell.showItemInFolder(
path.join(app.getPath("userData"), '.ledfx-cc', parameters.instance, 'config.json')
)
Expand All @@ -119,7 +119,7 @@ const handlers = async (wind, subprocesses, event, parameters) => {
path.join(app.getPath('home'), '.ledfx', 'config.json')
)
}

break
case 'restart-client':
app.relaunch()
Expand Down
6 changes: 3 additions & 3 deletions public/app/instances.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ function startInstance(wind, name, subprocesses, port) {
}
if (wind && wind.webContents && !wind.isDestroyed() && subprocesses) {
// `subprocesses` is defined, proceed with calling `sendStatus`
try {
try {
sendStatus(wind, subprocesses, false, name);
} catch (error) {
console.error(error);
console.error(error);
}
} else {
// `subprocesses` is not defined, handle this case as needed
Expand Down Expand Up @@ -110,7 +110,7 @@ function closeAllSubs(wind, subpy, subprocesses) {
Object.values(subprocesses).forEach((sub) => {
if (sub) kills(sub)
})
}
}
}

module.exports = {
Expand Down
2 changes: 1 addition & 1 deletion public/app/otp.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ function handleVerifyOTP(wind, event, parameters) {
user.mfaEnabled = true;
store.set('user', user);
}

// console.log('verified_otp:', verified ,user)
wind.webContents.send('fromMain', ['mfa-verified', verified]);
return;
Expand Down
6 changes: 3 additions & 3 deletions public/app/tray.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const isDev = require('electron-is-dev');

function createMenu(isCC, wind, thePath) {
let contextMenu;

if (isCC) {
contextMenu = Menu.buildFromTemplate([
{ label: 'Show', click: () => {
Expand Down Expand Up @@ -73,7 +73,7 @@ function createMenu(isCC, wind, thePath) {
{ label: 'Exit', click: () => app.quit() },
]);
}

return contextMenu;
}

Expand All @@ -83,7 +83,7 @@ function createTray(isCC, wind, thePath, dir) {
let tray = new Tray(icon);

let contextMenu = createMenu(isCC, wind, thePath);

tray.setToolTip(`LedFx Client${isDev ? ' DEV' : ''}`);
tray.setContextMenu(contextMenu);
tray.setIgnoreDoubleClickEvents(true);
Expand Down
6 changes: 3 additions & 3 deletions public/electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const ready = () =>
require('@electron/remote/main').enable(wind.webContents)

wind.webContents.setWindowOpenHandler(({ url }) => {
if (url.includes(' https://accounts.spotify.com/authorize')
if (url.includes(' https://accounts.spotify.com/authorize')
// || url.includes(`${backendUrl}/connect/github?callback`)
) {
shell.openExternal(url)
Expand All @@ -51,14 +51,14 @@ const ready = () =>

if (isCC) startInstance(wind, 'instance1', subprocesses)
if (isDev) installDevtools(installExtension)

createTray(isCC, wind, thePath, __dirname)

ipcMain.on('toMain', async (event, parameters) =>
handlers(wind, subprocesses, event, parameters)
)
wind.on('close', () => {
closeAllSubs(wind, subpy, subprocesses)
closeAllSubs(wind, subpy, subprocesses)
wind = null;
})
})
Expand Down
4 changes: 3 additions & 1 deletion src/components/Midi/LaunchpadButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ const LaunchpadButton = ({
borderless,
bgColor,
children,
hidden,
...props
}: {
hidden?: boolean
buttonNumber: number
children: React.ReactNode
active?: boolean
Expand Down Expand Up @@ -55,7 +57,7 @@ const LaunchpadButton = ({
}, [midiEvent.button])

return (
<div>
<div style={{ visibility: hidden ? 'hidden' : 'visible'}}>
<Button
variant="outlined"
onContextMenu={(e) => {
Expand Down
29 changes: 23 additions & 6 deletions src/components/Midi/LaunchpadButtonMap.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ArrowForwardIos, BrightnessHigh, Collections, Pause, PlayArrow, ViewSidebar, Menu as MenuIcon, Save, Delete, DeleteForever, Visibility } from '@mui/icons-material'
import { ArrowForwardIos, BrightnessHigh, Collections, Pause, PlayArrow, ViewSidebar, Menu as MenuIcon, Save, Delete, DeleteForever, Visibility, Autorenew } from '@mui/icons-material'
import { Box, Button, Divider, ListItemIcon, ListItemText, Menu, MenuItem, Stack, Typography, useTheme } from '@mui/material'
import BladeIcon from '../Icons/BladeIcon/BladeIcon'
import useStore from '../../store/useStore'
Expand All @@ -7,13 +7,14 @@ import { useEffect, useState } from 'react'
import { WebMidi } from 'webmidi'
import LaunchpadButton from './LaunchpadButton'
import { getColorFromValue } from './lpColors'
import { defaultMapping, IMapping } from '../../store/ui/storeMidi'
import { defaultMapping, IMapping, LpMapping } from '../../store/ui/storeMidi'
import LaunchpadColors from './LaunchpadColors'
import { download } from '../../utils/helpers'

const LaunchpadButtonMap = ({toggleSidebar, sideBarOpen}:{toggleSidebar: () => void, sideBarOpen: boolean}) => {
const theme = useTheme()
const [showMapping, setShowMapping] = useState(false)
const setMidiMappingButtonNumbers = useStore((state) => state.setMidiMappingButtonNumbers)
const initMidi = useStore((state) => state.initMidi)
const midiEvent = useStore((state) => state.midiEvent)
const midiOutput = useStore((state) => state.midiOutput)
Expand Down Expand Up @@ -119,6 +120,7 @@ const LaunchpadButtonMap = ({toggleSidebar, sideBarOpen}:{toggleSidebar: () => v
</Stack>
</Stack>
<Stack direction={'row'} alignItems={'center'} spacing={0}>
<Button onClick={() => initMidi()}><Autorenew /></Button>
<Button
id="basic-button"
aria-controls={open ? 'basic-menu' : undefined}
Expand Down Expand Up @@ -184,6 +186,21 @@ const LaunchpadButtonMap = ({toggleSidebar, sideBarOpen}:{toggleSidebar: () => v
<ListItemText primary="Reset Mapping" />
</MenuItem>
<Divider />
<MenuItem onClick={() => {
setMidiMappingButtonNumbers(LpMapping.LaunchpadX)
initMidi()
}}>
<ListItemIcon><BladeIcon name='launchpad' /></ListItemIcon>
<ListItemText primary="Launchpad X" />
</MenuItem>
<MenuItem onClick={() => {
setMidiMappingButtonNumbers(LpMapping.LaunchpadS)
initMidi()
}}>
<ListItemIcon><BladeIcon name='launchpad' /></ListItemIcon>
<ListItemText primary="Launchpad S" />
</MenuItem>
<Divider />
<MenuItem onClick={() => {
const m = JSON.parse(JSON.stringify(midiMapping));
Object.keys(m).forEach(mappingKey => {
Expand Down Expand Up @@ -219,12 +236,11 @@ const LaunchpadButtonMap = ({toggleSidebar, sideBarOpen}:{toggleSidebar: () => v

// Use the buttonNumber from the mapping for functional logic
const functionalButtonNumber = btn?.buttonNumber

const bgColor = midiEvent.button === (functionalButtonNumber || btnNumberInt)
? pressedButtonColor || theme.palette.primary.main
const bgColor = functionalButtonNumber === -1 ? '#000' : (midiEvent.button === functionalButtonNumber)
? ( pressedButtonColor || theme.palette.primary.main )
: btn?.command &&
btn?.command === 'scene' &&
btn?.payload.scene === recentScenes[0]
btn?.payload?.scene === recentScenes[0]
? getColorFromValue(btn?.colorSceneActive || '1E') || '#0f0'
: btn?.command &&
btn?.command === 'scene'
Expand All @@ -238,6 +254,7 @@ const LaunchpadButtonMap = ({toggleSidebar, sideBarOpen}:{toggleSidebar: () => v

return (
<LaunchpadButton
hidden={functionalButtonNumber === -1}
buttonNumber={btnNumberInt}
active={!!(rowIndex === 0 && btn?.command && btn?.command !== 'none')}
bgColor={bgColor}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Midi/MidiInputDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import BladeIcon from '../Icons/BladeIcon/BladeIcon'
import LaunchpadButtonMap from './LaunchpadButtonMap'

const MidiInputDialog = () => {
const midiEvent = useStore((state) => state.midiEvent)
const midiInput = useStore((state) => state.midiInput)
const [open, setOpen] = useState<boolean>(false)
const [sideBarOpen, setSideBarOpen] = useState(false)
const toggleSidebar = () => setSideBarOpen(!sideBarOpen)
Expand All @@ -29,7 +29,7 @@ const MidiInputDialog = () => {
}}
>
<DialogTitle display="flex" alignItems="center">
<BladeIcon name="mdi:midi" style={{ marginRight: '1rem'}} /> {/\((.*?)\)/.exec(midiEvent.name)?.[1]} Input Configuration
<BladeIcon name="mdi:midi" style={{ marginRight: '1rem'}} /> {/\((.*?)\)/.exec(midiInput)?.[1]} Input Configuration
</DialogTitle>
<DialogContent>
<LaunchpadButtonMap sideBarOpen={sideBarOpen} toggleSidebar={toggleSidebar} />
Expand Down
14 changes: 12 additions & 2 deletions src/components/Midi/MidiListener.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,17 @@ const MIDIListener = () => {

Object.entries(midiMapping[0]).forEach(([_key, value]) => {
const buttonNumber = value.buttonNumber
if (value.command !== 'scene' && value.command !== 'none') {
if (!value.command || value.command === 'none' || buttonNumber === -1) {
if (output) {
try {
output.send([0x90, buttonNumber, 0])
} catch (error) {
console.error('Error sending MIDI message:', error)
}
}
return
}
if (value.command && value.command !== 'scene' && value.command !== 'none') {
if (output) {
try {
output.send([0x90, buttonNumber, parseInt(value.colorCommand, 16) || 99])
Expand Down Expand Up @@ -214,7 +224,7 @@ const MIDIListener = () => {

Object.entries(midiMapping[0]).forEach(([key, value]) => {
const buttonNumber = value.buttonNumber
if (value.command !== 'scene' && value.command !== 'none') {
if (value.command !== 'scene' && value.command && value.command !== 'none') {
if (output) {
try {
output.send([parseInt(`0x${value.typeCommand}`) || 0x90, buttonNumber, parseInt(value.colorCommand, 16) || 99])
Expand Down
Loading

0 comments on commit 5a6bb34

Please sign in to comment.