libsstvenc
Asynchronous Analogue SSTV encoder
Loading...
Searching...
No Matches
sunau.c
Go to the documentation of this file.
1
6/*
7 * © Stuart Longland VK4MSL
8 * SPDX-License-Identifier: MIT
9 */
10
15#define SSTVENC_SUNAU_MAGIC (0x2e736e64u)
16
20#define SSTVENC_SUNAU_HEADER_SZ (7)
21
22#include <assert.h>
23#include <libsstvenc/sunau.h>
24
25#ifdef MISSING_ENDIAN_H
26/*
27 * These implement the missing features from the C library
28 * https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/endian.h.html
29 */
30#include <arpa/inet.h>
31static uint16_t be16toh(uint16_t in) { return ntohs(in); }
32static uint16_t htobe16(uint16_t in) { return htons(in); }
33
34static uint32_t be32toh(uint32_t in) { return ntohl(in); }
35static uint32_t htobe32(uint32_t in) { return htonl(in); }
36
37static uint64_t be64toh(uint64_t in) {
38 return ((uint64_t)((((uint64_t)ntohl(in >> 32)) << 32)
39 | ntohl(in & UINT32_MAX)));
40}
41static uint64_t htobe64(uint64_t in) {
42 return ((uint64_t)((((uint64_t)htonl(in >> 32)) << 32)
43 | htonl(in & UINT32_MAX)));
44}
45#else
46#include <endian.h>
47#endif
48
50#define SSTVENC_SUNAU_STATE_HEADER (0x0001)
51
53static uint32_t fhtobe32(float in) {
54 union {
55 float f;
56 uint32_t ui;
57 } tmp;
58
59 tmp.f = in;
60 return htobe32(tmp.ui);
61}
62
64static float fbe32toh(uint32_t in) {
65 union {
66 float f;
67 uint32_t ui;
68 } tmp;
69
70 tmp.ui = be32toh(in);
71 return be32toh(tmp.f);
72}
73
75static uint64_t dhtobe64(double in) {
76 union {
77 double f;
78 uint64_t ui;
79 } tmp;
80
81 tmp.f = in;
82 return htobe64(tmp.ui);
83}
84
86static double dbe64toh(uint64_t in) {
87 union {
88 double f;
89 uint64_t ui;
90 } tmp;
91
92 tmp.ui = be64toh(in);
93 return be64toh(tmp.f);
94}
95
99static int sstvenc_sunau_enc_write_header(struct sstvenc_sunau* const enc) {
100 uint32_t hdr[SSTVENC_SUNAU_HEADER_SZ] = {
101 SSTVENC_SUNAU_MAGIC, // Magic ".snd"
102 sizeof(hdr), // Data offset: 28 bytes (7*4-bytes)
103 UINT32_MAX, // Data size: unknown for now
104 enc->encoding, // Encoding: int16_t linear
105 enc->sample_rate, // Sample rate
106 enc->channels, // Channels
107 0, // Annotation (unused)
108 };
109
110 /* Check we have not written anything yet */
111 assert(enc->written_sz == 0);
112 assert(!(enc->state & SSTVENC_SUNAU_STATE_HEADER));
113
114 /* Convert to big-endian */
115 for (int i = 0; i < SSTVENC_SUNAU_HEADER_SZ; i++) {
116 /* Byte swap to big-endian */
117 hdr[i] = htobe32(hdr[i]);
118 }
119
120 /* Write */
121 size_t res
122 = fwrite(hdr, sizeof(int32_t), SSTVENC_SUNAU_HEADER_SZ, enc->fh);
123 if (res < SSTVENC_SUNAU_HEADER_SZ) {
124 return -errno;
125 } else {
127 return 0;
128 }
129}
130
134static int sstvenc_sunau_write_s8(struct sstvenc_sunau* const enc,
135 size_t n_sample, const double* sample) {
136 /* Scale to 8-bit fixed-point */
137 int8_t isample[n_sample];
138 int i;
139
140 for (i = 0; i < n_sample; i++) {
141 /* Scale */
142 isample[i] = INT8_MAX * sample[i];
143 }
144
145 size_t sz = fwrite(isample, n_sample, sizeof(int8_t), enc->fh);
146 if (sz < n_sample) {
147 return -errno;
148 } else {
149 enc->written_sz += sz * sizeof(int8_t);
150 return 0;
151 }
152}
153
157static int sstvenc_sunau_write_s16(struct sstvenc_sunau* const enc,
158 size_t n_sample, const double* sample) {
159 /* Scale to 16-bit fixed-point */
160 int16_t isample[n_sample];
161 int i;
162
163 for (i = 0; i < n_sample; i++) {
164 /* Scale */
165 isample[i] = INT16_MAX * sample[i];
166 /* Byte swap */
167 isample[i] = htobe16(isample[i]);
168 }
169
170 size_t sz = fwrite(isample, n_sample, sizeof(int16_t), enc->fh);
171 if (sz < n_sample) {
172 return -errno;
173 } else {
174 enc->written_sz += sz * sizeof(int16_t);
175 return 0;
176 }
177}
178
182static int sstvenc_sunau_write_s32(struct sstvenc_sunau* const enc,
183 size_t n_sample, const double* sample) {
184 /* Scale to 32-bit fixed-point */
185 int32_t isample[n_sample];
186 int i;
187
188 for (i = 0; i < n_sample; i++) {
189 /* Scale */
190 isample[i] = INT32_MAX * sample[i];
191 /* Byte swap */
192 isample[i] = htobe32(isample[i]);
193 }
194
195 size_t sz = fwrite(isample, n_sample, sizeof(int32_t), enc->fh);
196 if (sz < n_sample) {
197 return -errno;
198 } else {
199 enc->written_sz += sz * sizeof(int32_t);
200 return 0;
201 }
202}
203
207static int sstvenc_sunau_write_f32(struct sstvenc_sunau* const enc,
208 size_t n_sample, const double* sample) {
209 /* Convert to big-endian float */
210 int32_t fsample[n_sample];
211 int i;
212
213 for (i = 0; i < n_sample; i++) {
214 /* Byte swap */
215 fsample[i] = fhtobe32(sample[i]);
216 }
217
218 size_t sz = fwrite(fsample, n_sample, sizeof(int32_t), enc->fh);
219 if (sz < n_sample) {
220 return -errno;
221 } else {
222 enc->written_sz += sz * sizeof(int32_t);
223 return 0;
224 }
225}
226
230static int sstvenc_sunau_write_f64(struct sstvenc_sunau* const enc,
231 size_t n_sample, const double* sample) {
232 /* Convert to big-endian float */
233 int64_t fsample[n_sample];
234 int i;
235
236 for (i = 0; i < n_sample; i++) {
237 /* Byte swap */
238 fsample[i] = dhtobe64(sample[i]);
239 }
240
241 size_t sz = fwrite(fsample, n_sample, sizeof(int64_t), enc->fh);
242 if (sz < n_sample) {
243 return -errno;
244 } else {
245 enc->written_sz += sz * sizeof(int64_t);
246 return 0;
247 }
248}
249
250int sstvenc_sunau_check(uint32_t sample_rate, uint8_t encoding,
251 uint8_t channels) {
252 if (!channels)
253 return -EINVAL;
254 if (!sample_rate)
255 return -EINVAL;
256 switch (encoding) {
262 break;
263 default:
264 return -EINVAL;
265 }
266
267 return 0;
268}
269
270int sstvenc_sunau_enc_init_fh(struct sstvenc_sunau* const enc, FILE* fh,
271 uint32_t sample_rate, uint8_t encoding,
272 uint8_t channels) {
273 int res = sstvenc_sunau_check(sample_rate, encoding, channels);
274 if (res < 0) {
275 return res;
276 }
277
278 enc->fh = fh;
279 enc->written_sz = 0;
280 enc->state = 0;
281 enc->sample_rate = sample_rate;
282 enc->encoding = encoding;
283 enc->channels = channels;
284
285 return 0;
286}
287
288int sstvenc_sunau_enc_init(struct sstvenc_sunau* const enc, const char* path,
289 uint32_t sample_rate, uint8_t encoding,
290 uint8_t channels) {
291 int res = sstvenc_sunau_check(sample_rate, encoding, channels);
292 if (res < 0) {
293 return res;
294 }
295
296 enc->fh = fopen(path, "wb");
297 if (enc->fh == NULL) {
298 return -errno;
299 }
300
301 enc->written_sz = 0;
302 enc->state = 0;
303 enc->sample_rate = sample_rate;
304 enc->encoding = encoding;
305 enc->channels = channels;
306
307 return 0;
308}
309
310int sstvenc_sunau_enc_write(struct sstvenc_sunau* const enc, size_t n_samples,
311 const double* samples) {
312 if ((n_samples % enc->channels) != 0) {
313 return -EINVAL;
314 }
315
316 if (!(enc->state & SSTVENC_SUNAU_STATE_HEADER)) {
317 int res = sstvenc_sunau_enc_write_header(enc);
318 if (res < 0) {
319 return res;
320 }
321 }
322
323 switch (enc->encoding) {
325 return sstvenc_sunau_write_s8(enc, n_samples, samples);
327 return sstvenc_sunau_write_s16(enc, n_samples, samples);
329 return sstvenc_sunau_write_s32(enc, n_samples, samples);
331 return sstvenc_sunau_write_f32(enc, n_samples, samples);
333 return sstvenc_sunau_write_f64(enc, n_samples, samples);
334 default:
335 assert(0);
336 return -EINVAL;
337 }
338}
339
341 if (!(enc->state & SSTVENC_SUNAU_STATE_HEADER)) {
342 int res = sstvenc_sunau_enc_write_header(enc);
343 if (res < 0) {
344 return res;
345 }
346 }
347
348 /* Can we seek in this file? */
349 if (fseek(enc->fh, sizeof(uint32_t) * 2, SEEK_SET) == 0) {
350 /* We can, write out the *correct* file size */
351 uint32_t data_sz = htobe32(enc->written_sz);
352 size_t res = fwrite(&data_sz, 1, sizeof(uint32_t), enc->fh);
353 if (res < sizeof(uint32_t)) {
354 /* Write failed, close and bail! */
355 int ret = -errno;
356 fclose(enc->fh);
357 return ret;
358 }
359 }
360
361 /* If not, no harm done! We can close the file now. */
362 if (fclose(enc->fh) != 0) {
363 /* Close failed */
364 return -errno;
365 } else {
366 return 0;
367 }
368}
369
370int sstvenc_sunau_dec_init_fh(struct sstvenc_sunau* const dec, FILE* fh) {
371 uint32_t hdr[SSTVENC_SUNAU_HEADER_SZ];
372 int res = 0;
373
374 if (fread(hdr, sizeof(hdr), 1, fh) < 1) {
375 /* Incomplete or failed read */
376 return -errno;
377 }
378
379 /* Convert to host-endian format */
380 for (uint8_t i = 0; i < SSTVENC_SUNAU_HEADER_SZ; i++) {
381 hdr[i] = be32toh(hdr[i]);
382 }
383
384 /* Assert we have a valid SunAU header */
385 if (hdr[0] != SSTVENC_SUNAU_MAGIC) {
386 /* This is not a valid header */
387 return -EINVAL;
388 }
389
390 /* Extract the header fields we care about */
391 dec->encoding = hdr[3];
392 dec->sample_rate = hdr[4];
393 dec->channels = hdr[5];
394
395 /* Check these are supported */
397 dec->channels);
398 if (res < 0) {
399 return res;
400 }
401
402 /* All good, seek to the spot where the audio begins */
403 if (fseek(fh, hdr[1], SEEK_SET) < 0) {
404 /* Seek failed */
405 return -errno;
406 }
407
408 /* All ready */
409 dec->fh = fh;
410 return 0;
411}
412
414 const char* path) {
415 FILE* fh = fopen(path, "rb");
416 if (fh == NULL) {
417 return -errno;
418 }
419
420 int res = sstvenc_sunau_dec_init_fh(dec, fh);
421 if (res < 0) {
422 /* Try our best, if the close fails, too bad! */
423 fclose(fh);
424 }
425
426 return res;
427}
428
429static int sstvenc_sunau_read_s8(struct sstvenc_sunau* const dec,
430 size_t* const n_samples, double* samples) {
431 /* Use the end of the output buffer as scratch space. */
432 int8_t* in_buffer = (int8_t*)(&(samples[*n_samples]))
433 - (sizeof(int8_t) * (*n_samples));
434 size_t in_buffer_sz = *n_samples;
435 size_t read_sz = 0;
436
437 /* Read the raw audio data */
438 errno = 0;
439 read_sz = fread(in_buffer, sizeof(int8_t), in_buffer_sz, dec->fh);
440 if (read_sz < in_buffer_sz) {
441 /* Short read, check for read errors */
442 if (errno != 0) {
443 return -errno;
444 }
445 }
446
447 /* Read is successful, write sample count, convert samples to
448 * double-precision float */
449 *n_samples = read_sz;
450 while (read_sz) {
451 *samples = -(double)(*in_buffer) / (double)INT8_MIN;
452 samples++;
453 in_buffer++;
454 read_sz--;
455 }
456
457 return 0;
458}
459
460static int sstvenc_sunau_read_s16(struct sstvenc_sunau* const dec,
461 size_t* const n_samples, double* samples) {
462 /* Use the end of the output buffer as scratch space. */
463 int16_t* in_buffer = (int16_t*)(&(samples[*n_samples]))
464 - (sizeof(int16_t) * (*n_samples));
465 size_t in_buffer_sz = *n_samples;
466 size_t read_sz = 0;
467
468 /* Read the raw audio data */
469 errno = 0;
470 read_sz = fread(in_buffer, sizeof(int16_t), in_buffer_sz, dec->fh);
471 if (read_sz < in_buffer_sz) {
472 /* Short read, check for read errors */
473 if (errno != 0) {
474 return -errno;
475 }
476 }
477
478 /* Read is successful, write sample count, convert samples to
479 * double-precision float */
480 *n_samples = read_sz;
481 while (read_sz) {
482 int16_t host_endian = be16toh(*in_buffer);
483
484 *samples = -(double)(host_endian) / (double)INT16_MIN;
485 samples++;
486 in_buffer++;
487 read_sz--;
488 }
489
490 return 0;
491}
492
493static int sstvenc_sunau_read_s32(struct sstvenc_sunau* const dec,
494 size_t* const n_samples, double* samples) {
495 /* Use the end of the output buffer as scratch space. */
496 int32_t* in_buffer = (int32_t*)(&(samples[*n_samples]))
497 - (sizeof(int32_t) * (*n_samples));
498 size_t in_buffer_sz = *n_samples;
499 size_t read_sz = 0;
500
501 /* Read the raw audio data */
502 errno = 0;
503 read_sz = fread(in_buffer, sizeof(int32_t), in_buffer_sz, dec->fh);
504 if (read_sz < in_buffer_sz) {
505 /* Short read, check for read errors */
506 if (errno != 0) {
507 return -errno;
508 }
509 }
510
511 /* Read is successful, write sample count, convert samples to
512 * double-precision float */
513 *n_samples = read_sz;
514 while (read_sz) {
515 int32_t host_endian = be32toh(*in_buffer);
516
517 *samples = -(double)(host_endian) / (double)INT32_MIN;
518 samples++;
519 in_buffer++;
520 read_sz--;
521 }
522
523 return 0;
524}
525
526static int sstvenc_sunau_read_f32(struct sstvenc_sunau* const dec,
527 size_t* const n_samples, double* samples) {
528 /* Use the end of the output buffer as scratch space. */
529 uint32_t* in_buffer = (uint32_t*)(&(samples[*n_samples]))
530 - (sizeof(uint32_t) * (*n_samples));
531 size_t in_buffer_sz = *n_samples;
532 size_t read_sz = 0;
533
534 /* Read the raw audio data */
535 errno = 0;
536 read_sz = fread(in_buffer, sizeof(float), in_buffer_sz, dec->fh);
537 if (read_sz < in_buffer_sz) {
538 /* Short read, check for read errors */
539 if (errno != 0) {
540 return -errno;
541 }
542 }
543
544 /* Read is successful, write sample count, convert samples to native
545 * endianness */
546 *n_samples = read_sz;
547 while (read_sz) {
548 *samples = fbe32toh(*in_buffer);
549 samples++;
550 in_buffer++;
551 read_sz--;
552 }
553
554 return 0;
555}
556
557static int sstvenc_sunau_read_f64(struct sstvenc_sunau* const dec,
558 size_t* const n_samples, double* samples) {
559 /* Use the output buffer as scratch space */
560 uint64_t* in_buffer = (uint64_t*)samples;
561 size_t in_buffer_sz = *n_samples;
562 size_t read_sz = 0;
563
564 /* Read the raw audio data */
565 errno = 0;
566 read_sz = fread(in_buffer, sizeof(double), in_buffer_sz, dec->fh);
567 if (read_sz < in_buffer_sz) {
568 /* Short read, check for read errors */
569 if (errno != 0) {
570 return -errno;
571 }
572 }
573
574 /* Read is successful, write sample count, convert samples to native
575 * endianness */
576 *n_samples = read_sz;
577 while (read_sz) {
578 *samples = dbe64toh(*in_buffer);
579 samples++;
580 in_buffer++;
581 read_sz--;
582 }
583
584 return 0;
585}
586
588 size_t* const n_samples, double* samples) {
589 switch (dec->encoding) {
591 return sstvenc_sunau_read_s8(dec, n_samples, samples);
593 return sstvenc_sunau_read_s16(dec, n_samples, samples);
595 return sstvenc_sunau_read_s32(dec, n_samples, samples);
597 return sstvenc_sunau_read_f32(dec, n_samples, samples);
599 return sstvenc_sunau_read_f64(dec, n_samples, samples);
600 default:
601 assert(0);
602 return -EINVAL;
603 }
604}
605
607 int res = fclose(dec->fh);
608 dec->fh = NULL;
609
610 if (res < 0) {
611 return -errno;
612 } else {
613 return 0;
614 }
615}
616
#define SSTVENC_SUNAU_FMT_F64
Definition sunau.h:35
#define SSTVENC_SUNAU_FMT_F32
Definition sunau.h:34
#define SSTVENC_SUNAU_FMT_S16
Definition sunau.h:32
#define SSTVENC_SUNAU_FMT_S32
Definition sunau.h:33
#define SSTVENC_SUNAU_FMT_S8
Definition sunau.h:31
uint8_t channels
Definition sunau.h:57
FILE * fh
Definition sunau.h:47
uint32_t written_sz
Definition sunau.h:49
uint8_t encoding
Definition sunau.h:55
uint32_t sample_rate
Definition sunau.h:51
uint16_t state
Definition sunau.h:53
int sstvenc_sunau_dec_init_fh(struct sstvenc_sunau *const dec, FILE *fh)
Definition sunau.c:370
static int sstvenc_sunau_read_s8(struct sstvenc_sunau *const dec, size_t *const n_samples, double *samples)
Definition sunau.c:429
static int sstvenc_sunau_read_f64(struct sstvenc_sunau *const dec, size_t *const n_samples, double *samples)
Definition sunau.c:557
static double dbe64toh(uint64_t in)
Definition sunau.c:86
static int sstvenc_sunau_read_f32(struct sstvenc_sunau *const dec, size_t *const n_samples, double *samples)
Definition sunau.c:526
static uint64_t dhtobe64(double in)
Definition sunau.c:75
static int sstvenc_sunau_write_s16(struct sstvenc_sunau *const enc, size_t n_sample, const double *sample)
Definition sunau.c:157
#define SSTVENC_SUNAU_HEADER_SZ
Definition sunau.c:20
static int sstvenc_sunau_read_s16(struct sstvenc_sunau *const dec, size_t *const n_samples, double *samples)
Definition sunau.c:460
static int sstvenc_sunau_enc_write_header(struct sstvenc_sunau *const enc)
Definition sunau.c:99
int sstvenc_sunau_enc_write(struct sstvenc_sunau *const enc, size_t n_samples, const double *samples)
Definition sunau.c:310
int sstvenc_sunau_check(uint32_t sample_rate, uint8_t encoding, uint8_t channels)
Definition sunau.c:250
int sstvenc_sunau_enc_init_fh(struct sstvenc_sunau *const enc, FILE *fh, uint32_t sample_rate, uint8_t encoding, uint8_t channels)
Definition sunau.c:270
static int sstvenc_sunau_read_s32(struct sstvenc_sunau *const dec, size_t *const n_samples, double *samples)
Definition sunau.c:493
#define SSTVENC_SUNAU_MAGIC
Definition sunau.c:15
static int sstvenc_sunau_write_s8(struct sstvenc_sunau *const enc, size_t n_sample, const double *sample)
Definition sunau.c:134
#define SSTVENC_SUNAU_STATE_HEADER
Definition sunau.c:50
int sstvenc_sunau_dec_close(struct sstvenc_sunau *const dec)
Definition sunau.c:606
int sstvenc_sunau_dec_read(struct sstvenc_sunau *const enc, size_t *const n_samples, double *samples)
Definition sunau.c:587
static int sstvenc_sunau_write_s32(struct sstvenc_sunau *const enc, size_t n_sample, const double *sample)
Definition sunau.c:182
static float fbe32toh(uint32_t in)
Definition sunau.c:64
int sstvenc_sunau_enc_init(struct sstvenc_sunau *const enc, const char *path, uint32_t sample_rate, uint8_t encoding, uint8_t channels)
Definition sunau.c:288
static int sstvenc_sunau_write_f32(struct sstvenc_sunau *const enc, size_t n_sample, const double *sample)
Definition sunau.c:207
int sstvenc_sunau_dec_init(struct sstvenc_sunau *const dec, const char *path)
Definition sunau.c:413
static uint32_t fhtobe32(float in)
Definition sunau.c:53
int sstvenc_sunau_enc_close(struct sstvenc_sunau *const enc)
Definition sunau.c:340
static int sstvenc_sunau_write_f64(struct sstvenc_sunau *const enc, size_t n_sample, const double *sample)
Definition sunau.c:230