Scheduling ToneJS events while transport is playing - javascript

I have 4 notes playing in my Tone JS app and would like to change the 3rd note to be something else whilst the transport is currently playing. Here is my code:
JS:
import { Transport, start, Sampler } from "tone";
const notesToPlay = [
{
timing: 0.25,
sameAsLast: false,
duration: 0.1,
velocity: 1
},
{
timing: 0.5,
sameAsLast: true,
duration: 0.1,
velocity: 1
},
{
timing: 0.75,
sameAsLast: false,
duration: 0.1,
velocity: 1
},
{
timing: 1,
sameAsLast: false,
duration: 0.2,
velocity: 1
}
];
var eventIds = [];
(function () {
function playSynth() {
Transport.start();
start();
}
const sampler = new Sampler({
urls: {
A1: "A1.mp3",
A2: "A2.mp3"
},
baseUrl: "https://tonejs.github.io/audio/casio/",
onload: () => {
loadNotes();
}
}).toDestination();
function loadNotes() {
notesToPlay.forEach((n) => {
const eventId = Transport.scheduleRepeat((time) => {
sampler.triggerAttackRelease(
["A1"],
n.duration,
n.timing + time,
n.velocity
);
}, 4);
eventIds.push(eventId);
});
}
document.getElementById("play").addEventListener("click", function () {
playSynth();
});
document.getElementById("stop").addEventListener("click", function () {
Transport.stop();
});
document.getElementById("replace").addEventListener("click", function () {
const arrayIdxToReplace = 2;
Transport.clear(eventIds[arrayIdxToReplace]);
const note = notesToPlay[arrayIdxToReplace];
Transport.scheduleRepeat((time) => {
sampler.triggerAttackRelease(
["D1"],
note.duration,
note.timing + time,
note.velocity
);
}, 4);
});
})();
HTML:
<div id="app">
<button id="play">play me</button>
<button id="stop">stop</button>
<button id="replace">Replace 3rd note</button>
</div>
When I click the replace 3rd note button it removes the old event which is good but when it schedules the new event in it is out of sync with where the old 3rd note would be.
A way to get around this is by stopping the Transport then clicking to replace the 3rd note and then clicking play again however I want to be able to do this while the Transport is still playing. Where have I gone Wrong?
Here is a fiddle to demo the issue:
https://codesandbox.io/s/tonejs-forked-fxhzm?file=/src/index.js:0-1643

This is pretty awkward to fix the way it is structured now. The trouble is that every note you add is on its own loop, which begins whenever you call scheduleRepeat ... you would have to correct for the offset between the original loop and the new one, which seems kind of tricky.
I'd suggest that you have call scheduleRepeat only once, and have the callback read the list of notes you have stored. That way you can just replace or alter one of those notes and the next time around it'll be changed, and you won't have any timing problems. I think that would mean including the pitch in your note schema.

Related

Trying to make a play button in html, which can be pressed upon repeatly in order to play a set of notes. (Using Tone.js)

I have this code in html:
<script src="https://unpkg.com/tone"></script>
<button id="tunebtn">Play Notes</button>
<script>
let synth = new Tone.Synth().toDestination();
let notes = [
{ note: "C4", duration: "8n" , time: 0},
{ note: "D4", duration: "8n" , time: 0.5},
{ note: "E4", duration: "8n", time: 1}
];
let time = 0;
const playNotes = () => {
notes.forEach(note => {
synth.triggerAttackRelease(note.note, note.duration, note.time);
});
};
document.getElementById("tunebtn").addEventListener("click", playNotes);
</script>
I am able to press the button once, in order to play these three notes in the array "note". But when I press it again, the button stops working and nothing gets played. Any idea of what is causing this, and how could this be fixed?
YESSS! I found a fix! I checked this link on Github, where someone was having a similar issue: https://github.com/Tonejs/Tone.js/issues/281
When the code is executed, there is a delay before the first 'triggerAttackRelease' is called. By this time, the scheduled time of 0 for the first note has already passed, and it cannot be scheduled retroactively. As a result, the first note may be abruptly cut short or completely suppressed by the second note.
Using a time constant fixes this issue. Here is my solution:
<script src="https://unpkg.com/tone"></script>
<button id="tunebtn">Play Notes</button>
<script>
let synth = new Tone.Synth().toDestination();
let notes = [
{ note: "C4", duration: "8n" , time: 0},
{ note: "D4", duration: "8n" , time: 0.5},
{ note: "E4", duration: "8n", time: 1}
];
const playNotes = () => {
notes.forEach(note => {
const now = Tone.now()
synth.triggerAttackRelease(note.note, note.duration, note.time + now);
});
};
document.getElementById("tunebtn").addEventListener("click", playNotes);
</script>

