Преобразование 16-разрядных данных ALSA PCM в U8 в Linux

Я пытаюсь преобразовать 16-разрядные образцы PCS ALSA в 8-разрядные образцы PCM без знака для беспроводной передачи в Linux. Принимающая машина успешно воспроизводит переданные данные, и записанный голос присутствует и его можно узнать, но качество ужасное и шумное. Я пробовал микшер ALSA на обоих концах, чтобы настроить поток, но, похоже, с этим не намного лучше. Я полагаю, что что-то не так с моим преобразованием семплов в 8-битную PCM, но это просто простой переход, поэтому я не уверен, в чем может быть ошибка. У кого-нибудь есть предложения или что-то не так с моим кодом конверсии? Благодарю.

Код конверсии:

            // 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
}

}

Конфигурация 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);
}

0

Решение

(qint16)(sample[0]) >> 8 преобразует линейную 16-битную PCM со знаком в линейную 8-битную PCM со знаком. Если вы хотите беззнаковый линейный 8-битный, то это будет ((quint16)sample[0] ^ 0x8000) >> 8,

Хотя 16-разрядный PCM почти всегда находится в линейной шкале, 8-разрядный PCM чаще используется в логарифмическом масштабе (µ-закон или A-закон), и для преобразования обычно используется справочная таблица. Если вы действительно хотите использовать линейный 8-битный режим, тогда вы можете сначала отрегулировать усиление таким образом, чтобы пик составлял 0 дБFS, и использовать сжатие звука для уменьшения динамического диапазона, чтобы он умещался в 8 бит.

1

Другие решения

Если вы используете plughw:1,0 вместо hw:1,0, вы можете просто сказать устройство, которое вы хотите SND_PCM_FORMAT_U8и образцы будут преобразованы автоматически.
(Это работает также для µ-Law и A-Law.)

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector