PageSourceSearch

https://musicalgorithms.org/midi/js/midi/plugin.webaudio.js

js musicalgorithms.org collected 2026-10-05 00:47:06 UTC 9,335 bytes, 329 lines download raw bytes

1/*
2	----------------------------------------------------------
3	Web Audio API - OGG or MPEG Soundbank
4	----------------------------------------------------------
5	http://webaudio.github.io/web-audio-api/
6	----------------------------------------------------------
7*/
8
9(function(root) { 'use strict';
10
11	window.AudioContext && (function() {
12		var audioContext = null; // new AudioContext();
13		var useStreamingBuffer = false; // !!audioContext.createMediaElementSource;
14		var midi = root.WebAudio = {api: 'webaudio'};
15		var ctx; // audio context
16		var sources = {};
17		var effects = {};
18		var masterVolume = 127;
19		var audioBuffers = {};
20		///
21		midi.audioBuffers = audioBuffers;
22		midi.send = function(data, delay) { };
23		midi.setController = function(channelId, type, value, delay) { };
24
25		midi.setVolume = function(channelId, volume, delay) {
26			if (delay) {
27				setTimeout(function() {
28					masterVolume = volume;
29				}, delay * 1000);
30			} else {
31				masterVolume = volume;
32			}
33		};
34
35		midi.programChange = function(channelId, program, delay) {
36// 			if (delay) {
37// 				return setTimeout(function() {
38// 					var channel = root.channels[channelId];
39// 					channel.instrument = program;
40// 				}, delay);
41// 			} else {
42				var channel = root.channels[channelId];
43				channel.instrument = program;
44// 			}
45		};
46
47		midi.pitchBend = function(channelId, program, delay) {
48// 			if (delay) {
49// 				setTimeout(function() {
50// 					var channel = root.channels[channelId];
51// 					channel.pitchBend = program;
52// 				}, delay);
53// 			} else {
54				var channel = root.channels[channelId];
55				channel.pitchBend = program;
56// 			}
57		};
58
59		midi.noteOn = function(channelId, noteId, velocity, delay) {
60			delay = delay || 0;
61
62			/// check whether the note exists
63			var channel = root.channels[channelId];
64			var instrument = channel.instrument;
65			var bufferId = instrument + '' + noteId;
66			var buffer = audioBuffers[bufferId];
67			if (!buffer) {
68// 				console.log(MIDI.GM.byId[instrument].id, instrument, channelId);
69				return;
70			}
71
72			/// convert relative delay to absolute delay
73			if (delay < ctx.currentTime) {
74				delay += ctx.currentTime;
75			}
76		
77			/// create audio buffer
78			if (useStreamingBuffer) {
79				var source = ctx.createMediaElementSource(buffer);
80			} else { // XMLHTTP buffer
81				var source = ctx.createBufferSource();
82				source.buffer = buffer;
83			}
84
85			/// add effects to buffer
86			if (effects) {
87				var chain = source;
88				for (var key in effects) {
89					chain.connect(effects[key].input);
90					chain = effects[key];
91				}
92			}
93
94			/// add gain + pitchShift
95			var gain = (velocity / 127) * (masterVolume / 127) * 2 - 1;
96			source.connect(ctx.destination);
97			source.playbackRate.value = 1; // pitch shift 
98			source.gainNode = ctx.createGain(); // gain
99			source.gainNode.connect(ctx.destination);
100			source.gainNode.gain.value = Math.min(1.0, Math.max(-1.0, gain));
101			source.connect(source.gainNode);
102			///
103			if (useStreamingBuffer) {
104				if (delay) {
105					return setTimeout(function() {
106						buffer.currentTime = 0;
107						buffer.play()
108					}, delay * 1000);
109				} else {
110					buffer.currentTime = 0;
111					buffer.play()
112				}
113			} else {
114				source.start(delay || 0);
115			}
116			///
117			sources[channelId + '' + noteId] = source;
118			///
119			return source;
120		};
121
122		midi.noteOff = function(channelId, noteId, delay) {
123			delay = delay || 0;
124
125			/// check whether the note exists
126			var channel = root.channels[channelId];
127			var instrument = channel.instrument;
128			var bufferId = instrument + '' + noteId;
129			var buffer = audioBuffers[bufferId];
130			if (buffer) {
131				if (delay < ctx.currentTime) {
132					delay += ctx.currentTime;
133				}
134				///
135				var source = sources[channelId + '' + noteId];
136				if (source) {
137					if (source.gainNode) {
138						// @Miranet: 'the values of 0.2 and 0.3 could of course be used as 
139						// a 'release' parameter for ADSR like time settings.'
140						// add { 'metadata': { release: 0.3 } } to soundfont files
141						var gain = source.gainNode.gain;
142						gain.linearRampToValueAtTime(gain.value, delay);
143						gain.linearRampToValueAtTime(-1.0, delay + 0.3);
144					}
145					///
146					if (useStreamingBuffer) {
147						if (delay) {
148							setTimeout(function() {
149								buffer.pause();
150							}, delay * 1000);
151						} else {
152							buffer.pause();
153						}
154					} else {
155						if (source.noteOff) {
156							source.noteOff(delay + 0.5);
157						} else {
158							source.stop(delay + 0.5);
159						}
160					}
161					///
162					delete sources[channelId + '' + noteId];
163					///
164					return source;
165				}
166			}
167		};
168
169		midi.chordOn = function(channel, chord, velocity, delay) {
170			var res = {};
171			for (var n = 0, note, len = chord.length; n < len; n++) {
172				res[note = chord[n]] = midi.noteOn(channel, note, velocity, delay);
173			}
174			return res;
175		};
176
177		midi.chordOff = function(channel, chord, delay) {
178			var res = {};
179			for (var n = 0, note, len = chord.length; n < len; n++) {
180				res[note = chord[n]] = midi.noteOff(channel, note, delay);
181			}
182			return res;
183		};
184
185		midi.stopAllNotes = function() {
186			for (var sid in sources) {
187				var delay = 0;
188				if (delay < ctx.currentTime) {
189					delay += ctx.currentTime;
190				}
191				var source = sources[sid];
192				if (source.gainNode && source.gainNode.gain) {
193					var gain = source.gainNode.gain;
194					gain.linearRampToValueAtTime(gain.value, delay);
195					gain.linearRampToValueAtTime(0, delay + 0.05);
196				}
197				if (source.noteOff) { // old api
198					source.noteOff(delay + 0.05);
199				} else { // new api
200					source.stop(delay + 0.05);
201				}
202				delete sources[sid];
203			}
204		};
205
206		midi.setEffects = function(list) {
207			if (ctx.tunajs) {
208				for (var n = 0; n < list.length; n ++) {
209					var data = list[n];
210					var effect = new ctx.tunajs[data.type](data);
211					effect.connect(ctx.destination);
212					effects[data.type] = effect;
213				}
214			} else {
215				return console.log('Effects module not installed.');
216			}
217		};
218
219		midi.connect = function(opts) {
220			root.setDefaultPlugin(midi);
221			midi.setContext(ctx || createAudioContext(), opts.onsuccess);
222		};
223	
224		midi.getContext = function() {
225			return ctx;
226		};
227	
228		midi.setContext = function(newCtx, onload, onprogress, onerror) {
229			ctx = newCtx;
230
231			/// tuna.js effects module - https://github.com/Dinahmoe/tuna
232			if (typeof Tuna !== 'undefined' && !ctx.tunajs) {
233				ctx.tunajs = new Tuna(ctx);
234			}
235		
236			/// loading audio files
237			var urls = [];
238			var notes = root.keyToNote;
239			for (var key in notes) urls.push(key);
240			///
241			var waitForEnd = function(instrument) {
242				for (var key in bufferPending) { // has pending items
243					if (bufferPending[key]) return;
244				}
245				///
246				if (onload) { // run onload once
247					onload();
248					onload = null;
249				}
250			};
251			///
252			var requestAudio = function(soundfont, instrumentId, index, key) {
253				var url = soundfont[key];
254				if (url) {
255					bufferPending[instrumentId] ++;
256					loadAudio(url, function(buffer) {
257						buffer.id = key;
258						var noteId = root.keyToNote[key];
259						audioBuffers[instrumentId + '' + noteId] = buffer;
260						///
261						if (-- bufferPending[instrumentId] === 0) {
262							var percent = index / 87;
263// 							console.log(MIDI.GM.byId[instrumentId], 'processing: ', percent);
264							soundfont.isLoaded = true;
265							waitForEnd(instrument);
266						}
267					}, function(err) {
268		// 				console.log(err);
269					});
270				}
271			};
272			///
273			var bufferPending = {};
274			for (var instrument in root.Soundfont) {
275				var soundfont = root.Soundfont[instrument];
276				if (soundfont.isLoaded) {
277					continue;
278				}
279				///
280				var synth = root.GM.byName[instrument];
281				var instrumentId = synth.number;
282				///
283				bufferPending[instrumentId] = 0;
284				///
285				for (var index = 0; index < urls.length; index++) {
286					var key = urls[index];
287					requestAudio(soundfont, instrumentId, index, key);
288				}
289			}
290			///
291			setTimeout(waitForEnd, 1);
292		};
293
294		/* Load audio file: streaming | base64 | arraybuffer
295		---------------------------------------------------------------------- */
296		function loadAudio(url, onload, onerror) {
297			if (useStreamingBuffer) {
298				var audio = new Audio();
299				audio.src = url;
300				audio.controls = false;
301				audio.autoplay = false;
302				audio.preload = false;
303				audio.addEventListener('canplay', function() {
304					onload && onload(audio);
305				});
306				audio.addEventListener('error', function(err) {
307					onerror && onerror(err);
308				});
309				document.body.appendChild(audio);
310			} else if (url.indexOf('data:audio') === 0) { // Base64 string
311				var base64 = url.split(',')[1];
312				var buffer = Base64Binary.decodeArrayBuffer(base64);
313				ctx.decodeAudioData(buffer, onload, onerror);
314			} else { // XMLHTTP buffer
315				var request = new XMLHttpRequest();
316				request.open('GET', url, true);
317				request.responseType = 'arraybuffer';
318				request.onload = function() {
319					ctx.decodeAudioData(request.response, onload, onerror);
320				};
321				request.send();
322			}
323		};
324		
325		function createAudioContext() {
326			return new (window.AudioContext || window.webkitAudioContext)();
327		};
328	})();
329})(MIDI);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.