ToobPlayer UI. .mdata file handling.
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { pathConcat, pathParentDirectory } from "./FileUtils";
|
||||
import { pathConcat, pathParentDirectory, pathFileName } from "./FileUtils";
|
||||
import { PiPedalModel } from "./PiPedalModel";
|
||||
|
||||
export class ThumbnailType {
|
||||
@@ -32,6 +32,40 @@ export class ThumbnailType {
|
||||
};
|
||||
|
||||
|
||||
const fileVersionRegex = /\((\d+)\)\.[0-9a-zA-Z]*$/;
|
||||
function getVersionSuffix(filePath: string): string | null {
|
||||
const match = filePath.match(fileVersionRegex);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
|
||||
export function getTrackTitle(pathname: string, metadata: AudioFileMetadata | null | undefined): string {
|
||||
if (!metadata) {
|
||||
return pathFileName(pathname);
|
||||
}
|
||||
if (metadata.title === "") {
|
||||
return pathFileName(pathname);
|
||||
}
|
||||
let trackDisplay = "";
|
||||
if (metadata.track > 0) {
|
||||
if (metadata.track >= 1000) {
|
||||
trackDisplay = (metadata.track % 1000).toString() + "/" + Math.floor(metadata.track / 1000) + ". ";
|
||||
} else {
|
||||
trackDisplay = metadata.track.toString() + ". ";
|
||||
}
|
||||
}
|
||||
let result = trackDisplay + metadata.title;
|
||||
if (result === "") {
|
||||
result = pathFileName(pathname);
|
||||
}
|
||||
let versionSuffix = getVersionSuffix(pathname);
|
||||
if (versionSuffix) {
|
||||
result += " (" + versionSuffix + ")";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
export default class AudioFileMetadata {
|
||||
//AudioFileMetadata&operator=(const AudioFileMetadata&) = default;
|
||||
deserialize(o: any) {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import { BankIndexEntry, BankIndex } from './Banks';
|
||||
@@ -422,17 +422,17 @@ const BankDialog = withStyles(
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<AppBar className={classes.dialogAppBar} style={{ display: this.isEditMode() ? "none" : "block" }} >
|
||||
<Toolbar>
|
||||
<IconButton edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
<IconButtonEx tooltip="Back" edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
disabled={this.isEditMode()}
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<Typography noWrap variant="h6" className={classes.dialogTitle}>
|
||||
Banks
|
||||
</Typography>
|
||||
<IconButton color="inherit" onClick={(e) => this.showActionBar(true)} >
|
||||
<IconButtonEx tooltip="Edit" color="inherit" onClick={(e) => this.showActionBar(true)} >
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<AppBar className={actionBarClass} style={{ display: this.isEditMode() ? "block" : "none" }}
|
||||
@@ -440,14 +440,14 @@ const BankDialog = withStyles(
|
||||
>
|
||||
<Toolbar>
|
||||
{(!this.props.isEditDialog) ? (
|
||||
<IconButton edge="start" color="inherit" onClick={(e) => this.showActionBar(false)} aria-label="close">
|
||||
<IconButtonEx tooltip="Close" edge="start" color="inherit" onClick={(e) => this.showActionBar(false)} aria-label="close">
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
) : (
|
||||
<IconButton edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
<IconButtonEx tooltip="Back" edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
|
||||
)}
|
||||
<Typography noWrap variant="h6" className={classes.dialogTitle}>
|
||||
@@ -477,12 +477,12 @@ const BankDialog = withStyles(
|
||||
}
|
||||
}
|
||||
/>
|
||||
<IconButton color="inherit" onClick={(e) => this.handleDeleteClick()} >
|
||||
<IconButtonEx tooltip="Delete" color="inherit" onClick={(e) => this.handleDeleteClick()} >
|
||||
<img src="/img/old_delete_outline_white_24dp.svg" alt="Delete" style={{ width: 24, height: 24, opacity: 0.6 }} />
|
||||
</IconButton>
|
||||
<IconButton color="inherit" onClick={(e) => { this.onMoreClick(e) }} >
|
||||
</IconButtonEx>
|
||||
<IconButtonEx tooltip="More..." color="inherit" onClick={(e) => { this.onMoreClick(e) }} >
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<Menu
|
||||
id="more-menu"
|
||||
anchorEl={this.state.moreMenuAnchorEl}
|
||||
|
||||
@@ -42,7 +42,10 @@ function ButtonEx(props: ButtonExProps) {
|
||||
placement="top-start" arrow
|
||||
enterDelay={1500} enterNextDelay={1500}
|
||||
>
|
||||
<Button {...extra} style={style} />
|
||||
<span>
|
||||
{/* Using span to prevent tooltip from disappearing when button is disabled */}
|
||||
<Button {...extra} style={style} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ import UploadFileDialog from './UploadFileDialog';
|
||||
import OkCancelDialog from './OkCancelDialog';
|
||||
import HomeIcon from '@mui/icons-material/Home';
|
||||
import FilePropertyDirectorySelectDialog from './FilePropertyDirectorySelectDialog';
|
||||
import { getAlbumArtUri } from './AudioFileMetadata';
|
||||
import { getAlbumArtUri, getTrackTitle } from './AudioFileMetadata';
|
||||
|
||||
interface Point {
|
||||
x: number;
|
||||
@@ -95,6 +95,7 @@ const audioFileExtensions: { [name: string]: boolean } = {
|
||||
};
|
||||
|
||||
|
||||
|
||||
function screenToClient(element: HTMLDivElement, point: Point): Point {
|
||||
const dpr = (window.devicePixelRatio || 1) as number;;
|
||||
let rect = element.getBoundingClientRect();
|
||||
@@ -650,15 +651,18 @@ export default withStyles(
|
||||
}
|
||||
getCompactTrackTitle(fileEntry: FileEntry): string {
|
||||
let metadata = fileEntry.metadata;
|
||||
let title = getTrackTitle(fileEntry.pathname, metadata);
|
||||
if (!metadata) {
|
||||
return "#error";
|
||||
return title;
|
||||
}
|
||||
let title = this.getTrackTitle(fileEntry);
|
||||
|
||||
if (metadata.album !== "") {
|
||||
title += " (" + metadata.album + ")";
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
|
||||
getAlbumTitle(fileEntry: FileEntry): string {
|
||||
let artist = fileEntry.metadata?.artist || "";
|
||||
if (artist == "") {
|
||||
@@ -668,28 +672,6 @@ export default withStyles(
|
||||
let joiner = (artist !== "" && album !== "") ? " - " : "";
|
||||
return album + joiner + artist;
|
||||
}
|
||||
getTrackTitle(fileEntry: FileEntry): string {
|
||||
if (!fileEntry.metadata) {
|
||||
return pathFileName(fileEntry.pathname);
|
||||
}
|
||||
if (fileEntry.metadata.title === "") {
|
||||
return pathFileName(fileEntry.pathname);
|
||||
}
|
||||
let metadata = fileEntry.metadata;
|
||||
let trackDisplay = "";
|
||||
if (metadata.track > 0) {
|
||||
if (metadata.track >= 1000) {
|
||||
trackDisplay = (metadata.track % 1000).toString() + "/" + Math.floor(metadata.track / 1000) + ". ";
|
||||
} else {
|
||||
trackDisplay = metadata.track.toString() + ". ";
|
||||
}
|
||||
}
|
||||
let result = trackDisplay + metadata.title;
|
||||
if (result === "") {
|
||||
result = pathFileName(fileEntry.pathname);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
getTrackThumbnail(fileEntry: FileEntry): string {
|
||||
return getAlbumArtUri(this.model, fileEntry.metadata, fileEntry.pathname);
|
||||
}
|
||||
@@ -1100,7 +1082,7 @@ export default withStyles(
|
||||
className={classes.secondaryText}
|
||||
variant="body2"
|
||||
style={{ flex: "0 0 auto", textOverflow: "ellipsis", textAlign: "left", marginBottom: 4 }}>
|
||||
{this.getTrackTitle(value)}
|
||||
{getTrackTitle(value.pathname, value.metadata)}
|
||||
</Typography>
|
||||
<Typography noWrap className={classes.secondaryText}
|
||||
variant="body2"
|
||||
|
||||
@@ -42,6 +42,7 @@ import { AndroidHostInterface, FakeAndroidHost } from './AndroidHost';
|
||||
import { ColorTheme, getColorScheme, setColorScheme } from './DarkMode';
|
||||
import FilePropertyDirectoryTree from './FilePropertyDirectoryTree';
|
||||
import AudioFileMetadata from './AudioFileMetadata';
|
||||
import { pathFileName } from './FileUtils';
|
||||
|
||||
|
||||
export enum State {
|
||||
@@ -2445,10 +2446,13 @@ export class PiPedalModel //implements PiPedalModel
|
||||
let downloadUrl = this.varServerUrl + "downloadMediaFile?path=" + encodeURIComponent(filePath);
|
||||
|
||||
// download with no flashing temporary tab.
|
||||
let link = window.document.createElement("A") as HTMLLinkElement;
|
||||
let link = window.document.createElement("A") as HTMLAnchorElement;
|
||||
link.href = downloadUrl;
|
||||
link.setAttribute("download", "");
|
||||
link.target = "_blank";
|
||||
link.setAttribute("download", pathFileName(filePath));
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
download(targetType: string, instanceId: number | string): void {
|
||||
@@ -2457,10 +2461,13 @@ export class PiPedalModel //implements PiPedalModel
|
||||
|
||||
// window.open(url, "_blank");
|
||||
// download with no flashing temporary tab.
|
||||
let link = window.document.createElement("A") as HTMLLinkElement;
|
||||
let link = window.document.createElement("A") as HTMLAnchorElement;
|
||||
link.href = url;
|
||||
link.setAttribute("download", "");
|
||||
link.target = "_blank";
|
||||
link.download = "download.piPreset";
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
uploadUserFile(uploadPage: string, file: File, contentType: string = "application/octet-stream", abortController?: AbortController): Promise<string> {
|
||||
|
||||
@@ -450,13 +450,13 @@ const PluginControlView =
|
||||
/>
|
||||
));
|
||||
}
|
||||
private ixKey: number = 1;
|
||||
private ixKey: number = 1;
|
||||
|
||||
makeStandardControl(uiControl: UiControl, controlValues: ControlValue[]): ReactNode {
|
||||
let symbol = uiControl.symbol;
|
||||
if (!uiControl.is_input) {
|
||||
return (
|
||||
<PluginOutputControl key={uiControl.symbol } instanceId={this.props.instanceId} uiControl={uiControl} />
|
||||
<PluginOutputControl key={uiControl.symbol} instanceId={this.props.instanceId} uiControl={uiControl} />
|
||||
|
||||
);
|
||||
}
|
||||
@@ -472,7 +472,7 @@ const PluginControlView =
|
||||
throw new PiPedalStateError("Missing control value.");
|
||||
}
|
||||
return ((
|
||||
<PluginControl key={uiControl.symbol} instanceId={this.props.instanceId} uiControl={uiControl} value={controlValue.value}
|
||||
<PluginControl key={"ppc" + uiControl.symbol} instanceId={this.props.instanceId} uiControl={uiControl} value={controlValue.value}
|
||||
onChange={(value: number) => { this.onControlValueChanged(controlValue!.key, value) }}
|
||||
onPreviewChange={(value: number) => { this.onPreviewChange(controlValue!.key, value) }}
|
||||
requestIMEEdit={(uiControl: any, value: any) => this.requestImeEdit(uiControl, value)}
|
||||
@@ -498,7 +498,7 @@ const PluginControlView =
|
||||
let previousControl = controls[controls.length - 1];
|
||||
if (!(previousControl instanceof ControlGroup)) {
|
||||
let pair = (
|
||||
<div key={"k"+this.ixKey++} className={classes.controlPair}>
|
||||
<div key={"k" + this.ixKey++} className={classes.controlPair}>
|
||||
{previousControl as ReactNode}
|
||||
{newControl}
|
||||
</div>
|
||||
@@ -508,7 +508,7 @@ const PluginControlView =
|
||||
// (e.g.. inserting at position 4 still places the extended control after four previous controls
|
||||
controls.push((
|
||||
|
||||
<div key={"k"+this.ixKey++} className={classes.controlSpacer} />
|
||||
<div key={"k" + this.ixKey++} className={classes.controlSpacer} />
|
||||
));
|
||||
} else {
|
||||
controls.push(newControl);
|
||||
@@ -686,7 +686,7 @@ const PluginControlView =
|
||||
let item = controlGroup.controls[j];
|
||||
controls.push(
|
||||
(
|
||||
<div key={"ctl" + (this.controlKeyIndex++)} className={classes.controlPadding}>
|
||||
<div key={"ctlx" + (this.controlKeyIndex++)} className={classes.controlPadding}>
|
||||
{item}
|
||||
</div>
|
||||
|
||||
@@ -695,7 +695,7 @@ const PluginControlView =
|
||||
}
|
||||
|
||||
result.push((
|
||||
<div key={"ctl" + (this.controlKeyIndex++)} className={!isLandscapeGrid ? classes.portGroup : classes.portGroupLandscape}>
|
||||
<div key={"ctlx" + (this.controlKeyIndex++)} className={!isLandscapeGrid ? classes.portGroup : classes.portGroupLandscape}>
|
||||
<div className={classes.portGroupTitle}>
|
||||
<Tooltip title={controlGroup.name}
|
||||
placement="top-start" arrow enterDelay={1500} enterNextDelay={1500}
|
||||
@@ -713,21 +713,20 @@ const PluginControlView =
|
||||
));
|
||||
|
||||
} else {
|
||||
if (this.fullScreen())
|
||||
{
|
||||
result.push(
|
||||
<div style={{position: "relative", width: "100%", height:"100%"}}
|
||||
>
|
||||
{node as ReactNode}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
result.push((
|
||||
<div key={"ctl" + (this.controlKeyIndex++)} className={hasGroups ? classes.portgroupControlPadding : classes.controlPadding} >
|
||||
{node as ReactNode}
|
||||
</div>
|
||||
));
|
||||
}
|
||||
if (this.fullScreen()) {
|
||||
result.push(
|
||||
<div key={"fullScreen" + this.props.instanceId} style={{ position: "relative", width: "100%", height: "100%" }}
|
||||
>
|
||||
{node as ReactNode}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
result.push((
|
||||
<div key={"ctl" + (this.controlKeyIndex++)} className={hasGroups ? classes.portgroupControlPadding : classes.controlPadding} >
|
||||
{node as ReactNode}
|
||||
</div>
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,10 +20,11 @@
|
||||
import React, { SyntheticEvent,Component } from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
import {isDarkMode} from './DarkMode';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { PiPedalModel, PiPedalModelFactory, PresetIndexEntry, PresetIndex } from './PiPedalModel';
|
||||
import Button from "@mui/material/Button";
|
||||
import Button from '@mui/material/Button';
|
||||
import ButtonEx from './ButtonEx';
|
||||
import ButtonBase from "@mui/material/ButtonBase";
|
||||
import DialogEx from './DialogEx';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
@@ -351,17 +352,17 @@ const PresetDialog = withStyles(
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<AppBar className={classes.dialogAppBar} style={{ display: this.isEditMode() ? "none" : "block" }} >
|
||||
<Toolbar>
|
||||
<IconButton edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
<IconButtonEx tooltip="Back" edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
disabled={this.isEditMode()}
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<Typography variant="h6" className={classes.dialogTitle}>
|
||||
Presets
|
||||
</Typography>
|
||||
<IconButton color="inherit" onClick={(e) => this.showActionBar(true)} >
|
||||
<IconButtonEx tooltip="Edit"color="inherit" onClick={(e) => this.showActionBar(true)} >
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<AppBar className={actionBarClass} style={{ display: this.isEditMode() ? "block" : "none" }}
|
||||
@@ -369,14 +370,14 @@ const PresetDialog = withStyles(
|
||||
>
|
||||
<Toolbar>
|
||||
{(!this.props.isEditDialog) ? (
|
||||
<IconButton edge="start" color="inherit" onClick={(e) => this.showActionBar(false)} aria-label="close">
|
||||
<IconButtonEx tooltip="Close" edge="start" color="inherit" onClick={(e) => this.showActionBar(false)} aria-label="close">
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
) : (
|
||||
<IconButton edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
<IconButtonEx edge="start" tooltip="Back" color="inherit" onClick={this.handleDialogClose} aria-label="back"
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
|
||||
)}
|
||||
<Typography variant="h6" className={classes.dialogTitle}>
|
||||
@@ -402,12 +403,12 @@ const PresetDialog = withStyles(
|
||||
}
|
||||
}
|
||||
/>
|
||||
<IconButton color="inherit" onClick={(e) => this.handleDeleteClick()} >
|
||||
<IconButtonEx tooltip="Delete" color="inherit" onClick={(e) => this.handleDeleteClick()} >
|
||||
<img src="/img/old_delete_outline_white_24dp.svg" alt="Delete" style={{ width: 24, height: 24, opacity: 0.6 }} />
|
||||
</IconButton>
|
||||
<IconButton color="inherit" onClick={(e) => { this.onMoreClick(e) }} >
|
||||
</IconButtonEx>
|
||||
<IconButtonEx tooltip="More..." color="inherit" onClick={(e) => { this.onMoreClick(e) }} >
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
</IconButtonEx>
|
||||
<Menu
|
||||
id="more-menu"
|
||||
anchorEl={this.state.moreMenuAnchorEl}
|
||||
|
||||
@@ -45,6 +45,8 @@ import { getAlbumArtUri } from './AudioFileMetadata';
|
||||
import RepeatIcon from '@mui/icons-material/Repeat';
|
||||
import Timebase, { LoopParameters, TimebaseUnits } from './Timebase';
|
||||
import ControlSlider from './ControlSlider';
|
||||
import { getTrackTitle } from './AudioFileMetadata';
|
||||
|
||||
|
||||
let Player__seek = "http://two-play.com/plugins/toob-player#seek"
|
||||
const AUDIO_FILE_PROPERTY_URI = "http://two-play.com/plugins/toob-player#audioFile";
|
||||
@@ -320,19 +322,9 @@ export default function ToobPlayerControl(
|
||||
}
|
||||
model.getAudioFileMetadata(path)
|
||||
.then((metadata) => {
|
||||
let strTrack: string = "";
|
||||
if (metadata.track > 0) {
|
||||
let disc = (metadata.track / 1000);
|
||||
if (disc >= 1) {
|
||||
strTrack = (metadata.track % 1000).toString() + '/' + Math.floor(disc).toString();
|
||||
} else {
|
||||
strTrack = metadata.track.toString();
|
||||
}
|
||||
strTrack += ". ";
|
||||
}
|
||||
let coverArtUri = getAlbumArtUri(model, metadata, path);
|
||||
setCoverArt(coverArtUri);
|
||||
setTitle(strTrack + metadata.title);
|
||||
setTitle(getTrackTitle(path, metadata));
|
||||
setAlbum(metadata.album);
|
||||
setArtist(metadata.artist);
|
||||
setAlbumArtist(metadata.albumArtist);
|
||||
@@ -419,6 +411,8 @@ export default function ToobPlayerControl(
|
||||
);
|
||||
}
|
||||
function FilePanel() {
|
||||
let textColor = pluginState == PluginState.Error ? "error" : "textPrimary";
|
||||
|
||||
return (
|
||||
<ButtonBase style={{ display: "block", width: "100%", borderRadius: 10, textAlign: "left" }}
|
||||
onClick={() => { SelectFile() }}
|
||||
@@ -442,10 +436,10 @@ export default function ToobPlayerControl(
|
||||
</Typography>
|
||||
) : (
|
||||
<div>
|
||||
<Typography variant="body1" noWrap>
|
||||
<Typography variant="body1" color={textColor} noWrap>
|
||||
{titleLine}
|
||||
</Typography>
|
||||
<Typography variant="body2" noWrap sx={{}}>
|
||||
<Typography variant="body2" color={textColor} noWrap sx={{}}>
|
||||
{albumLine}
|
||||
</Typography>
|
||||
</div>
|
||||
@@ -483,6 +477,27 @@ export default function ToobPlayerControl(
|
||||
|
||||
function onLoopPropertyChanged(loopSettingsJson: string) {
|
||||
try {
|
||||
if (loopSettingsJson === "") {
|
||||
setTimebase(
|
||||
{
|
||||
units: TimebaseUnits.Seconds,
|
||||
tempo: 120.0,
|
||||
timeSignature: { numerator: 4, denominator: 4 }
|
||||
})
|
||||
let loopParameters = {
|
||||
start: 0.0,
|
||||
loopEnable: false,
|
||||
loopStart: 0.0,
|
||||
loopEnd: 0.0
|
||||
};
|
||||
setLoopParameters(loopParameters);
|
||||
|
||||
setStart(0.0);
|
||||
setLoopEnable(false);
|
||||
setLoopStart(0.0);
|
||||
setLoopEnd(0.0);
|
||||
return;
|
||||
}
|
||||
let atomObject = JSON.parse(loopSettingsJson);
|
||||
let loopParameters: LoopParameters = atomObject.loopParameters as LoopParameters;
|
||||
let newTimebase: Timebase | undefined = atomObject.timebase as (Timebase | undefined);;
|
||||
@@ -827,7 +842,7 @@ export default function ToobPlayerControl(
|
||||
playing={!paused}
|
||||
onPreview={() => { handlePreview(); }}
|
||||
value={loopParameters}
|
||||
onCancelPlaying= {()=> { handleCancelPlaying();}}
|
||||
onCancelPlaying={() => { handleCancelPlaying(); }}
|
||||
timebase={timebase}
|
||||
onTimebaseChange={(newTimebase) => {
|
||||
setTimebase(newTimebase);
|
||||
|
||||
@@ -110,14 +110,14 @@ const ToobPlayerView =
|
||||
for (let mixControl of mixPanel.controls) {
|
||||
extraControls.push(
|
||||
(
|
||||
<div key={"k"+ iKey++} style={{flex: "0 0 auto", position: "relative",height: "100%" }}>
|
||||
<div key={"kExtra"+ iKey++} style={{flex: "0 0 auto", position: "relative",height: "100%" }}>
|
||||
{mixControl}
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
||||
let panel = (
|
||||
<ToobPlayerControl key="MusicPlayer" instanceId={this.props.instanceId} extraControls={extraControls} />
|
||||
<ToobPlayerControl key={"MusicPlayer" + this.props.instanceId} instanceId={this.props.instanceId} extraControls={extraControls} />
|
||||
);
|
||||
|
||||
let result: (React.ReactNode | ControlGroup)[] = [];
|
||||
@@ -128,6 +128,7 @@ const ToobPlayerView =
|
||||
render() {
|
||||
return (
|
||||
<PluginControlView
|
||||
key={"ToobPlayerView" + this.props.instanceId}
|
||||
instanceId={this.props.instanceId}
|
||||
item={this.props.item}
|
||||
customization={this}
|
||||
@@ -145,7 +146,7 @@ class ToobPlayerViewFactory implements IControlViewFactory {
|
||||
uri: string = "http://two-play.com/plugins/toob-player";
|
||||
|
||||
Create(model: PiPedalModel, pedalboardItem: PedalboardItem): React.ReactNode {
|
||||
return (<ToobPlayerView instanceId={pedalboardItem.instanceId} item={pedalboardItem} />);
|
||||
return (<ToobPlayerView key={"ppmv"+pedalboardItem.instanceId} instanceId={pedalboardItem.instanceId} item={pedalboardItem} />);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user