MIDI.js icon indicating copy to clipboard operation
MIDI.js copied to clipboard

Stop all notes

Open davideoliveri opened this issue 10 years ago • 16 comments

Hi! Thanks a lot for your work, it's amazing and allows us to play and have fun!

I was wonder if there's a way to stop all notes that are scheduled to be played with

MIDI.chordOn(0, chord, 127, delayedStart); MIDI.chordOff(0, chord, 127, length);

if I call MIDI.stopAllNotes(); in the console while notes are being played nothing happens.

I was thinking I could create a midi file on the fly and then execute it with a Player, which is easier to stop. But I can't find a way to do that.

Thanks a lot for your effort again!

davideoliveri avatar Aug 17 '14 11:08 davideoliveri

This may or may not work for you (especially since I've only tried the web audio api so far), but the solution I ended up doing was whenever I called noteOn, it returns the note that was played, which you can later call stop() on which seems to at least mute the note. From there I had issues where trying to play new notes would somehow start to replay the old notes that I called stop on (I think its cause stop doesn't remove them from the audioBuffers but I didn't find out for sure) so I ended up just reinitializing the whole MIDI again with loadPlugin. It means the everytime the stop button is pressed, it takes a good 2 seconds to stop, but at least it consistently stops all notes from playing. Hope that helps someone!

jroweboy avatar Nov 13 '14 20:11 jroweboy

I am also struggling to implement stopAllNotes() function, can anybody share code to stop playing notes without player.

shivrajsa avatar Nov 03 '15 13:11 shivrajsa

@shivrajsa I ended up doing something like this to patch midi.js to fix the stop function https://github.com/jroweboy/mml.js/commit/06dfde351739ae680f9e20916178fabf47e0846e#diff-0fe2b7205021932be4392b3665e12b26

Then I wrote a stop function as follows https://github.com/jroweboy/mml.js/blob/master/mml.js#L383

Not sure how much of that stop code is necessary, but I was just trying to get something quick and dirty together.

jroweboy avatar Nov 04 '15 04:11 jroweboy

Hi!

How can I stop all notes in MIDI.loadPlugin? MIDI.stop(); and MIDI.stopAllNotes(); not working...

krancan avatar Jan 06 '16 20:01 krancan

I meet the same issue. It seems that MIDI.stop() and MIDI.stopAllNotes() are not the function included any more? Then how can I stop the note scheduled? Thanks a lot if there is any solution.

fengtinghao avatar Jan 24 '17 21:01 fengtinghao

The solution posted by jroweboy worked just fine for me, I've put an array called sourcesTracker inside noteOn funtion (just after of "sources[channelId + '' + noteId] = source;") that stores the loaded notes, then, when calling stopAllNotes changed the "sources" on the "for" with my own sources tracker. I think one of the problems it's the deletion of the sources on noteOff, but I didn't wanted to delete de line just in case. Hope this helps

	midi.noteOn = function(channelId, noteId, velocity, delay) {
		delay = delay || 0;

		/// check whether the note exists
		var channel = root.channels[channelId];
		var instrument = channel.instrument;
		var bufferId = instrument + '' + noteId;
		var buffer = audioBuffers[bufferId];
		if (!buffer) {

// console.log(MIDI.GM.byId[instrument].id, instrument, channelId); return; } /// convert relative delay to absolute delay if (delay < ctx.currentTime) { delay += ctx.currentTime; }

		/// create audio buffer
		if (useStreamingBuffer) {
			var source = ctx.createMediaElementSource(buffer);
		} else { // XMLHTTP buffer
			var source = ctx.createBufferSource();
			source.buffer = buffer;
		}

		/// add effects to buffer
		if (effects) {
			var chain = source;
			for (var key in effects) {
				chain.connect(effects[key].input);
				chain = effects[key];
			}
		}

		/// add gain + pitchShift
		var gain = (velocity / 127) * (masterVolume / 127) * 2 - 1;
		source.connect(ctx.destination);
		source.playbackRate.value = 1; // pitch shift 
		source.gainNode = ctx.createGain(); // gain
		source.gainNode.connect(ctx.destination);
		source.gainNode.gain.value = Math.min(1.0, Math.max(-1.0, gain));
		source.connect(source.gainNode);
		///
		if (useStreamingBuffer) {
			if (delay) {
				return setTimeout(function() {
					buffer.currentTime = 0;
					buffer.play()
				}, delay * 1000);
			} else {
				buffer.currentTime = 0;
				buffer.play()
			}
		} else {
			source.start(delay || 0);
		}
		///
		sources[channelId + '' + noteId] = source;
		sourcesTracker.push(sources[channelId + '' + noteId])
		///
		return source;
	};



	midi.stopAllNotes = function() {
		for (var sid in sourcesTracker) {
			var delay = 0;
			if (delay < ctx.currentTime) {
				delay += ctx.currentTime;
			}
			var source = sourcesTracker[sid];
			source.stop(delay + 0.3);
			source.gainNode.gain.linearRampToValueAtTime(1, delay);
			source.gainNode.gain.linearRampToValueAtTime(0, delay + 0.3);

		}
		sourcesTracker = [];
	};

Ducinaltum avatar Jun 25 '18 03:06 Ducinaltum

Ducinaltum,

I tried to implement your solution but it dosen't work for me. I guess I'm doing something wrong.

What I did is to substitute the code you wrote above in "plugin.webaudio.js" file. Then I tried to use "MIDI.stopAllNotes()" in my code but It didn't anything. Is it right?

Can you guess what I'm doing wrong, please?

makinavava1978 avatar Aug 08 '18 12:08 makinavava1978

Maki, sorry for the delay, have you declared the sourcesTracker outside the function?

Ducinaltum avatar Aug 27 '18 17:08 Ducinaltum

I use below solution, I don't know if it is good solution or not, but it works

(var i = 1; i < 9999; i++)
{
window.clearTimeout(i)        
window.clearInterval(i);
}

Also I do not play MIDI file, instead I use NoteOn and NoteOff to play my sequence. So don't know whether this solution is suitable to stop playing MIDI file.

shivrajsa avatar Aug 28 '18 03:08 shivrajsa

Ducinaltum,

Thank you for your answer.

I don't know much about programing so I am not sure what you mean. How can I declare the "sourcesTracker?" Where Should I do it?

What I did is to substitute all the files you changed in order to make "stop.allNotes()" work. These file are those you change in: https://github.com/Ducinaltum/Audio_game/commit/273e2fe5af23ba5efe8e5fefb5198d3595346f21.

Then I have created a minimal code to check it but It dosen't work. Here is the code: ////////////////////////////////////////////////////////// window.onload = function () { MIDI.loadPlugin({ soundfontUrl: "./soundfont/", instrument: "acoustic_grand_piano", onprogress: function(state, progress) { console.log(state, progress); }, onsuccess: function() { var delay = 0; // play one note every quarter second var note = 50; // the MIDI note var velocity = 127; // how hard the note hits // play the note MIDI.setVolume(0, 127);

		for (i = 0; i < 10; i++) { 
			MIDI.noteOn(0, note+i, velocity, delay+i/2);
			MIDI.noteOff(0, note+i, delay + 0.75+i/2);

		}
		
	}
});

};

