Red de conocimiento informático - Material del sitio web - Cómo modificar alsa para que admita salida pcm de 192 kHz

Cómo modificar alsa para que admita salida pcm de 192 kHz

Aquí aprenderemos sobre el significado de cada parámetro y algunos conceptos básicos.

Longitud de la muestra (sample): La muestra es la unidad más básica para grabar datos de audio, las más comunes son 8 bits y 16 bits.

Número de canales (channel): Este parámetro es 1 para mono y 2 para estéreo.

Cuadro: El cuadro graba una unidad de sonido, su duración es el producto de la duración de la muestra por el número de canales.

Frecuencia de muestreo (rate): El número de muestras por segundo, este número es para el fotograma.

Período: el número de fotogramas necesarios para un procesamiento por parte del dispositivo de audio. El acceso a los datos al dispositivo de audio y el almacenamiento de datos de audio se basan en esta unidad.

Intercalado: Es un método de grabación de datos de audio. En el modo entrelazado, los datos se almacenan en forma de fotogramas continuos, es decir, primero se graban la muestra del canal izquierdo y el canal derecho del fotograma 1. muestra (se supone que está en formato estéreo) y luego inicie la grabación del cuadro 2. En el modo no entrelazado, primero se registran las muestras del canal izquierdo de todos los fotogramas de un ciclo y luego se registran las muestras del canal derecho. Los datos se almacenan en un canal continuo. Pero en la mayoría de los casos, sólo necesitamos utilizar el modo intercalado.

Texto original en inglés: /article/6735

Período (ciclo): el intervalo entre interrupciones de hardware. Representa el retraso de entrada.

Hay un puntero en la interfaz de la tarjeta de sonido para indicar la ubicación actual de lectura y escritura en la caché del hardware de la tarjeta de sonido. Mientras la interfaz se esté ejecutando, este puntero recorrerá una ubicación en el búfer.

tamaño de fotograma = tamaño de (una muestra) * nChannels

Los tamaños de caché (buffer) y ciclo (tamaño) configurados en alsa se almacenan en forma de fotogramas (frames) en el tiempo de ejecución.

period_bytes = frames_to_bytes(runtime, runtime-gt; period_size);

bytes_to_frames()

Los tamaños de período y búfer no dependen del formato de muestra porque se miden en fotogramas; no es necesario cambiarlos.

Introducción a la programación de sonido ALSA

ALSA significa Arquitectura de sonido avanzada de Linux. Consta de una serie de controladores de kernel, interfaces de programación de aplicaciones (API) y utilidades que admiten sonido en Linux. En este artículo, presentaré brevemente el marco básico del proyecto ALSA y sus componentes de software. Se centra principalmente en la programación de la interfaz PCM, incluidos ejemplos de programas que puede practicar automáticamente.

Puede que estés usando ALSA porque es nueva, pero no es la única API de sonido disponible. ALSA es una buena opción si desea realizar manipulación de sonido de bajo nivel para poder tener el máximo control sobre el sonido y maximizar el rendimiento, o si desea utilizar funciones que no se encuentran en otras API de sonido. Si ha escrito un programa de audio, es posible que desee agregar soporte nativo para el controlador de sonido ALSA. Si no está interesado en el audio y solo desea reproducir archivos de audio, las API de alto nivel serían una mejor opción, como SDL, OpenAL y los conjuntos de herramientas proporcionados por esos entornos de escritorio. Además, sólo puede utilizar ALSA en un entorno Linux que sea compatible con ALSA.

Historia de ALSA

La razón por la que se lanzó el proyecto ALSA fue que los controladores de la tarjeta de sonido (OSS/controladores gratuitos) en Linux no se mantenían activamente. Y va por detrás de la nueva tecnología de tarjetas de sonido. Jaroslav Kysela había escrito anteriormente un controlador de tarjeta de sonido y comenzó el proyecto ALSA. A medida que pasó el tiempo, se unieron más desarrolladores al equipo de desarrollo, se admitieron más tarjetas de sonido y se reorganizó la estructura de la API.

