Added the message pinao system reusing a react context for simplicity and emitting note timing messages when scoro gives the result

This commit is contained in:
Clément Le Bihan
2023-09-13 16:26:04 +02:00
parent 607c35b621
commit cea6d8d0bc
6 changed files with 147 additions and 56 deletions
+2 -9
View File
@@ -1,7 +1,7 @@
import * as React from 'react';
import PartitionView from './PartitionView';
import PhaserCanvas from './PartitionVisualizer/PhaserCanvas';
import { PianoCursorPosition } from './PartitionVisualizer/PhaserCanvas';
import { PianoCursorPosition } from '../models/PianoGame';
type PartitionCoordProps = {
// The Buffer of the MusicXML file retreived from the API
@@ -10,9 +10,6 @@ type PartitionCoordProps = {
onEndReached: () => void;
onResume: () => void;
onPause: () => void;
// Timestamp of the play session, in milisecond
timestamp: number;
pressedKeys: Map<number, number>;
};
const PartitionCoord = ({
@@ -21,8 +18,6 @@ const PartitionCoord = ({
onEndReached,
onPause,
onResume,
timestamp,
pressedKeys,
}: PartitionCoordProps) => {
const [partitionData, setPartitionData] = React.useState<
[string, PianoCursorPosition[]] | null
@@ -40,17 +35,15 @@ const PartitionCoord = ({
onEndReached={() => {
console.log('osmd end reached');
}}
timestamp={timestamp}
timestamp={0}
/>
)}
{partitionData && (
<PhaserCanvas
partitionB64={partitionData?.[0]}
cursorPositions={partitionData?.[1]}
timestamp={timestamp}
onPause={onPause}
onResume={onResume}
pressedKeys={pressedKeys}
onEndReached={() => {
onEndReached();
}}
+1 -2
View File
@@ -10,8 +10,7 @@ import {
Note,
} from 'opensheetmusicdisplay';
import useColorScheme from '../hooks/colorScheme';
import { PianoCursorPosition } from './PartitionVisualizer/PhaserCanvas';
import { PianoCursorPosition } from '../models/PianoGame';
type PartitionViewProps = {
// The Buffer of the MusicXML file retreived from the API
file: string;
@@ -1,17 +1,21 @@
// create a simple phaser effect with a canvas that can be easily imported as a react component
import * as React from 'react';
import { useEffect } from 'react';
import { useEffect, useContext } from 'react';
import Phaser from 'phaser';
import useColorScheme from '../../hooks/colorScheme';
import { RootState, useSelector } from '../../state/Store';
import { setSoundPlayer as setSPStore } from '../../state/SoundPlayerSlice';
import { useDispatch } from 'react-redux';
import { SplendidGrandPiano, CacheStorage } from 'smplr';
import { Note } from 'opensheetmusicdisplay';
import { handlePianoGameMsg } from './PianoGameUpdateFunctions';
import { PianoCC } from '../../views/PlayView';
import { PianoCanvasMsg, PianoCursorNote, PianoCursorPosition } from '../../models/PianoGame';
let globalTimestamp = 0;
let globalPressedKeys: Map<number, number> = new Map();
// the messages are consummed from the end and new messages should be added at the end
let globalMessages: Array<PianoCanvasMsg> = [];
const globalStatus: 'playing' | 'paused' | 'stopped' = 'playing';
const isValidSoundPlayer = (soundPlayer: SplendidGrandPiano | undefined) => {
@@ -50,8 +54,7 @@ const getPianoScene = (
private partition!: Phaser.GameObjects.Image;
private cursor!: Phaser.GameObjects.Rectangle;
private emitter!: Phaser.GameObjects.Particles.ParticleEmitter;
private emitzone!: Phaser.GameObjects.Particles.Zones.EdgeZone;
private nbTextureTolad!: number;
private nbTextureToload!: number;
create() {
this.textures.addBase64(
'star',
@@ -59,12 +62,13 @@ const getPianoScene = (
);
this.textures.addBase64('partition', partitionB64);
this.cursorPositionsIdx = -1;
this.nbTextureTolad = 2;
// this is to prevent multiple initialisation of the scene
this.nbTextureToload = 2;
this.cameras.main.setBackgroundColor(colorScheme === 'light' ? '#FFFFFF' : '#000000');
this.textures.on('onload', () => {
this.nbTextureTolad--;
if (this.nbTextureTolad > 0) return;
this.nbTextureToload--;
if (this.nbTextureToload > 0) return;
this.partition = this.add.image(0, 0, 'partition').setOrigin(0, 0);
this.cameras.main.setBounds(0, 0, this.partition.width, this.partition.height);
@@ -103,13 +107,15 @@ const getPianoScene = (
return true;
}
if (globalPressedKeys.size > 0) {
// add particles at the position of the cursor
this.emitter.start(1);
this.cursor.fillAlpha = 0.9;
} else if (this.cursor) {
this.cursor.fillAlpha = 0.5;
}
if (globalMessages.length > 0) {
handlePianoGameMsg(globalMessages, this.emitter);
}
return false;
});
if (cP) {
@@ -134,50 +140,28 @@ const getPianoScene = (
return PianoScene;
};
type PianoCursorNote = {
note: Note;
duration: number;
};
export type PianoCursorPosition = {
// offset in pixels
x: number;
// timestamp in ms
timing: number;
timestamp: number;
notes: PianoCursorNote[];
};
export type UpdateInfo = {
currentTimestamp: number;
status: 'playing' | 'paused' | 'stopped';
};
export type PhaserCanvasProps = {
partitionB64: string;
cursorPositions: PianoCursorPosition[];
onEndReached: () => void;
onPause: () => void;
onResume: () => void;
// Timestamp of the play session, in milisecond
timestamp: number;
pressedKeys: Map<number, number>;
};
const PhaserCanvas = ({
partitionB64,
cursorPositions,
onEndReached,
timestamp,
pressedKeys,
}: PhaserCanvasProps) => {
const colorScheme = useColorScheme();
const dispatch = useDispatch();
const pianoCC = useContext(PianoCC);
const soundPlayer = useSelector((state: RootState) => state.soundPlayer.soundPlayer);
const [game, setGame] = React.useState<Phaser.Game | null>(null);
globalTimestamp = timestamp;
globalPressedKeys = pressedKeys;
globalTimestamp = pianoCC.timestamp;
globalPressedKeys = pianoCC.pressedKeys;
globalMessages = pianoCC.messages;
useEffect(() => {
if (isValidSoundPlayer(soundPlayer)) {
@@ -0,0 +1,34 @@
import { NoteTiming, PianoCanvasMsg } from '../../models/PianoGame';
const handleNoteTimingMsg = (
noteTiming: NoteTiming,
emitter: Phaser.GameObjects.Particles.ParticleEmitter
) => {
if (noteTiming === NoteTiming.Perfect) {
emitter.particleTint = 0x00ff00;
emitter.start(10);
} else if (noteTiming === NoteTiming.Great) {
emitter.particleTint = 0x00ffff;
emitter.start(5);
} else if (noteTiming === NoteTiming.Good) {
emitter.particleTint = 0xffff00;
emitter.start(3);
} else if (noteTiming === NoteTiming.Missed) {
emitter.particleTint = 0xff0000;
emitter.start(1);
} else if (noteTiming === NoteTiming.Wrong) {
// maybe add some other effect
}
};
export const handlePianoGameMsg = (
msgs: Array<PianoCanvasMsg>,
emitter: Phaser.GameObjects.Particles.ParticleEmitter
) => {
const msg = msgs.shift();
if (msg) {
if (msg.type === 'noteTiming') {
handleNoteTimingMsg(msg.data as NoteTiming, emitter);
}
}
};