]> git.alsa-project.org Git - tinycompress.git/commitdiff
utils: sofprobeclient: embed SOF probe parser for real-time demux
authorJyri Sarha <jyri.sarha@linux.intel.com>
Mon, 30 Mar 2026 21:47:33 +0000 (00:47 +0300)
committerJaroslav Kysela <perex@perex.cz>
Fri, 29 May 2026 12:04:53 +0000 (14:04 +0200)
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_<id>.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 <jyri.sarha@linux.intel.com>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
src/utils/Makefile.am
src/utils/probe_dma_frame.h [new file with mode: 0644]
src/utils/probes_demux.c [new file with mode: 0644]
src/utils/probes_demux.h [new file with mode: 0644]
src/utils/probes_wave.h [new file with mode: 0644]
src/utils/sofprobeclient.c

index 94647d79d0ff7ba5075d23c84e13afb3d5b2123f..9c833629419ac21e336e3a1ddb883696272cb339 100644 (file)
@@ -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 (file)
index 0000000..1b617f2
--- /dev/null
@@ -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 <stdint.h>
+
+#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 (file)
index 0000000..7d51d3c
--- /dev/null
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+//
+// Author: Adrian Bonislawski <adrian.bonislawski@intel.com>
+//         Jyri Sarha <jyri.sarha@intel.com> (restructured and moved to this file)
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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 (file)
index 0000000..93aa891
--- /dev/null
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+//
+// Author: Jyri Sarha <jyri.sarha@intel.com>
+//
+
+#ifndef _PROBES_DEMUX_H_
+#define _PROBES_DEMUX_H_
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+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 (file)
index 0000000..d5a1b70
--- /dev/null
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright(c) 2019 Intel Corporation. All rights reserved.
+ *
+ * Author: Adrian Bonislawski <adrian.bonislawski@intel.com>
+ */
+
+#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__ */
index 754084d5511d939063ba41cbae470b66948228f2..e5b4aeaaa921a874c0800f14e2c2c68f1021ef6c 100644 (file)
 #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_<id>.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);
 }