GSAP + Vue using props to calculate a maxWidth

I am trying to animate buttons in my application using GSAP. User clicks button and animates the maxWidth of the button. I'd like to have this dynamic and add a percentage of the max width that is set using props. is it possible to pass the prop maxwidth to the gsap timeline? as of now it does not work for me.
props: {
maxWidth: {
type: String,
required: true,
},
},
methods: {
buttonTo(path) {
let tl = this.$gsap.timeline({
onComplete: function () {
pushToPath();
},
});
tl.to(this.$refs.primaryButton, {
duration: 0.6,
ease: 'power2.in',
maxWidth: `calc(${this.maxWidth} + 5%)`,
});
const pushToPath = () => {
this.$router.push({ path: path });
};
},
},
I've run into a similar issue before. The nice thing is that it has nothing to do with the Vue Lifecycle, so the value is available within methods.
There are a couple of things that could be causing this issue. I'd start by making sure your prop, "maxWidth," has "px" or some form of CSS measurement tied to it. CSS calc can't have a plain number within the CSS "calc" function.
Here is an example using your function:
props: {
maxWidth: {
type: String,
required: true,
},
},
methods: {
buttonTo(path) {
let tl = this.$gsap.timeline({
onComplete: function () {
pushToPath();
},
});
tl.to(this.$refs.primaryButton, {
duration: 0.6,
ease: 'power2.in',
maxWidth: `calc(${this.maxWidth}px + 5%)`,
});
const pushToPath = () => {
this.$router.push({ path: path });
};
},
},
You could also switch your prop to be of the type "Number" in case, for some reason, the string is causing issues within the timeline.
If this helps, please let me know!

I am having trouble understanding the Leader-Line js documentation

I am trying to create code that will draw a line from point a to point b using the leaderline library. https://github.com/anseki/leader-line
It looks like the section I need to reference is under Methods
self = line.show([showEffectName[, animOptions]])
where showEffectName: 'draw' and animOptions: {duration: 500, timing: [0.58, 0, 0.42, 1]}
Here is an example shown using a button to show/hide the line
var line = new LeaderLine(startElement, endElement, {hide: true});
showButton.addEventListener('click', function() { line.show(); }, false);
hideButton.addEventListener('click', function() { line.hide(); }, false);
How do I implement the self= code into the button? I'm not even sure what self= is supposed to mean. The below code does not work
var line = new LeaderLine(startElement, endElement, {hide: true});
line.show() = line.show({showEffectName:'draw'}, {animOptions: {duration: 3000, timing: 'linear'}});
startElement.addEventListener('click', function() { line.show(); });
Here is what you are looking for
line.show('draw', {
animOptions: {
duration: 3000,
timing: [0.5, 0, 1, 0.42]
}
})
And now you can call this in any function
button.addEventListener('click', function() {
line.show('draw', {
animOptions: {
duration: 3000,
timing: [0.5, 0, 1, 0.42]
}
})
});
Your code does not work because you have to put only value like below.
var line = new LeaderLine(startElement, endElement, {hide: true});
startElement.addEventListener("click", function () {
line.show("draw", {duration: 3000, timing: 'linear'});
});

How to get the current slide no in Glide.js

