|<- [[3.12 Drum machine UI]]|[[3.17 Final drum machine code - standards compliant version]] ->| [code](https://live.codecircle.com/d/ywwcqZjWfujwiGX4J) https://live.codecircle.com/d/3WtdEjKJerkjQfLP3 html
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'); batterie.charger( 'caisse claire', 'brp_snrim1.wav'); // la séquence que l'on souhaite jouer var sequenceur = [ [0,0,0,0], [0,0,0,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.1; // 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 met à jour l'interface utilisateur // cela va firer un event, d'où la ligne 176 matrix.jumpToCol( etape % sequenceur[0].length ); // 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 ( sequenceur[0] [ etape % sequenceur[0].length ] ) { // on programme cela pour l'instant t correspondant batterie.jouer('charleston', t); } // si on doit jouer le sample à cette étape if ( sequenceur[1] [ etape % sequenceur[1].length ] ) { // on programme cela pour l'instant t correspondant batterie.jouer('caisse claire', 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); var matrix, son_mobile; nx.onload = function () { matrix.row = sequenceur.length; matrix.col = sequenceur[0].length; matrix.init(); matrix.on('*', function ( data ) { // comme ligne 122 on utilise jumpToCol, cela fire un event // on ne veut toutefois que la ligne suivante ne s'exécute que si on est dans le cas d'un événement qui refile data.row, data.col, data.level, on fait un test if ( data.row !== undefined ) { sequenceur[data.row][data.col] = data.level; } }); // sur mobile, on doit déclencher un son avec une action utilisateur avant de pouvoir utiliser le son son_mobile.on('*', function(){ batterie.jouer('charleston', contexte.currentTime); }); };