function Stop() {
MIDI.stopAllNotes();
} //////////////////////////////////////////////////////////

But whan I call Stop() function nothing happens. Can you see something wrong?

Maki.

makinavava1978 avatar Aug 29 '18 11:08 makinavava1978

Shivrajsa,

Thank you for your answer.

I tried to implement your solution but it doesn't work. Where Should I put the code you wrote?

Maki.

makinavava1978 avatar Aug 29 '18 11:08 makinavava1978

Just create a button in your HTML and call below function on onClick event of button. for the moment, you can keep this function in script tag, in your HTML for test purpose.

function clearAllNotes()
{
(var i = 1; i < 9999; i++)
{
window.clearTimeout(i)        
window.clearInterval(i);
}
}

shivrajsa avatar Aug 29 '18 11:08 shivrajsa

Maki, Did you just copy the funtions that i've changed? Try replacin the hole plugin.webaudio.js file with the one that I have in my repository. I declare the sources tracker in line 21, fed it in 120, iterate trough it in 185, between 190 and 194 stop individually all the notes in the tracke, and clean it in 196. Tell me if u prefer the help in spanish. If changing the file does not work we can arrange a time and talk to each other on discord or skype. Hope to help Ale

Ducinaltum avatar Aug 29 '18 16:08 Ducinaltum

Ducinaltum,

Finally I found the problem. I made a mess with the files but once I took the file plugin.webaudio.js from your repository the function "clearAllNotes()" is working.

Thank you very much for your help!

Maki.

makinavava1978 avatar Aug 29 '18 22:08 makinavava1978

Shivrajsa,

I couldn't implement your solution but the one of Ducitalum is good for me.

Thank you very much for your help too.

Maki.

makinavava1978 avatar Aug 29 '18 22:08 makinavava1978

Just following up on what @jroweboy said, one can store the note objects and then manually call the .stop() method on them:

window.onclick = function() {
  MIDI.loadPlugin({
    soundfontUrl: "./soundfont/",
    instrument: "acoustic_grand_piano",
    onsuccess: function() {
      var time = 0;
      var velocity = 120;
      MIDI.setVolume(0, velocity);

      // store the played notes
      window.notes = [];

      // play the note
      var idA = MIDI.noteOn(0, 60, velocity, time);
      var idB = MIDI.noteOff(0, 60, time + 1000);

      window.notes.push(idA);
      window.notes.push(idB);
    }
  });
};

window.onkeydown = function() {
  window.notes.forEach(note => note.stop())
}

duhaime avatar Sep 24 '21 18:09 duhaime