Skip to content

Instantly share code, notes, and snippets.

@xsbee
Last active April 26, 2023 19:07
Show Gist options
  • Save xsbee/98fb7be0d47b6588c3f0e7ada11a7ad3 to your computer and use it in GitHub Desktop.
Save xsbee/98fb7be0d47b6588c3f0e7ada11a7ad3 to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <cairo.h>
/**
* Loads single channel 32-bit little-endian single precision
* floating point PCM data.
*/
static void
load_f32le (uint8_t *block, float *pcm, int num)
{
int i;
union {
uint32_t z;
float f;
} s;
uint8_t *seq;
for (i = 0; i < num; ++i)
{
seq = block + sizeof(float) * i;
s.z = (int)seq[0] |
(int)seq[1] << 8 |
(int)seq[2] << 16 |
(int)seq[3] << 24;
pcm[i] = s.f;
}
}
static int
push_into_sliding_win (float *window, int winsz, float* buf, int bufsz)
{
if (winsz < bufsz)
return -1;
int winoff = winsz - bufsz;
memmove (window, window + bufsz, winoff * sizeof(float));
memcpy (window + winoff, buf, bufsz * sizeof(float));
return 0;
}
static size_t
dump_cairo_argb_data (FILE* out, unsigned char* data, int width, int height, int stride)
{
int j;
size_t written = 0;
for (j = 0; j < height; ++j)
written += fwrite (data + j*stride, 4, width, out);
return written;
}
int main (int argc, char **argv)
{
/* These values are placeholders. */
const double xstep = 2.0 / (float)WV_WINSZ;
/* No need for double buffering. */
setbuf (stdout, NULL);
setbuf (stdin, NULL);
uint8_t *block = NULL;
unsigned char *frame;
size_t blocksz;
float *pcm = NULL, *window = NULL;
int pcmsz, i, stride;
double x;
cairo_surface_t *surface = NULL;
cairo_status_t status;
cairo_t *cr = NULL;
blocksz = WV_NSAMPLES * sizeof(float);
block = malloc (blocksz);
if (!block)
goto cleanup;
pcm = malloc (blocksz);
if (!pcm)
goto cleanup;
window = calloc (WV_WINSZ, sizeof(float));
if (!window)
goto cleanup;
stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, WV_WIDTH);
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, WV_WIDTH, WV_HEIGHT);
status = cairo_surface_status (surface);
if (status == CAIRO_STATUS_NO_MEMORY)
goto cleanup;
cr = cairo_create (surface);
status = cairo_status (cr);
if (status == CAIRO_STATUS_NO_MEMORY)
goto cleanup;
cairo_set_line_width (cr, WV_LINE_WIDTH);
cairo_scale (cr, WV_WIDTH / 2.0, WV_HEIGHT / 2.0);
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
for (;;)
{
pcmsz = fread (block, sizeof (float), WV_NSAMPLES, stdin);
if (pcmsz == 0)
break;
load_f32le (block, pcm, pcmsz);
push_into_sliding_win (window, WV_WINSZ, pcm, pcmsz);
// TODO fill with zero samples if required at end
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_move_to (cr, 0, 1 - window[0]);
x = xstep;
for (i = 1; i < WV_WINSZ; ++i)
{
cairo_line_to (cr, x, 1 - window[i]);
x += xstep;
}
cairo_stroke (cr);
cairo_surface_flush (surface);
frame = cairo_image_surface_get_data (surface);
dump_cairo_argb_data (stdout, frame, WV_WIDTH, WV_HEIGHT, stride);
}
cleanup:
if (window)
free (window);
if (pcm)
free (pcm);
if (block)
free (block);
if (cr)
cairo_destroy (cr);
if (surface)
cairo_surface_finish (surface);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment