Estoy intentando convertir muestras de PCM ALSA de 16 bits en muestras PCM de 8 bits sin firmar para la transmisión inalámbrica en Linux. La máquina receptora está reproduciendo los datos transmitidos con éxito y la voz grabada está ahí y es reconocible, pero la calidad es terrible y ruidosa. Probé el mezclador ALSA en ambos extremos para sintonizar la transmisión, pero no parece mejorar mucho con eso. Creo que hay algo mal con mi conversión de las muestras a PCM de 8 bits, pero es solo un cambio simple, por lo que no estoy seguro de cuál podría ser el error. ¿Alguien tiene alguna sugerencia o ve algún problema con mi código de conversión? Gracias.

Código de conversión:

            // This byte array needs to be the packet size we wish to send
            QByteArray prepareToSend;
            prepareToSend.clear();

            // Keep reading from ALSA until we fill one full frame
            int frames = 1;
            while ( prepareToSend.size() < TARGET_TX_BUFFER_SIZE ) {

                // Create a ByteArray
                QByteArray readBytes;
                readBytes.resize(size);

                // Read with ALSA
                short sample[1]; // Data is signed 16-bit
                int rc = snd_pcm_readi(m_PlaybackHandle, sample, frames);
                if (rc == -EPIPE) {
                    /* EPIPE means overrun */
                    fprintf(stderr, "Overrun occurred\n");
                    snd_pcm_prepare(m_PlaybackHandle);
                } else if (rc < 0) {
                    fprintf(stderr,
                            "Error from read: %s\n",
                            snd_strerror(rc));
                } else if (rc != (int)frames) {
                    fprintf(stderr, "Short read, read %d frames\n", rc);
                }
                else {
                    // Copy bytes to the prepare to send buffer
                    //qDebug() << "Bytes for sample buffer: " << sizeof(sample);
                    prepareToSend.append((qint16)(sample[0]) >> 8); // signed 16-bit becomes u8
                }

            }

Configuración ALSA:

        // Setup parameters
        int size;
        snd_pcm_t *m_PlaybackHandle;
        snd_pcm_hw_params_t *m_HwParams;
        char *buffer;

        qDebug() << "Desire to Transmit Data - Setting up ALSA Now....";

        // Error handling
        int err;

        // Device to Write to
        const char *snd_device_in = "hw:1,0";

        if ((err = snd_pcm_open (&m_PlaybackHandle, snd_device_in, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
            fprintf (stderr, "Cannot open audio device %s (%s)\n",
                     snd_device_in,
                     snd_strerror (err));
            exit (1);
        }

        /* Allocate a hardware parameters object. */
        snd_pcm_hw_params_alloca(&m_HwParams);

        if ((err = snd_pcm_hw_params_malloc (&m_HwParams)) < 0) {
            fprintf (stderr, "Cannot allocate hardware parameter structure (%s)\n",
                     snd_strerror (err));
            exit (1);
        }

        if ((err = snd_pcm_hw_params_any (m_PlaybackHandle, m_HwParams)) < 0) {
            fprintf (stderr, "Cannot initialize hardware parameter structure (%s)\n",
                     snd_strerror (err));
            exit (1);
        }

        if ((err = snd_pcm_hw_params_set_access (m_PlaybackHandle, m_HwParams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
            fprintf (stderr, "Cannot set access type (%s)\n",
                     snd_strerror (err));
            exit (1);
        }

        if ((err = snd_pcm_hw_params_set_format(m_PlaybackHandle, m_HwParams, SND_PCM_FORMAT_S16)) < 0) { // Has to be 16 bit
            fprintf (stderr, "Cannot set sample format (%s)\n",
                     snd_strerror (err));
            exit (1);

        }

        uint sample_rate = 8000;
        if ((err = snd_pcm_hw_params_set_rate (m_PlaybackHandle, m_HwParams, sample_rate, 0)) < 0) { // 8 KHz
            fprintf (stderr, "Cannot set sample rate (%s)\n",
                     snd_strerror (err));
            exit (1);
        }

        if ((err = snd_pcm_hw_params_set_channels (m_PlaybackHandle, m_HwParams, 1)) < 0) { // 1 Channel Mono
            fprintf (stderr, "Cannot set channel count (%s)\n",
                     snd_strerror (err));
            exit (1);
        }

        /*
        Frames: samples x channels (i.e: stereo frames are composed of two samples, mono frames are composed of 1 sample,...)
        Period: Number of samples tranferred after which the device acknowledges the transfer to the apllication (usually via an interrupt).
        */

        /* Submit params to device */
        if ((err = snd_pcm_hw_params(m_PlaybackHandle, m_HwParams)) < 0) {
            fprintf (stderr, "Cannot set parameters (%s)\n",
                     snd_strerror (err));
            exit (1);
        }

        /* Free the Struct */
        snd_pcm_hw_params_free(m_HwParams);

        // Flush handle prepare for record
        snd_pcm_drop(m_PlaybackHandle);

        if ((err = snd_pcm_prepare (m_PlaybackHandle)) < 0) {
            fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
                     snd_strerror (err));
            exit (1);
        }

        qDebug() << "Done Setting up ALSA....";

        // Prepare the device
        if ((err = snd_pcm_prepare (m_PlaybackHandle)) < 0) {
            fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
                     snd_strerror (err));
            exit (1);
        }
1
PhilBot 16 feb. 2015 a las 05:04

2 respuestas

La mejor respuesta

(qint16)(sample[0]) >> 8 convertirá PCM lineal de 16 bits con signo en PCM lineal de 8 bits con signo. Si desea 8 bits lineales sin firmar, entonces sería ((quint16)sample[0] ^ 0x8000) >> 8.

Aunque el PCM de 16 bits casi siempre se encuentra en una escala lineal, el PCM de 8 bits se encuentra más comúnmente en una escala logarítmica (ley µ o ley A), y generalmente se usa una tabla de búsqueda para la conversión. Si realmente desea 8 bits lineales, primero puede ajustar la ganancia para que el pico esté en 0 dBFS y usar la compresión de audio para reducir el rango dinámico para que quepa en 8 bits.

1
mark4o 16 feb. 2015 a las 02:41

Si usa plughw:1,0 en lugar de hw:1,0, puede decirle al dispositivo que desea SND_PCM_FORMAT_U8 y las muestras se convertirán automáticamente. (Esto también funciona para µ-Law y A-Law).

1
CL. 16 feb. 2015 a las 07:52