UNPKG

flat-embed

Version:

Interact and get events from Flat's Sheet Music Embed

1,481 lines (1,480 loc) 56.4 kB
var d = Object.defineProperty; var f = (r, e, t) => e in r ? d(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t; var n = (r, e, t) => f(r, typeof e != "symbol" ? e + "" : e, t); if (typeof window.postMessage == "undefined") throw new Error("The Flat Embed JS API is not supported in this browser"); class b { constructor(e) { n(this, "embed"); n(this, "promises"); n(this, "eventCallbacks"); this.embed = e, this.promises = {}, this.eventCallbacks = {}; } pushCall(e, t, s) { this.promises[e] = this.promises[e] || [], this.promises[e].push({ resolve: t, reject: s }); } /** * Register a callback for a specified event * * @param event The name of the event. * @param callback The function to call when receiving an event * @return `true` if it is the first subscriber, `false otherwise` */ subscribeEvent(e, t) { return this.eventCallbacks[e] = this.eventCallbacks[e] || [], this.eventCallbacks[e].push(t), this.eventCallbacks[e].length === 1; } /** * Unregister a callback for a specified event * * @param event The name of the event. * @param callback The function to call when receiving an event * @return `true` if it is the last subscriber, `false otherwise` */ unsubscribeEvent(e, t) { if (!this.eventCallbacks[e]) return !1; if (t) { const s = this.eventCallbacks[e].indexOf(t); s >= 0 && this.eventCallbacks[e].splice(s, 1); } else this.eventCallbacks[e] = []; return !t || this.eventCallbacks[e].length === 0; } /** * Process a message received from postMessage * * @param {object} data The data received from postMessage */ process(e) { "method" in e && e.method ? this.processMethodResponse(e) : "event" in e && e.event && this.processEvent(e); } /** * Process a method response * * @param {object} data The data received from postMessage */ processMethodResponse(e) { if (!this.promises[e.method]) return; const t = this.promises[e.method].shift(); t && (e.error ? t.reject(e.error) : t.resolve(e.response)); } /** * Process a receieved event * * @param {object} data The data received from postMessage */ processEvent(e) { !this.eventCallbacks[e.event] || this.eventCallbacks[e.event].length === 0 || this.eventCallbacks[e.event].forEach((t) => { t.call(this.embed, e.parameters); }); } } function u(r, e, t) { if (!r.element.contentWindow || !r.element.contentWindow.postMessage) throw new Error("No `contentWindow` or `contentWindow.postMessage` available on the element"); const s = { method: e, parameters: t }; r.element.contentWindow.postMessage(s, r.origin); } function p(r) { return typeof r == "string" && (r = JSON.parse(r)), r; } function M(r) { if (typeof r == "string") { const e = document.getElementById(r); if (!e) throw new TypeError(`The DOM element with the identifier "${r}" was not found.`); r = e; } if (!(r instanceof window.HTMLElement)) throw new TypeError("The first parameter must be an existing DOM element or an identifier."); if (r.nodeName !== "IFRAME") { const e = r.querySelector("iframe"); e && (r = e); } return r; } function P(r) { let e = r.baseUrl || "https://flat-embed.com"; r.isCustomUrl || (e += `/${r.score || "blank"}`); const t = Object.assign( { jsapi: !0 }, r.embedParams ), s = Object.keys(t).map((i) => `${encodeURIComponent(i)}=${encodeURIComponent(t[i])}`).join("&"); return `${e}?${s}`; } function w(r, e) { const t = P(e), s = document.createElement("iframe"); return s.setAttribute("src", t), s.setAttribute("width", e.width || "100%"), s.setAttribute("height", e.height || "100%"), s.setAttribute("allowfullscreen", "true"), s.setAttribute("allow", "autoplay; midi"), s.setAttribute("frameborder", "0"), e.lazy && s.setAttribute("loading", "lazy"), r.appendChild(s), s; } const a = /* @__PURE__ */ new WeakMap(), c = /* @__PURE__ */ new WeakMap(); class C { /** * Create a new Flat Embed * * @param element A reference to a Flat Embed iframe or a container for the new iframe * @param parameters Parameters for the new iframe */ constructor(e, t = {}) { n(this, "origin", "*"); n(this, "element"); n(this, "embedCallback"); const s = M(e); if (s instanceof HTMLIFrameElement && a.has(s)) return a.get(s); let i; s.nodeName !== "IFRAME" ? i = w(s, t) : i = s, this.element = i, this.embedCallback = new b(this); const h = new Promise((g) => { const m = (l) => { if (this.element.contentWindow !== l.source) return; this.origin === "*" && (this.origin = l.origin); const o = p(l.data); if (o.event === "ready" || o.method === "ping") { g(); return; } this.embedCallback.process(o); }; window.addEventListener("message", m, !1), u(this, "ping"); }); return a.set(this.element, this), c.set(this.element, h), this; } /** * Wait for the embed to be ready * * Returns a promise that resolves when the embed iframe has fully loaded and * communication with the Flat embed has been established. This method is automatically * called by all other embed methods, so you typically don't need to call it directly. * However, it can be useful when you want to know exactly when the embed is ready * without performing any other action. * * @returns A promise that resolves when the embed is ready * * @example * // Explicitly wait for embed to be ready * const embed = new Embed('container', { * score: '56ae21579a127715a02901a6' * }); * await embed.ready(); * console.log('Embed is now ready!'); * * @example * // Note: Other methods automatically wait for ready state * const embed = new Embed('container'); * // No need to call ready() - play() will wait automatically * await embed.play(); * * @note All embed methods automatically call ready() internally, so explicit calls are optional */ ready() { return c.get(this.element) || Promise.resolve(); } /** * Call a method on the embed * * @param method Name of the method to call * @param parameters Method parameters * @returns Call result from Embed (if any) */ call(e, t = {}) { return new Promise((s, i) => this.ready().then(() => { this.embedCallback.pushCall(e, s, i), u(this, e, t); })); } /** * Subscribe to a specific event * * @param event The name of the event. * @param callback The function to call when receiving an event */ on(e, t) { if (typeof e != "string") throw new TypeError("An event name (string) is required"); if (typeof t != "function") throw new TypeError("An callback (function) is required"); this.embedCallback.subscribeEvent(e, t) && this.call("addEventListener", e).catch(() => { }); } /** * Unsubscribe to a specific event * * @param event The name of the event. * @param callback The function to unsubscribe */ off(e, t) { if (typeof e != "string") throw new TypeError("An event name (string) is required"); this.embedCallback.unsubscribeEvent(e, t) && this.call("removeEventListener", e).catch(() => { }); } /** * Load a score hosted on Flat * * Loads a Flat score by its unique identifier. For private scores, you must provide * the sharing key obtained from a private link. * * @param score - The score identifier as a string, or an object containing: * - `score`: The unique identifier of the score (required) * - `sharingKey`: The sharing key for private scores (optional) * @returns A promise that resolves when the score is successfully loaded * @throws {TypeError} If the score parameter is invalid * @throws {Error} If the score cannot be loaded (e.g., not found, access denied) * * @example * // Load a public score * await embed.loadFlatScore('56ae21579a127715a02901a6'); * * @example * // Load a private score with sharing key * await embed.loadFlatScore({ * score: '56ae21579a127715a02901a6', * sharingKey: 'f79c3c0dd1fc76ed8b30d6f2c845c8c30f11fe88d9fc39ab96e8e407629d4885' * }); */ loadFlatScore(e) { return typeof e == "string" && (e = { score: e }), this.call("loadFlatScore", e); } /** * Load a MusicXML score * * Loads a MusicXML score from a string or binary data. The score will be converted * to Flat's internal format and displayed in the embed. * * @param score - The MusicXML content as a string (XML) or Uint8Array (compressed MXL) * @returns A promise that resolves when the score is successfully loaded * @throws {TypeError} If the score format is invalid * @throws {Error} If the MusicXML cannot be parsed or loaded * * @example * // Load from XML string * const xmlString = '<?xml version="1.0"?>...'; * await embed.loadMusicXML(xmlString); * * @example * // Load from compressed MXL file * const response = await fetch('score.mxl'); * const buffer = await response.arrayBuffer(); * await embed.loadMusicXML(new Uint8Array(buffer)); */ loadMusicXML(e) { return this.call("loadMusicXML", e); } /** * Load a MIDI score * * Loads a MIDI file and converts it to sheet music notation. Note that MIDI files * contain performance data rather than notation, so the conversion may not perfectly * represent the original musical intent. * * @param score - The MIDI file as a Uint8Array * @returns A promise that resolves when the score is successfully loaded * @throws {TypeError} If the score format is invalid * @throws {Error} If the MIDI file cannot be parsed or loaded * * @example * // Load MIDI file from URL * const response = await fetch('song.mid'); * const buffer = await response.arrayBuffer(); * await embed.loadMIDI(new Uint8Array(buffer)); * * @example * // Load MIDI file from file input * const file = document.getElementById('fileInput').files[0]; * const buffer = await file.arrayBuffer(); * await embed.loadMIDI(new Uint8Array(buffer)); */ loadMIDI(e) { return this.call("loadMIDI", e); } /** * Load a Flat JSON score * * Loads a score from Flat's native JSON format. This format preserves all score * data and is the most reliable way to transfer scores between Flat applications. * * @param score - The score data as a JSON string or JavaScript object * @returns A promise that resolves when the score is successfully loaded * @throws {TypeError} If the JSON is invalid or cannot be parsed * @throws {Error} If the score data is malformed or cannot be loaded * * @example * // Load from JSON object * const scoreData = await fetch('score.json').then(r => r.json()); * await embed.loadJSON(scoreData); * * @example * // Load from JSON string * const jsonString = '{"score-partwise": {...}}'; * await embed.loadJSON(jsonString); */ loadJSON(e) { return this.call("loadJSON", e); } /** * Get the score in Flat JSON format * * Exports the currently loaded score as Flat's native JSON format. This format * preserves all score data including notation, layout, and metadata. * * @returns A promise that resolves with the score data as a JavaScript object * @throws {Error} If no score is currently loaded * * @example * // Export and save score data * const scoreData = await embed.getJSON(); * const jsonString = JSON.stringify(scoreData); * * // Save to file * const blob = new Blob([jsonString], { type: 'application/json' }); * const url = URL.createObjectURL(blob); * const a = document.createElement('a'); * a.href = url; * a.download = 'score.json'; * a.click(); */ getJSON() { return this.call("getJSON"); } /** * Convert the displayed score to MusicXML * * Exports the currently loaded score as MusicXML, the standard format for sheet music * notation exchange. Supports both uncompressed XML and compressed MXL formats. * * @param options - Export options: * - `compressed`: If true, returns compressed MusicXML (.mxl) as Uint8Array. * If false (default), returns uncompressed XML as string. * @returns A promise that resolves with the MusicXML data * @throws {TypeError} If options parameter is invalid * @throws {Error} If no score is loaded or conversion fails * * @example * // Get uncompressed MusicXML * const xml = await embed.getMusicXML(); * console.log(xml); // <?xml version="1.0"... * * @example * // Get compressed MusicXML (.mxl) * const mxl = await embed.getMusicXML({ compressed: true }); * const blob = new Blob([mxl], { type: 'application/vnd.recordare.musicxml' }); * const url = URL.createObjectURL(blob); */ getMusicXML(e) { return new Promise((t, s) => { if (e = e || {}, typeof e != "object") return s(new TypeError("Options must be an object")); this.call("getMusicXML", e).then((i) => t(typeof i == "string" ? i : new Uint8Array(i))).catch(s); }); } /** * Convert the displayed score to PNG image * * Exports the currently loaded score as a PNG image. Supports various export options * including resolution, layout mode, and output format. * * @param options - Export options: * - `result`: Output format - 'Uint8Array' (default) or 'dataURL' * - `dpi`: Resolution in dots per inch (50-300, default: 150) * - `layout`: Layout mode - 'track' (default, horizontal single system) or 'page' * @returns A promise that resolves with the PNG data * @throws {TypeError} If options parameter is invalid * @throws {Error} If no score is loaded or conversion fails * * @example * // Get PNG as Uint8Array * const pngData = await embed.getPNG(); * const blob = new Blob([pngData], { type: 'image/png' }); * * @example * // Get PNG as data URL for direct display * const dataUrl = await embed.getPNG({ result: 'dataURL' }); * document.getElementById('preview').src = dataUrl; * * @example * // High resolution export with page layout * const hqPng = await embed.getPNG({ dpi: 300, layout: 'page' }); */ getPNG(e) { return new Promise((t, s) => { if (e = e || {}, typeof e != "object") return s(new TypeError("Options must be an object")); this.call("getPNG", e).then((i) => { if (typeof i == "string") return t(i); t(new Uint8Array(i)); }).catch(s); }); } /** * Convert the displayed score to MIDI * * Exports the currently loaded score as a MIDI file. The MIDI file will contain * performance data including notes, tempo, dynamics, and instrument information. * Note that some notation elements may not be represented in MIDI format. * * @returns A promise that resolves with a Uint8Array containing the MIDI file data * @throws {Error} If no score is loaded or conversion fails * * @example * // Export score as MIDI * const midiData = await embed.getMIDI(); * * // Save as file * const blob = new Blob([midiData], { type: 'audio/midi' }); * const url = URL.createObjectURL(blob); * const a = document.createElement('a'); * a.href = url; * a.download = 'score.mid'; * a.click(); * * @example * // Play MIDI in browser (requires Web MIDI API) * const midiData = await embed.getMIDI(); * // ... use with Web MIDI API or MIDI player library */ getMIDI() { return this.call("getMIDI").then((e) => new Uint8Array(e)); } /** * Get the metadata of the score (for scores hosted on Flat) * * Retrieves metadata for scores that are hosted on Flat, including title, composer, * collaborators, creation date, and other information available through Flat's API. * This method only works for scores loaded via `loadFlatScore()`. * * @returns A promise that resolves with the score metadata object * @throws {Error} If no Flat score is loaded or metadata is unavailable * * @example * // Get metadata after loading a Flat score * await embed.loadFlatScore('56ae21579a127715a02901a6'); * const metadata = await embed.getFlatScoreMetadata(); * console.log(`Title: ${metadata.title}`); * console.log(`Created by: ${metadata.user.username}`); * * @see {@link https://flat.io/developers/api/reference/#operation/getScore} */ getFlatScoreMetadata() { return this.call("getFlatScoreMetadata"); } /** * Get the embed configuration * * Retrieves the complete configuration object for the embed, including display * settings, permissions, editor configuration, and enabled features. * * @returns A promise that resolves with the embed configuration object * @throws {Error} If the configuration cannot be retrieved * * @example * // Get current embed configuration * const config = await embed.getEmbedConfig(); * console.log(`Mode: ${config.mode}`); * console.log(`Controls enabled: ${config.controlsPlay}`); */ getEmbedConfig() { return this.call("getEmbedConfig"); } /** * Set the editor configuration * * Updates the editor configuration for the embed. These settings control various * aspects of the editor interface and behavior. The configuration is applied * when the next score is loaded. * * @param editor - Editor configuration object that may include: * - displayMode: Score display mode * - toolsetId: Active toolset identifier * - hiddenTools: Array of tool identifiers to hide * - Additional editor-specific settings * @returns A promise that resolves when the configuration is updated * @throws {Error} If the configuration is invalid * * @example * // Configure editor before loading a score * await embed.setEditorConfig({ * displayMode: 'responsive', * hiddenTools: ['note-duration', 'note-pitch'] * }); * await embed.loadFlatScore('56ae21579a127715a02901a6'); * * @note This configuration persists across score loads until changed */ setEditorConfig(e) { return this.call("setEditorConfig", e); } /** * Toggle fullscreen mode * * Switches the embed in or out of fullscreen mode. When in fullscreen, the embed * expands to fill the entire screen, providing an immersive view of the score. * * @param active - true to enter fullscreen, false to exit fullscreen * @returns A promise that resolves when the fullscreen state has changed * @throws {Error} If fullscreen mode cannot be toggled (e.g., browser restrictions) * * @example * // Enter fullscreen mode * await embed.fullscreen(true); * * @example * // Exit fullscreen mode * await embed.fullscreen(false); * * @example * // Toggle fullscreen with user interaction * button.addEventListener('click', () => { * embed.fullscreen(true); * }); * * @note Fullscreen requests may require user interaction due to browser policies */ fullscreen(e) { return this.call("fullscreen", e); } /** * Start playback * * Begins playing the score from the current cursor position. If playback was * previously paused, it resumes from the pause position. If stopped, it starts * from the beginning or the current cursor position. * * @returns A promise that resolves when playback has started * @throws {Error} If no score is loaded or playback cannot start * * @example * // Start playback * await embed.play(); * * @example * // Play with event handling * embed.on('play', () => console.log('Playback started')); * await embed.play(); * * @see {@link pause} - Pause playback * @see {@link stop} - Stop playback */ play() { return this.call("play"); } /** * Pause playback * * Pauses the score playback at the current position. The playback position * is maintained, allowing you to resume from the same point using `play()`. * * @returns A promise that resolves when playback has been paused * @throws {Error} If no score is loaded or playback cannot be paused * * @example * // Pause playback * await embed.pause(); * * @example * // Toggle play/pause * if (isPlaying) { * await embed.pause(); * } else { * await embed.play(); * } * * @see {@link play} - Start or resume playback * @see {@link stop} - Stop and reset playback */ pause() { return this.call("pause"); } /** * Stop playback * * Stops the score playback and resets the playback position to the beginning * of the score. Unlike `pause()`, the playback position is not maintained. * * @returns A promise that resolves when playback has been stopped * @throws {Error} If no score is loaded or playback cannot be stopped * * @example * // Stop playback * await embed.stop(); * * @example * // Stop and restart from beginning * await embed.stop(); * await embed.play(); // Starts from beginning * * @see {@link play} - Start playback * @see {@link pause} - Pause playback */ stop() { return this.call("stop"); } /** * Mute playback * * Mutes all audio output from the score playback. The playback continues * but without sound. This is equivalent to setting the master volume to 0 * but preserves the previous volume setting. * * @returns A promise that resolves when audio has been muted * @throws {Error} If muting fails * * @example * // Mute audio * await embed.mute(); * * @example * // Mute during playback * await embed.play(); * await embed.mute(); * * @see {@link setMasterVolume} - Set master volume level * @note There is no unmute method; use setMasterVolume to restore audio */ mute() { return this.call("mute"); } /** * Get the current master volume * * Retrieves the current master volume level for score playback. * * @returns A promise that resolves with the volume level (0-100) * @throws {Error} If the volume cannot be retrieved * * @example * // Get current volume * const volume = await embed.getMasterVolume(); * console.log(`Current volume: ${volume}%`); * * @see {@link setMasterVolume} - Set the master volume */ getMasterVolume() { return this.call("getMasterVolume"); } /** * Set the master volume * * Sets the master volume level for score playback. This affects all parts * proportionally based on their individual volume settings. * * @param parameters - Volume settings * @returns A promise that resolves when the volume has been set * @throws {Error} If the volume value is invalid or cannot be set * * @example * // Set volume to 50% * await embed.setMasterVolume({ volume: 50 }); * * @example * // Mute audio * await embed.setMasterVolume({ volume: 0 }); * * @example * // Maximum volume * await embed.setMasterVolume({ volume: 100 }); * * @see {@link getMasterVolume} - Get the current master volume */ setMasterVolume(e) { return this.call("setMasterVolume", e); } /** * Get the volume of a specific part * * Retrieves the current volume level for a specific instrument part in the score. * * @param parameters - Object containing: * - `partUuid`: The unique identifier of the part * @returns A promise that resolves with the part's volume level (0-100) * @throws {Error} If the part UUID is invalid or volume cannot be retrieved * * @example * // Get volume for a specific part * const parts = await embed.getParts(); * const violinVolume = await embed.getPartVolume({ * partUuid: parts[0].uuid * }); * * @see {@link setPartVolume} - Set the volume for a part * @see {@link getParts} - Get all parts information */ getPartVolume(e) { return this.call("getPartVolume", e); } /** * Set the volume of a specific part * * Sets the volume level for a specific instrument part in the score. Part volumes * are independent but affected by the master volume. * * @param parameters - Object containing: * - `partUuid`: The unique identifier of the part * - `volume`: Volume level (0-100, where 0 is muted and 100 is maximum) * @returns A promise that resolves when the volume has been set * @throws {Error} If the part UUID or volume value is invalid * * @example * // Set violin part to 75% volume * const parts = await embed.getParts(); * await embed.setPartVolume({ * partUuid: parts[0].uuid, * volume: 75 * }); * * @example * // Mute the bass part * await embed.setPartVolume({ * partUuid: bassPartUuid, * volume: 0 * }); * * @see {@link getPartVolume} - Get the volume for a part * @see {@link mutePart} - Mute a part */ setPartVolume(e) { return this.call("setPartVolume", e); } /** * Mute a specific part * * Mutes the audio output for a specific instrument part. The part's volume * setting is preserved and can be restored using `unmutePart()`. * * @param parameters - Object containing: * - `partUuid`: The unique identifier of the part to mute * @returns A promise that resolves when the part has been muted * @throws {Error} If the part UUID is invalid or muting fails * * @example * // Mute the drums part * const parts = await embed.getParts(); * const drumsPart = parts.find(p => p.instrument === 'drums'); * await embed.mutePart({ partUuid: drumsPart.uuid }); * * @see {@link unmutePart} - Unmute a part * @see {@link setPartVolume} - Set part volume to 0 (alternative) */ mutePart(e) { return this.call("mutePart", e); } /** * Unmute a specific part * * Restores the audio output for a previously muted part. The part returns * to its previous volume level before it was muted. * * @param parameters - Object containing: * - `partUuid`: The unique identifier of the part to unmute * @returns A promise that resolves when the part has been unmuted * @throws {Error} If the part UUID is invalid or unmuting fails * * @example * // Unmute a previously muted part * await embed.unmutePart({ partUuid: drumsPart.uuid }); * * @see {@link mutePart} - Mute a part */ unmutePart(e) { return this.call("unmutePart", e); } /** * Enable solo mode for a part * * Enables solo mode for a specific part, which mutes all other parts while * keeping the selected part audible. Multiple parts can be in solo mode * simultaneously. * * @param parameters - Object containing: * - `partUuid`: The unique identifier of the part to solo * @returns A promise that resolves when solo mode has been enabled * @throws {Error} If the part UUID is invalid or solo mode cannot be set * * @example * // Solo the violin part * const parts = await embed.getParts(); * const violinPart = parts.find(p => p.instrument === 'violin'); * await embed.setPartSoloMode({ partUuid: violinPart.uuid }); * * @example * // Solo multiple parts * await embed.setPartSoloMode({ partUuid: violinUuid }); * await embed.setPartSoloMode({ partUuid: celloUuid }); * * @see {@link unsetPartSoloMode} - Disable solo mode * @see {@link getPartSoloMode} - Check solo mode status */ setPartSoloMode(e) { return this.call("setPartSoloMode", e); } /** * Disable solo mode for a part * * Disables solo mode for a specific part. If this was the only part in solo * mode, all parts return to their normal volume/mute states. If other parts * remain in solo mode, this part will be muted. * * @param parameters - Object containing: * - `partUuid`: The unique identifier of the part * @returns A promise that resolves when solo mode has been disabled * @throws {Error} If the part UUID is invalid or solo mode cannot be unset * * @example * // Remove solo from a part * await embed.unsetPartSoloMode({ partUuid: violinPart.uuid }); * * @see {@link setPartSoloMode} - Enable solo mode * @see {@link getPartSoloMode} - Check solo mode status */ unsetPartSoloMode(e) { return this.call("unsetPartSoloMode", e); } /** * Get the solo mode status of a part * * Checks whether a specific part is currently in solo mode. * * @param parameters - Object containing: * - `partUuid`: The unique identifier of the part * @returns A promise that resolves with true if solo mode is enabled, false otherwise * @throws {Error} If the part UUID is invalid * * @example * // Check if violin is in solo mode * const isSolo = await embed.getPartSoloMode({ * partUuid: violinPart.uuid * }); * if (isSolo) { * console.log('Violin is in solo mode'); * } * * @see {@link setPartSoloMode} - Enable solo mode * @see {@link unsetPartSoloMode} - Disable solo mode */ getPartSoloMode(e) { return this.call("getPartSoloMode", e); } /** * Get the reverb level of a part * * Retrieves the current reverb (reverberation) effect level for a specific * instrument part. Reverb adds spatial depth and ambience to the sound. * * @param parameters - Object containing: * - `partUuid`: The unique identifier of the part * @returns A promise that resolves with the reverb level (0-100) * @throws {Error} If the part UUID is invalid or reverb cannot be retrieved * * @example * // Get reverb level for piano part * const parts = await embed.getParts(); * const pianoPart = parts.find(p => p.instrument === 'piano'); * const reverb = await embed.getPartReverb({ * partUuid: pianoPart.uuid * }); * console.log(`Piano reverb: ${reverb}%`); * * @see {@link setPartReverb} - Set the reverb level */ getPartReverb(e) { return this.call("getPartReverb", e); } /** * Set the reverb level of a part * * Sets the reverb (reverberation) effect level for a specific instrument part. * Higher values create more spacious, ambient sound. * * @param parameters - Object containing: * - `partUuid`: The unique identifier of the part * - `reverberation`: Reverb level (0-100, where 0 is dry and 100 is maximum reverb) * @returns A promise that resolves when the reverb has been set * @throws {Error} If the part UUID or reverb value is invalid * * @example * // Add subtle reverb to piano * await embed.setPartReverb({ * partUuid: pianoPart.uuid, * reverberation: 30 * }); * * @example * // Remove reverb (dry sound) * await embed.setPartReverb({ * partUuid: pianoPart.uuid, * reverberation: 0 * }); * * @see {@link getPartReverb} - Get the current reverb level */ setPartReverb(e) { return this.call("setPartReverb", e); } /** * Configure an audio or video track * * Sets up a new audio or video track to synchronize with the score playback. * This allows you to play backing tracks, reference recordings, or video * alongside the score. * * @param parameters - Track configuration object (see ScoreTrackConfiguration type) * @returns A promise that resolves when the track has been configured * @throws {Error} If the track configuration is invalid * * @example * // Configure an audio backing track * await embed.setTrack({ * id: 'backing-track-1', * type: 'audio', * url: 'https://example.com/backing-track.mp3', * synchronizationPoints: [ * { type: 'measure', measure: 0, time: 0 }, * { type: 'measure', measure: 16, time: 32.5 } * ] * }); * * @see {@link useTrack} - Enable a configured track * @see {@link seekTrackTo} - Seek to a position in the track */ setTrack(e) { return this.call("setTrack", e); } /** * Enable a previously configured track * * Activates a track that was previously configured with `setTrack()`. Only one * track can be active at a time. * * @param parameters - Object containing: * - `id`: The identifier of the track to enable * @returns A promise that resolves when the track has been enabled * @throws {Error} If the track ID is invalid or track cannot be enabled * * @example * // Enable a configured backing track * await embed.useTrack({ id: 'backing-track-1' }); * * @example * // Switch between multiple tracks * await embed.useTrack({ id: 'practice-tempo' }); * // Later... * await embed.useTrack({ id: 'full-tempo' }); * * @see {@link setTrack} - Configure a new track */ useTrack(e) { return this.call("useTrack", e); } /** * Seek to a position in the audio track * * Moves the playback position of the currently active audio/video track to * a specific time. This is useful for synchronizing with score playback or * jumping to specific sections. * * @param parameters - Object containing: * - `time`: Time position in seconds * @returns A promise that resolves when seeking is complete * @throws {Error} If no track is active or seeking fails * * @example * // Seek to 30 seconds * await embed.seekTrackTo({ time: 30 }); * * @example * // Seek to beginning * await embed.seekTrackTo({ time: 0 }); * * @see {@link setTrack} - Configure a track * @see {@link useTrack} - Enable a track */ seekTrackTo(e) { return this.call("seekTrackTo", e); } /** * Print the score * * Opens the browser's print dialog to print the currently loaded score. The score * is formatted for optimal printing with proper page breaks and sizing. * * @returns A promise that resolves when the print dialog has been opened * @throws {Error} If no score is loaded or printing cannot be initiated * * @example * // Print the current score * await embed.print(); * * @example * // Add print button * document.getElementById('printBtn').addEventListener('click', () => { * embed.print(); * }); * * @note The actual printing is controlled by the browser's print dialog */ print() { return this.call("print"); } /** * Get the current zoom ratio * * Retrieves the current zoom level of the score display. The zoom level affects * how large the notation appears on screen. * * @returns A promise that resolves with the zoom ratio (0.5 to 3) * @throws {Error} If the zoom level cannot be retrieved * * @example * // Get current zoom level * const zoom = await embed.getZoom(); * console.log(`Current zoom: ${zoom * 100}%`); * * @see {@link setZoom} - Set the zoom level * @see {@link getAutoZoom} - Check if auto-zoom is enabled */ getZoom() { return this.call("getZoom"); } /** * Set the zoom ratio * * Sets a specific zoom level for the score display. Setting a manual zoom level * automatically disables auto-zoom mode. * * @param zoom - The zoom ratio (0.5 to 3) * - 0.5 = 50% (minimum zoom) * - 1 = 100% (normal size) * - 3 = 300% (maximum zoom) * @returns A promise that resolves with the actual zoom ratio applied * @throws {Error} If the zoom value is outside the valid range * * @example * // Set zoom to 150% * await embed.setZoom(1.5); * * @example * // Zoom in/out buttons * const zoomIn = async () => { * const current = await embed.getZoom(); * await embed.setZoom(Math.min(current + 0.1, 3)); * }; * * @see {@link getZoom} - Get current zoom level * @see {@link setAutoZoom} - Enable automatic zoom */ setZoom(e) { return this.call("setZoom", e); } /** * Get the auto-zoom status * * Checks whether auto-zoom mode is currently enabled. When auto-zoom is active, * the score automatically adjusts its zoom level to fit the available space. * * @returns A promise that resolves with true if auto-zoom is enabled, false otherwise * @throws {Error} If the status cannot be retrieved * * @example * // Check auto-zoom status * const isAutoZoom = await embed.getAutoZoom(); * if (isAutoZoom) { * console.log('Score will auto-fit to container'); * } * * @see {@link setAutoZoom} - Enable or disable auto-zoom */ getAutoZoom() { return this.call("getAutoZoom"); } /** * Enable or disable auto-zoom * * Controls the auto-zoom feature. When enabled, the score automatically adjusts * its zoom level to fit the available container width. When disabled, the zoom * level remains fixed at the last set value. * * @param state - true to enable auto-zoom, false to disable * @returns A promise that resolves with the new auto-zoom state * @throws {Error} If auto-zoom cannot be toggled * * @example * // Enable auto-zoom * await embed.setAutoZoom(true); * * @example * // Disable auto-zoom and set fixed zoom * await embed.setAutoZoom(false); * await embed.setZoom(1.2); * * @see {@link getAutoZoom} - Check current auto-zoom status * @see {@link setZoom} - Set a specific zoom level */ setAutoZoom(e) { return this.call("setAutoZoom", e); } /** * Set focus to the score * * Gives keyboard focus to the score iframe, allowing keyboard shortcuts and * navigation to work. This is useful when embedding multiple interactive elements * on a page. * * @returns A promise that resolves when focus has been set * * @example * // Focus score for keyboard interaction * await embed.focusScore(); * * @example * // Focus score when user clicks a button * document.getElementById('editBtn').addEventListener('click', () => { * embed.focusScore(); * }); */ focusScore() { return this.call("focusScore"); } /** * Get the cursor position * * Retrieves the current position of the cursor in the score, including the part, * measure, voice, and note location. * * @returns A promise that resolves with the cursor position object * @throws {Error} If cursor position cannot be retrieved * * @example * // Get current cursor position * const pos = await embed.getCursorPosition(); * console.log(`Cursor at measure ${pos.measureIdx + 1}`); * * @see {@link setCursorPosition} - Set cursor position * @see {@link goLeft} - Move cursor left * @see {@link goRight} - Move cursor right */ getCursorPosition() { return this.call("getCursorPosition"); } /** * Set the cursor position * * Moves the cursor to a specific position in the score. You can specify any * combination of part, measure, voice, staff, and note indices. Unspecified * values remain at their current position. * * @param position - New cursor position with optional fields: * - `partIdx`: Target part index (optional) * - `measureIdx`: Target measure index (optional) * - `voiceIdx`: Target voice index (optional) * - `noteIdx`: Target note index (optional) * - `staffIdx`: Target staff index (optional) * @returns A promise that resolves when the cursor has been moved * @throws {Error} If the position is invalid or cursor cannot be moved * * @example * // Move to beginning of measure 5 * await embed.setCursorPosition({ * measureIdx: 4, // 0-based index * noteIdx: 0 * }); * * @example * // Move to second voice of current measure * await embed.setCursorPosition({ voiceIdx: 1 }); * * @see {@link getCursorPosition} - Get current position */ setCursorPosition(e) { return this.call("setCursorPosition", e); } /** * Get information about all parts * * Retrieves detailed information about all instrument parts in the score, * including their names, instruments, and unique identifiers. * * @returns A promise that resolves with an array of part configurations * @throws {Error} If no score is loaded or parts cannot be retrieved * * @example * // Get all parts * const parts = await embed.getParts(); * parts.forEach(part => { * console.log(`${part.name}: ${part.uuid}`); * }); * * @example * // Find specific instrument * const parts = await embed.getParts(); * const violin = parts.find(p => p.instrument === 'violin'); * * @see {@link getDisplayedParts} - Get currently visible parts * @see {@link setDisplayedParts} - Choose which parts to display */ getParts() { return this.call("getParts"); } /** * Get the currently displayed parts * * Retrieves information about which parts are currently visible in the score. * Some parts may be hidden for focused practice or simplified viewing. * * @returns A promise that resolves with an array of currently displayed part configurations * @throws {Error} If displayed parts cannot be retrieved * * @example * // Check which parts are visible * const displayedParts = await embed.getDisplayedParts(); * console.log(`Showing ${displayedParts.length} parts`); * * @see {@link getParts} - Get all parts in the score * @see {@link setDisplayedParts} - Change which parts are visible */ getDisplayedParts() { return this.call("getDisplayedParts"); } /** * Set which parts to display * * Controls which instrument parts are visible in the score. Parts can be * identified by their UUID, index, name, or abbreviation. Hidden parts * are not displayed but still play during playback unless muted. * * @param parts - Array of part identifiers. Each can be: * - UUID (recommended): The unique identifier * - Index: Numeric position (0-based) * - Name: Full part name (e.g., "Violin") * - Abbreviation: Short name (e.g., "Vln") * @returns A promise that resolves when parts visibility has been updated * @throws {Error} If part identifiers are invalid * * @example * // Show only violin and piano parts by UUID * const parts = await embed.getParts(); * const violin = parts.find(p => p.instrument === 'violin'); * const piano = parts.find(p => p.instrument === 'piano'); * await embed.setDisplayedParts([violin.uuid, piano.uuid]); * * @example * // Show parts by index * await embed.setDisplayedParts(['0', '2', '3']); * * @example * // Show all parts * const allParts = await embed.getParts(); * await embed.setDisplayedParts(allParts.map(p => p.uuid)); * * @see {@link getParts} - Get all available parts * @see {@link getDisplayedParts} - Check which parts are visible */ setDisplayedParts(e) { return this.call("setDisplayedParts", e); } /** * Get the number of measures in the score * * Retrieves the total count of measures (bars) in the currently loaded score. * * @returns A promise that resolves with the number of measures * @throws {Error} If no score is loaded * * @example * // Get measure count * const measureCount = await embed.getNbMeasures(); * console.log(`This score has ${measureCount} measures`); * * @see {@link getMeasuresUuids} - Get measure identifiers * @see {@link getMeasureDetails} - Get detailed measure information */ getNbMeasures() { return this.call("getNbMeasures"); } /** * Get the measure UUIDs of the score * * Retrieves the unique identifiers for all measures in the score. These UUIDs * can be used to reference specific measures in other API calls. * * @returns A promise that resolves with an array of measure UUIDs in order * @throws {Error} If no score is loaded * * @example * // Get all measure UUIDs * const measureUuids = await embed.getMeasuresUuids(); * console.log(`First measure UUID: ${measureUuids[0]}`); * * @see {@link getNbMeasures} - Get the total number of measures * @see {@link getMeasureDetails} - Get details for a specific measure */ getMeasuresUuids() { return this.call("getMeasuresUuids"); } /** * Get detailed measure information * * Retrieves comprehensive information about the current measure at the cursor * position, including time signature, key signature, tempo, and other properties. * * @returns A promise that resolves with the measure details * @throws {Error} If no score is loaded or details cannot be retrieved * * @example * // Get current measure details * const details = await embed.getMeasureDetails(); * console.log(`Time signature: ${details.time.beats}/${details.time['beat-type']}`); * console.log(`Key signature: ${details.key.fifths} sharps/flats`); * console.log(`Tempo: ${details.tempo.bpm} BPM`); * * @see {@link getNbMeasures} - Get measure count * @see {@link getMeasuresUuids} - Get measure identifiers */ getMeasureDetails() { return this.call("getMeasureDetails"); } /** * Get the number of parts in the score * * Retrieves the total count of instrument parts in the currently loaded score. * * @returns A promise that resolves with the number of parts * @throws {Error} If no score is loaded * * @example * // Get part count * const partCount = await embed.getNbParts(); * console.log(`Score has ${partCount} instruments`); * * @see {@link getParts} - Get detailed part information * @see {@link getPartsUuids} - Get part identifiers */ getNbParts() { return this.call("getNbParts"); } /** * Get the part UUIDs of the score * * Retrieves the unique identifiers for all parts in the score. These UUIDs * are used to reference specific parts in volume, mute, and other part-specific * operations. * * @returns A promise that resolves with an array of part UUIDs in order * @throws {Error} If no score is loaded * * @example * // Get all part UUIDs * const partUuids = await embed.getPartsUuids(); * // Mute the first part * await embed.mutePart({ partUuid: partUuids[0] }); * * @see {@link getNbParts} - Get the total number of parts * @see {@link getParts} - Get detailed part information */ getPartsUuids() { return this.call("getPartsUuids"); } /** * Get voice UUIDs for a specific measure * * Retrieves the unique identifiers of all voices present in a specific measure * of a part. Voices represent independent melodic lines within a part. * * @param parameters - Object containing: * - `partUuid`: UUID of the part * - `measureUuid`: UUID of the measure * @returns A promise that resolves with an array of voice UUIDs * @throws {Error} If the part or measure UUID is invalid * * @example * // Get voices in first measure of piano part * const parts = await embed.getParts(); * const measures = await embed.getMeasuresUuids(); * const voices = await embed.getMeasureVoicesUuids({ * partUuid: parts[0].uuid, * measureUuid: measures[0] * }); * console.log(`Found ${voices.length} voices`); * * @see {@link getMeasureNbNotes} - Count notes in a voice */ getMeasureVoicesUuids(e) { return this.call("getMeasureVoicesUuids", e); } /** * Get the number of notes in a voice * * Counts the total number of notes (including rests) in a specific voice * within a measure. This is useful for iterating through notes or determining * voice complexity. * * @param parameters - Object containing: * - `partUuid`: UUID of the part * - `measureUuid`: UUID of the measure * - `voiceUuid`: UUID of the voice * @returns A promise that resolves with the number of notes * @throws {Error} If any UUID is invalid * * @example * // Count notes in first voice * const noteCount = await embed.getMeasureNbNotes({ * partUuid: partUuid, * measureUuid: measureUuid, * voiceUuid: voiceUuids[0] * }); * * @see {@link getNoteData} - Get details about specific notes * @see {@link getMeasureVoicesUuids} - Get voice UUIDs */ getMeasureNbNotes(e) { return this.call("getMeasureNbNotes", e); } /** * Get information about a specific note * * Retrieves detailed information about a note at a specific position, including * pitch, duration, articulations, and other musical properties. * * @param parameters - Object containing: * - `partUuid`: UUID of the part * - `measureUuid`: UUID of the measure * - `voiceUuid`: UUID of the voice * - `noteIdx`: Index of the note (0-based) * @returns A promise that resolves with the note details * @throws {Error} If the position is invalid or note not found * * @example * // Get first note details * const noteData = await embed.getNoteData({ * partUuid: partUuid, * measureUuid: measureUuid, * voiceUuid: voiceUuid, * noteIdx: 0 * }); * console.log(`Note: ${noteData.pitch}`); * * @see {@link getMeasureNbNotes} - Get total notes in voice */ getNoteData(e) { return this.call("getNoteData", e); } /** * Convert playback position to note index * * Finds the note index that corresponds to a specific playback position. * This is useful for synchronizing visual elements with audio playback. * * @param parameters - Object containing: * - `partUuid`: UUID of the part * - `voiceUuid`: UUID of the voice * - `playbackPosition`: Position object with measure and beat information * @returns A promise that resolves with the note index (0-based) * @throws {Error} If the position cannot be mapped to a note * * @example * // Find note at current playback position * const noteIdx = await embed.playbackPositionToNoteIdx({