Comparing version 1.1.0 to 1.2.0
@@ -13,2 +13,16 @@ # Changelog | ||
## 1.2.0 - 2023-01-23 | ||
### Added | ||
* Throw `WasmError` when webmscore encounters score processing error | ||
### Changed | ||
* The log level can now be set before `WebMscore.load(...)`, overrides the default behaviour which turns off logs in `WebMscore.load(...)`. | ||
### Fixed | ||
* For score files created in MuseScore versions older than v3.6.0, instruments no longer all sound like piano in the exported audio. | ||
## 1.1.0 - 2023-01-15 | ||
@@ -15,0 +29,0 @@ |
{ | ||
"name": "webmscore", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "MuseScore's libmscore in WebAssembly! Read mscz data, and generate audio/MIDI/MusicXML/SVG/PNG/PDF sheets right in browsers", | ||
@@ -55,4 +55,5 @@ "type": "module", | ||
"postcompile": "perl -pi -e 's/\\\\([0-9a-f]{2})/chr(hex($1))/eg' webmscore.lib.symbols", | ||
"clean": "npm run clean:d-ts && cd ../web/ && make clean", | ||
"clean": "npm run clean:d-ts && npm run clean:md && cd ../web/ && make clean", | ||
"clean:d-ts": "rm -f src/*.d.ts *.d.ts", | ||
"clean:md": "rm -f *.md", | ||
"d-ts": "tsc --emitDeclarationOnly --declaration --allowJs --lib esnext --target esnext src/index.js", | ||
@@ -59,0 +60,0 @@ "prepack": "cp ../README.md . && cp ../CHANGELOG.md .", |
export function getStrPtr(str: string): number; | ||
export function getTypedArrayPtr(data: TypedArray): number; | ||
export function readData(ptr: number): Uint8Array; | ||
export class WasmRes { | ||
/** | ||
* @private | ||
* @param {number} ptr | ||
* @returns {number} | ||
*/ | ||
private static _getUint32; | ||
/** | ||
* @private | ||
* @param {number} ptr | ||
* @param {keyof WasmRes} method | ||
*/ | ||
private static _readAndFree; | ||
/** | ||
* read wasm responses as Uint8Array | ||
* @param {number} ptr | ||
* @returns {Uint8Array} | ||
*/ | ||
static readData(ptr: number): Uint8Array; | ||
/** | ||
* read wasm responses as UTF-8 string | ||
* @param {number} ptr | ||
* @returns {string} | ||
*/ | ||
static readText(ptr: number): string; | ||
/** | ||
* read wasm responses as number | ||
* @param {number} ptr | ||
* @returns {number} | ||
*/ | ||
static readNum(ptr: number): number; | ||
/** | ||
* Read responses from the wasm module | ||
* @param {number} ptr char* pointer to the responses data | ||
*/ | ||
constructor(ptr: number); | ||
/** @type {number} */ | ||
_ptr: number; | ||
/** @type {number} */ | ||
_size: number; | ||
/** | ||
* pointer to the error code | ||
* @private | ||
*/ | ||
private get _retCodePtr(); | ||
/** | ||
* pointer to the data size | ||
* @private | ||
*/ | ||
private get _sizePtr(); | ||
/** | ||
* pointer to the data contents | ||
* @private | ||
*/ | ||
private get _dataPtr(); | ||
/** | ||
* @private | ||
* throw error if not ok | ||
*/ | ||
private _checkRet; | ||
/** | ||
* Read the data contents as Uint8Array | ||
* @returns {Uint8Array} | ||
*/ | ||
data(): Uint8Array; | ||
/** | ||
* Read the data contents as UTF-8 string | ||
* @returns {string} | ||
*/ | ||
text(): string; | ||
/** | ||
* Read the data contents as number | ||
* @returns {number} | ||
*/ | ||
number(): number; | ||
free(): void; | ||
} | ||
export function freePtr(bufPtr: number): void; | ||
@@ -9,30 +85,10 @@ /** | ||
export const RuntimeInitialized: Promise<any>; | ||
/** | ||
* * | ||
*/ | ||
export type FileErrorEnum = number; | ||
/** | ||
* @enum {number} | ||
* @see libmscore/score.h#L396-L410 | ||
*/ | ||
export const FileErrorEnum: string[] & { | ||
FILE_NO_ERROR: number; | ||
FILE_ERROR: number; | ||
FILE_NOT_FOUND: number; | ||
FILE_OPEN_ERROR: number; | ||
FILE_BAD_FORMAT: number; | ||
FILE_UNKNOWN_TYPE: number; | ||
FILE_NO_ROOTFILE: number; | ||
FILE_TOO_OLD: number; | ||
FILE_TOO_NEW: number; | ||
FILE_OLD_300_FORMAT: number; | ||
FILE_CORRUPTED: number; | ||
FILE_USER_ABORT: number; | ||
FILE_IGNORE_ERROR: number; | ||
}; | ||
export class FileError extends Error { | ||
export class WasmError extends Error { | ||
/** @type {0} */ | ||
static OK: 0; | ||
/** | ||
* @param {FileErrorEnum} errorCode | ||
* @param {number} errorCode | ||
* @param {string} msg | ||
*/ | ||
constructor(errorCode: FileErrorEnum); | ||
constructor(errorCode: number, msg: string); | ||
errorCode: number; | ||
@@ -39,0 +95,0 @@ errorName: string; |
@@ -58,20 +58,138 @@ | ||
/** | ||
* read length-prefixed data (char*) as Uint8Array | ||
* @param {number} ptr | ||
* @returns {Uint8Array} | ||
*/ | ||
export const readData = (ptr) => { | ||
const sizeData = new DataView( | ||
new Uint8Array( // make a copy | ||
Module.HEAPU8.subarray(ptr, ptr + 4) | ||
).buffer | ||
) | ||
export class WasmRes { | ||
/** @type {number} */ | ||
_ptr | ||
/** @type {number} */ | ||
_size | ||
const size = sizeData.getUint32(0, true) | ||
const data = new Uint8Array(Module.HEAPU8.subarray(ptr + 4, ptr + 4 + size)) // make a copy | ||
/** | ||
* Read responses from the wasm module | ||
* @param {number} ptr char* pointer to the responses data | ||
*/ | ||
constructor(ptr) { | ||
this._ptr = ptr | ||
this._size = WasmRes._getUint32(this._sizePtr) | ||
this._checkRet() | ||
} | ||
freePtr(ptr) | ||
/** | ||
* pointer to the error code | ||
* @private | ||
*/ | ||
get _retCodePtr() { | ||
return this._ptr | ||
} | ||
return data | ||
/** | ||
* pointer to the data size | ||
* @private | ||
*/ | ||
get _sizePtr() { | ||
return this._retCodePtr + 4 | ||
} | ||
/** | ||
* pointer to the data contents | ||
* @private | ||
*/ | ||
get _dataPtr() { | ||
return this._sizePtr + 4 | ||
} | ||
/** | ||
* @private | ||
* throw error if not ok | ||
*/ | ||
_checkRet() { | ||
const retCode = WasmRes._getUint32(this._retCodePtr) | ||
if (retCode !== WasmError.OK) { | ||
// read the error message from data | ||
const retMsg = this.text() | ||
this.free() | ||
throw new WasmError(retCode, retMsg) | ||
} | ||
} | ||
/** | ||
* Read the data contents as Uint8Array | ||
* @returns {Uint8Array} | ||
*/ | ||
data() { | ||
return new Uint8Array( // make a copy | ||
Module.HEAPU8.subarray(this._dataPtr, this._dataPtr + this._size) | ||
) | ||
} | ||
/** | ||
* Read the data contents as UTF-8 string | ||
* @returns {string} | ||
*/ | ||
text() { | ||
return Module.UTF8ToString(this._dataPtr) | ||
} | ||
/** | ||
* Read the data contents as number | ||
* @returns {number} | ||
*/ | ||
number() { | ||
return WasmRes._getUint32(this._dataPtr) | ||
} | ||
free() { | ||
return freePtr(this._ptr) | ||
} | ||
/** | ||
* @private | ||
* @param {number} ptr | ||
* @returns {number} | ||
*/ | ||
static _getUint32(ptr) { | ||
const sizeData = new DataView( | ||
new Uint8Array( // make a copy | ||
Module.HEAPU8.subarray(ptr, ptr + 4) | ||
).buffer | ||
) | ||
return sizeData.getUint32(0, true) | ||
} | ||
/** | ||
* @private | ||
* @param {number} ptr | ||
* @param {keyof WasmRes} method | ||
*/ | ||
static _readAndFree(ptr, method) { | ||
const res = new WasmRes(ptr) | ||
const s = res[method]() | ||
res.free() | ||
return s | ||
} | ||
/** | ||
* read wasm responses as Uint8Array | ||
* @param {number} ptr | ||
* @returns {Uint8Array} | ||
*/ | ||
static readData(ptr) { | ||
return WasmRes._readAndFree(ptr, 'data') | ||
} | ||
/** | ||
* read wasm responses as UTF-8 string | ||
* @param {number} ptr | ||
* @returns {string} | ||
*/ | ||
static readText(ptr) { | ||
return WasmRes._readAndFree(ptr, 'text') | ||
} | ||
/** | ||
* read wasm responses as number | ||
* @param {number} ptr | ||
* @returns {number} | ||
*/ | ||
static readNum(ptr) { | ||
return WasmRes._readAndFree(ptr, 'number') | ||
} | ||
} | ||
@@ -94,54 +212,21 @@ | ||
Module.ccall('init') // init libmscore | ||
resolve() | ||
resolve(undefined) | ||
}) | ||
}) | ||
/** | ||
* @enum {number} | ||
* @see libmscore/score.h#L396-L410 | ||
*/ | ||
export const FileErrorEnum = Object.assign([ | ||
// error code -> error name | ||
'FILE_NO_ERROR', | ||
'FILE_ERROR', | ||
'FILE_NOT_FOUND', | ||
'FILE_OPEN_ERROR', | ||
'FILE_BAD_FORMAT', | ||
'FILE_UNKNOWN_TYPE', | ||
'FILE_NO_ROOTFILE', | ||
'FILE_TOO_OLD', | ||
'FILE_TOO_NEW', | ||
'FILE_OLD_300_FORMAT', | ||
'FILE_CORRUPTED', | ||
'FILE_USER_ABORT', | ||
'FILE_IGNORE_ERROR', | ||
], { | ||
// error name -> error code | ||
// make up TypeScript-like enum manually | ||
'FILE_NO_ERROR': 0, | ||
'FILE_ERROR': 1, | ||
'FILE_NOT_FOUND': 2, | ||
'FILE_OPEN_ERROR': 3, | ||
'FILE_BAD_FORMAT': 4, | ||
'FILE_UNKNOWN_TYPE': 5, | ||
'FILE_NO_ROOTFILE': 6, | ||
'FILE_TOO_OLD': 7, | ||
'FILE_TOO_NEW': 8, | ||
'FILE_OLD_300_FORMAT': 9, | ||
'FILE_CORRUPTED': 10, | ||
'FILE_USER_ABORT': 11, | ||
'FILE_IGNORE_ERROR': 12, | ||
}) | ||
export class FileError extends Error { | ||
export class WasmError extends Error { | ||
/** | ||
* @param {FileErrorEnum} errorCode | ||
* @param {number} errorCode | ||
* @param {string} msg | ||
*/ | ||
constructor(errorCode) { | ||
constructor(errorCode, msg) { | ||
super() | ||
this.name = 'FileError' | ||
this.name = 'WasmError' | ||
this.errorCode = errorCode | ||
this.errorName = FileErrorEnum[errorCode] || FileErrorEnum[1] | ||
this.message = `WebMscore: ${this.errorName}` | ||
this.errorName = msg | ||
this.message = `WebMscore Err${this.errorName}` | ||
} | ||
/** @type {0} */ | ||
static OK = 0 | ||
} |
@@ -239,3 +239,3 @@ export default WebMscore; | ||
/** | ||
* Export score metadata as JSON | ||
* Export score metadata as JSON text | ||
* @also `score.metadata()` | ||
@@ -242,0 +242,0 @@ * @returns {Promise<string>} contents of the JSON file |
@@ -9,5 +9,4 @@ | ||
getTypedArrayPtr, | ||
readData, | ||
WasmRes, | ||
freePtr, | ||
FileError, | ||
} from './helper.js' | ||
@@ -18,2 +17,7 @@ | ||
let _hasSoundfont = false | ||
/** | ||
* Don't turn off logs if already set log level before `WebMscore.load(...)` is called | ||
* @see WebMscore.setLogLevel | ||
*/ | ||
let _hasLogLevelSet = false | ||
@@ -47,2 +51,3 @@ class WebMscore { | ||
static async setLogLevel(level) { | ||
_hasLogLevelSet = true | ||
await WebMscore.ready | ||
@@ -109,3 +114,3 @@ return Module.ccall('setLogLevel', null, ['number'], [level]) | ||
// get the pointer to the MasterScore class instance in C | ||
const scoreptr = Module.ccall('load', // name of C function | ||
const resptr = Module.ccall('load', // name of C function | ||
'number', // return type | ||
@@ -115,14 +120,11 @@ ['number', 'number', 'number', 'boolean'], // argument types | ||
) | ||
freePtr(fileformatptr) | ||
freePtr(dataptr) | ||
const scoreptr = WasmRes.readNum(resptr) | ||
if (scoreptr < 16) { // contains error | ||
// `scoreptr` is the error code | ||
throw new FileError(scoreptr) | ||
if (!_hasLogLevelSet) { | ||
// turn off logs by default | ||
await WebMscore.setLogLevel(0); | ||
} | ||
// turn off logs by default | ||
await WebMscore.setLogLevel(0); | ||
const mscore = new WebMscore(scoreptr) | ||
@@ -228,6 +230,4 @@ return mscore | ||
async title() { | ||
const strptr = Module.ccall('title', 'number', ['number'], [this.scoreptr]) | ||
const str = Module.UTF8ToString(strptr) | ||
freePtr(strptr) | ||
return str | ||
const dataptr = Module.ccall('title', 'number', ['number'], [this.scoreptr]) | ||
return WasmRes.readText(dataptr) | ||
} | ||
@@ -248,3 +248,4 @@ | ||
async npages() { | ||
return Module.ccall('npages', 'number', ['number', 'number'], [this.scoreptr, this.excerptId]) | ||
const dataptr = Module.ccall('npages', 'number', ['number', 'number'], [this.scoreptr, this.excerptId]) | ||
return WasmRes.readNum(dataptr) | ||
} | ||
@@ -282,8 +283,3 @@ | ||
const dataptr = Module.ccall('saveXml', 'number', ['number', 'number'], [this.scoreptr, this.excerptId]) | ||
// MusicXML is plain text | ||
const data = Module.UTF8ToString(dataptr) | ||
freePtr(dataptr) | ||
return data | ||
return WasmRes.readText(dataptr) | ||
} | ||
@@ -297,3 +293,3 @@ | ||
const dataptr = Module.ccall('saveMxl', 'number', ['number', 'number'], [this.scoreptr, this.excerptId]) | ||
return readData(dataptr) | ||
return WasmRes.readData(dataptr) | ||
} | ||
@@ -308,3 +304,3 @@ | ||
const dataptr = Module.ccall('saveMsc', 'number', ['number', 'boolean', 'number'], [this.scoreptr, format == 'mscz', this.excerptId]) | ||
return readData(dataptr) | ||
return WasmRes.readData(dataptr) | ||
} | ||
@@ -324,8 +320,3 @@ | ||
) | ||
// SVG is plain text | ||
const data = Module.UTF8ToString(dataptr) | ||
freePtr(dataptr) | ||
return data | ||
return WasmRes.readText(dataptr) | ||
} | ||
@@ -346,3 +337,3 @@ | ||
) | ||
return readData(dataptr) | ||
return WasmRes.readData(dataptr) | ||
} | ||
@@ -356,3 +347,3 @@ | ||
const dataptr = Module.ccall('savePdf', 'number', ['number', 'number'], [this.scoreptr, this.excerptId]) | ||
return readData(dataptr) | ||
return WasmRes.readData(dataptr) | ||
} | ||
@@ -372,3 +363,3 @@ | ||
) | ||
return readData(dataptr) | ||
return WasmRes.readData(dataptr) | ||
} | ||
@@ -400,3 +391,3 @@ | ||
freePtr(fileformatptr) | ||
return readData(dataptr) | ||
return WasmRes.readData(dataptr) | ||
} | ||
@@ -539,12 +530,7 @@ | ||
) | ||
// JSON is plain text | ||
const data = Module.UTF8ToString(dataptr) | ||
freePtr(dataptr) | ||
return data | ||
return WasmRes.readText(dataptr) | ||
} | ||
/** | ||
* Export score metadata as JSON | ||
* Export score metadata as JSON text | ||
* @also `score.metadata()` | ||
@@ -555,8 +541,3 @@ * @returns {Promise<string>} contents of the JSON file | ||
const dataptr = Module.ccall('saveMetadata', 'number', ['number'], [this.scoreptr]) | ||
// JSON is plain text | ||
const data = Module.UTF8ToString(dataptr) | ||
freePtr(dataptr) | ||
return data | ||
return WasmRes.readText(dataptr) | ||
} | ||
@@ -563,0 +544,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
24626828
12684