22 * music21j -- Javascript reimplementation of Core music21p features.
33 * music21/webmidi -- webmidi or wrapper around the Jazz Plugin
44 *
5- * For non webmidi -- Uses the cross-platform, cross-browser plugin from
6- * http://jazz-soft.net/doc/Jazz-Plugin/Plugin.html
7- * P.S. by the standards of divinity of most major religions, Sema Kachalo is a god.
5+ * Recommendation (2026): use Google Chrome or Edge on Mac or PC.
86 *
9- * Copyright (c) 2013-24, Michael Scott Asato Cuthbert
10- * Based on music21 (=music21p), Copyright (c) 2006-24, Michael Scott Asato Cuthbert
7+ * For non webmidi -- Uses the cross-platform, cross-browser plugin from
8+ * https://jazz-soft.net/doc/Jazz-Plugin/Plugin.html
9+ *
10+ * Copyright (c) 2013-26, Michael Scott Asato Cuthbert
11+ * Based on music21 (=music21p), Copyright (c) 2006-26, Michael Scott Asato Cuthbert
1112 *
1213 */
1314/**
@@ -142,7 +143,7 @@ export function midiInArrived(midiMessageEvent) {
142143 const midiCallbacks : miditools . CallbackInterface = miditools . callbacks ;
143144 const eventObject = midiCallbacks . raw ( t , a , b , c ) ;
144145 if ( midiCallbacks . general instanceof Array ) {
145- return midiCallbacks . general . forEach ( ( el , index , array ) => {
146+ return midiCallbacks . general . forEach ( ( el , _index , _array ) => {
146147 el ( eventObject ) ;
147148 } ) ;
148149 } else if ( midiCallbacks . general instanceof Function ) {
@@ -203,21 +204,29 @@ export function createPlugin(
203204/**
204205 * Creates a <select> object for selecting among the MIDI choices in Jazz
205206 *
206- * @param {HTMLElement } [$newSelect=document.body] - object to append the select to
207+ * Changed 2026 Jan -- returns bool about whether it succeeded (does not call
208+ * onAccessFailure)
209+ *
210+ * @param {HTMLElement } newSelect - object to append the select to
207211 * @param {Object } [options] - see createSelector for details
208- * @returns {HTMLElement|undefined } DOM object containing the select tag, or undefined if Jazz cannot be loaded.
212+ * @returns {boolean } DOM object containing the select tag, or undefined if Jazz cannot be loaded.
209213 */
210- export function createJazzSelector ( newSelect : HTMLElement , options : MIDISelectorOptions = { } ) : HTMLElement | undefined {
214+ export function createJazzSelector (
215+ newSelect : HTMLElement ,
216+ options : MIDISelectorOptions = { } ,
217+ ) : boolean {
211218 const params : MIDISelectorOptions = {
219+ autoUpdate : false , // Jazz cannot autoUpdate
212220 onsuccess : undefined ,
213221 oninputsuccess : undefined ,
214222 oninputempty : undefined ,
223+ onAccessFailure : undefined ,
215224 } ;
216225 common . merge ( params , options ) ;
217226
218227 const jazzPlugin : Jazz = createPlugin ( ) ;
219- if ( jazzPlugin === undefined ) {
220- return undefined ;
228+ if ( ! jazzPlugin ) {
229+ return false ;
221230 }
222231
223232 newSelect . addEventListener ( 'change' , e => {
@@ -240,7 +249,7 @@ export function createJazzSelector(newSelect: HTMLElement, options: MIDISelector
240249 const noneAppendOption = < HTMLOptionElement > to_el ( "<option value='None'>None selected</option>" ) ;
241250 newSelect . appendChild ( noneAppendOption ) ;
242251
243- let anySelected = false ;
252+ let anySelected : boolean = false ;
244253 const allAppendOptions : HTMLOptionElement [ ] = [ ] ;
245254 for ( let i = 0 ; i < midiOptions . length ; i ++ ) {
246255 const appendOption = < HTMLOptionElement > to_el (
@@ -258,7 +267,7 @@ export function createJazzSelector(newSelect: HTMLElement, options: MIDISelector
258267 // console.log(appendOption);
259268 newSelect . appendChild ( appendOption ) ;
260269 }
261- if ( anySelected === false && midiOptions . length > 0 ) {
270+ if ( ! anySelected && midiOptions . length > 0 ) {
262271 allAppendOptions [ 0 ] . setAttribute ( 'selected' , 'true' ) ;
263272 webmidi . selectedJazzInterface = jazzPlugin . MidiInOpen (
264273 midiOptions [ 0 ] ,
@@ -268,15 +277,13 @@ export function createJazzSelector(newSelect: HTMLElement, options: MIDISelector
268277 } else {
269278 noneAppendOption . setAttribute ( 'selected' , 'true' ) ;
270279 }
271- if ( params . onsuccess !== undefined ) {
272- params . onsuccess ( ) ;
273- }
274- if ( anySelected === true && params . oninputsuccess !== undefined ) {
275- params . oninputsuccess ( ) ;
276- } else if ( anySelected === false && params . oninputempty !== undefined ) {
277- params . oninputempty ( ) ;
280+ params . onsuccess ?.( ) ;
281+ if ( anySelected ) {
282+ params . oninputsuccess ?.( ) ;
283+ } else {
284+ params . oninputempty ?.( ) ;
278285 }
279- return newSelect ;
286+ return true ;
280287}
281288
282289/**
@@ -319,7 +326,7 @@ interface MIDISelectorOptions {
319326 /**
320327 * Function to call on all successful port queries.
321328 */
322- onsuccess ?: Function ;
329+ onsuccess ?: ( ) => void ;
323330
324331 /**
325332 * Function to call if port query is successful and at least one input device exists.
@@ -330,6 +337,11 @@ interface MIDISelectorOptions {
330337 * Function to call if port query is successful but no input devices are found.
331338 */
332339 oninputempty ?: Function ;
340+
341+ /**
342+ * On access failure
343+ */
344+ onAccessFailure ?: ( e : DOMException ) => void ;
333345}
334346
335347
@@ -342,30 +354,40 @@ interface MIDISelectorOptions {
342354export function createSelector (
343355 where : HTMLElement ,
344356 options : MIDISelectorOptions = { }
345- ) : HTMLSelectElement {
357+ ) : HTMLSelectElement | undefined {
346358 let existingMidiSelect = false ;
359+ const midiSelectDiv = < HTMLDivElement > common . coerceHTMLElement ( where ) ;
360+
347361 const params : MIDISelectorOptions = {
348362 autoUpdate : true ,
349363 onsuccess : undefined ,
350364 oninputsuccess : undefined ,
351365 oninputempty : undefined ,
366+ onAccessFailure : ( e : DOMException ) => { midiSelectDiv . innerHTML = e . message ; } ,
352367 } ;
353368 common . merge ( params , options ) ;
354- const midiSelectDiv = < HTMLDivElement > common . coerceHTMLElement ( where ) ;
355369
356370 let newSelect : HTMLSelectElement ;
357- const foundMidiSelects = midiSelectDiv . querySelectorAll ( 'select#midiInSelect' ) ;
371+ const foundMidiSelects = midiSelectDiv . querySelectorAll < HTMLSelectElement > (
372+ 'select#midiInSelect'
373+ ) ;
358374 if ( foundMidiSelects . length > 0 ) {
359- newSelect = < HTMLSelectElement > foundMidiSelects [ 0 ] ;
375+ newSelect = foundMidiSelects [ 0 ] ;
360376 existingMidiSelect = true ;
361377 } else {
362- newSelect = < HTMLSelectElement > to_el ( '<select id="midiInSelect"></select>' ) ;
378+ newSelect = to_el < HTMLSelectElement > ( '<select id="midiInSelect"></select>' ) ;
363379 midiSelectDiv . appendChild ( newSelect ) ;
364380 }
365381 webmidi . select = newSelect ;
366382
367- if ( navigator . requestMIDIAccess === undefined ) {
368- createJazzSelector ( newSelect , params ) ;
383+ if ( ! navigator . requestMIDIAccess ) {
384+ const jazz_success = createJazzSelector ( newSelect , params ) ;
385+ if ( ! jazz_success ) {
386+ params . onAccessFailure ?.( new DOMException (
387+ 'Your browser does not support MIDI input; please use Chrome or Edge.' ,
388+ 'NotSupportedError'
389+ ) ) ;
390+ }
369391 } else {
370392 if ( ! existingMidiSelect ) {
371393 newSelect . addEventListener ( 'change' , e => selectionChanged ( e ) ) ;
@@ -379,24 +401,14 @@ export function createSelector(
379401 }
380402 const changeEvent = new Event ( 'change' , { bubbles : true } ) ;
381403 webmidi . select . dispatchEvent ( changeEvent ) ;
382- if ( params . onsuccess !== undefined ) {
383- params . onsuccess ( ) ;
384- }
385- if (
386- webmidi . selectedInputPort !== 'None'
387- && params . oninputsuccess !== undefined
388- ) {
389- params . oninputsuccess ( ) ;
390- } else if (
391- webmidi . selectedInputPort === 'None'
392- && params . oninputempty !== undefined
393- ) {
394- params . oninputempty ( ) ;
404+ params . onsuccess ?.( ) ;
405+ if ( webmidi . selectedInputPort !== 'None' ) {
406+ params . oninputsuccess ?.( ) ;
407+ } else {
408+ params . oninputempty ?.( ) ;
395409 }
396410 } ,
397- e => {
398- midiSelectDiv . innerHTML = e . message ;
399- }
411+ e => params . onAccessFailure ?.( e ) ,
400412 ) ;
401413 }
402414 miditools . clearOldChords ( ) ; // starts the chord checking process.
0 commit comments