New audio formats: 32 bit float (little and big endian).
authorAndre Noll <maan@tuebingen.mpg.de>
Sat, 8 Sep 2018 10:11:33 +0000 (12:11 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Sat, 12 Oct 2019 15:27:23 +0000 (17:27 +0200)
At least the little endian version seems to be popular these days. It
is only supported by ALSA, however. To check whether a given wave
file employs one of the two SF_FLOAT formats (aka IEEE float) we have
to consult the format code, a 16 bit integer stored at offset 20 of
the wave header which describes the waveform data according to the
table below.

0x0001 PCM
0x0003 IEEE float
0x0006 8-bit ITU-T G.711 A-law
0x0007 8-bit ITU-T G.711 MU-law
0xFFFE Determined by SubFormat

Additional checks are added to check_wav_post_select() which make
sure that the format code is either 1 or 3, and that the number is
compatible with the bits per sample value.

alsa_write.c
ao_write.c
check_wav.c
m4/lls/include/sample-format.m4
para.h

index 363e393..14c3555 100644 (file)
@@ -61,6 +61,8 @@ static snd_pcm_format_t get_alsa_pcm_format(enum sample_format sf)
        case SF_S16_BE: return SND_PCM_FORMAT_S16_BE;
        case SF_U16_LE: return SND_PCM_FORMAT_U16_LE;
        case SF_U16_BE: return SND_PCM_FORMAT_U16_BE;
+       case SF_FLOAT_LE: return SND_PCM_FORMAT_FLOAT_LE;
+       case SF_FLOAT_BE: return SND_PCM_FORMAT_FLOAT_BE;
        default: return SND_PCM_FORMAT_S16_LE;
        }
 }
index 447dea8..b4fa454 100644 (file)
@@ -87,6 +87,8 @@ static int aow_set_sample_format(unsigned sample_rate, unsigned channels,
                case SF_U8:
                case SF_U16_LE:
                case SF_U16_BE:
+               case SF_FLOAT_LE:
+               case SF_FLOAT_BE:
                        return -E_BAD_SAMPLE_FORMAT;
                case SF_S8:
                        /* no need to set byte_format */
index 89ebdac..8c2ecee 100644 (file)
@@ -127,7 +127,7 @@ int check_wav_post_select(struct check_wav_context *cwc)
        unsigned char *a;
        size_t sz;
        int ret;
-       uint16_t bps; /* bits per sample */
+       uint16_t format_code, bps; /* bits per sample */
        const char *sample_formats[] = {SAMPLE_FORMATS};
 
        if (!btrn)
@@ -157,13 +157,24 @@ int check_wav_post_select(struct check_wav_context *cwc)
        cwc->state = CWS_HAVE_HEADER;
        /* Only set those values which have not already been set. */
        cwc->channels = a[22];
+       format_code = read_u16(a + 20);
+       if (format_code != 1 && format_code != 3) {
+               cwc->state = CWS_NO_HEADER;
+               goto out;
+       }
        cwc->sample_rate = read_u32(a + 24);
        bps = read_u16(a + 34);
-       if (bps != 8 && bps != 16) {
+       if (bps != 8 && bps != 16 && bps != 32) {
                PARA_WARNING_LOG("%u bps not supported, assuming 16\n",
                        bps);
                bps = 16;
        }
+       if ((bps < 32 && format_code != 1) || (bps == 32 && format_code != 3)) {
+               PARA_WARNING_LOG("invalid bps/format_code (%u/%u)\n",
+                       bps, format_code);
+               cwc->state = CWS_NO_HEADER;
+               goto out;
+       }
        /*
         * 8-bit samples are stored as unsigned bytes, ranging from 0
         * to 255.  16-bit samples are stored as 2's-complement signed
@@ -171,9 +182,12 @@ int check_wav_post_select(struct check_wav_context *cwc)
         */
        if (bps == 8)
                cwc->sample_format = SF_U8;
-       else
+       else if (bps == 16)
                cwc->sample_format = (a[3] == 'F')?
                        SF_S16_LE : SF_S16_BE;
+       else /* 32 bit */
+               cwc->sample_format = (a[3] == 'F')?
+                       SF_FLOAT_LE : SF_FLOAT_BE;
        PARA_NOTICE_LOG("%uHz, %s, %s\n", cwc->sample_rate,
                cwc->channels == 1? "mono" : "stereo",
                sample_formats[cwc->sample_format]);
index 0daad19..030ab7d 100644 (file)
@@ -11,7 +11,9 @@
                SAMPLE_FORMAT_S16_LE = "S16_LE",
                SAMPLE_FORMAT_S16_BE = "S16_BE",
                SAMPLE_FORMAT_U16_LE = "U16_LE",
-               SAMPLE_FORMAT_U16_BE = "U16_BE"
+               SAMPLE_FORMAT_U16_BE = "U16_BE",
+               SAMPLE_FORMAT_FLOAT_LE = "FLOAT_LE",
+               SAMPLE_FORMAT_FLOAT_BE = "FLOAT_BE"
        }
        default_val = S16_LE
        [help]
diff --git a/para.h b/para.h
index b406818..c745006 100644 (file)
--- a/para.h
+++ b/para.h
@@ -202,6 +202,8 @@ _static_inline_ bool iov_valid(const struct iovec *iov)
        SAMPLE_FORMAT(SF_S16_BE, "16 bit signed, big endian"), \
        SAMPLE_FORMAT(SF_U16_LE, "16 bit unsigned, little endian"), \
        SAMPLE_FORMAT(SF_U16_BE, "16 bit unsigned, big endian"), \
+       SAMPLE_FORMAT(SF_FLOAT_LE, "32 bit float, little endian"), \
+       SAMPLE_FORMAT(SF_FLOAT_BE, "32 bit float, big endian"), \
 
 /** \cond sample_format */
 #define SAMPLE_FORMAT(a, b) a