Durante el proceso de desarrollo del kernel 2.5 de Linux, ALSA se fusionó en el árbol de fuentes oficial. Después del lanzamiento del kernel 2.6, ALSA se incorporó a la versión estable del kernel y será ampliamente utilizado.

Conceptos básicos del audio digital

El sonido consiste en la variación de la presión del aire. Se convierte a forma electrónica mediante un convertidor como un micrófono. Los convertidores analógico-digital (ADC) convierten voltajes analógicos en valores de muestra discretos. El sonido se muestrea a intervalos regulares y la velocidad a la que se muestrea se denomina frecuencia de muestreo. Las muestras se envían a un convertidor de digital a analógico (DAC), como un altavoz, y finalmente se convierten nuevamente a la señal analógica original.

El tamaño de la muestra se expresa en bits. El tamaño de la muestra es uno de los factores que afecta la precisión con la que se convierte el sonido en una señal digital. Otro factor importante es la frecuencia de muestreo. En la teoría de Nyquist, siempre que la frecuencia de Nyquist del sistema discreto sea mayor que la frecuencia o ancho de banda más alto de la señal muestreada, se puede evitar el fenómeno de aliasing.

Conceptos básicos de ALSA

ALSA consta de controladores de tarjetas de sonido para muchas tarjetas de sonido y también proporciona una biblioteca API llamada libasound. Los desarrolladores de aplicaciones deberían utilizar libasound en lugar de la interfaz ALSA en el kernel. Porque libasound proporciona la interfaz de programación más avanzada y cómoda. También proporciona una función de denominación lógica de dispositivos, por lo que los desarrolladores ni siquiera necesitan conocer interfaces de bajo nivel como archivos de dispositivos. Por el contrario, el controlador OSS/Free está programado en el nivel de llamada del sistema del kernel, lo que requiere que los desarrolladores proporcionen nombres de archivos de dispositivos y utilicen ioctrl para implementar las funciones correspondientes. Para compatibilidad con versiones anteriores, ALSA proporciona módulos del kernel para simular OSS, de modo que muchas aplicaciones previamente desarrolladas en OSS puedan ejecutarse en ALSA sin ninguna modificación. Además, la biblioteca libaoss también puede simular OSS y no requiere un módulo del kernel.

ALSA incluye funciones de complemento, que se pueden utilizar para ampliar nuevos controladores de tarjetas de sonido, incluidas tarjetas de sonido virtuales completamente implementadas en software. ALSA proporciona una serie de conjuntos de herramientas basadas en línea de comandos, como mezclador (mixer), reproductor de archivos de audio (aplay) y herramientas para controlar propiedades específicas de tarjetas de sonido específicas.

Arquitectura ALSA

ALSA API se puede descomponer en las siguientes interfaces principales:

1 Interfaz de control: proporciona funciones comunes para administrar el registro de la tarjeta de sonido y solicitar dispositivos disponibles Función

2 Interfaz PCM: interfaz para gestionar la reproducción (playback) y grabación (captura) de audio digital. El siguiente resumen de este artículo se centra en esta interfaz, porque es la interfaz más utilizada para desarrollar programas de audio digital.

3 Interfaz MIDI sin formato: admite MIDI (Musical Instrument Digital Interface), un instrumento musical electrónico estándar. Estas API brindan acceso al bus MIDI de la tarjeta de sonido. Esta interfaz primitiva funciona en base a eventos MIDI, siendo el programador responsable de gestionar el protocolo y el manejo del tiempo.

4 Interfaz de temporizador: proporciona acceso al hardware de procesamiento de tiempo en la tarjeta de sonido para eventos de audio sincronizados.

5 Interfaz del secuenciador

6 Interfaz del mezclador

Nombres de dispositivos

La biblioteca API utiliza el nombre de los dispositivos lógicos en lugar del archivo del dispositivo. El nombre del dispositivo puede ser el nombre real del hardware o el nombre del complemento. El nombre del hardware utiliza el formato hw:i,j. Donde i es el número de tarjeta y j es el número de dispositivo en esta tarjeta de sonido.

