Skyduino:~#
Articles
programmation, projet, python

[Hack] Un port série … carte son.

Bonjour tout le monde !

Il y a fort longtemps (plus d’un an déjà, le temps passe vite) je vous présentais un petit montage pour transformer une carte son en port série.
C’était un montage très simple, mais bien sympa et surtout bien pratique.

Mais voilà, il y a quelques semaines je m’ennuyai pendant une heure de libre (chose très rare en ce moment, je parle du temps libre).
Et soudain, une idée aussi loufoque que géniale m’est venue à l’esprit : faire l’inverse, transformer un port série en carte son.
Oui oui, vous avez bien lu : transformer un port série en carte son pour écouter de la musique.
Utilité de la chose ? Aucune, mais c’est fun donc autant essayer !

Bon, là vous devez vous dire "il est fou !?" ou "nan, mais t’es sérieux là ?".
Eh bien oui je suis sérieusement fou, car j’ai effectivement réussi à transformer un port série en carte son … avec seulement 3 composants de base que vous devez tous avoir dans vos tiroirs.

Intéressé ? Suivez le guide ;)

La sainte PWM

À force de parler de PWM dans mes articles, vous devriez savoir de quoi il s’agit.
Mais dans le doute, je vais en rajouter une couche.

La PWM (Pulse Width Modulation) est une méthode de modulation de signal qui permet de faire plein de choses.
Réguler la vitesse d’un moteur, moduler la luminosité d’une led, ou – moyennant un haut-parleur – de faire du son (enfin plutôt des "bips").
La PWM est aussi une façon simple et peu coûteuse de générer un signal quasi analogique à partir d’une simple sortie numérique.

DSCF2976

Le principe de base de la modulation PWM est ultra simple.
On dispose d’un signal logique (soit deux états : 1/haut/tension max ou 0/bas/0v) avec une fréquence de modulation fixe (ici représenter par la période T, rappel Fréquence = 1 / Période).
La fréquence de modulation ne change pas, c’est uniquement le rapport temps haut (thaut) / temps bas (tbas) qui est variable.
En faisant varier ce rapport, on fait varier la valeur moyenne du signal, donc la tension, ce n’est pas plus compliqué que cela.

Dans l’exemple ci-dessus, j’ai représenté un signal avec un rapport cyclique de 50% (thaut = tbas = 1/2 de T).
Envoyez ce signal dans un moteur et il tournera à la moitié de sa vitesse maximum.
Envoyez ce signal dans une led et elle s’allumera à presque la moitié de sa puissance maximale.
Au final en PWM ce qui compte c’est le rapport thaut/tbas.
Si on mesurait la tension moyenne du signal avec un multimètre, elle serait dans l’exemple ci-dessus de VCC/2 (50% de la tension maximum).

Du coup si l’on est malin on peut détourner ce principe de modulation pour faire des sons, ou plus généralement n’importe quel signal analogique (moyennant l’usage d’un filtre passe-bas pour lisser le signal).
Il suffit de moduler le signal PWM à une fréquence définie – de préférence la plus élevée possible – à plusieurs MHz par exemple, et vous obtenez un DAC ("Digital to Analogic Converter", un convertisseur numérique analogique).

Ensuite pour générer du son comme le ferait une vraie carte son, il suffit de changer à intervalle régulier la valeur du rapport cyclique, par exemple à 44.1KHz.
C’est grâce à cette astuce qu’il est possible de faire de la musique "chiptune" ou encore de jouer des petits morceaux de musique avec un microcontrôleur sans sortie analogique.

Et les ports série dans tout ça ?

Bon ok, la PWM ça permet de faire du son si on connait l’astuce. Mais quel rapport avec notre histoire de port série ?
J’y vient ;)

DSCF2978

Vu à l’oscilloscope un signal série ressemble au graphique ci-dessus.
Au début se trouve un "bit de start", toujours à "0", puis 8 bits de données (ou 5, 6, 7 voir 9 bits, ça dépend de la configuration du port série) et pour finir 1 (ou 2) "bits de stop", toujours à "1".

En y regardant de plus près, on se rend compte que sans le bit de start et de stop on dispose de 8 "temps" (ou "pas") modifiables à volonté.
Sans ces bits de start et de stop on pourrait donc générer à la main un signal PWM sans difficulté juste en envoyant les octets adéquats via le port série.

DSCF2974

La solution miracle ? Inverser le signal.

