La pub, c'est ça

samedi 5 juillet 2014

Créer un son sinusoïdal en c++ sous linux a l'aide de la librairie Jackd - La base

Aujourd'hui, j'ai envie de parler de la création de son sous GNU/Linux.
Encore un domaine où je ne connais rien...
J'ai décidé d'utiliser la librairie Jack, ce qui permet de faire de l'audio en temps réel sans trop se casser la tête.
Quand je dis que je ne connais rien, je ne suis pas loin de la vérité, en effet, il y a à peine quelques jours que j'ai vraiment commencé à étudier cette branche de l'informatique.

Bon, mis à part tout cela, entrons dans le vif du sujet et oublions le reste qui nous entoure, la concentration est de mise.

Tout d’abord, il faut se poser les bonnes questions:

  • Comment créer un son?
  • Comment l'intégrer à Jack?
  • Comment le contrôler?
Trois questions très courtes, certes, mais qui vont nécessiter chacune un développement bien complexe. C'est parti...

  1. Comment créer un son?
Un son "pur" est une onde sonore de forme sinusoïdale. C'est beau mais ça, c'est du domaine de l'analogique. Il faudra donc convertir un signal analogique fictif en signal digital bien réel.

Waaaaahhhhhh! Mais vous êtes fou? Ohhh! Oui!

Mais non, pas tant que cela. Vous allez voir, c'est pas sorcier!

Comment représente-t'on une sinusoïde graphiquement?
Il est possible de la créer par la géométrie et un peu de main levée. C'est ce que je vais vous montrer parce que techniquement, c'est la même méthode que le programme utilisera.


Comme vous pouvez le voir dans l'image ci dessus, si l'on reporte les angles du cercle sur une droite horizontale et que pour chacun de ces angles, on reporte la hauteur (y) du point d'intersection de l'angle avec le cercle, une sinusoïde apparaîtra comme par enchantement sur la droite.

Concrètement, en informatique, une portion d'angle comme si dessus correspondra à ce que l'on appelle un échantillon (sample en anglais). Le termes échantillon est très important en musique informatique. Plus on en a et plus le son sera beau. En effet, si sur 1s on prend 1 seul échantillon ou bien 10, on devine facilement qu'il y aura une différence de qualité. En effet, imaginez que dans le dessin ci dessus je n'aie pris qu'un seul échantillon, auriez vous pu dessiner la sinusoïde?

Oui, mais alors, combien il faut d'échantillon, et dans quel espace de temps?

En téléphonie, on considère que 8kHz est suffisant pour entendre correctement la voix.
En HiFi, on considère qu'il faut travailler entre 20Hz et 20kHz
En Studio, par contre on travaille au minimum sur la base de 24kHz

Pour avoir la possibilité de reconnaître la fréquence d'un son, il faut impérativement avoir au minimum 2 fois le nombre de sa fréquence, donc:
Pour le téléphone, il faut 16.000 échantillons par seconde.
En HiFi, 40.000 et au studio, au minimum 48.000. La tendance en ce moment dans les studios est de 192.000 échantillons par seconde.

Le CD audio a 44100 échantillons par seconde.
Le DAT a 48000 échantillons par seconde.

Maintenant que l'on connait le nombre d'échantillons nécessaire, il serait temps de savoir aussi comment on va encoder cette valeur d'échantillon. Heureusement pour nous, Jack s'occupe de cela en grande partie. Il nous suffira de lui donner les échantillons sous forme de nombre flottant 32 bits et puis Jack se fera un plaisir de convertir tout cela pour que ce soit compréhensible par notre matériel.

Concrètement, si sur notre carte interne de l'ordinateur, qui travaille probablement en 44100, on veut sortir un son à 1kHz, il va falloir faire un petit calcul. Quel sera l'angle du cercle pour notre échantillon?????

Pour un son, inaudible, de 1Hz, on fera 44100 échantillons.
Donc, pour un son de 1000Hz, on fera 44100/1000 = 44,1 échantillons (Parfait, pour notre oreille, ou presque) par révolution complète
Pour un son à 10kHz, on aura plus que 4,41 échantillons par révolution (Pas terrible, mais l'oreille humaine n'est plus aussi sensible aux différences,...).



  1. Comment l'intégrer à Jack?

L'ordinateur est une machine qui ne peut faire qu'une chose à la fois, grosso modo. Pourtant, on lui demande beaucoup plus que cela. En ce moment, je vous écris, j'écoute une radio sur le net, mon horloge tourne, j'ai une console, QtCreator, un gestionnaire de fichier et un programme qui me dit à combien mon processeur est surchargé, sans compter tout ce qui tourne en arrière plan sans qu'on en voit directement la couleur.

