tone.js: How to know when triggerRelease has finished - javascript

I want to update my UI when a note has finished playing with tone.js
const now = Tone.now()
synth.triggerAttack("G3", now);
synth.triggerRelease("G3", now + 60 / this.tempo)
How can I get a callback or add a listener to the triggerRelease so I can update the UI?

I have never used the library but reviewing its documentation it seems that the API defines an onsilence callback that you could use for your purpose.
You can provide that callback when creating the Synth instance passing the appropriate configuration option:
const synth = new Tone.Synth({
onsilence: () => console.log('I am invoked after the release has finished') // update the UI as appropriate
}).toDestination();
const now = Tone.now()
synth.triggerAttack("G3", now);
synth.triggerRelease(now + 60 / this.tempo);
The API provides some tests related to it as well.
As you indicated in your comments the onsilence callback seems to be invoked only when the instrument is completely in silence, but you need to perform some action when a note ends.
Probably the way to go would be working with the different events offered by the library.
The library documentation provides different related examples that exemplifies how to use them.
Consider for instance review this one and the companion wiki page that try explaining how to sync visuals, or this other one, close to the per note callback you are looking for.
These two examples can be found in the tone.js repository.
With these in mind, consider for instance this code, based on the second example provided when describing the Part event and the snippet of code provided in the aforementioned article about performance and how to sync visuals:
import * as Tone from 'tone';
const synth = new Tone.Synth().toDestination();
// use an array of objects as long as the object has a "time" attribute
const part = new Tone.Part(
(time, value) => {
// the value is an object which contains both the note and the velocity
synth.triggerAttackRelease(value.note, '8n', time);
Tone.Draw.schedule(function () {
//this callback is invoked from a requestAnimationFrame
//and will be invoked close to AudioContext time
document.getElementById('app').innerHTML += ' ' + value.note;
}, time);
},
[
{ time: 0, note: 'C3' },
{ time: '0:2', note: 'C4' },
]
).start(0);
Tone.Transport.start();
Please, reload the preview page in Stackblitz, you should see every note.
Finally, you have several ways if you need to access the parent context this object as indicated in your comments: the exact way will depend on your actual code. Please, consider for instance review this related SO question, I think it could be of help.

Related

monaco-editor: how to subscribe / listen native commands / actions as 'actions.find'

For a long time I've been trying to find a way, example or information about how I can subscribe or listen native commands/actions as actions.find.
My case is - to create some side-effect when user use "Find Matches" functionality in editor's workbench (called by keyboard shortcut or context-menu), at finally I want to get as argument 'searching 'substring' in my effect.
Hopefully the community can help me or place some similar case resolving sample.
After debugging and studying the source code of monaco-editor, I found, as it seems to me, the correct solution for using the API (IMHO) for my case.
We can use API of FindController, that is one of permanently contributions in each instance, we can use there public method onFindReplaceStateChange
// to get relevant FindController from editor instance we can use
var findController = editor.getContribution('editor.contrib.findController')
// to get FindReplaceState from FindController
var findSearchState = editor.getContribution('editor.contrib.findController').getState();
// to subscribe to 'find matches' event bus
editor.getContribution('editor.contrib.findController')
.getState().onFindReplaceStateChange(
(...rest) => console.log('FindReplaceStateChange: ', ...rest)
);
// to force & silent set current search substring
editor.getContribution('editor.contrib.findController').setSearchString('foo-bar');
// to programmatically call search with options,
// not 'editor.getAction('actions.find').run()' (that not have arguments)
editor.getContribution('editor.contrib.findController').start({
forceRevealReplace: false,
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: false,
shouldAnimate: false,
updateSearchScope: false
});
I have prepared an example with explanations for solving my case (synchronization of search between several editors) on a CodeSandbox -
Monaco Editor [ Multiple Instances search sync case ]
screencast 'how it works' (GIF)

JavaScript Adding to requestAnimationFrame function

