java android floating-point signal-processing

java - ¿Cómo convertir muestras de pcm en una matriz de bytes como números de punto flotante en el rango de-1.0 a 1.0 y viceversa?



android floating-point (2)

El algoritmo de remuestreo que utilizo espera una matriz flotante que contiene muestras de entrada en el rango de -1.0 a 1.0 . Los datos de audio son PCM de 16 bits con una velocidad de muestreo de 22 kHz .

Quiero bajar la muestra del audio de 22 kHz a 8 kHz, ¿cómo representar las muestras en la matriz de bytes como números de punto flotante > = -1 y <= 1 y volver a la matriz de bytes?


El PCM de 16 bits tiene un rango: 32768 a 32767. Entonces, multiplique cada una de sus muestras de PCM por (1.0f / 32768.0f) en una nueva matriz de flotadores, y pásela a su remuestreo.

Volver a flotar después de remuestrear, multiplicar por 32768.0, saturar (recortar cualquier cosa fuera del rango: 32768 a 32767), redondear (o interrumpir como Björn mencionó) y luego volver a abreviar.

Código de prueba que muestra la conversión hacia adelante y hacia atrás utilizando multiplicaciones sin errores de bits:

// PcmConvertTest.cpp : Defines the entry point for the console application. // #include <assert.h> #include <string.h> #include <stdint.h> #define SZ 65536 #define MAX(x,y) ((x)>(y)) ? (x) : (y) #define MIN(x,y) ((x)<(y)) ? (x) : (y) int main(int argc, char* argv[]) { int16_t *pIntBuf1 = new int16_t[SZ]; int16_t *pIntBuf2 = new int16_t[SZ]; float *pFloatBuf = new float[SZ]; // Create an initial short buffer for testing for( int i = 0; i < SZ; i++) { pIntBuf1[i] = (int16_t)(-32768 + i); } // Convert the buffer to floats. (before resampling) const float div = (1.0f/32768.0f); for( int i = 0; i < SZ; i++) { pFloatBuf[i] = div * (float)pIntBuf1[i]; } // Convert back to shorts const float mul = (32768.0f); for( int i = 0; i < SZ; i++) { int32_t tmp = (int32_t)(mul * pFloatBuf[i]); tmp = MAX( tmp, -32768 ); // CLIP < 32768 tmp = MIN( tmp, 32767 ); // CLIP > 32767 pIntBuf2[i] = tmp; } // Check that the conversion went int16_t to float and back to int for every PCM value without any errors. assert( 0 == memcmp( pIntBuf1, pIntBuf2, sizeof(int16_t) * SZ) ); delete pIntBuf1; delete pIntBuf2; delete pFloatBuf; return 0; }


Haces dos preguntas:

  1. ¿Cómo reducir la muestra de 22kHz a 8kHz?

  2. ¿Cómo convertir de flotante [-1,1] a 16 bits int y viceversa?

Tenga en cuenta que la pregunta se ha actualizado para indicar que el número 1 se atiende en otro lugar, pero dejaré esa parte de mi respuesta en caso de que ayude a alguien más.

1. ¿Cómo reducir la muestra de 22kHz a 8kHz?

Un comentarista insinuó que esto se puede resolver con la FFT. Esto es incorrecto (un paso en el remuestreo es el filtrado. Menciono por qué no usar la FFT para el filtrado aquí, en caso de que esté interesado: http://blog.bjornroche.com/2012/08/when-to-not-use-fft.html ).

Una muy buena manera de remuestrear una señal es con un filtro polifásico . Sin embargo, esto es bastante complejo, incluso para alguien con experiencia en el procesamiento de señales. Tienes varias otras opciones:

  • use una biblioteca que implemente remuestreo de alta calidad, como libsamplerate
  • hacer algo rápido y sucio

Parece que ya has ido con el primer enfoque, lo cual es genial.

Una solución rápida y sucia no suena tan bien, pero como está bajando a 8 kHz, supongo que la calidad del sonido no es su primera prioridad. Una opción rápida y sucia es:

  • Aplicar un filtro de paso bajo a la señal. Intenta deshacerte de la mayor cantidad de audio por encima de 4 kHz que puedas. Puedes usar los filtros que se describen here (aunque lo ideal es que quieras algo más empinado que esos filtros, al menos son mejores que nada).
  • seleccione cada muestra 2.75 de la señal original para producir la nueva señal remuestreada. Cuando necesite una muestra no entera, use interpolación lineal. Si necesita ayuda con la interpolación lineal, pruebe here .

Esta técnica debería ser más que suficiente para aplicaciones de voz. Sin embargo, no lo he probado, así que no lo sé con seguridad, así que recomiendo usar la biblioteca de otra persona.

Si realmente desea implementar su propia conversión de frecuencia de muestreo de alta calidad, como un filtro polifásico, debe investigarlo y luego hacer las preguntas que tenga en https://dsp.stackexchange.com/ , no aquí.

2. ¿Cómo convertir de flotante [-1,1] a int de 16 bits y viceversa?

Esto fue iniciado por c.fogelklou ya, pero déjame embellecer.

Para empezar, el rango de enteros de 16 bits es de -32768 a 32767 (generalmente el audio de 16 bits está firmado). Para convertir de int a flotar haces esto:

float f; int16 i = ...; f = ((float) i) / (float) 32768 if( f > 1 ) f = 1; if( f < -1 ) f = -1;

Por lo general, no necesita hacer ese "límite" adicional (de hecho, no es así si realmente está usando un entero de 16 bits), pero está ahí en caso de que tenga algunos enteros de> 16 bits por alguna razón.

Para volver a convertir, haces esto:

float f = ...; int16 i; f = f * 32768 ; if( f > 32767 ) f = 32767; if( f < -32768 ) f = -32768; i = (int16) f;

En este caso, generalmente es necesario vigilar los valores fuera de rango, especialmente los valores superiores a 32767. Es posible que se queje de que esto introduce cierta distorsión para f = 1. Este problema es muy debatido. Para una discusión (incompleta) de esto, vea esta publicación del blog .

Esto es más que "lo suficientemente bueno para el trabajo del gobierno". En otras palabras, funcionará bien, excepto en el caso de que esté preocupado por la máxima calidad de sonido. Ya que va a 8kHz, creo que hemos establecido que ese no es el caso, por lo que esta respuesta está bien.

Sin embargo, para completar, debo agregar esto: si está tratando de mantener las cosas absolutamente prístinas, tenga en cuenta que esta conversión introduce distorsión. ¿Por qué? Porque el error al convertir de float a int está correlacionado con la señal. Resulta que la correlación de ese error es terrible y que puedes escucharlo, aunque sea muy pequeño. (afortunadamente, es lo suficientemente pequeño para que no sea muy importante para cosas como el habla y la música de bajo rango dinámico) Para eliminar este error, debe usar algo llamado dither en la conversión de float a int. Nuevamente, si eso es algo que le interesa, investigue y haga preguntas relevantes y específicas en https://dsp.stackexchange.com/ , no aquí.

También podría interesarle las diapositivas de mi charla sobre los conceptos básicos de la programación de audio digital, que tiene una diapositiva sobre este tema, aunque básicamente dice lo mismo (tal vez incluso menos de lo que acabo de decir): http://blog.bjornroche.com/2011/11/slides-from-fundamentals-of-audio.html