@@ -173,6 +173,7 @@ if (_THIS_IS_MUSIC_BLOCKS_) {
173173 "widgets/sampler" ,
174174 "activity/lilypond" ,
175175 "activity/abc" ,
176+ "activity/midi" ,
176177 "activity/mxml"
177178 ] ;
178179 MYDEFINES = MYDEFINES . concat ( MUSICBLOCKS_EXTRAS ) ;
@@ -4227,348 +4228,6 @@ class Activity {
42274228 return closest . value . split ( '/' ) . map ( Number ) ;
42284229 }
42294230
4230- this . transcribeMidi = async function ( midi ) {
4231-
4232- const DRUM_MIDI_MAP = {
4233- 38 : [ "snare drum" ] ,
4234- 36 : [ "kick drum" ] ,
4235- 41 : [ "tom tom" ] ,
4236- 43 : [ "floor tom tom" ] ,
4237- 47 : [ "cup drum" ] ,
4238- 50 : [ "darbuka drum" ] ,
4239- 56 : [ "japanese drum" , "cow bell" ] ,
4240- 42 : [ "hi hat" ] ,
4241- 53 : [ "ride bell" ] ,
4242- 81 : [ "triangle bell" ] ,
4243- 69 : [ "finger cymbals" ] ,
4244- 82 : [ "chime" ] ,
4245- 52 : [ "gong" ] ,
4246- 55 : [ "clang" ] ,
4247- 49 : [ "crash" ] ,
4248- 39 : [ "clap" ] ,
4249- 40 : [ "slap" ] ,
4250- 88 : [ "raindrop" ]
4251- } ;
4252-
4253-
4254- let currentMidi = midi ;
4255- let jsONON = [ ] ;
4256- let actionBlockCounter = 0 ; // Counter for action blocks
4257- let actionBlockNames = [ ] ; // Array to store action block names
4258- let totalnoteblockCount = 0 ; // Initialize noteblock counter
4259- let noteblockCount = 0 ;
4260- const MAX_NOTEBLOCKS = 200 ;
4261- let shortestNoteDenominator = 0 ;
4262- let offset = 100 ;
4263- let stopProcessing = false ;
4264- let trackCount = 0 ;
4265- let actionBlockPerTrack = [ ] ;
4266- let instruments = [ ] ;
4267- let defaultTempo = 90 ;
4268- let currentMidiTempoBpm = currentMidi . header . tempos ;
4269- if ( currentMidiTempoBpm && currentMidiTempoBpm . length > 0 ) {
4270- currentMidiTempoBpm = Math . round ( currentMidiTempoBpm [ 0 ] . bpm ) ;
4271- } else {
4272- currentMidiTempoBpm = defaultTempo ;
4273- }
4274-
4275- let defaultTimeSignature = [ 4 , 4 ] ;
4276- let currentMidiTimeSignature = currentMidi . header . timeSignatures ;
4277- if ( currentMidiTimeSignature && currentMidiTimeSignature . length > 0 ) {
4278- currentMidiTimeSignature = currentMidiTimeSignature [ 0 ] . timeSignature ;
4279- } else {
4280- currentMidiTimeSignature = defaultTimeSignature ;
4281- }
4282-
4283- let precurssionFlag = false ;
4284- const isPercussion = [ ] ;
4285- // console.log("tempoBpm is: ", currentMidiTempoBpm);
4286- // console.log("tempo is : ",currentMidi.header.tempos);
4287- // console.log("time signatures are: ", currentMidi.header.timeSignatures);
4288- console . log ( currentMidi ) ;
4289- currentMidi . tracks . forEach ( ( track , trackIndex ) => {
4290- let k = 0 ;
4291- if ( stopProcessing ) return ; // Exit if flag is set
4292- if ( ! track . notes . length ) return ;
4293- let r = jsONON . length ;
4294- // console.log("notes: ",track.notes);
4295- // find matching instrument
4296- let instrument = "electronic synth" ;
4297- if ( track . instrument . name && ! track . instrument . percussion ) {
4298- for ( let voices of VOICENAMES ) {
4299- if ( track . instrument . name . indexOf ( voices [ 1 ] ) > - 1 ) {
4300- instrument = voices [ 0 ] ;
4301- }
4302- }
4303- } else if ( track . instrument . percussion ) {
4304- precurssionFlag = true ;
4305- }
4306- actionBlockPerTrack [ trackCount ] = 0 ;
4307- instruments [ trackCount ] = instrument ;
4308-
4309- let actionBlockName = `track${ trackCount } chunk${ actionBlockCounter } ` ;
4310-
4311- jsONON . push (
4312- [ r , [ "action" , { collapsed : false } ] , 150 , 100 , [ null , r + 1 , r + 2 , null ] ] ,
4313- [ r + 1 , [ "text" , { value : actionBlockName } ] , 0 , 0 , [ r ] ]
4314- ) ;
4315-
4316- let sched = [ ] ;
4317- isPercussion . push ( track . instrument . percussion && ( track . channel === 9 || track . channel === 10 ) ) ;
4318-
4319- track . notes . forEach ( ( note , index ) => {
4320- let name = note . name ;
4321- let start = Math . round ( note . time * 100 ) / 100 ;
4322- let end = Math . round ( ( note . time + note . duration ) * 100 ) / 100 ;
4323-
4324- if ( note . duration === 0 ) return ; // Skip zero-duration notes
4325-
4326- let lastNote = sched [ sched . length - 1 ] ;
4327-
4328- if ( index === 0 && start > 0 ) {
4329- sched . push ( { start : 0 , end : start , notes : [ "R" ] } ) ;
4330- }
4331-
4332- if ( lastNote && lastNote . start === start && lastNote . end === end ) {
4333- lastNote . notes . push ( name ) ;
4334- return ;
4335- }
4336-
4337- if ( lastNote && lastNote . start <= start && lastNote . end > start ) {
4338- let prevNotes = [ ...lastNote . notes ] ;
4339- let oldEnd = lastNote . end ;
4340-
4341- lastNote . end = start ;
4342-
4343- sched . push ( { start : start , end : end , notes : [ ...prevNotes , name ] } ) ;
4344-
4345- if ( oldEnd > end ) {
4346- sched . push ( { start : end , end : oldEnd , notes : prevNotes } ) ;
4347- }
4348- return ;
4349- }
4350-
4351- if ( lastNote && lastNote . end < start ) {
4352- sched . push ( { start : lastNote . end , end : start , notes : [ "R" ] } ) ;
4353- }
4354-
4355- // Add the new note to the schedule
4356- sched . push ( { start : start , end : end , notes : [ name ] } ) ;
4357- } ) ;
4358-
4359-
4360- let noteSum = 0 ;
4361- let currentActionBlock = [ ] ;
4362-
4363- let addNewActionBlock = ( isLastBlock = false ) => {
4364- let r = jsONON . length ;
4365- let actionBlockName = `track${ trackCount } chunk${ actionBlockCounter } ` ;
4366- actionBlockNames . push ( actionBlockName ) ;
4367- actionBlockPerTrack [ trackCount ] ++ ;
4368- if ( k == 0 ) {
4369- jsONON . push (
4370- ...currentActionBlock
4371- ) ;
4372- k = 1 ;
4373- } else {
4374- let settimbreIndex = r ;
4375- // Adjust the first note block's top connection to settimbre
4376- currentActionBlock [ 0 ] [ 4 ] [ 0 ] = settimbreIndex ;
4377- jsONON . push (
4378- [ r , [ "action" , { collapsed : false } ] , 100 + offset , 100 + offset , [ null , r + 1 , settimbreIndex + 2 , null ] ] ,
4379- [ r + 1 , [ "text" , { value : actionBlockName } ] , 0 , 0 , [ r ] ] ,
4380- ...currentActionBlock
4381- ) ;
4382-
4383- }
4384- if ( isLastBlock ) {
4385- let lastIndex = jsONON . length - 1 ;
4386- // Set the last hidden block's second value to null
4387- jsONON [ lastIndex ] [ 4 ] [ 1 ] = null ;
4388- }
4389-
4390- currentActionBlock = [ ] ;
4391- actionBlockCounter ++ ; // Increment the action block counter
4392- offset += 100 ;
4393- } ;
4394- //Using for loop for finding the shortest note value
4395- for ( let j in sched ) {
4396- let st = sched [ j ] . start ;
4397- let ed = sched [ j ] . end ;
4398- let dur = ed - st ;
4399- let temp = this . getClosestStandardNoteValue ( dur * 3 / 8 ) ;
4400- shortestNoteDenominator = Math . max ( shortestNoteDenominator , temp [ 1 ] ) ;
4401- }
4402-
4403- for ( let i in sched ) {
4404- if ( stopProcessing ) break ; // Exit inner loop if flag is set
4405- let { notes, start, end } = sched [ i ] ;
4406- let duration = end - start ;
4407- noteSum += duration ;
4408- let isLastNoteInBlock = ( noteSum >= 16 ) || ( noteblockCount > 0 && noteblockCount % 24 === 0 ) ;
4409- if ( isLastNoteInBlock ) {
4410- totalnoteblockCount += noteblockCount ;
4411- noteblockCount = 0 ;
4412- noteSum = 0 ;
4413- }
4414- let isLastNoteInSched = ( i == sched . length - 1 ) ;
4415- let last = isLastNoteInBlock || isLastNoteInSched ;
4416- let first = ( i == 0 ) ;
4417- let val = jsONON . length + currentActionBlock . length ;
4418- let getPitch = ( x , notes , prev ) => {
4419- let ar = [ ] ;
4420- if ( notes [ 0 ] == "R" ) {
4421- ar . push (
4422- [ x , "rest2" , 0 , 0 , [ prev , null ] ]
4423- ) ;
4424- } else if ( precurssionFlag ) {
4425- let drumname = DRUM_MIDI_MAP [ track . notes [ 0 ] . midi ] [ 0 ] || "kick drum" ;
4426- ar . push (
4427- [ x , "playdrum" , 0 , 0 , [ first ? prev : x - 1 , x + 1 , null ] ] ,
4428- [ x + 1 , [ "drumname" , { "value" :drumname } ] , 0 , 0 , [ x ] ] ,
4429- ) ;
4430- x += 2 ;
4431- } else {
4432- for ( let na in notes ) {
4433- let name = notes [ na ] ;
4434- let first = na == 0 ;
4435- let last = na == notes . length - 1 ;
4436- ar . push (
4437- [ x , "pitch" , 0 , 0 , [ first ? prev : x - 3 , x + 1 , x + 2 , last ? null : x + 3 ] ] ,
4438- [ x + 1 , [ "notename" , { "value" : name . substring ( 0 , name . length - 1 ) } ] , 0 , 0 , [ x ] ] ,
4439- [ x + 2 , [ "number" , { "value" : parseInt ( name [ name . length - 1 ] ) } ] , 0 , 0 , [ x ] ]
4440- ) ;
4441- x += 3 ;
4442- }
4443- }
4444- return ar ;
4445- } ;
4446- let obj = this . getClosestStandardNoteValue ( duration * 3 / 8 ) ;
4447- // let scalingFactor=1;
4448- // if(shortestNoteDenominator>32)
4449- // scalingFactor=shortestNoteDenominator/32;
4450-
4451- // if(obj[1]>=scalingFactor)
4452- // obj[1]=obj[1]/scalingFactor;
4453- // else
4454- // obj[0]=obj[0]*scalingFactor;
4455-
4456- // To get the reduced fraction for 4/2 to 2/1
4457- obj = this . getClosestStandardNoteValue ( obj [ 0 ] / obj [ 1 ] ) ;
4458-
4459- // Since we are going to add action block in the front later
4460- if ( k != 0 ) val = val + 2 ;
4461- let pitches = getPitch ( val + 5 , notes , val ) ;
4462- currentActionBlock . push (
4463- [ val , [ "newnote" , { "collapsed" : true } ] , 0 , 0 , [ first ? val - 2 : val - 1 , val + 1 , val + 4 , val + pitches . length + 5 ] ] ,
4464- [ val + 1 , "divide" , 0 , 0 , [ val , val + 2 , val + 3 ] ] ,
4465- [ val + 2 , [ "number" , { value : obj [ 0 ] } ] , 0 , 0 , [ val + 1 ] ] ,
4466- [ val + 3 , [ "number" , { value : obj [ 1 ] } ] , 0 , 0 , [ val + 1 ] ] ,
4467- [ val + 4 , "vspace" , 0 , 0 , [ val , val + 5 ] ] ,
4468- ) ;
4469- noteblockCount ++ ;
4470- pitches [ 0 ] [ 4 ] [ 0 ] = val + 4 ;
4471- currentActionBlock = currentActionBlock . concat ( pitches ) ;
4472-
4473- let newLen = jsONON . length + currentActionBlock . length ;
4474- if ( k != 0 ) newLen = newLen + 2 ;
4475- currentActionBlock . push (
4476- [ newLen , "hidden" , 0 , 0 , [ val , last ? null : newLen + 1 ] ]
4477- ) ;
4478- if ( isLastNoteInBlock || isLastNoteInSched ) {
4479- addNewActionBlock ( isLastNoteInSched ) ;
4480- }
4481-
4482- if ( totalnoteblockCount >= MAX_NOTEBLOCKS ) {
4483- this . textMsg ( "MIDI file is too large.. Generating only 100 noteblocks" ) ;
4484- stopProcessing = true ;
4485- break ;
4486- }
4487- }
4488-
4489- if ( currentActionBlock . length > 0 ) {
4490- addNewActionBlock ( true ) ;
4491- }
4492-
4493- trackCount ++ ;
4494- // console.log("current action block: ", currentActionBlock);
4495- // console.log("current json: ", jsONON);
4496- // console.log("noteblockCount: ", noteblockCount);
4497- // console.debug('finished when you see: "block loading finished "');
4498- document . body . style . cursor = "wait" ;
4499- } ) ;
4500-
4501- let len = jsONON . length ;
4502- let m = 0 ;
4503- let actionIndex = 0 ;
4504- console . log ( instruments ) ;
4505-
4506- for ( let i = 0 ; i < trackCount ; i ++ ) {
4507- let vspaceIndex = len + m + 6 ;
4508- let startIndex = len + m ;
4509- let flag = true ;
4510-
4511- if ( isPercussion [ i ] ) {
4512- jsONON . push (
4513- [ len + m , [ "start" , { collapsed : false } ] , 300 + offset , 100 , [ null , len + m + 14 + actionBlockPerTrack [ i ] , null ] ] ,
4514- [ len + m + 1 , "meter" , 0 , 0 , [ len + m + 14 + actionBlockPerTrack [ i ] , len + m + 2 , len + m + 3 , len + m + 6 ] ] ,
4515- [ len + m + 2 , [ "number" , { value : currentMidiTimeSignature [ 0 ] } ] , 0 , 0 , [ len + m + 1 ] ] ,
4516- [ len + m + 3 , "divide" , 0 , 0 , [ len + m + 1 , len + m + 4 , len + m + 5 ] ] ,
4517- [ len + m + 4 , [ "number" , { value : 1 } ] , 0 , 0 , [ len + m + 3 ] ] ,
4518- [ len + m + 5 , [ "number" , { value : currentMidiTimeSignature [ 1 ] } ] , 0 , 0 , [ len + m + 3 ] ] ,
4519- [ len + m + 6 , "vspace" , 0 , 0 , [ len + m + 1 , len + m + 8 + actionBlockPerTrack [ i ] ] ] ,
4520- [ len + m + 7 , "hidden" , 0 , 0 , [ len + m + 13 + actionBlockPerTrack [ i ] , len + m + 8 ] ] ,
4521- ) ;
4522- flag = false ;
4523- m += 8 ;
4524- } else {
4525- jsONON . push (
4526- [ len + m , [ "start" , { collapsed : false } ] , 300 + offset , 100 , [ null , len + m + 16 + actionBlockPerTrack [ i ] , null ] ] ,
4527- [ len + m + 1 , "meter" , 0 , 0 , [ len + m + 16 + actionBlockPerTrack [ i ] , len + m + 2 , len + m + 3 , len + m + 6 ] ] ,
4528- [ len + m + 2 , [ "number" , { value : currentMidiTimeSignature [ 0 ] } ] , 0 , 0 , [ len + m + 1 ] ] ,
4529- [ len + m + 3 , "divide" , 0 , 0 , [ len + m + 1 , len + m + 4 , len + m + 5 ] ] ,
4530- [ len + m + 4 , [ "number" , { value : 1 } ] , 0 , 0 , [ len + m + 3 ] ] ,
4531- [ len + m + 5 , [ "number" , { value : currentMidiTimeSignature [ 1 ] } ] , 0 , 0 , [ len + m + 3 ] ] ,
4532- [ len + m + 6 , "vspace" , 0 , 0 , [ len + m + 1 , len + m + 10 + actionBlockPerTrack [ i ] ] ] ,
4533- [ len + m + 7 , "settimbre" , 0 , 0 , [ len + m + 15 + actionBlockPerTrack [ i ] , len + m + 8 , len + m + 10 , len + m + 9 ] ] ,
4534- [ len + m + 8 , [ "voicename" , { value : instruments [ i ] } ] , 0 , 0 , [ len + m + 7 ] ] ,
4535- [ len + m + 9 , "hidden" , 0 , 0 , [ len + m + 7 , null ] ]
4536- ) ;
4537- m += 10 ;
4538- }
4539-
4540- for ( let j = 0 ; j < actionBlockPerTrack [ i ] ; j ++ ) {
4541- jsONON . push (
4542- [ len + m , [ "nameddo" , { value : actionBlockNames [ actionIndex ] } ] , 0 , 0 , [ flag ?len + m - 3 :len + m - 1 , len + m + 1 ] ]
4543- ) ;
4544- m ++ ;
4545- flag = false ;
4546- actionIndex ++ ;
4547- }
4548- jsONON [ len + m - 1 ] [ 4 ] [ 1 ] = null ;
4549- let setBpmIndex = jsONON . length ;
4550- jsONON . push (
4551- [ setBpmIndex , [ "setbpm3" ] , 0 , 0 , [ vspaceIndex , setBpmIndex + 1 , setBpmIndex + 2 , setBpmIndex + 5 ] ] ,
4552- [ setBpmIndex + 1 , [ "number" , { value : currentMidiTempoBpm } ] , 0 , 0 , [ setBpmIndex ] ] ,
4553- [ setBpmIndex + 2 , "divide" , 0 , 0 , [ setBpmIndex , setBpmIndex + 3 , setBpmIndex + 4 ] ] ,
4554- [ setBpmIndex + 3 , [ "number" , { value : 1 } ] , 0 , 0 , [ setBpmIndex + 2 ] ] ,
4555- [ setBpmIndex + 4 , [ "number" , { value : currentMidiTimeSignature [ 1 ] } ] , 0 , 0 , [ setBpmIndex + 2 ] ] ,
4556- [ setBpmIndex + 5 , "vspace" , 0 , 0 , [ setBpmIndex , vspaceIndex + 1 ] ] ,
4557- ) ;
4558- m += 6 ;
4559- jsONON . push (
4560- [ len + m , "setturtlename2" , 0 , 0 , [ startIndex , len + m + 1 , startIndex + 1 ] ] ,
4561- [ len + m + 1 , [ "text" , { value : `track${ i } ` } ] , 0 , 0 , [ len + m ] ]
4562- ) ;
4563- m += 2 ;
4564-
4565- }
4566-
4567- this . blocks . loadNewBlocks ( jsONON ) ;
4568- // this.textMsg("MIDI import is not currently precise. Consider changing the speed with the Beats Per Minute block or modifying note value with the Multiply Note Value block");
4569- return null ;
4570- } ;
4571-
45724231 /**
45734232 * Loads MB project from Planet.
45744233 * @param projectID {Planet project ID}
@@ -6776,7 +6435,7 @@ class Activity {
67766435 const midi = new Midi ( e . target . result ) ;
67776436 // eslint-disable-next-line no-console
67786437 console . debug ( midi ) ;
6779- this . transcribeMidi ( midi ) ;
6438+ transcribeMidi ( midi ) ;
67806439 } ;
67816440
67826441 const file = that . fileChooser . files [ 0 ] ;
@@ -6876,7 +6535,7 @@ class Activity {
68766535 const midi = new Midi ( e . target . result )
68776536 // eslint-disable-next-line no-console
68786537 console . debug ( midi ) ;
6879- this . transcribeMidi ( midi ) ;
6538+ transcribeMidi ( midi ) ;
68806539 }
68816540
68826541 // Music Block Parser from abc to MB
0 commit comments