The question could be referred to Three.JS framework, but I think it's a general JS problem.
animate();
function animate() {
{doSomething}
requestAnimationFrame( animate );
}
So the function animate() runs every frame and does something.
The question is how to add {doAnotherSomething} to animate() on the fly?
function anEventHappened(){
//adding some methods to animate
}
There are several ways to do this. But you'll somehow have to foresee something in this animate function that can detect that more needs to be done. This "something" could be an array with functions to execute as extras. Initially that array would be empty, but upon some event you could add a function to that list (or remove one from it).
Here is a practical example:
let container = document.querySelector("pre");
let todoList = []; // <--- list of functions to execute also as extras
animate();
function animate() {
todoList.forEach(f => f()); // execute whatever is in the todo-list
moveText(container);
requestAnimationFrame(animate);
}
document.querySelector("button").onclick = function anEventHappened() {
let newContainer = document.createElement("pre");
newContainer.textContent = Math.random();
document.body.appendChild(newContainer);
todoList.push(() => moveText(newContainer)); // add something extra
}
// Helper function to perform some HTML-text manipulation
function moveText(what) {
what.textContent = what.textContent.includes("<") ? what.textContent.slice(1)
: what.textContent.length < 60 ? " " + what.textContent
: what.textContent.replace(/ (\S)/, "<$1");
}
<button>Do extra</button>
<pre>phrase</pre>
It's quite easy once you know what to look for.
A simplest solution is to keep a collection of functions to be called every time (a collection of strategies)
In order not to share this collection among the whole code base you can apply Subscriber - Publisher approach where your module containing animate subscribes to events in the system that shall change what is to be rendered on the screen.
Here's a good reference about basic design patterns: https://refactoring.guru/design-patterns/
So... going back to your original question, what you can do is to send an event with a callback in it dispatchEvent({ type: 'render'; strategy: <some_function> }) and animate's module will listen to such events .addEventListener('render', event => strategies.push(event.strategy)).
Remarks:
Don't forget to remove old strategies.
strategies could be a dictionary instead of an array. In that case event render should also have some sort of a key to prevent multiple identical strategies from been pushed to the "queue"
https://threejs.org/docs/index.html#api/en/core/EventDispatcher

Filter collection by lastModified

