blob: 7d770a2e37f3af473dafd2d3bae08e72d605eb15 [file] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <alsa/asoundlib.h>
#include <math.h>
#include <pthread.h>
#include <stdint.h>
#include "args.h"
double g_phase = M_PI / 2;
pthread_mutex_t g_latency_test_mutex;
pthread_cond_t g_sine_start;
int g_terminate_playback;
int g_terminate_capture;
int g_sine_started;
int g_capture_count;
int g_playback_count;
void generate_sine(const snd_pcm_channel_area_t* areas,
snd_pcm_uframes_t offset,
int count,
double* _phase) {
static double max_phase = 2. * M_PI;
double phase = *_phase;
double step = max_phase * 1000 / (double)g_rate;
unsigned char* samples[g_channels];
int steps[g_channels];
unsigned int chn;
int format_bits = snd_pcm_format_width(g_format);
unsigned int maxval = (1 << (format_bits - 1)) - 1;
int bps = format_bits / 8; /* bytes per sample */
int phys_bps = snd_pcm_format_physical_width(g_format) / 8;
int big_endian = snd_pcm_format_big_endian(g_format) == 1;
int to_unsigned = snd_pcm_format_unsigned(g_format) == 1;
int is_float = (g_format == SND_PCM_FORMAT_FLOAT_LE ||
g_format == SND_PCM_FORMAT_FLOAT_BE);
/* Verify and prepare the contents of areas */
for (chn = 0; chn < g_channels; chn++) {
if ((areas[chn].first % 8) != 0) {
fprintf(stderr, "areas[%i].first == %i, aborting...\n", chn,
areas[chn].first);
exit(EXIT_FAILURE);
}
if ((areas[chn].step % 16) != 0) {
fprintf(stderr, "areas[%i].step == %i, aborting...\n", chn,
areas[chn].step);
exit(EXIT_FAILURE);
}
steps[chn] = areas[chn].step / 8;
samples[chn] = ((unsigned char*)areas[chn].addr) + (areas[chn].first / 8) +
offset * steps[chn];
}
/* Fill the channel areas */
while (count-- > 0) {
union {
float f;
int i;
} fval;
int res, i;
if (is_float) {
fval.f = sin(phase) * maxval;
res = fval.i;
} else
res = sin(phase) * maxval;
if (to_unsigned)
res ^= 1U << (format_bits - 1);
for (chn = 0; chn < g_channels; chn++) {
/* Generate data based on endian format */
if (big_endian) {
for (i = 0; i < bps; i++)
*(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
} else {
for (i = 0; i < bps; i++)
*(samples[chn] + i) = (res >> i * 8) & 0xff;
}
samples[chn] += steps[chn];
}
phase += step;
if (phase >= max_phase)
phase -= max_phase;
}
*_phase = phase;
}
void read_wav_file(const char* filename, short** pcm_data) {
const int header_size = 44;
FILE* file = fopen(filename, "rb");
if (!file) {
fprintf(stderr, "Failed to open file %s\n", filename);
exit(EXIT_FAILURE);
}
// Get the file size
fseek(file, 0, SEEK_END);
long file_size = ftell(file) - header_size;
fseek(file, header_size, SEEK_SET);
// Allocate memory to store the PCM data
*pcm_data = (short*)malloc(file_size);
if (!pcm_data) {
fclose(file);
fprintf(stderr, "Failed to allocate memory for PCM data\n");
exit(EXIT_FAILURE);
}
// Read the PCM data from the file
size_t num_read =
fread(*pcm_data, sizeof(short), file_size / sizeof(short), file);
if (num_read <= 0) {
fclose(file);
fprintf(stderr, "Failed to read PCM data from file\n");
exit(EXIT_FAILURE);
}
fclose(file);
}
/* Looks for the first sample in buffer whose absolute value exceeds
* noise_threshold. Returns the index of found sample in frames, -1
* if not found. */
int check_for_noise(short* buf, unsigned len, unsigned channels) {
unsigned int i;
for (i = 0; i < len * channels; i++)
if (abs(buf[i]) > g_noise_threshold)
return i / channels;
return -1;
}
unsigned long subtract_timevals(const struct timeval* end,
const struct timeval* beg) {
struct timeval diff;
/* If end is before geb, return 0. */
if ((end->tv_sec < beg->tv_sec) ||
((end->tv_sec == beg->tv_sec) && (end->tv_usec <= beg->tv_usec)))
diff.tv_sec = diff.tv_usec = 0;
else {
if (end->tv_usec < beg->tv_usec) {
diff.tv_sec = end->tv_sec - beg->tv_sec - 1;
diff.tv_usec = end->tv_usec + 1000000L - beg->tv_usec;
} else {
diff.tv_sec = end->tv_sec - beg->tv_sec;
diff.tv_usec = end->tv_usec - beg->tv_usec;
}
}
return diff.tv_sec * 1000000 + diff.tv_usec;
}