Ce qui nous empêche d’utiliser le signal série comme un signal PWM tient au fait que le signal série commence par un niveau "0" et fini par un niveau "1". C’est tout l’inverse d’un signal PWM qui commence par un niveau "1" puis se finit par un niveau "0".
Si on inverse le signal le problème ne se pose plus, on perd toujours deux 2 temps/pas (puisse que non modifiables), mais le reste du signal est à nous.

Il reste cependant un (gros) problème : générer une série d’octets qui une fois transmit via le port série font que celui-ci devient une sortie PWM.

PS On parle ici d’une sortie PWM de très faible précision. Avec 8 temps/pas par période cela représente un PWM sur … 3 bits (2^3 = 8).
Pour comparaison, une sortie PWM classique sur un microcontrôleur bas de gamme est sur 8 bits (soit 256 pas).
Et une carte son standard à une sortie sur 16 bits, contre 24 bits pour une carte son qualité studio.
Il ne faut donc pas s’attendre à une qualité audio type blueray ;)

Python à la rescousse

À tout problème une solution en python ;)

Le script ci-dessous prend en argument un fichier .wav (fichier son non compressé) comportant 1 canal audio mono, encodé en PCM 8 bits.
Le script traite chaque échantillon du fichier audio et le fait passer dans une fonction toute bête qui transforme la valeur brute 0~255 en une valeur PWM sur 3 bits.

Voici en image le principe de fonctionnement de cette fonction :

DSCF2984

Le plus important est de bien choisir la fréquence d’échantillonnage du fichier son.
Vous vous doutez bien que l’on n’utilisera pas une fréquence standard (44.1KHz, etc.), mais une fréquence supportée par le port série qui servira au montage.
Si vous comptez envoyer le fichier au travers d’un port série à 115200 bauds configuré en 8N1 (1 bit de start, 8 bits de données, 1 bit de stop, pas de parité), il vous faudra choisir une fréquence d’échantillonnage de 11520Hz (vitesse du port série / nombre total de bits par octet) pour le fichier son.

Petite remarque : à 115200 bauds (soit une fréquence d’échantillonnage de 11.52KHz pour le fichier son en 8N1) la fréquence de modulation est audible.
Vous aurez donc un sifflement aigu assez désagréable en plus du son. Il suffit de dépasser les 20KHz pour ne plus entendre ce sifflement ;)

import wave, sys

if len(sys.argv) < 2:
    print "USAGE: %s [INPUT .WAV FILE]" % sys.argv[0]
    exit(1)

INPUT_WAVE_FILE = sys.argv[1]

def resample(x):
    if x >= 224: return 0b11111111
    if x >= 195: return 0b1111111
    if x >= 168: return 0b111111
    if x >= 140: return 0b11111
    if x >= 112: return 0b1111
    if x >= 84: return 0b111
    if x >= 56: return 0b11
    if x >= 28: return 0b1
    return 0

audioFile = wave.open(INPUT_WAVE_FILE, 'rb')
outputFile = open(INPUT_WAVE_FILE + '.bin', 'wb+')

if not audioFile:
    print 'Cannot open input file', INPUT_WAVE_FILE
    exit(1)

if not outputFile:
    print 'Cannot create output file', INPUT_WAVE_FILE + '.bin'
    exit(1)
    
if audioFile.getnchannels() != 1:
    print 'Only mono wave files are supported'
    exit(1)

if audioFile.getsampwidth() != 1:
    print 'Only 8bits PCM wave files are supported'
    exit(1)

nbFrames = audioFile.getnframes()
print 'Dumping %d frames at %dHz' % (nbFrames, audioFile.getframerate())

nbFramesPerPercent = nbFrames / 100
for i in range(0, nbFrames):
    frame = audioFile.readframes(1)
    sample = resample(ord(frame[0])) ^ 255 # Inverted output
    outputFile.write(chr(sample))
    if (i % nbFramesPerPercent) == (nbFramesPerPercent - 1):
        print 'Dumping %d%%' % (i / nbFramesPerPercent)

print 'Done'
outputFile.close()
audioFile.close()

Le fichier source prêt à l’emploi est aussi disponible sur mon github : https://dl.dropboxusercontent.com/u/53604363/audio_serial/extract_bin.py

Le montage

La théorie, c’est fait. Le fichier son, c’est fait.
Maintenant un peu de pratique ;)

montage_serial_audio

Le montage se compose de trois composants de base :
– une résistance de 10K
– un transistor BC557 (attention c’est un transistor PNP et non NPN),
– d’une diode 1N4007.
À cela s’ajoute un petit haut-parleur de 8 ohms et un câble usb-série (style dongle FTDI basic).

