Lecteur de fichiers audio via un port série (bitbang PWM)

par skywodd | | Langue : Python 3 | Licence : GPLv3

Description :

Lit un fichier audio .wav via un port série en utilisant des données formatées pour générer un signal PWM rudimentaire.

Code source :

Voir le code source brut | Télécharger musical_serial.py | Télécharger musical_serial.py.zip

"""
Lit un fichier audio .wav via un port série en utilisant des données formatées pour générer un signal PWM rudimentaire.
"""

import wave
import argparse
import serial


# Taille du buffer pour l'envoi via le port série (permet l'arrêt du script via CTRL-C)
STREAM_BUFFER_SIZE = 1024


def chunks(l, n):
    """ Découpe le buffer l en morceau de n octets. """
    for i in range(0, len(l), n):
        yield l[i : i + n]


def resample(x):
    """ Converti l'échantillon audio x sur 8 bits en un signal PWM sur 3 bits (avec inversion). """
    if x >= 224: return 0b00000000
    if x >= 195: return 0b10000000
    if x >= 168: return 0b11000000
    if x >= 140: return 0b11100000
    if x >= 112: return 0b11110000
    if x >= 84:  return 0b11111000
    if x >= 56:  return 0b11111100
    if x >= 28:  return 0b11111110
    return 0b11111111


def stream_audio_file(filename, serial_port):
    """ Lit le fichier audio spécifié via le port série spécifié en utilisant des données formatées pour générer un signal PWM rudimentaire. """

    # Ouvre le fichier audio
    with wave.open(filename, 'rb') as fi:

        # Vérifie la compatibilité du format de fichier
        assert fi.getnchannels() == 1, "Only mono wave files are supported"
        assert fi.getsampwidth() == 1, "Only 8bits PCM wave files are supported"

        # Converti les données audio en données série PWM
        print("Converting audio file to PWM serial data ...")
        nb_frames = fi.getnframes()
        framerate = fi.getframerate()
        print('Source file "{}" is {} frames long, sampled at {} Hertz'.format(filename, nb_frames, framerate))
        data = bytes(map(resample, fi.readframes(nb_frames)))
    
    # Ouvre le port série et envoi les données
    serial_baudrate = framerate * 10  # 8N1: 1 Start, 8 Data, 1 Stop
    print('Setting serial port "{}" in mode 8N1, at {} bps ...'.format(serial_port, serial_baudrate))
    with serial.Serial(port=serial_port,
                       baudrate=serial_baudrate,
                       bytesize=serial.EIGHTBITS,
                       parity=serial.PARITY_NONE,
                       stopbits=serial.STOPBITS_ONE) as serial_com:
        print("Streaming data ... Use CTRL+C to stop.")
        for chunk in chunks(data, STREAM_BUFFER_SIZE):
            serial_com.write(chunk)
            serial_com.flush()


# Interface en ligne de commande
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Stream an audio wave file over a serial port using PWM-like serial data.')
    parser.add_argument('filename', metavar='<WAVE FILE>', help='The input audio wave file (must be a mono 8 bits PCM file).')
    parser.add_argument('serialport', metavar='<SERIAL PORT>', help='The output serial port.')

    args = parser.parse_args()
    stream_audio_file(args.filename, args.serialport)