UNPKG

smoosic

Version:

<sub>[Github site](https://github.com/Smoosic/smoosic) | [source documentation](https://smoosic.github.io/Smoosic/release/docs/modules.html) | [change notes](https://aarondavidnewman.github.io/Smoosic/changes.html) | [application](https://smoosic.github.i

604 lines (596 loc) 17.3 kB
// [Smoosic](https://github.com/AaronDavidNewman/Smoosic) // Copyright (c) Aaron David Newman 2021. import { SuiDialogBase, SuiDialogParams, DialogDefinition } from './dialog'; import { SmoScore } from '../../smo/data/score'; import { XmlToSmo } from '../../smo/mxml/xmlToSmo'; import { SmoToXml } from '../../smo/mxml/smoToXml'; import { SuiScoreViewOperations } from '../../render/sui/scoreViewOperations'; import { SuiFileDownloadComponent } from './components/fileDownload'; import { SuiDialogAdapterBase, SuiComponentAdapter } from './adapter'; import { addFileLink } from '../../common/htmlHelpers'; import { SmoToMidi } from '../../smo/midi/smoToMidi'; import { MidiToSmo } from '../../smo/midi/midiToSmo'; import { PromiseHelpers } from '../../common/promiseHelpers'; import { SmoToVex } from '../../render/vex/toVex'; import { parseMidi } from '../../common/midi-parser.js'; declare var $: any; // declare var MidiParser: any; declare var JSZip: any; /** * internal state of FileLoadDialog is just the string for the filename. * @category SuiDialog */ export class SuiSmoLoadAdapter extends SuiComponentAdapter { jsonFile: string = ''; constructor(view: SuiScoreViewOperations) { super(view); } get loadFile() { return this.jsonFile; } set loadFile(value: string) { this.jsonFile = value; } async commit() { let scoreWorks = false; if (this.jsonFile.length > 0) { try { const score = SmoScore.deserialize(this.jsonFile); scoreWorks = true; await this.view.changeScore(score); } catch (e) { console.warn('unable to score ' + e); } } } async cancel() { return PromiseHelpers.emptyPromise(); } } /** * Load a SMO JSON file * @category SuiDialog */ export class SuiLoadFileDialog extends SuiDialogAdapterBase<SuiSmoLoadAdapter> { static dialogElements: DialogDefinition = { label: 'Load File', elements: [{ smoName: 'loadFile', defaultValue: '', control: 'SuiFileDownloadComponent', label: '' } ], staticText: [] }; get loadFileCtrl() { return this.cmap['loadFileCtrl'] as SuiFileDownloadComponent; } modifier: SuiSmoLoadAdapter; constructor(parameters: SuiDialogParams) { const adapter = new SuiSmoLoadAdapter(parameters.view); parameters.ctor = 'SuiLoadFileDialog'; super(SuiLoadFileDialog.dialogElements, { adapter, ...parameters }); this.modifier = adapter; } async changed() { super.changed(); const enable = this.modifier.loadFile.length < 1; $(this.dgDom.element).find('.ok-button').prop('disabled', enable); } async commit() { await this.modifier.commit(); } } /** * internal state of FileLoadDialog is just the string for the filename. * @category SuiDialog */ export class SuiXmlLoadAdapter extends SuiComponentAdapter { xmlFile: string = ''; changeScore: boolean = false; constructor(view: SuiScoreViewOperations) { super(view); } get loadFile() { return this.xmlFile; } set loadFile(value: string) { this.xmlFile = value; } async commit() { try { const self = this; const parser = new DOMParser(); const xml = parser.parseFromString(this.xmlFile, 'text/xml'); const score = XmlToSmo.convert(xml); score.layoutManager!.zoomToWidth($('body').width()); this.changeScore = true; await this.view.changeScore(score); } catch (e) { console.warn('unable to score ' + e); } } async cancel() { return PromiseHelpers.emptyPromise(); } } /** * Load a music XML file * @category SuiDialog */ export class SuiLoadMxmlDialog extends SuiDialogAdapterBase<SuiXmlLoadAdapter> { static dialogElements: DialogDefinition = { label: 'Load File', elements: [{ smoName: 'loadFile', defaultValue: '', control: 'SuiFileDownloadComponent', label: '' }, ], staticText: [] }; constructor(parameters: SuiDialogParams) { parameters.ctor = 'SuiLoadMxmlDialog'; const adapter = new SuiXmlLoadAdapter(parameters.view); super(SuiLoadMxmlDialog.dialogElements, { adapter, ...parameters }); } async changed() { super.changed(); const enable = this.adapter.loadFile.length < 1; $(this.dgDom.element).find('.ok-button').prop('disabled', enable); } } /** * internal state of FileLoadDialog is just the string for the filename. * @category SuiDialog */ export class SuiMidiLoadAdapter extends SuiComponentAdapter { midiFile: any = null; changeScore: boolean = false; quantize: number = MidiToSmo.quantizeTicksDefault; constructor(view: SuiScoreViewOperations) { super(view); } get loadFile() { return this.midiFile; } set loadFile(value: any) { this.midiFile = value; } get quantizeDuration() { return this.quantize; } set quantizeDuration(value: number) { this.quantize = value; } async commit() { try { // midi parser expects data in UintArray form const ar = new Uint8Array(this.midiFile); const midi: any = parseMidi(ar); const midiParser = new MidiToSmo(midi, this.quantize); await this.view.changeScore(midiParser.convert()); } catch (e) { console.warn('unable to score ' + e); } } async cancel() { return PromiseHelpers.emptyPromise(); } } /** * @category SuiDialog */ export class SuiLoadMidiDialog extends SuiDialogAdapterBase<SuiMidiLoadAdapter> { static dialogElements: DialogDefinition = { label: 'Load File', elements: [{ smoName: 'loadFile', defaultValue: '', control: 'SuiFileDownloadComponent', label: '' }, { smoName: 'quantizeDuration', defaultValue: SmoScore.engravingFonts.Bravura, control: 'SuiDropdownComponent', dataType: 'int', label: 'Quantize to:', options: [{ value: 1024, label: '1/16th note' }, { value: 512, label: '1/32nd note' }, { value: 2048, label: '1/8th note' }] }, ], staticText: [] }; constructor(parameters: SuiDialogParams) { parameters.ctor = 'SuiLoadMidiDialog'; const adapter = new SuiMidiLoadAdapter(parameters.view); super(SuiLoadMidiDialog.dialogElements, { adapter, ...parameters }); } async changed() { super.changed(); const enable = this.adapter?.loadFile?.length < 1; $(this.dgDom.element).find('.ok-button').prop('disabled', enable); } } /** * @category SuiDialog */ export class SuiPrintFileDialog extends SuiDialogBase { static dialogElements: DialogDefinition = { label: 'Print Complete', elements: [], staticText: [] }; constructor(parameters: SuiDialogParams) { parameters.ctor = 'SuiPrintFileDialog'; super(SuiPrintFileDialog.dialogElements, parameters); } changed() { } bindElements() { const dgDom = this.dgDom; $(dgDom.element).find('.ok-button').off('click').on('click', () => { $('body').removeClass('printing'); this.view.renderer.restoreLayoutAfterPrint(); window.dispatchEvent(new Event('resize')); this.complete(); }); $(dgDom.element).find('.cancel-button').remove(); $(dgDom.element).find('.remove-button').remove(); } async commit() { return PromiseHelpers.emptyPromise(); } } /** * @category SuiDialog */ export class SuiVexSaveAdapter extends SuiComponentAdapter { fileName: string = ''; page: number = 0; constructor(view: SuiScoreViewOperations) { super(view); this.fileName = this.view.score.scoreInfo.name; } get saveFileName() { return this.fileName; } set saveFileName(value: string) { this.fileName = value; } get pageToRender() { return this.page; } set pageToRender(val: number) { this.page = val; } async _saveScore() { const vexText = SmoToVex.convert(this.view.score, { div: 'smoo', page: this.page }); if (!this.fileName.endsWith('.js')) { this.fileName = this.fileName + '.js'; } /* TODO: zip multiple render files const zipname = this.fileName.replace('.js', 'zip'); const zipFile = new JSZip(); zipFile.file(this.fileName, vexText); const content = await zipFile.generateAsync({ type: 'blob' }); addFileLink(zipname, content, $('.saveLink')); */ addFileLink(this.fileName, vexText, $('.saveLink')); $('.saveLink a')[0].click(); } async commit() { let filename = this.fileName; const rawFile = filename.split('.')[0]; if (!filename) { filename = 'vexRender.js'; } if (filename.indexOf('.js') < 0) { filename = filename + '.js'; } await this._saveScore(); } async cancel() { return PromiseHelpers.emptyPromise(); } } /** * @category SuiDialog */ export class SuiSaveVexDialog extends SuiDialogAdapterBase<SuiVexSaveAdapter>{ static dialogElements: DialogDefinition = { label: 'Save as Vex Code', elements: [{ smoName: 'saveFileName', defaultValue: '', control: 'SuiTextInputComponent', label: 'File Name' }, { smoName: 'pageToRender', defaultValue: 0, control: 'SuiRockerComponent', label: 'Page', dataType: 'int', }], staticText: [] }; constructor(parameters: SuiDialogParams) { parameters.ctor = 'SuiVexSaveDialog'; const adapter = new SuiVexSaveAdapter(parameters.view); super(SuiSaveVexDialog.dialogElements, { adapter, ...parameters }); } async commit() { await this.adapter.commit(); } } /** * @category SuiDialog */ export class SuiSmoSaveAdapter extends SuiComponentAdapter { fileName: string = ''; constructor(view: SuiScoreViewOperations) { super(view); this.fileName = this.view.score.scoreInfo.name; } get saveFileName() { return this.fileName; } set saveFileName(value: string) { this.fileName = value; } _saveScore() { const json = this.view.storeScore.serialize(); const jsonText = JSON.stringify(json); if (!this.fileName.endsWith('.json')) { this.fileName = this.fileName + '.json'; } addFileLink(this.fileName, jsonText, $('.saveLink')); $('.saveLink a')[0].click(); } async commit() { let filename = this.fileName; const rawFile = filename.split('.')[0]; if (!filename) { filename = 'myScore.json'; } if (filename.indexOf('.json') < 0) { filename = filename + '.json'; } const scoreInfo = this.view.score.scoreInfo; scoreInfo.name = rawFile; scoreInfo.version = scoreInfo.version + 1; await this.view.updateScoreInfo(scoreInfo); this._saveScore(); } async cancel() { return PromiseHelpers.emptyPromise(); } } /** * @category SuiDialog */ export class SuiSaveFileDialog extends SuiDialogAdapterBase<SuiSmoSaveAdapter>{ static dialogElements: DialogDefinition = { label: 'Save Score', elements: [{ smoName: 'saveFileName', defaultValue: '', control: 'SuiTextInputComponent', label: 'File Name' }], staticText: [] }; constructor(parameters: SuiDialogParams) { parameters.ctor = 'SuiSaveFileDialog'; const adapter = new SuiSmoSaveAdapter(parameters.view); super(SuiSaveFileDialog.dialogElements, { adapter, ...parameters }); } async commit() { await this.adapter.commit(); } } /** * @category SuiDialog */ export class SuiSaveJsonValidationAdapter extends SuiComponentAdapter { fileName: string = ''; constructor(view: SuiScoreViewOperations) { super(view); } get saveFileName() { return this.fileName; } set saveFileName(value: string) { this.fileName = value; } _saveScore() { const json = this.view.storeScore.serialize({ useDictionary: false, skipStaves: false, preserveStaffIds: false }); const jsonText = JSON.stringify(json); if (!this.fileName.endsWith('.json')) { this.fileName = this.fileName + '.json'; } addFileLink(this.fileName, jsonText, $('.saveLink')); $('.saveLink a')[0].click(); } async commit() { let filename = this.fileName; const rawFile = filename.split('.')[0]; if (!filename) { filename = 'myScore.json'; } if (filename.indexOf('.json') < 0) { filename = filename + '.json'; } const scoreInfo = this.view.score.scoreInfo; scoreInfo.name = rawFile; scoreInfo.version = scoreInfo.version + 1; await this.view.updateScoreInfo(scoreInfo); this._saveScore(); } // noop async cancel() { return PromiseHelpers.emptyPromise(); } } /** * @category SuiDialog */ export class SuiXmlSaveAdapter extends SuiComponentAdapter { fileName: string = ''; constructor(view: SuiScoreViewOperations) { super(view); } get saveFileName() { return this.fileName; } set saveFileName(value: string) { this.fileName = value; } _saveXml() { const dom = SmoToXml.convert(this.view.storeScore); const ser = new XMLSerializer(); const xmlText = ser.serializeToString(dom); if (!this.fileName.endsWith('.xml') && !this.fileName.endsWith('.mxml')) { this.fileName = this.fileName + '.xml'; } addFileLink(this.fileName, xmlText, $('.saveLink')); $('.saveLink a')[0].click(); } async commit() { let filename = this.fileName; if (!filename) { filename = 'myScore.xml'; } if (filename.indexOf('.xml') < 0) { filename = filename + '.xml'; } this.view.score.scoreInfo.version += 1; this._saveXml(); return PromiseHelpers.emptyPromise(); } // noop async cancel() { return PromiseHelpers.emptyPromise(); } } /** * @category SuiDialog */ export class SuiSaveXmlDialog extends SuiDialogAdapterBase<SuiXmlSaveAdapter> { static dialogElements: DialogDefinition = { label: 'Save Score', elements: [{ smoName: 'saveFileName', control: 'SuiTextInputComponent', label: 'File Name' }], staticText: [] }; constructor(parameters: SuiDialogParams) { parameters.ctor = 'SuiSaveXmlDialog'; const adapter = new SuiXmlSaveAdapter(parameters.view); super(SuiSaveXmlDialog.dialogElements, { adapter, ...parameters }); } async commit() { await this.adapter.commit(); } } /** * @category SuiDialog */ export class SuiSaveJsonValidationDialog extends SuiDialogAdapterBase<SuiSaveJsonValidationAdapter> { static dialogElements: DialogDefinition = { label: 'Export SMO XML', elements: [{ smoName: 'saveFileName', control: 'SuiTextInputComponent', label: 'File Name' }], staticText: [] }; constructor(parameters: SuiDialogParams) { parameters.ctor = 'SuiSaveJsonValidationDialog'; const adapter = new SuiSaveJsonValidationAdapter(parameters.view); super(SuiSaveJsonValidationDialog.dialogElements, { adapter, ...parameters }); } async commit() { await this.adapter.commit(); } } /** * @category SuiDialog */ export class SuiMidiSaveAdapter extends SuiComponentAdapter { fileName: string = ''; constructor(view: SuiScoreViewOperations) { super(view); } get saveFileName() { return this.fileName; } set saveFileName(value: string) { this.fileName = value; } _saveScore() { const bytes = SmoToMidi.convert(this.view.storeScore); if (!this.fileName.endsWith('.mid')) { this.fileName = this.fileName + '.mid'; } addFileLink(this.fileName, bytes, $('.saveLink'), 'audio/midi'); $('.saveLink a')[0].click(); } async commit() { let filename = this.fileName; if (!filename) { filename = 'myScore.mid'; } if (filename.indexOf('.mid') < 0) { filename = filename + '.mid'; } this.view.score.scoreInfo.version += 1; this._saveScore(); return PromiseHelpers.emptyPromise(); } async cancel() { return PromiseHelpers.emptyPromise(); } } /** * @category SuiDialog */ export class SuiSaveMidiDialog extends SuiDialogAdapterBase<SuiMidiSaveAdapter> { static dialogElements: DialogDefinition = { label: 'Save Score as Midi', elements: [{ smoName: 'saveFileName', control: 'SuiTextInputComponent', label: 'File Name' }], staticText: [] } constructor(parameters: SuiDialogParams) { parameters.ctor = 'SuiSaveMidiDialog'; const adapter = new SuiMidiSaveAdapter(parameters.view); super(SuiSaveMidiDialog.dialogElements, { adapter, ...parameters }); } async commit() { await this.adapter.commit(); } }