Improvements to UI controlsin prep for Recorder.
This commit is contained in:
Vendored
+4
-3
@@ -5,11 +5,12 @@
|
||||
"includePath": [
|
||||
"${default}",
|
||||
"${workspaceFolder}",
|
||||
"${workspaceFolder}/src",
|
||||
"${workspaceFolder}/build/src/**",
|
||||
"${workspaceFolder}/src/**",
|
||||
"${workspaceFolder}/**",
|
||||
"/usr/include/lilv-0",
|
||||
"/usr/include/x86_64-linux-gnu",
|
||||
"/usr/lib",
|
||||
"~/src/vst3sdk"
|
||||
"/usr/lib"
|
||||
],
|
||||
"compilerPath": "/usr/bin/gcc-12",
|
||||
"cStandard": "c17",
|
||||
|
||||
@@ -56,6 +56,9 @@ const styles = (theme: Theme) => createStyles({
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
opacity: theme.palette.mode === 'light' ? 0.38 : 0.3
|
||||
},
|
||||
controlFrame: {
|
||||
display: "flex", flexDirection: "column", alignItems: "start", justifyContent: "space-between", height: 116
|
||||
},
|
||||
displayValue: {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
@@ -63,10 +66,20 @@ const styles = (theme: Theme) => createStyles({
|
||||
right: 0,
|
||||
bottom: 4,
|
||||
textAlign: "center",
|
||||
background: "white",
|
||||
color: "#666",
|
||||
background: theme.mainBackground,
|
||||
color: theme.palette.text.secondary,
|
||||
// zIndex: -1,
|
||||
},
|
||||
titleSection: {
|
||||
flex: "0 0 auto", alignSelf:"stretch", marginBottom: 8, marginLeft: 8, marginRight: 0
|
||||
},
|
||||
midSection: {
|
||||
flex: "1 1 1", display: "flex",flexFlow: "column nowrap",alignContent: "center",justifyContent: "center"
|
||||
},
|
||||
editSection: {
|
||||
flex: "0 0 0", position: "relative", width: 60, height: 28,minHeight: 28
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -191,6 +204,7 @@ const FilePropertyControl =
|
||||
render() {
|
||||
//let classes = this.props.classes;
|
||||
let fileProperty = this.props.fileProperty;
|
||||
let classes = this.props.classes;
|
||||
|
||||
let value = "\u00A0";
|
||||
if (this.state.hasValue)
|
||||
@@ -206,9 +220,11 @@ const FilePropertyControl =
|
||||
let item_width = 264;
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "flex-start", width: item_width, margin: 8, paddingLeft: 8 }}>
|
||||
<div className={classes.controlFrame}
|
||||
style={{ width: item_width }}>
|
||||
{/* TITLE SECTION */}
|
||||
<div style={{ flex: "0 0 auto", width: "100%", marginBottom: 8, marginLeft: 0, marginRight: 0 }}>
|
||||
<div className={classes.titleSection}
|
||||
>
|
||||
<Tooltip title={fileProperty.label} placement="top-start" arrow
|
||||
enterDelay={1000} enterNextDelay={1000}
|
||||
>
|
||||
@@ -220,9 +236,9 @@ const FilePropertyControl =
|
||||
</div>
|
||||
{/* CONTROL SECTION */}
|
||||
|
||||
<div style={{ flex: "0 0 auto", width: "100%" }}>
|
||||
<div className={classes.midSection} style={{ width: "100%", paddingLeft: 8 }}>
|
||||
|
||||
<ButtonBase style={{ width: "100%", borderRadius: "4px 4px 0px 0px", overflow: "hidden", marginTop: 8 }} onClick={() => { this.onFileClick() }} >
|
||||
<ButtonBase style={{ width: "100%", borderRadius: "4px 4px 0px 0px", overflow: "hidden" }} onClick={() => { this.onFileClick() }} >
|
||||
<div style={{ width: "100%", background:
|
||||
isDarkMode()? "rgba(255,255,255,0.03)": "rgba(0,0,0,0.07)",
|
||||
borderRadius: "4px 4px 0px 0px" }}>
|
||||
@@ -238,7 +254,7 @@ const FilePropertyControl =
|
||||
</div>
|
||||
|
||||
{/* LABEL/EDIT SECTION*/}
|
||||
<div style={{ flex: "0 0 auto", position: "relative", width: 60 }}>
|
||||
<div className={classes.editSection} >
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
|
||||
+67
-11
@@ -25,6 +25,16 @@ interface Deserializable<T> {
|
||||
deserialize(input: any): T;
|
||||
}
|
||||
|
||||
const noteNames: string[] = [
|
||||
"C","C#","D","Eb","E","F","F#","G","Ab","A","Bb","B","C"
|
||||
];
|
||||
|
||||
function semitone12TETValue(value: number) : string {
|
||||
let iValue = Math.round(value) % 12;
|
||||
return noteNames[iValue];
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class Port implements Deserializable<Port> {
|
||||
deserialize(input: any): Port {
|
||||
@@ -445,7 +455,30 @@ export enum ControlType {
|
||||
Tuner,
|
||||
Vu,
|
||||
DbVu,
|
||||
OutputSelect
|
||||
OutputText
|
||||
}
|
||||
|
||||
const textUnits : Set<Units> = new Set<Units>([
|
||||
Units.none,
|
||||
Units.unknown,
|
||||
Units.bar,
|
||||
Units.beat,
|
||||
Units.bpm,
|
||||
Units.cent,
|
||||
Units.cm,
|
||||
Units.hz,
|
||||
Units.khz,
|
||||
Units.km,
|
||||
Units.m,
|
||||
Units.min,
|
||||
Units.ms,
|
||||
Units.s,
|
||||
Units.semitone12TET
|
||||
]);
|
||||
|
||||
function displayUnitAsText(unit: Units): boolean
|
||||
{
|
||||
return textUnits.has(unit);
|
||||
}
|
||||
|
||||
export class UiControl implements Deserializable<UiControl> {
|
||||
@@ -468,6 +501,7 @@ deserialize(input: any): UiControl {
|
||||
this.scale_points = ScalePoint.deserialize_array(input.scale_points);
|
||||
this.port_group = input.port_group;
|
||||
this.units = input.units as Units;
|
||||
this.pipedal_ledColor = input.pipedal_ledColor;
|
||||
|
||||
this.comment = input.comment ?? "";
|
||||
this.is_bypass = input.is_bypass ? true : false;
|
||||
@@ -489,10 +523,13 @@ deserialize(input: any): UiControl {
|
||||
} else if (this.units === Units.db) {
|
||||
this.controlType = ControlType.DbVu;
|
||||
} else if (this.enumeration_property) {
|
||||
this.controlType = ControlType.OutputSelect;
|
||||
this.controlType = ControlType.OutputText;
|
||||
} else if (displayUnitAsText(this.units))
|
||||
{
|
||||
this.controlType = ControlType.OutputText
|
||||
} else {
|
||||
this.controlType = ControlType.Vu;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.isValidEnumeration()) {
|
||||
this.controlType = ControlType.Select;
|
||||
@@ -566,6 +603,7 @@ deserialize(input: any): UiControl {
|
||||
is_program_controller: boolean = true;
|
||||
custom_units: string = "";
|
||||
connection_optional: boolean = false;
|
||||
pipedal_ledColor: string = "";
|
||||
|
||||
|
||||
// Return the value of the closest scale_point.
|
||||
@@ -602,8 +640,8 @@ deserialize(input: any): UiControl {
|
||||
isTrigger(): boolean {
|
||||
return this.controlType === ControlType.Trigger;
|
||||
}
|
||||
isOutputSelect(): boolean {
|
||||
return !this.is_input && this.controlType === ControlType.OutputSelect;
|
||||
isOutputText(): boolean {
|
||||
return !this.is_input && this.controlType === ControlType.OutputText;
|
||||
}
|
||||
|
||||
|
||||
@@ -666,6 +704,29 @@ deserialize(input: any): UiControl {
|
||||
return scalePoint.label;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.units === Units.s)
|
||||
{
|
||||
let iValue = Math.round(value);
|
||||
if (iValue >= 60)
|
||||
{
|
||||
let minutes = Math.floor(iValue/60);
|
||||
let seconds = iValue % 60;
|
||||
if (seconds < 10)
|
||||
{
|
||||
return minutes + ":0" + seconds;
|
||||
|
||||
}
|
||||
return minutes + ":" + seconds;
|
||||
} else {
|
||||
return this.formatShortValue(value);
|
||||
}
|
||||
}
|
||||
if (this.units === Units.semitone12TET)
|
||||
{
|
||||
return semitone12TETValue(value);
|
||||
}
|
||||
|
||||
let text = this.formatShortValue(value);
|
||||
|
||||
switch (this.units) {
|
||||
@@ -705,13 +766,8 @@ deserialize(input: any): UiControl {
|
||||
case Units.pc:
|
||||
text += "%";
|
||||
break;
|
||||
case Units.s:
|
||||
text += "s";
|
||||
default:
|
||||
break;
|
||||
// Midinote: not handled.
|
||||
// semitone12TET not handled.
|
||||
|
||||
|
||||
|
||||
}
|
||||
return text;
|
||||
|
||||
@@ -68,6 +68,13 @@ const styles = (theme: Theme) => createStyles({
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
opacity: theme.palette.mode === 'light' ? 0.38 : 0.3
|
||||
},
|
||||
controlFrame: {
|
||||
display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "space-between", height: 116
|
||||
},
|
||||
|
||||
titleSection: {
|
||||
flex: "0 0 auto", alignSelf:"stretch", marginBottom: 8, marginLeft: 0, marginRight: 0
|
||||
},
|
||||
displayValue: {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
@@ -78,7 +85,14 @@ const styles = (theme: Theme) => createStyles({
|
||||
background: theme.mainBackground,
|
||||
color: theme.palette.text.secondary,
|
||||
// zIndex: -1,
|
||||
},
|
||||
midSection: {
|
||||
flex: "1 1 1", display: "flex",flexFlow: "column nowrap",alignContent: "center",justifyContent: "center"
|
||||
},
|
||||
editSection: {
|
||||
flex: "0 0 0", position: "relative", width: 60, height: 28,minHeight: 28
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -723,9 +737,16 @@ const PluginControl =
|
||||
|
||||
|
||||
return (
|
||||
<div ref={this.frameRef} style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "flex-start", width: item_width, margin: 8 }}>
|
||||
<div ref={this.frameRef}
|
||||
className={this.props.classes.controlFrame}
|
||||
style={{ width: item_width }}
|
||||
>
|
||||
{/* TITLE SECTION */}
|
||||
<div style={{ flex: "0 0 auto", alignSelf:"stretch", marginBottom: 8, marginLeft: isSelect ? 16 : 0, marginRight: 0 }}>
|
||||
<div className={this.props.classes.titleSection}
|
||||
style={
|
||||
{ alignSelf:"stretch", marginBottom: 8, marginLeft: isSelect ? 8 : 0, marginRight: 0
|
||||
|
||||
}}>
|
||||
<ControlTooltip uiControl={control} >
|
||||
<Typography variant="caption" display="block" noWrap style={{
|
||||
width: "100%",
|
||||
@@ -735,7 +756,8 @@ const PluginControl =
|
||||
</div>
|
||||
{/* CONTROL SECTION */}
|
||||
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<div className={this.props.classes.midSection}>
|
||||
|
||||
{isTrigger ? (
|
||||
<Button variant="contained" color="primary" size="small"
|
||||
onMouseDown={
|
||||
@@ -747,7 +769,9 @@ const PluginControl =
|
||||
style={{
|
||||
textTransform: "none",
|
||||
background: (isDarkMode() ? "#6750A4" : undefined),
|
||||
marginLeft: 8, marginRight: 8,minWidth:60
|
||||
marginLeft: 8, marginRight: 8,minWidth:60,
|
||||
marginTop: 0
|
||||
|
||||
}}
|
||||
|
||||
>
|
||||
@@ -774,7 +798,7 @@ const PluginControl =
|
||||
</div>
|
||||
|
||||
{/* LABEL/EDIT SECTION*/}
|
||||
<div style={{ flex: "0 0 auto", position: "relative", width: 60 }}>
|
||||
<div className={this.props.classes.editSection} >
|
||||
{(!(isSelect || isOnOffSwitch || isTrigger)) &&
|
||||
(
|
||||
(isAbSwitch) ? (
|
||||
|
||||
@@ -126,11 +126,12 @@ const styles = (theme: Theme) => createStyles({
|
||||
|
||||
normalGrid: {
|
||||
position: "relative",
|
||||
paddingLeft: 22,
|
||||
paddingLeft: 25,
|
||||
paddingRight: 34,
|
||||
flex: "1 1 auto",
|
||||
display: "flex", flexDirection: "row", flexWrap: "wrap",
|
||||
justifyContent: "flex-start", alignItems: "flex_start",
|
||||
rowGap: 10
|
||||
|
||||
|
||||
},
|
||||
@@ -147,12 +148,26 @@ const styles = (theme: Theme) => createStyles({
|
||||
marginBottom: 8
|
||||
|
||||
},
|
||||
|
||||
controlPadding: {
|
||||
flex: "0 0 auto",
|
||||
marginTop: 0,
|
||||
marginBottom: 0
|
||||
marginBottom: 0,
|
||||
height: 116
|
||||
|
||||
},
|
||||
controlPair: {
|
||||
display: "flex", flexFlow: "row nowrap",
|
||||
flex: "0 0 auto",
|
||||
height: 116
|
||||
},
|
||||
controlSpacer: {
|
||||
display: "none",
|
||||
minWidth: 0,
|
||||
width: 0,
|
||||
height: 116
|
||||
},
|
||||
|
||||
portGroup: {
|
||||
marginLeft: 8,
|
||||
marginTop: 0,
|
||||
@@ -375,6 +390,34 @@ const PluginControlView =
|
||||
|
||||
}
|
||||
|
||||
push_control(controls: ReactNode[], pluginControl: UiControl,controlValues: ControlValue[])
|
||||
{
|
||||
// combine lamps with their previous control
|
||||
if ((pluginControl.isLamp() || pluginControl.isDbVu()) && controls.length !== 0)
|
||||
{
|
||||
let newControl = this.makeStandardControl(pluginControl,controlValues);
|
||||
let previousControl = controls[controls.length-1];
|
||||
let pair = (
|
||||
<div className={this.props.classes.controlPair}>
|
||||
{previousControl}
|
||||
{newControl}
|
||||
</div>
|
||||
);
|
||||
controls[controls.length-1] = pair;
|
||||
// push a spacer control in order to make placing of extended controls predictable.
|
||||
// (e.g.. inserting at position 4 still places the extended control after four previous controls
|
||||
controls.push((
|
||||
|
||||
<div className={this.props.classes.controlSpacer} />
|
||||
));
|
||||
|
||||
} else {
|
||||
controls.push(
|
||||
this.makeStandardControl(pluginControl, controlValues)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
getStandardControlNodes(plugin: UiPlugin, controlValues: ControlValue[]): ControlNodes {
|
||||
let result: ControlNodes = [];
|
||||
let portGroupMap: { [id: string]: ControlGroup } = {};
|
||||
@@ -394,10 +437,9 @@ const PluginControlView =
|
||||
while (i + 1 < plugin.controls.length && plugin.controls[i + 1].port_group === pluginControl.port_group) {
|
||||
++i;
|
||||
pluginControl = plugin.controls[i];
|
||||
|
||||
if (!pluginControl.isHidden()) {
|
||||
groupControls.push(
|
||||
this.makeStandardControl(pluginControl, controlValues)
|
||||
)
|
||||
this.push_control(groupControls,pluginControl,controlValues);
|
||||
indexes.push(pluginControl.index);
|
||||
}
|
||||
}
|
||||
@@ -407,9 +449,7 @@ const PluginControlView =
|
||||
)
|
||||
portGroupMap[pluginControl.port_group] = controlGroup;
|
||||
} else {
|
||||
result.push(
|
||||
this.makeStandardControl(pluginControl, controlValues)
|
||||
);
|
||||
this.push_control(result,pluginControl,controlValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,36 @@ import ControlTooltip from './ControlTooltip';
|
||||
|
||||
|
||||
|
||||
function makeLedGradient(color: string) {
|
||||
if (color === "green") {
|
||||
if (isDarkMode()) {
|
||||
return "radial-gradient(circle at center, #0F0 0, #0F0 20%, #333 100%)";
|
||||
} else {
|
||||
return "radial-gradient(circle at center, #4F4 0, #4F4 40%, #464 100%)";
|
||||
}
|
||||
}
|
||||
if (color === "blue") {
|
||||
if (isDarkMode()) {
|
||||
return "radial-gradient(circle at center, #00F 0, #00F 20%, #333 100%)";
|
||||
} else {
|
||||
return "radial-gradient(circle at center, #44F 0, #44F 40%, #446 100%)";
|
||||
}
|
||||
}
|
||||
if (color === "yellow") {
|
||||
if (isDarkMode()) {
|
||||
return "radial-gradient(circle at center, #FF0 0, #FF0 20%, #333 100%)";
|
||||
} else {
|
||||
return "radial-gradient(circle at center, #FF4 0, #FF4 40%, #664 100%)";
|
||||
}
|
||||
}
|
||||
if (isDarkMode()) {
|
||||
return "radial-gradient(circle at center, #F000 0, #F00 20%, #333 100%)";
|
||||
} else {
|
||||
return "radial-gradient(circle at center, #F44 0, #F44 40%, #644 100%)";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
@@ -60,7 +90,26 @@ const styles = (theme: Theme) => createStyles({
|
||||
background: "white",
|
||||
color: "#666",
|
||||
// zIndex: -1,
|
||||
}
|
||||
},
|
||||
controlFrame: {
|
||||
display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "space-between", height: 116
|
||||
},
|
||||
|
||||
titleSection: {
|
||||
flex: "0 0 auto", alignSelf:"stretch", marginBottom: 8, marginLeft: 0, marginRight: 0
|
||||
},
|
||||
|
||||
midSection: {
|
||||
flex: "1 1 1", display: "flex",flexFlow: "column nowrap",alignContent: "center",justifyContent: "center"
|
||||
},
|
||||
|
||||
editSection: {
|
||||
flex: "0 0 28", position: "relative", width: 60, height: 28, minHeight: 28
|
||||
},
|
||||
editSectionNoContent: {
|
||||
flex: "0 0 28", position: "relative", width: 1, height: 28, minHeight: 28
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -251,7 +300,10 @@ const PluginOutputControl =
|
||||
}
|
||||
}
|
||||
|
||||
isShortSelect(control: UiControl) {
|
||||
isShortSelectOrText(control: UiControl) {
|
||||
if (control.scale_points.length === 0) {
|
||||
return true;
|
||||
}
|
||||
for (let scale_point of control.scale_points) {
|
||||
if (scale_point.label.length > 12) return false;
|
||||
}
|
||||
@@ -276,9 +328,9 @@ const PluginOutputControl =
|
||||
let y = (control.max_value - value) * this.VU_HEIGHT / (control.max_value - control.min_value);
|
||||
return y;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
let classes = this.props.classes;
|
||||
let control: UiControl = this.props.uiControl;
|
||||
|
||||
|
||||
@@ -292,11 +344,11 @@ const PluginOutputControl =
|
||||
}
|
||||
}
|
||||
|
||||
let isSelect = control.isOutputSelect();
|
||||
let isText = control.isOutputText();
|
||||
|
||||
let item_width: number | undefined = isSelect ? 160 : 80;
|
||||
if (isSelect) {
|
||||
if (this.isShortSelect(control)) {
|
||||
let item_width: number | undefined = isText ? 160 : 80;
|
||||
if (isText) {
|
||||
if (this.isShortSelectOrText(control)) {
|
||||
item_width = 80;
|
||||
}
|
||||
}
|
||||
@@ -316,11 +368,13 @@ const PluginOutputControl =
|
||||
let redLevel = this.dbVuMap(0);
|
||||
let yellowLevel = this.dbVuMap(-10);
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", width: item_width, margin: 8, height: 98 }}>
|
||||
<div className={classes.controlFrame}
|
||||
style={{ width: item_width }}>
|
||||
{/* TITLE SECTION */}
|
||||
<div style={{ flex: "0 0 auto", width: "100%", marginBottom: 8, marginLeft: 0, marginRight: 0 }}>
|
||||
<div className={classes.titleSection}
|
||||
style={{ width: "100%" }}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
<Typography variant="caption" display="block" style={{
|
||||
<Typography variant="caption" display="block" style={{
|
||||
width: "100%",
|
||||
textAlign: "center"
|
||||
}}> {control.name === "" ? "\u00A0" : control.name}</Typography>
|
||||
@@ -328,7 +382,8 @@ const PluginOutputControl =
|
||||
</div>
|
||||
{/* CONTROL SECTION */}
|
||||
|
||||
<div style={{ flex: "1 1 auto", display: "flex", justifyContent: "center", alignItems: "start", flexFlow: "row nowrap" }}>
|
||||
<div className={classes.midSection}
|
||||
style={{ flex: "1 1 1", display: "flex", justifyContent: "center", alignItems: "start", flexFlow: "row nowrap" }}>
|
||||
<div style={{ width: 8, height: this.DB_VU_HEIGHT + 4, background: "#000", }}>
|
||||
<div style={{ height: this.DB_VU_HEIGHT, width: 4, overflow: "hidden", position: "absolute", margin: 2 }}>
|
||||
<div style={{ width: 4, height: redLevel, position: "absolute", marginTop: 0, background: "#F00" }} />
|
||||
@@ -341,27 +396,33 @@ const PluginOutputControl =
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className={classes.editSectionNoContent}>
|
||||
|
||||
</div>
|
||||
|
||||
</div >
|
||||
|
||||
);
|
||||
}
|
||||
else if (control.isVu()) {
|
||||
// yyx: convert this to a horizontal progress bar.
|
||||
item_width = undefined;
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", width: item_width, margin: 8, height: 98 }}>
|
||||
<div className={classes.controlFrame} style={{ width: item_width}}>
|
||||
{/* TITLE SECTION */}
|
||||
<div style={{ flex: "0 0 auto", width: "100%", marginBottom: 8, marginLeft: 0, marginRight: 0 }}>
|
||||
<div className={classes.titleSection}
|
||||
style={{ width: "100%" }}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
<Typography variant="caption" display="block" style={{
|
||||
width: "100%",
|
||||
<Typography noWrap display="block" variant="caption" style={{
|
||||
width: item_width,
|
||||
textAlign: "center"
|
||||
}}> {control.name === "" ? "\u00A0" : control.name}</Typography>
|
||||
</ControlTooltip>
|
||||
</div>
|
||||
{/* CONTROL SECTION */}
|
||||
|
||||
<div style={{ flex: "1 1 auto", display: "flex", justifyContent: "center", alignItems: "start", flexFlow: "row nowrap" }}>
|
||||
<div className={classes.midSection}
|
||||
style={{ display: "flex", justifyContent: "center", alignItems: "start", flexFlow: "row nowrap" }}>
|
||||
<div style={{ width: 8, height: this.VU_HEIGHT + 4, background: "#000", }}>
|
||||
<div style={{ height: this.VU_HEIGHT, overflow: "hidden", position: "absolute", margin: 2 }}>
|
||||
<div ref={this.vuRef} style={{ width: 4, height: this.VU_HEIGHT, background: "#0C0", }} />
|
||||
@@ -371,8 +432,7 @@ const PluginOutputControl =
|
||||
</div>
|
||||
|
||||
{/* LABEL/EDIT SECTION*, strictly a placeholder for visual alignment purposes.*/}
|
||||
<div style={{ flex: "0 0 auto", position: "relative", width: 30, height: 27 }}>
|
||||
|
||||
<div className={this.props.classes.editSectionNoContent}>
|
||||
</div>
|
||||
</div >
|
||||
|
||||
@@ -380,15 +440,23 @@ const PluginOutputControl =
|
||||
} else if (control.isLamp()) {
|
||||
item_width = undefined;
|
||||
let attachedLamp = control.name === "" || control.name === "\u00A0";
|
||||
|
||||
let ledGradient: string;
|
||||
if (this.props.uiControl.pipedal_ledColor.length === 0) {
|
||||
ledGradient = (isDarkMode() ? "radial-gradient(circle at center, #F00 0, #F00 20%, #333 100%)"
|
||||
: "radial-gradient(circle at center, #F44 0, #F44 40%, #644 100%)");
|
||||
} else {
|
||||
ledGradient = makeLedGradient(this.props.uiControl.pipedal_ledColor);
|
||||
}
|
||||
return (
|
||||
<div style={{
|
||||
display: "flex", flexDirection: "column", width: item_width,
|
||||
marginTop: 8, marginBottom: 8, marginRight: 8, marginLeft: (attachedLamp ? 0 : 8), height: 98
|
||||
}}>
|
||||
<div className={classes.controlFrame}
|
||||
style={{width: attachedLamp? 15: item_width}}
|
||||
>
|
||||
{/* TITLE SECTION */}
|
||||
<div style={{ flex: "0 0 auto", width: "100%", marginBottom: 8, marginLeft: 0, marginRight: 0 }}>
|
||||
<div className={classes.titleSection}
|
||||
style={{ flex: "0 0 auto", width: "100%"}}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
<Typography variant="caption" display="block" style={{
|
||||
<Typography noWrap variant="caption" display="block" style={{
|
||||
width: "100%",
|
||||
textAlign: "center"
|
||||
}}> {control.name === "" ? "\u00A0" : (control.name)}</Typography>
|
||||
@@ -396,12 +464,17 @@ const PluginOutputControl =
|
||||
</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 className={classes.midSection}
|
||||
style={{
|
||||
display: "flex", justifyContent: "center", alignItems: "center", 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%)"),
|
||||
background: ledGradient,
|
||||
opacity: 0, borderRadius: 3, margin: 2, position: "absolute"
|
||||
}} />
|
||||
</div>
|
||||
@@ -409,46 +482,52 @@ const PluginOutputControl =
|
||||
</div>
|
||||
|
||||
{/* LABEL/EDIT SECTION*, strictly a placeholder for visual alignment purposes.*/}
|
||||
<div style={{ flex: "0 0 auto", position: "relative", width: 30, height: 27 }}>
|
||||
|
||||
<div className={this.props.classes.editSectionNoContent}>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
} if (control.isOutputSelect()) {
|
||||
} if (control.isOutputText()) {
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", width: item_width, margin: 8, height: 98 }}>
|
||||
<div className={classes.controlFrame}
|
||||
style={{ display: "flex", flexDirection: "column", width: item_width }}>
|
||||
{/* TITLE SECTION */}
|
||||
<div style={{ flex: "0 0 auto", width: "100%", marginBottom: 8, marginLeft: 0, marginRight: 0 }}>
|
||||
<div className={classes.titleSection}
|
||||
style={{ flex: "0 0 auto", width: "100%" }}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
<Typography variant="caption" display="block" style={{
|
||||
<Typography noWrap variant="caption" display="block" style={{
|
||||
width: "100%",
|
||||
textAlign: "left"
|
||||
}}> {control.name}</Typography>
|
||||
}}> {control.name === "" ? "\u00A0": control.name}</Typography>
|
||||
</ControlTooltip>
|
||||
</div>
|
||||
|
||||
{/* CONTROL SECTION */}
|
||||
|
||||
<div style={{ flex: "1 1 auto", display: "flex", justifyContent: "start", alignItems: "center", flexFlow: "row nowrap" }}>
|
||||
<div className={classes.midSection} style={{
|
||||
display: "flex", justifyContent: "center",
|
||||
flexFlow: "row nowrap", paddingTop: 6
|
||||
|
||||
}}>
|
||||
<Typography variant="caption" display="block" noWrap style={{ width: "100%" }}>
|
||||
{text}
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
{/* LABEL/EDIT SECTION*, strictly a placeholder for visual alignment purposes.*/}
|
||||
<div style={{ flex: "0 0 auto", position: "relative", width: 40, height: 27 }}>
|
||||
|
||||
<div className={this.props.classes.editSectionNoContent}>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
|
||||
} else {
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", width: item_width, margin: 8, height: 98 }}>
|
||||
<div className={classes.controlFrame}
|
||||
style={{ display: "flex", flexDirection: "column", width: item_width }}>
|
||||
{/* TITLE SECTION */}
|
||||
<div style={{ flex: "0 0 auto", width: "100%", marginBottom: 8, marginLeft: 0, marginRight: 0 }}>
|
||||
<div className={classes.titleSection}
|
||||
style={{ flex: "0 0 auto", width: "100%" }}>
|
||||
<ControlTooltip uiControl={control}>
|
||||
<Typography variant="caption" display="block" style={{
|
||||
<Typography noWrap variant="caption" display="block" style={{
|
||||
width: "100%",
|
||||
textAlign: "left"
|
||||
}}> {control.name}</Typography>
|
||||
@@ -456,15 +535,15 @@ const PluginOutputControl =
|
||||
</div>
|
||||
{/* CONTROL SECTION */}
|
||||
|
||||
<div style={{ flex: "1 1 auto", display: "flex", justifyContent: "start", alignItems: "center", flexFlow: "row nowrap" }}>
|
||||
<Typography variant="caption" display="block" noWrap style={{ width: "100%" }}>
|
||||
<div className={classes.midSection}
|
||||
style={{ display: "flex", justifyContent: "start", alignItems: "center", flexFlow: "row nowrap" }}>
|
||||
<Typography noWrap variant="caption" display="block" style={{ width: "100%" }}>
|
||||
{text}
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
{/* LABEL/EDIT SECTION*, strictly a placeholder for visual alignment purposes.*/}
|
||||
<div style={{ flex: "0 0 auto", position: "relative", width: 40, height: 27 }}>
|
||||
|
||||
<div className={this.props.classes.editSectionNoContent}>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
|
||||
@@ -38,6 +38,8 @@ export const SplitTypeControl: UiControl = new UiControl().deserialize({
|
||||
not_on_gui: false,
|
||||
toggled_property: false,
|
||||
trigger_property: false,
|
||||
pipedal_ledColor: "",
|
||||
|
||||
scale_points: ScalePoint.deserialize_array(
|
||||
[
|
||||
{ value: 0, label: "A/B" },
|
||||
@@ -62,6 +64,7 @@ export const SplitAbControl: UiControl = new UiControl().deserialize({
|
||||
not_on_gui: false,
|
||||
toggled_property: false,
|
||||
trigger_property: false,
|
||||
pipedal_ledColor: "",
|
||||
scale_points: ScalePoint.deserialize_array(
|
||||
[
|
||||
{ value: 0, label: "A" },
|
||||
@@ -87,6 +90,8 @@ export const SplitMixControl: UiControl = new UiControl().deserialize({
|
||||
toggled_property: false,
|
||||
trigger_property: false,
|
||||
|
||||
pipedal_ledColor: "",
|
||||
|
||||
scale_points: []
|
||||
});
|
||||
|
||||
@@ -105,6 +110,7 @@ export const SplitPanLeftControl: UiControl = new UiControl().deserialize({
|
||||
not_on_gui: false,
|
||||
toggled_property: false,
|
||||
trigger_property: false,
|
||||
pipedal_ledColor: "",
|
||||
|
||||
scale_points: []
|
||||
});
|
||||
@@ -124,6 +130,7 @@ export const SplitVolLeftControl: UiControl = new UiControl().deserialize({
|
||||
not_on_gui: false,
|
||||
toggled_property: false,
|
||||
trigger_property: false,
|
||||
pipedal_ledColor: "",
|
||||
|
||||
units: Units.db,
|
||||
scale_points: [
|
||||
@@ -150,6 +157,7 @@ export const SplitPanRightControl: UiControl = new UiControl().deserialize({
|
||||
not_on_gui: false,
|
||||
toggled_property: false,
|
||||
trigger_property: false,
|
||||
pipedal_ledColor: "",
|
||||
|
||||
scale_points: []
|
||||
});
|
||||
@@ -169,6 +177,7 @@ export const SplitVolRightControl: UiControl = new UiControl().deserialize({
|
||||
not_on_gui: false,
|
||||
toggled_property: false,
|
||||
trigger_property: false,
|
||||
pipedal_ledColor: "",
|
||||
|
||||
units: Units.db,
|
||||
scale_points: [
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
// Copyright (c) 2024 Robin E. R. 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
|
||||
|
||||
+3
-2
@@ -55,13 +55,14 @@ namespace macaron
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'};
|
||||
|
||||
|
||||
size_t in_len = size;
|
||||
size_t out_len = 4 * ((in_len + 2) / 3);
|
||||
std::string ret(out_len, '\0');
|
||||
size_t i;
|
||||
size_t i = 0;
|
||||
char *p = const_cast<char *>(ret.c_str());
|
||||
|
||||
for (i = 0; i < in_len - 2; i += 3)
|
||||
for (i = 0; i+2 < in_len; i += 3)
|
||||
{
|
||||
*p++ = sEncodingTable[(data[i] >> 2) & 0x3F];
|
||||
*p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
|
||||
|
||||
@@ -44,6 +44,10 @@ const std::vector<ModFileTypes::ModDirectory> ModFileTypes::ModDirectories =
|
||||
{"nammodel", "NeuralAmpModels", "Neural Amp Models", {".nam"}}, // Ratatoille, Mike's NAM.
|
||||
{"aidadspmodel", "shared/aidaaix", "AIDA IAX Models", {".json", ".aidaiax"}}, // Ratatoille
|
||||
{"mlmodel", "ToobMlModels", "ML Models", {".json"}}, //
|
||||
|
||||
// pipedal-specific types. use pipedal_ui:filetypes instead (or in addition to) mod:fileTypes.
|
||||
{"recording", "shared/audio/Recordings", "Recordings", {"audio/*"}}, // Recordings from recordings plugins.
|
||||
|
||||
//
|
||||
};
|
||||
|
||||
@@ -134,7 +138,7 @@ void ModFileTypes::CreateDefaultDirectories(const std::filesystem::path &rootDir
|
||||
|
||||
fs::create_directories(path);
|
||||
}
|
||||
|
||||
|
||||
if (!fs::exists(rootDirectory / "shared" / "audio" / "Cab IR Files"))
|
||||
{
|
||||
fs::create_symlink(
|
||||
|
||||
+5
-1
@@ -59,6 +59,8 @@
|
||||
#define PIPEDAL_UI__yBottom PIPEDAL_UI_PREFIX "yBottom"
|
||||
#define PIPEDAL_UI__width PIPEDAL_UI_PREFIX "width"
|
||||
|
||||
#define PIPEDAL_UI__ledColor PIPEDAL_UI_PREFIX "ledColor"
|
||||
|
||||
|
||||
namespace pipedal {
|
||||
|
||||
@@ -122,14 +124,16 @@ namespace pipedal {
|
||||
UiFileProperty(PluginHost*pHost, const LilvNode*node, const std::filesystem::path&resourcePath);
|
||||
UiFileProperty(const std::string&name, const std::string&patchProperty,const std::string &directory);
|
||||
|
||||
|
||||
|
||||
std::vector<std::string>& modDirectories() { return modDirectories_; }
|
||||
const std::vector<std::string>& modDirectories() const { return modDirectories_; }
|
||||
|
||||
bool useLegacyModDirectory() const { return useLegacyModDirectory_; }
|
||||
void useLegacyModDirectory(bool value) { useLegacyModDirectory_ = value; }
|
||||
const std::string &label() const { return label_; }
|
||||
|
||||
int32_t index() const { return index_; }
|
||||
void index(int32_t value) { index_ = value; }
|
||||
|
||||
const std::string &directory() const { return directory_; }
|
||||
void directory(const std::string &path) { directory_ = path; }
|
||||
|
||||
+31
-1
@@ -150,6 +150,7 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld)
|
||||
pipedalUI__mimeType = lilv_new_uri(pWorld, PIPEDAL_UI__mimeType);
|
||||
pipedalUI__outputPorts = lilv_new_uri(pWorld, PIPEDAL_UI__outputPorts);
|
||||
pipedalUI__text = lilv_new_uri(pWorld, PIPEDAL_UI__text);
|
||||
pipedalUI__ledColor = lilv_new_uri(pWorld,PIPEDAL_UI__ledColor);
|
||||
|
||||
pipedalUI__frequencyPlot = lilv_new_uri(pWorld, PIPEDAL_UI__frequencyPlot);
|
||||
pipedalUI__xLeft = lilv_new_uri(pWorld, PIPEDAL_UI__xLeft);
|
||||
@@ -194,6 +195,7 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld)
|
||||
dc__format = lilv_new_uri(pWorld, "http://purl.org/dc/terms/format");
|
||||
|
||||
mod__fileTypes = lilv_new_uri(pWorld, "http://moddevices.com/ns/mod#fileTypes");
|
||||
pipedalui__fileTypes =lilv_new_uri(pWorld, PIPEDAL_UI__fileTypes);
|
||||
}
|
||||
|
||||
void PluginHost::LilvUris::Free()
|
||||
@@ -623,8 +625,11 @@ std::shared_ptr<PiPedalUI> Lv2PluginInfo::FindWritablePathProperties(PluginHost
|
||||
// rfs:range atom:Path?
|
||||
if (lilv_world_ask(pWorld, propertyUri, lv2Host->lilvUris->rdfs__range, lv2Host->lilvUris->atom__Path))
|
||||
{
|
||||
|
||||
|
||||
AutoLilvNode label = lilv_world_get(pWorld, propertyUri, lv2Host->lilvUris->rdfs__label, nullptr);
|
||||
std::string strLabel = label.AsString();
|
||||
|
||||
if (strLabel.length() != 0)
|
||||
{
|
||||
std::filesystem::path path = this->bundle_path();
|
||||
@@ -636,7 +641,20 @@ std::shared_ptr<PiPedalUI> Lv2PluginInfo::FindWritablePathProperties(PluginHost
|
||||
std::make_shared<UiFileProperty>(
|
||||
strLabel, propertyUri.AsUri(), lv2DirectoryName);
|
||||
|
||||
AutoLilvNodes mod__fileTypes = lilv_world_find_nodes(pWorld, propertyUri, lv2Host->lilvUris->mod__fileTypes, nullptr);
|
||||
|
||||
AutoLilvNode indexNode = lilv_world_get(pWorld, propertyUri, lv2Host->lilvUris->lv2core__index, nullptr);
|
||||
int32_t index = indexNode.AsInt(-1);
|
||||
fileProperty->index(index);
|
||||
|
||||
|
||||
// if there's a pipedalui_fileTypes node, use that instead.
|
||||
|
||||
AutoLilvNodes mod__fileTypes = lilv_world_find_nodes(pWorld, propertyUri, lv2Host->lilvUris->pipedalui__fileTypes, nullptr);
|
||||
if (!mod__fileTypes)
|
||||
{
|
||||
mod__fileTypes = lilv_world_find_nodes(pWorld, propertyUri, lv2Host->lilvUris->mod__fileTypes, nullptr);
|
||||
}
|
||||
|
||||
LILV_FOREACH(nodes, i, mod__fileTypes)
|
||||
{
|
||||
// "nam,nammodel"
|
||||
@@ -979,6 +997,15 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
|
||||
this->connection_optional_ = lilv_port_has_property(plugin, pPort, host->lilvUris->core__connectionOptional);
|
||||
this->trigger_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris->portprops__trigger);
|
||||
|
||||
AutoLilvNode port_ledColor = lilv_port_get(plugin,pPort,host->lilvUris->pipedalUI__ledColor);
|
||||
if (port_ledColor)
|
||||
{
|
||||
auto value = lilv_node_as_string(port_ledColor);
|
||||
if (value) {
|
||||
this->pipedal_ledColor_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
LilvScalePoints *pScalePoints = lilv_port_get_scale_points(plugin, pPort);
|
||||
LILV_FOREACH(scale_points, iSP, pScalePoints)
|
||||
{
|
||||
@@ -1650,6 +1677,8 @@ json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{
|
||||
MAP_REF(Lv2PortInfo, not_on_gui),
|
||||
MAP_REF(Lv2PortInfo, buffer_type),
|
||||
MAP_REF(Lv2PortInfo, port_group),
|
||||
MAP_REF(Lv2PortInfo, pipedal_ledColor),
|
||||
|
||||
json_map::enum_reference("units", &Lv2PortInfo::units_, get_units_enum_converter()),
|
||||
MAP_REF(Lv2PortInfo, comment)}};
|
||||
|
||||
@@ -1715,6 +1744,7 @@ json_map::storage_type<Lv2PluginUiPort> Lv2PluginUiPort::jmap{{
|
||||
MAP_REF(Lv2PluginUiPort, trigger_property),
|
||||
MAP_REF(Lv2PluginUiPort, scale_points),
|
||||
MAP_REF(Lv2PluginUiPort, port_group),
|
||||
MAP_REF(Lv2PluginUiPort, pipedal_ledColor),
|
||||
|
||||
json_map::enum_reference("units", &Lv2PluginUiPort::units_, get_units_enum_converter()),
|
||||
MAP_REF(Lv2PluginUiPort, comment),
|
||||
|
||||
@@ -227,6 +227,7 @@ namespace pipedal
|
||||
std::string comment_;
|
||||
PiPedalUI::ptr piPedalUI_;
|
||||
|
||||
std::string pipedal_ledColor_;
|
||||
|
||||
public:
|
||||
bool IsSwitch() const
|
||||
@@ -293,6 +294,7 @@ namespace pipedal
|
||||
LV2_PROPERTY_GETSET(port_group);
|
||||
LV2_PROPERTY_GETSET(comment);
|
||||
LV2_PROPERTY_GETSET_SCALAR(units);
|
||||
LV2_PROPERTY_GETSET(pipedal_ledColor);
|
||||
|
||||
LV2_PROPERTY_GETSET(buffer_type);
|
||||
|
||||
@@ -497,6 +499,7 @@ namespace pipedal
|
||||
is_logarithmic_(pPort->is_logarithmic()), integer_property_(pPort->integer_property()), enumeration_property_(pPort->enumeration_property()),
|
||||
toggled_property_(pPort->toggled_property()), not_on_gui_(pPort->not_on_gui()), scale_points_(pPort->scale_points()),
|
||||
trigger_property_(pPort->trigger_property()),
|
||||
pipedal_ledColor_(pPort->pipedal_ledColor()),
|
||||
comment_(pPort->comment()), units_(pPort->units()),
|
||||
connection_optional_(pPort->connection_optional())
|
||||
{
|
||||
@@ -540,6 +543,7 @@ namespace pipedal
|
||||
bool trigger_property_ = false;
|
||||
std::vector<Lv2ScalePoint> scale_points_;
|
||||
std::string port_group_;
|
||||
std::string pipedal_ledColor_;
|
||||
|
||||
Units units_ = Units::none;
|
||||
std::string comment_;
|
||||
@@ -572,6 +576,7 @@ namespace pipedal
|
||||
LV2_PROPERTY_GETSET_SCALAR(is_program_controller);
|
||||
LV2_PROPERTY_GETSET(custom_units);
|
||||
LV2_PROPERTY_GETSET(connection_optional);
|
||||
LV2_PROPERTY_GETSET(pipedal_ledColor);
|
||||
|
||||
public:
|
||||
static json_map::storage_type<Lv2PluginUiPort> jmap;
|
||||
@@ -707,6 +712,8 @@ namespace pipedal
|
||||
AutoLilvNode pipedalUI__outputPorts;
|
||||
AutoLilvNode pipedalUI__text;
|
||||
|
||||
AutoLilvNode pipedalUI__ledColor;
|
||||
|
||||
AutoLilvNode time_Position;
|
||||
AutoLilvNode time_barBeat;
|
||||
AutoLilvNode time_beatsPerMinute;
|
||||
@@ -717,6 +724,7 @@ namespace pipedal
|
||||
|
||||
AutoLilvNode ui__portNotification;
|
||||
AutoLilvNode ui__plugin;
|
||||
AutoLilvNode ui__ledColor;
|
||||
AutoLilvNode ui__protocol;
|
||||
AutoLilvNode ui__floatProtocol;
|
||||
AutoLilvNode ui__peakProtocol;
|
||||
@@ -735,6 +743,7 @@ namespace pipedal
|
||||
AutoLilvNode dc__format;
|
||||
|
||||
AutoLilvNode mod__fileTypes;
|
||||
AutoLilvNode pipedalui__fileTypes;
|
||||
|
||||
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user