This introduces an audio library, with dedicated helpers for both
generating signals and detecting peak frequencies in a signal.

This library paves the way for testing audio going through display
connectors, such as HDMI.

Signed-off-by: Paul Kocialkowski <paul.kocialkow...@linux.intel.com>
---
 .../intel-gpu-tools/intel-gpu-tools-docs.xml       |   1 +
 lib/Makefile.am                                    |   2 +
 lib/igt.h                                          |   1 +
 lib/igt_audio.c                                    | 326 +++++++++++++++++++++
 lib/igt_audio.h                                    |  47 +++
 5 files changed, 377 insertions(+)
 create mode 100644 lib/igt_audio.c
 create mode 100644 lib/igt_audio.h

diff --git a/docs/reference/intel-gpu-tools/intel-gpu-tools-docs.xml 
b/docs/reference/intel-gpu-tools/intel-gpu-tools-docs.xml
index f88afd2a..c77159cf 100644
--- a/docs/reference/intel-gpu-tools/intel-gpu-tools-docs.xml
+++ b/docs/reference/intel-gpu-tools/intel-gpu-tools-docs.xml
@@ -16,6 +16,7 @@
   <chapter>
     <title>API Reference</title>
     <xi:include href="xml/drmtest.xml"/>
+    <xi:include href="xml/igt_audio.xml"/>
     <xi:include href="xml/igt_aux.xml"/>
     <xi:include href="xml/igt_chamelium.xml"/>
     <xi:include href="xml/igt_core.xml"/>
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 9c932d6f..5ea08314 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -33,6 +33,8 @@ if HAVE_GSL
 lib_source_list +=             \
        igt_frame.c             \
        igt_frame.h             \
+       igt_audio.c             \
+       igt_audio.h             \
        $(NULL)
 endif
 
diff --git a/lib/igt.h b/lib/igt.h
index d16a4991..a75d2db7 100644
--- a/lib/igt.h
+++ b/lib/igt.h
@@ -35,6 +35,7 @@
 #include "igt_dummyload.h"
 #include "igt_fb.h"
 #include "igt_frame.h"
+#include "igt_audio.h"
 #include "igt_gt.h"
 #include "igt_kms.h"
 #include "igt_pm.h"
