change menu and start saving process
convert file to state wip finish file to state conversion load in file to state (needs to be fixed in order to properly load some things in) formatting Add microkorg sysex decoding Correctly parse bit array sections Add sysex encoding and header creation remove log Finish format file v1 Fix a definitions Move stuff around add file to store functions Finish prepare_timbre begin to Setup menu Gut a lot of stuff, and implement the menu Begin overhaul of fileupload process Finish hooking up the menu clean up start saving functions add unused state, mostly finish saveStore add self reminder complete store saving process 1 store to simplified store is complete
This commit is contained in:
Generated
+8
-3
@@ -3765,13 +3765,18 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"binary-parser-encoder": {
|
"binary-parser-encoder": {
|
||||||
"version": "1.5.2",
|
"version": "1.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/binary-parser-encoder/-/binary-parser-encoder-1.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/binary-parser-encoder/-/binary-parser-encoder-1.5.3.tgz",
|
||||||
"integrity": "sha512-bZKR5Qo+pujq+Pvye/ZR94aRbYJx+7WijKAD7RcFi6qgtNJ4kHqAbDaH588VM2N4HylWp08Pm2l0buXqh2t5kw==",
|
"integrity": "sha512-yu3tdLBYqPIwGRaXyswLoLrhaffkuZkNuXveq/jYoyBHQbFMjamHCWPFOmI2Qz+Go0Rh6wE9f6tt0EAvsgDD0g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"smart-buffer": "^4.1.0"
|
"smart-buffer": "^4.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"bitset": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/bitset/-/bitset-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-oKaRp6mzXedJ1Npo86PKhWfDelI6HxxJo+it9nAcBB0HLVvYVp+5i6yj6DT5hfFgo+TS5T57MRWtw8zhwdTs3g=="
|
||||||
|
},
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"version": "3.7.2",
|
"version": "3.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||||
|
|||||||
+2
-1
@@ -7,7 +7,8 @@
|
|||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@testing-library/react": "^9.5.0",
|
"@testing-library/react": "^9.5.0",
|
||||||
"@testing-library/user-event": "^7.2.1",
|
"@testing-library/user-event": "^7.2.1",
|
||||||
"binary-parser-encoder": "^1.5.2",
|
"binary-parser-encoder": "^1.5.3",
|
||||||
|
"bitset": "^5.1.1",
|
||||||
"rc-slider": "^9.6.3",
|
"rc-slider": "^9.6.3",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/node_modules/binary-parser-encoder/dist/binary_parser.js b/node_modules/binary-parser-encoder/dist/binary_parser.js
|
||||||
|
index ee49a70..ef0ac58 100644
|
||||||
|
--- a/node_modules/binary-parser-encoder/dist/binary_parser.js
|
||||||
|
+++ b/node_modules/binary-parser-encoder/dist/binary_parser.js
|
||||||
|
@@ -551,7 +551,7 @@ var Parser = /** @class */ (function () {
|
||||||
|
Parser.prototype.compile = function () {
|
||||||
|
var importPath = 'imports';
|
||||||
|
var ctx = this.getContext(importPath);
|
||||||
|
- this.compiled = new Function(importPath, 'TextDecoder', "return function (buffer, constructorFn) { " + ctx.code + " };")(ctx.imports, typeof TextDecoder === 'undefined'
|
||||||
|
+ this.compiled = new Function(importPath, 'TextDecoder', "return function (buffer, constructorFn) { ' + ctx.code + ' };")(ctx.imports, typeof TextDecoder === 'undefined'
|
||||||
|
? require('util').TextDecoder
|
||||||
|
: TextDecoder);
|
||||||
|
};
|
||||||
@@ -10,6 +10,11 @@
|
|||||||
content="Web site created using create-react-app"
|
content="Web site created using create-react-app"
|
||||||
/>
|
/>
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;700&family=Roboto:wght@100;300;400;500;700&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
<!--
|
<!--
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#31373D" d="M2 36s-2 0-2-2V2s0-2 2-2h32.031C36 0 36 2 36 2v32s0 2-2 2H2z"/><path d="M19 33s0 1 1 1h5c1 0 1-1 1-1V5h-7v28zm9-28v28s0 1 1 1h4c1 0 1-1 1-1V5h-6zM10 33s0 1 1 1h5c1 0 1-1 1-1V5h-7v28zm-8 0s0 1 1 1h4c1 0 1-1 1-1V5H2v28z" fill="#E1E8ED"/><path fill="#31373D" d="M30 23s0 1-1 1h-4c-1 0-1-1-1-1V3h6v20zm-9 0s0 1-1 1h-4c-1 0-1-1-1-1V3h6v20zm-9 0s0 1-1 1H7c-1 0-1-1-1-1V3h6v20z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 463 B |
+6
-1
@@ -16,7 +16,7 @@
|
|||||||
.App-header {
|
.App-header {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: calc(10px + 2vmin);
|
font-size: calc(10px + 2vmin);
|
||||||
@@ -25,6 +25,11 @@
|
|||||||
.App-link {
|
.App-link {
|
||||||
color: rgb(112, 76, 182);
|
color: rgb(112, 76, 182);
|
||||||
}
|
}
|
||||||
|
.App-spacer {
|
||||||
|
min-height: 100vh;
|
||||||
|
min-width: 15vw;
|
||||||
|
border-right: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes App-logo-float {
|
@keyframes App-logo-float {
|
||||||
0% {
|
0% {
|
||||||
|
|||||||
+4
-2
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import MainMenu from './features/menu/Menu';
|
import Menu from './features/menu';
|
||||||
import TimbreSwitcher from './features/timbre/timbreSwitcher';
|
import TimbreSwitcher from './features/timbre/timbreSwitcher';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
||||||
@@ -7,7 +7,9 @@ function App() {
|
|||||||
return (
|
return (
|
||||||
<div className='App'>
|
<div className='App'>
|
||||||
<header className='App-header'>
|
<header className='App-header'>
|
||||||
<MainMenu />
|
<div className='App-spacer'>
|
||||||
|
<Menu />
|
||||||
|
</div>
|
||||||
<TimbreSwitcher />
|
<TimbreSwitcher />
|
||||||
</header>
|
</header>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
|
||||||
import {
|
|
||||||
decrement,
|
|
||||||
increment,
|
|
||||||
incrementByAmount,
|
|
||||||
incrementAsync,
|
|
||||||
selectCount,
|
|
||||||
} from './counterSlice';
|
|
||||||
import styles from './Counter.module.css';
|
|
||||||
|
|
||||||
export function Counter() {
|
|
||||||
const count = useSelector(selectCount);
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const [incrementAmount, setIncrementAmount] = useState('2');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className={styles.row}>
|
|
||||||
<button
|
|
||||||
className={styles.button}
|
|
||||||
aria-label="Increment value"
|
|
||||||
onClick={() => dispatch(increment())}
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</button>
|
|
||||||
<span className={styles.value}>{count}</span>
|
|
||||||
<button
|
|
||||||
className={styles.button}
|
|
||||||
aria-label="Decrement value"
|
|
||||||
onClick={() => dispatch(decrement())}
|
|
||||||
>
|
|
||||||
-
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className={styles.row}>
|
|
||||||
<input
|
|
||||||
className={styles.textbox}
|
|
||||||
aria-label="Set increment amount"
|
|
||||||
value={incrementAmount}
|
|
||||||
onChange={e => setIncrementAmount(e.target.value)}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
className={styles.button}
|
|
||||||
onClick={() =>
|
|
||||||
dispatch(incrementByAmount(Number(incrementAmount) || 0))
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Add Amount
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className={styles.asyncButton}
|
|
||||||
onClick={() => dispatch(incrementAsync(Number(incrementAmount) || 0))}
|
|
||||||
>
|
|
||||||
Add Async
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row:not(:last-child) {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
font-size: 78px;
|
|
||||||
padding-left: 16px;
|
|
||||||
padding-right: 16px;
|
|
||||||
margin-top: 2px;
|
|
||||||
font-family: 'Courier New', Courier, monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
appearance: none;
|
|
||||||
background: none;
|
|
||||||
font-size: 32px;
|
|
||||||
padding-left: 12px;
|
|
||||||
padding-right: 12px;
|
|
||||||
outline: none;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
color: rgb(112, 76, 182);
|
|
||||||
padding-bottom: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: rgba(112, 76, 182, 0.1);
|
|
||||||
border-radius: 2px;
|
|
||||||
transition: all 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textbox {
|
|
||||||
font-size: 32px;
|
|
||||||
padding: 2px;
|
|
||||||
width: 64px;
|
|
||||||
text-align: center;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:hover, .button:focus {
|
|
||||||
border: 2px solid rgba(112, 76, 182, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:active {
|
|
||||||
background-color: rgba(112, 76, 182, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.asyncButton {
|
|
||||||
composes: button;
|
|
||||||
position: relative;
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asyncButton:after {
|
|
||||||
content: "";
|
|
||||||
background-color: rgba(112, 76, 182, 0.15);
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
opacity: 0;
|
|
||||||
transition: width 1s linear, opacity 0.5s ease 1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asyncButton:active:after {
|
|
||||||
width: 0%;
|
|
||||||
opacity: 1;
|
|
||||||
transition: 0s
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import { createSlice } from '@reduxjs/toolkit';
|
|
||||||
|
|
||||||
export const counterSlice = createSlice({
|
|
||||||
name: 'counter',
|
|
||||||
initialState: {
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
reducers: {
|
|
||||||
increment: state => {
|
|
||||||
// Redux Toolkit allows us to write "mutating" logic in reducers. It
|
|
||||||
// doesn't actually mutate the state because it uses the Immer library,
|
|
||||||
// which detects changes to a "draft state" and produces a brand new
|
|
||||||
// immutable state based off those changes
|
|
||||||
state.value += 1;
|
|
||||||
},
|
|
||||||
decrement: state => {
|
|
||||||
state.value -= 1;
|
|
||||||
},
|
|
||||||
incrementByAmount: (state, action) => {
|
|
||||||
state.value += action.payload;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
|
|
||||||
|
|
||||||
// The function below is called a thunk and allows us to perform async logic. It
|
|
||||||
// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This
|
|
||||||
// will call the thunk with the `dispatch` function as the first argument. Async
|
|
||||||
// code can then be executed and other actions can be dispatched
|
|
||||||
export const incrementAsync = amount => dispatch => {
|
|
||||||
setTimeout(() => {
|
|
||||||
dispatch(incrementByAmount(amount));
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
// The function below is called a selector and allows us to select a value from
|
|
||||||
// the state. Selectors can also be defined inline where they're used instead of
|
|
||||||
// in the slice file. For example: `useSelector((state) => state.counter.value)`
|
|
||||||
export const selectCount = state => state.counter.value;
|
|
||||||
|
|
||||||
export default counterSlice.reducer;
|
|
||||||
@@ -6,9 +6,9 @@ import './rcSelectTheme.css';
|
|||||||
const SliderWithToolTip = createSliderWithTooltip(RCSlider);
|
const SliderWithToolTip = createSliderWithTooltip(RCSlider);
|
||||||
|
|
||||||
const SelectStyle = {
|
const SelectStyle = {
|
||||||
control: (provided) => ({
|
control: (provided, state) => ({
|
||||||
...provided,
|
...provided,
|
||||||
width: '100%',
|
width: state.selectProps.width,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,9 +24,12 @@ const Slider = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SelectList = (props) => {
|
const SelectList = (props = { width: '100%' }) => {
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
|
className='react-select-container'
|
||||||
|
classNamePrefix='react-select'
|
||||||
|
menuPlacement='auto'
|
||||||
value={{ value: props.value }}
|
value={{ value: props.value }}
|
||||||
options={props.list}
|
options={props.list}
|
||||||
getOptionLabel={
|
getOptionLabel={
|
||||||
@@ -36,6 +39,7 @@ const SelectList = (props) => {
|
|||||||
}
|
}
|
||||||
onChange={(e) => props.onChange(e.value)}
|
onChange={(e) => props.onChange(e.value)}
|
||||||
styles={SelectStyle}
|
styles={SelectStyle}
|
||||||
|
width={props.width}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -43,9 +47,9 @@ const SelectList = (props) => {
|
|||||||
const Checkbox = (props) => {
|
const Checkbox = (props) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<span>{`${props.name}:`}</span>
|
<span>{props.name ? `${props.name}:` : ''}</span>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type='checkbox'
|
||||||
value={props.parameter}
|
value={props.parameter}
|
||||||
defaultChecked={props.parameter}
|
defaultChecked={props.parameter}
|
||||||
onChange={(e) => props.onChange(e.target.checked)}
|
onChange={(e) => props.onChange(e.target.checked)}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
.dropzone-container {
|
|
||||||
background: white;
|
|
||||||
padding: 0.95em;
|
|
||||||
position: fixed;
|
|
||||||
margin: auto;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: calc(7.5em);
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 14em;
|
|
||||||
z-index: 100;
|
|
||||||
border: 0.09em solid slategray;
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
import React, { useState, useEffect, useMemo } from 'react';
|
|
||||||
import { useDropzone } from 'react-dropzone';
|
|
||||||
import { Parse } from './ioParser';
|
|
||||||
import './IO.css';
|
|
||||||
|
|
||||||
const baseStyle = {
|
|
||||||
flex: 1,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
height: '4.25em',
|
|
||||||
padding: '0.5em',
|
|
||||||
borderWidth: 2,
|
|
||||||
borderRadius: 2,
|
|
||||||
borderColor: 'slategrey',
|
|
||||||
borderStyle: 'dashed',
|
|
||||||
backgroundColor: 'ghostwhite',
|
|
||||||
color: 'slategray',
|
|
||||||
outline: 'none',
|
|
||||||
transition: 'border .24s ease-in-out',
|
|
||||||
};
|
|
||||||
|
|
||||||
const activeStyle = {
|
|
||||||
borderColor: 'slateblue',
|
|
||||||
};
|
|
||||||
|
|
||||||
const acceptStyle = {
|
|
||||||
borderColor: 'mediumseagreen',
|
|
||||||
};
|
|
||||||
|
|
||||||
const rejectStyle = {
|
|
||||||
borderColor: 'crimson',
|
|
||||||
};
|
|
||||||
|
|
||||||
const Dropzone = ({ setFile }) => {
|
|
||||||
const {
|
|
||||||
getRootProps,
|
|
||||||
getInputProps,
|
|
||||||
isDragAccept,
|
|
||||||
isDragActive,
|
|
||||||
isDragReject,
|
|
||||||
} = useDropzone({
|
|
||||||
multiple: false,
|
|
||||||
maxFiles: 1,
|
|
||||||
noKeyboard: true,
|
|
||||||
onDrop: (files) => setFile(files[0]),
|
|
||||||
});
|
|
||||||
|
|
||||||
const style = useMemo(
|
|
||||||
() => ({
|
|
||||||
...baseStyle,
|
|
||||||
...(isDragActive ? activeStyle : {}),
|
|
||||||
...(isDragAccept ? acceptStyle : {}),
|
|
||||||
...(isDragReject ? rejectStyle : {}),
|
|
||||||
}),
|
|
||||||
[isDragActive, isDragReject, isDragAccept]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='dropzone-container'>
|
|
||||||
<div {...getRootProps({ style })}>
|
|
||||||
<input {...getInputProps()} />
|
|
||||||
<p>Drag and drop file here, or click to select</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function IO() {
|
|
||||||
const [file, setFile] = useState(null);
|
|
||||||
const reader = new FileReader();
|
|
||||||
|
|
||||||
// turn file into array buffer for parsing
|
|
||||||
const fileToArrayBuffer = (f) => {
|
|
||||||
reader.onload = (e) => {
|
|
||||||
setFile(new Uint8Array(e.target.result));
|
|
||||||
};
|
|
||||||
reader.readAsArrayBuffer(f);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (file) {
|
|
||||||
console.log(Parse(file));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='dropzone-container'>
|
|
||||||
<Dropzone className='dropzone-modal' setFile={fileToArrayBuffer} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO -- possibly add clicking outside the modal to close it*/
|
|
||||||
@@ -0,0 +1,793 @@
|
|||||||
|
import { returnFromList } from '../utils/functions';
|
||||||
|
|
||||||
|
export default function build_store(patch, timbre1, timbre2, vocoder) {
|
||||||
|
return {
|
||||||
|
...prepare_patch(patch),
|
||||||
|
...prepare_timbre(timbre1),
|
||||||
|
...prepare_timbre(timbre2, true),
|
||||||
|
...prepare_vocoder(vocoder),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepare_patch(object) {
|
||||||
|
console.log(object.voice_mode);
|
||||||
|
return {
|
||||||
|
unusedPatch: {
|
||||||
|
id: 'unusedPatch',
|
||||||
|
scaleKey: object.scale_key,
|
||||||
|
scaleType: object.scale_type,
|
||||||
|
keyboardOctave: object.keyboard_octave,
|
||||||
|
arpSwitch: object.arp_on_off,
|
||||||
|
},
|
||||||
|
patch: {
|
||||||
|
id: 'patch',
|
||||||
|
mode: returnFromList(
|
||||||
|
object.voice_mode,
|
||||||
|
'Single',
|
||||||
|
null,
|
||||||
|
'Multiple',
|
||||||
|
'Vocoder'
|
||||||
|
),
|
||||||
|
name: object.program_name,
|
||||||
|
},
|
||||||
|
delay: {
|
||||||
|
id: 'delay',
|
||||||
|
type: returnFromList(
|
||||||
|
object.delay_type,
|
||||||
|
'Stereo Delay',
|
||||||
|
'Cross Delay',
|
||||||
|
'L/R Delay'
|
||||||
|
),
|
||||||
|
tempoSync: object.delay_sync,
|
||||||
|
syncNote: returnFromList(
|
||||||
|
object.delay_timebase,
|
||||||
|
'1/32',
|
||||||
|
'1/24',
|
||||||
|
'1/16',
|
||||||
|
'1/12',
|
||||||
|
'3/32',
|
||||||
|
'1/8',
|
||||||
|
'1/6',
|
||||||
|
'3/16',
|
||||||
|
'1/4',
|
||||||
|
'1/3',
|
||||||
|
'3/8',
|
||||||
|
'1/2',
|
||||||
|
'2/3',
|
||||||
|
'3/4',
|
||||||
|
'1/1'
|
||||||
|
),
|
||||||
|
delayTime: object.delay_time,
|
||||||
|
delayDepth: object.delay_depth,
|
||||||
|
},
|
||||||
|
modfx: {
|
||||||
|
id: 'modfx',
|
||||||
|
type: returnFromList(
|
||||||
|
object.mod_type,
|
||||||
|
'Chorus/Flanger',
|
||||||
|
'Ensemble',
|
||||||
|
'Phaser'
|
||||||
|
),
|
||||||
|
lfoSpeed: object.mod_lfo_speed,
|
||||||
|
effectDepth: object.mod_depth,
|
||||||
|
},
|
||||||
|
eq: {
|
||||||
|
id: 'eq',
|
||||||
|
lowFrequency: returnFromList(
|
||||||
|
object.eq_low_freq,
|
||||||
|
40,
|
||||||
|
50,
|
||||||
|
60,
|
||||||
|
80,
|
||||||
|
100,
|
||||||
|
120,
|
||||||
|
140,
|
||||||
|
160,
|
||||||
|
180,
|
||||||
|
200,
|
||||||
|
220,
|
||||||
|
240,
|
||||||
|
260,
|
||||||
|
280,
|
||||||
|
300,
|
||||||
|
320,
|
||||||
|
340,
|
||||||
|
360,
|
||||||
|
380,
|
||||||
|
400,
|
||||||
|
420,
|
||||||
|
440,
|
||||||
|
460,
|
||||||
|
480,
|
||||||
|
500,
|
||||||
|
600,
|
||||||
|
700,
|
||||||
|
800,
|
||||||
|
900,
|
||||||
|
1000
|
||||||
|
),
|
||||||
|
lowGain: object.eq_low_gain - 64,
|
||||||
|
highFrequency: returnFromList(
|
||||||
|
object.eq_hi_freq,
|
||||||
|
1.0,
|
||||||
|
1.25,
|
||||||
|
1.5,
|
||||||
|
1.75,
|
||||||
|
2.0,
|
||||||
|
2.25,
|
||||||
|
2.5,
|
||||||
|
2.75,
|
||||||
|
3.0,
|
||||||
|
3.25,
|
||||||
|
3.5,
|
||||||
|
3.75,
|
||||||
|
4.0,
|
||||||
|
4.25,
|
||||||
|
4.5,
|
||||||
|
4.75,
|
||||||
|
5.0,
|
||||||
|
5.25,
|
||||||
|
5.5,
|
||||||
|
5.75,
|
||||||
|
6.0,
|
||||||
|
7.0,
|
||||||
|
8.0,
|
||||||
|
9.0,
|
||||||
|
10.0,
|
||||||
|
11.0,
|
||||||
|
12.0,
|
||||||
|
14.0,
|
||||||
|
16.0,
|
||||||
|
18.0
|
||||||
|
),
|
||||||
|
highGain: object.eq_hi_gain - 64,
|
||||||
|
},
|
||||||
|
arpeggA: {
|
||||||
|
id: 'arpeggA',
|
||||||
|
tempo: object.arp_tempo,
|
||||||
|
resolution: returnFromList(
|
||||||
|
object.arp_resolution,
|
||||||
|
'1/24',
|
||||||
|
'1/16',
|
||||||
|
'1/12',
|
||||||
|
'1/8',
|
||||||
|
'1/6',
|
||||||
|
'1/4'
|
||||||
|
),
|
||||||
|
gate: object.arp_gate_time,
|
||||||
|
type: returnFromList(
|
||||||
|
object.arp_type,
|
||||||
|
'Up',
|
||||||
|
'Down',
|
||||||
|
'Alternate 1',
|
||||||
|
'Alternate 2',
|
||||||
|
'Random',
|
||||||
|
'Trigger'
|
||||||
|
),
|
||||||
|
range: object.arp_range,
|
||||||
|
},
|
||||||
|
arpeggB: {
|
||||||
|
id: 'arpeggB',
|
||||||
|
latch: object.arp_latch,
|
||||||
|
swing: object.arp_swing,
|
||||||
|
keySync: object.arp_key_sync,
|
||||||
|
lastStep: object.arp_trigger_length,
|
||||||
|
target: object.arp_target,
|
||||||
|
pattern: object.arp_trigger_pattern,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepare_timbre(object, timbre2 = false) {
|
||||||
|
const num = timbre2 ? 2 : 1;
|
||||||
|
if (!object) return null;
|
||||||
|
return {
|
||||||
|
[`unusedTimbre${num}`]: {
|
||||||
|
id: `unusedTimbre${num}`,
|
||||||
|
midiChannel: object[`t${num}_midi_channel`],
|
||||||
|
keyPriority: object[`t${num}_key_priority`],
|
||||||
|
filterVelSense: object[`t${num}_filter_vel_sense`],
|
||||||
|
ampSwitch: object[`t${num}_amp_switch`],
|
||||||
|
ampVelSense: object[`t${num}_amp_vel_sense`],
|
||||||
|
},
|
||||||
|
[`vco${timbre2 ? num + 1 : num}`]: {
|
||||||
|
id: `vco${timbre2 ? num + 1 : num}`,
|
||||||
|
waveform: returnFromList(
|
||||||
|
object[`t${num}_osc_1_wave`],
|
||||||
|
'Saw',
|
||||||
|
'Square',
|
||||||
|
'Triangle',
|
||||||
|
'Sine',
|
||||||
|
'Vox Wave',
|
||||||
|
'DWGS',
|
||||||
|
'Noise',
|
||||||
|
'Audio In'
|
||||||
|
),
|
||||||
|
dwgsOrModType: returnFromList(
|
||||||
|
object[`t${num}_osc_1_dgws`],
|
||||||
|
'SynSine1',
|
||||||
|
'SynSine2',
|
||||||
|
'SynSine3',
|
||||||
|
'SynSine4',
|
||||||
|
'SynSine5',
|
||||||
|
'SynSine6',
|
||||||
|
'SynSine7',
|
||||||
|
'SynBass1',
|
||||||
|
'SynBass2',
|
||||||
|
'SynBass3',
|
||||||
|
'SynBass4',
|
||||||
|
'SynBass5',
|
||||||
|
'SynBass6',
|
||||||
|
'SynBass7',
|
||||||
|
'SynWave1',
|
||||||
|
'SynWave2',
|
||||||
|
'SynWave3',
|
||||||
|
'SynWave4',
|
||||||
|
'SynWave5',
|
||||||
|
'SynWave6',
|
||||||
|
'SynWave7',
|
||||||
|
'SynWave8',
|
||||||
|
'SynWave9',
|
||||||
|
'5thWave1',
|
||||||
|
'5thWave2',
|
||||||
|
'5thWave3',
|
||||||
|
'Digi1',
|
||||||
|
'Digi2',
|
||||||
|
'Digi3',
|
||||||
|
'Digi4',
|
||||||
|
'Digi5',
|
||||||
|
'Digi6',
|
||||||
|
'Digi7',
|
||||||
|
'Digi8',
|
||||||
|
'Endless',
|
||||||
|
'E.Piano1',
|
||||||
|
'E.Piano2',
|
||||||
|
'E.Piano3',
|
||||||
|
'E.Piano4',
|
||||||
|
'Organ1',
|
||||||
|
'Organ2',
|
||||||
|
'Organ3',
|
||||||
|
'Organ4',
|
||||||
|
'Organ5',
|
||||||
|
'Organ6',
|
||||||
|
'Organ7',
|
||||||
|
'Clav1',
|
||||||
|
'Clav2',
|
||||||
|
'Guitar1',
|
||||||
|
'Guitar2',
|
||||||
|
'Guitar3',
|
||||||
|
'Bass1',
|
||||||
|
'Bass2',
|
||||||
|
'Bass3',
|
||||||
|
'Bass4',
|
||||||
|
'Bass5',
|
||||||
|
'Bell1',
|
||||||
|
'Bell2',
|
||||||
|
'Bell3',
|
||||||
|
'Bell4',
|
||||||
|
'Voice1',
|
||||||
|
'Voice2',
|
||||||
|
'Voice3',
|
||||||
|
'Voice4'
|
||||||
|
),
|
||||||
|
waveMod: object[`t${num}_osc_1_ctrl_1`],
|
||||||
|
lfoMod: object[`t${num}_osc_1_ctrl_2`],
|
||||||
|
},
|
||||||
|
[`vco${num * 2}`]: {
|
||||||
|
id: `vco${num * 2}`,
|
||||||
|
waveform: returnFromList(
|
||||||
|
object[`t${num}_osc_2_wave`],
|
||||||
|
'Saw',
|
||||||
|
'Square',
|
||||||
|
'Triangle'
|
||||||
|
),
|
||||||
|
dwgsOrModType: returnFromList(
|
||||||
|
object[`t${num}_osc_2_mod_select`],
|
||||||
|
'Off',
|
||||||
|
'Ring',
|
||||||
|
'Sync',
|
||||||
|
'RingSync'
|
||||||
|
),
|
||||||
|
waveMod: object[`t${num}_osc_2_semitone`],
|
||||||
|
lfoMod: object[`t${num}_osc_2_tune`],
|
||||||
|
},
|
||||||
|
[`mixer${num}`]: {
|
||||||
|
id: `mixer${num}`,
|
||||||
|
vol1: object[`t${num}_osc_1_level`],
|
||||||
|
vol2: object[`t${num}_osc_2_level`],
|
||||||
|
vol3: object[`t${num}_noise_level`],
|
||||||
|
},
|
||||||
|
[`pitch${num}`]: {
|
||||||
|
id: `pitch${num}`,
|
||||||
|
voiceAssign: returnFromList(
|
||||||
|
object[`t${num}_assign_mode`],
|
||||||
|
'Mono',
|
||||||
|
'Poly',
|
||||||
|
'Unison'
|
||||||
|
),
|
||||||
|
triggerMode: returnFromList(
|
||||||
|
object[`t${num}_trigger_mode`],
|
||||||
|
'Single',
|
||||||
|
'Multiple'
|
||||||
|
),
|
||||||
|
transpose: object[`t${num}_transpose`] - 64,
|
||||||
|
tune: object[`t${num}_tune`] - 64,
|
||||||
|
unisonDetune: object[`t${num}_unison_detune`],
|
||||||
|
portamento: object[`t${num}_portamento`],
|
||||||
|
bendRange: object[`t${num}_bend_range`] - 64,
|
||||||
|
vibratoIntensity: object[`t${num}_vibrato_int`] - 64,
|
||||||
|
},
|
||||||
|
[`filter${num}`]: {
|
||||||
|
id: `filter${num}`,
|
||||||
|
filterType: returnFromList(
|
||||||
|
object[`t${num}_filter_type`],
|
||||||
|
'-24db Low Pass',
|
||||||
|
'-12db Low Pass',
|
||||||
|
'Band Pass',
|
||||||
|
'High Pass'
|
||||||
|
),
|
||||||
|
cutoff: object[`t${num}_filter_cutoff`],
|
||||||
|
resonance: object[`t${num}_filter_resonance`],
|
||||||
|
egIntensity: object[`t${num}_filter_eg_intensity`] - 64,
|
||||||
|
keyboardTrack: object[`t${num}_filter_key_track`] - 64,
|
||||||
|
},
|
||||||
|
[`amp${num}`]: {
|
||||||
|
id: `amp${num}`,
|
||||||
|
ampLevel: object[`t${num}_amp_level`],
|
||||||
|
panpot: object[`t${num}_amp_panpot`],
|
||||||
|
ampTrack: object[`t${num}_amp_key_track`] - 64,
|
||||||
|
distortion: object[`t${num}_amp_distortion`],
|
||||||
|
},
|
||||||
|
[`eg${timbre2 ? num + 1 : num}`]: {
|
||||||
|
id: `eg${timbre2 ? num + 1 : num}`,
|
||||||
|
attack: object[`t${num}_eg_1_attack`],
|
||||||
|
decay: object[`t${num}_eg_1_decay`],
|
||||||
|
sustain: object[`t${num}_eg_1_sustain`],
|
||||||
|
release: object[`t${num}_eg_1_release`],
|
||||||
|
egReset: object[`t${num}_eg_1_reset`],
|
||||||
|
},
|
||||||
|
[`eg${num * 2}`]: {
|
||||||
|
id: `eg${num * 2}`,
|
||||||
|
attack: object[`t${num}_eg_2_attack`],
|
||||||
|
decay: object[`t${num}_eg_2_decay`],
|
||||||
|
sustain: object[`t${num}_eg_2_sustain`],
|
||||||
|
release: object[`t${num}_eg_2_release`],
|
||||||
|
egReset: object[`t${num}_eg_2_reset`],
|
||||||
|
},
|
||||||
|
[`lfo${timbre2 ? num + 1 : num}`]: {
|
||||||
|
id: `lfo${timbre2 ? num + 1 : num}`,
|
||||||
|
waveform: returnFromList(
|
||||||
|
object[`t${num}_lfo_1_wave`],
|
||||||
|
'Saw',
|
||||||
|
'Square 1',
|
||||||
|
'Triangle',
|
||||||
|
'Sample & Hold'
|
||||||
|
),
|
||||||
|
keySync: returnFromList(
|
||||||
|
object[`t${num}_lfo_1_key_sync`],
|
||||||
|
'Off',
|
||||||
|
'Timbre',
|
||||||
|
'Voice'
|
||||||
|
),
|
||||||
|
tempoSync: object[`t${num}_lfo_1_tempo_sync`],
|
||||||
|
frequency: object[`t${num}_lfo_1_frequency`],
|
||||||
|
syncNote: returnFromList(
|
||||||
|
object[`t${num}_lfo_1_sync_note`],
|
||||||
|
'1/1',
|
||||||
|
'3/4',
|
||||||
|
'2/3',
|
||||||
|
'1/2',
|
||||||
|
'3/8',
|
||||||
|
'1/3',
|
||||||
|
'1/4',
|
||||||
|
'3/16',
|
||||||
|
'1/6',
|
||||||
|
'1/8',
|
||||||
|
'3/32',
|
||||||
|
'1/12',
|
||||||
|
'1/16',
|
||||||
|
'1/24',
|
||||||
|
'1/32'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
[`lfo${num * 2}`]: {
|
||||||
|
id: `lfo${num * 2}`,
|
||||||
|
waveform: returnFromList(
|
||||||
|
object[`t${num}_lfo_2_wave`],
|
||||||
|
'Saw',
|
||||||
|
'Square 2',
|
||||||
|
'Sine',
|
||||||
|
'Sample & Hold'
|
||||||
|
),
|
||||||
|
keySync: returnFromList(
|
||||||
|
object[`t${num}_lfo_2_key_sync`],
|
||||||
|
'Off',
|
||||||
|
'Timbre',
|
||||||
|
'Voice'
|
||||||
|
),
|
||||||
|
tempoSync: object[`t${num}_lfo_2_tempo_sync`],
|
||||||
|
frequency: object[`t${num}_lfo_2_frequency`],
|
||||||
|
syncNote: returnFromList(
|
||||||
|
object[`t${num}_lfo_2_sync_note`],
|
||||||
|
'1/1',
|
||||||
|
'3/4',
|
||||||
|
'2/3',
|
||||||
|
'1/2',
|
||||||
|
'3/8',
|
||||||
|
'1/3',
|
||||||
|
'1/4',
|
||||||
|
'3/16',
|
||||||
|
'1/6',
|
||||||
|
'1/8',
|
||||||
|
'3/32',
|
||||||
|
'1/12',
|
||||||
|
'1/16',
|
||||||
|
'1/24',
|
||||||
|
'1/32'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
[`patches${num}`]: {
|
||||||
|
id: `patches${num}`,
|
||||||
|
patch1: {
|
||||||
|
source: returnFromList(
|
||||||
|
object[`t${num}_patch_1_src`],
|
||||||
|
'Filter EG',
|
||||||
|
'Amp EG',
|
||||||
|
'LFO 1',
|
||||||
|
'LFO 2',
|
||||||
|
'Velocity',
|
||||||
|
'Keyboard Track',
|
||||||
|
'Pitch Bend',
|
||||||
|
'Mod Wheel'
|
||||||
|
),
|
||||||
|
destination: returnFromList(
|
||||||
|
object[`t${num}_patch_1_dest`],
|
||||||
|
'Pitch',
|
||||||
|
'OSC 2 Tune',
|
||||||
|
'OSC 1 Control 1',
|
||||||
|
'Noise Level',
|
||||||
|
'Cutoff',
|
||||||
|
'Amp',
|
||||||
|
'Pan',
|
||||||
|
'LFO 2 Frequency'
|
||||||
|
),
|
||||||
|
modIntensity: object[`t${num}_patch_1_intensity`],
|
||||||
|
},
|
||||||
|
patch2: {
|
||||||
|
source: returnFromList(
|
||||||
|
object[`t${num}_patch_2_src`],
|
||||||
|
'Filter EG',
|
||||||
|
'Amp EG',
|
||||||
|
'LFO 1',
|
||||||
|
'LFO 2',
|
||||||
|
'Velocity',
|
||||||
|
'Keyboard Track',
|
||||||
|
'Pitch Bend',
|
||||||
|
'Mod Wheel'
|
||||||
|
),
|
||||||
|
destination: returnFromList(
|
||||||
|
object[`t${num}_patch_2_dest`],
|
||||||
|
'Pitch',
|
||||||
|
'OSC 2 Tune',
|
||||||
|
'OSC 1 Control 1',
|
||||||
|
'Noise Level',
|
||||||
|
'Cutoff',
|
||||||
|
'Amp',
|
||||||
|
'Pan',
|
||||||
|
'LFO 2 Frequency'
|
||||||
|
),
|
||||||
|
modIntensity: object[`t${num}_patch_2_intensity`],
|
||||||
|
},
|
||||||
|
patch3: {
|
||||||
|
source: returnFromList(
|
||||||
|
object[`t${num}_patch_3_src`],
|
||||||
|
'Filter EG',
|
||||||
|
'Amp EG',
|
||||||
|
'LFO 1',
|
||||||
|
'LFO 2',
|
||||||
|
'Velocity',
|
||||||
|
'Keyboard Track',
|
||||||
|
'Pitch Bend',
|
||||||
|
'Mod Wheel'
|
||||||
|
),
|
||||||
|
destination: returnFromList(
|
||||||
|
object[`t${num}_patch_3_dest`],
|
||||||
|
'Pitch',
|
||||||
|
'OSC 2 Tune',
|
||||||
|
'OSC 1 Control 1',
|
||||||
|
'Noise Level',
|
||||||
|
'Cutoff',
|
||||||
|
'Amp',
|
||||||
|
'Pan',
|
||||||
|
'LFO 2 Frequency'
|
||||||
|
),
|
||||||
|
modIntensity: object[`t${num}_patch_3_intensity`],
|
||||||
|
},
|
||||||
|
patch4: {
|
||||||
|
source: returnFromList(
|
||||||
|
object[`t${num}_patch_4_src`],
|
||||||
|
'Filter EG',
|
||||||
|
'Amp EG',
|
||||||
|
'LFO 1',
|
||||||
|
'LFO 2',
|
||||||
|
'Velocity',
|
||||||
|
'Keyboard Track',
|
||||||
|
'Pitch Bend',
|
||||||
|
'Mod Wheel'
|
||||||
|
),
|
||||||
|
destination: returnFromList(
|
||||||
|
object[`t${num}_patch_4_dest`],
|
||||||
|
'Pitch',
|
||||||
|
'OSC 2 Tune',
|
||||||
|
'OSC 1 Control 1',
|
||||||
|
'Noise Level',
|
||||||
|
'Cutoff',
|
||||||
|
'Amp',
|
||||||
|
'Pan',
|
||||||
|
'LFO 2 Frequency'
|
||||||
|
),
|
||||||
|
modIntensity: object[`t${num}_patch_4_intensity`],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepare_vocoder(object) {
|
||||||
|
if (!object) return null;
|
||||||
|
return {
|
||||||
|
unusedVocoder: {
|
||||||
|
midiChannel: object.v_midi_channel,
|
||||||
|
egReset2: object.v_eg_2_reset,
|
||||||
|
keyPriority: object.v_key_priority,
|
||||||
|
filterEFSense: object.v_filter_e_f_sense,
|
||||||
|
ampVelSense: object.v_amp_vel_sense,
|
||||||
|
eg2attack: object.v_eg_2_attack,
|
||||||
|
eg2decay: object.v_eg_2_decay,
|
||||||
|
eg2sustain: object.v_eg_2_sustain,
|
||||||
|
eg2release: object.v_eg_2_release,
|
||||||
|
},
|
||||||
|
vco5: {
|
||||||
|
id: 'vco5',
|
||||||
|
waveform: returnFromList(
|
||||||
|
object.v_osc_wave,
|
||||||
|
'Saw',
|
||||||
|
'Square',
|
||||||
|
'Triangle',
|
||||||
|
'Sine',
|
||||||
|
'Vox Wave',
|
||||||
|
'DWGS',
|
||||||
|
'Noise',
|
||||||
|
'Audio In'
|
||||||
|
),
|
||||||
|
dwgsOrModType: returnFromList(
|
||||||
|
object.v_osc_dgws,
|
||||||
|
'SynSine1',
|
||||||
|
'SynSine2',
|
||||||
|
'SynSine3',
|
||||||
|
'SynSine4',
|
||||||
|
'SynSine5',
|
||||||
|
'SynSine6',
|
||||||
|
'SynSine7',
|
||||||
|
'SynBass1',
|
||||||
|
'SynBass2',
|
||||||
|
'SynBass3',
|
||||||
|
'SynBass4',
|
||||||
|
'SynBass5',
|
||||||
|
'SynBass6',
|
||||||
|
'SynBass7',
|
||||||
|
'SynWave1',
|
||||||
|
'SynWave2',
|
||||||
|
'SynWave3',
|
||||||
|
'SynWave4',
|
||||||
|
'SynWave5',
|
||||||
|
'SynWave6',
|
||||||
|
'SynWave7',
|
||||||
|
'SynWave8',
|
||||||
|
'SynWave9',
|
||||||
|
'5thWave1',
|
||||||
|
'5thWave2',
|
||||||
|
'5thWave3',
|
||||||
|
'Digi1',
|
||||||
|
'Digi2',
|
||||||
|
'Digi3',
|
||||||
|
'Digi4',
|
||||||
|
'Digi5',
|
||||||
|
'Digi6',
|
||||||
|
'Digi7',
|
||||||
|
'Digi8',
|
||||||
|
'Endless',
|
||||||
|
'E.Piano1',
|
||||||
|
'E.Piano2',
|
||||||
|
'E.Piano3',
|
||||||
|
'E.Piano4',
|
||||||
|
'Organ1',
|
||||||
|
'Organ2',
|
||||||
|
'Organ3',
|
||||||
|
'Organ4',
|
||||||
|
'Organ5',
|
||||||
|
'Organ6',
|
||||||
|
'Organ7',
|
||||||
|
'Clav1',
|
||||||
|
'Clav2',
|
||||||
|
'Guitar1',
|
||||||
|
'Guitar2',
|
||||||
|
'Guitar3',
|
||||||
|
'Bass1',
|
||||||
|
'Bass2',
|
||||||
|
'Bass3',
|
||||||
|
'Bass4',
|
||||||
|
'Bass5',
|
||||||
|
'Bell1',
|
||||||
|
'Bell2',
|
||||||
|
'Bell3',
|
||||||
|
'Bell4',
|
||||||
|
'Voice1',
|
||||||
|
'Voice2',
|
||||||
|
'Voice3',
|
||||||
|
'Voice4'
|
||||||
|
),
|
||||||
|
waveMod: object.v_osc_ctrl_1,
|
||||||
|
lfoMod: object.v_osc_ctrl_1,
|
||||||
|
},
|
||||||
|
audioIn: {
|
||||||
|
id: 'audioIn',
|
||||||
|
gate: object.v_audio_in_gate_sense,
|
||||||
|
threshold: object.v_audio_in_threshold,
|
||||||
|
hpfLevel: object.v_audio_in_hpf_level,
|
||||||
|
hpfGate: object.v_audio_in_hpf_gate,
|
||||||
|
},
|
||||||
|
mixer3: {
|
||||||
|
id: 'mixer3',
|
||||||
|
vol1: object.v_osc_level,
|
||||||
|
vol2: object.v_ext_level,
|
||||||
|
vol3: object.v_noise_level,
|
||||||
|
},
|
||||||
|
pitch3: {
|
||||||
|
id: 'pitch3',
|
||||||
|
voiceAssign: returnFromList(
|
||||||
|
object.v_assign_mode,
|
||||||
|
'Mono',
|
||||||
|
'Poly',
|
||||||
|
'Unison'
|
||||||
|
),
|
||||||
|
triggerMode: returnFromList(object.v_trigger_mode, 'Single', 'Multiple'),
|
||||||
|
transpose: object.v_transpose - 64,
|
||||||
|
tune: object.v_tune - 64,
|
||||||
|
unisonDetune: object.v_unison_detune,
|
||||||
|
portamento: object.v_portamento_time,
|
||||||
|
bendRange: object.v_bend_range - 64,
|
||||||
|
vibratoIntensity: object.v_vibrato_int - 64,
|
||||||
|
},
|
||||||
|
filter3: {
|
||||||
|
id: 'filter',
|
||||||
|
shift: returnFromList(object.v_filter_shift, 0, 1, 2, -1, -2),
|
||||||
|
cutoff: object.v_filter_cutoff,
|
||||||
|
resonance: object.v_filter_resonance,
|
||||||
|
efSense: object.v_filter_e_f_sense,
|
||||||
|
},
|
||||||
|
fcMod: {
|
||||||
|
id: 'fcMod',
|
||||||
|
source: returnFromList(
|
||||||
|
object.v_filter_mod_source,
|
||||||
|
'---',
|
||||||
|
'Amp EG',
|
||||||
|
'LFO 1',
|
||||||
|
'LFO 2',
|
||||||
|
'Velocity',
|
||||||
|
'Keyboard Track',
|
||||||
|
'Pitch Bend',
|
||||||
|
'Mod Wheel'
|
||||||
|
),
|
||||||
|
intensity: object.v_filter_intensity,
|
||||||
|
},
|
||||||
|
eg5: {
|
||||||
|
id: 'eg5',
|
||||||
|
attack: object.v_eg_1_attack,
|
||||||
|
decay: object.v_eg_1_decay,
|
||||||
|
sustain: object.v_eg_1_sustain,
|
||||||
|
release: object.v_eg_1_release,
|
||||||
|
egReset: object.v_eg_1_reset,
|
||||||
|
},
|
||||||
|
amp3: {
|
||||||
|
id: 'amp3',
|
||||||
|
ampLevel: object.v_amp_level,
|
||||||
|
directLevel: object.v_amp_direct_level,
|
||||||
|
ampTrack: object.v_amp_key_track - 64,
|
||||||
|
distortion: object.v_amp_direction,
|
||||||
|
},
|
||||||
|
lfo5: {
|
||||||
|
id: 'lfo5',
|
||||||
|
waveform: returnFromList(
|
||||||
|
object.v_lfo_1_wave,
|
||||||
|
'Saw',
|
||||||
|
'Square 1',
|
||||||
|
'Triangle',
|
||||||
|
'Sample & Hold'
|
||||||
|
),
|
||||||
|
keySync: returnFromList(
|
||||||
|
object.v_lfo_1_key_sync,
|
||||||
|
'Off',
|
||||||
|
'Timbre',
|
||||||
|
'Voice'
|
||||||
|
),
|
||||||
|
tempoSync: object.v_lfo_1_tempo_sync,
|
||||||
|
frequency: object.v_lfo_1_frequency,
|
||||||
|
syncNote: returnFromList(
|
||||||
|
object.v_lfo_1_sync_note,
|
||||||
|
'1/1',
|
||||||
|
'3/4',
|
||||||
|
'2/3',
|
||||||
|
'1/2',
|
||||||
|
'3/8',
|
||||||
|
'1/3',
|
||||||
|
'1/4',
|
||||||
|
'3/16',
|
||||||
|
'1/6',
|
||||||
|
'1/8',
|
||||||
|
'3/32',
|
||||||
|
'1/12',
|
||||||
|
'1/16',
|
||||||
|
'1/24',
|
||||||
|
'1/32'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
lfo6: {
|
||||||
|
id: 'lfo6',
|
||||||
|
waveform: returnFromList(
|
||||||
|
object.v_lfo_2_wave,
|
||||||
|
'Saw',
|
||||||
|
'Square 2',
|
||||||
|
'Sine',
|
||||||
|
'Sample & Hold'
|
||||||
|
),
|
||||||
|
keySync: returnFromList(
|
||||||
|
object.v_lfo_2_key_sync,
|
||||||
|
'Off',
|
||||||
|
'Timbre',
|
||||||
|
'Voice'
|
||||||
|
),
|
||||||
|
tempoSync: object.v_lfo_2_tempo_sync,
|
||||||
|
frequency: object.v_lfo_2_frequency,
|
||||||
|
syncNote: returnFromList(
|
||||||
|
object.v_lfo_2_sync_note,
|
||||||
|
'1/1',
|
||||||
|
'3/4',
|
||||||
|
'2/3',
|
||||||
|
'1/2',
|
||||||
|
'3/8',
|
||||||
|
'1/3',
|
||||||
|
'1/4',
|
||||||
|
'3/16',
|
||||||
|
'1/6',
|
||||||
|
'1/8',
|
||||||
|
'3/32',
|
||||||
|
'1/12',
|
||||||
|
'1/16',
|
||||||
|
'1/24',
|
||||||
|
'1/32'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
levels: {
|
||||||
|
id: 'levels',
|
||||||
|
level1: object.v_level_1,
|
||||||
|
level2: object.v_level_2,
|
||||||
|
level3: object.v_level_3,
|
||||||
|
level4: object.v_level_4,
|
||||||
|
level5: object.v_level_5,
|
||||||
|
level6: object.v_level_6,
|
||||||
|
level7: object.v_level_7,
|
||||||
|
level8: object.v_level_8,
|
||||||
|
},
|
||||||
|
pans: {
|
||||||
|
id: 'pans',
|
||||||
|
pan1: object.v_pan_1,
|
||||||
|
pan2: object.v_pan_2,
|
||||||
|
pan3: object.v_pan_3,
|
||||||
|
pan4: object.v_pan_4,
|
||||||
|
pan5: object.v_pan_5,
|
||||||
|
pan6: object.v_pan_6,
|
||||||
|
pan7: object.v_pan_7,
|
||||||
|
pan8: object.v_pan_8,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
import { getBits, toBitArray } from '../utils/functions';
|
||||||
|
|
||||||
|
export default function parse(sysex) {
|
||||||
|
const patch = build_patch(sysex.slice(0, 38));
|
||||||
|
switch (patch.voice_mode) {
|
||||||
|
default:
|
||||||
|
case 0:
|
||||||
|
return [
|
||||||
|
{ ...patch },
|
||||||
|
{ ...build_timbre(1, sysex.slice(38, 146)) },
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
];
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
return [
|
||||||
|
{ ...patch },
|
||||||
|
{ ...build_timbre(1, sysex.slice(38, 146)) },
|
||||||
|
{ ...build_timbre(2, sysex.slice(146, 254)) },
|
||||||
|
null,
|
||||||
|
];
|
||||||
|
case 3:
|
||||||
|
return [
|
||||||
|
{ ...patch },
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
{ ...build_vocoder(sysex.slice(38, 142)) },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function build_patch(patch) {
|
||||||
|
return {
|
||||||
|
/* BYTES 0, 11 NOT 0, 12 */
|
||||||
|
program_name: String.fromCharCode(...patch.slice(0, 12)),
|
||||||
|
/* BYTES 13 / 14 SKIPPED */
|
||||||
|
arp_trigger_length: patch[14],
|
||||||
|
arp_trigger_pattern: toBitArray(patch[15]),
|
||||||
|
voice_mode: getBits(patch[16], 4, 5),
|
||||||
|
scale_key: getBits(patch[17], 4, 7),
|
||||||
|
scale_type: getBits(patch[17], 0, 3),
|
||||||
|
/* BYTE 18 SKIPPED */
|
||||||
|
delay_sync: patch[19] & 0x80,
|
||||||
|
delay_timebase: getBits(patch[19], 0, 3),
|
||||||
|
delay_time: patch[20],
|
||||||
|
delay_depth: patch[21],
|
||||||
|
delay_type: patch[22],
|
||||||
|
mod_lfo_speed: patch[23],
|
||||||
|
mod_depth: patch[24],
|
||||||
|
mod_type: patch[25],
|
||||||
|
eq_hi_freq: patch[26],
|
||||||
|
eq_hi_gain: patch[27],
|
||||||
|
eq_low_freq: patch[28],
|
||||||
|
eq_low_gain: patch[29],
|
||||||
|
arp_tempo: patch.slice(30, 32).reduce((acc, curr) => acc + curr),
|
||||||
|
arp_on_off: patch[32] & 0x80,
|
||||||
|
arp_latch: patch[32] & 0x40,
|
||||||
|
arp_target: getBits(patch[32], 4, 5),
|
||||||
|
arp_key_sync: patch[32] & 0x01,
|
||||||
|
arp_type: getBits(patch[33], 0, 3),
|
||||||
|
arp_range: getBits(patch[33], 4, 7),
|
||||||
|
arp_gate_time: patch[34],
|
||||||
|
arp_resolution: patch[35],
|
||||||
|
arp_swing: patch[36],
|
||||||
|
keyboard_octave: patch[37],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function build_timbre(num, timbre) {
|
||||||
|
return {
|
||||||
|
[`t${num}_midi_channel`]: timbre[0],
|
||||||
|
[`t${num}_assign_mode`]: getBits(timbre[1], 6, 7),
|
||||||
|
[`t${num}_eg_2_reset`]: timbre[1] & 0x20,
|
||||||
|
[`t${num}_eg_1_reset`]: timbre[1] & 0x10,
|
||||||
|
[`t${num}_trigger_mode`]: timbre[1] & 0x08,
|
||||||
|
[`t${num}_key_priority`]: getBits(timbre[1], 0, 1),
|
||||||
|
[`t${num}_unison_detune`]: timbre[2],
|
||||||
|
[`t${num}_tune`]: timbre[3],
|
||||||
|
[`t${num}_bend_range`]: timbre[4],
|
||||||
|
[`t${num}_transpose`]: timbre[5],
|
||||||
|
[`t${num}_vibrato_int`]: timbre[6],
|
||||||
|
[`t${num}_osc_1_wave`]: timbre[7],
|
||||||
|
[`t${num}_osc_1_ctrl_1`]: timbre[8],
|
||||||
|
[`t${num}_osc_1_ctrl_2`]: timbre[9],
|
||||||
|
[`t${num}_osc_1_dgws`]: timbre[10],
|
||||||
|
/* SKIP BYTE 11 */
|
||||||
|
[`t${num}_osc_2_mod_select`]: getBits(timbre[12], 4, 5),
|
||||||
|
[`t${num}_osc_2_wave`]: getBits(timbre[12], 0, 1),
|
||||||
|
[`t${num}_osc_2_semitone`]: timbre[13],
|
||||||
|
[`t${num}_osc_2_tune`]: timbre[14],
|
||||||
|
[`t${num}_portamento`]: getBits(timbre[15], 0, 6),
|
||||||
|
[`t${num}_osc_1_level`]: timbre[16],
|
||||||
|
[`t${num}_osc_2_level`]: timbre[17],
|
||||||
|
[`t${num}_noise_level`]: timbre[18],
|
||||||
|
[`t${num}_filter_type`]: timbre[19],
|
||||||
|
[`t${num}_filter_cutoff`]: timbre[20],
|
||||||
|
[`t${num}_filter_resonance`]: timbre[21],
|
||||||
|
[`t${num}_filter_eg_intensity`]: timbre[22],
|
||||||
|
[`t${num}_filter_vel_sense`]: timbre[23],
|
||||||
|
[`t${num}_filter_key_track`]: timbre[24],
|
||||||
|
[`t${num}_amp_level`]: timbre[25],
|
||||||
|
[`t${num}_amp_panpot`]: timbre[26],
|
||||||
|
[`t${num}_amp_switch`]: timbre[27] & 0x40,
|
||||||
|
[`t${num}_amp_distortion`]: timbre[27] & 0x01,
|
||||||
|
[`t${num}_amp_vel_sense`]: timbre[28],
|
||||||
|
[`t${num}_amp_key_track`]: timbre[29],
|
||||||
|
[`t${num}_eg_1_attack`]: timbre[30],
|
||||||
|
[`t${num}_eg_1_decay`]: timbre[31],
|
||||||
|
[`t${num}_eg_1_sustain`]: timbre[32],
|
||||||
|
[`t${num}_eg_1_release`]: timbre[33],
|
||||||
|
[`t${num}_eg_2_attack`]: timbre[34],
|
||||||
|
[`t${num}_eg_2_decay`]: timbre[35],
|
||||||
|
[`t${num}_eg_2_sustain`]: timbre[36],
|
||||||
|
[`t${num}_eg_2_release`]: timbre[37],
|
||||||
|
[`t${num}_lfo_1_key_sync`]: getBits(timbre[38], 4, 5),
|
||||||
|
[`t${num}_lfo_1_wave`]: getBits(timbre[38], 0, 1),
|
||||||
|
[`t${num}_lfo_1_frequency`]: timbre[39],
|
||||||
|
[`t${num}_lfo_1_tempo_sync`]: timbre[40] & 0x80,
|
||||||
|
[`t${num}_lfo_1_sync_note`]: getBits(timbre[40], 0, 4),
|
||||||
|
[`t${num}_lfo_2_key_sync`]: getBits(timbre[41], 4, 5),
|
||||||
|
[`t${num}_lfo_2_wave`]: getBits(timbre[41], 0, 1),
|
||||||
|
[`t${num}_lfo_2_frequency`]: timbre[42],
|
||||||
|
[`t${num}_lfo_2_tempo_sync`]: timbre[43] & 0x80,
|
||||||
|
[`t${num}_lfo_2_sync_note`]: getBits(timbre[43], 0, 4),
|
||||||
|
[`t${num}_patch_1_dest`]: getBits(timbre[44], 4, 7),
|
||||||
|
[`t${num}_patch_1_src`]: getBits(timbre[44], 0, 3),
|
||||||
|
[`t${num}_patch_1_intensity`]: timbre[45],
|
||||||
|
[`t${num}_patch_2_dest`]: getBits(timbre[46], 4, 7),
|
||||||
|
[`t${num}_patch_2_src`]: getBits(timbre[46], 0, 3),
|
||||||
|
[`t${num}_patch_2_intensity`]: timbre[47],
|
||||||
|
[`t${num}_patch_3_dest`]: getBits(timbre[48], 4, 7),
|
||||||
|
[`t${num}_patch_3_src`]: getBits(timbre[48], 0, 3),
|
||||||
|
[`t${num}_patch_3_intensity`]: timbre[49],
|
||||||
|
[`t${num}_patch_4_dest`]: getBits(timbre[50], 4, 7),
|
||||||
|
[`t${num}_patch_4_src`]: getBits(timbre[50], 0, 3),
|
||||||
|
[`t${num}_patch_4_intensity`]: timbre[51],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function build_vocoder(vocoder) {
|
||||||
|
return {
|
||||||
|
v_midi_channel: vocoder[0],
|
||||||
|
v_assign_mode: getBits(vocoder[1], 6, 7),
|
||||||
|
v_eg_2_reset: vocoder[1] & 0x20,
|
||||||
|
v_eg_1_reset: vocoder[1] & 0x10,
|
||||||
|
v_trigger_mode: vocoder[1] & 0x08,
|
||||||
|
v_key_priority: getBits(vocoder[1], 0, 1),
|
||||||
|
v_unison_detune: vocoder[2],
|
||||||
|
v_tune: vocoder[3],
|
||||||
|
v_bend_range: vocoder[4],
|
||||||
|
v_transpose: vocoder[5],
|
||||||
|
v_vibrato_int: vocoder[6],
|
||||||
|
v_osc_wave: vocoder[7],
|
||||||
|
v_osc_ctrl_1: vocoder[8],
|
||||||
|
v_osc_ctrl_2: vocoder[9],
|
||||||
|
v_osc_dgws: vocoder[10],
|
||||||
|
/* BYTE 11 SKIPPED */
|
||||||
|
v_audio_in_hpf_gate: vocoder[12] & 0x01,
|
||||||
|
/* BYTE 13 SKIPPED */
|
||||||
|
v_portamento_time: getBits(vocoder[14], 0, 6),
|
||||||
|
v_osc_level: vocoder[15],
|
||||||
|
v_ext_level: vocoder[16],
|
||||||
|
v_noise_level: vocoder[17],
|
||||||
|
v_audio_in_hpf_level: vocoder[18],
|
||||||
|
v_audio_in_gate_sense: vocoder[19],
|
||||||
|
v_audio_in_threshold: vocoder[20],
|
||||||
|
v_filter_shift: vocoder[21],
|
||||||
|
v_filter_cutoff: vocoder[22],
|
||||||
|
v_filter_resonance: vocoder[23],
|
||||||
|
v_filter_mod_source: vocoder[24],
|
||||||
|
v_filter_intensity: vocoder[25],
|
||||||
|
v_filter_e_f_sense: vocoder[26],
|
||||||
|
v_amp_level: vocoder[27],
|
||||||
|
v_amp_direct_level: vocoder[28],
|
||||||
|
v_amp_distortion: vocoder[29] & 0x01,
|
||||||
|
v_amp_vel_sense: vocoder[30],
|
||||||
|
v_amp_key_track: vocoder[31],
|
||||||
|
v_eg_1_attack: vocoder[32],
|
||||||
|
v_eg_1_decay: vocoder[33],
|
||||||
|
v_eg_1_sustain: vocoder[34],
|
||||||
|
v_eg_1_release: vocoder[35],
|
||||||
|
v_eg_2_attack: vocoder[36],
|
||||||
|
v_eg_2_decay: vocoder[37],
|
||||||
|
v_eg_2_sustain: vocoder[38],
|
||||||
|
v_eg_2_release: vocoder[39],
|
||||||
|
v_lfo_1_key_sync: getBits(vocoder[40], 4, 5),
|
||||||
|
v_lfo_1_wave: getBits(vocoder[40], 0, 1),
|
||||||
|
v_lfo_1_frequency: vocoder[41],
|
||||||
|
v_lfo_1_tempo_sync: vocoder[42] & 0x80,
|
||||||
|
v_lfo_1_sync_note: getBits(vocoder[42], 0, 4),
|
||||||
|
v_lfo_2_key_sync: getBits(vocoder[43], 4, 5),
|
||||||
|
v_lfo_2_wave: getBits(vocoder[43], 0, 1),
|
||||||
|
v_lfo_2_frequency: vocoder[44],
|
||||||
|
v_lfo_2_tempo_sync: vocoder[45] & 0x80,
|
||||||
|
v_lfo_2_sync_note: getBits(vocoder[45], 0, 4),
|
||||||
|
v_level_1: vocoder[46],
|
||||||
|
v_level_2: vocoder[47],
|
||||||
|
v_level_3: vocoder[48],
|
||||||
|
v_level_4: vocoder[49],
|
||||||
|
v_level_5: vocoder[50],
|
||||||
|
v_level_6: vocoder[51],
|
||||||
|
v_level_7: vocoder[52],
|
||||||
|
v_level_8: vocoder[53],
|
||||||
|
v_pan_1: vocoder[62],
|
||||||
|
v_pan_2: vocoder[63],
|
||||||
|
v_pan_3: vocoder[64],
|
||||||
|
v_pan_4: vocoder[65],
|
||||||
|
v_pan_5: vocoder[66],
|
||||||
|
v_pan_6: vocoder[67],
|
||||||
|
v_pan_7: vocoder[68],
|
||||||
|
v_pan_8: vocoder[69],
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,732 @@
|
|||||||
|
import { returnValue } from '../utils/functions';
|
||||||
|
|
||||||
|
export default function saveStoreState(state) {
|
||||||
|
switch (state.patch.mode) {
|
||||||
|
default:
|
||||||
|
case 'Single':
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
...state_patch({
|
||||||
|
unused: { ...state.unusedPatch },
|
||||||
|
patch: { ...state.patch },
|
||||||
|
arpeggA: { ...state.arpeggA },
|
||||||
|
arpeggB: { ...state.arpeggB },
|
||||||
|
delay: { ...state.delay },
|
||||||
|
modfx: { ...state.modfx },
|
||||||
|
eq: { ...state.eq },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...state_timbre({
|
||||||
|
unused: { ...state.unusedTimbre1 },
|
||||||
|
vco1: { ...state.vco1 },
|
||||||
|
vco2: { ...state.vco2 },
|
||||||
|
mixer: { ...state.mixer1 },
|
||||||
|
pitch: { ...state.pitch1 },
|
||||||
|
filter: { ...state.filter1 },
|
||||||
|
amp: { ...state.amp1 },
|
||||||
|
eg1: { ...state.eg1 },
|
||||||
|
eg2: { ...state.eg2 },
|
||||||
|
lfo1: { ...state.lfo1 },
|
||||||
|
lfo2: { ...state.lfo2 },
|
||||||
|
patches: { ...state.patches1 },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
];
|
||||||
|
case 'Multiple':
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
...state_patch({
|
||||||
|
unused: { ...state.unusedPatch },
|
||||||
|
patch: { ...state.Patch },
|
||||||
|
arpeggA: { ...state.arpeggA },
|
||||||
|
arpeggB: { ...state.arpeggB },
|
||||||
|
delay: { ...state.delay },
|
||||||
|
modfx: { ...state.modfx },
|
||||||
|
eq: { ...state.eq },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...state_timbre({
|
||||||
|
unused: { ...state.unusedTimbre1 },
|
||||||
|
vco1: { ...state.vco1 },
|
||||||
|
vco2: { ...state.vco2 },
|
||||||
|
mixer: { ...state.mixer1 },
|
||||||
|
pitch: { ...state.pitch1 },
|
||||||
|
filter: { ...state.filter1 },
|
||||||
|
amp: { ...state.amp1 },
|
||||||
|
eg1: { ...state.eg1 },
|
||||||
|
eg2: { ...state.eg2 },
|
||||||
|
lfo1: { ...state.lfo1 },
|
||||||
|
lfo2: { ...state.lfo2 },
|
||||||
|
patches: { ...state.patches1 },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...state_timbre(
|
||||||
|
{
|
||||||
|
unused: { ...state.unusedTimbre2 },
|
||||||
|
vco1: { ...state.vco3 },
|
||||||
|
vco2: { ...state.vco4 },
|
||||||
|
mixer: { ...state.mixer2 },
|
||||||
|
pitch: { ...state.pitch2 },
|
||||||
|
filter: { ...state.filter2 },
|
||||||
|
amp: { ...state.amp2 },
|
||||||
|
eg1: { ...state.eg3 },
|
||||||
|
eg2: { ...state.eg4 },
|
||||||
|
lfo1: { ...state.lfo3 },
|
||||||
|
lfo2: { ...state.lfo4 },
|
||||||
|
patches: { ...state.patches2 },
|
||||||
|
},
|
||||||
|
true
|
||||||
|
),
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
];
|
||||||
|
case 'Vocoder':
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
...state_patch({
|
||||||
|
unused: { ...state.unusedPatch },
|
||||||
|
patch: { ...state.patch },
|
||||||
|
arpeggA: { ...state.arpeggA },
|
||||||
|
arpeggB: { ...state.arpeggB },
|
||||||
|
delay: { ...state.delay },
|
||||||
|
modfx: { ...state.modfx },
|
||||||
|
eq: { ...state.eq },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
...state_vocoder({
|
||||||
|
unused: { ...state.unusedVocoder },
|
||||||
|
vco1: { ...state.vco5 },
|
||||||
|
audioIn: { ...state.audioIn },
|
||||||
|
mixer: { ...state.mixer3 },
|
||||||
|
pitch: { ...state.pitch3 },
|
||||||
|
filter: { ...state.filter3 },
|
||||||
|
fcMod: { ...state.fcMod },
|
||||||
|
amp: { ...state.amp3 },
|
||||||
|
eg1: { ...state.eg5 },
|
||||||
|
lfo1: { ...state.lfo5 },
|
||||||
|
lfo2: { ...state.lfo6 },
|
||||||
|
levels: { ...state.levels },
|
||||||
|
pans: { ...state.pans },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function state_patch(state) {
|
||||||
|
return {
|
||||||
|
program_name: state.patch.name,
|
||||||
|
arp_trigger_length: state.arpeggB.lastStep,
|
||||||
|
arp_trigger_pattern: state.arpeggB.pattern,
|
||||||
|
voice_mode: returnValue(state.patch.mode, [
|
||||||
|
'Single',
|
||||||
|
null,
|
||||||
|
'Multiple',
|
||||||
|
'Vocoder',
|
||||||
|
]),
|
||||||
|
scale_key: state.unused.scaleKey,
|
||||||
|
scale_type: state.unused.scaleType,
|
||||||
|
arp_tempo: state.arpeggA.tempo,
|
||||||
|
arp_on_off: state.unused.arpSwitch,
|
||||||
|
arp_latch: state.arpeggB.latch,
|
||||||
|
arp_target: state.arpeggB.target,
|
||||||
|
arp_key_sync: state.arpeggB.keySync,
|
||||||
|
arp_type: returnValue(state.arpeggA.type, [
|
||||||
|
'Up',
|
||||||
|
'Down',
|
||||||
|
'Alternate 1',
|
||||||
|
'Alternate 2',
|
||||||
|
'Random',
|
||||||
|
'Trigger',
|
||||||
|
]),
|
||||||
|
arp_range: state.arpeggA.range,
|
||||||
|
arp_gate_time: state.arpeggA.gate,
|
||||||
|
arp_resolution: returnValue(state.arpeggA.resolution, [
|
||||||
|
'1/24',
|
||||||
|
'1/16',
|
||||||
|
'1/12',
|
||||||
|
'1/8',
|
||||||
|
'1/6',
|
||||||
|
'1/4',
|
||||||
|
]),
|
||||||
|
arp_swing: state.arpeggB.swing,
|
||||||
|
delay_sync: state.delay.tempoSync,
|
||||||
|
delay_timebase: returnValue(state.delay.syncNote, [
|
||||||
|
'1/32',
|
||||||
|
'1/24',
|
||||||
|
'1/16',
|
||||||
|
'1/12',
|
||||||
|
'3/32',
|
||||||
|
'1/8',
|
||||||
|
'1/6',
|
||||||
|
'3/16',
|
||||||
|
'1/4',
|
||||||
|
'1/3',
|
||||||
|
'3/8',
|
||||||
|
'1/2',
|
||||||
|
'2/3',
|
||||||
|
'3/4',
|
||||||
|
'1/1',
|
||||||
|
]),
|
||||||
|
delay_time: state.delay.delayTime,
|
||||||
|
delay_depth: state.delay.delayDepth,
|
||||||
|
delay_type: returnValue(state.delay.type, [
|
||||||
|
'Stereo Delay',
|
||||||
|
'Cross Delay',
|
||||||
|
'L/R Delay',
|
||||||
|
]),
|
||||||
|
mod_lfo_speed: state.modfx.lfoSpeed,
|
||||||
|
mod_depth: state.modfx.effectDepth,
|
||||||
|
mod_type: returnValue(state.modfx.type, [
|
||||||
|
'Chorus/Flanger',
|
||||||
|
'Ensemble',
|
||||||
|
'Phaser',
|
||||||
|
]),
|
||||||
|
eq_hi_freq: returnValue(state.eq.highFrequency, [
|
||||||
|
1.0,
|
||||||
|
1.25,
|
||||||
|
1.5,
|
||||||
|
1.75,
|
||||||
|
2.0,
|
||||||
|
2.25,
|
||||||
|
2.5,
|
||||||
|
2.75,
|
||||||
|
3.0,
|
||||||
|
3.25,
|
||||||
|
3.5,
|
||||||
|
3.75,
|
||||||
|
4.0,
|
||||||
|
4.25,
|
||||||
|
4.5,
|
||||||
|
4.75,
|
||||||
|
5.0,
|
||||||
|
5.25,
|
||||||
|
5.5,
|
||||||
|
5.75,
|
||||||
|
6.0,
|
||||||
|
7.0,
|
||||||
|
8.0,
|
||||||
|
9.0,
|
||||||
|
10.0,
|
||||||
|
11.0,
|
||||||
|
12.0,
|
||||||
|
14.0,
|
||||||
|
16.0,
|
||||||
|
18.0,
|
||||||
|
]),
|
||||||
|
eq_hi_gain: state.eq.highGain + 64,
|
||||||
|
eq_low_freq: returnValue(state.eq.lowFrequency, [
|
||||||
|
40,
|
||||||
|
50,
|
||||||
|
60,
|
||||||
|
80,
|
||||||
|
100,
|
||||||
|
120,
|
||||||
|
140,
|
||||||
|
160,
|
||||||
|
180,
|
||||||
|
200,
|
||||||
|
220,
|
||||||
|
240,
|
||||||
|
260,
|
||||||
|
280,
|
||||||
|
300,
|
||||||
|
320,
|
||||||
|
340,
|
||||||
|
360,
|
||||||
|
380,
|
||||||
|
400,
|
||||||
|
420,
|
||||||
|
440,
|
||||||
|
460,
|
||||||
|
480,
|
||||||
|
500,
|
||||||
|
600,
|
||||||
|
700,
|
||||||
|
800,
|
||||||
|
900,
|
||||||
|
1000,
|
||||||
|
]),
|
||||||
|
eq_low_gain: state.eq.lowGain + 64,
|
||||||
|
keyboard_octave: state.unused.keyboardOctave,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function state_timbre(state, timbre2 = false) {
|
||||||
|
const num = timbre2 ? 2 : 1;
|
||||||
|
return {
|
||||||
|
[`t${num}_midi_channel`]: state.unused.midiChannel,
|
||||||
|
[`t${num}_assign_mode`]: returnValue(state.pitch.voiceAssign, [
|
||||||
|
'Mono',
|
||||||
|
'Poly',
|
||||||
|
'Unison',
|
||||||
|
]),
|
||||||
|
[`t${num}_eg_2_reset`]: state.eg2.egReset,
|
||||||
|
[`t${num}_eg_1_reset`]: state.eg1.egReset,
|
||||||
|
[`t${num}_trigger_mode`]: returnValue(state.pitch.triggerMode, [
|
||||||
|
'Single',
|
||||||
|
'Multiple',
|
||||||
|
]),
|
||||||
|
[`t${num}_key_priority`]: state.unused.keyPriority,
|
||||||
|
[`t${num}_unison_detune`]: state.pitch.unisonDetune,
|
||||||
|
[`t${num}_tune`]: state.pitch.tune + 64,
|
||||||
|
[`t${num}_bend_range`]: state.pitch.bendRange + 64,
|
||||||
|
[`t${num}_transpose`]: state.pitch.transpose + 64,
|
||||||
|
[`t${num}_vibrato_int`]: state.pitch.vibratoIntensity + 64,
|
||||||
|
[`t${num}_osc_1_wave`]: returnValue(state.vco1.waveform, [
|
||||||
|
'Saw',
|
||||||
|
'Square',
|
||||||
|
'Triangle',
|
||||||
|
'Sine',
|
||||||
|
'Vox Wave',
|
||||||
|
'DWGS',
|
||||||
|
'Noise',
|
||||||
|
'Audio In',
|
||||||
|
]),
|
||||||
|
[`t${num}_osc_1_ctrl_1`]: state.vco1.waveMod,
|
||||||
|
[`t${num}_osc_1_ctrl_2`]: state.vco1.lfoMod,
|
||||||
|
[`t${num}_osc_1_dgws`]: returnValue(state.vco1.dwgsOrModType, [
|
||||||
|
'SynSine1',
|
||||||
|
'SynSine2',
|
||||||
|
'SynSine3',
|
||||||
|
'SynSine4',
|
||||||
|
'SynSine5',
|
||||||
|
'SynSine6',
|
||||||
|
'SynSine7',
|
||||||
|
'SynBass1',
|
||||||
|
'SynBass2',
|
||||||
|
'SynBass3',
|
||||||
|
'SynBass4',
|
||||||
|
'SynBass5',
|
||||||
|
'SynBass6',
|
||||||
|
'SynBass7',
|
||||||
|
'SynWave1',
|
||||||
|
'SynWave2',
|
||||||
|
'SynWave3',
|
||||||
|
'SynWave4',
|
||||||
|
'SynWave5',
|
||||||
|
'SynWave6',
|
||||||
|
'SynWave7',
|
||||||
|
'SynWave8',
|
||||||
|
'SynWave9',
|
||||||
|
'5thWave1',
|
||||||
|
'5thWave2',
|
||||||
|
'5thWave3',
|
||||||
|
'Digi1',
|
||||||
|
'Digi2',
|
||||||
|
'Digi3',
|
||||||
|
'Digi4',
|
||||||
|
'Digi5',
|
||||||
|
'Digi6',
|
||||||
|
'Digi7',
|
||||||
|
'Digi8',
|
||||||
|
'Endless',
|
||||||
|
'E.Piano1',
|
||||||
|
'E.Piano2',
|
||||||
|
'E.Piano3',
|
||||||
|
'E.Piano4',
|
||||||
|
'Organ1',
|
||||||
|
'Organ2',
|
||||||
|
'Organ3',
|
||||||
|
'Organ4',
|
||||||
|
'Organ5',
|
||||||
|
'Organ6',
|
||||||
|
'Organ7',
|
||||||
|
'Clav1',
|
||||||
|
'Clav2',
|
||||||
|
'Guitar1',
|
||||||
|
'Guitar2',
|
||||||
|
'Guitar3',
|
||||||
|
'Bass1',
|
||||||
|
'Bass2',
|
||||||
|
'Bass3',
|
||||||
|
'Bass4',
|
||||||
|
'Bass5',
|
||||||
|
'Bell1',
|
||||||
|
'Bell2',
|
||||||
|
'Bell3',
|
||||||
|
'Bell4',
|
||||||
|
'Voice1',
|
||||||
|
'Voice2',
|
||||||
|
'Voice3',
|
||||||
|
'Voice4',
|
||||||
|
]),
|
||||||
|
[`t${num}_osc_2_mod_select`]: returnValue(state.vco2.dwgsOrModType, [
|
||||||
|
'Off',
|
||||||
|
'Ring',
|
||||||
|
'Sync',
|
||||||
|
'RingSync',
|
||||||
|
]),
|
||||||
|
[`t${num}_osc_2_wave`]: returnValue(state.vco2.waveform, [
|
||||||
|
'Saw',
|
||||||
|
'Square',
|
||||||
|
'Triangle',
|
||||||
|
]),
|
||||||
|
[`t${num}_osc_2_semitone`]: state.vco2.waveMod,
|
||||||
|
[`t${num}_osc_2_tune`]: state.vco2.lfoMod,
|
||||||
|
[`t${num}_portamento`]: state.pitch.portamento,
|
||||||
|
[`t${num}_osc_1_level`]: state.mixer.vol1,
|
||||||
|
[`t${num}_osc_2_level`]: state.mixer.vol2,
|
||||||
|
[`t${num}_noise_level`]: state.mixer.vol3,
|
||||||
|
[`t${num}_filter_type`]: returnValue(state.filter.filterType, [
|
||||||
|
'-24db Low Pass',
|
||||||
|
'-12db Low Pass',
|
||||||
|
'Band Pass',
|
||||||
|
'High Pass',
|
||||||
|
]),
|
||||||
|
[`t${num}_filter_cutoff`]: state.filter.cutoff,
|
||||||
|
[`t${num}_filter_resonance`]: state.filter.resonance,
|
||||||
|
[`t${num}_filter_eg_intensity`]: state.filter.egIntensity + 64,
|
||||||
|
[`t${num}_filter_vel_sense`]: state.unused.filterVelSense,
|
||||||
|
[`t${num}_filter_key_track`]: state.filter.keyboardTrack + 64,
|
||||||
|
[`t${num}_amp_level`]: state.amp.ampLevel,
|
||||||
|
[`t${num}_amp_panpot`]: state.amp.panpot,
|
||||||
|
[`t${num}_amp_switch`]: state.unused.ampSwitch,
|
||||||
|
[`t${num}_amp_distortion`]: state.amp.distortion,
|
||||||
|
[`t${num}_amp_vel_sense`]: state.unused.ampVelSense,
|
||||||
|
[`t${num}_amp_key_track`]: state.amp.ampTrack + 64,
|
||||||
|
[`t${num}_eg_1_attack`]: state.eg1.attack,
|
||||||
|
[`t${num}_eg_1_decay`]: state.eg1.decay,
|
||||||
|
[`t${num}_eg_1_sustain`]: state.eg1.sustain,
|
||||||
|
[`t${num}_eg_1_release`]: state.eg1.release,
|
||||||
|
[`t${num}_eg_2_attack`]: state.eg2.attack,
|
||||||
|
[`t${num}_eg_2_decay`]: state.eg2.decay,
|
||||||
|
[`t${num}_eg_2_sustain`]: state.eg2.sustain,
|
||||||
|
[`t${num}_eg_2_release`]: state.eg2.release,
|
||||||
|
[`t${num}_lfo_1_key_sync`]: returnValue(state.lfo1.keySync, [
|
||||||
|
'Off',
|
||||||
|
'Timbre',
|
||||||
|
'Voice',
|
||||||
|
]),
|
||||||
|
[`t${num}_lfo_1_wave`]: returnValue(state.lfo1.waveform, [
|
||||||
|
'Saw',
|
||||||
|
'Square 1',
|
||||||
|
'Triangle',
|
||||||
|
'Sample & Hold',
|
||||||
|
]),
|
||||||
|
[`t${num}_lfo_1_frequency`]: state.lfo1.frequency,
|
||||||
|
[`t${num}_lfo_1_tempo_sync`]: state.lfo1.tempoSync,
|
||||||
|
[`t${num}_lfo_1_sync_note`]: returnValue(state.lfo1.syncNote, [
|
||||||
|
'1/1',
|
||||||
|
'3/4',
|
||||||
|
'2/3',
|
||||||
|
'1/2',
|
||||||
|
'3/8',
|
||||||
|
'1/3',
|
||||||
|
'1/4',
|
||||||
|
'3/16',
|
||||||
|
'1/6',
|
||||||
|
'1/8',
|
||||||
|
'3/32',
|
||||||
|
'1/12',
|
||||||
|
'1/16',
|
||||||
|
'1/24',
|
||||||
|
'1/32',
|
||||||
|
]),
|
||||||
|
[`t${num}_lfo_2_key_sync`]: returnValue(state.lfo2.keySync, [
|
||||||
|
'Off',
|
||||||
|
'Timbre',
|
||||||
|
'Voice',
|
||||||
|
]),
|
||||||
|
[`t${num}_lfo_2_wave`]: returnValue(state.lfo2.waveform, [
|
||||||
|
'Saw',
|
||||||
|
'Square 2',
|
||||||
|
'Sine',
|
||||||
|
'Sample & Hold',
|
||||||
|
]),
|
||||||
|
[`t${num}_lfo_2_frequency`]: state.lfo2.frequency,
|
||||||
|
[`t${num}_lfo_2_tempo_sync`]: state.lfo2.tempoSync,
|
||||||
|
[`t${num}_lfo_2_sync_note`]: returnValue(state.lfo2.syncNote, [
|
||||||
|
'1/1',
|
||||||
|
'3/4',
|
||||||
|
'2/3',
|
||||||
|
'1/2',
|
||||||
|
'3/8',
|
||||||
|
'1/3',
|
||||||
|
'1/4',
|
||||||
|
'3/16',
|
||||||
|
'1/6',
|
||||||
|
'1/8',
|
||||||
|
'3/32',
|
||||||
|
'1/12',
|
||||||
|
'1/16',
|
||||||
|
'1/24',
|
||||||
|
'1/32',
|
||||||
|
]),
|
||||||
|
[`t${num}_patch_1_dest`]: returnValue(state.patches.patch1.destination, [
|
||||||
|
'Pitch',
|
||||||
|
'OSC 2 Tune',
|
||||||
|
'OSC 1 Control 1',
|
||||||
|
'Noise Level',
|
||||||
|
'Cutoff',
|
||||||
|
'Amp',
|
||||||
|
'Pan',
|
||||||
|
'LFO 2 Frequency',
|
||||||
|
]),
|
||||||
|
[`t${num}_patch_1_src`]: returnValue(state.patches.patch1.source, [
|
||||||
|
'Filter EG',
|
||||||
|
'Amp EG',
|
||||||
|
'LFO 1',
|
||||||
|
'LFO 2',
|
||||||
|
'Velocity',
|
||||||
|
'Keyboard Track',
|
||||||
|
'Pitch Bend',
|
||||||
|
'Mod Wheel',
|
||||||
|
]),
|
||||||
|
[`t${num}_patch_1_intensity`]: state.patches.patch1.modIntensity,
|
||||||
|
[`t${num}_patch_2_dest`]: returnValue(state.patches.patch2.destination, [
|
||||||
|
'Pitch',
|
||||||
|
'OSC 2 Tune',
|
||||||
|
'OSC 1 Control 1',
|
||||||
|
'Noise Level',
|
||||||
|
'Cutoff',
|
||||||
|
'Amp',
|
||||||
|
'Pan',
|
||||||
|
'LFO 2 Frequency',
|
||||||
|
]),
|
||||||
|
[`t${num}_patch_2_src`]: returnValue(state.patches.patch2.source, [
|
||||||
|
'Filter EG',
|
||||||
|
'Amp EG',
|
||||||
|
'LFO 1',
|
||||||
|
'LFO 2',
|
||||||
|
'Velocity',
|
||||||
|
'Keyboard Track',
|
||||||
|
'Pitch Bend',
|
||||||
|
'Mod Wheel',
|
||||||
|
]),
|
||||||
|
[`t${num}_patch_2_intensity`]: state.patches.patch2.modIntensity,
|
||||||
|
[`t${num}_patch_3_dest`]: returnValue(state.patches.patch3.destination, [
|
||||||
|
'Pitch',
|
||||||
|
'OSC 2 Tune',
|
||||||
|
'OSC 1 Control 1',
|
||||||
|
'Noise Level',
|
||||||
|
'Cutoff',
|
||||||
|
'Amp',
|
||||||
|
'Pan',
|
||||||
|
'LFO 2 Frequency',
|
||||||
|
]),
|
||||||
|
[`t${num}_patch_3_src`]: returnValue(state.patches.patch3.source, [
|
||||||
|
'Filter EG',
|
||||||
|
'Amp EG',
|
||||||
|
'LFO 1',
|
||||||
|
'LFO 2',
|
||||||
|
'Velocity',
|
||||||
|
'Keyboard Track',
|
||||||
|
'Pitch Bend',
|
||||||
|
'Mod Wheel',
|
||||||
|
]),
|
||||||
|
[`t${num}_patch_3_intensity`]: state.patches.patch3.modIntensity,
|
||||||
|
[`t${num}_patch_4_dest`]: returnValue(state.patches.patch4.destination, [
|
||||||
|
'Pitch',
|
||||||
|
'OSC 2 Tune',
|
||||||
|
'OSC 1 Control 1',
|
||||||
|
'Noise Level',
|
||||||
|
'Cutoff',
|
||||||
|
'Amp',
|
||||||
|
'Pan',
|
||||||
|
'LFO 2 Frequency',
|
||||||
|
]),
|
||||||
|
[`t${num}_patch_4_src`]: returnValue(state.patches.patch4.source, [
|
||||||
|
'Filter EG',
|
||||||
|
'Amp EG',
|
||||||
|
'LFO 1',
|
||||||
|
'LFO 2',
|
||||||
|
'Velocity',
|
||||||
|
'Keyboard Track',
|
||||||
|
'Pitch Bend',
|
||||||
|
'Mod Wheel',
|
||||||
|
]),
|
||||||
|
[`t${num}_patch_4_intensity`]: state.patches.patch4.modIntensity,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function state_vocoder(state) {
|
||||||
|
return {
|
||||||
|
v_midi_channel: state.unused.midiChannel,
|
||||||
|
v_assign_mode: returnValue(state.pitch.assignMode, [
|
||||||
|
'Mono',
|
||||||
|
'Poly',
|
||||||
|
'Unison',
|
||||||
|
]),
|
||||||
|
v_eg_2_reset: state.unused.egReset2,
|
||||||
|
v_eg_1_reset: state.eg1.egReset,
|
||||||
|
v_trigger_mode: returnValue(state.pitch.triggerMode, [
|
||||||
|
'Single',
|
||||||
|
'Multiple',
|
||||||
|
]),
|
||||||
|
v_key_priority: state.unused.keyPriority,
|
||||||
|
v_unison_detune: state.pitch.unisonDetune,
|
||||||
|
v_tune: state.pitch.tune + 64,
|
||||||
|
v_bend_range: state.pitch.bendRange,
|
||||||
|
v_transpose: state.pitch.transpose + 64,
|
||||||
|
v_vibrato_int: state.pitch.vibratoIntensity,
|
||||||
|
v_osc_wave: returnValue(
|
||||||
|
state.vco1.waveform,
|
||||||
|
'Saw',
|
||||||
|
'Square',
|
||||||
|
'Triangle',
|
||||||
|
'Sine',
|
||||||
|
'Vox Wave',
|
||||||
|
'DWGS',
|
||||||
|
'Noise',
|
||||||
|
'Audio In'
|
||||||
|
),
|
||||||
|
v_osc_ctrl_1: state.vco1.waveMod,
|
||||||
|
v_osc_ctrl_2: state.vco1.lfoMod,
|
||||||
|
v_osc_dgws: returnValue(
|
||||||
|
state.vco1.dwgsOrModType,
|
||||||
|
'SynSine1',
|
||||||
|
'SynSine2',
|
||||||
|
'SynSine3',
|
||||||
|
'SynSine4',
|
||||||
|
'SynSine5',
|
||||||
|
'SynSine6',
|
||||||
|
'SynSine7',
|
||||||
|
'SynBass1',
|
||||||
|
'SynBass2',
|
||||||
|
'SynBass3',
|
||||||
|
'SynBass4',
|
||||||
|
'SynBass5',
|
||||||
|
'SynBass6',
|
||||||
|
'SynBass7',
|
||||||
|
'SynWave1',
|
||||||
|
'SynWave2',
|
||||||
|
'SynWave3',
|
||||||
|
'SynWave4',
|
||||||
|
'SynWave5',
|
||||||
|
'SynWave6',
|
||||||
|
'SynWave7',
|
||||||
|
'SynWave8',
|
||||||
|
'SynWave9',
|
||||||
|
'5thWave1',
|
||||||
|
'5thWave2',
|
||||||
|
'5thWave3',
|
||||||
|
'Digi1',
|
||||||
|
'Digi2',
|
||||||
|
'Digi3',
|
||||||
|
'Digi4',
|
||||||
|
'Digi5',
|
||||||
|
'Digi6',
|
||||||
|
'Digi7',
|
||||||
|
'Digi8',
|
||||||
|
'Endless',
|
||||||
|
'E.Piano1',
|
||||||
|
'E.Piano2',
|
||||||
|
'E.Piano3',
|
||||||
|
'E.Piano4',
|
||||||
|
'Organ1',
|
||||||
|
'Organ2',
|
||||||
|
'Organ3',
|
||||||
|
'Organ4',
|
||||||
|
'Organ5',
|
||||||
|
'Organ6',
|
||||||
|
'Organ7',
|
||||||
|
'Clav1',
|
||||||
|
'Clav2',
|
||||||
|
'Guitar1',
|
||||||
|
'Guitar2',
|
||||||
|
'Guitar3',
|
||||||
|
'Bass1',
|
||||||
|
'Bass2',
|
||||||
|
'Bass3',
|
||||||
|
'Bass4',
|
||||||
|
'Bass5',
|
||||||
|
'Bell1',
|
||||||
|
'Bell2',
|
||||||
|
'Bell3',
|
||||||
|
'Bell4',
|
||||||
|
'Voice1',
|
||||||
|
'Voice2',
|
||||||
|
'Voice3',
|
||||||
|
'Voice4'
|
||||||
|
),
|
||||||
|
v_audio_in_hpf_gate: state.audioIn.hpfGate,
|
||||||
|
v_portamento_time: state.pitch.portamento,
|
||||||
|
v_osc_level: state.mixer.vol1,
|
||||||
|
v_ext_level: state.mixer.vol2,
|
||||||
|
v_noise_level: state.mixer.vol3,
|
||||||
|
v_audio_in_hpf_level: state.audioIn.hpfLevel,
|
||||||
|
v_audio_in_gate_sense: state.audioIn.gate,
|
||||||
|
v_audio_in_threshold: state.audioIn.threshold,
|
||||||
|
v_filter_shift: returnValue(state.filter.shift, [0, 1, 2, -1, -2]),
|
||||||
|
v_filter_cutoff: state.filter.cutoff,
|
||||||
|
v_filter_resonance: state.filter.resonance,
|
||||||
|
v_filter_mod_source: returnValue(state.fcMod.source, [
|
||||||
|
'---',
|
||||||
|
'Amp EG',
|
||||||
|
'LFO 1',
|
||||||
|
'LFO 2',
|
||||||
|
'Velocity',
|
||||||
|
'Keyboard Track',
|
||||||
|
'Pitch Bend',
|
||||||
|
'Mod Wheel',
|
||||||
|
]),
|
||||||
|
v_filter_intensity: state.fcMod.intensity,
|
||||||
|
v_filter_e_f_sense: state.filter.efSense,
|
||||||
|
v_amp_level: state.amp.ampLevel,
|
||||||
|
v_amp_direct_level: state.amp.directLevel,
|
||||||
|
v_amp_distortion: state.amp.distortion,
|
||||||
|
v_amp_vel_sense: state.unused.ampVelSense,
|
||||||
|
v_amp_key_track: state.amp.keyTrack,
|
||||||
|
v_eg_1_attack: state.eg1.attack,
|
||||||
|
v_eg_1_decay: state.eg1.decay,
|
||||||
|
v_eg_1_sustain: state.eg1.sustain,
|
||||||
|
v_eg_1_release: state.eg1.release,
|
||||||
|
v_eg_2_attack: state.unused.eg2attack,
|
||||||
|
v_eg_2_decay: state.unused.eg2decay,
|
||||||
|
v_eg_2_sustain: state.unused.eg2sustain,
|
||||||
|
v_eg_2_release: state.unused.eg2release,
|
||||||
|
v_lfo_1_key_sync: returnValue(state.lfo1.keySync, [
|
||||||
|
'Off',
|
||||||
|
'Timbre',
|
||||||
|
'Voice',
|
||||||
|
]),
|
||||||
|
v_lfo_1_wave: returnValue(state.lfo1.waveform, [
|
||||||
|
'Saw',
|
||||||
|
'Square 1',
|
||||||
|
'Triangle',
|
||||||
|
'Sample & Hold',
|
||||||
|
]),
|
||||||
|
v_lfo_1_frequency: state.lfo1.frequency,
|
||||||
|
v_lfo_1_tempo_sync: state.lfo1.tempoSync,
|
||||||
|
v_lfo_1_sync_note: state.lfo1.syncNote,
|
||||||
|
v_lfo_2_key_sync: returnValue(state.lfo2.keySync, [
|
||||||
|
'Off',
|
||||||
|
'Timbre',
|
||||||
|
'Voice',
|
||||||
|
]),
|
||||||
|
v_lfo_2_wave: returnValue(state.lfo2.waveform, [
|
||||||
|
'Saw',
|
||||||
|
'Square 2',
|
||||||
|
'Sine',
|
||||||
|
'Sample & Hold',
|
||||||
|
]),
|
||||||
|
v_lfo_2_frequency: state.lfo2.frequency,
|
||||||
|
v_lfo_2_tempo_sync: state.lfo2.tempoSync,
|
||||||
|
v_lfo_2_sync_note: state.lfo2.syncNote,
|
||||||
|
v_level_1: state.levels.level1,
|
||||||
|
v_level_2: state.levels.level2,
|
||||||
|
v_level_3: state.levels.level3,
|
||||||
|
v_level_4: state.levels.level4,
|
||||||
|
v_level_5: state.levels.level5,
|
||||||
|
v_level_6: state.levels.level6,
|
||||||
|
v_level_7: state.levels.level7,
|
||||||
|
v_level_8: state.levels.level8,
|
||||||
|
v_pan_1: state.pans.pan1,
|
||||||
|
v_pan_2: state.pans.pan2,
|
||||||
|
v_pan_3: state.pans.pan3,
|
||||||
|
v_pan_4: state.pans.pan4,
|
||||||
|
v_pan_5: state.pans.pan5,
|
||||||
|
v_pan_6: state.pans.pan6,
|
||||||
|
v_pan_7: state.pans.pan7,
|
||||||
|
v_pan_8: state.pans.pan8,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export default function serialise(patch) {
|
||||||
|
serialise_patch(patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
function serialise_patch(patch) {}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { sysex_decode, sysex_check } from './utils/sysex';
|
||||||
|
import parse from './convert/parse';
|
||||||
|
import serialise from './convert/serialise';
|
||||||
|
import build_store from './convert/loadStore';
|
||||||
|
import saveStoreState from './convert/saveStore';
|
||||||
|
|
||||||
|
function return_store_from_file(file) {
|
||||||
|
sysex_check(file);
|
||||||
|
const parsed = parse(sysex_decode(file));
|
||||||
|
return build_store(...parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
function return_file_from_store(state) {
|
||||||
|
const simple_state = saveStoreState(state);
|
||||||
|
console.log(serialise(simple_state));
|
||||||
|
}
|
||||||
|
|
||||||
|
export { return_store_from_file, return_file_from_store };
|
||||||
@@ -1,257 +0,0 @@
|
|||||||
import { Parser } from 'binary-parser-encoder';
|
|
||||||
|
|
||||||
const timbre = new Parser()
|
|
||||||
.int8('midiChannel') // in 7 bits
|
|
||||||
.bit2('assignMode')
|
|
||||||
.bit1('eg2Reset')
|
|
||||||
.bit1('eg1Reset')
|
|
||||||
.bit1('triggerMode')
|
|
||||||
.bit2('keyPriority')
|
|
||||||
.uint8('unisonDetune')
|
|
||||||
.uint8('pitchTune')
|
|
||||||
.uint8('pitchBendRange')
|
|
||||||
.uint8('pitchTranspose')
|
|
||||||
.uint8('pitchVibratoInt')
|
|
||||||
.uint8('osc1Wave')
|
|
||||||
.uint8('osc1WaveCtrl1')
|
|
||||||
.uint8('osc1WaveCtrl2')
|
|
||||||
.uint8('osc1DWGSWave')
|
|
||||||
.skip(1)
|
|
||||||
.bit2('not-use1')
|
|
||||||
.bit2('osc2ModSelect')
|
|
||||||
.bit2('not-use2')
|
|
||||||
.bit2('osc2Wave')
|
|
||||||
.uint8('osc2Semitone')
|
|
||||||
.uint8('osc2Tune')
|
|
||||||
.bit1('not-use3')
|
|
||||||
.bit7('portamentoTime')
|
|
||||||
.uint8('osc1Level')
|
|
||||||
.uint8('osc2Level')
|
|
||||||
.uint8('noiseLevel')
|
|
||||||
.uint8('filterType')
|
|
||||||
.uint8('filterCutoff')
|
|
||||||
.uint8('filterResonance')
|
|
||||||
.uint8('filterEG1Intensity')
|
|
||||||
.uint8('filterVelocitySense')
|
|
||||||
.uint8('filterKeyboardTrack')
|
|
||||||
.uint8('ampLevel')
|
|
||||||
.uint8('ampPanpot')
|
|
||||||
.bit1('not-use4')
|
|
||||||
.bit1('ampSW')
|
|
||||||
.bit5('not-use5')
|
|
||||||
.bit1('ampDistortion')
|
|
||||||
.uint8('ampVelocitySense')
|
|
||||||
.uint8('ampKeyboardTrack')
|
|
||||||
.uint8('eg1Attack')
|
|
||||||
.uint8('eg1Decay')
|
|
||||||
.uint8('eg1Sustain')
|
|
||||||
.uint8('eg1Release')
|
|
||||||
.uint8('eg2Attack')
|
|
||||||
.uint8('eg2Decay')
|
|
||||||
.uint8('eg2Sustain')
|
|
||||||
.uint8('eg2Release')
|
|
||||||
.bit2('not-use6')
|
|
||||||
.bit2('lfo1KeySync')
|
|
||||||
.bit2('not-use7')
|
|
||||||
.bit2('lfo1Wave')
|
|
||||||
.uint8('lfo1Freq')
|
|
||||||
.bit1('lfo1TempoSync')
|
|
||||||
.bit2('not-use8')
|
|
||||||
.bit5('lfo1SyncNote')
|
|
||||||
.bit2('not-use9')
|
|
||||||
.bit2('lfo2KeySync')
|
|
||||||
.bit2('not-use10')
|
|
||||||
.bit2('lfo2Wave')
|
|
||||||
.uint8('lfo2Freq')
|
|
||||||
.bit1('lfo2TempoSync')
|
|
||||||
.bit2('not-use11')
|
|
||||||
.bit5('lfo2SyncNote')
|
|
||||||
.bit4('patch1Dest')
|
|
||||||
.bit4('patch1Src')
|
|
||||||
.uint8('patch1Intensity')
|
|
||||||
.bit4('patch2Dest')
|
|
||||||
.bit4('patch2Src')
|
|
||||||
.uint8('patch2Intensity')
|
|
||||||
.bit4('patch3Dest')
|
|
||||||
.bit4('patch3Src')
|
|
||||||
.uint8('patch3Intensity')
|
|
||||||
.bit4('patch4Dest')
|
|
||||||
.bit4('patch4Src')
|
|
||||||
.uint8('patch4Intensity');
|
|
||||||
|
|
||||||
const vocoder = new Parser()
|
|
||||||
.int8('midiChannel') // in 7 bits
|
|
||||||
.bit2('assignMode')
|
|
||||||
.bit1('eg2Reset')
|
|
||||||
.bit1('eg1Reset')
|
|
||||||
.bit1('triggerMode')
|
|
||||||
.bit2('keyPriority')
|
|
||||||
.uint8('unisonDetune')
|
|
||||||
.uint8('pitchTune')
|
|
||||||
.uint8('pitchBendRange')
|
|
||||||
.uint8('pitchTranspose')
|
|
||||||
.uint8('pitchVibratoInt')
|
|
||||||
.uint8('osc1Wave')
|
|
||||||
.uint8('osc1WaveCtrl1')
|
|
||||||
.uint8('osc1WaveCtrl2')
|
|
||||||
.uint8('osc1DWGSWave')
|
|
||||||
.skip(1) // dummy byte
|
|
||||||
.bit7('not-use17')
|
|
||||||
.bit1('HPF Gate')
|
|
||||||
.skip(1)
|
|
||||||
.bit1('not-use18')
|
|
||||||
.bit7('portamentoTime')
|
|
||||||
.uint8('osc1Level')
|
|
||||||
.uint8('ext1Level')
|
|
||||||
.uint8('noiseLevel')
|
|
||||||
.uint8('hpfLevel')
|
|
||||||
.uint8('gateSense')
|
|
||||||
.uint8('threshold')
|
|
||||||
.uint8('shift')
|
|
||||||
.uint8('cutoff')
|
|
||||||
.uint8('resonance')
|
|
||||||
.uint8('modSource')
|
|
||||||
.uint8('intensity')
|
|
||||||
.uint8('efSense')
|
|
||||||
.uint8('ampLevel')
|
|
||||||
.uint8('ampDirectLevel')
|
|
||||||
.bit7('not-use19')
|
|
||||||
.bit1('ampDistortion')
|
|
||||||
.uint8('velocitySense')
|
|
||||||
.uint8('keyTrack')
|
|
||||||
// always 0
|
|
||||||
.uint8('eg1Attack')
|
|
||||||
// always 0
|
|
||||||
.uint8('eg1Decay')
|
|
||||||
// always 127
|
|
||||||
.uint8('eg1Sustain')
|
|
||||||
// always 0
|
|
||||||
.uint8('eg1Release')
|
|
||||||
.uint8('eg2Attack')
|
|
||||||
.uint8('eg2Decay')
|
|
||||||
.uint8('eg2Sustain')
|
|
||||||
.uint8('eg2Release')
|
|
||||||
.bit2('not-use20')
|
|
||||||
.bit2('lfo1KeySync')
|
|
||||||
.bit2('not-use21')
|
|
||||||
.bit2('lfo1Wave')
|
|
||||||
.uint8('lfo1Freq')
|
|
||||||
.bit1('lfo1TempoSync')
|
|
||||||
.bit2('not-use22')
|
|
||||||
.bit5('lfo1SyncNote')
|
|
||||||
.bit2('not-use23')
|
|
||||||
.bit2('lfo2KeySync')
|
|
||||||
.bit2('not-use24')
|
|
||||||
.bit2('lfo2Wave')
|
|
||||||
.uint8('lfo2Freq')
|
|
||||||
.bit1('lfo2TempoSync')
|
|
||||||
.bit2('not-use25')
|
|
||||||
.bit5('lfo2SyncNote')
|
|
||||||
.array('levels', {
|
|
||||||
type: 'uint8',
|
|
||||||
length: 16,
|
|
||||||
})
|
|
||||||
.array('pans', {
|
|
||||||
type: 'uint8',
|
|
||||||
length: 16,
|
|
||||||
})
|
|
||||||
.array('efSenseLevels', {
|
|
||||||
type: 'uint8',
|
|
||||||
length: 16,
|
|
||||||
});
|
|
||||||
|
|
||||||
const parser = new Parser()
|
|
||||||
.endianess('big')
|
|
||||||
.string('name', { encoding: 'ASCII', length: 12 })
|
|
||||||
.skip(2)
|
|
||||||
.bit5('not-use12')
|
|
||||||
.bit3('arpTriggerLength')
|
|
||||||
.bit8('arpTriggerPattern')
|
|
||||||
.bit2('not-use13')
|
|
||||||
.bit2('voiceMode')
|
|
||||||
.bit4('not-use14')
|
|
||||||
.bit4('scaleKey')
|
|
||||||
.bit4('scaleType')
|
|
||||||
.skip(1)
|
|
||||||
.bit1('delayFxSync')
|
|
||||||
.bit3('not-use15')
|
|
||||||
.bit4('delayFXTimeBase')
|
|
||||||
.uint8('delayFXDelayTime')
|
|
||||||
.uint8('delayFXDepth')
|
|
||||||
.uint8('delayFXType')
|
|
||||||
.uint8('modFXLFOSpeed')
|
|
||||||
.uint8('modFXDepth')
|
|
||||||
.uint8('modFXType')
|
|
||||||
.uint8('eqHiFreq')
|
|
||||||
.uint8('eqHiGain')
|
|
||||||
.uint8('eqLowFreq')
|
|
||||||
.uint8('eqLowGain')
|
|
||||||
.uint8('arpTempo')
|
|
||||||
.uint8('arpSeqTempo')
|
|
||||||
.bit1('arpOnOff')
|
|
||||||
.bit1('arpLatchOnOff')
|
|
||||||
.bit2('arpTarget')
|
|
||||||
.bit1('not-use16')
|
|
||||||
.bit1('arpKeySync')
|
|
||||||
.bit4('arpRange')
|
|
||||||
.bit4('arpType')
|
|
||||||
.uint8('arpGateTime')
|
|
||||||
.uint8('arpResolution')
|
|
||||||
.uint8('arpSwing')
|
|
||||||
.uint8('kbdOctave')
|
|
||||||
.nest('timbre1', { type: timbre })
|
|
||||||
// dummy bytes *see documentation
|
|
||||||
.skip(56)
|
|
||||||
.nest('timbre2', { type: timbre });
|
|
||||||
// dummy bytes *see documentation
|
|
||||||
//.skip(56)
|
|
||||||
//.nest('vocoder', { type: vocoder })
|
|
||||||
// dummy bytes *see documentation
|
|
||||||
// .skip(111);
|
|
||||||
|
|
||||||
function parse(parser, binary) {
|
|
||||||
// check if the binary is a valid KORG-produced SYSEX file
|
|
||||||
if (binary.length !== 297) {
|
|
||||||
throw new Error('incorrect file length');
|
|
||||||
}
|
|
||||||
if (binary[0] !== 0xf0) {
|
|
||||||
throw new Error('first byte is not SYSEX byte');
|
|
||||||
}
|
|
||||||
if (binary[1] !== 0x42) {
|
|
||||||
throw new Error('manufacturer code is not KORG');
|
|
||||||
}
|
|
||||||
if (binary[4] !== 0x40) {
|
|
||||||
throw new Error('not a program dump');
|
|
||||||
}
|
|
||||||
if (binary[5] !== 0x00) {
|
|
||||||
// in all files found in the wild it's always zero
|
|
||||||
throw new Error('sixth byte is not zero');
|
|
||||||
}
|
|
||||||
if (binary[binary.length - 1] !== 0xf7) {
|
|
||||||
throw new Error('last byte is does not end SYSEX message');
|
|
||||||
}
|
|
||||||
|
|
||||||
const trimmedBinary = binary.slice(5, -1);
|
|
||||||
// every eigth byte is padding, so we remove it
|
|
||||||
const unpaddedBinary = trimmedBinary.filter((_, idx) => idx % 8);
|
|
||||||
|
|
||||||
const buffer = Buffer.from(unpaddedBinary).toString('hex');
|
|
||||||
|
|
||||||
console.log(buffer);
|
|
||||||
|
|
||||||
console.log(parser.parse(unpaddedBinary));
|
|
||||||
|
|
||||||
// do stuff with the parsed object
|
|
||||||
}
|
|
||||||
|
|
||||||
// converts patch object to dump format the system uses
|
|
||||||
function serialise(parser, object) {
|
|
||||||
// output object
|
|
||||||
console.log(parser.encode(object));
|
|
||||||
}
|
|
||||||
|
|
||||||
function Parse(object) {
|
|
||||||
return parse(parser, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Parse, serialise };
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
const toBitArray = (n, b = 8) => [...Array(b)].map((x, i) => (n >> i) & 1);
|
||||||
|
|
||||||
|
const getBits = (byte, indexStart, indexEnd = 7) =>
|
||||||
|
parseInt(
|
||||||
|
toBitArray(byte)
|
||||||
|
.slice(indexStart, indexEnd + 1)
|
||||||
|
.reverse()
|
||||||
|
.join(''),
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
|
const signedNumber = (byte) => (byte > 63 ? (byte ^ 64) - 64 : byte);
|
||||||
|
|
||||||
|
console.assert(1 === signedNumber(1));
|
||||||
|
console.assert(-1 === signedNumber(127));
|
||||||
|
|
||||||
|
// first argument must be type from file, and the rest the possible types in correct order (midi implementation)
|
||||||
|
function returnFromList(...args) {
|
||||||
|
for (var i = 1; i < arguments.length; i++) {
|
||||||
|
if (i - 1 === arguments[0]) {
|
||||||
|
return arguments[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function returnValue(setting, list) {
|
||||||
|
for (var i = 0; i < list.length; i++) {
|
||||||
|
if (list[i] === setting) return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { toBitArray, getBits, signedNumber, returnFromList, returnValue };
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
// Copyright 2021 Aoife Bradley
|
||||||
|
//
|
||||||
|
// Author: Aoife Bradley (aoife@spinlock.icu)
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||||
|
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
SYSEX Midi Functions for the microKORG
|
||||||
|
As per the microKORG midi specification found here: http://i.korg.com/uploads/Support/MK1_633652915168960000.pdf
|
||||||
|
|
||||||
|
How does this work?
|
||||||
|
|
||||||
|
The data handled by the microKORG is in 'sets' with each 'set' composed of 7 Bytes with 8 Bits as such:
|
||||||
|
|
||||||
|
0b0000 0000
|
||||||
|
0b1111 1111
|
||||||
|
0bnnnn nnnn
|
||||||
|
0b6666 6666
|
||||||
|
|
||||||
|
this gets turned into 'sets' of 8 bytes with 7 Bits:
|
||||||
|
|
||||||
|
0b0654 3210 -- this stores the MSB of the following 7 Bytes
|
||||||
|
0b0000 0000
|
||||||
|
0b0111 1111
|
||||||
|
0b0nnn nnnn
|
||||||
|
0b0666 6666
|
||||||
|
|
||||||
|
In addition, the data is stored with an extra 2 Bytes, which gets converted into 3 bytes in the SYSEX format.
|
||||||
|
|
||||||
|
Handily, they displayed this as:
|
||||||
|
CURRENT PROGRAM DATA (IN CURRENT BUFFER) DUMP FORMAT
|
||||||
|
254bytes = 7*36+2 -> 8*36+(1+2) => 291Bytes
|
||||||
|
|
||||||
|
It probably has something to do with dumping multiple programs as well as global settings, but I'm not handling those here.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SYSEX_LENGTH = 36 * 8 + (1 + 2);
|
||||||
|
const DATA_LENGTH = 36 * 7 + 2;
|
||||||
|
|
||||||
|
export function sysex_encode(data) {
|
||||||
|
let sysex = new Uint8Array(SYSEX_LENGTH);
|
||||||
|
let data_index = 0,
|
||||||
|
syx_index = 0;
|
||||||
|
while (data_index < DATA_LENGTH) {
|
||||||
|
if (data_index + 7 <= DATA_LENGTH) {
|
||||||
|
sysex[syx_index] =
|
||||||
|
((data[data_index] & 0x80) >> 7) |
|
||||||
|
((data[data_index + 1] & 0x80) >> 6) |
|
||||||
|
((data[data_index + 2] & 0x80) >> 5) |
|
||||||
|
((data[data_index + 3] & 0x80) >> 4) |
|
||||||
|
((data[data_index + 4] & 0x80) >> 3) |
|
||||||
|
((data[data_index + 5] & 0x80) >> 2) |
|
||||||
|
((data[data_index + 6] & 0x80) >> 1);
|
||||||
|
|
||||||
|
sysex[syx_index + 1] = data[data_index] & 0x7f;
|
||||||
|
sysex[syx_index + 2] = data[data_index + 1] & 0x7f;
|
||||||
|
sysex[syx_index + 3] = data[data_index + 2] & 0x7f;
|
||||||
|
sysex[syx_index + 4] = data[data_index + 3] & 0x7f;
|
||||||
|
sysex[syx_index + 5] = data[data_index + 4] & 0x7f;
|
||||||
|
sysex[syx_index + 6] = data[data_index + 5] & 0x7f;
|
||||||
|
sysex[syx_index + 7] = data[data_index + 6] & 0x7f;
|
||||||
|
}
|
||||||
|
data_index += 7;
|
||||||
|
syx_index += 8;
|
||||||
|
}
|
||||||
|
return sysex;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sysex_write_header(data, channel) {
|
||||||
|
const ch = 0x30 + channel;
|
||||||
|
return new Uint8Array(5 + SYSEX_LENGTH + 1)
|
||||||
|
.set([0xf0, 0x42, ch, 0x58, 0x40], 0)
|
||||||
|
.set(data, 5)
|
||||||
|
.set(0xf7, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sysex_decode(syx) {
|
||||||
|
const trimmed_data = syx.slice(5, -4);
|
||||||
|
let data = new Uint8Array(DATA_LENGTH);
|
||||||
|
let syx_index = 0,
|
||||||
|
data_index = 0;
|
||||||
|
while (syx_index < trimmed_data.length) {
|
||||||
|
data[data_index] =
|
||||||
|
((trimmed_data[syx_index] & 0x1) << 7) | trimmed_data[syx_index + 1];
|
||||||
|
data[data_index + 1] =
|
||||||
|
((trimmed_data[syx_index] & 0x2) << 6) | trimmed_data[syx_index + 2];
|
||||||
|
data[data_index + 2] =
|
||||||
|
((trimmed_data[syx_index] & 0x4) << 5) | trimmed_data[syx_index + 3];
|
||||||
|
data[data_index + 3] =
|
||||||
|
((trimmed_data[syx_index] & 0x8) << 4) | trimmed_data[syx_index + 4];
|
||||||
|
data[data_index + 4] =
|
||||||
|
((trimmed_data[syx_index] & 0x10) << 3) | trimmed_data[syx_index + 5];
|
||||||
|
data[data_index + 5] =
|
||||||
|
((trimmed_data[syx_index] & 0x20) << 2) | trimmed_data[syx_index + 6];
|
||||||
|
data[data_index + 6] =
|
||||||
|
((trimmed_data[syx_index] & 0x40) << 1) | trimmed_data[syx_index + 7];
|
||||||
|
data_index += 7;
|
||||||
|
syx_index += 8;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sysex_check(sysex) {
|
||||||
|
if (sysex.length !== 297) {
|
||||||
|
throw new Error('incorrect file length');
|
||||||
|
}
|
||||||
|
if (sysex[0] !== 0xf0) {
|
||||||
|
throw new Error('first byte is not SYSEX byte');
|
||||||
|
}
|
||||||
|
if (sysex[1] !== 0x42) {
|
||||||
|
throw new Error('manufacturer code is not KORG');
|
||||||
|
}
|
||||||
|
if (sysex[4] !== 0x40) {
|
||||||
|
throw new Error('not a program dump');
|
||||||
|
}
|
||||||
|
if (sysex[sysex.length - 1] !== 0xf7) {
|
||||||
|
throw new Error('last byte is does not end SYSEX message');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
.menu,
|
|
||||||
.menu-true,
|
|
||||||
.menu-false {
|
|
||||||
position: fixed;
|
|
||||||
background: white;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 99;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
min-height: 100vh;
|
|
||||||
min-width: 100vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-false {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-true-animation {
|
|
||||||
-webkit-animation: scale-in-out-tl 0.3s
|
|
||||||
cubic-bezier(0.55, 0.085, 0.68, 0.53) both;
|
|
||||||
animation: scale-in-out-tl 0.3s cubic-bezier(0.55, 0.085, 0.68, 0.53) both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-false-animation {
|
|
||||||
-webkit-animation: scale-in-out-tl 0.3s
|
|
||||||
cubic-bezier(0.55, 0.085, 0.68, 0.53) reverse both;
|
|
||||||
animation: scale-in-out-tl 0.3s cubic-bezier(0.55, 0.085, 0.68, 0.53)
|
|
||||||
reverse both;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------------
|
|
||||||
* Generated by Animista on 2020-11-21 14:21:54
|
|
||||||
* Licensed under FreeBSD License.
|
|
||||||
* See http://animista.net/license for more info.
|
|
||||||
* w: http://animista.net, t: @cssanimista
|
|
||||||
* ---------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ----------------------------------------
|
|
||||||
* animation scale-out-tl
|
|
||||||
* ----------------------------------------
|
|
||||||
*/
|
|
||||||
@-webkit-keyframes scale-in-out-tl {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: scale(1);
|
|
||||||
transform: scale(1);
|
|
||||||
clip-path: circle(100%);
|
|
||||||
-webkit-transform-origin: 0 0;
|
|
||||||
transform-origin: 0 0;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: scale(0);
|
|
||||||
transform: scale(0);
|
|
||||||
clip-path: circle(0%);
|
|
||||||
-webkit-transform-origin: 0 0;
|
|
||||||
transform-origin: 0 0;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes scale-in-out-tl {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: scale(1);
|
|
||||||
transform: scale(1);
|
|
||||||
-webkit-transform-origin: 0 0;
|
|
||||||
clip-path: circle(100%);
|
|
||||||
transform-origin: 0 0;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: scale(0);
|
|
||||||
transform: scale(0);
|
|
||||||
clip-path: circle(0%);
|
|
||||||
-webkit-transform-origin: 0 0;
|
|
||||||
transform-origin: 0 0;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-logo {
|
|
||||||
height: 40vmin;
|
|
||||||
pointer-events: none;
|
|
||||||
padding: 0.5em;
|
|
||||||
-webkit-user-drag: none;
|
|
||||||
-khtml-user-drag: none;
|
|
||||||
-moz-user-drag: none;
|
|
||||||
-o-user-drag: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-o-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-title {
|
|
||||||
color: #1c1d21;
|
|
||||||
font-family: 'Lato', sans-serif;
|
|
||||||
line-height: 1em;
|
|
||||||
margin: 0 0 0.5em 0;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
margin: 0.25em;
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 0.85em;
|
|
||||||
font-size: 2.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
margin: 0.25em;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1em;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-button,
|
|
||||||
.menu-open-button {
|
|
||||||
background: transparent;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 1em;
|
|
||||||
padding: 0.35em 1em;
|
|
||||||
border: 0.1em solid slateblue;
|
|
||||||
border-radius: 0.12em;
|
|
||||||
color: slateblue;
|
|
||||||
font-family: 'Roboto', sans-serif;
|
|
||||||
font-size: 1em;
|
|
||||||
font-weight: 300;
|
|
||||||
text-align: center;
|
|
||||||
outline: none;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
-webkit-touch-callout: none; /* iOS Safari */
|
|
||||||
-webkit-user-select: none; /* Safari */
|
|
||||||
-khtml-user-select: none; /* Konqueror HTML */
|
|
||||||
-moz-user-select: none; /* Old versions of Firefox */
|
|
||||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
.menu-button:hover {
|
|
||||||
color: white;
|
|
||||||
background: slateblue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-open-button:hover {
|
|
||||||
color: white;
|
|
||||||
background: #ef476f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-open-button {
|
|
||||||
z-index: 100;
|
|
||||||
height: 1.495em;
|
|
||||||
line-height: 0.9em;
|
|
||||||
position: absolute;
|
|
||||||
padding: 0em 0.3em;
|
|
||||||
color: #ef476f;
|
|
||||||
border: 0.09em solid #ef476f;
|
|
||||||
margin: 0;
|
|
||||||
top: 0.55em;
|
|
||||||
left: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* change how the button is handled when screen is smaller then 880px*/
|
|
||||||
|
|
||||||
.menu-open-button::before {
|
|
||||||
content: '\2630';
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal {
|
|
||||||
margin: auto;
|
|
||||||
height: 8em;
|
|
||||||
width: 14em;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 100;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-hide {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-display {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-main {
|
|
||||||
background: white;
|
|
||||||
padding: 0.75em 0 1em;
|
|
||||||
border: solid 0.09em slategrey;
|
|
||||||
border-radius: 6px;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-header {
|
|
||||||
font-size: 0.75em;
|
|
||||||
line-height: 1.25em;
|
|
||||||
text-align: left;
|
|
||||||
margin: 0em 9em 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-text {
|
|
||||||
flex-grow: 3;
|
|
||||||
margin: 0.5em 0em 1em 1.85em;
|
|
||||||
text-align: left;
|
|
||||||
line-height: 1.5em;
|
|
||||||
font-size: 0.65em;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-save,
|
|
||||||
.button-no-save,
|
|
||||||
.button-cancel {
|
|
||||||
width: 25%;
|
|
||||||
font-size: 0.5em;
|
|
||||||
margin: 0 0.5em;
|
|
||||||
border-radius: 0.2em;
|
|
||||||
padding: 0.35em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-save {
|
|
||||||
color: mediumseagreen;
|
|
||||||
border-color: mediumseagreen;
|
|
||||||
}
|
|
||||||
.button-save:hover {
|
|
||||||
background: mediumseagreen;
|
|
||||||
}
|
|
||||||
.button-no-save {
|
|
||||||
color: crimson;
|
|
||||||
border-color: crimson;
|
|
||||||
}
|
|
||||||
.button-no-save:hover {
|
|
||||||
background: crimson;
|
|
||||||
}
|
|
||||||
.button-cancel {
|
|
||||||
color: slategray;
|
|
||||||
border-color: grey;
|
|
||||||
}
|
|
||||||
.button-cancel:hover {
|
|
||||||
background: slategray;
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
|
||||||
import IO from '../io/IO';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux'; // probably temporarily here
|
|
||||||
import { parameterRefreshAll } from '../timbre/parameters/parameterSlice'; // probably temporarily here
|
|
||||||
import logo from '../../1f3b9.svg';
|
|
||||||
import './Menu.css';
|
|
||||||
|
|
||||||
// for things such as (do you want to save? or do you want to reset things?)
|
|
||||||
const Modal = ({ children, show, setShow }) => {
|
|
||||||
const node = useRef();
|
|
||||||
const modalDisplay = show ? 'modal modal-display' : 'modal modal-hide';
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const current = node.current;
|
|
||||||
|
|
||||||
const handleOutsideClick = (event) => {
|
|
||||||
console.log('clicking anywhere');
|
|
||||||
if (current.contains(event.target)) {
|
|
||||||
return;
|
|
||||||
} else setShow();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (show) {
|
|
||||||
document.addEventListener('mousedown', handleOutsideClick);
|
|
||||||
} else {
|
|
||||||
document.removeEventListener('mousedown', handleOutsideClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('mousedown', handleOutsideClick);
|
|
||||||
};
|
|
||||||
}, [show, setShow]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div ref={node} className={modalDisplay}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function MainMenu() {
|
|
||||||
const unsaved = useSelector((state) => state.parameters.unsaved);
|
|
||||||
const [open, setOpen] = useState({
|
|
||||||
className: '',
|
|
||||||
openState: true,
|
|
||||||
});
|
|
||||||
const [showModal, setShowModal] = useState({
|
|
||||||
saveCheck: false,
|
|
||||||
ioModal: false,
|
|
||||||
});
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const HandleClick = () => {
|
|
||||||
setOpen({
|
|
||||||
className: open.openState
|
|
||||||
? 'menu-true-animation'
|
|
||||||
: 'menu-false-animation',
|
|
||||||
openState: !open.openState,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const AnimationEnd = () => {
|
|
||||||
setOpen({
|
|
||||||
...open,
|
|
||||||
className: `menu-${open.openState}`,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const showSaveCheck = () => {
|
|
||||||
setShowModal({ ...showModal, saveCheck: !showModal.saveCheck });
|
|
||||||
};
|
|
||||||
|
|
||||||
const showIOModal = () => {
|
|
||||||
setShowModal({ ...showModal, ioModal: !showModal.ioModal });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Modal show={showModal.saveCheck} setShow={showSaveCheck}>
|
|
||||||
<div className="modal-main">
|
|
||||||
<span className="modal-header">Save Changes</span>
|
|
||||||
<span className="modal-text">
|
|
||||||
Do you want to save your changes?
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
className="menu-button button-save"
|
|
||||||
onClick={() => {
|
|
||||||
showSaveCheck();
|
|
||||||
HandleClick();
|
|
||||||
}} // temporary while I implement the binary parsing stuff
|
|
||||||
>
|
|
||||||
Save Changes
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="menu-button button-no-save"
|
|
||||||
onClick={() => {
|
|
||||||
dispatch(parameterRefreshAll());
|
|
||||||
showSaveCheck();
|
|
||||||
HandleClick();
|
|
||||||
}}>
|
|
||||||
Don't Save
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="menu-button button-cancel"
|
|
||||||
onClick={showSaveCheck}>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
<div
|
|
||||||
className={`menu ${open.className}`}
|
|
||||||
onAnimationEnd={AnimationEnd}>
|
|
||||||
<img
|
|
||||||
src={logo}
|
|
||||||
className="menu-logo"
|
|
||||||
alt="logo"
|
|
||||||
draggable={false}
|
|
||||||
/>
|
|
||||||
<header className="menu-title">
|
|
||||||
<h1>Alapatch</h1>
|
|
||||||
<h2>A microKORG Patch Editor</h2>
|
|
||||||
</header>
|
|
||||||
<div className="menu-button-container">
|
|
||||||
<button
|
|
||||||
className="menu-button"
|
|
||||||
onClick={() => {
|
|
||||||
!unsaved ? HandleClick() : showSaveCheck();
|
|
||||||
}}>
|
|
||||||
New Patch
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="menu-button"
|
|
||||||
onClick={() => {
|
|
||||||
!unsaved ? showIOModal() : showSaveCheck();
|
|
||||||
}}>
|
|
||||||
Load Patch
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Modal show={showModal.ioModal} setShow={showIOModal}>
|
|
||||||
<IO />
|
|
||||||
</Modal>
|
|
||||||
<button className="menu-open-button" onClick={HandleClick} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import { useRef, useState, useEffect } from 'react';
|
||||||
|
import './menu.css';
|
||||||
|
import { SelectList } from '../helpers/Helpers';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import {
|
||||||
|
parameterUpdateIndex,
|
||||||
|
parameterUpdated,
|
||||||
|
parameterFromFile,
|
||||||
|
parameterRefreshAll,
|
||||||
|
selectEntities,
|
||||||
|
} from '../timbre/parameters/parameterSlice'; // probably temporarily here
|
||||||
|
import { return_store_from_file, return_file_from_store } from '../io';
|
||||||
|
|
||||||
|
export default function Menu() {
|
||||||
|
const [file, setFile] = useState(null);
|
||||||
|
const fileRef = useRef(null);
|
||||||
|
const state = useSelector((state) => selectEntities(state));
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const reader = new FileReader();
|
||||||
|
const loadFile = (f) => {
|
||||||
|
reader.onloadend = (e) => {
|
||||||
|
setFile(new Uint8Array(e.target.result));
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(f);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (file) {
|
||||||
|
dispatch(parameterFromFile(return_store_from_file(file)));
|
||||||
|
}
|
||||||
|
}, [file, dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='menu'>
|
||||||
|
<div className='menu-patch-container'>
|
||||||
|
<div className='patch-name'>
|
||||||
|
<input
|
||||||
|
placeholder='Patch Name'
|
||||||
|
type='text'
|
||||||
|
maxLength='12'
|
||||||
|
value={state.patch.name}
|
||||||
|
onChange={(e) => {
|
||||||
|
dispatch(
|
||||||
|
parameterUpdated({
|
||||||
|
id: 'Patch',
|
||||||
|
changes: { name: e.target.value },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='patch-load'>
|
||||||
|
<button
|
||||||
|
className='menu-button'
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(parameterRefreshAll());
|
||||||
|
}}>
|
||||||
|
New Patch
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='menu-button'
|
||||||
|
onClick={() => {
|
||||||
|
fileRef.current.click();
|
||||||
|
}}>
|
||||||
|
Load Patch
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='menu-button'
|
||||||
|
onClick={() => {
|
||||||
|
return_file_from_store(state);
|
||||||
|
}}>
|
||||||
|
Save Patch
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type='file'
|
||||||
|
accept='.syx'
|
||||||
|
className='menu-file-input'
|
||||||
|
ref={fileRef}
|
||||||
|
onChange={(e) => {
|
||||||
|
loadFile(e.target.files[0]);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='patch-settings'>
|
||||||
|
<SelectList
|
||||||
|
value={state.patch.mode}
|
||||||
|
list={[
|
||||||
|
{ value: 'Single' },
|
||||||
|
{ value: 'Multiple' },
|
||||||
|
{ value: 'Vocoder' },
|
||||||
|
]}
|
||||||
|
onChange={(value) => {
|
||||||
|
dispatch(
|
||||||
|
parameterUpdated({
|
||||||
|
id: 'patch',
|
||||||
|
changes: { mode: value },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='menu-timbre-select'>
|
||||||
|
<button
|
||||||
|
disabled={state.patch.mode === 'Vocoder'}
|
||||||
|
className='menu-button'
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(parameterUpdateIndex(0));
|
||||||
|
}}>
|
||||||
|
Timbre 1
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={state.patch.mode !== 'Multiple'}
|
||||||
|
className='menu-button'
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(parameterUpdateIndex(1));
|
||||||
|
}}>
|
||||||
|
Timbre 2
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={state.patch.mode !== 'Vocoder'}
|
||||||
|
className='menu-button'
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(parameterUpdateIndex(2));
|
||||||
|
}}>
|
||||||
|
Vocoder
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='menu-button'
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(parameterUpdateIndex(3));
|
||||||
|
}}>
|
||||||
|
Effects
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='menu-button'
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(parameterUpdateIndex(4));
|
||||||
|
}}>
|
||||||
|
Arpeggio
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className='menu-title'>
|
||||||
|
Alapatch <br /> A microKORG Patch Editor
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
.menu {
|
||||||
|
height: 100vh;
|
||||||
|
min-width: 15vw;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-patch-container > * {
|
||||||
|
margin-top: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='text'] {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
text-indent: 8pt;
|
||||||
|
width: 70%;
|
||||||
|
padding: 6pt 0;
|
||||||
|
outline: none;
|
||||||
|
border-radius: 4pt;
|
||||||
|
border: 1px solid gainsboro;
|
||||||
|
}
|
||||||
|
input[type='text']:hover {
|
||||||
|
border-color: silver;
|
||||||
|
}
|
||||||
|
input[type='text']:focus {
|
||||||
|
border: 2px solid dodgerblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-button {
|
||||||
|
width: 65%;
|
||||||
|
display: block;
|
||||||
|
padding: 4pt;
|
||||||
|
margin: 8px auto;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: left;
|
||||||
|
font-size: x-large;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-file-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.patch-settings {
|
||||||
|
font-size: small;
|
||||||
|
width: 75%;
|
||||||
|
margin: 10pt auto;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { useRef, useEffect } from 'react';
|
||||||
|
|
||||||
|
const Modal = ({ children, show, setShow }) => {
|
||||||
|
const node = useRef();
|
||||||
|
const modalDisplay = show ? 'modal modal-display' : 'modal modal-hide';
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const current = node.current;
|
||||||
|
|
||||||
|
const handleOutsideClick = (event) => {
|
||||||
|
console.log('clicking anywhere');
|
||||||
|
if (current.contains(event.target)) {
|
||||||
|
return;
|
||||||
|
} else setShow();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (show) {
|
||||||
|
document.addEventListener('mousedown', handleOutsideClick);
|
||||||
|
} else {
|
||||||
|
document.removeEventListener('mousedown', handleOutsideClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handleOutsideClick);
|
||||||
|
};
|
||||||
|
}, [show, setShow]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={node} className={modalDisplay}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Modal;
|
||||||
@@ -16,7 +16,7 @@ export default function Timbre(props) {
|
|||||||
return (
|
return (
|
||||||
<div className={styles.timbreStyle}>
|
<div className={styles.timbreStyle}>
|
||||||
<VCO
|
<VCO
|
||||||
name="VCO 1"
|
name='VCO 1'
|
||||||
id={ids.vco1}
|
id={ids.vco1}
|
||||||
waveforms={[
|
waveforms={[
|
||||||
{ value: 'Saw' },
|
{ value: 'Saw' },
|
||||||
@@ -28,7 +28,7 @@ export default function Timbre(props) {
|
|||||||
{ value: 'Noise' },
|
{ value: 'Noise' },
|
||||||
{ value: 'Audio In' },
|
{ value: 'Audio In' },
|
||||||
]}
|
]}
|
||||||
dwgsOrModTypeName="DWGS"
|
dwgsOrModTypeName='DWGS'
|
||||||
dwgsOrModType={[
|
dwgsOrModType={[
|
||||||
{ value: 'SynSine1' },
|
{ value: 'SynSine1' },
|
||||||
{ value: 'SynSine2' },
|
{ value: 'SynSine2' },
|
||||||
@@ -97,30 +97,30 @@ export default function Timbre(props) {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<VCO
|
<VCO
|
||||||
name="VCO 2"
|
name='VCO 2'
|
||||||
id={ids.vco2}
|
id={ids.vco2}
|
||||||
waveforms={[
|
waveforms={[
|
||||||
{ value: 'Saw' },
|
{ value: 'Saw' },
|
||||||
{ value: 'Square' },
|
{ value: 'Square' },
|
||||||
{ value: 'Triangle' },
|
{ value: 'Triangle' },
|
||||||
]}
|
]}
|
||||||
dwgsOrModTypeName="Mod Type"
|
dwgsOrModTypeName='Mod Type'
|
||||||
dwgsOrModType={[
|
dwgsOrModType={[
|
||||||
{ value: 'Off' },
|
{ value: 'Off' },
|
||||||
{ value: 'Ring' },
|
{ value: 'Ring' },
|
||||||
{ value: 'Sync' },
|
{ value: 'Sync' },
|
||||||
{ value: 'RingSync' },
|
{ value: 'Ring Sync' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Mixer name="Mixer" id={ids.mixer} />
|
<Mixer name='Mixer' id={ids.mixer} />
|
||||||
<Pitch name="Pitch" id={ids.pitch} />
|
<Pitch name='Pitch' id={ids.pitch} />
|
||||||
<Filter name="Filter" id={ids.filter} />
|
<Filter name='Filter' id={ids.filter} />
|
||||||
<Amp name="Amp" id={ids.amp} />
|
<Amp name='Amp' id={ids.amp} />
|
||||||
<EG name="EG 1" id={ids.eg1} />
|
<EG name='EG 1' id={ids.eg1} />
|
||||||
<EG name="EG 2" id={ids.eg2} />
|
<EG name='EG 2' id={ids.eg2} />
|
||||||
<div className={styles.lineBreak} />
|
<div className={styles.lineBreak} />
|
||||||
<LFO
|
<LFO
|
||||||
name="LFO 1"
|
name='LFO 1'
|
||||||
id={ids.lfo1}
|
id={ids.lfo1}
|
||||||
waveforms={[
|
waveforms={[
|
||||||
{ value: 'Saw' },
|
{ value: 'Saw' },
|
||||||
@@ -130,7 +130,7 @@ export default function Timbre(props) {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<LFO
|
<LFO
|
||||||
name="LFO 2"
|
name='LFO 2'
|
||||||
id={ids.lfo2}
|
id={ids.lfo2}
|
||||||
waveforms={[
|
waveforms={[
|
||||||
{ value: 'Saw' },
|
{ value: 'Saw' },
|
||||||
@@ -139,7 +139,7 @@ export default function Timbre(props) {
|
|||||||
{ value: 'Sample & Hold' },
|
{ value: 'Sample & Hold' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Patches name="Patches" id={ids.patches} />
|
<Patches name='Patches' id={ids.patches} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,15 +58,14 @@
|
|||||||
|
|
||||||
.tabList {
|
.tabList {
|
||||||
border-bottom: 1px solid black;
|
border-bottom: 1px solid black;
|
||||||
display: inline-flex;
|
display: none;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
top: 0;
|
top: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100vw;
|
width: 85vw;
|
||||||
|
|
||||||
background: white;
|
background: white;
|
||||||
}
|
}
|
||||||
.tab {
|
.tab {
|
||||||
@@ -117,18 +116,15 @@
|
|||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
.tabPanel::-webkit-scrollbar {
|
.tabPanel::-webkit-scrollbar {
|
||||||
width: 0.25em;
|
width: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabPanel::-webkit-scrollbar-track,
|
.tabPanel::-webkit-scrollbar-track,
|
||||||
.tabPanel::-webkit-scrollbar-thumb {
|
.tabPanel::-webkit-scrollbar-thumb {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
.tabPanel:hover::-webkit-scrollbar-track {
|
|
||||||
background-color: lightgray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabPanel:hover::-webkit-scrollbar-thumb {
|
.tabPanel:hover::-webkit-scrollbar-thumb {
|
||||||
background-color: slateblue;
|
background-color: lightgray;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export default function LFO(props) {
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.lfo}>
|
||||||
<span className={styles.header}>{`${props.name}`}</span>
|
<span className={styles.header}>{`${props.name}`}</span>
|
||||||
LFO Waveform
|
LFO Waveform
|
||||||
<SelectList
|
<SelectList
|
||||||
@@ -27,7 +27,7 @@ export default function LFO(props) {
|
|||||||
Key Sync
|
Key Sync
|
||||||
<SelectList
|
<SelectList
|
||||||
value={parameters.keySync}
|
value={parameters.keySync}
|
||||||
list={['Off', 'Timbre', 'Voice']}
|
list={[{ value: 'Off' }, { value: 'Timbre' }, { value: 'Voice' }]}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
dispatch(
|
dispatch(
|
||||||
parameterUpdated({
|
parameterUpdated({
|
||||||
@@ -37,8 +37,9 @@ export default function LFO(props) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<div className='check-list'>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
name="Tempo Sync"
|
name='Tempo Sync'
|
||||||
parameter={parameters.tempoSync}
|
parameter={parameters.tempoSync}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
dispatch(
|
dispatch(
|
||||||
@@ -49,6 +50,35 @@ export default function LFO(props) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<SelectList
|
||||||
|
value={parameters.syncNote}
|
||||||
|
list={[
|
||||||
|
{ value: '1/1' },
|
||||||
|
{ value: '3/4' },
|
||||||
|
{ value: '2/3' },
|
||||||
|
{ value: '1/2' },
|
||||||
|
{ value: '3/8' },
|
||||||
|
{ value: '1/3' },
|
||||||
|
{ value: '1/4' },
|
||||||
|
{ value: '3/16' },
|
||||||
|
{ value: '1/6' },
|
||||||
|
{ value: '1/8' },
|
||||||
|
{ value: '3/32' },
|
||||||
|
{ value: '1/12' },
|
||||||
|
{ value: '1/16' },
|
||||||
|
{ value: '1/24' },
|
||||||
|
{ value: '1/32' },
|
||||||
|
]}
|
||||||
|
onChange={(value) =>
|
||||||
|
dispatch(
|
||||||
|
parameterUpdated({
|
||||||
|
id: props.id,
|
||||||
|
changes: { syncNote: value },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
Frequency
|
Frequency
|
||||||
<Slider
|
<Slider
|
||||||
parameter={parameters.frequency}
|
parameter={parameters.frequency}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
.container,
|
.container,
|
||||||
|
.lfo,
|
||||||
.horizontalContainer,
|
.horizontalContainer,
|
||||||
.patchesContainer {
|
.patchesContainer {
|
||||||
border: solid 1pt slategrey;
|
border: solid 1pt slategrey;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container,
|
||||||
|
.lfo {
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: small;
|
font-size: small;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@@ -17,6 +19,12 @@
|
|||||||
height: 214px;
|
height: 214px;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lfo {
|
||||||
|
height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lfo > *,
|
||||||
.container > * {
|
.container > * {
|
||||||
margin: 3pt 0;
|
margin: 3pt 0;
|
||||||
}
|
}
|
||||||
@@ -31,6 +39,7 @@
|
|||||||
border-right: 1px solid slategrey;
|
border-right: 1px solid slategrey;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
.lfo > .header,
|
||||||
.container > .header {
|
.container > .header {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -7.5%;
|
top: -7.5%;
|
||||||
@@ -66,7 +75,7 @@
|
|||||||
padding: 16pt 8pt 16pt 8pt;
|
padding: 16pt 8pt 16pt 8pt;
|
||||||
margin: 10pt;
|
margin: 10pt;
|
||||||
width: 680px;
|
width: 680px;
|
||||||
height: 214px;
|
height: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.horizontalContainer > .container,
|
.horizontalContainer > .container,
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ const defaultState = {
|
|||||||
'eq',
|
'eq',
|
||||||
'arpeggA',
|
'arpeggA',
|
||||||
'arpeggB',
|
'arpeggB',
|
||||||
|
'patch',
|
||||||
|
'unusedPatch',
|
||||||
|
'unusedTimbre1',
|
||||||
|
'unusedTimbre2',
|
||||||
|
'unusedVocoder',
|
||||||
],
|
],
|
||||||
entities: {
|
entities: {
|
||||||
vco1: {
|
vco1: {
|
||||||
@@ -154,6 +159,7 @@ const defaultState = {
|
|||||||
},
|
},
|
||||||
filter3: {
|
filter3: {
|
||||||
id: 'filter3',
|
id: 'filter3',
|
||||||
|
shift: 2,
|
||||||
cutoff: 0,
|
cutoff: 0,
|
||||||
resonance: 0,
|
resonance: 0,
|
||||||
efSense: 0,
|
efSense: 0,
|
||||||
@@ -230,6 +236,7 @@ const defaultState = {
|
|||||||
keySync: 'Off',
|
keySync: 'Off',
|
||||||
tempoSync: false,
|
tempoSync: false,
|
||||||
frequency: 0,
|
frequency: 0,
|
||||||
|
syncNote: '1/1',
|
||||||
},
|
},
|
||||||
lfo2: {
|
lfo2: {
|
||||||
id: 'lfo2',
|
id: 'lfo2',
|
||||||
@@ -237,6 +244,7 @@ const defaultState = {
|
|||||||
keySync: 'Off',
|
keySync: 'Off',
|
||||||
tempoSync: false,
|
tempoSync: false,
|
||||||
frequency: 0,
|
frequency: 0,
|
||||||
|
syncNote: '1/1',
|
||||||
},
|
},
|
||||||
lfo3: {
|
lfo3: {
|
||||||
id: 'lfo3',
|
id: 'lfo3',
|
||||||
@@ -244,6 +252,7 @@ const defaultState = {
|
|||||||
keySync: 'Off',
|
keySync: 'Off',
|
||||||
tempoSync: false,
|
tempoSync: false,
|
||||||
frequency: 0,
|
frequency: 0,
|
||||||
|
syncNote: '1/1',
|
||||||
},
|
},
|
||||||
lfo4: {
|
lfo4: {
|
||||||
id: 'lfo4',
|
id: 'lfo4',
|
||||||
@@ -251,6 +260,7 @@ const defaultState = {
|
|||||||
keySync: 'Off',
|
keySync: 'Off',
|
||||||
tempoSync: false,
|
tempoSync: false,
|
||||||
frequency: 0,
|
frequency: 0,
|
||||||
|
syncNote: '1/1',
|
||||||
},
|
},
|
||||||
lfo5: {
|
lfo5: {
|
||||||
id: 'lfo5',
|
id: 'lfo5',
|
||||||
@@ -258,13 +268,14 @@ const defaultState = {
|
|||||||
keySync: 'Off',
|
keySync: 'Off',
|
||||||
tempoSync: false,
|
tempoSync: false,
|
||||||
frequency: 0,
|
frequency: 0,
|
||||||
|
syncNote: '1/1',
|
||||||
},
|
},
|
||||||
lfo6: {
|
lfo6: {
|
||||||
id: 'lfo6',
|
id: 'lfo6',
|
||||||
waveform: 'Saw',
|
waveform: 'Saw',
|
||||||
keySync: 'Off',
|
keySync: 'Off',
|
||||||
tempoSync: false,
|
tempoSync: false,
|
||||||
frequency: 0,
|
frequency: '1/1',
|
||||||
},
|
},
|
||||||
patches1: {
|
patches1: {
|
||||||
id: 'patches1',
|
id: 'patches1',
|
||||||
@@ -370,9 +381,51 @@ const defaultState = {
|
|||||||
keySync: false,
|
keySync: false,
|
||||||
lastStep: 1,
|
lastStep: 1,
|
||||||
target: 'Both',
|
target: 'Both',
|
||||||
|
pattern: [0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
},
|
||||||
|
patch: {
|
||||||
|
id: 'patch',
|
||||||
|
name: 'Default',
|
||||||
|
mode: 'Single',
|
||||||
|
},
|
||||||
|
unusedPatch: {
|
||||||
|
id: 'unusedPatch',
|
||||||
|
scaleKey: 0,
|
||||||
|
scaleType: 0,
|
||||||
|
keyboardOctave: 0,
|
||||||
|
arpSwitch: 0,
|
||||||
|
},
|
||||||
|
unusedTimbre1: {
|
||||||
|
id: 'unusedTimbre1',
|
||||||
|
midiChannel: 0,
|
||||||
|
keyPriority: 0,
|
||||||
|
filterVelSense: 0,
|
||||||
|
ampSwitch: 0,
|
||||||
|
ampVelSense: 0,
|
||||||
|
},
|
||||||
|
unusedTimbre2: {
|
||||||
|
id: 'unusedTimbre2',
|
||||||
|
midiChannel: 0,
|
||||||
|
keyPriority: 0,
|
||||||
|
filterVelSense: 0,
|
||||||
|
ampSwitch: 0,
|
||||||
|
ampVelSense: 0,
|
||||||
|
},
|
||||||
|
unusedVocoder: {
|
||||||
|
id: 'unusedVocoder',
|
||||||
|
egReset2: 0,
|
||||||
|
midiChannel: 0,
|
||||||
|
keyPriority: 0,
|
||||||
|
filterEFSense: 0,
|
||||||
|
ampVelSense: 0,
|
||||||
|
eg2attack: 0,
|
||||||
|
eg2decay: 0,
|
||||||
|
eg2sustain: 0,
|
||||||
|
eg2release: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
unsaved: false,
|
unsaved: false,
|
||||||
|
tabIndex: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const parametersAdapter = createEntityAdapter({});
|
const parametersAdapter = createEntityAdapter({});
|
||||||
@@ -389,6 +442,12 @@ const parametersSlice = createSlice({
|
|||||||
parameterFlipUnsaved(state) {
|
parameterFlipUnsaved(state) {
|
||||||
state.unsaved = !state.unsaved;
|
state.unsaved = !state.unsaved;
|
||||||
},
|
},
|
||||||
|
parameterUpdateIndex(state, action) {
|
||||||
|
state.tabIndex = action.payload;
|
||||||
|
},
|
||||||
|
parameterFromFile(state, action) {
|
||||||
|
parametersAdapter.upsertMany(state, action.payload);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -404,6 +463,8 @@ export const {
|
|||||||
parameterUpdated,
|
parameterUpdated,
|
||||||
parameterRefreshAll,
|
parameterRefreshAll,
|
||||||
parameterFlipUnsaved,
|
parameterFlipUnsaved,
|
||||||
|
parameterFromFile,
|
||||||
|
parameterUpdateIndex,
|
||||||
} = parametersSlice.actions;
|
} = parametersSlice.actions;
|
||||||
|
|
||||||
export default parametersSlice.reducer;
|
export default parametersSlice.reducer;
|
||||||
|
|||||||
@@ -4,14 +4,17 @@ import Vocoder from './Vocoder';
|
|||||||
import Effects from './Effects';
|
import Effects from './Effects';
|
||||||
import Arpeggio from './Arpeggio';
|
import Arpeggio from './Arpeggio';
|
||||||
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
|
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import styles from './Timbre.module.css';
|
import styles from './Timbre.module.css';
|
||||||
|
|
||||||
export default function TimbreSwitcher(props) {
|
export default function TimbreSwitcher() {
|
||||||
|
const index = useSelector((state) => state.parameters.tabIndex);
|
||||||
return (
|
return (
|
||||||
<div className={styles.tabs}>
|
<div className={styles.tabs}>
|
||||||
<Tabs
|
<Tabs
|
||||||
className={styles.tabs}
|
className={styles.tabs}
|
||||||
defaultIndex={0}
|
selectedIndex={index}
|
||||||
|
onSelect={() => {}}
|
||||||
selectedTabClassName={styles.selected}
|
selectedTabClassName={styles.selected}
|
||||||
selectedTabPanelClassName={styles.tabPanel}>
|
selectedTabPanelClassName={styles.tabPanel}>
|
||||||
<TabList className={styles.tabList}>
|
<TabList className={styles.tabList}>
|
||||||
@@ -23,19 +26,19 @@ export default function TimbreSwitcher(props) {
|
|||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<Timbre id="timbre1" />
|
<Timbre id='timbre1' />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<Timbre id="timbre2" />
|
<Timbre id='timbre2' />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<Vocoder id="vocoder" />
|
<Vocoder id='vocoder' />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<Effects id="effects" />
|
<Effects id='effects' />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<Arpeggio id="arpeggio" />
|
<Arpeggio id='arpeggio' />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&family=Noto+Sans+HK&family=Roboto:wght@100;300;400&display=swap');
|
|
||||||
/* import Lato and Roboto and Noto Fonts*/
|
|
||||||
+2
-3
@@ -1,8 +1,7 @@
|
|||||||
@import url(./fonts.css);
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',
|
font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Oxygen',
|
||||||
'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
sans-serif;
|
sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><g fill="#764ABC"><path d="M65.6 65.4c2.9-.3 5.1-2.8 5-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 1.5.7 2.8 1.6 3.7-3.4 6.7-8.6 11.6-16.4 15.7-5.3 2.8-10.8 3.8-16.3 3.1-4.5-.6-8-2.6-10.2-5.9-3.2-4.9-3.5-10.2-.8-15.5 1.9-3.8 4.9-6.6 6.8-8-.4-1.3-1-3.5-1.3-5.1-14.5 10.5-13 24.7-8.6 31.4 3.3 5 10 8.1 17.4 8.1 2 0 4-.2 6-.7 12.8-2.5 22.5-10.1 28-21.4z"/><path d="M83.2 53c-7.6-8.9-18.8-13.8-31.6-13.8H50c-.9-1.8-2.8-3-4.9-3h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 3 2.6 5.4 5.6 5.4h.2c2.2-.1 4.1-1.5 4.9-3.4H52c7.6 0 14.8 2.2 21.3 6.5 5 3.3 8.6 7.6 10.6 12.8 1.7 4.2 1.6 8.3-.2 11.8-2.8 5.3-7.5 8.2-13.7 8.2-4 0-7.8-1.2-9.8-2.1-1.1 1-3.1 2.6-4.5 3.6 4.3 2 8.7 3.1 12.9 3.1 9.6 0 16.7-5.3 19.4-10.6 2.9-5.8 2.7-15.8-4.8-24.3z"/><path d="M32.4 67.1c.1 3 2.6 5.4 5.6 5.4h.2c3.1-.1 5.5-2.7 5.4-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-.2 0-.5 0-.7.1-4.1-6.8-5.8-14.2-5.2-22.2.4-6 2.4-11.2 5.9-15.5 2.9-3.7 8.5-5.5 12.3-5.6 10.6-.2 15.1 13 15.4 18.3 1.3.3 3.5 1 5 1.5-1.2-16.2-11.2-24.6-20.8-24.6-9 0-17.3 6.5-20.6 16.1-4.6 12.8-1.6 25.1 4 34.8-.5.7-.8 1.8-.7 2.9z"/></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
Reference in New Issue
Block a user