Programmable Web 2015-2016

De $1

Version de 14:01, 17 Avr 2021

cette version.

Revenir à liste des archives.

Voir la version actuelle

Introduction

Dans ce cours vous allez voir différents aspects de la programmation Web :

  • JavaScript un peu plus compliqué que d'habitude, utilisation de frameworks MVC, etc.
  • APIs de HTML5: dessin et animation dans un Canvas, Web Audio, éventuellement WebGL/ThreeJS ou BabylonJS pour ceux qui veulent faire de la 3D, XHR2 ou Fetch pour de l'Ajax, etc.
  • Web Services
  • Micro serveur NodeJS et MongoDB (cours de P.Sanders)
  • Web de données
  • Autres ?

Le sujet tournera autours du développement d'un logiciel d'écoute de chansons multipistes à l'aide de HTML5 et d'APIs gravitant autours comme WebAudio API. D'autres APIs HTML5 seront mises à contribution comme l'API du canvas pour le dessin et l'animation, la persistence avec XHR2 et IndexedDB ou Web Storage etc.

Voici une image préliminaire d'un prototype (ne faites pas attention à l'interface graphique) fonctionnel:

 multi1.jpg

multi2.jpg

 Le but n'est pas que vous réalisiez exactement ce prototype, le but est que l'on vous aide à développer votre propre version, en ajoutant de nombreuses améliorations:

  • Ergonomie meilleure, interface plus belle,
  • Monitorer le download et le décodage des pistes dans une barre de progression,
  • Du code orienté objet et maintenable, si vous n'êtes pas débutant en JavaScript, utilisation d'un framework MVC, par exemple AngularJS,
  • Modèle de donné plus complet (titres des chansons complet, biographie de l'artiste, histoire de la chanson, nom des musiciens, etc),
  • Possibilité d'uploader de nouvelles chansons,
  • Possibilité de règler par piste le volume individuel, la stéréo, l'égalisation, delay, etc. FAIRE UNE VRAIE TABLE DE MIXAGE!
  • Sauvegarder et partager les réglages du mix d'une chanson, pouvoir importer depuis un serveur des mixes faits par les autres...
  • Pouvoir mettre une piste en solo (muter toutes les autres)
  • Possibilité d'intégrer des effets audio (reverbération, echo, égaliseur, etc.)
  • Zoom avant/arrière sur les échantillons (donc il faudra dessiner les échantillons et non pas juste afficher une image prédéfinie, on verra comment faire...)
  • Synchroniser des animations sur la musique frequences, waveforms, volume meters, autres...
  • Etc....

Nous allons découper le travail que vous allez faire en séances de TP et à chaque fois nous vous fourniront des éléments pour avancer. Les dernières séances seront consacrées à la finalisation de votre travail et à la présentation de vos résultats.

Séance numéro 1

Travail à faire:

  1. Vous inscrire au MOOC HTML5 part 1 et aussi part 2 (aller sur cette adresse et vous inscrire, c'est gratuit), ainsi vous aurez accès à tout le contenu. Les Weeks 3 et 4 de HTML5 part 1 vont vous servir pour le dessin et l'animation dans un canvas, la Week1 du cours part 2 présente WebAudio.
     
  2. Installer, faire tourner et étudier le prototype quick and dirty du lecteur multi-pistes dont vous avez les screenshots ci-dessus. Ca se passe sur cette page!

Séance numéro 2

Vous aurez certainement besoin, pour vos projets, d'utiliser des composants graphiques évolués: boutons qui tournent, sliders, interrupteurs, vu mètres, etc... Une approche nouvelle de la conception de GUIs pour le Web s'appelle les "Web Components", ce sera le sujet du cours d'aujourd'hui! 

Web Components qui pourront vous être utiles:

Si cela ne convient pas, voici quelques ressources pour des composants graphiques pour Web Audio:

  • Nexus UI et ici un builder d'interfaces basé sur Nexus UI, créé par son auteur: BRAID 
  • Kievll, un ensemble de widgets

Supports de cours:

Travail à faire:

  • Récupérez si vous le souhaitez des fichiers multipistes en allant voir votre enseignant,
  • Le reste de la séance consiste à ajouter de nouvelles fonctionnalités à votre lecteur multi-pistes. Ca se passe sur cette page!
     
  • Les groupes ayant choisi le sujet de la création d'effets pour guitare peuvent commencer à réfléchir à une IHM permettant de manipuler et relier des composants unitaires correspondants à des noeuds WebAudio. Par exemple, allez voir: http://www.cim.mcgill.ca/~clark/nord...n_effects.html qui explique comment réaliser la plupart des effets (chrosu, flanger, distorsion qui sonne, etc...), ou encore les démos sur https://webaudiodemos.appspot.com/, notamment le "web audio playground". Vous pourrez également regarder le source de la démo sur les "input effects" (sur le github de Chris Wilson) et voir comment il a réalisé ses effets. Vous pourrez tester vos effets, si vous n'avez pas de guitare, en branchant en entrée des riffs ou des solos de guitare enregistrés, comme dans les exemples du MOOC HTML5 partie 2, semaine 1, sur Web Audio.

Séance numéro 3: dessin, animation, dessiner la forme d'onde d'un son, XhR2, fetch, promesses...

Comment dessiner une forme d'onde, à la manière de ?

 Waveform.png

Ce n'est pas aussi simple que de dessiner la forme d'onde qui danse en musique. Si on balaye le canvas colonne par colonne et qu'on va chercher l'échantillon qui correspond, on fait du sous échantillonage (il y en a 44100 pour une seconde de musique) et cela ne sera pas représentatif. Inversement, si on ballaye tous les échantillons, on va dessiner des centaines de fois des valeurs souvent très proches dans la même colonne du canvas. La bonne solution consiste à balayer l'échantillon mais avec une certaine "définition", en gros, on va sauter dans l'échantillon et ne prendre qu'un échantillon tous les n échantillons. Le logiciel MT5 (http://mainline.i3s.unice.fr, lien github dans le menu help) utilise le code du fichier waveformDrawer.js pour cela. Voici le code qui l'utilise:

// Object that draws a sample waveform in a canvas
var waveformDrawer new WaveformDrawer();

function drawTrack(decodedBuffer)
    waveformDrawer.init(decodedBuffer, canvas, 'green');
    // First parameter = Y position (top left corner)
    // second = height of the sample drawing
    waveformDrawer.drawWave(0, canvas.height);
}

Mais vous pouvez aussi utiliser des librairies comme WaveSurfer.js ou autres... Avec cela vous n'aurez plus besoin d'images pour dessiner les formes d'ondes mais vous pourrez les dessiner à la volée.

Ecrire un chargeur de sons multiples en Ajax

Vous avez certainement lu le MOOC HTML5 part 2 sur Web Audio, ou regardé le chargeur multiple fourni avec le lecteur multi-pistes. Pour les plus curieux d'entre vous, il y a moyen d'écrire du code bien plus concis avec les nouveautés de EcmaScript 6, notamment la methode fetch() et les "promesses".

Pour voir les nouveautés du nouveau JavaScript, allez faire un tour sur http://www.es6fiddle.net/

Vous pouvez comparer un chargeur multiple de sons utilisant Xhr2 avec le même utilisant fetch et les promesses.

Lire aussi: un tutorial sur fetch()  et un autre sur les promesses  

Remarquez qu'il existe des polyfills pour que tout cela tourne dans tous les navigateurs (même assez anciens), il faut utiliser babel et un polyfill pour fetch. En général on utilise un outil de build pour "transcompiler" le code ES6 en ES5 avant de l'exécuter. Cela peut se faire également à la volée dans le browser avec babel-standalone (c'est ce qui est utilisé dans l'exemple tournant dans jsbin, que je vous ai montré 5 lignes au-dessus). Dans ce cas, une fois le code chargé dans le browser, il est trans-compilé dans le browser avant d'être exécuté. Notez que FireFox supporte 100% les promesses et fetch(), et que le support natif arrive à grand pas. Si le code est exécuté dans un navigateur qui supporte nativement ces features, le polyfill ne doit normalement rien faire.

Ceux parmis vous qui sont habitués à développer en JS avec des outils comme gulp vont trouver des gulp files qui viennent déja avec le support pour babel et fetch. Les autres, si vous ne comprenez pas cette dernière phrase, ce n'est pas grave, vous n'utilisez pas gulp !

Uploader des fichiers avec NodeJS

Testez cet exemple qui fait de l'upload de fichiers en Ajax. Il utilise les modules nodeJS express et le module multer, qui permet le support de formulaires multipart. Je vous fournis cet exemple car la plupart de ceux que j'ai trouvé sur le net sont obsolètes ou bien ne supportent pas l'envoi de multiples fichiers, ou bien ne sont pas avec des clients Ajax modernes...

Noter que si on veut envoyer en plus des champs de formulaires, ou pouvoir envoyer des fichiers par drag'n'drop, très très peu de modifications sont nécessaires (cf le MOOC HTML5 partie 2, sur Xhr2 upload de fichiers avec FormData et sur drag'n'drop).

index.html (code client):

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>HTML5 file upload with monitoring</title>
  </head>

  <body>
     <h1>Example of Xhr2 file upload, with progress bar</h1>
    Choose a file and wait a little until it is uploaded (on a fake server).
    <p>
    <input id="file" type="file" multiple/>
    <br/><br />
    Progress: <progress value = 0 id="progress"></progress>

    <script>
    var fileInput = document.querySelector('#file'),
        progress = document.querySelector('#progress');

    fileInput.onchange = function() {
        var xhr = new XMLHttpRequest();
        xhr.open('POST', 'api/file');

        xhr.upload.onprogress = function(e) {
            progress.value = e.loaded;
            progress.max = e.total;
        };
        
        xhr.onload = function() {
            console.log('Upload complete!');
            showUploadedImages();
        };

        var form = new FormData(); // ici on aurait pu ajouter un formulaire
                                   // comme param de FormData, cela
                                   // aurait envoyé les champs en plus
       
        for(var i=0; i < fileInput.files.length; i++)
            form.append('file', fileInput.files[i]);

        xhr.send(form);
    };
    // for displaying uploaded images
    function showUploadedImages() {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', 'uploads');

        xhr.onload = function(e) {
            var images = JSON.parse(this.response);
            for(var i=0; i < images.length; i++) {
                var img = document.createElement("img");
                img.src = "uploads/" + images[i];
                img.width=100;
                document.body.appendChild(img);
            }
        };
        xhr.send();
    }
    </script>
  </body>
</html>


server.js (code serveur):

var express = require("express");
var fs = require("fs");
var multer  = require("multer");

var app     = express();

var storage = multer.diskStorage({
  destination: "./public/uploads",
  filename: function (req, file, cb) {
    cb(null, file.originalname + '-' + Date.now());
  }
});

var upload = multer({ storage: storage });

app.use(express.static('./public'));

app.post('/api/file', upload.array('file'), function (req, res) {

  console.log("received " + req.files.length + " files");// form files
  for(var i=0; i < req.files.length; i++) {
  	console.log("### " + req.files[i].path);
  }
  //console.log("The URL for the file is:" + "localhost:3000\\"+req.file.path);

  res.status(204).end();  

});

app.get('/', function (req, res) {

  res.sendFile("index.html");

});

app.get('/uploads', function (req, res) {
	fs.readdir("./public/uploads", function(err, list) {
			res.end(JSON.stringify(list));
	});
  
});

app.listen(3000, function () {

  console.log("Server is listening on port 3000");
  console.log("Open http://localhost:3000 and upload some files!")

});

t