From 39cc9bf89c293c92d55aad803c08816ad922585d Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Mon, 21 Aug 2017 18:27:18 +0300 Subject: lib: Add audio library with dedicated helpers 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 Reviewed-by: Lyude Paul --- lib/igt_audio.c | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 lib/igt_audio.c (limited to 'lib/igt_audio.c') diff --git a/lib/igt_audio.c b/lib/igt_audio.c new file mode 100644 index 00000000..6b35529a --- /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 + */ + +#include "config.h" + +#include +#include + +#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; +} -- cgit v1.2.3