Working page

Current implementation of the LiteWebSynth synthesizer core

/**
* Description: WebAudio Synthesizer Core.
*
* @package     LiteWebSynth
* @category    module js
* @author      Oleg Klenitsky <https://adminkov.bcr.by/midi/>
* @author      g200kg <https://github.com/g200kg/webaudio-tinysynth>
* @copyright
* @license     GPL-2.0-or-later
*/

( function(window){
"use strict";

function WebAudioTinySynthCore(target) {
	Object.assign(target,{
	  properties:{
			masterVol:  			{type:Number, value:0.5, observer:"setMasterVol"},
			reverbLev:  			{type:Number, value:0.3, observer:"setReverbLev"},

			channelsPlay: 		{type:Number, value:[], observer:"setChannelsPlay"},
			analysVol:    		{type:Number, value:1, observer:"setAnalysVol"},

			compThreshold:    {type:Number, value:-24, observer:"setCompThreshold"},
			compKnee:         {type:Number, value:30, observer:"setCompKnee"},
			compRatio:        {type:Number, value:12, observer:"setCompRatio"},
			compAttack:       {type:Number, value:0.003, observer:"setCompAttack"},
			compRelease:      {type:Number, value:0.25, observer:"setCompRelease"},

			convDuration: 		{type:Number, value:0, observer:"setConvDuration"},
			convDecay:    		{type:Number, value:0, observer:"setConvDecay"},

			quality:    			{type:Number, value:1, observer:"setQuality"},
			debug:      			{type:Number, value:0},
			src:        			{type:String, value:null, observer:"loadMIDIfromSrc"},
			loop:       			{type:Number, value:0},
			internalcontext: 	{type:Number, value:1},
			tsmode:     			{type:Number, value:0},
			voices:     			{type:Number, value:64},
			useReverb:  			{type:Number, value:1},
			/*@@gui*/
			width:      			{type:String, value:"290px", observer:"layout"},
			height:     			{type:String, value:"32px", observer:"layout"},
			graph:      			{type:Number, value:1},
			disabledrop: 			{type:Number, value:0},
			perfmon:    			{type:Number, value:0},
			/*@@guiEND*/
	  },
	  /*@@gui*/
	  layout:(()=>{
			this.canvas.style.width=this.width;
			this.canvas.style.height=this.height;
	  }),
	  /*@@guiEND*/
		arrNotes:["A0","A#0","B0",
		"C1","C#1","D1","D#1","E1","F1","F#1","G1","G#1","A1","A#1","B1",
		"C2","C#2","D2","D#2","E2","F2","F#2","G2","G#2","A2","A#2","B2",
		"C3","C#3","D3","D#3","E3","F3","F#3","G3","G#3","A3","A#3","B3",
		"C4","C#4","D4","D#4","E4","F4","F#4","G4","G#4","A4","A#4","B4",
		"C5","C#5","D5","D#5","E5","F5","F#5","G5","G#5","A5","A#5","B5",
		"C6","C#6","D6","D#6","E6","F6","F#6","G6","G#6","A6","A#6","B6",
		"C7","C#7","D7","D#7","E7","F7","F#7","G7","G#7","A7","A#7","B7",
		"C8"],
	  program:[
		// 1-8 : Piano
		{name:"Acoustic Grand Piano"},    {name:"Bright Acoustic Piano"},
		{name:"Electric Grand Piano"},    {name:"Honky-tonk Piano"},
		{name:"Electric Piano 1"},        {name:"Electric Piano 2"},
		{name:"Harpsichord"},             {name:"Clavi"},
		/* 9-16 : Chromatic Perc*/
		{name:"Celesta"},                 {name:"Glockenspiel"},
		{name:"Music Box"},               {name:"Vibraphone"},
		{name:"Marimba"},                 {name:"Xylophone"},
		{name:"Tubular Bells"},           {name:"Dulcimer"},
		/* 17-24 : Organ */
		{name:"Drawbar Organ"},           {name:"Percussive Organ"},
		{name:"Rock Organ"},              {name:"Church Organ"},
		{name:"Reed Organ"},              {name:"Accordion"},
		{name:"Harmonica"},               {name:"Tango Accordion"},
		/* 25-32 : Guitar */
		{name:"Acoustic Guitar nylon"},   {name:"Acoustic Guitar steel"},
		{name:"Electric Guitar jazz"},    {name:"Electric Guitar clean"},
		{name:"Electric Guitar muted"},   {name:"Overdriven Guitar"},
		{name:"Distortion Guitar"},       {name:"Guitar harmonics"},
		/* 33-40 : Bass */
		{name:"Acoustic Bass"},           {name:"Electric Bass finger"},
		{name:"Electric Bass pick"},      {name:"Fretless Bass"},
		{name:"Slap Bass 1"},             {name:"Slap Bass 2"},
		{name:"Synth Bass 1"},            {name:"Synth Bass 2"},
		/* 41-48 : Strings */
		{name:"Violin"},                  {name:"Viola"},
		{name:"Cello"},                   {name:"Contrabass"},
		{name:"Tremolo Strings"},         {name:"Pizzicato Strings"},
		{name:"Orchestral Harp"},         {name:"Timpani"},
		/* 49-56 : Ensamble */
		{name:"String Ensemble 1"},       {name:"String Ensemble 2"},
		{name:"SynthStrings 1"},          {name:"SynthStrings 2"},
		{name:"Choir Aahs"},              {name:"Voice Oohs"},
		{name:"Synth Voice"},             {name:"Orchestra Hit"},
		/* 57-64 : Brass */
		{name:"Trumpet"},                 {name:"Trombone"},
		{name:"Tuba"},                    {name:"Muted Trumpet"},
		{name:"French Horn"},             {name:"Brass Section"},
		{name:"SynthBrass 1"},            {name:"SynthBrass 2"},
		/* 65-72 : Reed */
		{name:"Soprano Sax"},             {name:"Alto Sax"},
		{name:"Tenor Sax"},               {name:"Baritone Sax"},
		{name:"Oboe"},                    {name:"English Horn"},
		{name:"Bassoon"},                 {name:"Clarinet"},
		/* 73-80 : Pipe */
		{name:"Piccolo"},                 {name:"Flute"},
		{name:"Recorder"},                {name:"Pan Flute"},
		{name:"Blown Bottle"},            {name:"Shakuhachi"},
		{name:"Whistle"},                 {name:"Ocarina"},
		/* 81-88 : SynthLead */
		{name:"Lead 1 (square)"},         {name:"Lead 2 (sawtooth)"},
		{name:"Lead 3 (calliope)"},       {name:"Lead 4 (chiff)"},
		{name:"Lead 5 (charang)"},        {name:"Lead 6 (voice)"},
		{name:"Lead 7 (fifths)"},         {name:"Lead 8 (bass + lead)"},
		/* 89-96 : SynthPad */
		{name:"Pad 1 (new age)"},         {name:"Pad 2 (warm)"},
		{name:"Pad 3 (polysynth)"},       {name:"Pad 4 (choir)"},
		{name:"Pad 5 (bowed)"},           {name:"Pad 6 (metallic)"},
		{name:"Pad 7 (halo)"},            {name:"Pad 8 (sweep)"},
		/* 97-104 : FX */
		{name:"FX 1 (rain)"},             {name:"FX 2 (soundtrack)"},
		{name:"FX 3 (crystal)"},          {name:"FX 4 (atmosphere)"},
		{name:"FX 5 (brightness)"},       {name:"FX 6 (goblins)"},
		{name:"FX 7 (echoes)"},           {name:"FX 8 (sci-fi)"},
		/* 105-112 : Ethnic */
		{name:"Sitar"},                   {name:"Banjo"},
		{name:"Shamisen"},                {name:"Koto"},
		{name:"Kalimba"},                 {name:"Bag pipe"},
		{name:"Fiddle"},                  {name:"Shanai"},
		/* 113-120 : Percussive */
		{name:"Tinkle Bell"},             {name:"Agogo"},
		{name:"Steel Drums"},             {name:"Woodblock"},
		{name:"Taiko Drum"},              {name:"Melodic Tom"},
		{name:"Synth Drum"},              {name:"Reverse Cymbal"},
		/* 121-128 : SE */
		{name:"Guitar Fret Noise"},       {name:"Breath Noise"},
		{name:"Seashore"},                {name:"Bird Tweet"},
		{name:"Telephone Ring"},          {name:"Helicopter"},
		{name:"Applause"},                {name:"Gunshot"},
	  ],
	  drummap:[
		// 35
		{name:"Acoustic Bass Drum"}, {name:"Bass Drum 1"},    {name:"Side Stick"},     {name:"Acoustic Snare"},
		{name:"Hand Clap"},          {name:"Electric Snare"}, {name:"Low Floor Tom"},  {name:"Closed Hi Hat"},
		{name:"High Floor Tom"},     {name:"Pedal Hi-Hat"},   {name:"Low Tom"},        {name:"Open Hi-Hat"},
		{name:"Low-Mid Tom"},        {name:"Hi-Mid Tom"},     {name:"Crash Cymbal 1"}, {name:"High Tom"},
		{name:"Ride Cymbal 1"},      {name:"Chinese Cymbal"}, {name:"Ride Bell"},      {name:"Tambourine"},
		{name:"Splash Cymbal"},      {name:"Cowbell"},        {name:"Crash Cymbal 2"}, {name:"Vibraslap"},
		{name:"Ride Cymbal 2"},      {name:"Hi Bongo"},       {name:"Low Bongo"},      {name:"Mute Hi Conga"},
		{name:"Open Hi Conga"},      {name:"Low Conga"},      {name:"High Timbale"},   {name:"Low Timbale"},
		{name:"High Agogo"},         {name:"Low Agogo"},      {name:"Cabasa"},         {name:"Maracas"},
		{name:"Short Whistle"},      {name:"Long Whistle"},   {name:"Short Guiro"},    {name:"Long Guiro"},
		{name:"Claves"},             {name:"Hi Wood Block"},  {name:"Low Wood Block"}, {name:"Mute Cuica"},
		{name:"Open Cuica"},         {name:"Mute Triangle"},  {name:"Open Triangle"}
	  ],
	  program1:[
		// 1-8 : Piano
		[{w:"sine",v:.4,d:0.7,r:0.1,},{w:"triangle",v:3,d:0.7,s:0.1,g:1,a:0.01,k:-1.2}],
		[{w:"triangle",v:0.4,d:0.7,r:0.1,},{w:"triangle",v:4,t:3,d:0.4,s:0.1,g:1,k:-1,a:0.01,}],
		[{w:"sine",d:0.7,r:0.1,},{w:"triangle",v:4,f:2,d:0.5,s:0.5,g:1,k:-1}],
		[{w:"sine",d:0.7,v:0.2,},{w:"triangle",v:4,t:3,f:2,d:0.3,g:1,k:-1,a:0.01,s:0.5,}],
		[{w:"sine",v:0.35,d:0.7,},{w:"sine",v:3,t:7,f:1,d:1,s:1,g:1,k:-.7}],
		[{w:"sine",v:0.35,d:0.7,},{w:"sine",v:8,t:7,f:1,d:0.5,s:1,g:1,k:-.7}],
		[{w:"sawtooth",v:0.34,d:2,},{w:"sine",v:8,f:0.1,d:2,s:1,r:2,g:1,}],
		[{w:"triangle",v:0.34,d:1.5,},{w:"square",v:6,f:0.1,d:1.5,s:0.5,r:2,g:1,}],
		/* 9-16 : Chromatic Perc*/
		[{w:"sine",d:0.3,r:0.3,},{w:"sine",v:7,t:11,d:0.03,g:1,}],
		[{w:"sine",d:0.3,r:0.3,},{w:"sine",v:11,t:6,d:0.2,s:0.4,g:1,}],
		[{w:"sine",v:0.2,d:0.3,r:0.3,},{w:"sine",v:11,t:5,d:0.1,s:0.4,g:1,}],
		[{w:"sine",v:0.2,d:0.6,r:0.6,},{w:"triangle",v:11,t:5,f:1,s:0.5,g:1,}],
		[{w:"sine",v:0.3,d:0.2,r:0.2,},{w:"sine",v:6,t:5,d:0.02,g:1,}],
		[{w:"sine",v:0.3,d:0.2,r:0.2,},{w:"sine",v:7,t:11,d:0.03,g:1,}],
		[{w:"sine",v:0.2,d:1,r:1,},{w:"sine",v:11,t:3.5,d:1,r:1,g:1,}],
		[{w:"triangle",v:0.2,d:0.5,r:0.2,},{w:"sine",v:6,t:2.5,d:0.2,s:0.1,r:0.2,g:1,}],
		/* 17-24 : Organ */
		[{w:"w9999",v:0.22,s:0.9,},{w:"w9999",v:0.22,t:2,f:2,s:0.9,}],
		[{w:"w9999",v:0.2,s:1,},{w:"sine",v:11,t:6,f:2,s:0.1,g:1,h:0.006,r:0.002,d:0.002,},{w:"w9999",v:0.2,t:2,f:1,h:0,s:1,}],
		[{w:"w9999",v:0.2,d:0.1,s:0.9,},{w:"w9999",v:0.25,t:4,f:2,s:0.5,}],
		[{w:"w9999",v:0.3,a:0.04,s:0.9,},{w:"w9999",v:0.2,t:8,f:2,a:0.04,s:0.9,}],
		[{w:"sine",v:0.2,a:0.02,d:0.05,s:1,},{w:"sine",v:6,t:3,f:1,a:0.02,d:0.05,s:1,g:1,}],
		[{w:"triangle",v:0.2,a:0.02,d:0.05,s:0.8,},{w:"square",v:7,t:3,f:1,d:0.05,s:1.5,g:1,}],
		[{w:"square",v:0.2,a:0.02,d:0.2,s:0.5,},{w:"square",v:1,d:0.03,s:2,g:1,}],
		[{w:"square",v:0.2,a:0.02,d:0.1,s:0.8,},{w:"square",v:1,a:0.3,d:0.1,s:2,g:1,}],
		/* 25-32 : Guitar */
		[{w:"sine",v:0.3,d:0.5,f:1,},{w:"triangle",v:5,t:3,f:-1,d:1,s:0.1,g:1,}],
		[{w:"sine",v:0.4,d:0.6,f:1,},{w:"triangle",v:12,t:3,d:0.6,s:0.1,g:1,f:-1,}],
		[{w:"triangle",v:0.3,d:1,f:1,},{w:"triangle",v:6,f:-1,d:0.4,s:0.5,g:1,t:3,}],
		[{w:"sine",v:0.3,d:1,f:-1,},{w:"triangle",v:11,f:1,d:0.4,s:0.5,g:1,t:3,}],
		[{w:"sine",v:0.4,d:0.1,r:0.01},{w:"sine",v:7,g:1,}],
		[{w:"triangle",v:0.4,d:1,f:1,},{w:"square",v:4,f:-1,d:1,s:0.7,g:1,}],//[{w:"triangle",v:0.35,d:1,f:1,},{w:"square",v:7,f:-1,d:0.3,s:0.5,g:1,}],
		[{w:"triangle",v:0.35,d:1,f:1,},{w:"square",v:7,f:-1,d:0.3,s:0.5,g:1,}],//[{w:"triangle",v:0.4,d:1,f:1,},{w:"square",v:4,f:-1,d:1,s:0.7,g:1,}],//[{w:"triangle",v:0.4,d:1,},{w:"square",v:4,f:2,d:1,s:0.7,g:1,}],
		[{w:"sine",v:0.2,t:1.5,a:0.005,h:0.2,d:0.6,},{w:"sine",v:11,t:5,f:2,d:1,s:0.5,g:1,}],
		/* 33-40 : Bass */
		[{w:"sine",d:0.3,},{w:"sine",v:4,t:3,d:1,s:1,g:1,}],
		[{w:"sine",d:0.3,},{w:"sine",v:4,t:3,d:1,s:1,g:1,}],
		[{w:"w9999",d:0.3,v:0.7,s:0.5,},{w:"sawtooth",v:1.2,d:0.02,s:0.5,g:1,h:0,r:0.02,}],
		[{w:"sine",d:0.3,},{w:"sine",v:4,t:3,d:1,s:1,g:1,}],
		[{w:"triangle",v:0.3,t:2,d:1,},{w:"triangle",v:15,t:2.5,d:0.04,s:0.1,g:1,}],
		[{w:"triangle",v:0.3,t:2,d:1,},{w:"triangle",v:15,t:2.5,d:0.04,s:0.1,g:1,}],
		[{w:"triangle",d:0.7,},{w:"square",v:0.4,t:0.5,f:1,d:0.2,s:10,g:1,}],
		[{w:"triangle",d:0.7,},{w:"square",v:0.4,t:0.5,f:1,d:0.2,s:10,g:1,}],
		/* 41-48 : Strings */
		[{w:"sawtooth",v:0.4,a:0.1,d:11,},{w:"sine",v:5,d:11,s:0.2,g:1,}],
		[{w:"sawtooth",v:0.4,a:0.1,d:11,},{w:"sine",v:5,d:11,s:0.2,g:1,}],
		[{w:"sawtooth",v:0.4,a:0.1,d:11,},{w:"sine",v:5,t:0.5,d:11,s:0.2,g:1,}],
		[{w:"sawtooth",v:0.4,a:0.1,d:11,},{w:"sine",v:5,t:0.5,d:11,s:0.2,g:1,}],
		[{w:"sine",v:0.4,a:0.1,d:11,},{w:"sine",v:6,f:2.5,d:0.05,s:1.1,g:1,}],
		[{w:"sine",v:0.3,d:0.1,r:0.1,},{w:"square",v:4,t:3,d:1,s:0.2,g:1,}],
		[{w:"sine",v:0.3,d:0.5,r:0.5,},{w:"sine",v:7,t:2,f:2,d:1,r:1,g:1,}],
		[{w:"triangle",v:0.6,h:0.03,d:0.3,r:0.3,t:0.5,},{w:"n0",v:8,t:1.5,d:0.08,r:0.08,g:1,}],
		/* 49-56 : Ensamble */
		[{w:"sawtooth",v:0.3,a:0.03,s:0.5,},{w:"sawtooth",v:0.2,t:2,f:2,d:1,s:2,}],
		[{w:"sawtooth",v:0.3,f:-2,a:0.03,s:0.5,},{w:"sawtooth",v:0.2,t:2,f:2,d:1,s:2,}],
		[{w:"sawtooth",v:0.2,a:0.02,s:1,},{w:"sawtooth",v:0.2,t:2,f:2,a:1,d:1,s:1,}],
		[{w:"sawtooth",v:0.2,a:0.02,s:1,},{w:"sawtooth",v:0.2,f:2,a:0.02,d:1,s:1,}],
		[{w:"triangle",v:0.3,a:0.03,s:1,},{w:"sine",v:3,t:5,f:1,d:1,s:1,g:1,}],
		[{w:"sine",v:0.4,a:0.03,s:0.9,},{w:"sine",v:1,t:2,f:3,d:0.03,s:0.2,g:1,}],
		[{w:"triangle",v:0.6,a:0.05,s:0.5,},{w:"sine",v:1,f:0.8,d:0.2,s:0.2,g:1,}],
		[{w:"square",v:0.15,a:0.01,d:0.2,r:0.2,t:0.5,h:0.03,},{w:"square",v:4,f:0.5,d:0.2,r:11,a:0.01,g:1,h:0.02,},{w:"square",v:0.15,t:4,f:1,a:0.02,d:0.15,r:0.15,h:0.03,},{g:3,w:"square",v:4,f:-0.5,a:0.01,h:0.02,d:0.15,r:11,}],
		/* 57-64 : Brass */
		[{w:"square",v:0.2,a:0.01,d:1,s:0.6,r:0.04,},{w:"sine",v:1,d:0.1,s:4,g:1,}],
		[{w:"square",v:0.2,a:0.02,d:1,s:0.5,r:0.08,},{w:"sine",v:1,d:0.1,s:4,g:1,}],
		[{w:"square",v:0.2,a:0.04,d:1,s:0.4,r:0.08,},{w:"sine",v:1,d:0.1,s:4,g:1,}],
		[{w:"square",v:0.15,a:0.04,s:1,},{w:"sine",v:2,d:0.1,g:1,}],
		[{w:"square",v:0.2,a:0.02,d:1,s:0.5,r:0.08,},{w:"sine",v:1,d:0.1,s:4,g:1,}],
		[{w:"square",v:0.2,a:0.02,d:1,s:0.6,r:0.08,},{w:"sine",v:1,f:0.2,d:0.1,s:4,g:1,}],
		[{w:"square",v:0.2,a:0.02,d:0.5,s:0.7,r:0.08,},{w:"sine",v:1,d:0.1,s:4,g:1,}],
		[{w:"square",v:0.2,a:0.02,d:1,s:0.5,r:0.08,},{w:"sine",v:1,d:0.1,s:4,g:1,}],
		/* 65-72 : Reed */
		[{w:"square",v:0.2,a:0.02,d:2,s:0.6,},{w:"sine",v:2,d:1,g:1,}],
		[{w:"square",v:0.2,a:0.02,d:2,s:0.6,},{w:"sine",v:2,d:1,g:1,}],
		[{w:"square",v:0.2,a:0.02,d:1,s:0.6,},{w:"sine",v:2,d:1,g:1,}],
		[{w:"square",v:0.2,a:0.02,d:1,s:0.6,},{w:"sine",v:2,d:1,g:1,}],
		[{w:"sine",v:0.4,a:0.02,d:0.7,s:0.5,},{w:"square",v:5,t:2,d:0.2,s:0.5,g:1,}],
		[{w:"sine",v:0.3,a:0.05,d:0.2,s:0.8,},{w:"sawtooth",v:6,f:0.1,d:0.1,s:0.3,g:1,}],
		[{w:"sine",v:0.3,a:0.03,d:0.2,s:0.4,},{w:"square",v:7,f:0.2,d:1,s:0.1,g:1,}],
		[{w:"square",v:0.2,a:0.05,d:0.1,s:0.8,},{w:"square",v:4,d:0.1,s:1.1,g:1,}],
		/* 73-80 : Pipe */
		[{w:"sine",a:0.02,d:2,},{w:"sine",v:6,t:2,d:0.04,g:1,}],
		[{w:"sine",v:0.7,a:0.03,d:0.4,s:0.4,},{w:"sine",v:4,t:2,f:0.2,d:0.4,g:1,}],
		[{w:"sine",v:0.7,a:0.02,d:0.4,s:0.6,},{w:"sine",v:3,t:2,d:0,s:1,g:1,}],
		[{w:"sine",v:0.4,a:0.06,d:0.3,s:0.3,},{w:"sine",v:7,t:2,d:0.2,s:0.2,g:1,}],
		[{w:"sine",a:0.02,d:0.3,s:0.3,},{w:"sawtooth",v:3,t:2,d:0.3,g:1,}],
		[{w:"sine",v:0.4,a:0.02,d:2,s:0.1,},{w:"sawtooth",v:8,t:2,f:1,d:0.5,g:1,}],
		[{w:"sine",v:0.7,a:0.03,d:0.5,s:0.3,},{w:"sine",v:0.003,t:0,f:4,d:0.1,s:0.002,g:1,}],
		[{w:"sine",v:0.7,a:0.02,d:2,},{w:"sine",v:1,t:2,f:1,d:0.02,g:1,}],
		/* 81-88 : SynthLead */
		[{w:"square",v:0.3,d:1,s:0.5,},{w:"square",v:1,f:0.2,d:1,s:0.5,g:1,}],
		[{w:"sawtooth",v:0.3,d:2,s:0.5,},{w:"square",v:2,f:0.1,s:0.5,g:1,}],
		[{w:"triangle",v:0.5,a:0.05,d:2,s:0.6,},{w:"sine",v:4,t:2,g:1,}],
		[{w:"triangle",v:0.3,a:0.01,d:2,s:0.3,},{w:"sine",v:22,t:2,f:1,d:0.03,s:0.2,g:1,}],
		[{w:"sawtooth",v:0.3,d:1,s:0.5,},{w:"sine",v:11,t:11,a:0.2,d:0.05,s:0.3,g:1,}],
		[{w:"sine",v:0.3,a:0.06,d:1,s:0.5,},{w:"sine",v:7,f:1,d:1,s:0.2,g:1,}],
		[{w:"sawtooth",v:0.3,a:0.03,d:0.7,s:0.3,r:0.2,},{w:"sawtooth",v:0.3,t:0.75,d:0.7,a:0.1,s:0.3,r:0.2,}],
		[{w:"triangle",v:0.3,a:0.01,d:0.7,s:0.5,},{w:"square",v:5,t:0.5,d:0.7,s:0.5,g:1,}],
		/* 89-96 : SynthPad */
		[{w:"triangle",v:0.3,a:0.02,d:0.3,s:0.3,r:0.3,},{w:"square",v:3,t:4,f:1,a:0.02,d:0.1,s:1,g:1,},{w:"triangle",v:0.08,t:0.5,a:0.1,h:0,d:0.1,s:0.5,r:0.1,b:0,c:0,}],
		[{w:"sine",v:0.3,a:0.05,d:1,s:0.7,r:0.3,},{w:"sine",v:2,f:1,d:0.3,s:1,g:1,}],
		[{w:"square",v:0.3,a:0.03,d:0.5,s:0.3,r:0.1,},{w:"square",v:4,f:1,a:0.03,d:0.1,g:1,}],
		[{w:"triangle",v:0.3,a:0.08,d:1,s:0.3,r:0.1,},{w:"square",v:2,f:1,d:0.3,s:0.3,g:1,t:4,a:0.08,}],
		[{w:"sine",v:0.3,a:0.05,d:1,s:0.3,r:0.1,},{w:"sine",v:0.1,t:2.001,f:1,d:1,s:50,g:1,}],
		[{w:"triangle",v:0.3,a:0.03,d:0.7,s:0.3,r:0.2,},{w:"sine",v:12,t:7,f:1,d:0.5,s:1.7,g:1,}],
		[{w:"sine",v:0.3,a:0.05,d:1,s:0.3,r:0.1,},{w:"sawtooth",v:22,t:6,d:0.06,s:0.3,g:1,}],
		[{w:"triangle",v:0.3,a:0.05,d:11,r:0.3,},{w:"triangle",v:1,d:1,s:8,g:1,}],
		/* 97-104 : FX */
		[{w:"sawtooth",v:0.3,d:4,s:0.8,r:0.1,},{w:"square",v:1,t:2,f:8,a:1,d:1,s:1,r:0.1,g:1,}],
		[{w:"triangle",v:0.3,d:1,s:0.5,t:0.8,a:0.2,p:1.25,q:0.2,},{w:"sawtooth",v:0.2,a:0.2,d:0.3,s:1,t:1.2,p:1.25,q:0.2,}],
		[{w:"sine",v:0.3,d:1,s:0.3,},{w:"square",v:22,t:11,d:0.5,s:0.1,g:1,}],
		[{w:"sawtooth",v:0.3,a:0.04,d:1,s:0.8,r:0.1,},{w:"square",v:1,t:0.5,d:1,s:2,g:1,}],
		[{w:"triangle",v:0.3,d:1,s:0.3,},{w:"sine",v:22,t:6,d:0.6,s:0.05,g:1,}],
		[{w:"sine",v:0.6,a:0.1,d:0.05,s:0.4,},{w:"sine",v:5,t:5,f:1,d:0.05,s:0.3,g:1,}],
		[{w:"sine",a:0.1,d:0.05,s:0.4,v:0.8,},{w:"sine",v:5,t:5,f:1,d:0.05,s:0.3,g:1,}],
		[{w:"square",v:0.3,a:0.1,d:0.1,s:0.4,},{w:"square",v:1,f:1,d:0.3,s:0.1,g:1,}],
		/* 105-112 : Ethnic */
		[{w:"sawtooth",v:0.3,d:0.5,r:0.5,},{w:"sawtooth",v:11,t:5,d:0.05,g:1,}],
		[{w:"square",v:0.3,d:0.2,r:0.2,},{w:"square",v:7,t:3,d:0.05,g:1,}],
		[{w:"triangle",d:0.2,r:0.2,},{w:"square",v:9,t:3,d:0.1,r:0.1,g:1,}],
		[{w:"triangle",d:0.3,r:0.3,},{w:"square",v:6,t:3,d:1,r:1,g:1,}],
		[{w:"triangle",v:0.4,d:0.2,r:0.2,},{w:"square",v:22,t:12,d:0.1,r:0.1,g:1,}],
		[{w:"sine",v:0.25,a:0.02,d:0.05,s:0.8,},{w:"square",v:1,t:2,d:0.03,s:11,g:1,}],
		[{w:"sine",v:0.3,a:0.05,d:11,},{w:"square",v:7,t:3,f:1,s:0.7,g:1,}],
		[{w:"square",v:0.3,a:0.05,d:0.1,s:0.8,},{w:"square",v:4,d:0.1,s:1.1,g:1,}],
		/* 113-120 : Percussive */
		[{w:"sine",v:0.4,d:0.3,r:0.3,},{w:"sine",v:7,t:9,d:0.1,r:0.1,g:1,}],
		[{w:"sine",v:0.7,d:0.1,r:0.1,},{w:"sine",v:22,t:7,d:0.05,g:1,}],
		[{w:"sine",v:0.6,d:0.15,r:0.15,},{w:"square",v:11,t:3.2,d:0.1,r:0.1,g:1,}],
		[{w:"sine",v:0.8,d:0.07,r:0.07,},{w:"square",v:11,t:7,r:0.01,g:1,}],
		[{w:"triangle",v:0.7,t:0.5,d:0.2,r:0.2,p:0.95,},{w:"n0",v:9,g:1,d:0.2,r:0.2,}],
		[{w:"sine",v:0.7,d:0.1,r:0.1,p:0.9,},{w:"square",v:14,t:2,d:0.005,r:0.005,g:1,}],
		[{w:"square",d:0.15,r:0.15,p:0.5,},{w:"square",v:4,t:5,d:0.001,r:0.001,g:1,}],
		[{w:"n1",v:0.3,a:1,s:1,d:0.15,r:0,t:0.5,}],
		/* 121-128 : SE */
		[{w:"sine",t:12.5,d:0,r:0,p:0.5,v:0.3,h:0.2,q:0.5,},{g:1,w:"sine",v:1,t:2,d:0,r:0,s:1,},{g:1,w:"n0",v:0.2,t:2,a:0.6,h:0,d:0.1,r:0.1,b:0,c:0,}],
		[{w:"n0",v:0.2,a:0.05,h:0.02,d:0.02,r:0.02,}],
		[{w:"n0",v:0.4,a:1,d:1,t:0.25,}],
		[{w:"sine",v:0.3,a:0.1,d:1,s:0.5,},{w:"sine",v:4,t:0,f:1.5,d:1,s:1,r:0.1,g:1,},{g:1,w:"sine",v:4,t:0,f:2,a:0.6,h:0,d:0.1,s:1,r:0.1,b:0,c:0,}],
		[{w:"square",v:0.3,t:0.25,d:11,s:1,},{w:"square",v:12,t:0,f:8,d:1,s:1,r:11,g:1,}],
		[{w:"n0",v:0.4,t:0.5,a:1,d:11,s:1,r:0.5,},{w:"square",v:1,t:0,f:14,d:1,s:1,r:11,g:1,}],
		[{w:"sine",t:0,f:1221,a:0.2,d:1,r:0.25,s:1,},{g:1,w:"n0",v:3,t:0.5,d:1,s:1,r:1,}],
		[{w:"sine",d:0.4,r:0.4,p:0.1,t:2.5,v:1,},{w:"n0",v:12,t:2,d:1,r:1,g:1,}],
		],
		program0:[
		// 1-8 : Piano
		[{w:"triangle",v:.5,d:.7}],                   [{w:"triangle",v:.5,d:.7}],
		[{w:"triangle",v:.5,d:.7}],                   [{w:"triangle",v:.5,d:.7}],
		[{w:"triangle",v:.5,d:.7}],                   [{w:"triangle",v:.5,d:.7}],
		[{w:"sawtooth",v:.3,d:.7}],                   [{w:"sawtooth",v:.3,d:.7}],
		/* 9-16 : Chromatic Perc*/
		[{w:"sine",v:.5,d:.3,r:.3}],                  [{w:"triangle",v:.5,d:.3,r:.3}],
		[{w:"square",v:.2,d:.3,r:.3}],                [{w:"square",v:.2,d:.3,r:.3}],
		[{w:"sine",v:.5,d:.1,r:.1}],                  [{w:"sine",v:.5,d:.1,r:.1}],
		[{w:"square",v:.2,d:1,r:1}],                  [{w:"sawtooth",v:.3,d:.7,r:.7}],
		/* 17-24 : Organ */
		[{w:"sine",v:0.5,a:0.01,s:1}],                [{w:"sine",v:0.7,d:0.02,s:0.7}],
		[{w:"square",v:.2,s:1}],                      [{w:"triangle",v:.5,a:.01,s:1}],
		[{w:"square",v:.2,a:.02,s:1}],                [{w:"square",v:0.2,a:0.02,s:1}],
		[{w:"square",v:0.2,a:0.02,s:1}],              [{w:"square",v:.2,a:.05,s:1}],
		/* 25-32 : Guitar */
		[{w:"triangle",v:.5,d:.5}],                   [{w:"square",v:.2,d:.6}],
		[{w:"square",v:.2,d:.6}],                     [{w:"triangle",v:.8,d:.6}],
		[{w:"triangle",v:.4,d:.05}],                  [{w:"square",v:.2,d:1}],
		[{w:"square",v:.2,d:1}],                      [{w:"sine",v:.4,d:.6}],
		/* 33-40 : Bass */
		[{w:"triangle",v:.7,d:.4}],                   [{w:"triangle",v:.7,d:.7}],
		[{w:"triangle",v:.7,d:.7}],                   [{w:"triangle",v:.7,d:.7}],
		[{w:"square",v:.3,d:.2}],                     [{w:"square",v:.3,d:.2}],
		[{w:"square",v:.3,d:.1,s:.2}],                [{w:"sawtooth",v:.4,d:.1,s:.2}],
		/* 41-48 : Strings */
		[{w:"sawtooth",v:.2,a:.02,s:1}],              [{w:"sawtooth",v:.2,a:.02,s:1}],
		[{w:"sawtooth",v:.2,a:.02,s:1}],              [{w:"sawtooth",v:.2,a:.02,s:1}],
		[{w:"sawtooth",v:.2,a:.02,s:1}],              [{w:"sawtooth",v:.3,d:.1}],
		[{w:"sawtooth",v:.3,d:.5,r:.5}],              [{w:"triangle",v:.6,d:.1,r:.1,h:0.03,p:0.8}],
		/* 49-56 : Ensamble */
		[{w:"sawtooth",v:.2,a:.02,s:1}],              [{w:"sawtooth",v:.2,a:.02,s:1}],
		[{w:"sawtooth",v:.2,a:.02,s:1}],              [{w:"sawtooth",v:.2,a:.02,s:1}],
		[{w:"triangle",v:.3,a:.03,s:1}],              [{w:"sine",v:.3,a:.03,s:1}],
		[{w:"triangle",v:.3,a:.05,s:1}],              [{w:"sawtooth",v:.5,a:.01,d:.1}],
		/* 57-64 : Brass */
		[{w:"square",v:.3,a:.05,d:.2,s:.6}],          [{w:"square",v:.3,a:.05,d:.2,s:.6}],
		[{w:"square",v:.3,a:.05,d:.2,s:.6}],          [{w:"square",v:0.2,a:.05,d:0.01,s:1}],
		[{w:"square",v:.3,a:.05,s:1}],                [{w:"square",v:.3,s:.7}],
		[{w:"square",v:.3,s:.7}],                     [{w:"square",v:.3,s:.7}],
		/* 65-72 : Reed */
		[{w:"square",v:.3,a:.02,d:2}],                [{w:"square",v:.3,a:.02,d:2}],
		[{w:"square",v:.3,a:.03,d:2}],                [{w:"square",v:.3,a:.04,d:2}],
		[{w:"square",v:.3,a:.02,d:2}],                [{w:"square",v:.3,a:.05,d:2}],
		[{w:"square",v:.3,a:.03,d:2}],                [{w:"square",v:.3,a:.03,d:2}],
		/* 73-80 : Pipe */
		[{w:"sine",v:.7,a:.02,d:2}],                  [{w:"sine",v:.7,a:.02,d:2}],
		[{w:"sine",v:.7,a:.02,d:2}],                  [{w:"sine",v:.7,a:.02,d:2}],
		[{w:"sine",v:.7,a:.02,d:2}],                  [{w:"sine",v:.7,a:.02,d:2}],
		[{w:"sine",v:.7,a:.02,d:2}],                  [{w:"sine",v:.7,a:.02,d:2}],
		/* 81-88 : SynthLead */
		[{w:"square",v:.3,s:.7}],                     [{w:"sawtooth",v:.4,s:.7}],
		[{w:"triangle",v:.5,s:.7}],                   [{w:"sawtooth",v:.4,s:.7}],
		[{w:"sawtooth",v:.4,d:12}],                   [{w:"sine",v:.4,a:.06,d:12}],
		[{w:"sawtooth",v:.4,d:12}],                   [{w:"sawtooth",v:.4,d:12}],
		/* 89-96 : SynthPad */
		[{w:"sawtooth",v:.3,d:12}],                   [{w:"triangle",v:.5,d:12}],
		[{w:"square",v:.3,d:12}],                     [{w:"triangle",v:.5,a:.08,d:11}],
		[{w:"sawtooth",v:.5,a:.05,d:11}],             [{w:"sawtooth",v:.5,d:11}],
		[{w:"triangle",v:.5,d:11}],                   [{w:"triangle",v:.5,d:11}],
		/* 97-104 : FX */
		[{w:"triangle",v:.5,d:11}],                   [{w:"triangle",v:.5,d:11}],
		[{w:"square",v:.3,d:11}],                     [{w:"sawtooth",v:0.5,a:0.04,d:11}],
		[{w:"sawtooth",v:.5,d:11}],                   [{w:"triangle",v:.5,a:.8,d:11}],
		[{w:"triangle",v:.5,d:11}],                   [{w:"square",v:.3,d:11}],
		/* 105-112 : Ethnic */
		[{w:"sawtooth",v:.3,d:1,r:1}],                [{w:"sawtooth",v:.5,d:.3}],
		[{w:"sawtooth",v:.5,d:.3,r:.3}],              [{w:"sawtooth",v:.5,d:.3,r:.3}],
		[{w:"square",v:.3,d:.2,r:.2}],                [{w:"square",v:.3,a:.02,d:2}],
		[{w:"sawtooth",v:.2,a:.02,d:.7}],             [{w:"triangle",v:.5,d:1}],
		/* 113-120 : Percussive */
		[{w:"sawtooth",v:.3,d:.3,r:.3}],              [{w:"sine",v:.8,d:.1,r:.1}],
		[{w:"square",v:.2,d:.1,r:.1,p:1.05}],         [{w:"sine",v:.8,d:.05,r:.05}],
		[{w:"triangle",v:0.5,d:0.1,r:0.1,p:0.96}],    [{w:"triangle",v:0.5,d:0.1,r:0.1,p:0.97}],
		[{w:"square",v:.3,d:.1,r:.1,}],               [{w:"n1",v:0.3,a:1,s:1,d:0.15,r:0,t:0.5,}],
		/* 121-128 : SE */
		[{w:"triangle",v:0.5,d:0.03,t:0,f:1332,r:0.001,p:1.1}],
		[{w:"n0",v:0.2,t:0.1,d:0.02,a:0.05,h:0.02,r:0.02}],
		[{w:"n0",v:0.4,a:1,d:1,t:0.25,}],
		[{w:"sine",v:0.3,a:0.8,d:1,t:0,f:1832}],
		[{w:"triangle",d:0.5,t:0,f:444,s:1,}],
		[{w:"n0",v:0.4,d:1,t:0,f:22,s:1,}],
		[{w:"n0",v:0.5,a:0.2,d:11,t:0,f:44}],
		[{w:"n0",v:0.5,t:0.25,d:0.4,r:0.4}],
		],
		drummap1:[
			/*35*/
		[{w:"triangle",t:0,f:70,v:1,d:0.05,h:0.03,p:0.9,q:0.1,},{w:"n0",g:1,t:6,v:17,r:0.01,h:0,p:0,}],
		  [{w:"triangle",t:0,f:88,v:1,d:0.05,h:0.03,p:0.5,q:0.1,},{w:"n0",g:1,t:5,v:42,r:0.01,h:0,p:0,}],
		  [{w:"n0",f:222,p:0,t:0,r:0.01,h:0,}],
		  [{w:"triangle",v:0.3,f:180,d:0.05,t:0,h:0.03,p:0.9,q:0.1,},{w:"n0",v:0.6,t:0,f:70,h:0.02,r:0.01,p:0,},{g:1,w:"square",v:2,t:0,f:360,r:0.01,b:0,c:0,}],
		  [{w:"square",f:1150,v:0.34,t:0,r:0.03,h:0.025,d:0.03,},{g:1,w:"n0",t:0,f:13,h:0.025,d:0.1,s:1,r:0.1,v:1,}],
			/*40*/
		  [{w:"triangle",f:200,v:1,d:0.06,t:0,r:0.06,},{w:"n0",g:1,t:0,f:400,v:12,r:0.02,d:0.02,}],
		  [{w:"triangle",f:100,v:0.9,d:0.12,h:0.02,p:0.5,t:0,r:0.12,},{g:1,w:"n0",v:5,t:0.4,h:0.015,d:0.005,r:0.005,}],
		  [{w:"n1",f:390,v:0.25,r:0.01,t:0,}],
		  [{w:"triangle",f:120,v:0.9,d:0.12,h:0.02,p:0.5,t:0,r:0.12,},{g:1,w:"n0",v:5,t:0.5,h:0.015,d:0.005,r:0.005,}],
		  [{w:"n1",v:0.25,f:390,r:0.03,t:0,h:0.005,d:0.03,}],
			/*45*/
		  [{w:"triangle",f:140,v:0.9,d:0.12,h:0.02,p:0.5,t:0,r:0.12,},{g:1,w:"n0",v:5,t:0.3,h:0.015,d:0.005,r:0.005,}],
		  [{w:"n1",v:0.25,f:390,t:0,d:0.2,r:0.2,},{w:"n0",v:0.3,t:0,c:0,f:440,h:0.005,d:0.05,}],
		  [{w:"triangle",f:155,v:0.9,d:0.12,h:0.02,p:0.5,t:0,r:0.12,},{g:1,w:"n0",v:5,t:0.3,h:0.015,d:0.005,r:0.005,}],
		  [{w:"triangle",f:180,v:0.9,d:0.12,h:0.02,p:0.5,t:0,r:0.12,},{g:1,w:"n0",v:5,t:0.3,h:0.015,d:0.005,r:0.005,}],
		  [{w:"n1",v:0.3,f:1200,d:0.2,r:0.2,h:0.05,t:0,},{w:"n1",t:0,v:1,d:0.1,r:0.1,p:1.2,f:440,}],
			/*50*/
		  [{w:"triangle",f:220,v:0.9,d:0.12,h:0.02,p:0.5,t:0,r:0.12,},{g:1,w:"n0",v:5,t:0.3,h:0.015,d:0.005,r:0.005,}],
		  [{w:"n1",f:500,v:0.15,d:0.4,r:0.4,h:0,t:0,},{w:"n0",v:0.1,t:0,r:0.01,f:440,}],
		  [{w:"n1",v:0.3,f:800,d:0.2,r:0.2,h:0.05,t:0,},{w:"square",t:0,v:1,d:0.1,r:0.1,p:0.1,f:220,g:1,}],
		  [{w:"sine",f:1651,v:0.15,d:0.2,r:0.2,h:0,t:0,},{w:"sawtooth",g:1,t:1.21,v:7.2,d:0.1,r:11,h:1,},{g:1,w:"n0",v:3.1,t:0.152,d:0.002,r:0.002,}],
		  null,
			/*55*/
		  [{w:"n1",v:.3,f:1200,d:0.2,r:0.2,h:0.05,t:0,},{w:"n1",t:0,v:1,d:0.1,r:0.1,p:1.2,f:440,}],
		  null,
		  [{w:"n1",v:0.3,f:555,d:0.25,r:0.25,h:0.05,t:0,},{w:"n1",t:0,v:1,d:0.1,r:0.1,f:440,a:0.005,h:0.02,}],
		  [{w:"sawtooth",f:776,v:0.2,d:0.3,t:0,r:0.3,},{g:1,w:"n0",v:2,t:0,f:776,a:0.005,h:0.02,d:0.1,s:1,r:0.1,c:0,},{g:11,w:"sine",v:0.1,t:0,f:22,d:0.3,r:0.3,b:0,c:0,}],
		  [{w:"n1",f:440,v:0.15,d:0.4,r:0.4,h:0,t:0,},{w:"n0",v:0.4,t:0,r:0.01,f:440,}],
			/*60*/
		  null,null,null,null,null,
			/*65*/
		  null,null,null,null,null,
			/*70*/
		  null,null,null,null,null,
			/*75*/
		  null,null,null,null,null,
			/*80*/
		  [{w:"sine",f:1720,v:0.3,d:0.02,t:0,r:0.02,},{w:"square",g:1,t:0,f:2876,v:6,d:0.2,s:1,r:0.2,}],
		  [{w:"sine",f:1720,v:0.3,d:0.25,t:0,r:0.25,},{w:"square",g:1,t:0,f:2876,v:6,d:0.2,s:1,r:0.2,}],
		],
		drummap0:[
			/*35*/
		[{w:"triangle",t:0,f:110,v:1,d:0.05,h:0.02,p:0.1,}],
			[{w:"triangle",t:0,f:150,v:0.8,d:0.1,p:0.1,h:0.02,r:0.01,}],
			[{w:"n0",f:392,v:0.5,d:0.01,p:0,t:0,r:0.05}],
			[{w:"n0",f:33,d:0.05,t:0,}],
			[{w:"n0",f:100,v:0.7,d:0.03,t:0,r:0.03,h:0.02,}],
			/*40*/
			[{w:"n0",f:44,v:0.7,d:0.02,p:0.1,t:0,h:0.02,}],
			[{w:"triangle",f:240,v:0.9,d:0.1,h:0.02,p:0.1,t:0,}],
			[{w:"n0",f:440,v:0.2,r:0.01,t:0,}],
			[{w:"triangle",f:270,v:0.9,d:0.1,h:0.02,p:0.1,t:0,}],
			[{w:"n0",f:440,v:0.2,d:0.04,r:0.04,t:0,}],
			/*45*/
			[{w:"triangle",f:300,v:0.9,d:0.1,h:0.02,p:0.1,t:0,}],
			[{w:"n0",f:440,v:0.2,d:0.1,r:0.1,h:0.02,t:0,}],
			[{w:"triangle",f:320,v:0.9,d:0.1,h:0.02,p:0.1,t:0,}],
			[{w:"triangle",f:360,v:0.9,d:0.1,h:0.02,p:0.1,t:0,}],
			[{w:"n0",f:150,v:0.2,d:0.1,r:0.1,h:0.05,t:0,p:0.1,}],
			/*50*/
			[{w:"triangle",f:400,v:0.9,d:0.1,h:0.02,p:0.1,t:0,}],
			[{w:"n0",f:150,v:0.2,d:0.1,r:0.01,h:0.05,t:0,p:0.1}],
			[{w:"n0",f:150,v:0.2,d:0.1,r:0.01,h:0.05,t:0,p:0.1}],
			[{w:"n0",f:440,v:0.3,d:0.1,p:0.9,t:0,r:0.1,}],
			[{w:"n0",f:200,v:0.2,d:0.05,p:0.9,t:0,}],
			/*55*/
			[{w:"n0",f:440,v:0.3,d:0.12,p:0.9,t:0,}],
			[{w:"sine",f:800,v:0.4,d:0.06,t:0,}],
			[{w:"n0",f:150,v:0.2,d:0.1,r:0.01,h:0.05,t:0,p:0.1}],
			[{w:"n0",f:33,v:0.3,d:0.2,p:0.9,t:0,}],
			[{w:"n0",f:300,v:0.3,d:0.14,p:0.9,t:0,}],
			/*60*/
			[{w:"sine",f:200,d:0.06,t:0,}],
			[{w:"sine",f:150,d:0.06,t:0,}],
			[{w:"sine",f:300,t:0,}],
			[{w:"sine",f:300,d:0.06,t:0,}],
			[{w:"sine",f:250,d:0.06,t:0,}],
			/*65*/
			[{w:"square",f:300,v:.3,d:.06,p:.8,t:0,}],
			[{w:"square",f:260,v:.3,d:.06,p:.8,t:0,}],
			[{w:"sine",f:850,v:.5,d:.07,t:0,}],
			[{w:"sine",f:790,v:.5,d:.07,t:0,}],
			[{w:"n0",f:440,v:0.3,a:0.05,t:0,}],
			/*70*/
			[{w:"n0",f:440,v:0.3,a:0.05,t:0,}],
			[{w:"triangle",f:1800,v:0.4,p:0.9,t:0,h:0.03,}],
			[{w:"triangle",f:1800,v:0.3,p:0.9,t:0,h:0.13,}],
			[{w:"n0",f:330,v:0.3,a:0.02,t:0,r:0.01,}],
			[{w:"n0",f:330,v:0.3,a:0.02,t:0,h:0.04,r:0.01,}],
			/*75*/
			[{w:"n0",f:440,v:0.3,t:0,}],
			[{w:"sine",f:800,t:0,}],
			[{w:"sine",f:700,t:0,}],
			[{w:"n0",f:330,v:0.3,t:0,}],
			[{w:"n0",f:330,v:0.3,t:0,h:0.1,r:0.01,p:0.7,}],
			/*80*/
			[{w:"sine",t:0,f:1200,v:0.3,r:0.01,}],
			[{w:"sine",t:0,f:1200,v:0.3,d:0.2,r:0.2,}],
		],
		programSFname:[],
		programSFnote:[],
		notesSF:[],
	  /*@@gui*/
	  _guiInit:()=>{
			if(this.canvas){
			  this.ctx=this.canvas.getContext("2d");
			  this.ctx.fillStyle="#000";
			  this.ctx.fillRect(0,0,290,32);
			  this.canvas.addEventListener("dragover",this.dragOver.bind(this),false);
			  this.canvas.addEventListener("dragleave",this.dragLeave.bind(this),false);
			  this.canvas.addEventListener("drop",this.execDrop.bind(this),false);
			  this.canvas.addEventListener("click",this.click.bind(this),false);
			  this.canvas.addEventListener("mousedown",this.pointerdown.bind(this),false);
			  this.canvas.addEventListener("mousemove",this.pointermove.bind(this),false);
			  this.canvas.addEventListener("touchstart",this.pointerdown.bind(this),false);
			  this.canvas.addEventListener("touchend",this.pointerup.bind(this),false);
			  this.canvas.addEventListener("touchcancel",this.pointerup.bind(this),false);
			  this.canvas.addEventListener("touchmove",this.pointermove.bind(this),false);
			}
	  },
	  _guiUpdate:()=>{
			if(this.canvas){
			  this.ctx.fillStyle="#000";
			  this.ctx.fillRect(0,0,290,32);
			  var row1=8,row2=20;
			  if(this.song)
					row1=4,row2=24;
			  else {
					this.ctx.fillStyle="#fff";
					this.ctx.fillText("TinySynth",8,20);
			  }
			  if(this.graph){
					this.ctx.fillStyle="#800";
					this.ctx.fillRect(80,row1,132,4);
					this.ctx.fillRect(80,row2,132,4);
					this.ctx.fillStyle="#f00";
					for(let i=this.notetab.length-1;i>=0;--i){
						const nt=this.notetab[i];
						if(!nt.f || this.rhythm[nt.ch]){
						  this.ctx.fillRect(80+nt.n,row1,4,4);
						  this.ctx.fillRect(80+nt.ch*8,row2,6,4);
						}
					}
			  }
			  if(this.perfmon){
					this.ctx.fillStyle="#fff";
					this.ctx.fillRect(180,30,28,-12);
					this.ctx.fillStyle="#000";
					this.ctx.fillText(this.notetab.length,185,28);
			  }
			  this.ctx.fillStyle="#fff";
			  this.ctx.fillRect(250,15,32,2);
			  this.ctx.fillStyle="#fff";
			  this.ctx.strokeStyle="#000";
			  this.ctx.beginPath();
			  this.ctx.arc(250+this.masterVol*32,16,6,0,6.28,0);
			  this.ctx.moveTo(220,12); this.ctx.lineTo(224,12); this.ctx.lineTo(230,6);
			  this.ctx.lineTo(230,26); this.ctx.lineTo(224,20); this.ctx.lineTo(220,20);
			  this.ctx.fill();
			  this.ctx.stroke();
			  this.ctx.strokeStyle="#fff";
			  this.ctx.lineWidth=2;
			  this.ctx.beginPath();
			  this.ctx.arc(230,16,4,-1,1,false);
			  this.ctx.stroke();
			  this.ctx.beginPath();
			  this.ctx.arc(230,16,8,-1,1,false);
			  this.ctx.stroke();
			  if(this.masterVol==0){
					this.ctx.strokeStyle="#000";
					this.ctx.lineWidth=4;
					this.ctx.beginPath();
					this.ctx.moveTo(220,7);
					this.ctx.lineTo(238,25);
					this.ctx.stroke();
					this.ctx.strokeStyle="#fff";
					this.ctx.lineWidth=2;
					this.ctx.stroke();
			  }
			  if(this.song){
					this.ctx.fillStyle="#fff";
					this.ctx.fillRect(4,2,28,28);
					this.ctx.fillRect(80,15,128,2);
					this.ctx.fillStyle="#000";
					if(this.playing){
						this.ctx.fillRect(12,10,4,12);
						this.ctx.fillRect(22,10,4,12);
					}else{
						this.ctx.beginPath();
						this.ctx.moveTo(12,9);
						this.ctx.lineTo(25,16);
						this.ctx.lineTo(12,23);
						this.ctx.fill();
					}
					this.ctx.fillStyle="#fff"

					this.ctx.fillText("Nt",65,10);
					this.ctx.fillText("Ch",65,28);

					this.ctx.fillText(this.toTime(this.playTick),34,10);
					this.ctx.fillText(this.toTime(this.maxTick),34,28);
					this.ctx.strokeStyle="#000";
					this.ctx.beginPath();
					this.ctx.arc(80+this.playTick/this.maxTick*128,16,6,0,6.28,0);
					this.ctx.fill();
					this.ctx.stroke();
			  }
			  if(this.waitdrop){
					this.ctx.fillStyle="rgba(0,0,0,0.7)"
					this.ctx.fillRect(0,0,290,32);
					this.ctx.fillStyle="#fff";
					this.ctx.fillText("Drop MIDI File Here",100,20);
			  }
			}
	  },
	  toTime:(ti)=>{
			ti=(ti*4*60/this.song.timebase/this.song.tempo)|0;
			const m=(ti/60)|0;
			const s=ti%60;
			return ("00"+m).substr(-2)+":"+("00"+s).substr(-2);
			},
			preventScroll:(e)=>{
			e.preventDefault();
			},
			pointerup:(ev)=>{
			document.body.removeEventListener('touchstart',this.preventScroll,false);
	  },
	  getPos:(e)=>{
			var p=e.target.getBoundingClientRect();
			if(p.right!=p.left)
			  return {x:(e.clientX-p.left)*290/(p.right-p.left),y:e.clientY-p.top};
			return {x:0,y:0};
			},
			pointerdown:(ev)=>{
			let e=ev;
			if(ev.touches)
			  e=ev.touches[0];
			this.downpos=this.getPos(e);
			if(ev.touches || (e.buttons&1)){
			  if(this.song&&this.downpos.x>=80&&this.downpos.x<=208){
				const p=(this.downpos.x-80)/128*this.maxTick;
				this.locateMIDI(p);
				document.body.addEventListener('touchstart',this.preventScroll,false);
			  }
			  if(this.downpos.x>=250&&this.downpos.x<282){
				const p=(this.downpos.x-250)/32;
				this.setMasterVol(p);
				document.body.addEventListener('touchstart',this.preventScroll,false);
			  }
			}
			},
			pointermove:(ev)=>{
			let e=ev;
			if(ev.touches)
			  e=ev.touches[0];
			if(ev.touches || (e.buttons&1)){
			  const pos=this.getPos(e);
			  if(this.song&&pos.x>=70&&pos.x<=208){
				if(pos.x<80) pos.x=80;
				const p=(pos.x-80)/128*this.maxTick;
				this.locateMIDI(p);
			  }
			  if(pos.x>=250&&pos.x<282){
				const p=(pos.x-250)/32;
				this.setMasterVol(p);
			  }
			}
	  },
	  click:(e)=>{
			const pos=this.getPos(e);
			if(pos.x<40 && this.song){
			  if(this.playing){
					this.stopMIDI();
			  }
			  else if(this.song){
					this.playMIDI();
			  }
			}
			if(pos.x>=215&&pos.x<243 && this.downpos.x>=215 && this.downpos.x<243){
			  if(this.masterVol>0){
					this.lastMasterVol=this.masterVol;
					this.masterVol=0;
			  }else	this.masterVol=this.lastMasterVol;
			}
		},
		dragLeave:(e)=>{
			this.waitdrop=0;
		},
		dragOver:(e)=>{
			this.waitdrop=1;
			e.stopPropagation();
			e.preventDefault();
			e.dataTransfer.dropEffect = "copy";
		},
		execDrop:(e)=>{
			this.waitdrop=0;
			const f = e.dataTransfer.files;
			if(this.disabledrop==0){
			  var reader = new FileReader();
			  reader.onload=function(e){
					this.loadMIDI(reader.result);
			  }.bind(this);
			  reader.readAsArrayBuffer(f[0]);
			}
			e.stopPropagation();
			e.preventDefault();
		},
		getSongDurationTicks:()=>{
			return this.maxTick;
		},
		getSongDurationSecs:()=>{
			const tiks=(this.maxTick*4*60/this.song.timebase/this.song.tempo)|0;
			const m=(tiks/60)|0;
			const s=tiks%60;

			return (m*60)+s;
		},
	  getSongDurationSecPlay:()=>{
			const tiks=(this.playTick*4*60/this.song.timebase/this.song.tempo)|0;
			const m=(tiks/60)|0;
			const s=tiks%60;

			return (m*60)+s;
		},
			/*@@guiEND*/
		ready:()=>{
			return new Promise((resolve)=>{
			  const timerid=setInterval(()=>{
				  if(this.isReady){
						clearInterval(timerid);
						if(this.debug)
						console.log("Initialized.");
							resolve();
					}
				},100);
			});
	  },
	  init:()=>{
			this.ptrTime;this.ptrTimeStave;this.tempoBPM;
			this.numBank = 0;
			this.channels=[]; this.channelSel=""; this.notesOn=[]; this.arrTrkCh=[]; this.scale=1;
			this.pg=[]; this.vol=[]; this.ex=[]; this.bend=[]; this.rpnidx=[]; this.brange=[];
			this.sustain=[]; this.sostenuto=[]; this.softPedal=[]; this.notetab=[]; this.rhythm=[];
			this.masterTuningC=0; this.masterTuningF=0; this.tuningC=[]; this.tuningF=[]; this.scaleTuning=[];
			this.maxTick=0, this.playTick=0, this.playing=0; this.releaseRatio=3.5;
			for(let i=0;i<16;++i){
			  this.pg[i]=0; this.vol[i]=3*100*100/(127*127);
			  this.bend[i]=0; this.brange[i]=0x100;
			  this.tuningC[i]=0; this.tuningF[i]=0;
			  this.scaleTuning[i]=[0,0,0,0,0,0,0,0,0,0,0,0];
			  this.rhythm[i]=0;
			}
			this.rhythm[9]=1;
			this.preroll=0.2;
			this.relcnt=0;
			setInterval(
			  async function(){
				if(++this.relcnt>=3){
					this.relcnt=0;
					for(let i=this.notetab.length-1;i>=0;--i){
					  var nt=this.notetab[i];
					  if(this.actx.currentTime>nt.e){
							if("GM" == this.standMidi) {
								this._pruneNote(nt);
							}
							this.notetab.splice(i,1);
					  }
					}
					/*@@gui*/
					/*@@guiEND*/
				}
				if(this.playing && this.song.ev.length>0){
					let e=this.song.ev[this.playIndex];
					while(this.actx.currentTime+this.preroll>this.playTime){
					  if(e.m[0]==0xff51){
							this.song.tempo=e.m[1];
							this.tick2Time=4*60/this.song.tempo/this.song.timebase;
							if(this.tempoBPM){
								this.tempoBPM.value=this.song.tempo;
								this.tempoBPM.style.background="#C3C3C3";
								let pauseBPM = await setTimeout(() => {
									this.tempoBPM.style.background="white";
									clearTimeout(pauseBPM);
								}, 250);
							}else{
								this.tempoBPM = document.getElementById("tempoBPM");
							}
					  }else{
							let ch = e.m[0]&0xf;
							if(0 == this.channels.length){
								this.send(e.m,this.playTime);
							}else{
								if(this.channels.includes(ch)){
								  this.send(e.m,this.playTime);
								}
							}
					  }
					  ++this.playIndex;
					  if(this.playIndex>=this.song.ev.length){
							if(this.loop){

								this.posX = 0;

								document.getElementById("subwin_part2").scrollLeft = 0;
								document.getElementById("sequencer_subfooter2").scrollLeft = 0;

								document.getElementById("ptrTime").style.left = "0px";
								document.getElementById("ptrTimeStave").style.left = "0px";

								e=this.song.ev[this.playIndex=0];
								this.playTick=e.t;
							}else{
								this.playTick=this.maxTick;
								this.playing=0;

								//The End play
								let svgStave = document.getElementById("stave").querySelector("svg");
								if(svgStave){
									let posX = parseInt(svgStave.getAttribute('width'))-10;
									sequencer_subfooter2.scrollLeft = posX;
									this.ptrTimeStave.style.left = posX + "px";
								}
								//Scale sequencer: on/off
								SequencerScale();
								break;
							}
						}else{
							e=this.song.ev[this.playIndex];
							this.playTime+=(e.t-this.playTick)*this.tick2Time;
							this.playTick=e.t;

							//Current position of the sequencer ptrTime
							if(0 == this.playTick){
								this.posX = 0;
							}
							if(false == this.record) {
								if(this.ptrTime){
									let box = this.ptrTime.getBoundingClientRect();
									if(1140 < box.left) subwin_part2.scrollLeft += 680;
									this.ptrTime.style.left=(Math.trunc(this.playTick/12)-2)*this.scale +"px";
								}else{
									this.ptrTime = document.getElementById("ptrTime");
								}
							}
							//Current position of the sequencer ptrTimeStave
							if(this.arrMeasures && this.arrMeasuresStave){
								let idxMeasure = this.arrMeasures.findIndex(item => this.playTick < item.tick);
								if(-1 !== idxMeasure && this.arrMeasuresStave){
									let measure = this.arrMeasures[idxMeasure].measure;
									let idxMeasureStave = this.arrMeasuresStave.findIndex(item => item.measure == measure);
									let posX = 0;

									if(-1 !== idxMeasureStave){
										this.idxMeasureStaveLast = idxMeasureStave;
										posX = this.arrMeasuresStave[idxMeasureStave].posX * .6;
										if(760 < Math.abs(posX - this.posX)){  //760 or ("sequencer_subfooter2").width
											this.posX += 680;
											sequencer_subfooter2.scrollLeft += 680;
										}
										this.ptrTimeStave.style.left = posX + "px";
									}else{
										if(0 !== this.idxMeasureStaveLast){
											if(this.arrMeasuresStave[this.idxMeasureStaveLast+1]){
												posX = this.arrMeasuresStave[this.idxMeasureStaveLast+1].posX * .6;
											}else{
												//The End play
												let svgStave = document.getElementById("stave").querySelector("svg");
												if(svgStave){
													posX = parseInt(svgStave.getAttribute('width'))-60;
													sequencer_subfooter2.scrollLeft = posX;
												}
											}
										}else{
											posX = 50;
										}
										this.ptrTimeStave.style.left = posX + "px";
									}
								}
							}
					  }
					}
				}
			}.bind(this),30);
			if(this.debug)
			  console.log("internalcontext:"+this.internalcontext)
			if(this.internalcontext){
			  window.AudioContext = window.AudioContext || window.webkitAudioContext;
			  this.setAudioContext(new AudioContext());
			}
			this.isReady=1;
	  },
	  setMasterVol:(v)=>{
			if(v!=undefined)
			  this.masterVol=v;
			if(this.out)
			  this.out.gain.value=this.masterVol;
	  },
	  setReverbLev:(v)=>{
			if(v!=undefined)
			  this.reverbLev=v;
			var r=parseFloat(this.reverbLev);
			if(this.rev&&!isNaN(r))
			  this.rev.gain.value=r*8;
	  },
	  setChannelsPlay:(v)=>{
			if(v!=undefined)
				this.channels = v;
	  },
	  setChannelSel(channelSel){
	  	this.channelSel = channelSel;
  		for(let i=0; i<this.notesOn.length; i++){
  			kbd.setNote(0, this.notesOn[i]);
  		}
  		this.notesOn = [];
	  },
	  setAnalysVol:(v)=>{
			if(v!=undefined)
			  this.analysVol=v;
			if(this.analysGain)
			  this.analysGain.gain.value=this.analysVol;
	  },
	  setCompThreshold:(v)=>{
			if(v!=undefined)
			  this.compThreshold = v;
			if(this.comp)
			  this.comp.threshold.setValueAtTime(this.compThreshold, this.audioContext.currentTime);
	  },
	  setCompKnee:(v)=>{
			if(v!=undefined)
			  this.compKnee = v;
			if(this.comp)
			  this.comp.knee.setValueAtTime(this.compKnee, this.audioContext.currentTime);
	  },
	  setCompRatio:(v)=>{
			if(v!=undefined)
			  this.compRatio = v;
			if(this.comp)
			  this.comp.ratio.setValueAtTime(this.compRatio, this.audioContext.currentTime);
	  },
	  setCompAttack:(v)=>{
			if(v!=undefined)
			  this.compAttack = v;
			if(this.comp)
			  this.comp.attack.setValueAtTime(this.compAttack, this.audioContext.currentTime);
	  },
	  setCompRelease:(v)=>{
			if(v!=undefined)
			  this.compRelease = v;
			if(this.comp)
			  this.comp.release.setValueAtTime(this.compRelease, this.audioContext.currentTime);
	  },
	  setConvDuration:(v)=>{
			if(v!=undefined)
			  this.convDuration=v;
		},
		setConvDecay:(v)=>{
			if(v!=undefined)
			  this.convDecay=v;
		},
		setLoop:(f)=>{
			this.loop=f;
	  },
	  setVoices:(v)=>{
			this.voices=v;
		},
		setRecord:(v)=>{
			this.record=v;
		},
		getPlayStatus:()=>{
			return {play:this.playing, maxTick:this.maxTick, curTick:this.playTick};
		},
		locateMIDI:(tick)=>{
			let i,p=this.playing;
			this.stopMIDI();
			for(i=0;i<this.song.ev.length && tick>this.song.ev[i].t;++i){
			  var m=this.song.ev[i];
			  var ch=m.m[0]&0xf;
			  switch(m.m[0]&0xf0){
				  case 0xb0:
					switch(m.m[1]){
						case 1:  this.setModulation(ch,m.m[2]); break;
						case 7:  this.setChVol(ch,m.m[2]); break;
						case 10: this.setPan(ch,m.m[2]); break;
						case 11: this.setExpression(ch,m.m[2]); break;
						case 64: this.setSustain(ch,m.m[2]); break;
						case 66: this.setSostenuto(ch,m.m[2]); break;
						case 67: this.setSoftPedal(ch,m.m[2]); break;
					}
					break;
				  case 0xc0: this.pg[m.m[0]&0x0f]=m.m[1]; break;
			  }
			  if(m.m[0]==0xff51)
				this.song.tempo=m.m[1];
			}
			if(!this.song.ev[i]){
			  this.playIndex=0;
			  this.playTick=this.maxTick;
			}
			else{
			  this.playIndex=i;
			  this.playTick=this.song.ev[i].t;
			}
			if(p)
			  this.playMIDI();
		},
		getProgNameGM:(m,n)=>{
			if(m==0){
				if(!this.program[n]) return undefined;
				return this.program[n].name;
			}else{
				if(!this.drummap[n]) return undefined;
				return this.drummap[n].name;
			}
	  },
	  loadMIDIfromSrc:()=>{
			this.loadMIDIUrl(this.src);
	  },
	  loadMIDIUrl:(url)=>{
			if(!url)return;
			var xhr=new XMLHttpRequest();
			xhr.open("GET",url,true);
			xhr.responseType="arraybuffer";
			xhr.loadMIDI=this.loadMIDI.bind(this);
			xhr.onload= async function(e){
			  if(this.status==200){
				this.loadMIDI(this.response);
				await Sequencer();
			  }
			};
			xhr.send();
		},
		setStandMidi:(standMidi)=>{
			this.standMidi = standMidi;
			this.notesSF = [];
			this.programSFname = [];
			this.programSFnote = [];
		},
		getProgNameSF:(BankNumber, PresetNumber)=>{
			let presetName;
			if(this.programSFname[BankNumber]){
				let idxPreset = this.programSFname[BankNumber].findIndex(item => item.idx == PresetNumber);
				if(-1 !== idxPreset) presetName = this.programSFname[BankNumber][idxPreset].namePreset;
					return presetName;
			}else{
				return undefined;
			}
		},
		setProgNameSF:(progSFnames)=>{
			this.programSFname = progSFnames;
		},
		getProgNoteSF:(BankNumber, PresetNumber, numNote)=>{
			if(this.programSFnote[BankNumber]){
				if(this.programSFnote[BankNumber][PresetNumber]){
					if(this.programSFnote[BankNumber][PresetNumber][numNote]){
						return this.programSFnote[BankNumber][PresetNumber][numNote];
					}
				}
			}
			return undefined;
		},
		setProgNoteSF:(BankNumber, PresetNumber, numNote, activeZones)=>{
			if(undefined == this.programSFnote[BankNumber])
				this.programSFnote[BankNumber] = [];
			if(undefined == this.programSFnote[BankNumber][PresetNumber])
				this.programSFnote[BankNumber][PresetNumber] = [];

			this.programSFnote[BankNumber][PresetNumber][numNote] = {zones: activeZones};
		},
	  reset:()=>{
			for(let i=0;i<16;++i){
			  this.setProgram(i,0);
			  this.setBendRange(i,0x100);
			  this.setModulation(i,0);
			  this.setChVol(i,100);
			  this.setPan(i,64);
			  this.resetAllControllers(i);
			  this.allSoundOff(i, this.actx);
			  this.rhythm[i]=0;
			  this.tuningC[i]=0;
			  this.tuningF[i]=0;
			}
			this.masterTuningC=0;
			this.masterTuningF=0;
			this.rhythm[9]=1;
		},
	  stopMIDI:()=>{
			this.playing=0;
			for(var i=0;i<16;++i)this.allSoundOff(i,this.actx);
			if(this.arrTrkCh){
				let speaker;
				let speakerOn;
				let speakerOff;
				this.arrTrkCh.forEach(function(item, index){
					speaker   = document.getElementById("speaker_"+item);
					speakerOn = document.getElementById("speakerOn_"+item);
					speakerOff = document.getElementById("speakerOff_"+item);
					if("none" == speakerOff.style.display){
						speakerOn.style.display = "none";
						speaker.style.display = "block";
					}
				});
			}
			for(let i=0; i<this.notesOn.length; i++){
				kbd.setNote(0, this.notesOn[i]);
			}
  		this.notesOn = [];
  		this.notetab = [];
	  },
		playMIDI:()=>{
			if(!this.song)return;
			const dummy=this.actx.createOscillator();
			dummy.connect(this.actx.destination);
			dummy.frequency.value=0;
			dummy.start(0);
			dummy.stop(this.actx.currentTime+0.001);
			if(this.playTick>=this.maxTick){
				this.playTick=0,this.playIndex=0;
				this.posX = 0;

				document.getElementById("subwin_part2").scrollLeft = 0;
				document.getElementById("sequencer_subfooter2").scrollLeft = 0;

				document.getElementById("ptrTime").style.left = "0px";
				document.getElementById("ptrTimeStave").style.left = "0px";
			}
			this.playTime=this.actx.currentTime+.1;
			if(!this.song.tempo)this.song.tempo=this.getTempoBPM();
			this.tick2Time=4*60/this.song.tempo/this.song.timebase;
			this.playing=1;
		},
		loadMIDI:(data)=>{
			var timeSignature = [];
			var channels = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];

			function Get2(s, i) { return (s[i]<<8) + s[i+1]; }
			function Get3(s, i) { return (s[i]<<16) + (s[i+1]<<8) + s[i+2]; }
			function Get4(s, i) { return (s[i]<<24) + (s[i+1]<<16) + (s[i+2]<<8) + s[i+3]; }
			function GetStr(s, i, len) {
			  return String.fromCharCode.apply(null,s.slice(i,i+len));
			}
			function GetParams(s, i, len) {
			  return Array.from(s.slice(i,i+len));
			}
			function Delta(s, i) {
			  var v, d;
			  v = 0;
			  datalen = 1;
			  while((d = s[i]) & 0x80) {
				v = (v<<7) + (d&0x7f);
				++datalen;
				++i;
			  }
			  return (v<<7)+d;
			}
			function Msg(song,tick,s,i,numtrk,timeSignature,channels){
			  var v=s[i];
			  datalen=1;
			  if((v&0x80)==0)
				v=runst,datalen=0;
			  runst=v;
			  switch(v&0xf0){
				  case 0xc0: case 0xd0:
					let ch = v&0xf0>>4;
					let idx = channels.indexOf(ch);
					if(-1 !== idx) channels.splice(idx, 1);
					song.ev.push({t:tick,m:[v,s[i+datalen]]});
					datalen+=1;
					break;
				  case 0xf0:
					switch(v) {
						case 0xf0:
						case 0xf7:
							var len=Delta(s,i+1);
							datastart=1+datalen;
							var exd=Array.from(s.slice(i+datastart,i+datastart+len));
							exd.unshift(0xf0);
							song.ev.push({t:tick,m:exd});
							/*
								var sysex=[];
								for(var jj=0;jj<len;++jj)
								  sysex.push(s[i+datastart+jj].toString(16));
								if(this.debug)
								  console.log(sysex);
							*/
							datalen+=len+1;
							break;
						  case 0xff:
								var len = Delta(s, i + 2);
								datastart = 2+datalen;
								datalen = len+datalen+2;
								switch(s[i+1]) {
									case 0x01:
									  song.metatext+=GetStr(s, i + datastart, datalen - 3);
									  break;
									case 0x02:
									  song.copyright+=GetStr(s, i + datastart, datalen - 3);
									  break;
									case 0x03:

									  break;
									case 0x2f:
									  return 1;
									case 0x51:
									  var val = Math.floor(60000000 / Get3(s, i + 3));
									  song.ev.push({t:tick, m:[0xff51, val]});
									  if(undefined == song.tempoBPM) song.tempoBPM = val;
									  timeSignature.push({type:0xff ,subtype:0x51 ,tick:tick, tempoBPM:val});
									  break;
									case 0x58:
									  timeSignature.push({type:0xff ,subtype:0x58 ,tick:tick, timeSignature: GetParams(s, i + datastart, datalen-3)});
									  break;
								}
							break;
				}
				break;
				  default:
					song.ev.push({t:tick,m:[v,s[i+datalen],s[i+datalen+1]]});
					datalen+=2;
			  }
			  return 0;
			}
			this.stopMIDI();
			var s=new Uint8Array(data);
			var datalen = 0, datastart = 0, runst = 0x90;
			var idx = 0;
			var hd = s.slice(0,  4);
			if(hd.toString()!="77,84,104,100")  //MThd
			  return;
			var len = Get4(s, 4);
			var tp = Get2(s, 8);
			var numtrk = Get2(s, 10);

			this.dataBuffer = data;
			this.typeMidi = tp;
			this.ppqn = Get2(s, 12);

			this.record = false;

			this.maxTick=0;
			var tb = Get2(s, 12)*4;
			idx = (len + 8);
			this.song={copyright:"",metatext:"",text:"",tempo:"",timebase:tb,ev:[]};

			for(let tr=0;tr<numtrk;++tr){
			  hd=s.slice(idx, idx+4);
			  len=Get4(s, idx+4);

			  if(hd.toString()=="77,84,114,107") {//MTrk
			  	this.notetab.length = 0;
					var tick = 0;
					var j = 0;
					for(;;){
						tick += Delta(s, idx + 8 + j);
						j += datalen;
						var e = Msg(this.song, tick, s, idx + 8 + j, tr, timeSignature, channels);
						j += datalen;
						if(e) break;
					}
					if(tick>this.maxTick)this.maxTick=tick;
			  }
			  idx += (len+8);
			}

			//swap array elements
			for(let i=0; i<timeSignature.length; i++){
				if(timeSignature[i+1] &&
				timeSignature[i].tick == timeSignature[i+1].tick &&
				81 == timeSignature[i].subtype &&
				88 == timeSignature[i+1].subtype){
					[timeSignature[i], timeSignature[i+1]]  = [timeSignature[i+1], timeSignature[i]];
				}
			}
			//delete dublicate record
			for(let i=0; i<timeSignature.length; i++){
				if(timeSignature[i+1] &&
					timeSignature[i].tick == timeSignature[i+1].tick &&
					timeSignature[i].subtype == timeSignature[i+1].subtype){
					timeSignature.splice(i, 1);
					break;
				}
			}
			this.timeSignature = timeSignature;
			this.channelsFree  = channels;

			this.song.ev.sort(function(x,y){return x.t-y.t});
			this.reset();
			this.locateMIDI(0);
	  },
	  getChannelsFree(){
			return this.channelsFree;
	  },
	  updateChannelsFree(chOld, chNew){
			let index;
			this.channelsFree.forEach(function(item, idx){
		  	if(item == parseInt(chNew-1)) index = idx;
			});
			if(undefined !== index) {
				if("" == chOld){
					this.channelsFree.splice(index,1);
				}else{
					this.channelsFree[index] = parseInt(chOld)-1;
				}
			}
			this.channelsFree.sort(function (a, b){
			  return a - b;
			});
	  },
	  getMidiSrc(){
			return this.dataBuffer;
	  },
	  getMetaText(){
	  	return this.song.metatext;
	  },
	  getCopyright(){
			return this.song.copyright;
	  },
	  getTempoBPM(){
			if(undefined !== this.song.tempoBPM){
				return this.song.tempoBPM;
			}else{
				return 120;
			}
	  },
	  getTypeMidi(){
			return this.typeMidi;
	  },
	  getPPQN(){
			return this.ppqn;
	  },
	  getTimeSignature(){
			if(0 !== this.timeSignature.length){
				return this.timeSignature;
			}else{
				let timeSignatureDefault = [];
				timeSignatureDefault.push({type: 255, subtype: 88, tick: 0, timeSignature: [4, 2, 24, 8]});
				timeSignatureDefault.push({type: 255, subtype: 81, tick: 0, tempoBPM: 120});
				return timeSignatureDefault;
			}
	  },
	  getCurrBPM(numMeasure){
			let tickOfmeasure;
			for (let i = 0; i < this.timeSignature.length; i++){
			  if(88 == this.timeSignature[i].subtype && numMeasure == this.timeSignature[i].measure){
				tickOfmeasure = this.timeSignature[i].tick;
				break;
			  }
			}
			let currBPM;
			for (let i = 0; i < this.timeSignature.length; i++){
			  if(81 == this.timeSignature[i].subtype) currBPM = this.timeSignature[i].tempoBPM;
			  if(currBPM && tickOfmeasure <= this.timeSignature[i].tick){
				break;
			  }
			}
			return currBPM;
	  },
	  setScaleMeasures(arrMeasures){
	  	this.arrMeasures = arrMeasures;
	  },
	  setStaveMeasures(arrMeasuresStave){
	  	this.arrMeasuresStave = arrMeasuresStave;
			this.ptrTimeStave = document.getElementById("ptrTimeStave");
			this.idxMeasureStaveLast = 0;
			this.posX = 0;
	  },
	  setTrkCh(arrTrkCh){
			this.arrTrkCh = arrTrkCh;
	  },
	  setScale(scale){
			this.scale = scale;
	  },
	  setQuality:(q)=>{
			if(q!=undefined)
			  this.quality=q;
			for(let i=0;i<128;++i){
			  this.setTimbre(0,i,this.program0[i]);
			}
			for(let i=0;i<this.drummap0.length;++i){
			  this.setTimbre(1,i+35,this.drummap0[i]);
			}
			if(1 == this.quality){
			  for(let i=0;i<this.program1.length;++i){
			  	this.setTimbre(0,i,this.program1[i]);
			  }
			  for(let i=0;i<this.drummap.length;++i){
					if(this.drummap1[i]){
						this.setTimbre(1,i+35,this.drummap1[i]);
					}
			  }
			}
	  },
	  setTimbre:(m,n,p)=>{
			const defp={g:0,w:"sine",t:1,f:0,v:0.5,a:0,h:0.01,d:0.01,s:0,r:0.05,p:1,q:1,k:0};
			function filldef(p){
			  for(n=0;n<p.length;++n){
					for(let k in defp){
						if(!p[n].hasOwnProperty(k) || typeof(p[n][k])=="undefined"){
						  p[n][k]=defp[k];
						}
					}
			  }
			  return p;
			}
			if(m && n>=35 && n<=81)
			  this.drummap[n-35].p=filldef(p);
			if(m==0 && n>=0 && n<=127)
			  this.program[n].p=filldef(p);
	  },
	  _pruneNote:(nt)=>{
			for(let k=nt.o.length-1;k>=0;--k){
			  if(nt.o[k].frequency){
					nt.o[k].frequency.cancelScheduledValues(0);
			  }else{
					nt.o[k].playbackRate.cancelScheduledValues(0);
			  }
			  nt.g[k].gain.cancelScheduledValues(0);

			  nt.o[k].stop();
			  if(nt.o[k].detune) {
					try {
						this.chmod[nt.ch].disconnect(nt.o[k].detune);
					} catch (e) {}
			  }
			  nt.g[k].gain.value = 0;
			}
	  },
	  _limitVoices:(ch,n)=>{
			this.notetab.sort(function(n1,n2){
			  if(n1.f!=n2.f) return n1.f-n2.f;
			  if(n1.e!=n2.e) return n2.e-n1.e;
			  return n2.t-n1.t;
			});
			for(let i=this.notetab.length-1;i>=0;--i){
			  var nt=this.notetab[i];
			  if(this.actx.currentTime>nt.e || i>=(this.voices-1)){
					if("GM" == this.standMidi){
						this._pruneNote(nt);
					}
					this.notetab.splice(i,1);
			  }
			}
	  },
	  _noteGM:(ch,n,v,t,p)=>{
			this.audioNodes=[];
			this.audioNodesPrm=[];
			this.audioNodesProg=[];

			let out,sc,pn;
			const o=[],g=[],vp=[],fp=[],r=[];
			const f=440*Math.pow(2,(n-69 + this.masterTuningC + this.tuningC[ch] + (this.masterTuningF + this.tuningF[ch]/8192 + this.scaleTuning[ch][n%12]))/12);
			this._limitVoices(ch,n);
			for(let i=0;i<p.length;++i){
			  pn=p[i];
			  const dt=t+pn.a+pn.h;
			  if(pn.g==0)
				out=this.chvol[ch], sc=v*v/16384, fp[i]=f*pn.t+pn.f;
			  else if(pn.g>10)
				out=g[pn.g-11].gain, sc=1, fp[i]=fp[pn.g-11]*pn.t+pn.f;
			  else if(o[pn.g-1].frequency)
				out=o[pn.g-1].frequency, sc=fp[pn.g-1], fp[i]=fp[pn.g-1]*pn.t+pn.f;
			  else
				out=o[pn.g-1].playbackRate, sc=fp[pn.g-1]/440, fp[i]=fp[pn.g-1]*pn.t+pn.f;
			  switch(pn.w[0]){
				  case "n":
						o[i]=this.actx.createBufferSource();
						o[i].buffer=this.noiseBuf[pn.w];
						o[i].loop=true;
						o[i].playbackRate.value=fp[i]/440;
						if(pn.p!=1)
							this._setParamTarget(o[i].playbackRate,fp[i]/440*pn.p,t,pn.q);
						if (o[i].detune) {
							this.chmod[ch].connect(o[i].detune);
							o[i].detune.value=this.bend[ch];
						}
					break;
				  default:
						o[i]=this.actx.createOscillator();
						o[i].frequency.value=fp[i];
						if(pn.p!=1)
							this._setParamTarget(o[i].frequency,fp[i]*pn.p,t,pn.q);
						if(pn.w[0]=="w")
							o[i].setPeriodicWave(this.wave[pn.w]);
						else
							o[i].type=pn.w;
						if (o[i].detune) {
							this.chmod[ch].connect(o[i].detune);
							o[i].detune.value=this.bend[ch];
						}
					break;
			  }
			  g[i]=this.actx.createGain();
			  r[i]=pn.r;
			  o[i].connect(g[i]); g[i].connect(out);
			  vp[i]=sc*pn.v;
			  if(pn.k)
					vp[i]*=Math.pow(2,(n-60)/12*pn.k);
			  if(pn.a){
					g[i].gain.value=0;
					g[i].gain.setValueAtTime(0,t);
					g[i].gain.linearRampToValueAtTime(vp[i],t+pn.a);
			  }else g[i].gain.setValueAtTime(vp[i],t);

			  this._setParamTarget(g[i].gain,pn.s*vp[i],dt,pn.d);
			  o[i].start(t);
			  if(this.rhythm[ch]){
					o[i].onended = ()=>{
						try {
						  // if(o[i].detune)this.chmod[ch].disconnect(o[i].detune);
						}
						catch(e){}
					};
					o[i].stop(t+p[0].d*this.releaseRatio);
		  	}

			  if (o[i].frequency && o[i].detune && undefined == o[i].__resource_id__) {
					let min = Math.ceil(1);
					let max = Math.floor(1000);
					o[i].__resource_id__ = Math.floor(Math.random() * (max - min) + min);
					o[i].frequency.__resource_id__ = o[i].__resource_id__ + 1;
					o[i].detune.__resource_id__    = o[i].__resource_id__ + 2;
			  }
			  if (o[i].buffer && undefined == o[i].__resource_id__) {
					let min = Math.ceil(1);
					let max = Math.floor(1000);
					o[i].__resource_id__ = Math.floor(Math.random() * (max - min) + min);
					o[i].buffer.__resource_id__ = o[i].__resource_id__ + 1;
					o[i].detune.__resource_id__ = o[i].__resource_id__ + 2;
			  }
			  if (g[i] && undefined == g[i].__resource_id__) {
					let min = Math.ceil(1);
					let max = Math.floor(1000);
					g[i].__resource_id__ = Math.floor(Math.random() * (max - min) + min);
					g[i].gain.__resource_id__ = g[i].__resource_id__ + 1;
			  }
			  if (out && undefined == out.__resource_id__) {
					let min = Math.ceil(1);
					let max = Math.floor(1000);
					out.__resource_id__ = Math.floor(Math.random() * (max - min) + min);
			  }
			  if (this.chpan[i] && undefined == this.chpan[i].__resource_id__) {
					let min = Math.ceil(1);
					let max = Math.floor(1000);
					this.chpan[i].__resource_id__ = Math.floor(Math.random() * (max - min) + min);
					this.chpan[i].context.__resource_id__ = this.chpan[i].__resource_id__ + 1;
					this.chpan[i].context.destination.__resource_id__ = this.chpan[i].__resource_id__ + 2;
			  }

			  if (o[i] && g[i] && out) {
					let node1 = o[i].constructor.name + " " + o[i].__resource_id__;
					let node2 = g[i].constructor.name + " " + g[i].__resource_id__;
					let nodeOut = "";

					this.audioNodesProg.push({p : i, "node1" : node1, "node2" : node2});

					if ("AudioParam" == out.constructor.name){
						for (let k = 0; k < o.length; k++){
						  if (o[k].frequency && out.__resource_id__  == o[k].frequency.__resource_id__) {
								nodeOut = o[k].constructor.name + " " + o[k].__resource_id__ + "/f";
								break;
						  }
						}
						for (let k = 0; k < o.length; k++){
						  if (o[k].detune && out.__resource_id__  == o[k].detune.__resource_id__) {
								nodeOut = o[k].constructor.name + " " + o[k].__resource_id__ + "/d";
								break;
						  }
						}
					}else{
						nodeOut = out.constructor.name + " " + out.__resource_id__;
					}

					this.audioNodes.push({"Prog" : i, "node1" : node1, "node2" : node2});
					this.audioNodes.push({"Prog" : i, "node1" : node2, "node2" : nodeOut});

					if ("OscillatorNode" == o[i].constructor.name) {
						this.audioNodesPrm.push({"nodeName" : node1, "osc" : o[i]});
					}
					if ("AudioBufferSourceNode" == o[i].constructor.name) {
						this.audioNodesPrm.push({"nodeName" : node1, "buf" : o[i]});
					}
					if ("GainNode" == g[i].constructor.name) {
						this.audioNodesPrm.push({"nodeName" : node2, "gain" : g[i]});
					}
			  }
			}

			let node1 = this.chvol[ch].constructor.name + " " + this.chvol[ch].__resource_id__;
			let node2 = this.chpan[0].constructor.name + " " + this.chpan[0].__resource_id__;
			this.audioNodes.push({"node1" : node1, "node2" : node2});

			this.audioNodesPrm.push({"nodeName" : node1, "gain" : this.chvol[0]});
			this.audioNodesPrm.push({"nodeName" : node2, "pan" : this.chpan[0]});

			if(!this.rhythm[ch])
			  this.notetab.push({t:t,e:99999,ch:ch,n:n,o:o,g:g,t2:t+pn.a,v:vp,r:r,f:0});

			//Convolver
			if (0 != this.convDuration) {
			  let length   = this.actx.sampleRate * this.convDuration;
			  let impulse  = this.audioContext.createBuffer(2, length, this.actx.sampleRate);
			  let impulseL = impulse.getChannelData(0);
			  let impulseR = impulse.getChannelData(1);

			  for(let i=0; i<length; i++){
					let n = length-i;
					impulseL[i] = (Math.random()*2-1) * Math.pow(1-n/length, this.convDecay);
					impulseR[i] = (Math.random()*2-1) * Math.pow(1-n/length, this.convDecay);
			  }
			  this.conv.buffer = impulse;
			}else{
			  this.conv.buffer=this.convBuf;
			}
	  },
	  _setParamTarget:(p,v,t,d)=>{
			if(d!=0)
			  p.setTargetAtTime(v,t,d);
			else
			  p.setValueAtTime(v,t);
	  },
	  _releaseNote:(nt,t)=>{
			if(nt.ch!=9){
			  for(let k=nt.g.length-1;k>=0;--k){
					nt.g[k].gain.cancelScheduledValues(t);
					if(t==nt.t2)
						nt.g[k].gain.setValueAtTime(nt.v[k],t);
					else if(t<nt.t2)
						nt.g[k].gain.setValueAtTime(nt.v[k]*(t-nt.t)/(nt.t2-nt.t),t);
					this._setParamTarget(nt.g[k].gain,0,t,nt.r[k]);
				}
			}
			nt.e=t+nt.r[0]*this.releaseRatio;
			nt.f=1;
	  },
	  setModulation:(ch,v,t)=>{
			this.chmod[ch].gain.setValueAtTime(v*100/127,this._tsConv(t));
	  },
	  setChVol:(ch,v,t)=>{
	  	this.vol[ch]=v*v/(127*127);
			this.chvol[ch].gain.setValueAtTime(this.vol[ch]*this.ex[ch],this._tsConv(t));
			if(this.arrTrkCh[ch]){
				let vol = document.getElementById("part1_"+this.arrTrkCh[ch]+"_vol");
				if(vol) vol.value = v;
			}
	  },
	  setPan:(ch,v,t)=>{
			if(this.chpan[ch]){
				this.chpan[ch].pan.setValueAtTime((v-64)/64,this._tsConv(t));
			}
			if(this.arrTrkCh[ch]){
				let pan = document.getElementById("part1_"+this.arrTrkCh[ch]+"_pan");
				if(pan) pan.value = v;
			}
	  },
	  setExpression:(ch,v,t)=>{
			this.ex[ch]=v*v/(127*127);
			this.chvol[ch].gain.setValueAtTime(this.vol[ch]*this.ex[ch],this._tsConv(t));
	  },
	  setSustain:(ch,v,t)=>{
			this.sustain[ch]=v;
			t=this._tsConv(t);
			if(v<64){
			  for(let i=this.notetab.length-1;i>=0;--i){
					const nt=this.notetab[i];
					if(t>=nt.t && nt.ch==ch && nt.f==1)this._releaseNote(nt,t);
			  }
			}
	  },
	  setSostenuto:(ch,v,t)=>{
		this.sostenuto[ch]=v;

	  },
	  setSoftPedal:(ch,v,t)=>{
		this.softPedal[ch]=v;

	  },
	  allSoundOff:(ch, actx)=>{
	  	if("GM" == this.standMidi){
	  		for(let i=this.notetab.length-1;i>=0;--i){
				  const nt=this.notetab[i];
				  if(nt.ch==ch){
						this._pruneNote(nt);
						this.notetab.splice(i,1);
					}
			  }
	  	}else{
	  		if(this.notesSF[ch]){
					for (let i = 0; i < this.notesSF[ch].length; i++) {
						if(this.notesSF[ch][i])this.notesSF[ch][i](actx.currentTime);
						this.notesSF[ch].splice(i, 1);
						this.notetab.splice(i,1);
						i--;
					}
				}
	  	}
	  },
	  resetAllControllers:(ch)=>{
			this.bend[ch]=0; this.ex[ch]=1.0;
			this.rpnidx[ch]=0x3fff; this.sustain[ch]=0; this.sostenuto[ch]=0; this.softPedal[ch]=0;
			if(this.chvol[ch]){
			  this.chvol[ch].gain.value=this.vol[ch]*this.ex[ch];
			  this.chmod[ch].gain.value=0;
			}
	  },
	  setBendRange:(ch,v)=>{
			this.brange[ch]=v;
	  },
	  setProgram:(ch,v)=>{
			if(this.debug)
			  console.log("Pg("+ch+")="+v);
			this.pg[ch]=v;
	  },
	  _tsConv:(t)=>{
			if(t==undefined || t<=0){
			  t=0;
			  if(this.actx)t=this.actx.currentTime;
			}else{
			  if(this.tsmode)t=t*.001-this.tsdiff;
			}
			return t;
	  },
	  setBend:(ch,v,t)=>{
			t=this._tsConv(t);
			const br=this.brange[ch]*100/127;
			this.bend[ch]=(v-8192)*br/8192;
			for(let i=this.notetab.length-1;i>=0;--i){
			  const nt=this.notetab[i];
			  if(nt.ch==ch){
					for(let k=nt.o.length-1;k>=0;--k){
					if(nt.o[k].frequency)
					  if (nt.o[k].detune) nt.o[k].detune.setValueAtTime(this.bend[ch],t);
					}
			  }
			}
	  },
	  noteOff:(ch,n,t)=>{
			if(this.arrTrkCh[ch]){
				let speaker    = document.getElementById("speaker_"+this.arrTrkCh[ch]);
				let speakerOn  = document.getElementById("speakerOn_"+this.arrTrkCh[ch]);
				let speakerOff = document.getElementById("speakerOff_"+this.arrTrkCh[ch]);

			  if("block" !== speakerOff.style.display){
					speakerOn.setAttribute("style","display:none;width:16px;height:16px;cursor:pointer;");
					speaker.setAttribute("style","display:block;width:16px;height:16px;cursor:pointer;");
			  }
			}

			if(this.rhythm[ch])
			  return;
			t=this._tsConv(t);
			if("GM" == this.standMidi){
				for(let i=this.notetab.length-1;i>=0;--i){
				  const nt=this.notetab[i];
				  if(t>=nt.t && nt.ch==ch && nt.n==n && nt.f==0){
						nt.f=1;
						if(this.sustain[ch]<64)this._releaseNote(nt,t);
				  }
				}
			}else{
				if(this.notesSF[ch] && this.notesSF[ch][n]){
					this.notesSF[ch][n](this.actx.currentTime);
					this.notesSF[ch][n] = null;
				}
				for(let i=this.notetab.length-1;i>=0;--i){
				  var nt=this.notetab[i];
				  if(nt.ch == ch && nt.n == n){
						this.notetab.splice(i,1);
				  }
				}
			}
	  },
	  noteOn:(ch,n,v,t)=>{
			if(this.arrTrkCh[ch]){
				let speaker    = document.getElementById("speaker_"+this.arrTrkCh[ch]);
				let speakerOn  = document.getElementById("speakerOn_"+this.arrTrkCh[ch]);
				let speakerOff = document.getElementById("speakerOff_"+this.arrTrkCh[ch]);

			  if("block" !== speakerOff.style.display){
					speakerOn.setAttribute("style","display:block;width:16px;height:16px;cursor:pointer;");
					speaker.setAttribute("style","display:none;width:16px;height:16px;cursor:pointer;");
			  }
			}

			if(v==0){
			  this.noteOff(ch,n,t);
			  return;
			}
			t=this._tsConv(t);
			if(this.rhythm[ch]){
				if("GM" == this.standMidi){
					if(n>=35&&n<=81) this._noteGM(ch,n,v,t,this.drummap[n-35].p);
				}else{
					if(n>=35&&n<=81) this._noteGM(ch,n,v,this.actx.currentTime,this.drummap[n-35].p);
				}
			  return;
			}
			if("GM" == this.standMidi){
				this._noteGM(ch,n,v,t,this.program[this.pg[ch]].p);
			}else{
				this.notetab.push({ch:ch,n:n});

				// "GS", "XG" - standard
				let curProg = this.pg[ch];
				let dataNote = soundFont.getKeyData(this.numBank, curProg, n);

				if(null !== dataNote){
					let preset = dataNote.preset;
					let vol = this.chvol[ch].gain.value/3;
					let vel = v;
					let exp = this.ex[ch];

					// Инициализируем массив каналов, если его еще нет
					if (undefined == this.notesSF[ch]) this.notesSF[ch] = [];

					// Если эта нота УЖЕ звучит на этом канале — глушим старую немедленно
					if (typeof this.notesSF[ch][n] === 'function') {
						this.notesSF[ch][n](this.actx.currentTime);
						this.notesSF[ch][n] = null;
					}

					// КРИТИЧЕСКОЕ ИСПРАВЛЕНИЕ: Создаем изолированный контейнер для ЭТОЙ конкретной 64-й ноты.
					// Даже если через 1 миллисекунду прилетит следующая нота, она создаст СВОЙ контейнер,
					// и они не перепутают свои флаги отмены!
					const noteState = {
						isCancelled: false,
						cancelTime: 0,
						realRelease: null
					};

					// МГНОВЕННО записываем в ядро защитную функцию-перехватчик.
					// Если метод noteOff() вызовется до того, как сэмпл прочитается с диска,
					// он вызовет эту функцию, и мы взведем флаг конкретно для ЭТОГО вызова.
					this.notesSF[ch][n] = (end = this.actx.currentTime) => {
						noteState.isCancelled = true;
						noteState.cancelTime = end;

						// Если реальный звук уже успел запуститься к моменту noteOff, глушим его
						if (typeof noteState.realRelease === 'function') {
							noteState.realRelease(end);
						}
					};

					// Запускаем асинхронное чтение Blob и старт ноты
					startPresetNote(this.actx, preset, n, vel, vol, exp, this.out).then(releaseFn => {

						// 1. ПЕРЕХВАТ ОПЕРЕЖЕНИЯ: Если noteOff успел прилететь, пока читали с диска
						if (noteState.isCancelled) {
							releaseFn(this.actx.currentTime);
							if (this.notesSF[ch][n] && !this.notesSF[ch][n].isRealRelease) {
								this.notesSF[ch][n] = null;
							}
							return;
						}

						// Маркируем реальную функцию релиза
						releaseFn.isRealRelease = true;
						noteState.realRelease = releaseFn;

						// 2. ЗАКРЕПЛЯЕМ НАСТОЯЩИЙ РЕЛИЗ В ЯДРЕ ДЛЯ КЛАВИАТУРЫ И МИДИ
						// Если ячейка ядра свободна или содержит старую заглушку — пишем туда полноценный релиз.
						// Благодаря этому, когда вы ОТПУСТИТЕ клавишу на M-AUDIO, сработает clearTimeout и нота затихнет!
						if (this.notesSF[ch][n] && !this.notesSF[ch][n].isRealRelease) {
							this.notesSF[ch][n] = (end = this.actx.currentTime) => {
								// Если музыкант сам отпустил клавишу — СРОЧНО отменяем авто-таймер!
								if (noteState.timeoutId) {
									clearTimeout(noteState.timeoutId);
									noteState.timeoutId = null;
								}
								releaseFn(end);
							};
							this.notesSF[ch][n].isRealRelease = true;
						}

						// 3. СПЕЦИФИКА ВАШЕГО КОДА ДЛЯ ФИКСИРОВАННОЙ ДЛИНЫ (t == 0.5)
						if (0.5 == t) {
							releaseFn(this.actx.currentTime + t);
							if (this.notesSF[ch][n] && this.notesSF[ch][n].isRealRelease) {
								this.notesSF[ch][n] = null;
							}
						}
						// 4. УМНЫЙ ОГРАНИЧИТЕЛЬ С ЗАСЛОНКОЙ ОТ ЖИВОЙ ИГРЫ
						else {
							// Мы взводим таймер авто-тушения на 400 мс для ВСЕХ нот БЕЗУСЛОВНО.
							// НО! Если вы играете с клавиатуры, метод noteOff() вызовется в момент отпускания клавиши.
							// А что делать, если вы удерживаете ноту на Органе ДОЛЬШЕ 400 мс?
							// Чтобы она не оборвалась, мы проверяем: если эта нота ДО СИХ ПОР удерживается в ядре
							// (то есть в this.notesSF[ch][n] сидит наш релиз), мы НЕ БУДЕМ её тушить!
							noteState.timeoutId = setTimeout(() => {
								if (noteState.realRelease === releaseFn) {

									// КРИТИЧЕСКАЯ ПРОВЕРКА: Если музыкант ВСЕ ЕЩЕ ДЕРЖИТ клавишу органа на клавиатуре M-AUDIO,
									// значит в ячейке ядра notesSF[ch][n] лежит активная функция.
									// В таком случае мы ИГНОРИРУЕМ этот таймер и даем органу звучать бесконечно!
									if (this.notesSF[ch] && this.notesSF[ch][n] && this.notesSF[ch][n].isRealRelease) {
										// Музыкант держит клавишу — ничего не делаем, таймер аннулирован!
										return;
									}

									// Если же в ячейке ядра пусто, или там сидит чужая нота (клик секвенсора),
									// мы экстренно тушим этот зависший звук!
									releaseFn(this.actx.currentTime);
									if (this.notesSF[ch][n] && this.notesSF[ch][n].isRealRelease) {
										this.notesSF[ch][n] = null;
									}
								}
							}, 400); // Поставим 400 мс для надежности, чтобы секвенсор успел сообразить
						}

					}).catch(err => {
						console.error("LiteWebSynthCore: Критическая ошибка ленивой загрузки сэмпла ноты:", err);
						this.notesSF[ch][n] = null;
					});
				}
			}
	  },
	  setTsMode:(tsmode)=>{
			this.tsmode=tsmode;
	  },
	  send:(msg,t)=>{    /* send midi message */
			const ch=msg[0]&0xf;
			const cmd=msg[0]&~0xf;

			if(cmd>=0x100)
			  return;
			if(this.audioContext.state=="suspended"){
			  this.audioContext.resume();
			}
			switch(cmd){
				case 0xb0:  /* ctl change */
				  switch(msg[1]){
					  case 1:  this.setModulation(ch,msg[2],t); break;
					  case 7:  this.setChVol(ch,msg[2],t); break;
					  case 10: this.setPan(ch,msg[2],t); break;
					  case 11: this.setExpression(ch,msg[2],t); break;
					  case 64: this.setSustain(ch,msg[2],t); break;
					  case 66: this.setSostenuto(ch,msg[2],t); break;
					  case 67: this.setSoftPedal(ch,msg[2],t); break;
					  case 98:  case 99: this.rpnidx[ch]=0x3fff; break; /* nrpn lsb/msb */
					  case 100: this.rpnidx[ch]=(this.rpnidx[ch]&0x3f80)|msg[2]; break; /* rpn lsb */
					  case 101: this.rpnidx[ch]=(this.rpnidx[ch]&0x7f)|(msg[2]<<7); break; /* rpn msb */
					  case 6:  /* data entry msb */
							switch (this.rpnidx[ch]) {
								case 0:
								  this.brange[ch]=(msg[2]<<7)+(this.brange[ch]&0x7f);
								  break;
								case 1:
								  this.tuningF[ch]=(msg[2]<<7)+((this.tuningF[ch]+0x2000)&0x7f)-0x2000;
								  break;
								case 2:
								  this.tuningC[ch]=msg[2]-0x40;
								  break;
							}
							break;
						  case 38:  /* data entry lsb */
								switch (this.rpnidx[ch]) {
									case 0:
									  this.brange[ch]=(this.brange[ch]&0x3f80)|msg[2];
									  break;
									case 1:
									  this.tuningF[ch]=(((this.tuningF[ch]+0x2000)&0x3f80)|msg[2])-0x2000;
									  break;
									case 2: break;
								}
						  break;
						  case 120:  /* all sound off */
						  case 123:  /* all notes off */
						  case 124: case 125: case 126: case 127: /* omni off/on mono/poly */
							this.allSoundOff(ch, this.actx);
							break;
						  case 121: this.resetAllControllers(ch); break;
						}
					break;
				case 0xc0: this.setProgram(ch,msg[1]); break;
				case 0xe0: if("GM" == this.standMidi) this.setBend(ch,(msg[1]+(msg[2]<<7)),t); break;
				case 0x20: this.numBank = msg[1]; break;
				case 0x90:
					if(ch == this.channelSel && "" !== this.channelSel) {
						if(0 !== msg[2]){
							this.notesOn.push(msg[1]);
							kbd.setNote(1, msg[1]);
						}else{
							kbd.setNote(0, msg[1]);
						}
					}
				  this.noteOn(ch,msg[1],msg[2],t);
				  break;
				case 0x80:
					if(ch == this.channelSel && "" !== this.channelSel) {
						kbd.setNote(0, msg[1]);
					}
				  this.noteOff(ch,msg[1],t);
				  break;
				case 0xf0:
				  if (msg[0] == 0xff) {
						this.reset();
						break;
				  }
				  if(msg[0]!=254 && this.debug){
						var ds=[];
						for(let ii=0;ii<msg.length;++ii)
							ds.push(msg[ii].toString(16));
				  }
				  if (msg[0]==0xf0) {
						if (msg[1]==0x7f && msg[3]==4) {
							if (msg[4]==3 && msg.length >= 8) { // Master Fine Tuning
							  this.masterTuningF = (msg[6]*0x80 + msg[5] - 8192) / 8192;
							}
							if (msg[4]==4 && msg.length >= 8) { // Master Coarse Tuning
							  this.masterTuningC = msg[6]-0x40;
							}
						}
						if (msg[1]==0x41 && msg[3]==0x42 && msg[4]==0x12 &&msg[5]==0x40) { // GS
							if ((msg[6]&0xf0)==0x10 && msg.length==11) {
							  const c=[9,0,1,2,3,4,5,6,7,8,10,11,12,13,14,15][msg[6]&0xf];
							  if (msg[7]==0x15) {
									this.rhythm[c]=msg[8];
							  }else if (msg[7] >= 0x40 && msg[7] <= 0x4b) { // Scale Tuning
									this.scaleTuning[c][msg[7]-0x40] = (msg[8]-0x40) / 100;
							  }
							}else if (msg[6]==0) {
							  if (msg[7]==0 && msg.length==14) { // Master Tuning
									this.masterTuningF = (msg[8]*0x1000 + msg[9]*0x100 + msg[10]*0x10 + msg[11] - 0x400) / 1000;
							  }else if (msg[7]==5 && msg.length==11) { // Master Transpose
									this.masterTuningC = msg[8]-0x40;
							  }
							}
						}
					}
				break;
			}
	  },
	  _createWave:(w)=>{
			const imag=new Float32Array(w.length);
			const real=new Float32Array(w.length);
			for(let i=1;i<w.length;++i)
			  imag[i]=w[i];
			return this.actx.createPeriodicWave(real,imag);
	  },
	  getAudioNodesProg:()=>{
			return this.audioNodesProg;
	  },
	  getAudioNodesCtx:()=>{
			return this.audioNodesCtx;
	  },
	  getAudioNodes:()=>{
			return this.audioNodes;
	  },
	  getAudioNodesPrm:()=>{
			return Array.from(new Set(this.audioNodesPrm));
	  },
	  getAudioNodesCtxPrm:()=>{
			return Array.from(new Set(this.audioNodesCtxPrm));
	  },
	  getAnalyzerMain:()=>{
			return this.analys;
	  },
	  getAudioContext:()=>{
			return this.actx;
	  },
	  setAudioContext:(actx,dest)=>{
			this.audioNodesCtx=[];
			this.audioNodesCtxPrm=[];

			this.audioContext=this.actx=actx;
			this.dest=dest;
			if(!dest)
			  this.dest=actx.destination;
			this.tsdiff=performance.now()*.001-this.actx.currentTime;
			if(this.debug)
			  console.log("TSDiff:"+this.tsdiff);
			this.out=this.actx.createGain();
			this.comp=this.actx.createDynamicsCompressor();
			this.conv=this.actx.createConvolver();
			var blen=this.actx.sampleRate*.5|0
			this.convBuf=this.actx.createBuffer(2,blen,this.actx.sampleRate);
			this.conv.buffer=this.convBuf;
			this.noiseBuf={};
			this.noiseBuf.n0=this.actx.createBuffer(1,blen,this.actx.sampleRate);
			this.noiseBuf.n1=this.actx.createBuffer(1,blen,this.actx.sampleRate);
			var d1=this.convBuf.getChannelData(0);
			var d2=this.convBuf.getChannelData(1);
			var dn=this.noiseBuf.n0.getChannelData(0);
			var dr=this.noiseBuf.n1.getChannelData(0);
			for(let i=0;i<blen;++i){
			  if(i/blen<Math.random()){
				d1[i]=Math.exp(-3*i/blen)*(Math.random()-.5)*.5;
				d2[i]=Math.exp(-3*i/blen)*(Math.random()-.5)*.5;
			  }
			  dn[i]=Math.random()*2-1;
			}
			for(let jj=0;jj<64;++jj){
			  const r1=Math.random()*10+1;
			  const r2=Math.random()*10+1;
			  for(let i=0;i<blen;++i){
				var dd=Math.sin((i/blen)*2*Math.PI*440*r1)*Math.sin((i/blen)*2*Math.PI*440*r2);
				dr[i]+=dd/8;
			  }
			}
			if(this.useReverb){
			  this.rev=this.actx.createGain();
			  this.rev.gain.value=this.reverbLev;
			  this.out.connect(this.conv);
			  this.conv.connect(this.rev);
			  this.rev.connect(this.comp);

			  if (this.comp && undefined == this.comp.__resource_id__) {
					let min = Math.ceil(1);
					let max = Math.floor(1000);
					this.comp.__resource_id__ = Math.floor(Math.random() * (max - min) + min);
			  }
			  if (this.dest && undefined == this.dest.__resource_id__) {
					let min = Math.ceil(1);
					let max = Math.floor(1000);
					this.dest.__resource_id__ = Math.floor(Math.random() * (max - min) + min);
			  }
			  if (this.out && undefined == this.out.__resource_id__) {
					let min = Math.ceil(1);
					let max = Math.floor(1000);
					this.out.__resource_id__ = Math.floor(Math.random() * (max - min) + min);
					this.out.gain.__resource_id__ = this.out.__resource_id__ + 1;
			  }
			  if (this.conv && undefined == this.conv.__resource_id__) {
					let min = Math.ceil(1);
					let max = Math.floor(1000);
					this.conv.__resource_id__ = Math.floor(Math.random() * (max - min) + min);
			  }
			  if (this.rev && undefined == this.rev.__resource_id__) {
					let min = Math.ceil(1);
					let max = Math.floor(1000);
					this.rev.__resource_id__ = Math.floor(Math.random() * (max - min) + min);
			  }
			  let node1 = this.out.constructor.name + " " + this.out.__resource_id__;
			  let node2 = this.conv.constructor.name + " " + this.conv.__resource_id__;

			  this.audioNodesCtx.push({"node1" : node1, "node2" : node2});
			  this.audioNodesCtxPrm.push({"nodeName" : node1, "gain" : this.out});
			  this.audioNodesCtxPrm.push({"nodeName" : node2, "conv" : this.conv});

			  node1 = this.conv.constructor.name + " " + this.conv.__resource_id__;
			  node2 = this.rev.constructor.name + " " + this.rev.__resource_id__;

			  this.audioNodesCtx.push({"node1" : node1, "node2" : node2});
			  this.audioNodesCtxPrm.push({"nodeName" : node2, "gain" : this.rev});

			  node1 = this.rev.constructor.name + " " + this.rev.__resource_id__;
			  node2 = this.comp.constructor.name + " " + this.comp.__resource_id__;

			  this.audioNodesCtx.push({"node1" : node1, "node2" : node2});
			  this.audioNodesCtxPrm.push({"nodeName" : node2, "comp" : this.comp});
			}

			this.setMasterVol();
			this.out.connect(this.comp);
			this.comp.connect(this.dest);
			this.chvol=[]; this.chmod=[]; this.chpan=[];
			this.wave={"w9999":this._createWave("w9999")};
			this.lfo=this.actx.createOscillator();
			this.lfo.frequency.value=5;
			this.lfo.start(0);
			for(let i=0;i<16;++i){
			  this.chvol[i]=this.actx.createGain();
			  this.chvol[i].gain.value = 0.3;
			  if(this.actx.createStereoPanner){
			    this.chpan[i]=this.actx.createStereoPanner();
			    this.chvol[i].connect(this.chpan[i]);
			    this.chpan[i].connect(this.out);
			  }
			  else{
			    this.chpan[i]=null;
			    this.chvol[i].connect(this.out);
			  }
			  this.chmod[i]=this.actx.createGain();
			  this.lfo.connect(this.chmod[i]);
			  this.pg[i]=0;
			  this.resetAllControllers(i);
			}
			this.setReverbLev();
			this.reset();

			// Add Analyser.
			this.analysGain = this.actx.createGain();
			this.analys     = this.actx.createAnalyser();

			if (this.analysGain && undefined == this.analysGain.__resource_id__) {
			  let min = Math.ceil(1);
			  let max = Math.floor(1000);
			  this.analysGain.__resource_id__ = Math.floor(Math.random() * (max - min) + min);
			  this.analysGain.gain.__resource_id__ = this.analysGain.__resource_id__ + 1;
			}
			if (this.analys && undefined == this.analys.__resource_id__) {
			  let min = Math.ceil(1);
			  let max = Math.floor(1000);
			  this.analys.__resource_id__ = Math.floor(Math.random() * (max - min) + min);
			}

			this.comp.connect(this.analysGain);
			this.analysGain.connect(this.analys);

			let node1 = this.out.constructor.name + " " + this.out.__resource_id__;
			let node2 = this.comp.constructor.name + " " + this.comp.__resource_id__;
			this.audioNodesCtx.push({"node1" : node1, "node2" : node2});

			node1 = this.comp.constructor.name + " " + this.comp.__resource_id__;
			node2 = this.dest.constructor.name + " " + this.dest.__resource_id__;
			this.audioNodesCtx.push({"node1" : node1, "node2" : node2});
			this.audioNodesCtxPrm.push({"nodeName" : node2, "dest" : this.dest});

			node1 = this.comp.constructor.name + " " + this.comp.__resource_id__;
			node2 = this.analysGain.constructor.name + " " + this.analysGain.__resource_id__;
			this.audioNodesCtx.push({"node1" : node1, "node2" : node2});
			this.audioNodesCtxPrm.push({"nodeName" : node2, "gain" : this.analysGain});

			node1 = this.analysGain.constructor.name + " " + this.analysGain.__resource_id__;
			node2 = this.analys.constructor.name + " " + this.analys.__resource_id__;
			this.audioNodesCtx.push({"node1" : node1, "node2" : node2});
			this.audioNodesCtxPrm.push({"nodeName" : node2, "analys" : this.analys});
	  },
  });
}
if(window && window.customElements){
  class WebAudioTinySynthElement extends HTMLElement {
		constructor(){
			super();
		}
		connectedCallback(){
			const div = document.createElement("div");
			div.innerHTML=
		  `<canvas
			id='wa-canvas' width='290' height='32'
			touch-action='none' tabindex='0'
			style='
			position:relative;
			margin:0;
			width:290px;
			height:32px;
			border: 1px solid rgb(72, 72, 72);
			'
		  ></canvas>
		  <div id='wa-logo'
			style='
			display:none;
			position:absolute;
			top:5px;
			left:5px;
			color:#fff;
			font-size:8px;
			background:rgba(0,0,0,0.5);
			border: 1px solid rgb(72, 72, 72);
			'
		  >TinySynth</div>`;

			this.getAttr = (n,def)=>{
			  let v=this.getAttribute(n);
			  if(v==""||v==null) return def;
			  switch(typeof(def)){
			  case "number":
				if(v=="true") return 1;
				v=+v;
				if(isNaN(v)) return 0;
				return v;
			  }
			  return v;
			};

			this.canvas = div.children[0];
			this.appendChild(div);
			WebAudioTinySynthCore.bind(this)(this);
			const plist=this.properties;
			for(let k in plist){
			  const v = plist[k];
			  if(v.observer){
					this["_"+k] = v.value;
					Object.defineProperty(this, k, {
						get:()=>{return this["_"+k]},
						set:(val)=>{
						  this["_"+k] = val;
						  this[v.observer]();
						}
					});
			  }else{
					this[k]=v;
			  }
			}
			for(let k in plist){
			  const v = plist[k];
			  this[k] = this.getAttr(k,v.value);
			}
			this.standMidi = "GM";
			this.setQuality(1);
			this.init();
			this._guiInit.bind(this)();
			setInterval(this._guiUpdate.bind(this),100);
		}
  }
  window.customElements.define('webaudio-tinysynth', WebAudioTinySynthElement);
}

class WebAudioTinySynth {
  constructor(opt){
		WebAudioTinySynthCore.bind(this)(this);
		for(let k in this.properties){
			this[k]=this.properties[k].value;
		}
		this.standMidi = "GM";
		this.setQuality(1);
		if(opt){
			if(opt.useReverb!=undefined)
			  this.useReverb=opt.useReverb;
			if(opt.quality!=undefined)
			  this.setQuality(opt.quality);
			if(opt.voices!=undefined)
			  this.setVoices(opt.voices);
		}
		this.init();
  }
}

if(typeof exports === 'object' && typeof module !== 'undefined'){
  module.exports = WebAudioTinySynth;
}
else if(typeof define === 'function' && define.amd){
  define(function(){
		return WebAudioTinySynth;
  });
}
else{
  window.WebAudioTinySynth = WebAudioTinySynth;
}

})(this);