From: Alexandru Gagniuc Date: Sun, 23 Dec 2012 18:57:37 +0000 (-0600) Subject: alsa: Fix sample acquisition and send normalized values X-Git-Tag: dsupstream~389 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=729850c9e7dfa2c08c11a025d5a54b8e8e803f3c;p=libsigrok.git 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 --- diff --git a/hardware/alsa/api.c b/hardware/alsa/api.c index dc762e06..a8806fc2 100644 --- a/hardware/alsa/api.c +++ b/hardware/alsa/api.c @@ -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; diff --git a/hardware/alsa/protocol.c b/hardware/alsa/protocol.c index 1c1d8849..a501ec8f 100644 --- a/hardware/alsa/protocol.c +++ b/hardware/alsa/protocol.c @@ -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; } }