Hi how can I get the current slide no when I click next and previous button in GLide.js http://glide.jedrzejchalubek.com/docs.html#intro.
var carousel = $('#Carousel').glide({
type: 'carousel',
startAt: 1,
touchDistance: 2,
afterInit:function(){console.log("paintSlider")},
autoplay: 0
});
console.log(carousel.current());
For some reason, the function carousel.current() is not working.
You can use the code's callback and events instead.
Example:
var carousel = $('#Carousel').glide({
type: 'carousel',
...
afterTransition : function(event) {
console.log(event.index); // the current slide number
}
});
Also, carousel.index() works too!
const glide = new Glide('.glide');
glide.on(['mount.after', 'run'], () => {
const currentIndex = glide.index;
console.log(currentIndex)
});
glide.mount();
Not sure why, but documentation about accessing API is missing. I'll fix that.
You need to access api via .data('glide_api'), before making api calls.
var carousel = $('#Carousel').glide({
type: 'carousel',
startAt: 1,
touchDistance: 2,
afterInit:function(){console.log("paintSlider")},
autoplay: 0
}).data('glide_api');
console.log(carousel.current());
Thanks for using plugin!
It works for me:
jQuery(document).ready(function(){
var glide = jQuery('.slider').glide({
afterTransition : function() {
console.log(glide.current());
}
}).data('api_glide'); // data('api_glide') to get opportunity to use glide.current()
});
You can access the property index on your glide instance.
For example:
import Glide, { Controls } from '#glidejs/glide/dist/glide.modular.esm';
var glider = new Glide( el, options );
glider.mount( {
Controls,
} );
// You can get the current index using glider.index;
console.log( glider.index );
Glibert answer works
var stGlide = new Glide(`.heroglide-${article.id}`, {
type: 'carousel',
animationDuration: 500,
startAt: 1,
perView: 1,
peek: {
before: 50,
after: 50
},
// afterTransition : function(event) {
// console.log("========transition",event.index); // the current slide number
// }
});
try{
stGlide.on(['mount.after', 'run'], () => {
const currentIndex = stGlide.index;
console.log("====>index==>",currentIndex)
});
stGlide.mount();
}
const glide = new Glide('.glide');
glide.on("run", () => {
console.log(slider.index);
});

Dojo floating panes

I recently started working with Dojo framework and have to make floating panes for a website. After searching on both google and stackoverflow, I come across a very good example:
jsfiddle
But the problem is every time I minimize the floating pane and then click on the small tab on bottom to show it again, the size of pane increases! It can also be seen in the jsfiddle example although you need to repeat it multiple times since the change is like 2px.
Can someone help me with this weird behavior?
JavaScript code:
dojo.require("dojo.dnd.move");
dojo.require("dojox.layout.FloatingPane");
dojo.ready(function() {
var ParentConstrainedFloatingPane = dojo.declare(dojox.layout.FloatingPane, {
postCreate: function() {
this.inherited(arguments);
this.moveable = new dojo.dnd.move.parentConstrainedMoveable(
this.domNode, {
handle: this.focusNode,
area: "content",
within: true
}
);
}
});
var BoxConstrainedFloatingPane = dojo.declare(dojox.layout.FloatingPane, {
postCreate: function() {
this.inherited(arguments);
this.moveable = new dojo.dnd.move.boxConstrainedMoveable(
this.domNode, {
handle: this.focusNode,
box: {l: 10, t: 10, w: 400, h: 400},
within: true
}
);
}
});
var ConstrainedFloatingPane = dojo.declare(dojox.layout.FloatingPane, {
postCreate: function() {
this.inherited(arguments);
this.moveable = new dojo.dnd.move.constrainedMoveable(
this.domNode, {
handle: this.focusNode,
constraints: function() {
var coordsBody = dojo.coords(dojo.body());
// or
var coordsWindow = {
l: 0,
t: 0,
w: window.innerWidth,
h: window.innerHeight
};
return coordsWindow;
},
within: true
}
);
}
});
var searchboxNode = dojo.byId("searchbox");
var floatingPane = new ParentConstrainedFloatingPane({
title: "A floating pane",
resizable: true,
dockable: true,
//constrainToContainer: true,
style: "position:absolute;top:40px;left:40px;width:160px;height:100px;"
}, searchboxNode);
floatingPane.startup();
});
EDIT#1 : I've raised an official bug ticket for it and further responce can be checked via this link:
http://bugs.dojotoolkit.org/ticket/16598
that is a dojox floatingpane bug, i don't know if its reported or not, but it also happens when you use directly the class
dojox.layout.FloatingPane
instead of
ParentConstrainedFloatingPane
and also if you change the dojo version (tried 1.8.3 to verify), so unless you are willing to get your hands messy fixing it, try reporting it to dojo too see if its been fixed.

Categories