Skip to content

Commit

Permalink
midi player v1
Browse files Browse the repository at this point in the history
  • Loading branch information
felix-commits committed Apr 21, 2024
1 parent 8151fbf commit c624ec1
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 0 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
"@mui/material": "^5.15.4",
"@reduxjs/toolkit": "^1.9.7",
"@vitejs/plugin-react-swc": "^3.5.0",
"midi-player-js": "^2.0.16",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.1.3",
"react-router-dom": "6.21.1",
"react-window": "^1.8.10",
"smplr": "^0.13.3",
"verovio": "^4.1.0",
"vite": "^5.0.11",
"vite-plugin-svgr": "^4.2.0"
Expand Down
2 changes: 2 additions & 0 deletions src/features/score/MeiViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { StyleNote } from './StyleNote'
import { useTheme } from '@mui/material/styles'
import { Loader } from '../../components/Loader'
import { getId } from '../../utils'
import { Player } from './Player'

export const MeiViewer = ({ file }) => {
const theme = useTheme()
Expand Down Expand Up @@ -260,6 +261,7 @@ export const MeiViewer = ({ file }) => {
</Stack>
</Alert>
</Snackbar>
<Player {...{ pageCount }} />
</Stack>
</Stack>
)
Expand Down
81 changes: 81 additions & 0 deletions src/features/score/Player.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Alert, IconButton, Slider, Snackbar, Stack, Typography } from '@mui/material'
import { useEffect, useRef, useState } from 'react'
import { Pause, PlayArrow } from '@mui/icons-material'
import { SplendidGrandPiano } from 'smplr'
import MidiPlayer from 'midi-player-js'

const base64ToArrayBuffer = base64 => Uint8Array.from(atob(base64), c => c.charCodeAt(0))
const formatTime = time => new Date(1000 * time).toTimeString().slice(4, 8)

export const Player = ({ pageCount }) => {
const [time, setTime] = useState(0)
const [isOpen, setIsOpen] = useState(false)
const [isPlaying, setIsPlaying] = useState(false)
const context = new AudioContext()
const pianoApi = useRef(null)
const [midiApi] = useState(
() =>
new MidiPlayer.Player(e => {
if (e.name.includes('Note on'))
pianoApi.current.start({ note: e.noteName, velocity: e.velocity, time: 5, duration: 1 })
setTime(100 - midiApi.getSongPercentRemaining())
setIsPlaying(midiApi.isPlaying())
})
)

const setTimePosition = (e, time) => {
setTime(time)
setIsPlaying(midiApi.isPlaying())
midiApi.skipToPercent(time)
}

const loadPiano = async () => {
pianoApi.current = await new SplendidGrandPiano(context).load
setIsOpen(true)
}

useEffect(() => {
if (pageCount) {
const base64 = window.tk.renderToMIDI()
const buffer = base64ToArrayBuffer(base64)
midiApi.loadArrayBuffer(buffer)
}
}, [pageCount])

useEffect(() => {
if (!pianoApi.current) loadPiano()
}, [])

if (midiApi)
return (
<Snackbar open={isOpen} anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}>
<Alert
sx={{
borderRadius: 3,
boxShadow: 1,
bgcolor: 'secondary.light',
'& .MuiAlert-icon': { display: 'none' },
}}
>
<Stack width={300} spacing={2} direction="row" alignItems="center">
<IconButton
onClick={() => {
try {
setIsPlaying(!isPlaying)
context.resume()
midiApi.play()
} catch (error) {
midiApi.pause()
}
}}
>
{isPlaying ? <Pause /> : <PlayArrow />}
</IconButton>
<Typography>{formatTime(midiApi.getSongTime() - midiApi.getSongTimeRemaining())}</Typography>
<Slider value={time} onChange={setTimePosition} />
<Typography>-{formatTime(midiApi.getSongTimeRemaining())}</Typography>
</Stack>
</Alert>
</Snackbar>
)
}
16 changes: 16 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2458,6 +2458,13 @@ __metadata:
languageName: node
linkType: hard

"midi-player-js@npm:^2.0.16":
version: 2.0.16
resolution: "midi-player-js@npm:2.0.16"
checksum: 10/eeee55a1fe1cc9b35415b6209d595e02f9cf3c86017c8d752af081511f461b1d5f174ddd2e693e70fbba0edd1056475f3d31e916eda31cb1067aa87cdbee3390
languageName: node
linkType: hard

"minimatch@npm:^3.1.1":
version: 3.1.2
resolution: "minimatch@npm:3.1.2"
Expand Down Expand Up @@ -3151,6 +3158,13 @@ __metadata:
languageName: node
linkType: hard

"smplr@npm:^0.13.3":
version: 0.13.3
resolution: "smplr@npm:0.13.3"
checksum: 10/a6434f1b723fd144a63c4bdf8e05c4d169025b02c7613f4d3e47dfc9f1b8e9f29b3eac80b53f284d017ce9f684d0173b7a34da9cd409fc7b0926d0fb1577c846
languageName: node
linkType: hard

"snake-case@npm:^3.0.4":
version: 3.0.4
resolution: "snake-case@npm:3.0.4"
Expand Down Expand Up @@ -3316,11 +3330,13 @@ __metadata:
"@mui/material": "npm:^5.15.4"
"@reduxjs/toolkit": "npm:^1.9.7"
"@vitejs/plugin-react-swc": "npm:^3.5.0"
midi-player-js: "npm:^2.0.16"
react: "npm:^18.2.0"
react-dom: "npm:^18.2.0"
react-redux: "npm:^8.1.3"
react-router-dom: "npm:6.21.1"
react-window: "npm:^1.8.10"
smplr: "npm:^0.13.3"
verovio: "npm:^4.1.0"
vite: "npm:^5.0.11"
vite-plugin-svgr: "npm:^4.2.0"
Expand Down

0 comments on commit c624ec1

Please sign in to comment.