@@ -132,13 +132,18 @@ function MusicKeyboard(activity) {
132132 * @type {Array }
133133 */
134134 this . idContainer = [ ] ;
135-
135+
136136 /**
137137 * Flag to track tick status.
138138 * @type {boolean }
139139 */
140140 this . tick = false ;
141141
142+ /** Flag to track if the metronome is on.
143+ * @type {boolean }
144+ */
145+ this . metronomeInterval = false ;
146+
142147 /**
143148 * Meter arguments.
144149 * @type {Array }
@@ -164,6 +169,12 @@ function MusicKeyboard(activity) {
164169 */
165170 this . instrumentMapper = { } ;
166171
172+ /**
173+ * Flag to track first note while using metronome.
174+ * @type {boolean }
175+ */
176+ this . firstNote = false ;
177+
167178 /**
168179 * Array of selected notes.
169180 * @type {Array }
@@ -260,37 +271,47 @@ function MusicKeyboard(activity) {
260271 * Adds keyboard shortcuts for triggering musical notes.
261272 */
262273 this . addKeyboardShortcuts = function ( ) {
263- // ;
274+
264275 let duration = 0 ;
265276 const startTime = { } ;
266277 const temp1 = { } ;
267278 const temp2 = { } ;
268279 const current = new Set ( ) ;
269280
281+ /**
282+ * Gets the ID of the musical note associated with a keyboard event.
283+ * @param {KeyboardEvent } event - The keyboard event.
284+ * @returns {string } The ID of the musical note.
285+ */
286+ const __getNoteId = ( event ) => {
287+ let id ;
288+ const key = event . keyCode ;
289+
290+ if ( WHITEKEYS . includes ( key ) ) {
291+ id = `whiteRow${ WHITEKEYS . indexOf ( key ) } ` ;
292+ } else if ( BLACKKEYS . includes ( key ) ) {
293+ const i = BLACKKEYS . indexOf ( key ) ;
294+ if ( [ 2 , 6 , 9 , 13 , 16 , 20 ] . includes ( i ) ) return null ;
295+ id = `blackRow${ i } ` ;
296+ } else if ( HERTZKEYS . includes ( key ) ) {
297+ id = `hertzRow${ HERTZKEYS . indexOf ( key ) } ` ;
298+ } else if ( key === SPACE ) {
299+ id = 'rest' ;
300+ }
301+
302+ return id ;
303+ } ;
304+
270305 /**
271306 * Handles the start of a musical note when a keyboard key is pressed.
272307 * @param {KeyboardEvent } event - The keyboard event.
273308 */
274309 const __startNote = ( event ) => {
275- let i , id ;
276- if ( WHITEKEYS . includes ( event . keyCode ) ) {
277- i = WHITEKEYS . indexOf ( event . keyCode ) ;
278- id = "whiteRow" + i . toString ( ) ;
279- } else if ( BLACKKEYS . includes ( event . keyCode ) ) {
280- i = BLACKKEYS . indexOf ( event . keyCode ) ;
281- if ( [ 2 , 6 , 9 , 13 , 16 , 20 ] . includes ( i ) ) return ;
282- id = "blackRow" + i . toString ( ) ;
283- } else if ( HERTZKEYS . includes ( event . keyCode ) ) {
284- i = HERTZKEYS . indexOf ( event . keyCode ) ;
285- id = "hertzRow" + i . toString ( ) ;
286- } else if ( SPACE == event . keyCode ) {
287- id = "rest" ;
288- }
310+ const id = __getNoteId ( event ) ;
289311
290312 const ele = docById ( id ) ;
291313 if ( ! ( id in startTime ) ) {
292- const startDate = new Date ( ) ;
293- startTime [ id ] = startDate . getTime ( ) ;
314+ startTime [ id ] = new Date ( ) . getTime ( ) ;
294315 }
295316
296317 if ( ele !== null && ele !== undefined ) {
@@ -309,7 +330,6 @@ function MusicKeyboard(activity) {
309330 }
310331
311332 if ( id == "rest" ) {
312- this . endTime = undefined ;
313333 return ;
314334 }
315335
@@ -322,31 +342,28 @@ function MusicKeyboard(activity) {
322342 null
323343 ) ;
324344
325- if ( this . tick ) {
345+ if ( this . tick && this . endTime !== undefined && this . firstNote ) {
326346 let restDuration = ( startTime [ id ] - this . endTime ) / 1000.0 ;
327-
328- restDuration /= 60 ; // time in minutes
347+ restDuration /= 60 ; // Convert time to minutes
329348 restDuration *= this . bpm ;
330349 restDuration *= this . meterArgs [ 1 ] ;
331-
332350 restDuration = parseFloat ( ( Math . round ( restDuration * unit ) / unit ) . toFixed ( 4 ) ) ;
351+ const EPSILON = 0.0600 ;
333352
334- if ( restDuration === 0 ) {
335- restDuration = 0 ;
336- } else {
353+ if ( restDuration > EPSILON && current . size === 0 ) {
337354 this . _notesPlayed . push ( {
338355 startTime : this . endTime ,
339356 noteOctave : "R" ,
340357 objId : null ,
341358 duration : parseFloat ( restDuration )
342359 } ) ;
343- //this._createTable();
344360 }
345361 }
346- this . endTime = undefined ;
362+ this . firstNote = true ;
347363 }
348364 } ;
349365
366+
350367 /**
351368 * Handles the keyboard key down event to start playing musical notes.
352369 * @param {KeyboardEvent } event - The keyboard event triggered when a key is pressed down.
@@ -365,25 +382,11 @@ function MusicKeyboard(activity) {
365382 * @param {KeyboardEvent } event - The keyboard event triggered when a key is released.
366383 */
367384 const __endNote = ( event ) => {
368- let i , id ;
369- if ( WHITEKEYS . includes ( event . keyCode ) ) {
370- i = WHITEKEYS . indexOf ( event . keyCode ) ;
371- id = "whiteRow" + i . toString ( ) ;
372- } else if ( BLACKKEYS . includes ( event . keyCode ) ) {
373- i = BLACKKEYS . indexOf ( event . keyCode ) ;
374- if ( [ 2 , 6 , 9 , 13 , 16 , 20 ] . includes ( i ) ) return ;
375- id = "blackRow" + i . toString ( ) ;
376- } else if ( HERTZKEYS . includes ( event . keyCode ) ) {
377- i = HERTZKEYS . indexOf ( event . keyCode ) ;
378- id = "hertzRow" + i . toString ( ) ;
379- } else if ( SPACE == event . keyCode ) {
380- id = "rest" ;
381- }
382-
385+ const id = __getNoteId ( event ) ;
383386 const ele = docById ( id ) ;
384387 const newDate = new Date ( ) ;
385- this . endTime = newDate . getTime ( ) ;
386- duration = ( this . endTime - startTime [ id ] ) / 1000.0 ;
388+ const noteEndTime = newDate . getTime ( ) ;
389+ duration = ( noteEndTime - startTime [ id ] ) / 1000.0 ;
387390
388391 if ( ele !== null && ele !== undefined ) {
389392 if ( id . includes ( "blackRow" ) ) {
@@ -394,31 +397,29 @@ function MusicKeyboard(activity) {
394397
395398 // no = ele.getAttribute("alt").split("__")[2];
396399
397- duration /= 60 ;
398- duration *= this . bpm ;
399- duration *= this . meterArgs [ 1 ] ;
400-
401- duration = parseFloat ( ( Math . round ( duration * unit ) / unit ) . toFixed ( 4 ) ) ;
400+ let processedDuration = duration / 60 ;
401+ processedDuration *= this . bpm ;
402+ processedDuration *= this . meterArgs [ 1 ] ;
403+ processedDuration = parseFloat ( ( Math . round ( processedDuration * unit ) / unit ) . toFixed ( 4 ) ) ;
402404
403- if ( duration === 0 ) {
404- duration = 1 / unit ;
405- } else if ( duration < 0 ) {
406- duration = - duration ;
405+ if ( processedDuration <= 0 ) {
406+ processedDuration = 1 / unit ;
407407 }
408+
408409 if ( id == "rest" ) {
409410 this . _notesPlayed . push ( {
410411 startTime : startTime [ id ] ,
411412 noteOctave : "R" ,
412413 objId : null ,
413- duration : parseFloat ( duration )
414+ duration : parseFloat ( processedDuration )
414415 } ) ;
415416 } else {
416417 this . activity . logo . synth . stopSound ( 0 , this . instrumentMapper [ id ] , temp2 [ id ] ) ;
417418 this . _notesPlayed . push ( {
418419 startTime : startTime [ id ] ,
419420 noteOctave : temp2 [ id ] ,
420421 objId : id ,
421- duration : duration ,
422+ duration : processedDuration ,
422423 voice : this . instrumentMapper [ id ] ,
423424 blockNumber : this . blockNumberMapper [ id ]
424425 } ) ;
@@ -448,6 +449,7 @@ function MusicKeyboard(activity) {
448449 docById ( "mkbOuterDiv" ) . style . width = w + "px" ;
449450 docById ( "mkbInnerDiv" ) . style . height = "100%" ;
450451 }
452+ this . endTime = noteEndTime ;
451453 delete startTime [ id ] ;
452454 delete temp1 [ id ] ;
453455 delete temp2 [ id ] ;
@@ -673,6 +675,10 @@ function MusicKeyboard(activity) {
673675 myNode . innerHTML = "" ;
674676 }
675677
678+ this . tick = false ;
679+ this . firstNote = false ;
680+ this . metronomeON = false ;
681+
676682 selectedNotes = [ ] ;
677683 if ( this . loopTick ) this . loopTick . stop ( ) ;
678684 docById ( "wheelDivptm" ) . style . display = "none" ;
@@ -761,24 +767,74 @@ function MusicKeyboard(activity) {
761767 */
762768 this . tickButton = widgetWindow . addButton ( "metronome.svg" , ICONSIZE , _ ( "Metronome" ) ) ;
763769 this . tickButton . onclick = ( ) => {
764- if ( this . tick ) {
770+ if ( this . metronomeInterval || this . metronomeON ) {
771+ // Turn off metronome
772+ this . tickButton . style . removeProperty ( "background" ) ;
773+
774+ if ( this . tick && this . loopTick ) {
775+ this . loopTick . stop ( ) ;
776+ }
765777 this . tick = false ;
766- this . loopTick . stop ( ) ;
778+ this . firstNote = false ;
779+ this . metronomeON = false ;
780+
781+ const countdownContainer = docById ( "countdownContainer" ) ;
782+ if ( countdownContainer ) {
783+ countdownContainer . remove ( ) ;
784+ }
785+ if ( this . metronomeInterval ) {
786+ clearInterval ( this . metronomeInterval ) ;
787+ this . metronomeInterval = null ;
788+ }
789+
767790 } else {
768- this . tick = true ;
769- this . activity . logo . synth . loadSynth ( 0 , "cow bell" ) ;
770- this . loopTick = this . activity . logo . synth . loop (
771- 0 ,
772- "cow bell" ,
773- "C5" ,
774- 1 / 64 ,
775- 0 ,
776- this . bpm || 90 ,
777- 0.07
778- ) ;
779- setTimeout ( ( ) => {
791+ // Turn on metronome
792+ this . metronomeON = true ;
793+ this . tickButton . style . background = platformColor . orange ;
794+
795+ const winBody = document . getElementsByClassName ( "wfbWidget" ) [ 0 ] ;
796+ const countdownContainer = document . createElement ( "div" ) ;
797+ countdownContainer . id = "countdownContainer" ;
798+
799+ const countdownDisplay = document . createElement ( "div" ) ;
800+ countdownDisplay . id = "countdownDisplay" ;
801+ countdownDisplay . textContent = "3" ;
802+ countdownContainer . appendChild ( countdownDisplay ) ;
803+ winBody . appendChild ( countdownContainer ) ;
804+
805+ // Start countdown
806+ let count = 3 ;
807+ this . metronomeInterval = setInterval ( ( ) => {
808+ count -- ;
809+
810+ if ( count === 0 ) {
811+ clearInterval ( this . metronomeInterval ) ;
812+ this . metronomeInterval = null ;
813+
814+ countdownContainer . remove ( ) ;
815+
816+ if ( this . metronomeON ) {
817+ this . tick = true ;
818+ this . activity . logo . synth . loadSynth ( 0 , "cow bell" ) ;
819+ this . loopTick = this . activity . logo . synth . loop (
820+ 0 ,
821+ "cow bell" ,
822+ "C5" ,
823+ 1 / 64 ,
824+ 0 ,
825+ this . bpm || 90 ,
826+ 0.07
827+ ) ;
828+ }
829+ } else {
830+ countdownDisplay . textContent = count ;
831+ }
832+ } , 1000 ) ;
833+
834+ // Start audio
835+ if ( this . metronomeON ) {
780836 this . activity . logo . synth . start ( ) ;
781- } , 500 ) ;
837+ }
782838 }
783839 } ;
784840
0 commit comments