I need to fetch sub-set of documents in Firestore collection modified after some moment. I tried going theses ways:
It seems that native filtering can work only with some real fields in stored document - i.e. nevertheless Firestore API internally has DocumentSnapshot.getUpdateTime() I cannot use this information in my query.
I tried adding my _lastModifiedAt 'service field' via server-side firestore cloud function, but ... that updating of _lastModifiedAt causes recursive invocation of the onWrite() function. I.e. is does also not work as needed (recursion finally stops with Error: quota exceeded (Function invocations : per 100 seconds)).
Are there other ideas how to filter collection by 'lastModifiedTime'?
Here is my 'cloud function' for reference
It would work if I could identify who is modifying the document, i.e. ignore own updates of _lastModified field, but I see no way to check for this
_lastModifiedBy is set to null because of current inability of Firestore to provide auth information (see here)
exports.updateLastModifiedBy = functions.firestore.document('/{collId}/{documentId}').onWrite(event => {
console.log(event.data.data());
var lastModified = {
_lastModifiedBy: null,
_lastModifiedAt: now
}
return event.data.ref.set(lastModified, {merge: true});
});
I've found the way to prevent recursion while updating '_lastModifiedAt'.
Note: this will not work reliably if client can also update '_lastModifiedAt'. It does not matter much in my environment, but in general case I think writing to '_lastModifiedAt' should be allowed only to service accounts.
exports.updateLastModifiedBy = functions.firestore.document('/{collId}/{documentId}').onWrite(event => {
var doc = event.data.data();
var prevDoc = event.data.previous.data();
if( doc && prevDoc && (doc._lastModifiedAt != prevDoc._lastModifiedAt) )
// this is my own change
return 0;
var lastModified = getLastModified(event);
return event.data.ref.set(lastModified, {merge: true});
});
Update: Warning - updating lastModified in onWrite() event causes infinite recursion when trying to delete all documents in Firebase console. This happens because onWrite() is also triggered for delete and writing lastModified into deleted document actually resurrects it. That document propagates back into console and is tried to be deleted once again, indefinitely (until WEB page is closed).
To fix that issue above mentioned code has to be specified individually for onCreate() and onUpdate().
How about letting the client write the timestamp with FieldValue.serverTimestamp() and then validate that the value written is equal to time in security rules?
Also see Mike's answer here for an example: Firestore Security Rules: If timestamp (FieldValue.serverTimestamp) equals now
You could try the following function, which will not update the _lastModifiedAt if it has been marked as modified within the last 5 seconds. This should ensure that this function only runs once, per update (as long as you don't update more than once in 5 seconds).
exports.updateLastModifiedBy = functions.firestore.document('/{collId}/{documentId}').onWrite(event => {
console.log(event.data.data());
if ((Date.now() - 5000) < event.data.data()._lastModifiedAt) {return null};
var lastModified = {
_lastModifiedBy: null,
_lastModifiedAt: now
}
return event.data.ref.set(lastModified, {merge: true});
});

Subscribe to a count of an existing collection

I need to keep track of a counter of a collection with a huge number of documents that's constantly being updated. (Think a giant list of logs). What I don't want to do is to have the server send me a list of 250k documents. I just want to see a counter rising.
I found a very similar question here, and I've also looked into the .observeChanges() in the docs but once again, it seems that .observe() as well as .observeChanges() actually return the whole set before tracking what's been added, changed or deleted.
In the above example, the "added" function will fire once per every document returned to increment a counter.
This is unacceptable with a large set - I only want to keep track of a change in the count as I understand .count() bypasses the fetching of the entire set of documents. The former example involves counting only documents related to a room, which isn't something I want (or was able to reproduce and get working, for that matter)
I've gotta be missing something simple, I've been stumped for hours.
Would really appreciate any feedback.
You could accomplish this with the meteor-streams smart package by Arunoda. It lets you do pub/sub without needing the database, so one thing you could send over is a reactive number, for instance.
Alternatively, and this is slightly more hacky but useful if you've got a number of things you need to count or something similar, you could have a separate "Statistics" collection (name it whatever) with a document containing that count.
There is an example in the documentation about this use case. I've modified it to your particular question:
// server: publish the current size of a collection
Meteor.publish("nbLogs", function () {
var self = this;
var count = 0;
var initializing = true;
var handle = Messages.find({}).observeChanges({
added: function (id) {
count++;
if (!initializing)
self.changed("counts", roomId, {nbLogs: count});
},
removed: function (id) {
count--;
self.changed("counts", roomId, {nbLogs: count});
}
// don't care about moved or changed
});
// Observe only returns after the initial added callbacks have
// run. Now return an initial value and mark the subscription
// as ready.
initializing = false;
self.added("counts", roomId, {nbLogs: count});
self.ready();
// Stop observing the cursor when client unsubs.
// Stopping a subscription automatically takes
// care of sending the client any removed messages.
self.onStop(function () {
handle.stop();
});
});
// client: declare collection to hold count object
Counts = new Meteor.Collection("counts");
// client: subscribe to the count for the current room
Meteor.subscribe("nbLogs");
// client: use the new collection
Deps.autorun(function() {
console.log("nbLogs: " + Counts.findOne().nbLogs);
});
There might be some higher level ways to do this in the future.

Creating a custom echo node with web-audio

I'm playing with the webkit Audio API and I'm trying to create an Echo effect, to accomplish that I've connected a DelayNode with a GainNode in a loop (The output of one is the input of the other, and viceversa).
The effect works fine, but now I want to create an EchoNode Object that I can just plug-in and connect with the other AudioNode objects.
Something like:
myEchoNode = new EchoNode();
myConvolverNode = context.createConvolver();
myConvolverNode.connect(myEchoNode);
I think that I should make my EchoNode inherit from AudioNode, so that the connect function of every other AudioNode would work, but I don't know how to do that in Javascript with the web Audio API.
Can anyone give me a hint, or if you think that there is a better way to accomplish that I would greatly appreciate it.
Thanks
Oskar's solution should do the trick, but I want to point out that it will require you to connect to your EchoNode in a nonstandard way (using EchoNode.input rather than simply connecting to the EchoNode itself). For simple effects such as feedback delay, this can be avoided by creating the EchoNode via a factory function that returns a native DelayNode mixed with some extra properties. Here's an example from SynthJS:
function FeedbackDelayNode(context, delay, feedback){
this.delayTime.value = delay;
this.gainNode = context.createGainNode();
this.gainNode.gain.value = feedback;
this.connect(this.gainNode);
this.gainNode.connect(this);
}
function FeedbackDelayFactory(context, delayTime, feedback){
var delay = context.createDelayNode(delayTime + 1);
FeedbackDelayNode.call(delay, context, delayTime, feedback);
return delay;
}
AudioContext.prototype.createFeedbackDelay = function(delay, feedback){
return FeedbackDelayFactory(this, delay, feedback);
};
As you can see, the result is a native DelayNode that can be connected to other nodes in the standard fashion, but it has an attached gain node that provides the feedback effect.
Have a look at this article I wrote, it might give you some ideas: http://www.html5rocks.com/en/tutorials/casestudies/jamwithchrome-audio/ (which explains the basic idea behind tuna.js that Taoist recommended).

Categories