"Et l’inversion du signal t’en fait quoi ?"
Héhé, toute la magie du montage réside dans l’utilisation d’un transistor PNP (et non NPN).
Il sert à la fois d’inverseur de signal et de sortie de puissance.
Quand le signal série est à "1" le transistor est bloquant, quand il est à "0" le transistor est passant.
Un montage simple, mais efficace ;)

Voilà ce que donne le montage sur une breadboard :

DSCF2982

Annexe : Comment générer un fichier .wav avec audacity

Avant que l’on me pose la question voici comment générer un fichier .wav, mono canal, encodé en PCM 8bits, avec Audacity.

audacity_import_p1

Étape 1 : Importation d’un fichier son (mp3, flac, ce que vous voulez).

audacity_import_p2

Étape 2 : Passage en mono canal.

audacity_import_p3

Étape 3 : Choix de la fréquence d’échantillonnage cible.

audacity_import_p4

Étape 4 : Export du fichier en .wav.

audacity_import_p5

audacity_import_p6

Étape 5 : Choisir "Autre type de fichier non compressé", puis cliquer sur "Options" et choisir les options ci-dessus.

Étape 6 : Enregistrer, voilà le fichier .wav est prêt !

Remarque : pour transformer le fichier .wav en .bin il suffit de le glisser sur l’icône du script python.
Celui-ci générera automatiquement un fichier du style "le_nom_du_fichier.wav.bin".

Annexe : Envoyer un fichier binaire sur un port série

Dans le même ordre d’idée, et avant qu’on me pose la question, voici comment faire pour envoyer un fichier binaire sur un port série sous Windows avec Tera Term.

tera_option_transfert_fichier

Étape 1 : Une fois TeraTerm ouvert et le port série configuré (voir le menu "Configuration" pour ça, je vous laisse vous débrouiller), choisir dans le menu "Fichier" l’option "Envoyer un fichier"

transfert_fichier_tera

Étape 2 : Choisir le fichier à envoyer et – très important – cocher la case "Binaire" en bas à gauche sinon rien ne fonctionnera correctement.

Étape 3 : Si votre fichier a été correctement généré, que le port série est bien configuré et que le montage est bon, vous devriez pouvoir jouir d’une douce mélodie issue d’une carte son artisanale de qualité médiocre (mais quand même supérieur à une radio FM un jour de mauvais temps ;)).

Pour les curieux ne voulant pas s’embêter voici quelques fichiers d’exemples :
Pour un port série à 115200 bauds, 8N1 :
https://dl.dropboxusercontent.com/u/53604363/audio_serial/chiptune_nyancat.wav.bin
Résultat "in real life" : https://dl.dropboxusercontent.com/u/53604363/audio_serial/test_115200bps.mp3

Pour un port série à 460800 bauds, 8N1 :
https://dl.dropboxusercontent.com/u/53604363/audio_serial/When_Im_Bored.wav.bin
Résultat "in real life" : https://dl.dropboxusercontent.com/u/53604363/audio_serial/test_460800bps.mp3

Bonus
Pour un port série à 115200 bauds, 8N1 :
https://dl.dropboxusercontent.com/u/53604363/audio_serial/mysterious_song.wav.bin
Résultat "in real life" : non, pour celle-là je vous laisse essayer par vous même ;)
(merci de ne pas gâcher la surprise des autres lecteurs dans les commentaires)

Bon WE à toutes et à tous, et surtout, ne faite pas trop droper les basses avec cette carte son old-school ;)

About these ads

Discussion

5 réflexions sur “[Hack] Un port série … carte son.

  1. Et avec un port // ? ;)

    Publié par Daniel Requejo | 5 juillet 2014, 14 h 58 min
  2. Trop fort, j’adore tes articles fun !

    Publié par CasoTronics | 8 juillet 2014, 11 h 25 min
  3. En effet, tu est fou ! :) La question que je me pose, comment il est possible de réaliser cette experience en 1H de temps libre ? :)

    Publié par Bruno Adelé | 1 août 2014, 15 h 42 min
    • 4 composants sur une breadboard et 50 lignes de code python, en une heure c’est largement faisable.
      Au final j’ai passé plus de temps à chercher un petit haut parleur dans mes tiroirs qu’à faire le montage ;)

      Publié par Skywodd | 6 août 2014, 15 h 07 min

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

Archives

Wow. Dogecoin. Amaze.

Laissez un tip en Dogecoin

DMMNFk6WBVTpx2Wu1Z35GL61QXSd6r6WQx

Suivre

Recevez les nouvelles publications par mail.

Rejoignez 753 autres abonnés