Dark Mode, Toob Neural Amp Modeler

This commit is contained in:
Robin Davies
2023-07-03 07:02:59 -04:00
parent 28215eb1d5
commit 15ae8ec049
65 changed files with 1169 additions and 223 deletions
+5 -10
View File
@@ -21,6 +21,7 @@ add_custom_command(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/react
DEPENDS
src/SelectThemeDialog.tsx
src/OkCancelDialog.tsx
src/PluginControl.tsx
src/reportWebVitals.ts
@@ -133,9 +134,9 @@ add_custom_command(
public/img/fx_distortion.svg
public/img/Pi-Logo-3.png
public/img/fx_oscillator.svg
public/img/save_bank_as_white.svg
public/img/edit_banks_white.svg
public/img/settings_black_24dp.svg
src/svg/ic_save_bank_as.svg
src/svg/ic_edit_banks.svg
src/svg/ic_settings.svg
public/img/fx_filter.svg
public/img/delete_outline_black_24dp.svg
public/img/fx_function.svg
@@ -150,7 +151,7 @@ add_custom_command(
public/img/cc-by.png
public/img/fx_split_a.svg
public/img/fx_utility.svg
public/img/help_outline_white_24dp.svg
src/svg/ic_help_outline.svg
public/img/ic_navigate_before.svg
public/img/ic_drawer_2.svg
public/img/fx_parametric_eq.svg
@@ -161,10 +162,8 @@ add_custom_command(
public/img/fx_analyzer.svg
public/img/fx_delay.svg
public/img/fx_spectral.svg
public/img/edit_banks.svg
public/img/fx_chorus.svg
public/img/fx_simulator.svg
public/img/drive_file_rename_outline_black_24dp.svg
public/img/fx_mixer.svg
public/img/fx_eq.svg
public/img/fx_modulator.svg
@@ -173,7 +172,6 @@ add_custom_command(
public/img/fx_phaser.svg
public/img/ic_drawer.svg
public/img/fx_amplifier.svg
public/img/save_bank_as.svg
public/img/fx_lr.svg
public/img/fx_instrument.svg
public/img/Pi-Logo-3.svg
@@ -183,14 +181,11 @@ add_custom_command(
public/img/fx_empty.svg
public/img/fx_dial.svg
public/img/ic_logo.svg
public/img/help_outline_black_24dp.svg
public/img/fx_flanger2.svg
public/img/old_delete_outline_white_24dp.svg
public/img/fx_split_b.svg
public/img/fx_spatial.svg
public/img/fx_converter.svg
public/img/settings_white_24dp.svg
public/img/drive_file_rename_outline_white_24dp.svg
public/img/vst.svg
public/img/vst.png
public/sample_lv2_plugins.json
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><g><polygon points="15,16 11,20 21,20 21,16"/><path d="M12.06,7.19L3,16.25V20h3.75l9.06-9.06L12.06,7.19z M5.92,18H5v-0.92l7.06-7.06l0.92,0.92L5.92,18z"/><path d="M18.71,8.04c0.39-0.39,0.39-1.02,0-1.41l-2.34-2.34C16.17,4.09,15.92,4,15.66,4c-0.25,0-0.51,0.1-0.7,0.29l-1.83,1.83 l3.75,3.75L18.71,8.04z"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 503 B

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><g><rect fill="none" height="24" width="24"/></g><g><g><polygon points="15,16 11,20 21,20 21,16"/><path d="M12.06,7.19L3,16.25V20h3.75l9.06-9.06L12.06,7.19z M5.92,18H5v-0.92l7.06-7.06l0.92,0.92L5.92,18z"/><path d="M18.71,8.04c0.39-0.39,0.39-1.02,0-1.41l-2.34-2.34C16.17,4.09,15.92,4,15.66,4c-0.25,0-0.51,0.1-0.7,0.29l-1.83,1.83 l3.75,3.75L18.71,8.04z"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 503 B

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"/></svg>

Before

Width:  |  Height:  |  Size: 402 B

-15
View File
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve" fill="#FFFFFF">
<path d="M16,1H4C2.9,1,2,1.9,2,3v14h2V3h12V1z"/>
<path d="M20.993,10.993L20.99,7c0-1.1-0.891-2-1.99-2h-4h-3H8C6.9,5,6.01,5.9,6.01,7l-0.003,3.993L6,11v10c0,1.1,0.891,2,1.99,2H8
h11h0.01c1.1,0,1.99-0.9,1.99-2V11L20.993,10.993z M19,12v9H8v-9V7h5h1h5V12z"/>
<rect x="10" y="9" width="2" height="2"/>
<rect x="14" y="9" width="3" height="2"/>
<rect x="10" y="13" width="2" height="2"/>
<rect x="14" y="13" width="3" height="2"/>
<rect x="10" y="17" width="2" height="2"/>
<rect x="14" y="17" width="3" height="2"/>
</svg>

Before

Width:  |  Height:  |  Size: 1012 B

+38 -24
View File
@@ -1,26 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="PiPedal Guitar Pedals"
/>
<!--
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="PiPedal Guitar Pedals" />
<!--
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
-->
<link rel="stylesheet" href="/css/roboto.css" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
<link rel="stylesheet" href="/css/roboto.css" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
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/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
@@ -29,14 +27,30 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>PiPedal</title>
<style>
BODY { background: #D0D0D0; }
@media (prefers-color-scheme: dark) {
BODY { background: #333; }
}
<title>PiPedal</title>
<style>
BODY {
background: #D0D0D0;
}
</style>
</style>
<style id="bgStyle">
BODY {
background: #333;
}
</style>
<script>
const darkMode = localStorage.getItem("darkMode"); if (darkMode) { }
if (darkMode)
{
let bgStyle = document.getElementById("bgStyle");
if (bgStyle)
{
// disable the style block.
bgStyle.setAttribute('media',"max-width: 1px");
}
}
</script>
</head>
<body >
<noscript>You need to enable JavaScript to run this app.</noscript>
@@ -52,4 +66,4 @@
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
</html>
+10 -5
View File
@@ -25,10 +25,12 @@ import isDarkMode from './DarkMode';
declare module '@mui/material/styles' {
interface Theme {
mainBackground: string;
mainBackground: React.CSSProperties['color'];
toolbarColor: React.CSSProperties['color'];
}
interface ThemeOptions {
mainBackground?: string;
mainBackground?: React.CSSProperties['color'];
toolbarColor?: React.CSSProperties['color'];
}
}
@@ -50,13 +52,14 @@ const theme = createTheme(
palette: {
mode: 'dark',
primary: {
main: "#6750A4" // #5B5690 #60529A #5C5694
main: '#A770E4'// #6750A4" // #5B5690 #60529A #5C5694
},
secondary: {
main: "#FF6060"
},
},
mainBackground: "#222"
mainBackground: "#222",
toolbarColor: '#FFFFFF'
}
:
{
@@ -69,7 +72,9 @@ const theme = createTheme(
}
},
mainBackground: "#FFFFFF"
mainBackground: "#FFFFFF",
toolbarColor: '#FFFFFF'
}
);
-5
View File
@@ -16,8 +16,3 @@ div {
min-width: 0;
}
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
}
}
+37 -29
View File
@@ -62,14 +62,27 @@ import JackStatusView from './JackStatusView';
import { Theme } from '@mui/material/styles';
import isDarkMode from './DarkMode';
import {ReactComponent as RenameOutlineIcon} from './svg/drive_file_rename_outline_black_24dp.svg';
import {ReactComponent as SaveBankAsIcon} from './svg/ic_save_bank_as.svg';
import {ReactComponent as EditBanksIcon} from './svg/ic_edit_banks.svg';
import {ReactComponent as SettingsIcon} from './svg/ic_settings.svg';
import {ReactComponent as HelpOutlineIcon} from './svg/ic_help_outline.svg';
const appStyles = (theme: Theme) => createStyles({
menuIcon: {
fill: "green"
"&": { // :root
colorScheme: (isDarkMode()? "dark": "light")
},
appBar: {
menuListItem: {
color: "#FE8!important" as any, //theme.palette.text.primary,
fill: "#FE8!important" as any, //theme.palette.text.primary,
},
menuIcon: {
fill: (theme.palette.text.primary + "!important") as any, //theme.palette.text.primary,
opacity: 0.6
},
toolBar: {
color: "white"
},
listSubheader: {
backgroundImage: "linear-gradient(255,255,255,0.15),rgba(255,255,255,0.15)"
@@ -499,7 +512,7 @@ const AppThemed = withStyles(appStyles)(class extends ResizeResponsiveComponent<
private unmountListener(e: Event) {
e.preventDefault();
if (this.model_.state.get() === State.Ready && !this.model_.isAndroidHosted()) {
if ((!this.model_.reloadRequested) && this.model_.state.get() === State.Ready && !this.model_.isAndroidHosted()) {
(e as any).returnValue = "Are you sure you want to leave this page?";
return "Are you sure you want to leave this page?";
}
@@ -652,6 +665,7 @@ render() {
return (
<div style={{
colorScheme: isDarkMode() ? "dark": "light", // affects scrollbar color
minHeight: 345, minWidth: 390,
position: "absolute", width: "100%", height: "100%", background: "#F88", userSelect: "none",
display: "flex", flexDirection: "column", flexWrap: "nowrap",
@@ -666,14 +680,15 @@ render() {
<CssBaseline />
{(!this.state.tinyToolBar) ?
(
<AppBar className="appBar" position="absolute" >
<Toolbar variant="dense" >
<AppBar position="absolute" >
<Toolbar variant="dense" className={classes.toolBar} >
<IconButton
edge="start"
aria-label="menu"
color="inherit"
onClick={() => { this.showDrawer() }}
size="large">
<MenuButton />
<MenuButton style={{opacity: 0.75}} />
</IconButton>
<div style={{ flex: "0 1 400px", minWidth: 100 }}>
<PresetSelector />
@@ -683,11 +698,12 @@ render() {
<IconButton
aria-label="menu"
onClick={() => { this.toggleFullScreen(); }}
color="inherit"
size="large">
{this.state.isFullScreen ? (
<FullscreenExitIcon />
<FullscreenExitIcon style={{opacity: 0.75}} />
) : (
<FullscreenIcon />
<FullscreenIcon style={{opacity: 0.75}} />
)}
@@ -701,6 +717,7 @@ render() {
style={{ position: "absolute", left: 12, top: 8, zIndex: 2 }}
aria-label="menu"
onClick={() => { this.showDrawer() }}
color="inherit"
size="large">
<MenuButton />
</IconButton>
@@ -708,6 +725,7 @@ render() {
<IconButton
style={{ position: "absolute", right: 8, top: 8, zIndex: 2 }}
aria-label="menu"
color="inherit"
onClick={() => { this.toggleFullScreen(); }}
size="large">
{this.state.isFullScreen ? (
@@ -756,26 +774,20 @@ render() {
<Divider />
<List>
<ListItem button key='RenameBank' onClick={() => { this.handleDrawerRenameBank() }}>
<ListItemIcon>
<img className='menuIcon'
src={isDarkMode()? "img/drive_file_rename_outline_white_24dp.svg" :"img/drive_file_rename_outline_black_24dp.svg"}
alt="" style={{ opacity: 0.6 }} />
<ListItemIcon >
<RenameOutlineIcon color='inherit' className={classes.menuIcon} />
</ListItemIcon>
<ListItemText primary='Rename Bank' />
</ListItem>
<ListItem button key='SaveBank' onClick={() => { this.handleDrawerSaveBankAs() }} >
<ListItemIcon>
<img className='menuIcon'
src={isDarkMode()? "img/save_bank_as_white.svg" :"img/save_bank_as.svg"}
alt="" style={{ opacity: 0.6 }} />
<SaveBankAsIcon color="inherit" className={classes.menuIcon}/>
</ListItemIcon>
<ListItemText primary='Save As New Bank' />
</ListItem>
<ListItem button key='CreateBank' onClick={() => { this.handleDrawerManageBanks(); }}>
<ListItem button key='EditBanks' onClick={() => { this.handleDrawerManageBanks(); }}>
<ListItemIcon>
<img className='menuIcon'
src={isDarkMode()? "img/edit_banks_white.svg" :"img/edit_banks.svg"}
alt="" style={{ opacity: 0.6 }} />
<EditBanksIcon color="inherit" className={classes.menuIcon}/>
</ListItemIcon>
<ListItemText primary='Manage Banks...' />
</ListItem>
@@ -784,23 +796,19 @@ render() {
<List>
<ListItem button key='Settings' onClick={() => { this.handleDrawerSettingsClick() }}>
<ListItemIcon>
<img className='menuIcon'
src={isDarkMode()? "img/settings_white_24dp.svg" :"img/settings_black_24dp.svg"}
alt="" style={{ opacity: 0.6 }} />
<SettingsIcon color="inherit" className={classes.menuIcon}/>
</ListItemIcon>
<ListItemText primary='Settings' />
</ListItem>
<ListItem button key='About' onClick={() => { this.handleDrawerAboutClick() }}>
<ListItemIcon>
<img className='menuIcon'
src= {isDarkMode() ? "img/help_outline_white_24dp.svg": "img/help_outline_black_24dp.svg"}
alt="" style={{ opacity: 0.6 }} />
<HelpOutlineIcon color="inherit" className={classes.menuIcon}/>
</ListItemIcon>
<ListItemText primary='About' />
</ListItem>
<ListItem button key='Donations' onClick={() => { this.handleDrawerDonationClick() }}>
<ListItemIcon>
<VolunteerActivismIcon style={{ opacity: 0.6 }}/>
<ListItemIcon >
<VolunteerActivismIcon className={classes.menuIcon} color="inherit" />
</ListItemIcon>
<ListItemText primary='Donations' />
</ListItem>
+6 -4
View File
@@ -19,8 +19,10 @@
export default function isDarkMode(): boolean {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
return true;
}
return false;
let value = localStorage.getItem("darkMode");
return value === "true";
}
export function setDarkMode(value: boolean): void {
localStorage.setItem("darkMode",value? "true": "false");
}
+1 -1
View File
@@ -602,7 +602,7 @@ export const LoadPluginDialog =
<SelectHoverBackground selected={value.uri === this.state.selected_uri} showHover={true} />
<div className={classes.content}>
<div className={classes.iconBorder} >
<PluginIcon pluginType={value.plugin_type} size={24} />
<PluginIcon pluginType={value.plugin_type} size={24} opacity={0.6} />
</div>
<div className={classes.content2}>
<div className={classes.label} style={{ display: "flex", flexFlow: "row nowrap", alignItems: "center" }} >
+65 -2
View File
@@ -204,6 +204,42 @@ export class UiPropertyNotification {
plugin: string = "";
protocol: string = "";
};
export class UiFrequencyPlot {
deserialize(input: any): UiFrequencyPlot
{
this.patchProperty = input.patchProperty;
this.index = input.index;
this.portGroup = input.portGroup;
this.xLeft = input.xLeft;
this.xRight = input.xRight;
this.xLog = input.xLog;
this.yTop = input.yTop;
this.yBottom = input.yBottom;
this.width = input.width;
return this;
}
static deserialize_array(input: any): UiFrequencyPlot[]
{
let result: UiFrequencyPlot[] = [];
for (let i = 0; i < input.length; ++i)
{
result[i] = new UiFrequencyPlot().deserialize(input[i]);
}
return result;
}
patchProperty: string = "";
index: number = -1;
portGroup: string = "";
xLeft: number = -1;
xRight: number = -1;
xLog: boolean = true;
yTop: number = -1;
yBottom: number = -1;
width: number = -1;
};
export class UiFileProperty {
deserialize(input: any): UiFileProperty
@@ -253,6 +289,10 @@ export class UiFileProperty {
{
return true;
}
if (fileType.fileExtension === "*" || fileType.fileExtension === "")
{
return true;
}
}
return false;
}
@@ -270,6 +310,8 @@ export class Lv2Plugin implements Deserializable<Lv2Plugin> {
{
this.uri = input.uri;
this.name = input.name;
this.brand = input.name? input.name: "";
this.label = input.label? input.label: this.name;
this.plugin_class = input.plugin_class;
this.supported_features = input.supported_features;
this.required_features = input.required_features;
@@ -285,6 +327,13 @@ export class Lv2Plugin implements Deserializable<Lv2Plugin> {
} else {
this.fileProperties = [];
}
if (input.frequencyPlots)
{
this.frequencyPlots = UiFrequencyPlot.deserialize_array(input.frequencyPlots)
} else {
this.frequencyPlots = [];
}
if (input.uiPortNotifications)
{
this.uiPortNotifications = UiPropertyNotification.deserialize_array(input.uiPortNotifications);
@@ -297,6 +346,8 @@ export class Lv2Plugin implements Deserializable<Lv2Plugin> {
uri: string = "";
name: string = "";
brand: string = "";
label: string = "";
plugin_class: string = "";
supported_features: string[] = Lv2Plugin.EmptyFeatures;
required_features: string[] = Lv2Plugin.EmptyFeatures;
@@ -306,7 +357,8 @@ export class Lv2Plugin implements Deserializable<Lv2Plugin> {
comment: string = "";
ports: Port[] = Port.EmptyPorts;
port_groups: PortGroup[] = [];
fileProperties: UiFileProperty[] = [];
fileProperties: UiFileProperty[] = [];
frequencyPlots: UiFrequencyPlot[] = [];
uiPortNotifications: UiPropertyNotification[] = [];
}
@@ -668,6 +720,8 @@ export class UiPlugin implements Deserializable<UiPlugin> {
{
this.uri = input.uri;
this.name = input.name;
this.brand = input.brand ? input.brand: "";
this.label = input.label? input.label: this.name;
this.plugin_type = input.plugin_type as PluginType;
this.plugin_display_type = input.plugin_display_type;
this.author_name = input.author_name;
@@ -685,6 +739,12 @@ export class UiPlugin implements Deserializable<UiPlugin> {
} else {
this.fileProperties = [];
}
if (input.frequencyPlots)
{
this.frequencyPlots = UiFrequencyPlot.deserialize_array(input.frequencyPlots)
} else {
this.frequencyPlots = [];
}
this.is_vst3 = input.is_vst3;
return this;
@@ -738,6 +798,8 @@ export class UiPlugin implements Deserializable<UiPlugin> {
uri: string = "";
name: string = "";
brand: string = "";
label: string = "";
plugin_type: PluginType = PluginType.InvalidPlugin;
plugin_display_type: string = "";
author_name: string = "";
@@ -749,7 +811,8 @@ export class UiPlugin implements Deserializable<UiPlugin> {
description: string = "";
controls: UiControl[] = [];
port_groups: PortGroup[] = [];
fileProperties: UiFileProperty[] = [];
fileProperties: UiFileProperty[] = [];
frequencyPlots: UiFrequencyPlot[] = [];
is_vst3 : boolean = false;
}
+4 -3
View File
@@ -47,7 +47,7 @@ import MidiBindingsDialog from './MidiBindingsDialog';
import PluginPresetSelector from './PluginPresetSelector';
import {ReactComponent as OldDeleteIcon} from "./svg/old_delete_outline_24dp.svg";
import {ReactComponent as MidiIcon} from "./svg/ic_midi.svg";
import isDarkMode from './DarkMode';
const SPLIT_CONTROLBAR_THRESHHOLD = 650;
@@ -463,7 +463,7 @@ export const MainPage =
</div>
<div style={{ flex: "0 0 auto", display: (canInsert || canAppend) ? "block" : "none", paddingRight: 8 }}>
<IconButton onClick={(e) => { this.onAddClick(e) }} size="large">
<AddIcon style={{opacity: 0.6}} />
<AddIcon style={{height: 24, width: 24,fill: this.props.theme.palette.text.primary, opacity: 0.6}} />
</IconButton>
<Menu
id="add-menu"
@@ -495,7 +495,8 @@ export const MainPage =
onClick={this.onLoadClick}
disabled={this.state.selectedPedal === -1 || (!canLoad) || (this.getSelectedPedalboardItem()?.isSplit() ?? true)}
startIcon={<InputIcon />}
style={{ textTransform: "none" }}
style={{ textTransform: "none",
background: (isDarkMode() ? "#6750A4": undefined) }}
>
Load
</Button>
+3 -3
View File
@@ -298,7 +298,7 @@ class PedalLayout {
if (uiPlugin != null) {
this.pluginType = uiPlugin.plugin_type;
this.iconUrl = SelectIconUri(uiPlugin.plugin_type);
this.name = uiPlugin.name;
this.name = uiPlugin.label;
this.numberOfInputs = Math.max(uiPlugin.audio_inputs,2);
this.numberOfOutputs = Math.max(uiPlugin.audio_outputs,2);
} else {
@@ -679,7 +679,7 @@ const PedalboardView =
event.preventDefault();
event.stopPropagation();
if (!Utility.isTouchDevice()) {
if (!Utility.needsZoomedControls()) {
if (this.props.onDoubleClick && instanceId) {
this.props.onDoubleClick(instanceId);
}
@@ -1014,7 +1014,7 @@ const PedalboardView =
}}>
<Typography variant="caption" display="block" noWrap={true}
style={{ width: CELL_WIDTH - 4, textAlign: "center", flex: "0 1 auto" }}
>{item.pedalItem?.pluginName}</Typography>
>{item.name}</Typography>
</div>
)
result.push(<div key={this.renderKey++} className={classes.pedalItem} style={{ left: item.bounds.x, top: item.bounds.y }} >
+38 -3
View File
@@ -38,7 +38,12 @@ import GovernorSettings from './GovernorSettings';
import WifiChannel from './WifiChannel';
import AlsaDeviceInfo from './AlsaDeviceInfo';
import { AndroidHostInterface, FakeAndroidHost } from './AndroidHost';
import isDarkMode, {setDarkMode} from './DarkMode';
export enum ColorTheme {
Light,
Dark
};
export enum State {
Loading,
@@ -470,8 +475,11 @@ export class PiPedalModel //implements PiPedalModel
let value = body as number;
this._setInputVolume(value,false);
} else if (message === "onLv2StateChanged") {
let instanceId = body as number;
this.onLv2StateChanged(instanceId);
let instanceId = body.instanceId as number;
let state = body.state as [boolean,any];
this.onLv2StateChanged(instanceId,state);
} else if (message === "onVst3ControlChanged") {
let controlChangedBody = body as Vst3ControlChangedBody;
this._setVst3PedalboardControlValue(
@@ -665,6 +673,10 @@ export class PiPedalModel //implements PiPedalModel
this.getWebSocket().request<number>("hello")
.then(clientId => {
this.clientId = clientId;
return this.getWebSocket().request<any>("plugins");
})
.then(data => {
this.ui_plugins.set(UiPlugin.deserialize_array(data));
return this.getWebSocket().request<Pedalboard>("currentPedalboard");
})
.then(data => {
@@ -1116,7 +1128,9 @@ export class PiPedalModel //implements PiPedalModel
}
}
private onLv2StateChanged(instanceId: number): void {
private onLv2StateChanged(instanceId: number, state: [boolean,any]): void {
let item = this.pedalboard.get().getItem(instanceId);
item.lv2State = state;
for (let item of this.stateChangedListeners) {
if (item.instanceId === instanceId) {
item.onStateChanged(instanceId);
@@ -2531,6 +2545,27 @@ export class PiPedalModel //implements PiPedalModel
return nullCast(this.webSocket).request<number>("newPreset");
}
getTheme(): ColorTheme {
return isDarkMode() ? ColorTheme.Dark: ColorTheme.Light;
}
setTheme(value: ColorTheme) {
if (this.getTheme() !== value)
{
setDarkMode(value === ColorTheme.Dark);
this.reloadPage();
}
}
reloadRequested: boolean = false;
reloadPage() {
this.reloadRequested = true;
// eslint-disable-next-line no-restricted-globals
location.reload();
}
};
let instance: PiPedalModel | undefined = undefined;
+3 -3
View File
@@ -137,7 +137,7 @@ const PluginControl =
this.onInputKeyPress = this.onInputKeyPress.bind(this);
}
isTouchDevice(): boolean {
return Utility.isTouchDevice();
return Utility.needsZoomedControls();
}
showZoomedControl()
@@ -547,7 +547,7 @@ const PluginControl =
);
} else {
return (
<Select variant="standard"
<Select variant="standard"
ref={this.selectRef}
value={control.clampSelectValue(value)}
onChange={this.onSelectChanged}
@@ -556,7 +556,7 @@ const PluginControl =
id: 'id' + control.symbol,
style: { fontSize: FONT_SIZE }
}}
style={{ marginLeft: 4, marginRight: 4, width: 140 }}
style={{ marginLeft: 4, marginRight: 4, width: 140,fontSize: 14 }}
>
{control.scale_points.map((scale_point: ScalePoint) => (
<MenuItem key={scale_point.value} value={scale_point.value}>{scale_point.label}</MenuItem>
+52 -1
View File
@@ -23,7 +23,7 @@ import { WithStyles } from '@mui/styles';
import createStyles from '@mui/styles/createStyles';
import withStyles from '@mui/styles/withStyles';
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
import { UiPlugin, UiControl, UiFileProperty, ScalePoint } from './Lv2Plugin';
import { UiPlugin, UiControl, UiFileProperty,UiFrequencyPlot, ScalePoint } from './Lv2Plugin';
import {
Pedalboard, PedalboardItem, ControlValue
} from './Pedalboard';
@@ -39,6 +39,7 @@ import FilePropertyDialog from './FilePropertyDialog';
import JsonAtom from './JsonAtom';
import PluginOutputControl from './PluginOutputControl';
import Units from './Units';
import ToobFrequencyResponseView from './ToobFrequencyResponseView';
export const StandardItemSize = { width: 80, height: 110 };
@@ -335,6 +336,14 @@ const PluginControlView =
/>
));
}
makeFrequencyPlotUI(frequencyPlot: UiFrequencyPlot): ReactNode {
return ((
<ToobFrequencyResponseView instanceId={this.props.instanceId}
propertyName={frequencyPlot.patchProperty}
width={frequencyPlot.width}
/>
));
}
makeStandardControl(uiControl: UiControl, controlValues: ControlValue[]): ReactNode {
let symbol = uiControl.symbol;
if (!uiControl.is_input) {
@@ -445,6 +454,48 @@ const PluginControlView =
);
}
}
for (let i = 0; i < plugin.frequencyPlots.length; ++i) {
let frequencyPlot = plugin.frequencyPlots[i];
let frequencyPlotUi = this.makeFrequencyPlotUI(frequencyPlot);
if (frequencyPlot.portGroup !== "" && plugin.getPortGroupByUri(frequencyPlot.portGroup)) {
let portGroup = nullCast(plugin.getPortGroupByUri(frequencyPlot.portGroup));
let controlGroup = portGroupMap[portGroup.symbol];
if (controlGroup) {
let insertPosition = controlGroup.indexes.length;
if (frequencyPlot.index !== -1) {
for (let i = 0; i < controlGroup.controls.length; ++i) {
if (controlGroup.indexes[i] >= frequencyPlot.index) {
insertPosition = i;
break;
}
}
}
let index = frequencyPlot.index !== -1 ? frequencyPlot.index : 100;
controlGroup.controls.splice(insertPosition, 0, frequencyPlotUi);
controlGroup.indexes.splice(insertPosition, 0, index);
} else {
let index = frequencyPlot.index !== -1 ? frequencyPlot.index : 100;
let controlGroup = new ControlGroup(
portGroup.name,
[index],
[frequencyPlotUi]);
result.push(
controlGroup
);
portGroupMap[portGroup.symbol] = controlGroup;
}
} else if (frequencyPlot.index !== -1) {
result.splice(frequencyPlot.index, 0, frequencyPlotUi);
} else {
result.push(
frequencyPlotUi
);
}
}
return result;
}
+5 -1
View File
@@ -54,6 +54,10 @@ const styles = (theme: Theme) =>
top: theme.spacing(1),
color: theme.palette.grey[500],
},
icon: {
fill: theme.palette.text.primary,
opacity: 0.6
}
});
};
export interface PluginInfoDialogTitleProps extends WithStyles<typeof styles> {
@@ -228,7 +232,7 @@ const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => {
style={{ display: (props.plugin_uri !== "") ? "inline-flex" : "none" }}
onClick={handleClickOpen}
size="large">
<InfoOutlinedIcon style={{opacity: 0.6}} />
<InfoOutlinedIcon className={classes.icon} color='inherit' />
</IconButton>
{open && (
<Dialog onClose={handleClose} open={open} fullWidth >
+41
View File
@@ -25,6 +25,7 @@ import withStyles from '@mui/styles/withStyles';
import { UiControl } from './Lv2Plugin';
import Typography from '@mui/material/Typography';
import { PiPedalModel, PiPedalModelFactory, MonitorPortHandle,State } from './PiPedalModel';
import isDarkMode from './DarkMode';
@@ -77,11 +78,13 @@ const PluginOutputControl =
private model: PiPedalModel;
private vuRef: React.RefObject<HTMLDivElement>;
private lampRef: React.RefObject<HTMLDivElement>;
constructor(props: PluginOutputControlProps) {
super(props);
this.vuRef = React.createRef<HTMLDivElement>();
this.lampRef = React.createRef<HTMLDivElement>();
this.state = {
hasValue: false,
value: 0
@@ -130,6 +133,12 @@ const PluginOutputControl =
private animationHandle: number | undefined = undefined;
private updateValue(value: number) {
if (this.lampRef.current)
{
let control = this.props.uiControl;
let range = (value-control.min_value)/(control.max_value-control.min_value);
this.lampRef.current.style.opacity = range +"";
}
if (this.vuRef.current) {
let control = this.props.uiControl;
let range = (value-control.min_value)/(control.max_value-control.min_value);
@@ -241,6 +250,38 @@ const PluginOutputControl =
</div >
);
} else if (control.toggled_property && control.scale_points.length === 0) {
item_width = undefined;
let attachedLamp = control.name === "" || control.name === "\u00A0" ;
return (
<div style={{ display: "flex", flexDirection: "column", width: item_width,
marginTop: 8, marginBottom: 8, marginRight: 8,marginLeft: (attachedLamp? 0: 8), height: 98
}}>
{/* TITLE SECTION */}
<div style={{ flex: "0 0 auto", width: "100%", marginBottom: 8, marginLeft: 0, marginRight: 0 }}>
<Typography variant="caption" display="block" style={{
width: "100%",
textAlign: "center"
}}> {control.name === "" ? "\u00A0": control.name}</Typography>
</div>
{/* CONTROL SECTION */}
<div style={{ flex: "1 1 auto", display: "flex", justifyContent: "left", alignItems: "start", flexFlow: "row nowrap" }}>
<div style={{ width: 12, height: 12, background: isDarkMode()? "#111" : "#444", borderRadius: 5, position: "relative" }}>
<div ref={this.lampRef} style={{ width: 8, height: 8,
background: (isDarkMode()? "radial-gradient(circle at center, #F00 0, #F00 20%, #333 100%)"
: "radial-gradient(circle at center, #F44 0, #F44 40%, #644 100%)"),
opacity: 0, borderRadius: 3,margin: 2,position: "absolute" }} />
</div>
</div>
{/* LABEL/EDIT SECTION*, strictly a placeholder for visual alignment purposes.*/}
<div style={{ flex: "0 0 auto", position: "relative", width: 30, height: 27 }}>
&nbsp;
</div>
</div >
);
} else {
return (
<div style={{ display: "flex", flexDirection: "column", width: item_width, margin: 8, height: 98 }}>
+9 -8
View File
@@ -58,7 +58,7 @@ interface PresetSelectorState {
}
const selectColor = isDarkMode()? "#888": "black";
const selectColor = isDarkMode()? "#888": "#FFFFFF";
const styles = (theme: Theme) => createStyles({
select: { // fu fu fu.Overrides for white selector on dark background.
@@ -301,17 +301,17 @@ const PresetSelector =
}}>
<div style={{ flex: "0 0 auto" }}>
<IconButton
style={{ flex: "0 0 auto", opacity: this.state.presets.presetChanged ? 1.0 : 0.0 }}
style={{ flex: "0 0 auto", opacity: this.state.presets.presetChanged ? 1.0 : 0.0, color: "#FFFFFF" }}
onClick={(e) => { this.handleSave(); }}
size="large">
<SaveIconOutline style={{ opacity: 0.75 }} />
<SaveIconOutline style={{ opacity: 0.75 }} color="inherit" />
</IconButton>
</div>
<div style={{ flex: "1 1 auto", minWidth: 60, maxWidth: 300, position: "relative", paddingRight: 12 }} >
<Select variant="standard"
className={classes.select}
style={{ width: "100%", position: "relative", top: 0 }} disabled={!this.state.enabled}
style={{ width: "100%", position: "relative", top: 0, color: "#FFFFFF" }} disabled={!this.state.enabled}
onChange={(e, extra) => this.handleChange(e, extra)}
onClose={(e) => this.handleSelectClose(e)}
displayEmpty
@@ -336,12 +336,13 @@ const PresetSelector =
}
</Select>
</div>
<div style={{ flex: "0 0 auto" }}>
<div style={{ flex: "0 0 auto"}}>
<IconButton
style={{ flex: "0 0 auto" }}
style={{ flex: "0 0 auto", color: "#FFFFFF" }}
onClick={(e) => this.handlePresetMenuClick(e)}
size="large">
<MoreVertIcon style={{ opacity: 0.75 }} />
size="large"
>
<MoreVertIcon style={{ opacity: 0.75 }} color="inherit" />
</IconButton>
<Menu
id="edit-presets-menu"
+85
View File
@@ -0,0 +1,85 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
// Copyright (c) 2023 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// 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 OR
// COPYRIGHT HOLDERS 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.
import { useState } from 'react';
import Button from '@mui/material/Button';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';
import Dialog from '@mui/material/Dialog';
import FormControlLabel from '@mui/material/FormControlLabel';
import FormControl from '@mui/material/FormControl';
import RadioGroup from '@mui/material/RadioGroup';
import Radio from '@mui/material/Radio';
import {ColorTheme} from './PiPedalModel';
export interface SelectThemesDialogProps {
open: boolean;
defaultTheme: ColorTheme;
onClose: () => void;
onOk: (value: ColorTheme) => void;
}
function SelectThemesDialog(props: SelectThemesDialogProps) {
//const classes = useStyles();
const { onClose, defaultTheme, open,onOk } = props;
const [ selectedTheme, setSelectedTheme ] = useState(defaultTheme);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
let value = ((event.target as HTMLInputElement).value) === '0' ? ColorTheme.Light: ColorTheme.Dark;
setSelectedTheme(value);
};
const handleClose = (): void => {
onClose();
};
const handleOk = (): void => {
onOk(selectedTheme)
};
return (
<Dialog onClose={handleClose} open={open}>
<DialogTitle id="simple-dialog-title">Theme</DialogTitle>
<DialogContent>
<FormControl>
<RadioGroup
defaultValue={defaultTheme}
onChange={handleChange}
>
<FormControlLabel value={ColorTheme.Light} control={<Radio size='small' />} label="Light" />
<FormControlLabel value={ColorTheme.Dark} control={<Radio size='small' />} label="Dark" />
</RadioGroup>
</FormControl>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleOk} color="secondary" >
OK
</Button>
</DialogActions>
</Dialog>
);
}
export default SelectThemesDialog;
+67 -26
View File
@@ -23,7 +23,7 @@ import OkCancelDialog from './OkCancelDialog';
import ListSelectDialog from './ListSelectDialog';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import { PiPedalModel, PiPedalModelFactory, State } from './PiPedalModel';
import { PiPedalModel, PiPedalModelFactory, State, ColorTheme } from './PiPedalModel';
import ButtonBase from "@mui/material/ButtonBase";
import AppBar from '@mui/material/AppBar';
import Button from '@mui/material/Button';
@@ -45,6 +45,7 @@ import DialogEx from './DialogEx'
import GovernorSettings from './GovernorSettings';
import { AlsaMidiDeviceInfo } from './AlsaMidiDeviceInfo';
import SystemMidiBindingsDialog from './SystemMidiBindingsDialog';
import SelectThemeDialog from './SelectThemeDialog';
import Slide, { SlideProps } from '@mui/material/Slide';
import { createStyles, Theme } from '@mui/material/styles';
@@ -79,6 +80,7 @@ interface SettingsDialogState {
showInputSelectDialog: boolean;
showOutputSelectDialog: boolean;
showMidiSelectDialog: boolean;
showThemeSelectDialog: boolean;
showJackServerSettingsDialog: boolean;
shuttingDown: boolean;
restarting: boolean;
@@ -184,6 +186,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
showInputSelectDialog: false,
showOutputSelectDialog: false,
showMidiSelectDialog: false,
showThemeSelectDialog: false,
showJackServerSettingsDialog: false,
shuttingDown: false,
restarting: false,
@@ -384,6 +387,11 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
showMidiSelectDialog: true
})
}
handleThemeSelection() {
this.setState({
showThemeSelectDialog: true
});
}
handleMidiMessageSettings() {
this.setState({
showSystemMidiBindingsDialog: true
@@ -553,22 +561,22 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
}}
>
<div >
{this.props.onboarding &&
(
<div>
<Typography display="block" variant="body1" color="textSecondary" style={{paddingLeft: 24,paddingBottom: 8 }}>
Select and configure an audio device. You may optionally configure MIDI inputs, and set up a Wi-Fi Direct Hotspot now as well.
</Typography>
<Typography display="block" variant="body1" color="textSecondary" style={{paddingLeft: 24,paddingBottom: 8 }}>
Access and modify these settings later by selecting the <i>Settings</i> menu item on the main menu.
</Typography>
<Divider />
</div>
)}
{this.props.onboarding &&
(
<div>
<Typography display="block" variant="body1" color="textSecondary" style={{ paddingLeft: 24, paddingBottom: 8 }}>
Select and configure an audio device. You may optionally configure MIDI inputs, and set up a Wi-Fi Direct Hotspot now as well.
</Typography>
<Typography display="block" variant="body1" color="textSecondary" style={{ paddingLeft: 24, paddingBottom: 8 }}>
Access and modify these settings later by selecting the <i>Settings</i> menu item on the main menu.
</Typography>
<Divider />
</div>
)}
<div>
<Typography className={classes.sectionHead} display="block" variant="caption" color="secondary">
STATUS
STATUS
</Typography>
{(!isConfigValid) ?
(
@@ -582,7 +590,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
(
<div className={classes.cpuStatusColor} style={{ paddingLeft: 48, position: "relative", top: -12 }}>
{JackHostStatus.getDisplayView("", this.state.jackStatus)}
{ (!this.props.onboarding) && JackHostStatus.getCpuInfo("Governor:\u00A0", this.state.jackStatus)}
{(!this.props.onboarding) && JackHostStatus.getCpuInfo("Governor:\u00A0", this.state.jackStatus)}
</div>
)
}
@@ -657,7 +665,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
<SelectHoverBackground selected={false} showHover={true} />
<div style={{ width: "100%" }}>
<Typography className={classes.primaryItem} display="block" variant="body2" noWrap>System MIDI Bindings</Typography>
</div>
</ButtonBase>
@@ -713,7 +721,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
</div>
</ButtonBase>
</div>
{ (!this.props.onboarding) ? (
{(!this.props.onboarding) ? (
<div >
<Divider />
<Typography className={classes.sectionHead} display="block" variant="caption" color="secondary">SYSTEM</Typography>
@@ -735,7 +743,23 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
<ButtonBase
className={classes.setting}
onClick={() => { this.setState({ showStatusMonitorDialog: true }); }} >
onClick={() => { this.handleThemeSelection(); }} >
<SelectHoverBackground selected={false} showHover={true} />
<div style={{ width: "100%" }}>
<Typography className={classes.primaryItem} display="block" variant="body2" color="textPrimary" noWrap>
Color Theme</Typography>
<Typography className={classes.secondaryItem} display="block" variant="caption" color="textSecondary" noWrap>
{ this.model.getTheme() === ColorTheme.Dark ? "Dark" :"Light"}
</Typography>
</div>
</ButtonBase>
<ButtonBase
className={classes.setting}
onClick={() => {
this.model.setShowStatusMonitor(!this.state.showStatusMonitor)
}} >
<SelectHoverBackground selected={false} showHover={true} />
<div style={{ width: "100%" }}>
<div style={{
@@ -746,6 +770,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
<Typography className={classes.primaryItem} display="block" variant="body2" color="textPrimary" noWrap>
Show status monitor on main screen.</Typography>
</div>
<div style={{ flex: "0 0 auto" }}>
<Switch
checked={this.state.showStatusMonitor}
@@ -787,16 +812,18 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
</div >
</ButtonBase>
</div>
): (
) : (
<div>
<Divider/>
<div style={{display: "grid", placeContent: "end", maxWidth: 550, marginLeft: 16,marginRight: 16,
marginTop: 16,marginBottom: 16}}>
<Button variant="outlined" onClick={()=> {this.onOnboardingContinue()}} disabled={this.state.continueDisabled} style={{width: 120}}>Continue</Button>
<Divider />
<div style={{
display: "grid", placeContent: "end", maxWidth: 550, marginLeft: 16, marginRight: 16,
marginTop: 16, marginBottom: 16
}}>
<Button variant="outlined" onClick={() => { this.onOnboardingContinue() }} disabled={this.state.continueDisabled} style={{ width: 120 }}>Continue</Button>
</div>
</div>
)
}
}
@@ -849,6 +876,20 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
)
}
{
(this.state.showThemeSelectDialog) &&
(
<SelectThemeDialog open={this.state.showThemeSelectDialog}
onClose={()=> { this.setState({showThemeSelectDialog: false});} }
onOk={(selectedTheme: ColorTheme) => {
this.model.setTheme(selectedTheme);
this.setState({showThemeSelectDialog: false});
}}
defaultTheme={this.model.getTheme()}
/>
)
}
<WifiConfigDialog wifiConfigSettings={this.state.wifiConfigSettings} open={this.state.showWifiConfigDialog}
onClose={() => this.setState({ showWifiConfigDialog: false })}
onOk={(wifiConfigSettings) => this.handleApplyWifiConfig(wifiConfigSettings)}
@@ -869,8 +910,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
/>
<SystemMidiBindingsDialog
open={this.state.showSystemMidiBindingsDialog}
onClose={()=> { this.setState({showSystemMidiBindingsDialog: false});}}
/>
onClose={() => { this.setState({ showSystemMidiBindingsDialog: false }); }}
/>
</DialogEx >
);
+58 -15
View File
@@ -25,11 +25,11 @@ import createStyles from '@mui/styles/createStyles';
import withStyles from '@mui/styles/withStyles';
import { PiPedalModelFactory, PiPedalModel, State } from "./PiPedalModel";
import { StandardItemSize } from './PluginControlView';
import Utility from './Utility';
import SvgPathBuilder from './SvgPathBuilder';
const StandardItemSize = { width: 80, height: 110 };
const FREQUENCY_RESPONSE_VECTOR_URI = "http://two-play.com/plugins/toob#frequencyResponseVector";
@@ -47,13 +47,15 @@ const styles = (theme: Theme) => createStyles({
interface ToobFrequencyResponseProps extends WithStyles<typeof styles> {
instanceId: number;
maxDb?: number;
minDb?: number;
minFrequency?: number;
maxFrequency?: number;
propertyName?: string;
width?: number;
}
interface ToobFrequencyResponseState {
minDb: number;
maxDb: number;
minF: number;
maxF: number;
path: string;
}
@@ -70,7 +72,11 @@ const ToobFrequencyResponseView =
super(props);
this.model = PiPedalModelFactory.getInstance();
this.state = {
path: ""
path: "",
minDb: -30,
maxDb: 5,
minF: 30,
maxF: 20000
};
this.pathRef = React.createRef();
@@ -146,24 +152,61 @@ const ToobFrequencyResponseView =
onFrequencyResponseUpdated(data: number[]) {
if (!this.mounted) return;
let pathBuilder = new SvgPathBuilder();
let minF = data[0];
let maxF = data[1];
let dbMin = data[2];
let dbMax = data[3];
let logMin = Math.log(minF);
let logMax = Math.log(maxF);
let toX_ = (frequency: number): number => {
var logV = Math.log(frequency);
return (this.xMax - this.xMin) * (logV - logMin) / (logMax - logMin) + this.xMin;
};
let toY_ = (value: number): number => {
var db;
if (value < this.MIN_DB_AF) {
db = -192.0;
} else {
db = 20 * Math.log10(value);
}
var y = (db - dbMin) / (dbMax - dbMin) * (this.yMax - this.yMin) + this.yMin;
return y;
};
if (data.length > 2) {
pathBuilder.moveTo(this.toX(data[0]), this.toY(data[1]))
for (let i = 2; i < data.length; i += 2) {
pathBuilder.lineTo(this.toX(data[i]), this.toY(data[i + 1]));
pathBuilder.moveTo(toX_(data[4]), toY_(data[5]))
for (let i = 6; i < data.length; i += 2) {
pathBuilder.lineTo(toX_(data[i]), toY_(data[i + 1]));
}
}
this.currentPath = pathBuilder.toString();
this.setState({ path: this.currentPath });
this.setState({
minF: data[0],
maxF: data[1],
maxDb: data[2],
minDb: data[3],
path: this.currentPath
});
}
private getPropertyUri()
{
return this.props.propertyName? this.props.propertyName: FREQUENCY_RESPONSE_VECTOR_URI;
}
updateAllWaveShapes() {
if (this.requestOutstanding) { // throttling.
this.requestDeferred = true;
return;
}
this.requestOutstanding = true;
this.model.getPatchProperty<any>(this.props.instanceId, FREQUENCY_RESPONSE_VECTOR_URI)
this.model.getPatchProperty<any>(this.props.instanceId, this.getPropertyUri())
.then((json) => {
if (json && json.otype_ === "Vector" && json.vtype_ === "Float") {
this.onFrequencyResponseUpdated(json.value as number[]);
@@ -237,11 +280,11 @@ const ToobFrequencyResponseView =
render() {
// deliberately reversed to flip up and down.
this.dbMax = this.props.minDb ?? this.dbMinDefault;
this.dbMin = this.props.maxDb ?? this.dbMaxDefault;
this.dbMax = this.state.minDb;
this.dbMin = this.state.maxDb;
this.fMin = this.props.minFrequency ?? 30;
this.fMax = this.props.maxFrequency ?? 20000;
this.fMin = this.state.minF;
this.fMax = this.state.maxF;
this.logMin = Math.log(this.fMin);
this.logMax = Math.log(this.fMax);
+1 -1
View File
@@ -109,7 +109,7 @@ const ToobMLView =
{
let group = controls[4] as ControlGroup;
group.controls.splice(0,0,
( <ToobFrequencyResponseView instanceId={this.props.instanceId} minDb={-20} maxDb={20} />)
( <ToobFrequencyResponseView instanceId={this.props.instanceId} />)
);
let gainControl: React.ReactElement = controls[2] as React.ReactElement;
+1 -1
View File
@@ -92,7 +92,7 @@ const ToobToneStackView =
{
controls.splice(0,0,
( <ToobFrequencyResponseView instanceId={this.props.instanceId}
minDb={-20} maxDb={20} />)
/>)
);
} else {
controls.splice(0,0,
+1 -1
View File
@@ -206,7 +206,7 @@ export default class UploadFileDialog extends ResizeResponsiveComponent<UploadFi
upload.abortController = new AbortController();
if (!this.wantsFile(this.uploadList[i].file))
{
throw new Error("Invalid file type.");
throw new Error("Invalid file extension.");
}
let filename = await this.model.uploadFile(this.props.uploadPage, this.uploadList[i].file, "application/octet-stream", upload.abortController);
this.props.onUploaded(filename);
+2 -2
View File
@@ -41,11 +41,11 @@ const Utility = class {
static isLandscape(): boolean {
return window.innerWidth > window.innerHeight;
}
static isTouchDevice(): boolean {
static needsZoomedControls(): boolean {
if (!this.isLandscape()) return false;
if (window.innerHeight > 500) return false;
if (window.innerHeight > 300) return true;
return (('ontouchstart' in window) &&
((navigator.maxTouchPoints??0) > 0) );
}
+24 -18
View File
@@ -22,6 +22,7 @@ import { WithStyles } from '@mui/styles';
import withStyles from '@mui/styles/withStyles';
import createStyles from '@mui/styles/createStyles';
import { PiPedalModel, PiPedalModelFactory, ZoomedControlInfo } from './PiPedalModel';
import {ReactComponent as DialIcon} from './svg/fx_dial.svg';
const SELECTED_OPACITY = 0.8;
const DEFAULT_OPACITY = 0.6;
@@ -34,6 +35,12 @@ const DEAD_ZONE_SIZE = 5;
const styles = (theme: Theme) => createStyles({
dialIcon: {
overscrollBehavior: "none", touchAction: "none",
fill: theme.palette.text.primary,
opacity: DEFAULT_OPACITY,
}
});
@@ -56,7 +63,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })(
class extends React.Component<ZoomedDialProps, ZoomedDialState> {
model: PiPedalModel = PiPedalModelFactory.getInstance();
imgRef: React.RefObject<HTMLImageElement> = React.createRef();
imgRef: React.RefObject<SVGSVGElement> = React.createRef();
constructor(props: ZoomedDialProps) {
super(props);
@@ -75,7 +82,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })(
}
onPointerUp(e: PointerEvent<HTMLImageElement>) {
onPointerUp(e: PointerEvent<SVGSVGElement>) {
if (this.isCapturedPointer(e)) {
--this.pointersDown;
@@ -94,7 +101,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })(
}
releaseCapture(e: PointerEvent<HTMLImageElement>)
releaseCapture(e: PointerEvent<SVGSVGElement>)
{
let img = this.imgRef.current;
@@ -118,7 +125,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })(
}
onPointerLostCapture(e: PointerEvent<HTMLImageElement>) {
onPointerLostCapture(e: PointerEvent<SVGSVGElement>) {
if (this.isCapturedPointer(e)) {
--this.pointersDown;
@@ -128,10 +135,10 @@ const ZoomedDial = withStyles(styles, { withTheme: true })(
}
onTouchStart(e: TouchEvent<HTMLImageElement>) {
onTouchStart(e: TouchEvent<SVGSVGElement>) {
//must be defined to get onTouchMove
}
onTouchMove(e: TouchEvent<HTMLImageElement>) {
onTouchMove(e: TouchEvent<SVGSVGElement>) {
// e.preventDefault();
e.stopPropagation(); // cancels scroll!!!
@@ -150,7 +157,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })(
currentAngle: number = 0;
lastPointerAngle: number = 0;
isValidPointer(e: PointerEvent<HTMLImageElement>): boolean {
isValidPointer(e: PointerEvent<SVGSVGElement>): boolean {
if (e.pointerType === "mouse") {
return e.button === 0;
} else if (e.pointerType === "pen") {
@@ -202,7 +209,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })(
return 0;
}
pointerToAngle(e: PointerEvent<HTMLImageElement>): number {
pointerToAngle(e: PointerEvent<SVGSVGElement>): number {
if (this.imgRef.current) {
let img = this.imgRef.current;
@@ -220,10 +227,10 @@ const ZoomedDial = withStyles(styles, { withTheme: true })(
}
return NaN;
}
captureElement?: HTMLElement;
captureElement?: SVGSVGElement;
pointersDown: number = 0;
onPointerDown(e: PointerEvent<HTMLImageElement>): void {
onPointerDown(e: PointerEvent<SVGSVGElement>): void {
if (!this.mouseDown && this.isValidPointer(e)) {
e.preventDefault();
e.stopPropagation();
@@ -262,7 +269,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })(
}
}
isCapturedPointer(e: PointerEvent<HTMLImageElement>): boolean {
isCapturedPointer(e: PointerEvent<SVGSVGElement>): boolean {
return this.mouseDown
&& e.pointerId === this.pointerId
&& e.pointerType === this.pointerType;
@@ -271,7 +278,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })(
onPointerMove(e: PointerEvent<HTMLImageElement>): void {
onPointerMove(e: PointerEvent<SVGSVGElement>): void {
if (this.isCapturedPointer(e)) {
e.preventDefault();
this.updateAngle(e)
@@ -286,7 +293,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })(
}
updateAngle(e: PointerEvent<HTMLImageElement>): void {
updateAngle(e: PointerEvent<SVGSVGElement>): void {
let angle: number = this.pointerToAngle(e);
if (!isNaN(angle)) {
if (!isNaN(this.lastPointerAngle)) {
@@ -352,14 +359,13 @@ const ZoomedDial = withStyles(styles, { withTheme: true })(
render() {
let classes = this.props.classes;
return (
<img ref={this.imgRef} src="img/fx_dial.svg" alt=""
<DialIcon ref={this.imgRef} className={classes.dialIcon}
style={{
overscrollBehavior: "none", touchAction: "none",
width: this.props.size, height: this.props.size, opacity: DEFAULT_OPACITY,
transform: this.getDefaultRotationTransform()
transform: this.getDefaultRotationTransform(),
width: this.props.size, height: this.props.size,
}}
draggable="true"
onTouchStart={this.onTouchStart} onTouchMove={this.onTouchMove}
onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp}
onPointerMoveCapture={this.onPointerMove} onDrag={this.onDrag}
+3 -3
View File
@@ -215,8 +215,8 @@ const ZoomedUiControl = withStyles(styles, { withTheme: true })(
onClose={() => { this.props.onDialogClose() }}
onAbort={() => { this.props.onDialogClose() }}
>
<div style={{
width: 380, height: 300, background: "FFFF",
<div style={{
width: 380, height: 300,
display: "flex", flexFlow: "row", alignItems: "center", alignContent: " center", justifyContent: "center"
}}>
<IconButton sx={{ height: "100%", width: 48, borderRadius: "0% 50% 50% 0%" }} onClick={
@@ -227,7 +227,7 @@ const ZoomedUiControl = withStyles(styles, { withTheme: true })(
<NavigateBeforeIcon />
</IconButton>
<div style={{
width: 200, flexGrow: 1, height: 300, background: "#FFF",
width: 200, flexGrow: 1, height: 300,
display: "flex", flexFlow: "column", alignItems: "center", alignContent: "center", justifyContent: "center"
}}
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px">
<g>
<g>
<polygon points="15,16 11,20 21,20 21,16"/>
<path d="M12.06,7.19L3,16.25V20h3.75l9.06-9.06L12.06,7.19z M5.92,18H5v-0.92l7.06-7.06l0.92,0.92L5.92,18z"/>
<path d="M18.71,8.04c0.39-0.39,0.39-1.02,0-1.41l-2.34-2.34C16.17,4.09,15.92,4,15.66,4c-0.25,0-0.51,0.1-0.7,0.29l-1.83,1.83 l3.75,3.75L18.71,8.04z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 507 B

+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<rect x="6" y="9" width="2" height="2"/>
<rect x="6" y="13" width="2" height="2"/>
<rect x="6" y="17" width="2" height="2"/>
<rect x="10" y="17" width="3" height="2"/>
<g>
<g>
<path d="M17.059,5.19l-7.061,7.06V16h3.75l7.061-7.061L17.059,5.19z M12.918,14h-0.92v-0.92l5.061-5.061l0.92,0.92L12.918,14z"/>
<path d="M23.708,6.04c0.39-0.39,0.39-1.02,0-1.41l-2.34-2.34c-0.2-0.2-0.45-0.29-0.71-0.29c-0.25,0-0.51,0.1-0.7,0.29l-1.83,1.83
l3.75,3.75L23.708,6.04z"/>
</g>
</g>
<path d="M15,16.641V21H4v-9V7h5h1h3.672l1.904-1.904C15.393,5.04,15.202,5,15,5h-4H8H4C2.9,5,2.01,5.9,2.01,7l-0.003,3.993L2,11v10
c0,1.1,0.891,2,1.99,2H4h11h0.01c1.1,0,1.99-0.9,1.99-2v-6.359L15,16.641z"/>
<polygon points="11.672,9 10,9 10,10.672 "/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 402 B

After

Width:  |  Height:  |  Size: 402 B

Before

Width:  |  Height:  |  Size: 997 B

After

Width:  |  Height:  |  Size: 997 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M19.43 12.98c.04-.32.07-.64.07-.98 0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.09-.16-.26-.25-.44-.25-.06 0-.12.01-.17.03l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.06-.02-.12-.03-.18-.03-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98 0 .33.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.09.16.26.25.44.25.06 0 .12-.01.17-.03l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73 0 .21-.02.43-.05.73l-.14 1.13.89.7 1.08.84-.7 1.21-1.27-.51-1.04-.42-.9.68c-.43.32-.84.56-1.25.73l-1.06.43-.16 1.13-.2 1.35h-1.4l-.19-1.35-.16-1.13-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7-1.06.43-1.27.51-.7-1.21 1.08-.84.89-.7-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13-.89-.7-1.08-.84.7-1.21 1.27.51 1.04.42.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43.16-1.13.2-1.35h1.39l.19 1.35.16 1.13 1.06.43c.43.18.83.41 1.23.71l.91.7 1.06-.43 1.27-.51.7 1.21-1.07.85-.89.7.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB