audio-source-composer
Version:
Audio Source Composer
230 lines (200 loc) • 9.09 kB
JavaScript
import React from "react";
import {
ASUIIcon,
ASUIFormEntry,
ASUIPanel,
ASUIClickable,
ASUIClickableDropDown,
ASUIMenuAction, ASUIInputRange
} from "../../components";
import Instruction from "../../song/instruction/Instruction";
import {ArgType, Values} from "../../song";
export default class ASComposerTrackPanel extends React.Component {
constructor(props) {
super(props);
const composer = props.composer;
this.cb = {
renderMenuSelectTrack: () => this.renderMenuSelectTrack(),
trackSelectIndicesPrompt: () => composer.trackSelectIndicesPrompt(),
instructionInsertAtSelectedTrackCursor: () => composer.instructionInsertAtSelectedTrackCursor(),
instructionDeleteSelected: () => composer.instructionDeleteIndices(),
renderMenuKeyboardSetOctave: () => this.renderMenuKeyboardSetOctave(),
}
this.state = {
selectedIndices: [],
selectedInstructionData: [0, 'C4', '1B'],
selectedTrackName: null,
keyboardOctave: 4
}
}
updateSelectedTrackIndices(selectedTrackName, selectedIndices=[]) {
const composer = this.props.composer;
const state = {
selectedTrackName,
selectedIndices,
};
if(selectedIndices.length > 0) {
const instructionData = composer.getSong().instructionDataGetByIndex(selectedTrackName, selectedIndices[0]);
state.selectedInstructionData = instructionData.slice();
state.selectedInstructionData[0] = 0;
// console.log('selectedInstructionData', state.selectedInstructionData);
}
this.setState(state);
}
render() {
const selectedIndices = this.state.selectedIndices;
// const activeTrack = composer.trackHasActive(selectedTrackName) ? composer.trackGetState(selectedTrackName) : null;
// const selectedIndices = activeTrack ? activeTrack.getSelectedIndices() : [];
return [
<ASUIPanel
key="track"
viewKey="track"
header={`Selected Track`}>
<ASUIFormEntry className="track-name" header="Current">
<ASUIClickableDropDown
button wide
// className="track-selection"
options={this.cb.renderMenuSelectTrack}
title="Current Track"
children={this.state.selectedTrackName || "N/A"}
/>
</ASUIFormEntry>
<ASUIFormEntry className="track-selection" header="Selection">
<ASUIClickable
button wide
// className="track-selection"
onAction={this.cb.trackSelectIndicesPrompt}
title="Selected Track Notes"
children={selectedIndices.length > 0 ? getSelectedIndicesString(selectedIndices) : "None"}
/>
</ASUIFormEntry>
<ASUIFormEntry className="track-insert" header="Add">
<ASUIClickable
button wide
// className="instruction-insert"
onAction={this.cb.instructionInsertAtSelectedTrackCursor}
title="Insert Instruction"
// disabled={selectedIndices.length > 0}
>
<ASUIIcon source="insert"/>
</ASUIClickable>
</ASUIFormEntry>
<ASUIFormEntry className="track-delete" header="Rem">
<ASUIClickable
button wide
// className="instruction-delete"
onAction={this.cb.instructionDeleteSelected}
title="Delete Instruction"
disabled={selectedIndices.length === 0}
>
<ASUIIcon source="remove"/>
</ASUIClickable>
</ASUIFormEntry>
<ASUIFormEntry className="keyboard-octave" header="Octave">
<ASUIClickableDropDown
button wide
arrow={'▼'}
className="keyboard-octave"
options={this.cb.renderMenuKeyboardSetOctave}
title="Change Keyboard Octave"
>{this.state.keyboardOctave}</ASUIClickableDropDown>
</ASUIFormEntry>
</ASUIPanel>,
<ASUIPanel
key="instruction"
viewKey="instruction"
header={`Selected Instruction`}>
{this.renderInstructionForms()}
</ASUIPanel>
];
}
/** Keyboard Commands **/
keyboardChangeOctave(keyboardOctave = null) {
if (!Number.isInteger(keyboardOctave))
throw new Error("Invalid segment ID");
this.setState({keyboardOctave});
}
/** Forms **/
renderInstructionForms() {
const composer = this.props.composer;
const instructionData = this.state.selectedInstructionData;
const processor = new Instruction(instructionData);
const [, argTypeList] = processor.processInstructionArgList();
const formatStats = {
timeDivision: composer.getSong().getTimeDivision()
};
// console.log('commandString', commandString, params);
let argIndex = 0;
return argTypeList.map((argType, i) => {
if(!argType.consumesArgument)
return null;
argIndex++;
let paramValue = instructionData[argIndex];
switch(argType) {
case ArgType.command:
case ArgType.duration:
case ArgType.frequency:
case ArgType.offset:
case ArgType.trackName:
default:
return this.renderDropDownForm(argType, argIndex, paramValue, formatStats);
case ArgType.velocity:
return this.renderVelocityForm(argType, argIndex, paramValue);
}
});
}
renderDropDownForm(argType, argIndex, paramValue, formatStats={}) {
const selectedInstructionData = this.state.selectedInstructionData;
let header = argType.title.split(' ').pop(); // Long text hack
const composer = this.props.composer;
return <ASUIFormEntry key={argIndex} header={header}>
<ASUIClickableDropDown
button wide
arrow={'▼'}
title={`Change ${argType.title}`}
options={() => composer.renderMenuEditInstructionArgOptions(selectedInstructionData, argType, argIndex, paramValue, newArgValue => {
composer.instructionReplaceArgByType(this.state.selectedTrackName, this.state.selectedIndices, argType, newArgValue);
const selectedInstructionData = this.state.selectedInstructionData.slice();
selectedInstructionData[argIndex] = newArgValue;
this.setState({selectedInstructionData})
})}
// TODO: update state.selectedInstructionData
>{argType.format(paramValue, formatStats)}</ASUIClickableDropDown>
</ASUIFormEntry>
}
renderVelocityForm(argType, argIndex, paramValue, header="Velocity", title="Instruction Velocity") {
const composer = this.props.composer;
return <ASUIFormEntry key={argIndex} header={header}>
<ASUIInputRange
min={0}
max={100}
title={title}
value={paramValue || 100}
format={ASUIInputRange.formats.percent}
onChange={(newVelocity) => {
composer.instructionReplaceArgByType(this.state.selectedTrackName, this.state.selectedIndices, argType, newVelocity);
const selectedInstructionData = this.state.selectedInstructionData.slice();
selectedInstructionData[argIndex] = newVelocity;
this.setState({selectedInstructionData})
}}
/>
</ASUIFormEntry>;
}
/** Menu **/
renderMenuSelectTrack() {
const composer = this.props.composer;
return composer.renderMenuSelectTrack(trackName => {
composer.trackSelect(trackName)
}, null, composer.getSelectedTrackName())
}
renderMenuKeyboardSetOctave() {
return Values.instance.getNoteOctaves(octave =>
<ASUIMenuAction key={octave} onAction={(e) => this.keyboardChangeOctave(octave)}>{octave}</ASUIMenuAction>
);
}
}
function getSelectedIndicesString(selectedIndices) {
if(selectedIndices.length <= 8)
return selectedIndices.join(',');
return `[${selectedIndices.length} selected]`;
}