El primer dispositivo de sonido es hw:0,0. Este alias se refiere al primer dispositivo de sonido de forma predeterminada y se utilizará en los ejemplos de este artículo. Los complementos usan otro nombre único. Por ejemplo, plughw: representa un complemento. Este complemento no proporciona acceso a dispositivos de hardware, pero proporciona funciones de software como la conversión de frecuencia de muestreo. El hardware en sí no admite dichas funciones.

Almacenamiento en caché de sonido y transferencia de datos

Cada tarjeta de sonido tiene un caché de hardware para guardar muestras grabadas. Cuando el búfer esté lo suficientemente lleno, la tarjeta de sonido generará una interrupción. Luego, el controlador de sonido del kernel utiliza un canal de acceso directo a la memoria (DMA) para transferir las muestras al búfer de la aplicación en la memoria. De manera similar, para la reproducción, cualquier aplicación utiliza DMA para transferir sus propios datos del búfer al búfer de hardware de la tarjeta de sonido.

De esta manera, el área de caché del hardware es un caché en anillo. Es decir, cuando los datos lleguen al final del área del búfer, volverán a la posición inicial del área del búfer. ALSA mantiene un puntero a la ubicación actual de las operaciones de datos en la caché del hardware y la caché de la aplicación. Desde fuera del kernel, solo nos interesa el caché de la aplicación, por lo que este artículo solo analiza el caché de la aplicación.

El tamaño del búfer de la aplicación se puede controlar mediante llamadas a funciones de la biblioteca ALSA. El área del buffer puede ser muy grande y una operación de transferencia puede causar retrasos inaceptables. A esto lo llamamos latencia. Para resolver este problema, ALSA divide el área del buffer en una serie de períodos (llamados fragmentos en OSS/Free utiliza períodos como unidades para transmitir datos).

Un punto almacena algunos fotogramas. Cada cuadro contiene muestras capturadas en un momento determinado. Para un dispositivo estéreo, un cuadro contendrá muestras de ambos canales. La Figura 1 muestra el proceso de descomposición: un búfer se descompone en ciclos, luego cuadros y luego muestras. La figura contiene algunos valores supuestos. En la figura, la información de los canales izquierdo y derecho se almacena alternativamente dentro de un cuadro. Esto se llama modo entrelazado. En el modo no entrelazado, todos los datos de muestra de un canal se almacenan después de los datos del otro canal.

Over y Under Run

Cuando una tarjeta de sonido está activa, los datos siempre se transfieren continuamente entre el caché del hardware y el caché de la aplicación. Pero hay excepciones. En el ejemplo de grabación, si la aplicación no lee los datos lo suficientemente rápido, el búfer del bucle se sobrescribirá con datos nuevos. Este tipo de pérdida de datos se denomina desbordamiento. En el ejemplo de reproducción, si la aplicación no puede escribir datos en el búfer con la suficiente rapidez, el búfer "se morirá de hambre". Estos errores se denominan "infrautilizaciones". En la documentación de ALSA, estos dos escenarios a veces se denominan colectivamente "XRUN". El diseño adecuado de las aplicaciones puede minimizar y recuperarse de XRUN.

Un programa de sonido típico

Un programa que utiliza PCM normalmente se parece al siguiente pseudocódigo:

Abre la interfaz de reproducción o grabación

Configuración Parámetros de hardware (modo de acceso, formato de datos, número de canales, frecuencia de muestreo, etc.)

mientras hay datos para procesar:

Leer datos PCM (grabación)

O escribir datos PCM (reproducción)

Cerrar la interfaz

Veremos un código de trabajo a continuación. Le recomiendo que pruebe la ejecución de este código en su sistema Linux. Revise el resultado e intente modificar el código recomendado. Todos los listados de instancias relacionados con este artículo se pueden obtener desde FTP: ftp.ssc.com/pub/lj/listings/issue126/6735.tgz.

Listado 1. Mostrar algunos tipos y formatos de PCM

#include lt/asoundlib.hgt

