https://live.codecircle.com/d/uDHicYCHDCrPBbGf2
[drum machine with good timing code](https://live.codecircle.com/d/GAR9Tv9LXrRYBxSL7)
javascript
function Samples_set ( contexte ) { /* Set de samples permettant de charger simplement des samples et de les jouer */ this.contexte = contexte; this.samples_set = {}; } Samples_set.prototype.charger = function ( nom, url ) { /* Charge le sample et l'ajoute au set String, String -> Void */ // on créé une référence à this (notre occurence de Sample_set) var self = this; // on créé l'objet requête var requete = new XMLHttpRequest(); // que l'on paramètre : // 1 : requête GET, url de la requête, requête asynchrone : true requete.open('GET', url, true); // 2 : type de réponse requete.responseType = 'arraybuffer'; // 3 : écouteur onload et fonction à exécuter alors requete.onload = function () { // les données audio var donnees_audio = requete.response; // sont passées pour décodage au contexte audio contexte.decodeAudioData( donnees_audio, function( buffer ) { // on ajoute le buffer à notre objet instruments self.samples_set[nom] = buffer; }); }; // on envoie la requête requete.send(); }; Samples_set.prototype.jouer = function ( nom, instant ) { /* Joue le sample à un instant t s'il est trouvé String, Number -> Void */ // si le sample existe, a été chargé if ( this.samples_set[nom] ) { try { //on créé un BufferSource var player = contexte.createBufferSource(); // on y charge notre sample player.buffer = this.samples_set[nom]; // on spécifie qu'on ne le joue pas en boucle player.loop = false; // on le connecte au contexte player.connect(this.contexte.destination); // on le lancera à l'instant t player.start( instant ); // en cas d'erreur } catch (e) { console.error('erreur : Samples_set.jouer :', e); } } }; var contexte_audio = window.AudioContext || window.webkitAudioContext; var contexte = new contexte_audio(); var batterie = new Samples_set( contexte ); batterie.charger( 'charleston', 'cl_hat_3001.wav'); // la séquence que l'on souhaite jouer var sequence = [1, 1, 0, 1, 0, 1, 0]; // référence à l'étape où l'on en est dans la séquence var etape = 0; // base_rythmique : temps entre deux étapes de la séquence *en secondes* // car on va se baser sur le temps tel que donné par l'API audio en secondes var base_rythmique = 0.125; // intervalle entre deux appel à setInterval // on continue à travailler en secondes et on multipliera pour setInterval qui prends de millisecondes en paramètre var intervalle = 0.5; // référence à l'instant t max déjà traité var t_max_traite; // toutes les 0.5 secondes (intervalle) setInterval( function(){ // on va travailler à partir de l'instant t qui est égal à maintenant var t = contexte.currentTime; // on va déclarer tous les événement entre t et t_max qui est égal à 1.5 fois le temps entre deux base_rythmique // cela nous donne une marge de jusqu'à 0.5 fois notre base_rythmique // pour absorber les cas où setInterval serait en retard // si on change quelque chose on aura aussi une latence allant jusqu'à intervalle * 1.5 var t_max = t + (intervalle * 1.5); // si on a déjà traité au-delà de t if ( t_max_traite > t ) { // on passe déjà à l'instant t où l'on avait précédemment déjà été t = t_max_traite; } // de l'instant t à notre limite t_max while ( t <= t_max ){ // on passe à l'étape suivant dans notre séquenceur etape ++; // si on doit jouer le sample à cette étape if ( sequence [ etape % sequence.length ] ) { // on programme cela pour l'instant t correspondant batterie.jouer('charleston', t); } // on incrémente t pour passer à l'étape suivant t += base_rythmique; } // quant on a fini, on garde en mémoire le dernier instant t traité t_max_traite = t; /// tous les intervalle * 1000 car setInterval prend en paramètre des millisecondes et intervalle est en secondes }, intervalle * 1000);