alsa: Fix sample acquisition and send normalized values

The alsa driver requested signed 16-bit integers from ALSA, but casted them to
to an unsigned 16bit before finally casting them to a float. The end result was
that half of the wave would be clipped off.
We also requested data in little endian format. ALSA can be instructed to send
data with the correct endianness for the platform, without needing to worry
about what that is.

This patch attempts three points, which, together, fix the acquisition:
1) Request data from ALSA without specifying endianness; ALSA will handle the
endianness.
2) Simplify the int16_t to float loop by using straightforward indexes.
3) Normalize every value before sending it on the session bus.

NOTE: If testing with PulseView, it will appear as if sigrok is sending all
zeroes. sigrok is sending correct data, but since the data is normalized,
PulseView will incorrectly plot it as a straight line.

Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
This commit is contained in:
Alexandru Gagniuc 2012-12-23 12:57:37 -06:00 committed by Uwe Hermann
parent 6944b2d02f
commit 729850c9e7
2 changed files with 23 additions and 8 deletions

View File

@ -209,9 +209,11 @@ static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi,
struct sr_datafeed_meta_analog meta;
struct dev_context *devc;
int count, ret;
char *endianess;
devc = sdi->priv;
devc->cb_data = cb_data;
devc->num_samples = 0;
sr_dbg("Setting audio access type to RW/interleaved.");
ret = snd_pcm_hw_params_set_access(devc->capture_handle,
@ -222,9 +224,14 @@ static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi,
}
/* FIXME: Hardcoded for 16bits. */
sr_dbg("Setting audio sample format to signed 16bit (little endian).");
if (SND_PCM_FORMAT_S16 == SND_PCM_FORMAT_S16_LE)
endianess = "lilltle endian";
else
endianess = "big endian";
sr_dbg("Setting audio sample format to signed 16bit (%s).", endianess);
ret = snd_pcm_hw_params_set_format(devc->capture_handle,
devc->hw_params, SND_PCM_FORMAT_S16_LE);
devc->hw_params,
SND_PCM_FORMAT_S16);
if (ret < 0) {
sr_err("Can't set audio sample format: %s.", snd_strerror(ret));
return SR_ERR;

View File

@ -230,9 +230,10 @@ SR_PRIV int alsa_receive_data(int fd, int revents, void *cb_data)
struct dev_context *devc;
struct sr_datafeed_packet packet;
struct sr_datafeed_analog analog;
char inbuf[4096];
int16_t inbuf[4096];
int i, x, count, offset, samples_to_get;
uint16_t tmp16;
int16_t tmp16;
const float s16norm = 1 / (float)(1<<15);
(void)fd;
(void)revents;
@ -262,11 +263,18 @@ SR_PRIV int alsa_receive_data(int fd, int revents, void *cb_data)
}
offset = 0;
for (i = 0; i < count; i++) {
/*
* It's impossible to know what voltage levels the soundcard handles.
* Some handle 0 dBV rms, some 0dBV peak-to-peak, +4dbmW (600 ohm), etc
* Each of these corresponds to a different voltage, and there is no
* mechanism to determine this voltage. The best solution is to send all
* audio data as a normalized float, and let the frontend or user worry
* about the calibration.
*/
for (i = 0; i < count; i += devc->num_probes) {
for (x = 0; x < devc->num_probes; x++) {
tmp16 = *(uint16_t *)(inbuf + (i * 4) + (x * 2));
analog.data[offset++] = (float)tmp16;
tmp16 = inbuf[i+x];
analog.data[offset++] = tmp16 * s16norm;
}
}