💡 Add mute and hide controls
Enable to set whether an instrument should be shown or played in instruments window.
This commit is contained in:
parent
49b98d447b
commit
bcbb2a7a69
css
images/icons/instruments
js
|
@ -44,6 +44,22 @@ input[type="url"]:focus, input[type="week"]:focus {
|
|||
padding: 0.75em 2em;
|
||||
}
|
||||
|
||||
/* icon button */
|
||||
.bt.icon {
|
||||
-webkit-appearance: none;
|
||||
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
border: 0;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
|
||||
background: center center no-repeat none;
|
||||
text-indent: -10000em;
|
||||
}
|
||||
|
||||
/* primary button */
|
||||
.bt.primary {
|
||||
border: 1px solid #e0e0e0;
|
||||
|
|
|
@ -258,25 +258,6 @@ a {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.main .control button {
|
||||
-webkit-appearance: none;
|
||||
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
|
||||
background: none;
|
||||
}
|
||||
|
||||
.main .control .switch-play-state, .main .control .open-channels-window,
|
||||
.main .control .close {
|
||||
background: center center no-repeat;
|
||||
text-indent: -10000em;
|
||||
}
|
||||
|
||||
.main .control > * {
|
||||
display: block;
|
||||
margin: 0 auto 1em auto;
|
||||
|
@ -424,6 +405,28 @@ a {
|
|||
background-image: url('../images/icons/instruments/organ.png');
|
||||
}
|
||||
|
||||
/** actions **/
|
||||
.vex .channels li button.sound, .vex .channels li button.notes {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.vex .channels li button.sound {
|
||||
background-image: url('../images/icons/instruments/sound-on.png');
|
||||
}
|
||||
|
||||
.vex .channels li button.sound.off {
|
||||
background-image: url('../images/icons/instruments/sound-off.png');
|
||||
}
|
||||
|
||||
.vex .channels li button.notes {
|
||||
background-image: url('../images/icons/instruments/notes-on.png');
|
||||
}
|
||||
|
||||
.vex .channels li button.notes.off {
|
||||
background-image: url('../images/icons/instruments/notes-off.png');
|
||||
}
|
||||
|
||||
/**
|
||||
* UI Components
|
||||
*/
|
||||
|
|
Binary file not shown.
After (image error) Size: 274 B |
Binary file not shown.
After (image error) Size: 227 B |
Binary file not shown.
After (image error) Size: 294 B |
Binary file not shown.
After (image error) Size: 354 B |
|
@ -7,9 +7,10 @@
|
|||
/**
|
||||
* Modal for managing channels
|
||||
*
|
||||
* @prop {channels: Array} List of channels
|
||||
* @prop {open: func} Called to open the window
|
||||
* @prop {close: func} Called to close the window
|
||||
* @prop {channels: Array} List of channels
|
||||
* @prop {switch: func} Called to switch a param on given channel
|
||||
* @prop {open: func} Called to open the window
|
||||
* @prop {close: func} Called to close the window
|
||||
*/
|
||||
App.components.modal('Channel', {
|
||||
displayName: 'Board',
|
||||
|
@ -18,6 +19,7 @@
|
|||
propTypes: {
|
||||
channels: React.PropTypes.array,
|
||||
|
||||
switch: React.PropTypes.func,
|
||||
open: React.PropTypes.func,
|
||||
close: React.PropTypes.func
|
||||
},
|
||||
|
@ -26,21 +28,37 @@
|
|||
return {
|
||||
channels: [],
|
||||
|
||||
switch: function () {},
|
||||
open: function () {},
|
||||
close: function () {}
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a function to switch a param on given channel
|
||||
*
|
||||
* @param {channel: object} Channel object
|
||||
*/
|
||||
switch: function (channel) {
|
||||
return function (param) {
|
||||
this.props.switch(param, channel.id);
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Render modal
|
||||
*/
|
||||
render: function () {
|
||||
var title = 'Instruments', channels;
|
||||
|
||||
console.log('rendering!');
|
||||
|
||||
channels = this.props.channels.map(function (channel) {
|
||||
channel.key = 'channel-' + channel.id;
|
||||
channel.switch = this.switch(channel);
|
||||
|
||||
return App.components.Channel.Channel(channel);
|
||||
});
|
||||
}, this);
|
||||
|
||||
return React.DOM.form({
|
||||
className: 'vex-dialog-form channels'
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
/*jshint browser:true */
|
||||
/*globals React, App */
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Display channel
|
||||
* Display a channel
|
||||
*
|
||||
* @prop {id: number} Channel ID
|
||||
* @prop {program: number} Program number
|
||||
* @prop {sound: bool} Whether sound should be played or not
|
||||
* @prop {notes: bool} Whether notes should be displayed or not
|
||||
* @prop {switch: func} Called to switch a param
|
||||
*/
|
||||
App.components.create('Channel', {
|
||||
displayName: 'Channel',
|
||||
|
@ -191,25 +198,38 @@
|
|||
propTypes: {
|
||||
id: React.PropTypes.number.isRequired,
|
||||
program: React.PropTypes.number.isRequired,
|
||||
muted: React.PropTypes.bool,
|
||||
solo: React.PropTypes.bool
|
||||
sound: React.PropTypes.bool,
|
||||
notes: React.PropTypes.bool,
|
||||
|
||||
switch: React.PropTypes.func
|
||||
},
|
||||
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
id: 0,
|
||||
program: 0,
|
||||
muted: false,
|
||||
solo: false
|
||||
sound: true,
|
||||
notes: true,
|
||||
|
||||
switch: function () {}
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a function to switch given parameter
|
||||
*
|
||||
* @param {param: string} Parameter name
|
||||
*/
|
||||
switcher: function (param) {
|
||||
return function (e) {
|
||||
this.props.switch(param);
|
||||
e.preventDefault();
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Render channel
|
||||
*/
|
||||
render: function () {
|
||||
var title = 'Gestion des canaux', type, name,
|
||||
channel = App.components.Channel.Channel;
|
||||
var type, name, channel = App.components.Channel.Channel;
|
||||
|
||||
if (this.props.id === 9) {
|
||||
type = channel.types[2];
|
||||
|
@ -222,7 +242,21 @@
|
|||
return React.DOM.li({
|
||||
className: 'channel ' + type,
|
||||
'data-channel': this.props.id
|
||||
}, React.DOM.span(null, name));
|
||||
}, React.DOM.span(null, [
|
||||
name,
|
||||
React.DOM.button({
|
||||
key: 'sound',
|
||||
className: 'bt icon sound ' +
|
||||
((this.props.notes) ? 'on' : 'off'),
|
||||
onClick: this.switcher('notes')
|
||||
}, 'Afficher/masquer'),
|
||||
React.DOM.button({
|
||||
key: 'notes',
|
||||
className: 'bt icon notes ' +
|
||||
((this.props.sound) ? 'on' : 'off'),
|
||||
onClick: this.switcher('sound')
|
||||
}, 'Jouer/Ne pas jouer')
|
||||
]));
|
||||
}
|
||||
});
|
||||
}());
|
|
@ -86,20 +86,20 @@
|
|||
if (this.props.opened) {
|
||||
controls.push(React.DOM.button({
|
||||
key: 'switchPlayState',
|
||||
className: (this.props.playing) ?
|
||||
'switch-play-state pause' : 'switch-play-state',
|
||||
className: 'bt icon ' + ((this.props.playing) ?
|
||||
'switch-play-state pause' : 'switch-play-state'),
|
||||
onClick: this.switchPlayState
|
||||
}, 'Play/pause'));
|
||||
|
||||
controls.push(React.DOM.button({
|
||||
key: 'openChannelsWindow',
|
||||
className: 'open-channels-window',
|
||||
className: 'bt icon open-channels-window',
|
||||
onClick: this.props.showChannelsModal
|
||||
}, 'Ouvrir le gestionnaire de canaux'));
|
||||
|
||||
controls.push(App.components.UI.Selector({
|
||||
key: 'setPlaySpeed',
|
||||
className: 'set-play-speed',
|
||||
className: 'bt icon set-play-speed',
|
||||
|
||||
values: [1, 0.5, 0.3, 1, 2, 3],
|
||||
value: this.props.speed,
|
||||
|
@ -109,14 +109,14 @@
|
|||
|
||||
controls.push(React.DOM.button({
|
||||
key: 'close',
|
||||
className: 'close',
|
||||
className: 'bt icon close',
|
||||
|
||||
onClick: this.props.close
|
||||
}));
|
||||
} else {
|
||||
controls.push(React.DOM.button({
|
||||
key: 'close',
|
||||
className: 'close',
|
||||
className: 'bt icon close',
|
||||
|
||||
onClick: window.close
|
||||
}));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*jshint es5: true */
|
||||
/*jshint browser:true */
|
||||
/*globals App, React */
|
||||
|
||||
(function () {
|
||||
|
@ -294,11 +294,13 @@
|
|||
if (event.time >= start && event.time < end) {
|
||||
switch (event.type) {
|
||||
case 'noteOn':
|
||||
this.refs.keyboard.on(
|
||||
event.note,
|
||||
event.channel,
|
||||
event.velocity
|
||||
);
|
||||
if (this.state.meta.channels[event.channel].sound) {
|
||||
this.refs.keyboard.on(
|
||||
event.note,
|
||||
event.channel,
|
||||
event.velocity
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'noteOff':
|
||||
this.refs.keyboard.off(
|
||||
|
@ -348,6 +350,39 @@
|
|||
e.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Display a modal for managing channels
|
||||
*/
|
||||
showChannelsModal: function () {
|
||||
if (this.state.url === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.channelsModal.open();
|
||||
},
|
||||
|
||||
/**
|
||||
* Switch given parameter on channel
|
||||
*
|
||||
* @param {param: string} Parameter to switch
|
||||
* @param {channel: number} Channel ID
|
||||
*/
|
||||
switchChannelParam: function (param, channel) {
|
||||
var channels = this.state.meta.channels.slice(0),
|
||||
length = channels.length, i;
|
||||
|
||||
for (i = 0; i < length; i += 1) {
|
||||
if (channels[i].id === channel) {
|
||||
channels[i][param] = !channels[i][param];
|
||||
}
|
||||
}
|
||||
|
||||
this.state.meta.channels = channels;
|
||||
this.setState({
|
||||
meta: this.state.meta
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Delegates
|
||||
*/
|
||||
|
@ -377,18 +412,6 @@
|
|||
});
|
||||
},
|
||||
|
||||
showChannelsModal: function () {
|
||||
if (this.state.url === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
var modal = App.components.Channel.Modal({
|
||||
channels: this.state.meta.channels
|
||||
});
|
||||
|
||||
modal.open();
|
||||
},
|
||||
|
||||
/**
|
||||
* Render panel
|
||||
*/
|
||||
|
@ -405,6 +428,7 @@
|
|||
ref: 'noteboard',
|
||||
|
||||
notes: this.state.meta.notes,
|
||||
channels: this.state.meta.channels,
|
||||
|
||||
playing: this.state.playing,
|
||||
speed: this.state.speed,
|
||||
|
@ -444,6 +468,11 @@
|
|||
onDragEnd: this.resume
|
||||
});
|
||||
|
||||
this.channelsModal = App.components.Channel.Board({
|
||||
channels: this.state.meta.channels,
|
||||
switch: this.switchChannelParam
|
||||
});
|
||||
|
||||
return React.DOM.div({
|
||||
className: 'main',
|
||||
onDragOver: this.dragOver,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/*jshint browser:true */
|
||||
/*globals React, App */
|
||||
|
||||
(function () {
|
||||
|
@ -7,6 +8,7 @@
|
|||
* Displays a noteboard from given notes
|
||||
*
|
||||
* @prop {notes: array} Array of notes to show
|
||||
* @prop {channels: array} Array of configured channels
|
||||
* @prop {time: int} Playback time
|
||||
* @prop {length: int} Playback length
|
||||
* @prop {speed: int} Playback speed
|
||||
|
@ -34,7 +36,8 @@
|
|||
* State and props config
|
||||
*/
|
||||
propTypes: {
|
||||
notes: React.PropTypes.array.isRequired,
|
||||
notes: React.PropTypes.array,
|
||||
channels: React.PropTypes.array,
|
||||
|
||||
time: React.PropTypes.number,
|
||||
length: React.PropTypes.number,
|
||||
|
@ -50,7 +53,7 @@
|
|||
getDefaultProps: function () {
|
||||
return {
|
||||
notes: [],
|
||||
|
||||
channels: [],
|
||||
playing: false,
|
||||
time: 0,
|
||||
length: 0,
|
||||
|
@ -151,6 +154,7 @@
|
|||
className: 'score',
|
||||
|
||||
notes: this.props.notes,
|
||||
channels: this.props.channels,
|
||||
time: this.props.time
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/*globals React, Kinetic, App */
|
||||
/*jshint browser:true */
|
||||
/*globals React, App */
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
@ -6,8 +7,9 @@
|
|||
/**
|
||||
* Display a set of notes
|
||||
*
|
||||
* @prop {notes: array} List of notes
|
||||
* @prop {time: int} Current playing time
|
||||
* @prop {notes: array} List of notes
|
||||
* @prop {channels: array} List of configured channels
|
||||
* @prop {time: int} Current playing time
|
||||
*/
|
||||
App.components.create('Note', {
|
||||
displayName: 'Score',
|
||||
|
@ -35,12 +37,12 @@
|
|||
|
||||
propTypes: {
|
||||
notes: React.PropTypes.array.isRequired,
|
||||
channels: React.PropTypes.array.isRequired,
|
||||
time: React.PropTypes.number
|
||||
},
|
||||
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
notes: [],
|
||||
time: 0
|
||||
};
|
||||
},
|
||||
|
@ -170,7 +172,8 @@
|
|||
xUnit = width / 52, yUnit = xUnit * 5,
|
||||
maxTime = Math.ceil(height / yUnit),
|
||||
note, length, i, ctx, offset, x, y, noteWidth, noteHeight,
|
||||
channels = App.components.Note.Score.channels, count = 0;
|
||||
colors = App.components.Note.Score.channels, count = 0,
|
||||
channels = this.props.channels;
|
||||
|
||||
ctx = this.getDOMNode().getContext('2d');
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
@ -183,7 +186,8 @@
|
|||
note = notes[i];
|
||||
|
||||
if (note.start + note.length > time &&
|
||||
note.start < time + maxTime) {
|
||||
note.start < time + maxTime &&
|
||||
channels[note.channel].notes) {
|
||||
offset = this.getOffset(note.note);
|
||||
count += 1;
|
||||
|
||||
|
@ -201,7 +205,7 @@
|
|||
noteWidth = Math.floor(xUnit / 2);
|
||||
}
|
||||
|
||||
ctx.fillStyle = channels[note.channel];
|
||||
ctx.fillStyle = colors[note.channel];
|
||||
ctx.strokeRect(x, y, noteWidth, noteHeight);
|
||||
ctx.fillRect(x, y, noteWidth, noteHeight);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*globals React, App */
|
||||
/*globals jQuery, React, App */
|
||||
|
||||
(function () {
|
||||
(function ($) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
|
@ -82,10 +82,15 @@
|
|||
* Render handle
|
||||
*/
|
||||
render: function () {
|
||||
return React.DOM.button({
|
||||
className: 'selector',
|
||||
onClick: this.click
|
||||
}, this.state.value);
|
||||
var props = $.extend({}, this.props);
|
||||
|
||||
delete props.value;
|
||||
delete props.index;
|
||||
props.onClick = this.click;
|
||||
props.className = 'selector ' +
|
||||
((props.className) ? props.className : '');
|
||||
|
||||
return React.DOM.button(props, this.state.value);
|
||||
}
|
||||
});
|
||||
}());
|
||||
}(jQuery));
|
|
@ -28,15 +28,13 @@
|
|||
*
|
||||
* Represent a channel object
|
||||
*/
|
||||
function Channel(options) {
|
||||
options = options || {};
|
||||
|
||||
this.id = options.id || 0;
|
||||
this.meta = options.meta || {};
|
||||
this.program = options.program || 0;
|
||||
this.muted = options.muted || false;
|
||||
this.hidden = options.solo || false;
|
||||
this.pending = options.pending || {};
|
||||
function Channel(id) {
|
||||
this.id = id;
|
||||
this.meta = {};
|
||||
this.program = 0;
|
||||
this.sound = true;
|
||||
this.notes = true;
|
||||
this.pending = {};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,14 +67,13 @@
|
|||
*/
|
||||
function pushNote(meta, event, time) {
|
||||
var channel = event.channel,
|
||||
note = event.noteNumber,
|
||||
data = {
|
||||
start: time,
|
||||
channel: channel,
|
||||
note: note
|
||||
};
|
||||
note = event.noteNumber;
|
||||
|
||||
meta.channels[channel].pending[note] = data;
|
||||
meta.channels[channel].pending[note] = new Note({
|
||||
start: time,
|
||||
channel: channel,
|
||||
note: note
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,7 +149,7 @@
|
|||
function parseData(data) {
|
||||
return new window.Promise(function (resolve, reject) {
|
||||
var tracks, events, tracksLength, eventsLength, length,
|
||||
event, helpLink, i, j,
|
||||
event, i, j,
|
||||
// parsing data
|
||||
timeline = [], meta = {},
|
||||
channelPrefix = null, metaObject,
|
||||
|
@ -166,14 +163,11 @@
|
|||
try {
|
||||
data = new MidiFile(data);
|
||||
} catch (err) {
|
||||
helpLink = 'http://fr.wikipedia.org/wiki/Fichier_midi';
|
||||
|
||||
if (err === 'Bad .mid file - header not found') {
|
||||
reject(new Error(
|
||||
'Le fichier n\'est pas un fichier MIDI ' +
|
||||
'valide. Ce logiciel ne prend en charge ' +
|
||||
'que les fichiers de type ' +
|
||||
'<a href="' + helpLink + '">MIDI</a>.'
|
||||
'que les fichiers de type MIDI.'
|
||||
));
|
||||
} else {
|
||||
reject(new Error(
|
||||
|
@ -206,9 +200,7 @@
|
|||
length = 16;
|
||||
|
||||
for (i = 0; i < length; i += 1) {
|
||||
meta.channels[i] = new Channel({
|
||||
id: i
|
||||
});
|
||||
meta.channels[i] = new Channel(i);
|
||||
}
|
||||
|
||||
// parse all events
|
||||
|
|
Loading…
Reference in New Issue