diff --git a/src/App.js b/src/App.js index 25b1ed5..7428747 100644 --- a/src/App.js +++ b/src/App.js @@ -1,62 +1,58 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import logo from "./logo.svg"; import "./App.css"; -import { makeStyles } from "@material-ui/styles"; -import Slider from "@material-ui/core/Slider"; +import FileIOComponent from "./components/FileIOComponent"; import MidiComponent from "./components/MidiComponent"; +import EditorComponent from "./components/EditorComponent"; -const useStyles = makeStyles({ - root: { - width: 127, - }, -}); +function PatchEditor() { + const defaultPatch = { + name: "", + arpeggio: { length: 8, pattern: [0, 0, 0, 0, 0, 0, 0, 0] }, + arpeggio2: {}, + voiceMode: 0, + scaleKey: 0, // C + scaleType: 0, // Equal Temperment + delayFX: { sync: false, timeBase: 5, delayTime: 0, depth: 0, type: 0 }, + modFX: { lfoSpeed: 0, depth: 0, type: 0 }, + eq: { hiFreq: 0, hiGain: 0, lowFreq: 0, lowGain: 0 }, + octave: 0, // this is a 7 bit number so -1 is 127 and -3 is 125 + synthParameter1: {}, // only use this one in single voice mode + synthParameter2: {}, // use this and the previous in multi voice mode + synthParameter3: {}, // this is for vocoder mode + }; -function valuetext(value) { - return `${value}`; -} + const [patch, updatePatch] = useState(defaultPatch); -function SettingSlider() { - const classes = useStyles(); - const [value, setValue] = React.useState(0); - const handleEvent = (event, newValue) => { - setValue(newValue); - }; - - return ( -
- -
- ); + return ( +
+ + + +
+ ); } function App() { - return ( -
-
- logo -
- - + return ( +
+
+ logo +
+ +
+ + Learn React + +
- - Learn React - -
-
- ); + ); } export default App; diff --git a/src/components/EditorComponent.js b/src/components/EditorComponent.js new file mode 100644 index 0000000..f068bcf --- /dev/null +++ b/src/components/EditorComponent.js @@ -0,0 +1,39 @@ +import React from "react"; +import { NameInput } from "./InputComponents.js"; +import { createMuiTheme, ThemeProvider } from "@material-ui/styles"; +import Box from "@material-ui/core/Box"; +import { deepPurple } from "@material-ui/core/colors"; + +const theme = createMuiTheme({ + palette: { + primary: { + main: deepPurple[400], + }, + secondary: {}, + }, +}); + +function EditorComponent(props) { + const patch = props.patch; + + if (!props.show) { + return null; + } + + return ( + // placeholder div + + + { + let copyPatch = JSON.parse(JSON.stringify(patch)); + copyPatch.name = name; + props.updatePatch(copyPatch); + }} + /> + + + ); +} + +export default EditorComponent; diff --git a/src/components/FileIOComponent.js b/src/components/FileIOComponent.js new file mode 100644 index 0000000..5168455 --- /dev/null +++ b/src/components/FileIOComponent.js @@ -0,0 +1,118 @@ +import React, { useState, useEffect } from "React"; + +function isValidSysex(data) { + if (data.length !== 297) { + console.log("incorrect file length"); + return false; + } + if (data[0] !== 0xf0) { + console.log("first byte is not SYSEX byte"); + return false; + } + if (data[1] !== 0x42) { + console.log("manufacturer code is not KORG"); + return false; + } + if (data[data.length - 1] !== 0xf7) { + console.log("last byte is does not end SYSEX message"); + return false; + } + if (data[4] !== 0x40) { + console.log("not a program dump"); + return false; + } + + return true; +} + +function loadSysex(input) { + let file = input.current.files[0]; + console.log(file); + let reader = new FileReader(); + reader.onload = (e) => { + let data = new Uint8ClampedArray(e.target.result); + console.log(data); + if (isValidSysex(data)) { + console.log("read in as valid sysex file"); + return data; + } else { + console.log("could not verify sysex"); + return null; + } + }; + reader.readAsArrayBuffer(file); +} + +// converts SYSEX file to the dump format the system uses (and the implementation refers to) +function dumpBufferFromSysexFile(array) { + // remove the header and footer bytes + let dump = array.slice(5, -1); + // quick test for verification; + if (dump[0] !== 0x00) { + console.log("incorrect byte"); + return; + } + console.log("trimmed file", dump); + // convert from 291 bytes to 254 (ala the implementation) + dump = dump.filter(function (num, index, array) { + return index % 8; + }); + console.log("dump format", dump); + return dump; +} + +function LoadSYSEXFile(props) { + const fileInput = React.useRef(); + const [data, updateData] = useState(new Uint8ClampedArray()); + + // make sure data is updating correctly + useEffect(() => { + console.log(data); + }, [data]); + + // on file load, verify correct file format + const getFile = (event) => { + event.preventDefault(); + if (loadSysex(fileInput) !== null) { + let reader = new FileReader(); + reader.onload = (e) => { + // here we load the file into a clamped byte array and trim and convert it to the dump format (see implementation) + let l_data = new Uint8ClampedArray(e.target.result); + // updata state with dump format + updateData(dumpBufferFromSysexFile(l_data)); + }; + reader.readAsArrayBuffer(fileInput.current.files[0]); + } else { + alert("Please load a SYSEX File"); + return null; + } + }; + + // on submit button press, pass the file to the parent component + const handleInput = (event) => { + event.preventDefault(); + if (fileLoaded()) { + props.sendFileData(data); + } + }; + + const fileLoaded = () => { + console.log(data.length); + return data.length === 254; + }; + + return ( +
+ +
+ +
+ ); +} + +export default LoadSYSEXFile; diff --git a/src/components/InputComponents.js b/src/components/InputComponents.js index 7a5fa13..b5ffcbe 100644 --- a/src/components/InputComponents.js +++ b/src/components/InputComponents.js @@ -5,6 +5,7 @@ import MenuItem from "@material-ui/core/MenuItem"; import InputLabel from "@material-ui/core/InputLabel"; import FormControl from "@material-ui/core/FormControl"; import TextField from "@material-ui/core/TextField"; +import Slider from "@material-ui/core/Slider"; const useStyles = makeStyles((theme) => ({ formControl: { @@ -14,8 +15,37 @@ const useStyles = makeStyles((theme) => ({ selectEmpty: { marginTop: theme.spacing(2), }, + root: { + width: 127, + }, })); +function SettingSlider() { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + + const handleEvent = (event, newValue) => { + setValue(newValue); + }; + + const valuetext = (value) => { + return `${value}`; + }; + + return ( +
+ +
+ ); +} + function NameInput(props) { const [name, setName] = useState("init"); @@ -77,5 +107,6 @@ function SettingSelect(props) { export default { SettingSelect, + SettingSlider, NameInput, }; diff --git a/src/components/MidiComponent.js b/src/components/MidiComponent.js index 1b507d6..73fbb2c 100644 --- a/src/components/MidiComponent.js +++ b/src/components/MidiComponent.js @@ -15,125 +15,11 @@ function onMIDIFailure() { console.log("Could not access MIDI devices!"); } -function recieveMIDIMessage(message) { - console.log(message); -} - -function isValidSysex(data) { - if (data.length !== 297) { - console.log("incorrect file length"); - return false; - } - if (data[0] !== 0xf0) { - console.log("first byte is not SYSEX byte"); - return false; - } - if (data[1] !== 0x42) { - console.log("manufacturer code is not KORG"); - return false; - } - if (data[data.length - 1] !== 0xf7) { - console.log("last byte is does not end SYSEX message"); - return false; - } - if (data[4] !== 0x40) { - console.log("not a program dump"); - return false; - } - - return true; -} - -function loadSysex(input) { - let file = input.current.files[0]; - console.log(file); - let reader = new FileReader(); - reader.onload = (e) => { - let data = new Uint8ClampedArray(e.target.result); - console.log(data); - if (isValidSysex(data)) { - console.log("read in as valid sysex file"); - return data; - } else { - console.log("could not verify sysex"); - return null; - } - }; - reader.readAsArrayBuffer(file); -} - -// converts SYSEX file to the dump format the system uses (and the implementation refers to) -function dumpBufferFromSysexFile(array) { - // remove the header and footer bytes - let dump = array.slice(5, -1); - // quick test for verification; - if (dump[0] !== 0x00) { - console.log("incorrect byte"); - return; - } - console.log("trimmed file", dump); - // convert from 291 bytes to 254 (ala the implementation) - dump = dump.filter(function (num, index, array) { - return index % 8; - }); - console.log("dump format", dump); - return dump; -} +// function recieveMIDIMessage(message) { +// console.log(message); +// } // Component to load a SYSEX File into the MIDI component -function LoadSYSEXFile(props) { - const fileInput = React.useRef(); - const [data, updateData] = useState(new Uint8ClampedArray()); - - // make sure data is updating correctly - useEffect(() => { - console.log(data); - }, [data]); - - // on file load, verify correct file format - const getFile = (event) => { - event.preventDefault(); - if (loadSysex(fileInput) !== null) { - let reader = new FileReader(); - reader.onload = (e) => { - // here we load the file into a clamped byte array and trim and convert it to the dump format (see implementation) - let l_data = new Uint8ClampedArray(e.target.result); - // updata state with dump format - updateData(dumpBufferFromSysexFile(l_data)); - }; - reader.readAsArrayBuffer(fileInput.current.files[0]); - } else { - alert("Please load a SYSEX File"); - return null; - } - }; - - // on submit button press, pass the file to the parent component - const handleInput = (event) => { - event.preventDefault(); - if (fileLoaded()) { - props.sendFileData(data); - } - }; - - const fileLoaded = () => { - console.log(data.length); - return data.length === 254; - }; - - return ( -
- -
- -
- ); -} function MIDIComponent() { const [loadedFile, loadFile] = useState(null); @@ -153,7 +39,7 @@ function MIDIComponent() { loadFile(childData); }; - return ; + return; //; } export default MIDIComponent; diff --git a/src/components/PatchEditorComponent.js b/src/components/PatchEditorComponent.js deleted file mode 100644 index 68a8813..0000000 --- a/src/components/PatchEditorComponent.js +++ /dev/null @@ -1,60 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { NameInput } from "./InputComponents.js"; -import { createMuiTheme, makeStyles, ThemeProvider } from "@material-ui/styles"; -import Box from "@material-ui/core/Box"; -import { deepPurple } from "@material-ui/core/colors"; - -const theme = createMuiTheme({ - palette: { - primary: { - main: deepPurple[400], - }, - secondary: {}, - }, -}); - -function PatchEditorComponent(props) { - const defaultPatch = { - name: "", - arpeggio: { length: 8, pattern: [0, 0, 0, 0, 0, 0, 0, 0] }, - arpeggio2: {}, - voiceMode: 0, - scaleKey: 0, // C - scaleType: 0, // Equal Temperment - delayFX: { sync: false, timeBase: 5, delayTime: 0, depth: 0, type: 0 }, - modFX: { lfoSpeed: 0, depth: 0, type: 0 }, - eq: { hiFreq: 0, hiGain: 0, lowFreq: 0, lowGain: 0 }, - octave: 0, // this is a 7 bit number so -1 is 127 and -3 is 125 - synthParameter1: {}, // only use this one in single voice mode - synthParameter2: {}, // use this and the previous in multi voice mode - synthParameter3: {}, // this is for vocoder mode - }; - - const [patch, updatePatch] = useState(defaultPatch); - - useEffect(() => { - // update the dump and send out a sysex message to the system - console.log(patch); - }, [patch]); - - if (!props.show) { - return null; - } - - return ( - // placeholder div - - - { - let copyPatch = JSON.parse(JSON.stringify(patch)); - copyPatch.name = name; - updatePatch(copyPatch); - }} - /> - - - ); -} - -export default PatchEditorComponent;