mirror of https://github.com/LedFx/LedFx.git
Spotify Graph song audio analysis
Co-Authored-By: GabeDahl <45904434+GabeDahl@users.noreply.github.com>
This commit is contained in:
parent
f377cda44c
commit
34980f3d79
|
@ -23,6 +23,7 @@
|
|||
"history": "^4.10.1",
|
||||
"material-ui-theme-state": "^1.0.30",
|
||||
"moment": "^2.29.1",
|
||||
"moment-duration-format": "^2.3.2",
|
||||
"pkce-challenge": "^2.1.0",
|
||||
"qs": "^6.10.1",
|
||||
"react": "^17.0.2",
|
||||
|
@ -43,6 +44,7 @@
|
|||
"react-schema-form": "^0.9.10",
|
||||
"react-spotify-web-playback": "^0.8.2",
|
||||
"react-toastify": "^8.0.3",
|
||||
"recharts": "^2.1.8",
|
||||
"redux": "^4.0.5",
|
||||
"redux-actions": "^2.6.5",
|
||||
"redux-thunk": "^2.3.0",
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
import React, { PureComponent } from 'react';
|
||||
import moment from 'moment';
|
||||
import { ComposedChart, ResponsiveContainer, XAxis, YAxis, Tooltip, Legend, Area } from 'recharts';
|
||||
|
||||
var momentDurationFormatSetup = require('moment-duration-format');
|
||||
momentDurationFormatSetup(moment);
|
||||
|
||||
export default class Chart extends PureComponent {
|
||||
render() {
|
||||
const pitchClasses = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
|
||||
const pitchColors = [
|
||||
'#ff3333',
|
||||
'#ff9933',
|
||||
'#ffff33',
|
||||
'#99ff33',
|
||||
'#33ff33',
|
||||
'#33ff99',
|
||||
'#33ffff',
|
||||
'#3399ff',
|
||||
'#3333ff',
|
||||
'#9933ff',
|
||||
'#ff33ff',
|
||||
'#ff3399',
|
||||
];
|
||||
|
||||
const { segments, width, height, pitches } = this.props;
|
||||
return (
|
||||
<ResponsiveContainer width={width} height={height} debounce={5}>
|
||||
<ComposedChart
|
||||
barGap={1}
|
||||
data={segments}
|
||||
margin={{
|
||||
top: 15,
|
||||
right: 20,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
>
|
||||
<XAxis
|
||||
dataKey="start"
|
||||
tickFormatter={time => {
|
||||
let timeStr = time.toString().split('');
|
||||
if (timeStr.includes('.')) {
|
||||
const sec = timeStr
|
||||
.slice(
|
||||
0,
|
||||
timeStr.findIndex(el => el === '.')
|
||||
)
|
||||
.join('');
|
||||
let milsec = timeStr
|
||||
.slice(timeStr.findIndex(el => el === '.') + 1)
|
||||
.join('');
|
||||
if (milsec.length === 2) {
|
||||
milsec = milsec.concat('0');
|
||||
} else {
|
||||
milsec = milsec.concat('00');
|
||||
}
|
||||
let t = moment.duration.format(
|
||||
[
|
||||
moment.duration({
|
||||
seconds: sec,
|
||||
milliseconds: milsec,
|
||||
}),
|
||||
],
|
||||
'm:ss.S'
|
||||
)[0];
|
||||
return t.substring(0, t.length - 1);
|
||||
} else {
|
||||
return moment.duration.format(
|
||||
[
|
||||
moment.duration({
|
||||
seconds: time,
|
||||
}),
|
||||
],
|
||||
'm:ss.S'
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<YAxis
|
||||
tickFormatter={value => `${value.toFixed(0)}%`}
|
||||
domain={[0, 15]}
|
||||
allowDataOverflow="true"
|
||||
/>
|
||||
<Tooltip
|
||||
formatter={(value, name) =>
|
||||
value > 6 ? [`${value.toFixed(0)}%`, name] : [null, null]
|
||||
}
|
||||
contentStyle={{ backgroundColor: '#0f0f0f', border: 'none' }}
|
||||
labelStyle={{ color: '#f1f1f1' }}
|
||||
itemSorter={item => -item.value}
|
||||
/>
|
||||
<Legend align="left" height={250} layout="vertical" content={renderLegend} />
|
||||
{pitchClasses.map((p, i) => {
|
||||
if (pitches[p] === true) {
|
||||
return (
|
||||
<Area
|
||||
fillOpacity="1"
|
||||
strokeOpacity="1"
|
||||
name={p}
|
||||
stackId="1"
|
||||
stroke={pitchColors[i]}
|
||||
fill={pitchColors[i]}
|
||||
dataKey={s => s.pitches[i]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
|
||||
{/* Dynamically Painted Bar Chart */}
|
||||
{/* <Bar dataKey={segments}>
|
||||
{data.map((entry, index) => (
|
||||
<Cell fill/>
|
||||
))}
|
||||
</Bar> */}
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const renderLegend = props => {
|
||||
const payload = props;
|
||||
payload.payload.reverse();
|
||||
return (
|
||||
<ul style={{ height: 210, display: 'table', paddingLeft: 10, marginTop: 5 }}>
|
||||
{payload.payload.map((entry, index) => (
|
||||
<li key={`item-${index}`} style={{ color: entry.color, display: 'table-row' }}>
|
||||
{entry.value}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
import React from 'react';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Slider from '@material-ui/core/Slider';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
maxWidth: 400,
|
||||
},
|
||||
input: {
|
||||
width: 120,
|
||||
},
|
||||
});
|
||||
|
||||
export default function ChartSize(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Typography id="input-slider" gutterBottom color="primary">
|
||||
Zoom
|
||||
</Typography>
|
||||
<Grid container spacing={2} alignItems="center">
|
||||
<Grid item xs>
|
||||
<Slider
|
||||
value={props.value}
|
||||
step={100}
|
||||
max={20000}
|
||||
min={300}
|
||||
valueLabelDisplay="auto"
|
||||
onChange={props.handleSizeSliderChange}
|
||||
aria-labelledby="chart-size-slider"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
makeStyles,
|
||||
Typography,
|
||||
Grid,
|
||||
Select,
|
||||
MenuItem,
|
||||
InputLabel,
|
||||
FormControl,
|
||||
} from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
select: {
|
||||
color: '#1ED760',
|
||||
width: '90%',
|
||||
'&:before': {
|
||||
borderColor: '#1ED760',
|
||||
},
|
||||
'&:after': {
|
||||
borderColor: '#1ED760',
|
||||
},
|
||||
},
|
||||
icon: {
|
||||
fill: '#1ED760',
|
||||
},
|
||||
}));
|
||||
|
||||
const chords = {
|
||||
'Major Chords': {
|
||||
'C Major': [0, 4, 7],
|
||||
'C Sharp Major': [1, 5, 8],
|
||||
'D Major': [2, 6, 9],
|
||||
'E Flat Major': [3, 7, 10],
|
||||
'E Major': [4, 8, 11],
|
||||
'F Major': [5, 9, 0],
|
||||
'F Sharp Major': [6, 10, 1],
|
||||
'G Major': [7, 11, 2],
|
||||
'A Flat Major': [8, 0, 3],
|
||||
'A Major': [9, 1, 4],
|
||||
'B Flat Major': [10, 2, 5],
|
||||
'B Major': [11, 3, 6],
|
||||
},
|
||||
'Minor Chords': {
|
||||
'C Minor': [0, 3, 7],
|
||||
'C Sharp Minor': [1, 4, 8],
|
||||
'D Minor': [2, 5, 9],
|
||||
'E Flat Minor': [3, 6, 10],
|
||||
'E Minor': [4, 7, 11],
|
||||
'F Minor': [5, 8, 0],
|
||||
'F Sharp Minor': [6, 9, 1],
|
||||
'G Minor': [7, 10, 2],
|
||||
'A Flat Minor': [8, 11, 3],
|
||||
'A Minor': [9, 0, 4],
|
||||
'B Flat Minor': [10, 1, 5],
|
||||
'B Minor': [11, 2, 6],
|
||||
},
|
||||
'Diminished Chords': {
|
||||
'C Diminished': [0, 3, 6],
|
||||
'C Sharp Diminished': [1, 4, 7],
|
||||
'D Diminished': [2, 5, 8],
|
||||
'E Flat Diminished': [3, 6, 9],
|
||||
'E Diminished': [4, 7, 10],
|
||||
'F Diminished': [5, 8, 11],
|
||||
'F Sharp Diminished': [6, 9, 0],
|
||||
'G Diminished': [7, 10, 1],
|
||||
'A Flat Diminished': [8, 11, 2],
|
||||
'A Diminished': [9, 0, 3],
|
||||
'B Flat Diminished': [10, 1, 4],
|
||||
'B Diminished': [11, 2, 5],
|
||||
},
|
||||
'Augmented Chords': {
|
||||
'C Augmented': [0, 4, 8],
|
||||
'C Sharp Augmented': [1, 5, 9],
|
||||
'D Augmented': [2, 6, 10],
|
||||
'E Flat Augmented': [3, 7, 11],
|
||||
'E Augmented': [4, 8, 0],
|
||||
'F Augmented': [5, 9, 1],
|
||||
'F Sharp Augmented': [6, 10, 2],
|
||||
'G Augmented': [7, 11, 3],
|
||||
'A Flat Augmented': [8, 0, 4],
|
||||
'A Augmented': [9, 1, 5],
|
||||
'B Flat Augmented': [10, 2, 6],
|
||||
'B Augmented': [11, 3, 7],
|
||||
},
|
||||
};
|
||||
|
||||
export default function PitchSelect(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
{Object.keys(chords).map(group => (
|
||||
<Grid item xs={6} sm={3}>
|
||||
<FormControl style={{ width: '100%' }}>
|
||||
<InputLabel id={group}>
|
||||
<Typography color="primary">{group}</Typography>
|
||||
</InputLabel>
|
||||
<Select
|
||||
onChange={props.handleChordClick}
|
||||
labelId={group}
|
||||
color="primary"
|
||||
className={classes.select}
|
||||
inputProps={{ classes: { icon: classes.icon } }}
|
||||
>
|
||||
{Object.keys(chords[group]).map(chord => {
|
||||
return <MenuItem value={chords[group][chord]}>{chord}</MenuItem>;
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
import React, { Component } from 'react';
|
||||
import Chart from './Chart';
|
||||
import SectionChart from './SectionChart';
|
||||
import ChartSize from './ChartSize';
|
||||
import ChordButtons from './ChordButtons';
|
||||
import PitchSelect from './PitchSelect';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
Card,
|
||||
Grid,
|
||||
Typography,
|
||||
CardContent,
|
||||
Avatar,
|
||||
Backdrop,
|
||||
CircularProgress,
|
||||
LinearProgress,
|
||||
} from '@material-ui/core';
|
||||
import { updatePlayerState } from 'modules/spotify';
|
||||
import { getAsyncIntegrations } from 'modules/integrations';
|
||||
|
||||
export class Layout extends Component {
|
||||
state = {
|
||||
width: 2000,
|
||||
height: 255,
|
||||
pitches: {
|
||||
C: true,
|
||||
'C#': true,
|
||||
D: true,
|
||||
'D#': true,
|
||||
E: true,
|
||||
F: true,
|
||||
'F#': true,
|
||||
G: true,
|
||||
'G#': true,
|
||||
A: true,
|
||||
'A#': true,
|
||||
B: true,
|
||||
},
|
||||
openFromSearch: false,
|
||||
};
|
||||
|
||||
handleSizeSliderChange = (event, newValue) => {
|
||||
this.setState({ width: newValue });
|
||||
};
|
||||
|
||||
handleCheck = event => {
|
||||
const e = event;
|
||||
this.setState({
|
||||
pitches: {
|
||||
...this.state.pitches,
|
||||
[e.target.name]: e.target.checked,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
handleChordClick = event => {
|
||||
const e = event;
|
||||
const pitchClasses = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
|
||||
const numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
||||
|
||||
numbers.map(item => {
|
||||
this.setState(state => ({
|
||||
pitches: {
|
||||
...state.pitches,
|
||||
[pitchClasses[item]]: false,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
e.target.value.map(item => {
|
||||
this.setState((state, props) => ({
|
||||
pitches: {
|
||||
...this.state.pitches,
|
||||
[pitchClasses[item]]: true,
|
||||
},
|
||||
}));
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="wrapper" style={{ width: '100%' }}>
|
||||
<Grid
|
||||
container
|
||||
direction="column"
|
||||
justify="center"
|
||||
alignItems="center"
|
||||
spacing={2}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Grid item xs={12} sm={12}>
|
||||
{/* <Header /> */}
|
||||
</Grid>
|
||||
<Grid container item xs={12} sm={12} justify="center">
|
||||
<Card
|
||||
style={{
|
||||
maxWidth: '99%',
|
||||
minWidth: '99%',
|
||||
overflowX: 'auto',
|
||||
backgroundColor: '#0f0f0f',
|
||||
}}
|
||||
>
|
||||
<CardContent>
|
||||
{Object.keys(this.props.analysis).length < 1 ? (
|
||||
<Grid
|
||||
container
|
||||
direction="column"
|
||||
justify="center"
|
||||
alignItems="center"
|
||||
style={{ height: this.state.height, width: '95vw' }}
|
||||
>
|
||||
<div style={{ height: 5, marginBottom: 40, width: '100%' }}>
|
||||
<LinearProgress style={{ height: 5, width: '100%' }} />
|
||||
</div>
|
||||
<Typography variant="h5" align="center" color="secondary">
|
||||
Audio Analysis for current song generated here
|
||||
</Typography>
|
||||
|
||||
<div style={{ height: 5, marginTop: 40, width: '100%' }}>
|
||||
<LinearProgress
|
||||
variant="query"
|
||||
style={{ height: 5, width: '100%' }}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
) : (
|
||||
<div>
|
||||
<Grid
|
||||
container
|
||||
spacing={3}
|
||||
alignItems="center"
|
||||
justify="center"
|
||||
style={{ maxWidth: '95vw' }}
|
||||
>
|
||||
<Grid item>
|
||||
<Avatar
|
||||
style={{ border: '1px solid white' }}
|
||||
alt="album-image"
|
||||
src={this.props.albumURL}
|
||||
variant="rounded"
|
||||
>
|
||||
A
|
||||
</Avatar>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography
|
||||
variant="h5"
|
||||
style={{ color: '#f1f1f1' }}
|
||||
>
|
||||
{this.props.name}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="h7" style={{ color: '#666' }}>
|
||||
{this.props.artist}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<div
|
||||
style={{
|
||||
maxWidth: '98%',
|
||||
overflowX: 'auto',
|
||||
overflowY: 'hidden',
|
||||
}}
|
||||
>
|
||||
<SectionChart
|
||||
sections={this.props.sections}
|
||||
segments={this.props.segments}
|
||||
width={this.state.width}
|
||||
height={this.state.height}
|
||||
/>
|
||||
<Chart
|
||||
segments={this.props.segments}
|
||||
width={this.state.width}
|
||||
height={this.state.height}
|
||||
pitches={this.state.pitches}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item container xs={12} sm={12} spacing={2}>
|
||||
<Grid item id="left" xs={12} sm={4}>
|
||||
<Card style={{ height: '100%', backgroundColor: '#0f0f0f' }}>
|
||||
<CardContent>
|
||||
<ChartSize
|
||||
handleSizeSliderChange={this.handleSizeSliderChange}
|
||||
value={this.state.width}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item id="right" xs={12} sm={8}>
|
||||
<Card style={{ height: '100%', backgroundColor: '#0f0f0f' }}>
|
||||
<CardContent
|
||||
style={{ height: '100%', paddingTop: 0, paddingBottom: 0 }}
|
||||
>
|
||||
<Grid
|
||||
style={{ height: '100%' }}
|
||||
container
|
||||
alignItems="center"
|
||||
justify="center"
|
||||
>
|
||||
<PitchSelect
|
||||
pitches={this.state.pitches}
|
||||
handleCheck={this.handleCheck}
|
||||
/>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item id="right" xs={12} sm={12}>
|
||||
<Card style={{ backgroundColor: '#0f0f0f' }}>
|
||||
<CardContent>
|
||||
<ChordButtons handleChordClick={this.handleChordClick} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{/* <Backdrop style={{zIndex: 1000}} open='true'>
|
||||
<CircularProgress />
|
||||
</Backdrop> */}
|
||||
</Grid>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
state => ({
|
||||
analysis: state.spotify.audioAnalysis,
|
||||
segments: state.spotify.audioAnalysis.segments,
|
||||
sections: state.spotify.audioAnalysis.sections,
|
||||
}),
|
||||
{ updatePlayerState, getAsyncIntegrations }
|
||||
)(Layout);
|
|
@ -0,0 +1,38 @@
|
|||
import React from 'react';
|
||||
import Checkbox from '@material-ui/core/Checkbox';
|
||||
import { FormGroup, FormControlLabel, makeStyles } from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
color: '#1ED760',
|
||||
'&$checked': {
|
||||
color: '#1ED760',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export default function PitchSelect(props) {
|
||||
const pitchClasses = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<FormGroup row="true">
|
||||
{pitchClasses.map(p => {
|
||||
return (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
color="primary"
|
||||
className={classes.root}
|
||||
checked={props.pitches[p]}
|
||||
onClick={props.handleCheck}
|
||||
name={p}
|
||||
/>
|
||||
}
|
||||
label={p}
|
||||
style={{ color: '#1ED760' }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
import React, { PureComponent } from 'react';
|
||||
import {
|
||||
ComposedChart,
|
||||
ResponsiveContainer,
|
||||
XAxis,
|
||||
YAxis,
|
||||
Tooltip,
|
||||
Bar,
|
||||
Cell,
|
||||
Legend,
|
||||
} from 'recharts';
|
||||
|
||||
export default class Chart extends PureComponent {
|
||||
render() {
|
||||
const pitchColors = [
|
||||
'#ff3333',
|
||||
'#ff9933',
|
||||
'#ffff33',
|
||||
'#99ff33',
|
||||
'#33ff33',
|
||||
'#33ff99',
|
||||
'#33ffff',
|
||||
'#3399ff',
|
||||
'#3333ff',
|
||||
'#9933ff',
|
||||
'#ff33ff',
|
||||
'#ff3399',
|
||||
];
|
||||
|
||||
const { sections, width } = this.props;
|
||||
return (
|
||||
<ResponsiveContainer width={width} height={20} debounce={5}>
|
||||
<ComposedChart
|
||||
barGap={1}
|
||||
data={sections}
|
||||
margin={{
|
||||
top: 15,
|
||||
right: 20,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
>
|
||||
<XAxis hide="true" dataKey="start" unit="s" />
|
||||
<YAxis hide="true" height={10} />
|
||||
<Legend align="left" height={20} layout="vertical" content={renderLegend} />
|
||||
{/* <Tooltip
|
||||
content={renderTooltip}
|
||||
/> */}
|
||||
<Bar dataKey="start" minPointSize={10} barGap={0}>
|
||||
{sections
|
||||
? sections.map((entry, index) => (
|
||||
<Cell key={`cell-${index}`} fill={pitchColors[entry.key]} />
|
||||
))
|
||||
: null}
|
||||
</Bar>
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const renderLegend = () => {
|
||||
return (
|
||||
<ul
|
||||
style={{
|
||||
height: 20,
|
||||
display: 'table',
|
||||
paddingLeft: 10,
|
||||
marginTop: 0,
|
||||
listStyleType: 'none',
|
||||
}}
|
||||
>
|
||||
<li style={{ color: '#f1f1f1' }}>Key</li>
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
// const renderTooltip = ({ active, payload, label }) => {
|
||||
// const pitchClasses = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
|
||||
// if (active === true) {
|
||||
// return (
|
||||
// <div style={{backgroundColor: '#010101', padding: 10}}>
|
||||
// <div style={{color:'#f1f1f1'}}>{`${pitchClasses[payload[0].payload.key]}`}</div>
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
// }
|
|
@ -24,7 +24,7 @@ const DataRow = ({ id, name, type, data }) => {
|
|||
event_filter: { scene_name: dr[1].scene_name },
|
||||
},
|
||||
});
|
||||
//let newData = data.filter(x => x[0] !== dr[0]);
|
||||
|
||||
setTest(!test);
|
||||
};
|
||||
return data
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import withStyles from '@material-ui/core/styles/withStyles';
|
||||
import Moment from 'react-moment';
|
||||
import moment from 'moment';
|
||||
import { ToastContainer, toast } from 'react-toastify';
|
||||
// import { makeStyles } from '@material-ui/core/styles';
|
||||
import Button from '@material-ui/core/Button';
|
||||
//import Card from '@material-ui/core/Card';
|
||||
//import CardHeader from '@material-ui/core/CardHeader';
|
||||
//import CardContent from '@material-ui/core/CardContent';
|
||||
import {
|
||||
Button,
|
||||
AppBar,
|
||||
Checkbox,
|
||||
FormControl,
|
||||
|
@ -14,33 +15,44 @@ import {
|
|||
InputLabel,
|
||||
Select,
|
||||
Typography,
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
Link,
|
||||
Slider,
|
||||
Table,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableContainer,
|
||||
TableBody,
|
||||
TableCell,
|
||||
Paper,
|
||||
} from '@material-ui/core';
|
||||
import {
|
||||
PlayArrow,
|
||||
Pause,
|
||||
SkipNext,
|
||||
SkipPrevious,
|
||||
ExpandMore,
|
||||
Info,
|
||||
} from '@material-ui/icons';
|
||||
|
||||
} from '@material-ui/core';
|
||||
import { updatePlayerState } from 'modules/spotify';
|
||||
import { getAsyncIntegrations } from 'modules/integrations';
|
||||
import PlayArrow from '@material-ui/icons/PlayArrow';
|
||||
import Pause from '@material-ui/icons/Pause';
|
||||
import SkipNext from '@material-ui/icons/SkipNext';
|
||||
import SkipPrevious from '@material-ui/icons/SkipPrevious';
|
||||
import InfoIcon from '@material-ui/icons/Info';
|
||||
import Link from '@material-ui/core/Link';
|
||||
import { activateScene } from 'modules/scenes';
|
||||
import { addTrigger } from 'proxies/spotify';
|
||||
import Moment from 'react-moment';
|
||||
import moment from 'moment';
|
||||
import Slider from '@material-ui/core/Slider';
|
||||
import { ToastContainer, toast } from 'react-toastify';
|
||||
import RadarChart from 'components/SpotifyPlayer/RadarChart';
|
||||
import Accordion from '@material-ui/core/Accordion';
|
||||
import AccordionSummary from '@material-ui/core/AccordionSummary';
|
||||
import AccordionDetails from '@material-ui/core/AccordionDetails';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
import Table from '@material-ui/core/Table';
|
||||
import TableBody from '@material-ui/core/TableBody';
|
||||
import TableCell from '@material-ui/core/TableCell';
|
||||
import TableContainer from '@material-ui/core/TableContainer';
|
||||
import TableHead from '@material-ui/core/TableHead';
|
||||
import TableRow from '@material-ui/core/TableRow';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import Layout from 'components/AudioAnalysis/Layout';
|
||||
|
||||
// const useStylesAccordin = makeStyles(theme => ({
|
||||
// root: {
|
||||
// width: '100%',
|
||||
// },
|
||||
// heading: {
|
||||
// fontSize: theme.typography.pxToRem(15),
|
||||
// fontWeight: theme.typography.fontWeightRegular,
|
||||
// },
|
||||
// }));
|
||||
|
||||
const data = {
|
||||
datasets: [
|
||||
|
@ -93,6 +105,18 @@ const styles = theme => ({
|
|||
},
|
||||
});
|
||||
|
||||
// const useStyles = makeStyles(theme => ({
|
||||
// sceneButton: {
|
||||
// size: 'large',
|
||||
// margin: theme.spacing(1),
|
||||
// },
|
||||
// submitControls: {
|
||||
// display: 'flex',
|
||||
// flexWrap: 'wrap',
|
||||
// width: '100%',
|
||||
// height: '100%',
|
||||
// },
|
||||
// }));
|
||||
|
||||
class SpotifyPlayer extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -103,6 +127,21 @@ class SpotifyPlayer extends React.Component {
|
|||
effects: '',
|
||||
play: false,
|
||||
player: {},
|
||||
expanded: true,
|
||||
pitches: {
|
||||
C: true,
|
||||
'C#': true,
|
||||
D: true,
|
||||
'D#': true,
|
||||
E: true,
|
||||
F: true,
|
||||
'F#': true,
|
||||
G: true,
|
||||
'G#': true,
|
||||
A: true,
|
||||
'A#': true,
|
||||
B: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -210,6 +249,9 @@ class SpotifyPlayer extends React.Component {
|
|||
getTime(duration) {
|
||||
var seconds = Math.floor((duration / 1000) % 60),
|
||||
minutes = Math.floor((duration / (1000 * 60)) % 60);
|
||||
// hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
|
||||
|
||||
// hours = hours < 10 ? '0' + hours : hours;
|
||||
minutes = minutes < 10 ? '0' + minutes : minutes;
|
||||
seconds = seconds < 10 ? '0' + seconds : seconds;
|
||||
|
||||
|
@ -277,7 +319,7 @@ class SpotifyPlayer extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { playerState, classes, scenes, audioFeatures } = this.props;
|
||||
const { playerState, classes, scenes, audioFeatures, audioAnalysis } = this.props;
|
||||
|
||||
const rows = [];
|
||||
function capitalizeFirstLetter(string) {
|
||||
|
@ -298,7 +340,7 @@ class SpotifyPlayer extends React.Component {
|
|||
return Object.keys(playerState).length === 0 ? (
|
||||
<Link target="_blank" href="https://support.spotify.com/us/article/spotify-connect/">
|
||||
<Typography color="textPrimary">
|
||||
Using Spotify Connect, select LedFX <Info></Info>
|
||||
Using Spotify Connect, select LedFX <InfoIcon></InfoIcon>
|
||||
</Typography>
|
||||
</Link>
|
||||
) : (
|
||||
|
@ -495,17 +537,21 @@ class SpotifyPlayer extends React.Component {
|
|||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid md={12} container item style={{ margin: '30px 20px' }}>
|
||||
<Accordion style={{ width: '100%' }}>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMore />}
|
||||
aria-controls="panel1a-content"
|
||||
id="panel1a-header"
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Typography>Audio Features</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Accordion
|
||||
style={{ width: '100%' }}
|
||||
expanded={this.state.expanded}
|
||||
onChange={() => this.setState({ expanded: !this.state.expanded })}
|
||||
>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-controls="panel1a-content"
|
||||
id="panel1a-header"
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
<Typography>Audio Features</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Grid md={12} container item style={{ margin: '30px 20px' }}>
|
||||
<Grid xs={6} item>
|
||||
<TableContainer component={Paper}>
|
||||
<Table sx={{ minWidth: 650 }} aria-label="simple table">
|
||||
|
@ -544,7 +590,7 @@ class SpotifyPlayer extends React.Component {
|
|||
</Table>
|
||||
</TableContainer>
|
||||
</Grid>
|
||||
<Grid xs={6} item>
|
||||
<Grid xs={6} item sx={{ background: 'gray !important' }}>
|
||||
<RadarChart
|
||||
chartData={data}
|
||||
chartValues={audioFeatures}
|
||||
|
@ -552,9 +598,12 @@ class SpotifyPlayer extends React.Component {
|
|||
// positon={playerState.position}
|
||||
/>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
</Grid>
|
||||
<Grid container xs={12}>
|
||||
<Layout />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
</Grid>
|
||||
<ToastContainer />
|
||||
</AppBar>
|
||||
|
@ -566,6 +615,8 @@ export default connect(
|
|||
state => ({
|
||||
playerState: state.spotify.playerState,
|
||||
audioFeatures: state.spotify.audioFeatures,
|
||||
audioAnalysis: state.spotify.audioAnalysis,
|
||||
|
||||
accessToken: state.spotify.accessToken,
|
||||
scenes: state.scenes.list,
|
||||
integrations: state.integrations.list.spotify,
|
||||
|
|
Loading…
Reference in New Issue