Pfffff! On s'en fout, on veut faire de la musique!!!!!!!
Ben oui! Moi aussi!

J'y arrive... Jack, comme tout les autres programmes doit gérer le temps du processeur pour faire croire aux pauvres humains que nous sommes que l'ordinateur fait plusieurs choses en même temps. Jack n'a donc pas la main mise sur l'ordinateur en permanence. Il va donc devoir gérer au mieux son temps de parole.

Les ordinateurs actuels sont capable de créer des sons plus vite qu'il ne sont joué. Jack utilise cette capacité afin de remplir un buffer de sortie régulièrement, qui sera joué par l'ordinateur de manière différée.

Différée, dites vous?

Malheureusement, oui. C'est la hantise de tout les musiciens informatisés. Rassurez vous, le système de Jack est reconnu comme l'un des meilleurs moteur audio du moment. ASIO du Microsoft Windows ne lui arrive pas à la cheville, et pourtant beaucoup de gens l'utilise aujourd'hui.

Plus le buffer est petit et moins le son sera différé, mais plus l'ordinateur sera surchargé et donc sensible aux "déraillements du disque".

Si vous ne l'avez jamais fait, installer donc Jackd2, configurer le et entraînez vous à l'utiliser avec différent logiciel.

Nous allons, maintenant pouvoir passer à la partie pratique de ce tutoriel, nous connaissons tout ce qu'il nous faut pour commencer lentement.

Pour la programmation, je ne vais pas passer mon temps à créer des classes, mais je vais tout de même utiliser le compilateur du c++ afin de profiter tout de même de certaines fonctionnalités que j'affectionne tout particulièrement.

#include <jack/jack.h>

#include <iostream>
#include <unistd.h>

using namespace std;

int main()
{
 jack_status_t jackStatus;
 cout << "On commence par créer le client lui même." << endl;
 jack_client_t* client = jack_client_open("MonClientJack",
                      JackServerName,
                      &jackStatus,
                      "default");
 cout << "Ensuite, il faut créer un port audio de sortie." << endl;
 jack_port_t* outPort = jack_port_register(client,
                      "SortieAudio1",
                      JACK_DEFAULT_AUDIO_TYPE,
                      JackPortIsOutput|
                      JackPortIsTerminal,
                      0);
 cout << "On active notre client fraichement créé." << endl;
 jack_activate(client);

 cout << "Maintenant on rentre dans une boucle infinie." << endl;
 while (true)
 {
  // On attends un peu et puis on passe à la suite!
  usleep(1);
 }
}
Et voici nos premières lignes de code. Un peu d'explication s'impose...
On fait du Jack, on a donc besoin de jack/jack.h
iostream pour la fonction cout
et unistd.h pour la fonction usleep.

jack_status_t jackStatus; Pour le moment, on aura pas vraiment besoin de ceci en dehors de la création du client, je le laisse donc volontairement de côté.

jack_client_t* client = jack_client_open(.....);
Cette fonction va créer physiquement un nouveau client Jack. Si vous utilisez patchage pour faire vos connections entre les différentes sources vous verrez un nouveau rectangle s'afficher avec pour nom, celui créé ici même.

jack_port_t* outPort = jack_port_register( ..... );
C'est ici que l'on va créer le port de sortie.
"SortieAudio1" c'est le nom du port de sortie dans patchage, par exemple.
JACK_DEFAULT_AUDIO_TYPE pour dire qu'on utilise l'audio et pas le midi
JackPortIsOutput pour dire que c'est un port de sortie
JackPortIsTerminal pour dire que c'est la fin d'une chaine qui peut être utilisée par un autre programme.

Ensuite on active le client par jack_activate(client);

En dernier, on crée une boucle infinie car sinon le programme se terminerais directement. Pour ne pas que notre programme vole toutes les ressources de l'ordinateur, on le fait attendre un peu, pour qu'il puisse s'acquitter de ses autres tâches.

Et maintenant, on compile:

g++ creationclient.cpp -ljack -o creationclient

le -ljack est là pour dire qu'il faut prendre Jack en compte pour le linkage.

Si vous ouvrez le programme Patchage vous verrez un nouveau rectangle nommé MonClientJack avec une sortie nommée SortieAudio1. Si vous le désirez, vous pouvez même connecter cette sortie à n'importe quelle entrée audio.

Malheureusement, vous pouvez la connecter à autant d'entrée que vous voulez, aucun son ne sortira de notre programme.

Dans le prochain article, je reprendrai ce qui a été écrit dans celui ci afin de sortir enfin un son de nos haut-parleur.

A bientôt.

Dagal.

Aucun commentaire:

Enregistrer un commentaire