Un búfer FIFO es una estructura de datos comúnmente necesaria para muchos casos de uso. Los elementos se colocan en el búfer y se recuperan en el orden en que fueron colocados. Esta es una funcionalidad similar a la de una cola. La diferencia entre una cola y un búfer es que en el búfer los datos que se insertan son todos del mismo tipo de datos. Además, se puede insertar una cantidad arbitraria de datos y la cantidad de recuperación también es arbitraria.
¿Cuáles son los casos de uso en los que desea colocar y recuperar elementos numéricos arbitrarios del mismo tipo? Diversificando en campos distintos al procesamiento de datos: Piense en las cuentas bancarias: el dinero se pone en diferentes cantidades y se retira en cantidades según sea necesario... Lo mismo se aplica al almacenamiento de granos en una granja... Pero los bancos y los graneros se encargan de esto. Lo que nos queda en TI son los datos de audio, que llegan muy rápido, pero tienen que salir a una velocidad específica más lenta para que se puedan escuchar. Hoy en día, los culpables de esto son los motores de texto a voz que facilitan la comunicación interactiva entre máquinas y humanos. La máquina recibe texto (probablemente de un motor de inteligencia artificial) que convierte en bytes de audio para enviarlos al humano a una velocidad específica que el humano pueda escuchar. Como era de esperar, la máquina genera bytes de audio a un ritmo rápido que luego debe almacenarse en un buffer para poder mantener la entrega al ser humano a un ritmo mucho más lento. Una analogía es la gasolinera local. El Texto a voz es el camión cisterna de gasolina que bombea mucha gasolina a un ritmo rápido, llenando los tanques de gasolina en las entrañas de la gasolinera. Luego se entregan a un ritmo mucho más lento en los automóviles u otros vehículos del cliente.
En resumen, la conversión de texto a voz (audio) puede realizarse mucho más rápidamente. Existe la necesidad de un búfer de audio que reciba audio del texto a voz (tts) para llenar el búfer. Este buffer luego se agota al ritmo del habla humana y es comprensible para el ser humano.
Datos de audio: los datos constan de una secuencia de números que representan valores de una señal de audio a intervalos regulares (llamado frecuencia de muestreo). También existe el concepto de canales, que son múltiples señales de audio, lo que da como resultado una secuencia de múltiples valores.
A los efectos de este artículo, consideraremos solo un canal en el lado de entrada y un canal en el lado de salida.
Los requisitos son: poder ingresar un número arbitrario de cuadros de datos de audio (un cuadro es un número que representa un punto de datos de audio), que es una matriz ordenada de valores de señales de audio. En el lado de salida, podrá recuperar una cantidad arbitraria de estos fotogramas. Por supuesto, tenemos que agregar características convenientes para manejar las limitaciones, que son un tamaño de búfer limitado (provoca condiciones de búfer lleno en la entrada), no hay datos de audio disponibles (búfer vacío en el lado de salida). Otras características convenientes incluirán el llenado cero de datos de audio, en el caso de que se soliciten más datos de audio de los disponibles en la recuperación del búfer.
A continuación se describe una implementación de dicho búfer en Python:
Los bytes de audio entrantes se almacenan en Buffer. El búfer tiene un puntero "inferior", que señala hasta qué punto está lleno el búfer. También tiene un 'puntero de inicio' que es el comienzo del búfer donde se pueden enviar nuevos datos. El puntero de inicio se fija al comienzo del búfer. El puntero inferior es "dinámico" y va "arriba y abajo": arriba cuando se extraen datos y "abajo" cuando se insertan datos. Los datos siempre se insertan en la parte superior (puntero de inicio) del búfer, lo que hace que los datos existentes en el búfer se empujen "hacia abajo", aumentando así el valor del puntero inferior.
La condición de búfer vacío se produce cuando el puntero inferior es igual al puntero de inicio. La condición de búfer lleno ocurre cuando el puntero inferior es igual a la longitud del búfer.
También podemos incluir 'fallos elegantes' para manejar las condiciones en las que el búfer está lleno.
Cuando el búfer esté lleno y sea necesario insertar datos, genere una excepción. Cuando el búfer está vacío (incluido el caso en el que se solicitan más datos de los disponibles en el búfer), devuelve "ceros" para los datos que faltan. Este es el equivalente en audio del "silencio" cuando no se pronuncian palabras.
Un diagrama de este buffer es:
Codificación: (Descargo de responsabilidad: no se utilizó IA para generar el siguiente código. Toda la culpa (los elogios son mejores) deben asignarse al autor...)
Siguiendo los principios orientados a objetos, el código está escrito como una clase/objeto y es fácil de usar. El código completo es:
import numpy as np #numpy is the standard package or numerical array processing class AudioBuf: def __init__(self,bufSize:int,name:str='',dtype=np.int16): self.buffer = np.zeros((bufSize), dtype=dtype) self.bufSize=bufSize self.dtype=dtype self.idx=0 self.name=name #give a name to the buffer. def putInBuf(self,inData:np.ndarray[np.dtype[np.int16]]): inData=inData[:, 0] #Get the 1st col = channel 0 - mono remainder = self.bufSize - self.idx #space available for insertion if remainder < len(inData): msg=f'Error: Buffer {self.name} is full' print(msg) self.showBuf() raise ValueError(msg) self.buffer[self.idx:self.idx + len(inData)] = inData self.idx += len(inData) def getFromBuf(self,outDataLen:int = None)->np.ndarray: if not outDataLen: outDataLen=self.idx # return entire data of length is not specified if self.idx >= outDataLen: retVal = self.buffer[:outDataLen] self.buffer[0:self.idx-outDataLen]=self.buffer[outDataLen:self.idx] #move buffer up self.idx -= outDataLen else: retVal=np.zeros((outDataLen), dtype=self.dtype) retVal[0:self.idx] = self.buffer[0:self.idx] self.idx=0 return np.reshape(retVal,(-1,1)) #The -1 value automatically calculates to the number of elements def showBuf(self): print(f'AudioBuf : {self.name} has length= {self.idx} with capacity {self.bufSize}')
El almacenamiento en búfer de audio es esencial y más importante ahora debido a numerosas aplicaciones de procesamiento de audio. El algoritmo de búfer de audio presentado anteriormente es una clase conveniente de Python.