Skip to content

Commit

Permalink
make midi usage optional and fix some midi detection issues
Browse files Browse the repository at this point in the history
  • Loading branch information
philippotto committed Oct 29, 2017
1 parent c7ee7f2 commit e275160
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 43 deletions.
1 change: 1 addition & 0 deletions app/scripts/AppFreezer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ let defaultSettings = {
},
keySignature: [7, 7],
useAccidentals: false,
tryToUseMidi: true,
midi: {
inputs: Freezer.createLeaf([]),
activeInputIndex: 0,
Expand Down
5 changes: 1 addition & 4 deletions app/scripts/services/bar_generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,7 @@ export default {
return ["treble", "bass"].map(clef => _.random.apply(_, settings.chordSizeRanges[clef]));
},

generateBars: function(settings, level) {
const isMidiAvailable = settings.midi.inputs.get().length > 0;
const onePerTime = !isMidiAvailable;

generateBars: function(settings, level, onePerTime) {
const [trebleNotes, bassNotes] = _.unzip(
_.range(0, options.chordsPerBar).map(() => {
const generatePossibleNotes = clef => {
Expand Down
10 changes: 8 additions & 2 deletions app/scripts/views/claviature_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,15 @@ export default class ClaviatureView extends Component {
["a#", "A# B♭", "black"],
["b", "B", "white"],
].map(args => this.renderKey.apply(this, args));

return (
<div className={classNames({ "scale noSelect": true, noPointerEvents: this.props.disabled })}>
<ol>{keys}</ol>
<div>
<div
className={classNames({ "scale noSelect": true, noPointerEvents: this.props.disabled })}
>
<ol>{keys}</ol>
</div>
Click on the keys of the claviature or use your keyboard to hit notes!
</div>
);
}
Expand Down
2 changes: 1 addition & 1 deletion app/scripts/views/collapsable_container.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default class CollapsableContainer extends Component {
}),
]).join(" ");

const maxHeight = this.props.maxHeight || 300;
const maxHeight = this.props.maxHeight || 500;
const style = collapsed ? {} : { maxHeight };
const children =
collapsed && this.props.freeze && this.oldChildren ? this.oldChildren : this.props.children;
Expand Down
58 changes: 41 additions & 17 deletions app/scripts/views/pitch_reading_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export default class PitchReadingView extends Component {
this.midiService = new MidiService({
successCallback: this.onSuccess.bind(this),
failureCallback: this.onFailure.bind(this),
errorCallback: this.onError.bind(this),
errorResolveCallback: this.onErrorResolve.bind(this),
errorCallback: this.onMidiError.bind(this),
errorResolveCallback: this.onMidiErrorResolve.bind(this),
});
this.startDate = new Date();
this.midiService.setDesiredKeys(this.getAllCurrentKeys(), this.state.currentKeySignature);
Expand Down Expand Up @@ -106,7 +106,15 @@ export default class PitchReadingView extends Component {
generateNewBars(settings) {
const levelIndex = LevelService.getLevelOfUser(this.props.statisticService.getAllEvents()) + 1;
const level = LevelService.getLevelByIndex(levelIndex);
return BarGenerator.generateBars(settings, settings.useAutomaticDifficulty ? level : null);

const { isMidiAvailable } = this.getMidiInfo();
const onePerTime = !isMidiAvailable;

return BarGenerator.generateBars(
settings,
settings.useAutomaticDifficulty ? level : null,
onePerTime,
);
}

generateNewBarState() {
Expand All @@ -120,7 +128,7 @@ export default class PitchReadingView extends Component {
constructor(props, context) {
super(props, context);
this.state = {
errorMessage: null,
midiErrorMessage: null,
running: false,
...this.generateNewBarState(),
};
Expand All @@ -132,16 +140,30 @@ export default class PitchReadingView extends Component {
this.startDate = new Date();
}

getMidiInfo() {
const tryToUseMidi = this.props.settings.tryToUseMidi;
const isMidiAvailable = this.props.settings.midi.inputs.get().length > 0;
const useMidi = tryToUseMidi && isMidiAvailable;
const noMidiErrors = !this.state ? true : this.state.midiErrorMessage == null;

return {
tryToUseMidi,
isMidiAvailable,
useMidi,
noMidiErrors,
};
}

render() {
const claviatureContainerClasses = classNames({
"content-box": true,
"claviature-container": true,
});

const isMidiAvailable = this.props.settings.midi.inputs.get().length > 0;
const noErrors = this.state.errorMessage !== null;
const { tryToUseMidi, isMidiAvailable, useMidi, noMidiErrors } = this.getMidiInfo();

const miniClaviature =
isMidiAvailable && noErrors ? null : (
useMidi && noMidiErrors ? null : (
<ClaviatureView
desiredKeys={this.getAllCurrentKeys()}
keySignature={this.state.currentKeySignature}
Expand Down Expand Up @@ -176,15 +198,15 @@ export default class PitchReadingView extends Component {
welcomeText: true,
})}
>
<h3>Welcome to this pitch training!</h3>
<h3>Welcome to pitch training!</h3>
<p>
{"When you hit Start, notes will be displayed in the stave above. "}
{isMidiAvailable
{useMidi
? "Since we found a connected piano, you can use it to play the notes. "
: "Just use the mini claviature below to play the notes. "}
{"Don't worry about the rhythm or speed for now."}
</p>
{isMidiAvailable ? null : midiSetUpText}
{tryToUseMidi && !isMidiAvailable ? midiSetUpText : null}
</div>
</CollapsableContainer>
);
Expand All @@ -194,6 +216,8 @@ export default class PitchReadingView extends Component {
bass: [],
};

const hideMidiError = !tryToUseMidi || noMidiErrors;

return (
<div className={classNames({ trainer: true, trainerHidden1: !this.props.isActive })}>
<div className="row center-lg center-md center-sm center-xs">
Expand All @@ -217,16 +241,16 @@ export default class PitchReadingView extends Component {
</div>
</div>
</div>
<CollapsableContainer collapsed={!miniClaviature}>
<CollapsableContainer collapsed={!miniClaviature && hideMidiError}>
<div className={claviatureContainerClasses}>
{miniClaviature}
<div
className={classNames({
message: true,
hide: this.state.errorMessage === null,
hide: hideMidiError,
})}
>
<h3>{this.state.errorMessage}</h3>
<h3>{this.state.midiErrorMessage}</h3>
</div>
</div>
</CollapsableContainer>
Expand Down Expand Up @@ -257,13 +281,13 @@ export default class PitchReadingView extends Component {
this.midiService.setDesiredKeys(this.getAllCurrentKeys(), this.state.currentKeySignature);
}

onError(msg) {
onMidiError(msg) {
console.error.apply(console, arguments);
this.setState({ errorMessage: msg });
this.setState({ midiErrorMessage: msg });
}

onErrorResolve() {
this.setState({ errorMessage: null });
onMidiErrorResolve() {
this.setState({ midiErrorMessage: null });
}

getAllCurrentKeys() {
Expand Down
50 changes: 32 additions & 18 deletions app/scripts/views/pitch_settings_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,25 +57,39 @@ export default class PitchSettingsView extends Component {
const midiSettings = this.props.settings.midi;
const midiInputs = midiSettings.inputs.get();
const isMidiAvailable = midiInputs.length > 0;
const tryToUseMidi = this.props.settings.tryToUseMidi;
const deviceSelector = !isMidiAvailable ? null : (
<SettingLine label="Midi device">
<select
name="select"
onChange={this.onMidiSelectChange.bind(this)}
defaultValue={midiSettings.currentInput}
ref={c => {
this.midiSelect = c;
}}
>
{midiInputs.map((el, index) => {
return (
<option value={index} key={index}>
Device {index + 1}
</option>
);
})}
</select>
</SettingLine>
<div>
<SettingLine className="setting_checkbox" label="Use Midi:">
<input
type="checkbox"
checked={tryToUseMidi}
id="try_to_use_midi_checkbox"
name="check"
onChange={this.buildCheckboxStateChanger("tryToUseMidi")}
/>
<label htmlFor="try_to_use_midi_checkbox" />
</SettingLine>
<SettingLine label="Midi device">
<select
name="select"
onChange={this.onMidiSelectChange.bind(this)}
defaultValue={midiSettings.currentInput}
ref={c => {
this.midiSelect = c;
}}
disabled={!tryToUseMidi}
>
{midiInputs.map((el, index) => {
return (
<option value={index} key={index}>
Device {index + 1}
</option>
);
})}
</select>
</SettingLine>
</div>
);

const useAutomaticDifficulty = this.props.settings.useAutomaticDifficulty;
Expand Down
2 changes: 1 addition & 1 deletion app/scripts/views/rhythm_reading_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ export default class RhythmReadingView extends Component {
render() {
const welcomeText = (
<CollapsableContainer collapsed={this.state.phase !== Phases.welcome}>
<h3>Welcome to this rhythm training!</h3>
<h3>Welcome to rhythm training!</h3>
<p>
When you start the training, we will count in for 4 beats and afterwards you can tap the
given rhythm (either use your &lsquo;space&rsquo; button or your touchscreen). Make sure
Expand Down

0 comments on commit e275160

Please sign in to comment.