int main() {

<; p>int val;

printf("Versión de la biblioteca ALSA: s/n",

SND_LIB_VERSION_STR);

printf("/tipos de secuencia nPCM:/ n");

for (val = 0; val lt; = SND_PCM_STREAM_LAST; val)

printf(" s/n",

snd_pcm_stream_name(( ( snd_pcm_stream_t)val));

printf("/nTipos de acceso PCM:/n");

for (val = 0; val lt;= SND_PCM_ACCESS_LAST; val)

printf(" s/n",

snd_pcm_access_name((snd_pcm_access_t)val));

printf("/nPCM formatos:/n"); p >

for (val = 0; val lt; = SND_PCM_FORMAT_LAST; val )

if (snd_pcm_format_name((snd_pcm_format_t)val)

!= NULL)

printf(" s (s)/n",

snd_pcm_format_name((snd_pcm_format_t)val),

snd_pcm_format_description(

(snd_pcm_format_t)val) ) ;

printf("/nSubformatos PCM:/n");

for (val = 0; val lt; = SND_PCM_SUBFORMAT_LAST;

val )< / p>

printf(" s (s)/n",

snd_pcm_subformat_name((

snd_pcm_subformat_t)val),

snd_pcm_subformat_description((

p>

snd_pcm_subformat_t)val));

printf("/nPCM estados:/n");

for (val = 0; val lt; = SND_PCM_STATE_LAST; val )

printf(" s/n",

snd_pcm_state_name((snd_pcm_state_t)val));

return 0;

}

El Listado 1 muestra algunos de los tipos de datos y parámetros PCM utilizados por ALSA. Lo primero que debe hacer es incluir los archivos de encabezado. Estos archivos de encabezado contienen declaraciones para todas las funciones de la biblioteca. Uno de ellos es mostrar la versión de la biblioteca ALSA.

El resto de este programa itera a través de algunos tipos de datos PCM, comenzando con el tipo de flujo. ALSA proporciona nombres constantes simbólicos para el valor final de cada iteración y proporciona funciones para mostrar una descripción de cadena de un valor específico. Como verás, ALSA soporta muchos formatos, y en mi versión 1.0.15 se soportan hasta 36 formatos.

Este programa debe estar vinculado a la biblioteca alsalib añadiendo la opción -lasound al compilar. Algunas funciones de la biblioteca alsa utilizan la función dlopen y operaciones de punto flotante, por lo que es posible que también deba agregar las opciones -ldl, -lm.

El siguiente es el Makefile del programa:

CC=gcc

TARGET=test

SRC=$(wildcard *. c)

OBJECT= ${SRC:.c=.o}

INCLUDES=-I/usr/include/alsa

LDFLAGS=-lasound

todos: $(OBJETIVO)

$(OBJETO): $(SRC)

$(CC) -c $(INCLUDES) $lt;

$(TARGET):$(OBJETO)

$(CC) -o $@ $lt $(LDFLAGS)

.PHONY:limpio

clean:

@rm -rf $(OBJECT) $(TARGET) *~

Listado 2. Apertura del dispositivo PCM y configuración de parámetros

/*

Este ejemplo abre el dispositivo PCM predeterminado, establece

algunos parámetros y luego muestra el valor

de la mayoría de los parámetros de hardware.

p>

Realiza cualquier reproducción o grabación de sonido.

*/

/* Utiliza la nueva API ALSA */

#define ALSA_PCM_NEW_HW_PARAMS_API

/* Toda la API de la biblioteca ALSA está definida

* en este encabezado */

#include lt;alsa/asoundlib.hgt ;

int main() {

int rc;

snd_pcm_t *handle;

snd_pcm_hw_params_t *params;

unsigned int val, val2 ;

int dir;

snd_pcm_uframes_t frames;

/* Abrir dispositivo PCM para reproducción */

rc = snd_pcm_open(amp ; handle, "predeterminado",

SND_PCM_STREAM_PLAYBACK, 0);

if (rc lt; 0) {

fprintf( stderr,

"no se puede abrir el dispositivo pcm: s/n",

snd_strerror(rc));

exit(1);

}

/* Asignar un objeto de parámetros de hardware */

snd_pcm_hw_p.

arams_alloca(amp; params);

/* Rellénelo con los valores predeterminados */

snd_pcm_hw_params_any(handle, params);

/* Establezca el. parámetros de hardware deseados */

/* Modo entrelazado */

snd_pcm_hw_params_set_access(handle, params,

SND_PCM_ACCESS_RW_INTERLEAVED);

/ * Formato little-endian de 16 bits firmado */

snd_pcm_hw_params_set_format(handle, params,

SND_PCM_FORMAT_S16_LE);

/* Dos canales (estéreo) */

snd_pcm_hw_params_set_channels(handle, params, 2);

/* Frecuencia de muestreo de 44100 bits/segundo (calidad de CD) */

val = 44100;

p>

snd_pcm_hw_params_set_rate_near(handle,

params, amp; val, amp; dir

/* Escribe los parámetros en el controlador */

rc = snd_pcm_hw_params(handle, params);

if (rc lt; 0) {

fprintf(stderr,

"no se puede para configurar los parámetros de hardware: s/n",

snd_strerror(rc));

exit(1);

}

/* Muestra información sobre la interfaz PCM */

printf("nombre del identificador PCM = 's'/n",

snd_pcm_name(handle));

printf("PCM state = s/n",

snd_pcm_state_name(snd_pcm_state(handle)));

snd_pcm_hw_params_get_access(params,

(snd_pcm_access_t *) amp; val);

printf("tipo de acceso = s/n",

snd_pcm_access_name(

(snd_pcm_access_t)val));

snd_pcm_hw_params_get_format(params, amp; val);

printf("formato = 's' (s)/n",

snd_pcm_format_name((snd_pcm_format_t)val),

snd_pcm_format_description(

(snd_pcm_format_t)val));

snd_pcm_hw_params_get_subformat(params,

(snd_pcm_subformat_t *)amp; val);

printf("subformat = 's' (s)/n",

snd_pcm_subformat_name((snd_pcm_subformat_t)val),

snd_pcm_subformat_description(

(snd_pcm_subformat_t)val));

snd_pcm_hw_params_get_channels(params, amp; val);

printf("canales = d/ n", val);

snd_pcm_hw_params_get_rate(params, amp; val, amp; dir);

printf("rate = d bps/n", val);

snd_pcm_hw_params_get_period_time(params,

amp; val, amp; dir);

printf ("periodo de tiempo = d us/n", val

);

snd_pcm_hw_params_get_period_size(params,

amp;frames, amp;dir);

printf("tamaño del período = d frames/n", (int)frames);

p>

snd_pcm_hw_params_get_buffer_time(params,

amp; val, amp; dir;

printf ("tiempo de búfer = d us/n", val);

snd_pcm_hw_params_get_buffer_size(params,

(snd_pcm_uframes_t *) amp; val);

printf("tamaño del búfer = d fotogramas/n", val) ;

snd

_pcm_hw_params_get_periods(params, amp;val, amp;dir);

printf("períodos por búfer = d fotogramas/n", val);

snd_pcm_hw_params_get_rate_numden(params,

amp;val,amp;val2);

printf("tasa exacta = d/d bps/n", val, val2);

val = snd_pcm_hw_params_get_sbits( params);

printf("bits significativos = d/n", val);

snd_pcm_hw_params_get_tick_time(params,

amp; val, amp; dir) ;

printf("tick tiempo = d us/n", val);

val = snd_pcm_hw_params_is_batch(params);

printf("es lote = d/n", val);

val = snd_pcm_hw_params_is_block_transfer(params);

printf("es transferencia en bloque = d/n", val);

val = snd_pcm_hw_params_is_double(params);

printf("es doble = d/n", val);

val = snd_pcm_hw_params_is_half_duplex(params);

printf("es semidúplex = d/n", val);

val = snd_pcm_hw_params_is_joint_duplex(params);

printf("es dúplex conjunto = d/n", val) ;

val = snd_pcm_hw_params_can_overrange(params);

printf("puede exceder el rango = d/n", val);

val = snd_pcm_hw_params_can_mmap_sample_solving(params);

printf("puede mmap = d/n", val);

val = snd_pcm_hw_params_can_pause(params);

printf("puede pausar = d/ n", val);

val = snd_pcm_hw_params_can_resume(params);

printf("puede reanudar = d/n", val);

val = snd_pcm_hw_params_can_sync_start(p

arams);

printf("puede sincronizar inicio = d/n", val);

snd_pcm_close(handle);

devuelve 0;

}