| /* |
| * 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.kocialkowski@linux.intel.com> |
| */ |
| |
| #include "config.h" |
| |
| #include <math.h> |
| #include <gsl/gsl_fft_real.h> |
| |
| #include "igt_audio.h" |
| #include "igt_core.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 = 0; |
| 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] = hypot(data[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; |
| } |