Fichiers audio, journal, annales
This commit is contained in:
198
cours/audio/vcd_lib.c
Normal file
198
cours/audio/vcd_lib.c
Normal file
@@ -0,0 +1,198 @@
|
||||
#include "vcd_lib.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
size_t vcd_sizeof_signal_type(vcd_signal_type_t type) {
|
||||
switch (type) {
|
||||
case VCD_SIGNAL_TYPE_BOOL:
|
||||
return sizeof(int);
|
||||
case VCD_SIGNAL_TYPE_INT:
|
||||
return sizeof(int);
|
||||
case VCD_SIGNAL_TYPE_FLOAT:
|
||||
return sizeof(float);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct vcd_signal {
|
||||
char *name;
|
||||
vcd_signal_type_t type;
|
||||
buffer_t *samples;
|
||||
} vcd_signal_t;
|
||||
|
||||
vcd_signal_t *vcd_signal_alloc(const char *name,
|
||||
vcd_signal_type_t type,
|
||||
size_t initial_buffer_size) {
|
||||
vcd_signal_t *res = malloc_checked(sizeof *res);
|
||||
res->name = strdup_checked(name);
|
||||
res->type = type;
|
||||
res->samples =
|
||||
buffer_alloc(initial_buffer_size * vcd_sizeof_signal_type(type));
|
||||
return res;
|
||||
}
|
||||
|
||||
void vcd_signal_free(vcd_signal_t *signal) {
|
||||
assert (signal);
|
||||
|
||||
buffer_free(signal->samples);
|
||||
free(signal->name);
|
||||
free(signal);
|
||||
}
|
||||
|
||||
void vcd_add_samples(vcd_signal_t *signal, void *samples, size_t count) {
|
||||
assert (signal);
|
||||
assert (samples);
|
||||
buffer_write(signal->samples,
|
||||
samples,
|
||||
count * vcd_sizeof_signal_type(signal->type));
|
||||
}
|
||||
|
||||
const char *vcd_time_unit_repr(vcd_time_unit_t u) {
|
||||
const char *table[] = { "s", "ms", "us", "ns", "ps", "fs" };
|
||||
assert (VCD_TIME_UNIT_S <= u && u <= VCD_TIME_UNIT_FS);
|
||||
return table[u];
|
||||
}
|
||||
|
||||
typedef struct vcd_file {
|
||||
char *filename;
|
||||
buffer_t *signals;
|
||||
vcd_time_unit_t time_unit;
|
||||
size_t time_unit_factor;
|
||||
} vcd_file_t;
|
||||
|
||||
vcd_file_t *vcd_file_alloc(const char *filename,
|
||||
vcd_time_unit_t time_unit,
|
||||
size_t time_unit_factor) {
|
||||
assert (filename);
|
||||
|
||||
vcd_file_t *vcd = malloc_checked(sizeof *vcd);
|
||||
vcd->filename = strdup_checked(filename);
|
||||
vcd->time_unit = time_unit;
|
||||
vcd->time_unit_factor = time_unit_factor;
|
||||
vcd->signals = buffer_alloc(10 * sizeof(vcd_signal_t));
|
||||
|
||||
return vcd;
|
||||
}
|
||||
|
||||
void vcd_file_free(vcd_file_t *vcd) {
|
||||
assert (vcd);
|
||||
|
||||
/* Free buffers. */
|
||||
buffer_foreach (vcd_signal_t *, psig, vcd->signals)
|
||||
vcd_signal_free(*psig);
|
||||
buffer_free(vcd->signals);
|
||||
|
||||
free(vcd->filename);
|
||||
free(vcd);
|
||||
}
|
||||
|
||||
vcd_signal_t *vcd_file_lookup_signal(const vcd_file_t *vcd, const char *name) {
|
||||
assert (vcd);
|
||||
assert (name);
|
||||
|
||||
vcd_signal_t *res = NULL;
|
||||
|
||||
buffer_foreach (vcd_signal_t *, psig, vcd->signals) {
|
||||
if (strcmp((*psig)->name, name) == 0) {
|
||||
res = *psig;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool vcd_file_add_signal(const vcd_file_t *vcd, vcd_signal_t *signal) {
|
||||
assert (vcd);
|
||||
assert (signal);
|
||||
|
||||
if (vcd_file_lookup_signal(vcd, signal->name))
|
||||
return false;
|
||||
|
||||
buffer_write(vcd->signals, &signal, sizeof signal);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vcd_file_write(vcd_file_t *vcd) {
|
||||
FILE *f = fopen(vcd->filename, "w");
|
||||
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
time_t current_time;
|
||||
time(¤t_time);
|
||||
|
||||
fprintf(f, "$version Generated by vcd.c $end\n");
|
||||
fprintf(f, "$date %s $end\n", ctime(¤t_time));
|
||||
fprintf(f, "$timescale %zu %s $end\n",
|
||||
vcd->time_unit_factor,
|
||||
vcd_time_unit_repr(vcd->time_unit));
|
||||
|
||||
/* Dump signal declarations. */
|
||||
fprintf(f, "$scope module Top $end\n");
|
||||
buffer_foreach (vcd_signal_t *, psig, vcd->signals) {
|
||||
fprintf(f, "$var ");
|
||||
switch ((*psig)->type) {
|
||||
case VCD_SIGNAL_TYPE_BOOL:
|
||||
fprintf(f, "wire 1");
|
||||
break;
|
||||
case VCD_SIGNAL_TYPE_INT:
|
||||
fprintf(f, "integer %zu", 8 * sizeof(int));
|
||||
break;
|
||||
case VCD_SIGNAL_TYPE_FLOAT:
|
||||
fprintf(f, "real 32");
|
||||
break;
|
||||
}
|
||||
fprintf(f, " %p %s $end\n", (*psig), (*psig)->name);
|
||||
}
|
||||
fprintf(f, "$upscope $end\n");
|
||||
fprintf(f, "$enddefinitions\n");
|
||||
|
||||
/* Dump samples. */
|
||||
fprintf(f, "$dumpvars\n");
|
||||
|
||||
/* We maintain a pointer to the current sample in each buffer. */
|
||||
size_t signal_count = vcd->signals->occupancy / sizeof(vcd_signal_t *);
|
||||
unsigned char **psamples = calloc(signal_count, sizeof(unsigned char *));
|
||||
assert (psamples);
|
||||
for (size_t i = 0; i < signal_count; i++)
|
||||
psamples[i] = ((vcd_signal_t **)vcd->signals->data)[i]->samples->data;
|
||||
|
||||
/* We dump */
|
||||
bool active = true;
|
||||
for (size_t step = 0; active; step++) {
|
||||
fprintf(f, "#%zu\n", step);
|
||||
active = false;
|
||||
for (size_t i = 0; i < signal_count; i++) {
|
||||
vcd_signal_t *sig = ((vcd_signal_t **)vcd->signals->data)[i];
|
||||
if (psamples[i] < sig->samples->data + sig->samples->occupancy) {
|
||||
active = true;
|
||||
switch (sig->type) {
|
||||
case VCD_SIGNAL_TYPE_BOOL:
|
||||
fprintf(f, "%d%p\n", (*(int *)psamples[i] ? 1 : 0), sig);
|
||||
psamples[i] += sizeof(int);
|
||||
break;
|
||||
case VCD_SIGNAL_TYPE_INT:
|
||||
fprintf(f, "r%d %p\n", *(int *)psamples, sig);
|
||||
psamples[i] += sizeof(int);
|
||||
break;
|
||||
case VCD_SIGNAL_TYPE_FLOAT:
|
||||
fprintf(f, "r%.16g %p\n", *(float *)psamples, sig);
|
||||
psamples[i] += sizeof(float);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(psamples);
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user