webmscore
Version:
MuseScore's libmscore in WebAssembly! Read mscz data, and generate audio/MIDI/MusicXML/SVG/PNG/PDF sheets right in browsers
91 lines (78 loc) • 2.32 kB
JavaScript
// The main entry point inside a Web Worker
import WebMscore from './index.js'
/** @type {WebMscore} */
let score
/**
* @typedef {{ id: number; method: Exclude<keyof import('./index').default, 'scoreptr' | 'excerptId'> | 'load' | 'ready' | 'setLogLevel'; params: any[]; }} RPCReq
* @typedef {{ id: number; result?: any; error?: any; }} RPCRes
* @param {number} id
* @param {any} result
* @param {Transferable[]} transfer
*/
const rpcRes = (id, result, transfer = undefined) => {
/** @type {RPCRes} */
const res = {
id,
result,
}
self.postMessage(res, transfer)
}
/**
* @param {number} id
* @param {Error} err
*/
const rpcErr = (id, err) => {
/** @type {RPCRes} */
const res = {
id,
error: {
name: err.name,
message: err.message,
stack: err.stack,
},
}
self.postMessage(res)
}
/**
* @typedef {import('../schemas').SynthRes | Uint8Array | undefined} Res
* @param {Res | Res[]} obj
* @returns {Transferable[] | undefined}
*/
const getTransferable = (obj) => {
if (!obj) return
if (Array.isArray(obj)) {
return obj.reduce((p, c) => p.concat(getTransferable(c)), []).filter(Boolean)
} else if (obj instanceof Uint8Array) {
return [obj.buffer]
} else if (obj.chunk instanceof Uint8Array) {
return [obj.chunk.buffer]
}
}
self.onmessage = async (e) => {
/** @type {RPCReq} */
const req = e.data // JSON-RPC
const { id, method, params } = req
try {
switch (method) {
case 'ready':
await WebMscore.ready
rpcRes(id, 'done')
break
case 'load':
await WebMscore.ready
score = await WebMscore.load.apply(undefined, params)
rpcRes(id, 'done')
break;
case 'setLogLevel':
await WebMscore.setLogLevel.apply(undefined, params)
rpcRes(id, 'done')
break
default:
if (!score) { rpcErr(id, new Error('Score not loaded')) }
const result = await score[method].apply(score, params)
rpcRes(id, result, getTransferable(result))
}
} catch (err) {
rpcErr(id, err)
}
}