Radionomy Board
It is currently Tue Mar 26, 2019 2:53 pm

All times are UTC




Post new topic Reply to topic  [ 13 posts ]  Go to page Previous  1, 2
Author Message
PostPosted: Mon Jul 27, 2015 7:54 am 
Offline
User avatar

Joined: Mon Jul 20, 2015 8:14 am
Posts: 181
Radio 1: http://radionomy.com/UtopicRadio
À défaut d'avoir un traitement poussé dans le RMO, j'ai décidé de faire mon propre traitement côté client. C'est-à-dire que c'est le navigateur de l'utilisateur qui se charge de traiter le son.
Ce n'est pas la meilleure solution parce que utiliser ce genre de traitement avec les navigateurs, c'est faire appel à des technologies encore expérimentales, et qui ne sont pas disponibles partout (exemple : IE11 ne dispose pas de cette fonctionnalité, par contre, c'est à venir sur Microsoft Edge ; et sur Firefox ça ne fonctionne pas encore mais impossible de savoir pourquoi).

Je reviendrai vers vous quand j'aurai réglé tous ces problèmes :)

_________________
Christopher

Image


Top
 Profile Send private message  
 
PostPosted: Mon Jul 27, 2015 4:37 pm 
Offline
User avatar

Joined: Tue Oct 07, 2014 8:03 pm
Posts: 44
Location: Issoire Puy de Dôme Auvergne
Radio 1: http://www.a1rlaradio.com
O.S: MacOS 10.7
christophermh44 wrote:
À défaut d'avoir un traitement poussé dans le RMO, j'ai décidé de faire mon propre traitement côté client. C'est-à-dire que c'est le navigateur de l'utilisateur qui se charge de traiter le son.
Ce n'est pas la meilleure solution parce que utiliser ce genre de traitement avec les navigateurs, c'est faire appel à des technologies encore expérimentales, et qui ne sont pas disponibles partout (exemple : IE11 ne dispose pas de cette fonctionnalité, par contre, c'est à venir sur Microsoft Edge ; et sur Firefox ça ne fonctionne pas encore mais impossible de savoir pourquoi).

Je reviendrai vers vous quand j'aurai réglé tous ces problèmes :)


Bonjour,

Là je suis curieux... Je ne savais pas que cela était possible !

_________________
Nouvelle RADIO ! => A1R http://www.a1rlaradio.com


Top
 Profile Send private message  
 
PostPosted: Tue Jul 28, 2015 3:14 pm 
Offline
User avatar

Joined: Mon Jul 20, 2015 8:14 am
Posts: 181
Radio 1: http://radionomy.com/UtopicRadio
AVERTISSEMENT : Ce post est très technique. Il allie à la fois les connaissances des traitements de son en détail (filtrage, compression, égalisation entre autres), et la programmation web en javascript.

Dans mon précédent post, j'expliquais qu'il était possible d'avoir un traitement de son exécuté par le navigateur de l'auditeur. Tout d'abord, sachez que cette technologie est encore expérimentale, et je n'ai constaté son bon fonctionnement qu'avec Google Chrome (dernière version publique) ; Firefox a besoin d'un paramétrage (avec about:config), mais même avec ce paramétrage, l'audio ne semble pas suivre ; je n'ai pas testé avec Safari, ni avec Internet Explorer (mais de toute façon même IE 11 n'est pas compatible avec cette technologie…) ; les navigateurs mobiles ne sont également pas compatibles. J'ai donc prévu un "fallback" (une alternative) pour que les utilisateurs d'autres navigateurs puissent eux aussi écouter ma radio, mais sans traitement de son (tant pis).

Plus de détails sur la compatibilité de la solution.

Du coup, vu que seul Chrome est compatible à 100%, j'ai ajouté un premier contrôle pour éviter que l'audio ne plante chez ceux qui utilisent un autre navigateur :

Code:
   window.isGoogleChrome = function() {
      var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
      return is_chrome;
   };


Et un peu plus loin dans mon fichier :

Code:
   if (!isGoogleChrome()) {
      return;
   }


Même contrôle pour les mobiles :

Code:
   window.mobileAndTabletcheck = function() {
   var check = false;
      (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
      return check;
   };


Et un peu plus loin :

Code:
   
   if (!hasAudioContext()) {
      return;
   }


Le "return;" nécessite évidemment que l'on se trouve à l'intérieur d'une fonction.

Maintenant qu'on est assurés d'être dans un contexte d'exécution fonctionnel, on passe au traitement de son. Pour cela, il faut utiliser l'API Web Audio.

Code:
   var audio = new Audio();
   audio.src = '/listen';
   audio.play();


Sur mon serveur, l'URL /listen redirige vers mon flux Radionomy (1).

Code:
   var context = new (AudioContext || webkitAudioContext)();
   var source = context.createMediaElementSource(audio);


Selon la version de Chrome, on peut être en présence de la version expérimentale de AudioContext, d'où le préfixage avec "webkit" quand on crée le contexte.
Petite explication : le contexte audio, c'est un peu l'espace de travail dans lequel on va traiter le son. Dans cet espace, on manipule des sources audio, des sorties, et différents traitements du signal audio. Ici, pour indiquer que nous allons travailler avec notre flux Radionomy déclaré plus haut, on demande au contexte de nous créer une source de type "Media Element" (c'est cette fonctionnalité qui n'est disponible sous Firefox qu'avec l'activation d'un paramètre).

ATTENTION. À partir d'ici, c'est une chaîne d'effet que je vous propose, avec mes propres réglages. Libre à vous, une fois que vous aurez compris le principe de l'adapter selon vos besoins.

Code:
   var sp;
   sp.input = context.createGain();
   sp.input.gain.value = 2;
   source.connect(sp.input);


Ensuite, on crée une variable sp, qui sera notre objet contenant tout le traitement de son.
On demande au contexte de nous créer un effet de type "gain". Qui va permettre (vous l'aurez compris) d'amplifier ou diminuer notre signal. Important à savoir, cet effet ne travaille pas en dB mais avec des nombres flottants positifs ou nuls, une valeur comprise entre 0 et 1 diminuera le gain du signal, une valeur égale à 1 le laissera intact, et une valeur supérieure à 1 amplifiera le signal.
Une fois le gain créé, on connecte la source à notre gain.

Code:
   sp.output = context.createGain();
   sp.output.connect(context.destination);


Je fais la même chose pour la sortie, j'applique un gain, que je laisse à 1 (valeur par défaut), et je le connecte à la destination (context.destination) : c'est ce qui va nous permettre de faire ressortir le son du contexte.
À partir d'ici, on travaille à l'envers, on part de la fin de la chaîne et on va remonter petit à petit.

Code:
   sp.limiter = context.createDynamicsCompressor();
   sp.limiter.attack.value = 0.001;
   sp.limiter.release.value = 0.010;
   sp.limiter.ratio.value = 20000;
   sp.limiter.threshold.value = -0.1;
   sp.limiter.connect(sp.output);


Ici, on demande au contexte de nous créer un compresseur, que je vais régler comme un limiteur (d'où le ratio énorme !). Les valeurs sont en secondes pour l'attaque et la relâche, en dB pour le seuil, et en dB:1 pour le ratio. Comme mon limiteur est le dernier effet de ma chaîne d'effet, je le connecte au gain de sortie créé plus haut.

Code:
   sp.basseq = context.createBiquadFilter();
   sp.basseq.type='peaking';
   sp.basseq.frequency.value=110;
   sp.basseq.Q.value=0.5;
   sp.basseq.gain.value=1.2;
   sp.basseq.connect(sp.limiter);


Un petit filtre paramétrique pour réhausser les basses vers 110Hz avant mon limiteur. De la même manière, on demande au contexte de nous créer un filtre paramétrique, et on effectue le réglage.

Code:
   sp.mutlibandCompressor = {band: []};
   sp.multibandCompressor.band[0] = newBand(sp.input, sp.basseq, 170, 0.06, 0.23, 5, -25.3, 0.66);
   sp.multibandCompressor.band[1] = newBand(sp.input, sp.basseq, 1000, 0.04, 0.42, 5, -20.5, 0.72);
   sp.multibandCompressor.band[2] = newBand(sp.input, sp.basseq, 3200, 0.03, 1, 4, -18.8, 0.60);
   sp.multibandCompressor.band[3] = newBand(sp.input, sp.basseq, 7200, 0.03, 1.5, 3, -22.2, 0.59);
   sp.multibandCompressor.band[4] = newBand(sp.input, sp.basseq, 12000, 0.03, 2.7, 3, -21.4, 0.68);


Pour le compresseur multibande qui vient avant le correcteur de basses ci-dessous, c'est un peu plus complexe. Comme chaque bande est composée d'un filtre, d'un compresseur et d'un gain de sortie, j'ai créé une fonction (newBand) que je détaille ci-dessous :

Code:
   var newBand = function(src, dest, centerFrequency, attack, release, ratio, threshold, gain) {
      var filter = context.createBiquadFilter();
      var compressor = context.createDynamicsCompressor();
      var outputGain = context.createGain();

      src.connect(filter);
      filter.connect(compressor);
      compressor.connect(outputGain);
      outputGain.connect(dest);

      filter.frequency.value = centerFrequency;
      filter.Q.value = 0.5;
      filter.type = 'bandpass';

      compressor.attack.value = attack;
      compressor.release.value = release;
      compressor.ratio.value = ratio;
      compressor.threshold.value = threshold;

      outputGain.gain.value = gain;

      return {
         filter: filter,
         compressor: compressor,
         gain: outputGain,
      };
   };


Cette fonction crée un filtre de type passe-bande sur la fréquence centrale passée en paramètre (centralFrequency), puis ajoute un compresseur, et en fin un gain de sortie de la bande. Comme vous avez pu le voir, au moment où on crée chaque bande, on passe également en paramètre notre source commune (ici, le gain d'entrée input), et la destination commune (le correcteur de basses). (2)

En résumé, on a :

Code:
   Source -> Gain -> Compresseur multibande -> Correcteur des basses -> Limiteur -> Gain -> Destination.


Et le compresseur multibande :

Code:
   Source -> Filtre passe-bande -> Compresseur -> Gain -> Destination.


Cette chaîne d'effets est inspirée du traitement de son de SoundSolution. Je précise aussi que j'ai laissé la normalisation de Radionomy sur mon flux car le traitement que je vous propose n'inclue pas d'AGC, étant donné que je n'ai pas encore les outils nécessaires pour en réaliser un… (3)

--
(1) : Comment récupérer le flux Radionomy ?
Méthode sous Google Chrome (qui possède des équivalents dans les autres navigateurs)

1. Allez sur http://radionomy.com/votre_radio
2. Appuyez sur F12 (pour ouvrir les outils de développeurs)
3. Cliquez sur le petit onglet "Network"
4. Cliquez sur le bouton "Lecture" pour lancer votre radio
5. À l'intérieur de l'onglet Network, ça devrait s'affoler un peu, et une des lignes devrait porter une inscription ressemblant à ça "votre_radio?cntUid=abcdef01-2345-6789-0123-456789abcdef?ad=adionoweb", et en gris au dessous "listen.radionomy.com"
6. Faites un clic droit sur cette ligne, puis cliquez sur "Copier l'adresse du lien"
7. Collez-là quelque part, et remplacez le "?" juste devant "ad=adionoweb" par un "&". Vous avez à présent une adresse valide (je suppose que ça doit être un petit bug de chez Radionomy).

L'URL finale devrait ressembler à ça : https://listen.radionomy.com/votre_radi ... =adionoweb

Vous pouvez ensuite tout à fait l'affecter dans votre variable "audio" de tout à l'heure :

Code:
   audio.src = 'https://listen.radionomy.com/votre_radio?cntUid=abcdef01-2345-6789-0123-456789abcdef&ad=adionoweb';


(2) : Pour le filtre passe bande, il est tout à fait possible de le remplacer par deux filtres passe-bas et passe-haut pour découper le signal en bandes, mais la sonorité s'en trouvera changée. Personnellement je préfère utiliser un passe-bande.

(3) : Ce dont j'ai besoin pour réaliser un AGC.
Un AGC n'est ni plus ni moins qu'un compresseur qui réagit lentement. Seul problème, c'est que ce genre de compresseur est très sensible aux basses (c'est ce qui se produit sur des morceaux comme "Fresh Prince" de Soprano avec la normalisation de Radionomy). Aussi, il faudrait pouvoir faire un effet audio que je coderais moi-même (ce qui est possible avec l'API Web Audio), où j'implémenterais un compresseur avec sidechain.
Le compresseur avec sidechain fonctionne comme un compresseur classique : il compresse le signal, mais en prenant un autre signal comme référence (le sidechain). Ici, mon sidechain serait donc le signal agrémenté d'un filtre coupe-bas, les fréquences aiguës étant généralement plus stables et moins dynamiques dans une chanson.
Aussi, si quelqu'un sait où je peux trouver un algorithme de compresseur, je suis preneur :)

_________________
Christopher

Image


Top
 Profile Send private message  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 13 posts ]  Go to page Previous  1, 2

All times are UTC


Who is online

Users browsing this forum: Google [Bot] and 15 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group