diff --git a/lib/igt_audio.c b/lib/igt_audio.c
new file mode 100644
index 00000000..527a4930
--- /dev/null
+++ b/lib/igt_audio.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright © 2017 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *  Paul Kocialkowski <paul.kocialkow...@linux.intel.com>
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <gsl/gsl_fft_real.h>
+
+#include "igt.h"
+
+#define FREQS_MAX      8
+
+/**
+ * SECTION:igt_audio
+ * @short_description: Library for audio-related tests
+ * @title: Audio
+ * @include: igt_audio.h
+ *
+ * This library contains helpers for audio-related tests. More specifically,
+ * it allows generating additions of sine signals as well as detecting them.
+ */
+
+struct audio_signal_freq {
+       int freq;
+
+       short *period;
+       int frames;
+       int offset;
+};
+
+struct audio_signal {
+       int channels;
+       int sampling_rate;
+
+       struct audio_signal_freq freqs[FREQS_MAX];
+       int freqs_count;
+};
+
+/**
+ * audio_signal_init:
+ * @channels: The number of channels to use for the signal 
+ * @sampling_rate: The sampling rate to use for the signal
+ *
+ * Allocate and initialize an audio signal structure with the given parameters.
+ *
+ * Returns: A newly-allocated audio signal structure
+ */
+struct audio_signal *audio_signal_init(int channels, int sampling_rate)
+{
+       struct audio_signal *signal;
+
+       signal = malloc(sizeof(struct audio_signal));
+       memset(signal, 0, sizeof(struct audio_signal));
+
+       signal->sampling_rate = sampling_rate;
+       signal->channels = channels;
+
+       return signal;
+}
+
+/**
+ * audio_signal_add_frequency:
+ * @signal: The target signal structure
+ * @frequency: The frequency to add to the signal
+ *
+ * Add a frequency to the signal.
+ *
+ * Returns: An integer equal to zero for success and negative for failure
+ */
+int audio_signal_add_frequency(struct audio_signal *signal, int frequency)
+{
+       int index = signal->freqs_count;
+
+       if (index == FREQS_MAX)
+               return -1;
+
+       /* Stay within the Nyquist–Shannon sampling theorem. */
+       if (frequency > signal->sampling_rate / 2)
+               return -1;
+
+       /* Clip the frequency to an integer multiple of the sampling rate.
+        * This to be able to store a full period of it and use that for
+        * signal generation, instead of recurrent calls to sin().
+        */
+       frequency = signal->sampling_rate / (signal->sampling_rate / frequency);
+
+       igt_debug("Adding test frequency %d\n", frequency);
+
+       signal->freqs[index].freq = frequency;
+       signal->freqs[index].frames = 0;
+       signal->freqs[index].offset = 0;
+       signal->freqs_count++;
+
+       return 0;
+}
+
+/**
+ * audio_signal_synthesize:
+ * @signal: The target signal structure
+ *
+ * Synthesize the data tables for the audio signal, that can later be used
+ * to fill audio buffers. The resources allocated by this function must be
+ * freed with a call to audio_signal_clean when the signal is no longer used.
+ */
+void audio_signal_synthesize(struct audio_signal *signal)
+{
+       short *period;
+       double value;
+       int frames;
+       int freq;
+       int i, j;
+
+       if (signal->freqs_count == 0)
+               return;
+
+       for (i = 0; i < signal->freqs_count; i++) {
+               freq = signal->freqs[i].freq;
+               frames = signal->sampling_rate / freq;
+
+               period = calloc(1, frames * sizeof(short));
+
+               for (j = 0; j < frames; j++) {
+                       value = 2.0 * M_PI * freq / signal->sampling_rate * j;
+                       value = sin(value) * SHRT_MAX / signal->freqs_count;
+
+                       period[j] = (short) value; 
+               }
+
+               signal->freqs[i].period = period;
+               signal->freqs[i].frames = frames;
+       }
+}
+
+/**
+ * audio_signal_synthesize:
+ * @signal: The target signal structure
+ *
+ * Free the resources allocated by audio_signal_synthesize and remove
+ * the previously-added frequencies.
+ */
+void audio_signal_clean(struct audio_signal *signal)
+{
+       int i;
+
+       for (i = 0; i < signal->freqs_count; i++) {
+               if (signal->freqs[i].period)
+                       free(signal->freqs[i].period);
+
+               memset(&signal->freqs[i], 0, sizeof(struct audio_signal_freq));
+       }
+
+       signal->freqs_count = 0;
+}
+
+/**
+ * audio_signal_fill:
+ * @signal: The target signal structure
+ * @buffer: The target buffer to fill
+ * @frames: The number of frames to fill
+ *
+ * Fill the requested number of frames to the target buffer with the audio
+ * signal data (in interleaved S16_LE format), at the requested sampling rate
+ * and number of channels.
+ */
+void audio_signal_fill(struct audio_signal *signal, short *buffer, int frames)
+{
+       short *destination;
+       short *source;
+       int total;
+       int freq_frames;
+       int freq_offset;
+       int count;
+       int i, j, k;
+
+       memset(buffer, 0, sizeof(short) * signal->channels * frames);
+
+       for (i = 0; i < signal->freqs_count; i++) {
+               total = 0;
+
+               while (total < frames) {
+                       freq_frames = signal->freqs[i].frames;
+                       freq_offset = signal->freqs[i].offset;
+
+                       source = signal->freqs[i].period + freq_offset;
+                       destination = buffer + total * signal->channels;
+
+                       count = freq_frames - freq_offset;
+                       if (count > (frames - total))
+                               count = frames - total;
+
+                       freq_offset += count;
+                       freq_offset %= freq_frames;
+
+                       signal->freqs[i].offset = freq_offset;
+
+                       for (j = 0; j < count; j++) {
+                               for (k = 0; k < signal->channels; k++) {
+                                       destination[j * signal->channels + k] 
+= source[j];
+                               }
+                       }
+
+                       total += count;
+               }
+       }
+}
+
+/**
+ * audio_signal_detect:
+ * @signal: The target signal structure
+ * @channels: The input data's number of channels
+ * @sampling_rate: The input data's sampling rate
+ * @buffer: The input data's buffer
+ * @frames: The input data's number of frames
+ *
+ * Detect that the frequencies specified in @signal, and only those, are
+ * present in the input data. The input data's format is required to be S16_LE.
+ *
+ * Returns: A boolean indicating whether the detection was successful
+ */
+bool audio_signal_detect(struct audio_signal *signal, int channels,
+                        int sampling_rate, short *buffer, int frames)
+{
+       double data[frames];
+       int amplitude[frames / 2];
+       bool detected[signal->freqs_count];
+       int threshold;
+       bool above;
+       int error;
+       int freq;
+       int max;
+       int c, i, j;
+
+       /* Allowed error in Hz due to FFT step. */
+       error = sampling_rate / frames;
+
+       for (c = 0; c < channels; c++) {
+               for (i = 0; i < frames; i++)
+                       data[i] = (double) buffer[i * channels + c];
+
+               gsl_fft_real_radix2_transform(data, 1, frames);
+
+               max = 0;
+
+               for (i = 0; i < frames / 2; i++) {
+                       amplitude[i] = sqrt(data[i] * data[i] +
+                                                 data[frames - i] *
+                                                 data[frames - i]);
+                       if (amplitude[i] > max)
+                               max = amplitude[i];
+               }
+
+               for (i = 0; i < signal->freqs_count; i++)
+                       detected[i] = false;
+
+               threshold = max / 2;
+               above = false;
+               max = 0;
+
+               for (i = 0; i < frames / 2; i++) {
+                       if (amplitude[i] > threshold)
+                               above = true;
+
+                       if (above) {
+                               if (amplitude[i] < threshold) {
+                                       above = false;
+                                       max = 0;
+
+                                       for (j = 0; j < signal->freqs_count; 
j++) {
+                                               if (signal->freqs[j].freq >
+                                                   freq - error &&
+                                                   signal->freqs[j].freq <
+                                                   freq + error) {
+                                                       detected[j] = true;
+                                                       break;
+                                               }
+                                       }
+
+                                       /* Detected frequency was not 
generated. */
+                                       if (j == signal->freqs_count) {
+                                               igt_debug("Detected additional 
frequency: %d\n",
+                                                         freq);
+                                               return false;
+                                       }
+                               }
+
+                               if (amplitude[i] > max) {
+                                       max = amplitude[i];
+                                       freq = sampling_rate * i / frames;
+                               }
+                       }
+               }
+
+               for (i = 0; i < signal->freqs_count; i++) {
+                       if (!detected[i]) {
+                               igt_debug("Missing frequency: %d\n",
+                                         signal->freqs[i].freq);
+                               return false;
+                       }
+               }
+       }
+
+       return true;
+}
diff --git a/lib/igt_audio.h b/lib/igt_audio.h
new file mode 100644
index 00000000..507e7ab9
--- /dev/null
+++ b/lib/igt_audio.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2017 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *  Paul Kocialkowski <paul.kocialkow...@linux.intel.com>
+ */
+
+#ifndef IGT_AUDIO_H
+#define IGT_AUDIO_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "igt.h"
+#include <stdbool.h>
+
+struct audio_signal;
+
+struct audio_signal *audio_signal_init(int channels, int sampling_rate);
+int audio_signal_add_frequency(struct audio_signal *signal, int frequency);
+void audio_signal_synthesize(struct audio_signal *signal);
+void audio_signal_clean(struct audio_signal *signal);
+void audio_signal_fill(struct audio_signal *signal, short *buffer, int frames);
+bool audio_signal_detect(struct audio_signal *signal, int channels,
+                        int sampling_rate, short *buffer, int frames);
+
+#endif
-- 
2.14.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to