From cb3340621f73bb2d0b939461d8e22835d8c4b845 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Tue, 31 Mar 2026 00:47:33 +0300 Subject: [PATCH] utils: sofprobeclient: embed SOF probe parser for real-time demux Integrate the sof-probes demux engine directly into sofprobeclient so captured compressed data is parsed in real time instead of being written to a raw file. Log output from non-audio probe points is printed to stdout (equivalent to sof-probes -l). Audio probe data is extracted into buffer_.wav files in the current directory. The file output parameter is removed; sofprobeclient no longer acts as a generic recorder. New files copied from SOF v2.9 sources tools/probes/ with include paths adjusted for standalone tinycompress build: probes_demux.c / probes_demux.h - probe stream parser probes_wave.h - WAV header definitions probe_dma_frame.h - DMA frame packet format Closes: https://github.com/alsa-project/tinycompress/pull/35 Signed-off-by: Jyri Sarha Signed-off-by: Jaroslav Kysela --- src/utils/Makefile.am | 2 +- src/utils/probe_dma_frame.h | 92 ++++++++++ src/utils/probes_demux.c | 338 ++++++++++++++++++++++++++++++++++++ src/utils/probes_demux.h | 29 ++++ src/utils/probes_wave.h | 45 +++++ src/utils/sofprobeclient.c | 262 +++++++--------------------- 6 files changed, 565 insertions(+), 203 deletions(-) create mode 100644 src/utils/probe_dma_frame.h create mode 100644 src/utils/probes_demux.c create mode 100644 src/utils/probes_demux.h create mode 100644 src/utils/probes_wave.h diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index 94647d7..9c83362 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -2,7 +2,7 @@ bin_PROGRAMS = cplay crecord sofprobeclient cplay_SOURCES = cplay.c wave.c crecord_SOURCES = crecord.c wave.c -sofprobeclient_SOURCES = sofprobeclient.c wave.c +sofprobeclient_SOURCES = sofprobeclient.c probes_demux.c cplay_CFLAGS = -I$(top_srcdir)/include crecord_CFLAGS = -I$(top_srcdir)/include diff --git a/src/utils/probe_dma_frame.h b/src/utils/probe_dma_frame.h new file mode 100644 index 0000000..1b617f2 --- /dev/null +++ b/src/utils/probe_dma_frame.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2022 Intel Corporation. All rights reserved. + * + * Local copy of ipc/probe_dma_frame.h with MASK defined inline + * so it can be built without SOF RTOS headers. + */ + +#ifndef __PROBE_DMA_FRAME_H__ +#define __PROBE_DMA_FRAME_H__ + +#include + +#ifndef MASK +#define MASK(b_hi, b_lo) \ + (((1ULL << ((b_hi) - (b_lo) + 1ULL)) - 1ULL) << (b_lo)) +#endif + +/** + * Header for data packets sent via compressed PCM from extraction probes + */ +struct probe_data_packet { + uint32_t sync_word; /**< PROBE_EXTRACT_SYNC_WORD */ + uint32_t buffer_id; /**< Buffer ID from which data was extracted */ + uint32_t format; /**< Encoded data format */ + uint32_t timestamp_low; /**< Low 32 bits of timestamp in us */ + uint32_t timestamp_high; /**< High 32 bits of timestamp in us */ + uint32_t data_size_bytes; /**< Size of following audio data */ + uint8_t data[]; /**< Audio data extracted from buffer */ +} __attribute__((packed, aligned(4))); + +#define PROBE_EXTRACT_SYNC_WORD 0xBABEBEBA + +/** + * \brief Definitions of shifts and masks for format encoding in probe + * extraction stream + * + * Audio format from extraction probes is encoded as 32 bit value. Following + * graphic explains encoding. + * + * A|BBBB|CCCC|DDDD|EEEEE|FF|GG|H|I|J|XXXXXXX + * A - 1 bit - Specifies Type Encoding - 1 for Standard encoding + * B - 4 bits - Specify Standard Type - 0 for Audio + * C - 4 bits - Specify Audio format - 0 for PCM + * D - 4 bits - Specify Sample Rate - value enumerating standard sample rates: + * 8000 Hz = 0x0 + * 11025 Hz = 0x1 + * 12000 Hz = 0x2 + * 16000 Hz = 0x3 + * 22050 Hz = 0x4 + * 24000 Hz = 0x5 + * 32000 Hz = 0x6 + * 44100 Hz = 0x7 + * 48000 Hz = 0x8 + * 64000 Hz = 0x9 + * 88200 Hz = 0xA + * 96000 Hz = 0xB + * 128000 Hz = 0xC + * 176400 Hz = 0xD + * 192000 Hz = 0xE + * none of the above = 0xF + * E - 5 bits - Specify Number of Channels minus 1 + * F - 2 bits - Specify Sample Size, number of valid sample bytes minus 1 + * G - 2 bits - Specify Container Size, number of container bytes minus 1 + * H - 1 bit - Specifies Sample Format - 0 for Integer, 1 for Floating point + * I - 1 bit - Specifies Sample Endianness - 0 for LE + * J - 1 bit - Specifies Interleaving - 1 for Sample Interleaving + */ + +#define PROBE_SHIFT_FMT_TYPE 31 +#define PROBE_SHIFT_STANDARD_TYPE 27 +#define PROBE_SHIFT_AUDIO_FMT 23 +#define PROBE_SHIFT_SAMPLE_RATE 19 +#define PROBE_SHIFT_NB_CHANNELS 14 +#define PROBE_SHIFT_SAMPLE_SIZE 12 +#define PROBE_SHIFT_CONTAINER_SIZE 10 +#define PROBE_SHIFT_SAMPLE_FMT 9 +#define PROBE_SHIFT_SAMPLE_END 8 +#define PROBE_SHIFT_INTERLEAVING_ST 7 + +#define PROBE_MASK_FMT_TYPE MASK(31, 31) +#define PROBE_MASK_STANDARD_TYPE MASK(30, 27) +#define PROBE_MASK_AUDIO_FMT MASK(26, 23) +#define PROBE_MASK_SAMPLE_RATE MASK(22, 19) +#define PROBE_MASK_NB_CHANNELS MASK(18, 14) +#define PROBE_MASK_SAMPLE_SIZE MASK(13, 12) +#define PROBE_MASK_CONTAINER_SIZE MASK(11, 10) +#define PROBE_MASK_SAMPLE_FMT MASK(9, 9) +#define PROBE_MASK_SAMPLE_END MASK(8, 8) +#define PROBE_MASK_INTERLEAVING_ST MASK(7, 7) + +#endif /* __PROBE_DMA_FRAME_H__ */ diff --git a/src/utils/probes_demux.c b/src/utils/probes_demux.c new file mode 100644 index 0000000..7d51d3c --- /dev/null +++ b/src/utils/probes_demux.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. +// +// Author: Adrian Bonislawski +// Jyri Sarha (restructured and moved to this file) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "probe_dma_frame.h" + +#include "probes_wave.h" + +#define APP_NAME "sof-probes" + +#define PACKET_MAX_SIZE 4096 /**< Size limit for probe data packet */ +#define DATA_READ_LIMIT 4096 /**< Data limit for file read */ +#define FILES_LIMIT 32 /**< Maximum num of probe output files */ +#define FILE_PATH_LIMIT 128 /**< Path limit for probe output files */ + +struct wave_files { + FILE *fd; + uint32_t buffer_id; + uint32_t fmt; + uint32_t size; + struct wave header; +}; + +enum p_state { + READY = 0, /**< At this stage app is looking for a SYNC word */ + SYNC, /**< SYNC received, copying data */ + CHECK /**< Check crc and save packet if valid */ +}; + +struct dma_frame_parser { + bool log_to_stdout; + enum p_state state; + struct probe_data_packet *packet; + size_t packet_size; + uint8_t *w_ptr; /* Write pointer to copy data to */ + uint32_t total_data_to_copy; /* Total bytes left to copy */ + int start; /* Start of unfilled data */ + int len; /* Data buffer fill level */ + uint8_t data[DATA_READ_LIMIT]; + struct wave_files files[FILES_LIMIT]; +}; + +static uint32_t sample_rate[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, + 48000, 64000, 88200, 96000, 128000, 176400, 192000 +}; + +int get_buffer_file(struct wave_files *files, uint32_t buffer_id) +{ + int i; + + for (i = 0; i < FILES_LIMIT; i++) { + if (files[i].fd != NULL && files[i].buffer_id == buffer_id) + return i; + } + return -1; +} + +int get_buffer_file_free(struct wave_files *files) +{ + int i; + + for (i = 0; i < FILES_LIMIT; i++) { + if (files[i].fd == NULL) + return i; + } + return -1; +} + +bool is_audio_format(uint32_t format) +{ + return (format & PROBE_MASK_FMT_TYPE) != 0 && (format & PROBE_MASK_AUDIO_FMT) == 0; +} + +int init_wave(struct dma_frame_parser *p, uint32_t buffer_id, uint32_t format) +{ + bool audio = is_audio_format(format); + char path[FILE_PATH_LIMIT]; + int i; + + i = get_buffer_file_free(p->files); + if (i == -1) { + fprintf(stderr, "error: too many buffers\n"); + exit(0); + } + + sprintf(path, "buffer_%d.%s", buffer_id, audio ? "wav" : "bin"); + + fprintf(stderr, "%s:\t Creating file %s\n", APP_NAME, path); + + if (!audio && p->log_to_stdout) { + p->files[i].fd = stdout; + } else { + p->files[i].fd = fopen(path, "wb"); + if (!p->files[i].fd) { + fprintf(stderr, "error: unable to create file %s, error %d\n", + path, errno); + exit(0); + } + } + + p->files[i].buffer_id = buffer_id; + p->files[i].fmt = format; + + if (!audio) + return i; + + p->files[i].header.riff.chunk_id = HEADER_RIFF; + p->files[i].header.riff.format = HEADER_WAVE; + p->files[i].header.fmt.subchunk_id = HEADER_FMT; + p->files[i].header.fmt.subchunk_size = 16; + p->files[i].header.fmt.audio_format = 1; + p->files[i].header.fmt.num_channels = ((format & PROBE_MASK_NB_CHANNELS) >> PROBE_SHIFT_NB_CHANNELS) + 1; + p->files[i].header.fmt.sample_rate = sample_rate[(format & PROBE_MASK_SAMPLE_RATE) >> PROBE_SHIFT_SAMPLE_RATE]; + p->files[i].header.fmt.bits_per_sample = (((format & PROBE_MASK_CONTAINER_SIZE) >> PROBE_SHIFT_CONTAINER_SIZE) + 1) * 8; + p->files[i].header.fmt.byte_rate = p->files[i].header.fmt.sample_rate * + p->files[i].header.fmt.num_channels * + p->files[i].header.fmt.bits_per_sample / 8; + p->files[i].header.fmt.block_align = p->files[i].header.fmt.num_channels * + p->files[i].header.fmt.bits_per_sample / 8; + p->files[i].header.data.subchunk_id = HEADER_DATA; + + fwrite(&p->files[i].header, sizeof(struct wave), 1, p->files[i].fd); + + return i; +} + +void finalize_wave_files(struct dma_frame_parser *p) +{ + struct wave_files *files = p->files; + uint32_t i, chunk_size; + + /* fill the header at the beginning of each file */ + /* and close all opened files */ + /* check wave struct to understand the offsets */ + for (i = 0; i < FILES_LIMIT; i++) { + if (!is_audio_format(files[i].fmt)) + continue; + + if (files[i].fd) { + chunk_size = files[i].size + sizeof(struct wave) - + offsetof(struct riff_chunk, format); + + fseek(files[i].fd, sizeof(uint32_t), SEEK_SET); + fwrite(&chunk_size, sizeof(uint32_t), 1, files[i].fd); + fseek(files[i].fd, sizeof(struct wave) - + offsetof(struct data_subchunk, subchunk_size), + SEEK_SET); + fwrite(&files[i].size, sizeof(uint32_t), 1, files[i].fd); + + fclose(files[i].fd); + } + } +} + +int validate_data_packet(struct probe_data_packet *packet) +{ + uint64_t *checksump; + uint64_t sum; + + sum = (uint32_t) (packet->sync_word + + packet->buffer_id + + packet->format + + packet->timestamp_high + + packet->timestamp_low + + packet->data_size_bytes); + + checksump = (uint64_t *) (packet->data + packet->data_size_bytes); + + if (sum != *checksump) { + fprintf(stderr, "Checksum error 0x%016" PRIx64 " != 0x%016" PRIx64 "\n", + sum, *checksump); + return -EINVAL; + } + + return 0; +} + +int process_sync(struct dma_frame_parser *p) +{ + struct probe_data_packet *temp_packet; + + /* request to copy data_size from probe packet and 64-bit checksum */ + p->total_data_to_copy = p->packet->data_size_bytes + sizeof(uint64_t); + + if (sizeof(struct probe_data_packet) + p->total_data_to_copy > + p->packet_size) { + p->packet_size = sizeof(struct probe_data_packet) + + p->total_data_to_copy; + + temp_packet = realloc(p->packet, p->packet_size); + + if (!temp_packet) + return -ENOMEM; + + p->packet = temp_packet; + } + + p->w_ptr = (uint8_t *)p->packet->data; + + return 0; +} + +struct dma_frame_parser *parser_init(void) +{ + struct dma_frame_parser *p = malloc(sizeof(*p)); + if (!p) { + fprintf(stderr, "error: allocation failed, err %d\n", + errno); + return NULL; + } + memset(p, 0, sizeof(*p)); + p->packet = malloc(PACKET_MAX_SIZE); + if (!p) { + fprintf(stderr, "error: allocation failed, err %d\n", + errno); + free(p); + return NULL; + } + memset(p->packet, 0, PACKET_MAX_SIZE); + p->packet_size = PACKET_MAX_SIZE; + return p; +} + +void parser_free(struct dma_frame_parser *p) +{ + free(p->packet); + free(p); +} + +void parser_log_to_stdout(struct dma_frame_parser *p) +{ + p->log_to_stdout = true; +} + +void parser_fetch_free_buffer(struct dma_frame_parser *p, uint8_t **d, size_t *len) +{ + *d = &p->data[p->start]; + *len = sizeof(p->data) - p->start; +} + +int parser_parse_data(struct dma_frame_parser *p, size_t d_len) +{ + uint i = 0; + + p->len = p->start + d_len; + /* processing all loaded bytes */ + while (i < p->len) { + if (p->total_data_to_copy == 0) { + switch (p->state) { + case READY: + /* check for SYNC */ + if (p->len - i < sizeof(p->packet->sync_word)) { + p->start = p->len - i; + memmove(&p->data[0], &p->data[i], p->start); + i += p->start; + } else if (*((uint32_t *)&p->data[i]) == + PROBE_EXTRACT_SYNC_WORD) { + memset(p->packet, 0, p->packet_size); + /* request to copy full data packet */ + p->total_data_to_copy = + sizeof(struct probe_data_packet); + p->w_ptr = (uint8_t *)p->packet; + p->state = SYNC; + p->start = 0; + } else { + i++; + } + break; + case SYNC: + /* SYNC -> CHECK */ + if (process_sync(p) < 0) { + fprintf(stderr, "OOM, quitting\n"); + return -ENOMEM; + } + p->state = CHECK; + break; + case CHECK: + /* CHECK -> READY */ + /* find corresponding file and save data if valid */ + if (validate_data_packet(p->packet) == 0) { + int file = get_buffer_file(p->files, + p->packet->buffer_id); + + if (file < 0) + file = init_wave(p, p->packet->buffer_id, + p->packet->format); + + if (file < 0) { + fprintf(stderr, + "unable to open file for %u\n", + p->packet->buffer_id); + return -EIO; + } + + fwrite(p->packet->data, 1, + p->packet->data_size_bytes, + p->files[file].fd); + p->files[file].size += p->packet->data_size_bytes; + } + p->state = READY; + break; + } + } + /* data copying section */ + if (p->total_data_to_copy > 0) { + uint data_to_copy; + + /* check if there is enough bytes loaded */ + /* or copy partially if not */ + if (i + p->total_data_to_copy > p->len) { + data_to_copy = p->len - i; + p->total_data_to_copy -= data_to_copy; + } else { + data_to_copy = p->total_data_to_copy; + p->total_data_to_copy = 0; + } + memcpy(p->w_ptr, &p->data[i], data_to_copy); + p->w_ptr += data_to_copy; + i += data_to_copy; + } + } + return 0; +} diff --git a/src/utils/probes_demux.h b/src/utils/probes_demux.h new file mode 100644 index 0000000..93aa891 --- /dev/null +++ b/src/utils/probes_demux.h @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. +// +// Author: Jyri Sarha +// + +#ifndef _PROBES_DEMUX_H_ +#define _PROBES_DEMUX_H_ + +#include +#include +#include + +struct dma_frame_parser; + +struct dma_frame_parser *parser_init(void); + +void parser_log_to_stdout(struct dma_frame_parser *p); + +void parser_free(struct dma_frame_parser *p); + +void parser_fetch_free_buffer(struct dma_frame_parser *p, uint8_t **d, size_t *len); + +int parser_parse_data(struct dma_frame_parser *p, size_t d_len); + +void finalize_wave_files(struct dma_frame_parser *p); + +#endif diff --git a/src/utils/probes_wave.h b/src/utils/probes_wave.h new file mode 100644 index 0000000..d5a1b70 --- /dev/null +++ b/src/utils/probes_wave.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2019 Intel Corporation. All rights reserved. + * + * Author: Adrian Bonislawski + */ + +#ifndef __WAVE_H__ +#define __WAVE_H__ + +#define HEADER_RIFF 0x46464952 /**< ASCII "RIFF" */ +#define HEADER_WAVE 0x45564157 /**< ASCII "WAVE" */ +#define HEADER_FMT 0x20746d66 /**< ASCII "fmt " */ +#define HEADER_DATA 0x61746164 /**< ASCII "data" */ + +struct riff_chunk { + uint32_t chunk_id; + uint32_t chunk_size; + uint32_t format; +}; + +struct fmt_subchunk { + uint32_t subchunk_id; + uint32_t subchunk_size; + uint16_t audio_format; + uint16_t num_channels; + uint32_t sample_rate; + uint32_t byte_rate; + uint16_t block_align; + uint16_t bits_per_sample; +}; + +struct data_subchunk { + uint32_t subchunk_id; + uint32_t subchunk_size; + uint32_t data[]; +}; + +struct wave { + struct riff_chunk riff; + struct fmt_subchunk fmt; + struct data_subchunk data; +}; + +#endif /* __WAVE_H__ */ diff --git a/src/utils/sofprobeclient.c b/src/utils/sofprobeclient.c index 754084d..e5b4aea 100644 --- a/src/utils/sofprobeclient.c +++ b/src/utils/sofprobeclient.c @@ -77,87 +77,40 @@ #include "sound/compress_params.h" #include "sound/compress_offload.h" #include "tinycompress/tinycompress.h" -#include "tinycompress/tinywave.h" + +#include "probes_demux.h" static int verbose; -static int file; static FILE *finfo; -static bool streamed; static const unsigned int DEFAULT_CHANNELS = 4; static const unsigned int DEFAULT_RATE = 48000; static const unsigned int DEFAULT_FORMAT = SNDRV_PCM_FORMAT_S32_LE; static const unsigned int DEFAULT_CODEC_ID = SND_AUDIOCODEC_PCM; -static const struct { - const char *name; - unsigned int id; -} codec_ids[] = { - { "PCM", SND_AUDIOCODEC_PCM }, - { "MP3", SND_AUDIOCODEC_MP3 }, - { "AMR", SND_AUDIOCODEC_AMR }, - { "AMRWB", SND_AUDIOCODEC_AMRWB }, - { "AMRWBPLUS", SND_AUDIOCODEC_AMRWBPLUS }, - { "AAC", SND_AUDIOCODEC_AAC }, - { "WMA", SND_AUDIOCODEC_WMA }, - { "REAL", SND_AUDIOCODEC_REAL }, - { "VORBIS", SND_AUDIOCODEC_VORBIS }, - { "FLAC", SND_AUDIOCODEC_FLAC }, - { "IEC61937", SND_AUDIOCODEC_IEC61937 }, - { "G723_1", SND_AUDIOCODEC_G723_1 }, - { "G729", SND_AUDIOCODEC_G729 }, -/* BESPOKE isn't defined on older kernels */ -#ifdef SND_AUDIOCODEC_BESPOKE - { "BESPOKE", SND_AUDIOCODEC_BESPOKE }, -#endif -}; -#define CREC_NUM_CODEC_IDS (sizeof(codec_ids) / sizeof(codec_ids[0])) - -static const char *codec_name_from_id(unsigned int id) -{ - static char hexname[12]; - int i; - - for (i = 0; i < CREC_NUM_CODEC_IDS; ++i) { - if (codec_ids[i].id == id) - return codec_ids[i].name; - } - - snprintf(hexname, sizeof(hexname), "0x%x", id); - return hexname; /* a static is safe because we're single-threaded */ -} +static struct dma_frame_parser *parser; static void usage(void) { - int i; - - fprintf(stderr, "usage: sofprobeclient [OPTIONS] [filename.wav]\n" - "-c\tcard number\n" - "-d\tdevice node\n" - "-b\tbuffer size\n" - "-f\tfragments\n" + fprintf(stderr, "usage: sofprobeclient [OPTIONS]\n" + "-c\tcard number (default 3)\n" + "-d\tdevice node (default 0)\n" + "-b\tbuffer size (default 8192)\n" + "-f\tfragments (default 4)\n" "-v\tverbose mode\n" - "-l\tlength of record in seconds\n" + "-l\tlength of record in seconds (0 = unlimited)\n" "-h\tPrints this help list\n\n" "-C\tSpecify the number of channels (default %u)\n" "-R\tSpecify the sample rate (default %u)\n" - "-F\tSpecify the format: S16_LE, S32_LE (default S16_LE)\n" - "-I\tSpecify codec ID (default %s)\n\n" - "If filename.wav is not given the output is written to stdout\n" - "Only PCM data can be written to a WAV file.\n\n" + "-F\tSpecify the format: S16_LE, S32_LE (default S32_LE)\n\n" + "Captured probe data is parsed in real time.\n" + "Log output (non-audio probes) is written to stdout.\n" + "Audio probe data is written to buffer_.wav files\n" + "in the current directory.\n\n" "Example:\n" - "\tsofprobeclient -c 1 -d 2 test.wav\n" - "\tsofprobeclient -f 5 test.wav\n" - "\tsofprobeclient -I BESPOKE >raw.bin\n\n" - "Valid codec IDs:\n", - DEFAULT_CHANNELS, DEFAULT_RATE, - codec_name_from_id(DEFAULT_CODEC_ID)); - - for (i = 0; i < CREC_NUM_CODEC_IDS; ++i) - fprintf(stderr, "%s%c", codec_ids[i].name, - ((i + 1) % 8) ? ' ' : '\n'); - - fprintf(stderr, "\nor the value in decimal or hex\n"); + "\tsofprobeclient\n" + "\tsofprobeclient -c 1 -d 2\n", + DEFAULT_CHANNELS, DEFAULT_RATE); exit(EXIT_FAILURE); } @@ -178,67 +131,20 @@ static int print_time(struct compress *compress) return 0; } -static int finish_record(void) -{ - struct wave_header header; - int ret; - size_t nread, written; - - if (!file) - return -ENOENT; - - /* can't rewind if streaming to stdout */ - if (streamed) - return 0; - - /* Get amount of data written to file */ - ret = lseek(file, 0, SEEK_END); - if (ret < 0) - return -errno; - - written = ret; - if (written < sizeof(header)) - return -ENOENT; - written -= sizeof(header); - - /* Sync file header from file */ - ret = lseek(file, 0, SEEK_SET); - if (ret < 0) - return -errno; - - nread = read(file, &header, sizeof(header)); - if (nread != sizeof(header)) - return -errno; - - /* Update file header */ - ret = lseek(file, 0, SEEK_SET); - if (ret < 0) - return -errno; - - size_wave_header(&header, written); - - written = write(file, &header, sizeof(header)); - if (written != sizeof(header)) - return -errno; - - return 0; -} - -static void capture_samples(char *name, unsigned int card, unsigned int device, - unsigned long buffer_size, unsigned int frag, - unsigned int length, unsigned int rate, - unsigned int channels, unsigned int format, - unsigned int codec_id) +static void capture_and_parse(unsigned int card, unsigned int device, + unsigned long buffer_size, unsigned int frag, + unsigned int length, unsigned int rate, + unsigned int channels, unsigned int format) { struct compr_config config; struct snd_codec codec; struct compress *compress; - struct wave_header header; char *buffer; - size_t written; int read, ret; unsigned int size, total_read = 0; unsigned int samplebits; + uint8_t *parse_buf; + size_t parse_len; switch (format) { case SNDRV_PCM_FORMAT_S32_LE: @@ -254,37 +160,16 @@ static void capture_samples(char *name, unsigned int card, unsigned int device, if (verbose) fprintf(finfo, "%s: entry, reading %u bytes\n", __func__, length); - if (!name) { - file = STDOUT_FILENO; - } else { - file = open(name, O_RDWR | O_CREAT, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - if (file == -1) { - fprintf(stderr, "Unable to open file '%s'\n", name); - exit(EXIT_FAILURE); - } - } - - /* Write a header, will update with size once record is complete */ - if (!streamed) { - init_wave_header(&header, channels, rate, samplebits); - written = write(file, &header, sizeof(header)); - if (written != sizeof(header)) { - fprintf(stderr, "Error writing output file header: %s\n", - strerror(errno)); - goto file_exit; - } - } memset(&codec, 0, sizeof(codec)); memset(&config, 0, sizeof(config)); - codec.id = codec_id; + codec.id = DEFAULT_CODEC_ID; codec.ch_in = channels; codec.ch_out = channels; codec.sample_rate = rate; if (!codec.sample_rate) { fprintf(stderr, "invalid sample rate %d\n", rate); - goto file_exit; + return; } codec.format = format; if ((buffer_size != 0) && (frag != 0)) { @@ -298,7 +183,7 @@ static void capture_samples(char *name, unsigned int card, unsigned int device, fprintf(stderr, "Unable to open Compress device %d:%d\n", card, device); fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); - goto file_exit; + return; }; if (verbose) @@ -311,15 +196,15 @@ static void capture_samples(char *name, unsigned int card, unsigned int device, goto comp_exit; } - fprintf(finfo, "Recording file %s On Card %u device %u, with buffer of %u bytes\n", - name, card, device, size); + fprintf(finfo, "Capturing probes on Card %u device %u, buffer %u bytes\n", + card, device, size); fprintf(finfo, "Codec %u Format %u Channels %u, %u Hz\n", codec.id, codec.format, codec.ch_out, rate); compress_start(compress); if (verbose) - fprintf(finfo, "%s: Capturing audio NOW!!!\n", __func__); + fprintf(finfo, "%s: Capturing probe data NOW!!!\n", __func__); do { read = compress_read(compress, buffer, size); @@ -336,12 +221,20 @@ static void capture_samples(char *name, unsigned int card, unsigned int device, if (read > 0) { total_read += read; - written = write(file, buffer, read); - if (written != (size_t)read) { - fprintf(stderr, "Error writing output file: %s\n", - strerror(errno)); + /* Feed captured data into the probe parser */ + parser_fetch_free_buffer(parser, &parse_buf, &parse_len); + if ((size_t)read > parse_len) { + fprintf(stderr, "Warning: read %d > parser buffer %zu, truncating\n", + read, parse_len); + read = parse_len; + } + memcpy(parse_buf, buffer, read); + ret = parser_parse_data(parser, read); + if (ret < 0) { + fprintf(stderr, "Parser error %d, stopping\n", ret); goto buf_exit; } + if (verbose) { print_time(compress); fprintf(finfo, "%s: read %d\n", __func__, read); @@ -355,19 +248,13 @@ static void capture_samples(char *name, unsigned int card, unsigned int device, fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); } - ret = finish_record(); - if (ret < 0) { - fprintf(stderr, "Failed to finish header: %s\n", strerror(ret)); - goto buf_exit; - } + /* Finalize any open wave files */ + finalize_wave_files(parser); if (verbose) fprintf(finfo, "%s: exit success\n", __func__); free(buffer); - close(file); - file = 0; - compress_close(compress); return; @@ -375,8 +262,6 @@ buf_exit: free(buffer); comp_exit: compress_close(compress); -file_exit: - close(file); if (verbose) fprintf(finfo, "%s: exit failure\n", __func__); @@ -386,34 +271,38 @@ file_exit: static void sig_handler(int signum __attribute__ ((unused))) { - finish_record(); - - if (file) - close(file); + /* Finalize wave files on signal */ + if (parser) + finalize_wave_files(parser); _exit(EXIT_FAILURE); } int main(int argc, char **argv) { - char *file; unsigned long buffer_size = 8192; - int c, i; + int c; unsigned int card = 3, device = 0, frag = 4, length = 0; unsigned int rate = DEFAULT_RATE, channels = DEFAULT_CHANNELS; unsigned int format = DEFAULT_FORMAT; - unsigned int codec_id = DEFAULT_CODEC_ID; if (signal(SIGINT, sig_handler) == SIG_ERR) { fprintf(stderr, "Error registering signal handler\n"); exit(EXIT_FAILURE); } - if (argc < 1) - usage(); + /* Initialize the probe parser with log-to-stdout */ + parser = parser_init(); + if (!parser) { + fprintf(stderr, "Failed to initialize probe parser\n"); + exit(EXIT_FAILURE); + } + parser_log_to_stdout(parser); verbose = 0; - while ((c = getopt(argc, argv, "hvl:R:C:F:I:b:f:c:d:")) != -1) { + finfo = stderr; + + while ((c = getopt(argc, argv, "hvl:R:C:F:b:f:c:d:")) != -1) { switch (c) { case 'h': usage(); @@ -453,47 +342,16 @@ int main(int argc, char **argv) usage(); } break; - case 'I': - if (optarg[0] == '0') { - codec_id = strtol(optarg, NULL, 0); - } else { - for (i = 0; i < CREC_NUM_CODEC_IDS; ++i) { - if (strcmp(optarg, - codec_ids[i].name) == 0) { - codec_id = codec_ids[i].id; - break; - } - } - - if (i == CREC_NUM_CODEC_IDS) { - fprintf(stderr, "Unrecognised ID: %s\n", - optarg); - usage(); - } - } - break; default: exit(EXIT_FAILURE); } } - if (optind >= argc) { - file = NULL; - finfo = fopen("/dev/null", "w"); - streamed = true; - } else if (codec_id == SND_AUDIOCODEC_PCM) { - file = argv[optind]; - finfo = stdout; - streamed = false; - } else { - fprintf(stderr, "ERROR: Only PCM can be written to a WAV file\n"); - exit(EXIT_FAILURE); - } - - capture_samples(file, card, device, buffer_size, frag, length, - rate, channels, format, codec_id); + capture_and_parse(card, device, buffer_size, frag, length, + rate, channels, format); fprintf(finfo, "Finish capturing... Close Normally\n"); + parser_free(parser); exit(EXIT_SUCCESS); } -- 2.52.0