From ac7bb3539fd613ded10477620654f4c0f73a9430 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 15 Nov 2017 18:56:39 +0100 Subject: Restructure: Move wave from common code to 'libwave' --- .gitignore | 1 + configure.ac | 1 + src/Makefile.am | 2 +- src/amps/Makefile.am | 1 + src/anetz/Makefile.am | 1 + src/bnetz/Makefile.am | 1 + src/cnetz/Makefile.am | 1 + src/common/Makefile.am | 1 - src/common/sender.h | 2 +- src/common/wave.c | 512 ------------------------------------------------ src/common/wave.h | 37 ---- src/jtacs/Makefile.am | 1 + src/libwave/Makefile.am | 6 + src/libwave/wave.c | 512 ++++++++++++++++++++++++++++++++++++++++++++++++ src/libwave/wave.h | 37 ++++ src/nmt/Makefile.am | 1 + src/r2000/Makefile.am | 1 + src/tacs/Makefile.am | 1 + src/test/Makefile.am | 4 + src/tv/Makefile.am | 1 + src/tv/main.c | 2 +- 21 files changed, 573 insertions(+), 553 deletions(-) delete mode 100644 src/common/wave.c delete mode 100644 src/common/wave.h create mode 100644 src/libwave/Makefile.am create mode 100644 src/libwave/wave.c create mode 100644 src/libwave/wave.h diff --git a/.gitignore b/.gitignore index f9fe084..6a78789 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ src/libtimer/libtimer.a src/libsamplerate/libsamplerate.a src/libscrambler/libscrambler.a src/libfilter/libfilter.a +src/libwave/libwave.a src/common/libcommon.a src/common/libmobile.a src/common/libimage.a diff --git a/configure.ac b/configure.ac index 006acc0..d59d068 100644 --- a/configure.ac +++ b/configure.ac @@ -81,6 +81,7 @@ AC_OUTPUT( src/libsamplerate/Makefile src/libscrambler/Makefile src/libfilter/Makefile + src/libwave/Makefile src/common/Makefile src/anetz/Makefile src/bnetz/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 187ab9d..c4670e3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,3 +1,3 @@ AUTOMAKE_OPTIONS = foreign -SUBDIRS = libdtmf libgermanton libtimer libsamplerate libscrambler libfilter common anetz bnetz cnetz nmt amps tacs jtacs r2000 tv test +SUBDIRS = libdtmf libgermanton libtimer libsamplerate libscrambler libfilter libwave common anetz bnetz cnetz nmt amps tacs jtacs r2000 tv test diff --git a/src/amps/Makefile.am b/src/amps/Makefile.am index f37c5ff..c090792 100644 --- a/src/amps/Makefile.am +++ b/src/amps/Makefile.am @@ -31,6 +31,7 @@ amps_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libwave/libwave.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ diff --git a/src/anetz/Makefile.am b/src/anetz/Makefile.am index fcec1fc..3df8889 100644 --- a/src/anetz/Makefile.am +++ b/src/anetz/Makefile.am @@ -17,6 +17,7 @@ anetz_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libwave/libwave.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ diff --git a/src/bnetz/Makefile.am b/src/bnetz/Makefile.am index 505a915..c9f3978 100644 --- a/src/bnetz/Makefile.am +++ b/src/bnetz/Makefile.am @@ -19,6 +19,7 @@ bnetz_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libwave/libwave.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ diff --git a/src/cnetz/Makefile.am b/src/cnetz/Makefile.am index 0550625..39439e6 100644 --- a/src/cnetz/Makefile.am +++ b/src/cnetz/Makefile.am @@ -23,6 +23,7 @@ cnetz_LDADD = \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libscrambler/libscrambler.a \ $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libwave/libwave.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 35b1e5f..ba74b61 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -6,7 +6,6 @@ libcommon_a_SOURCES = \ sample.c \ debug.c \ sound_alsa.c \ - wave.c \ goertzel.c \ jitter.c \ emphasis.c \ diff --git a/src/common/sender.h b/src/common/sender.h index c222fda..4da32cd 100644 --- a/src/common/sender.h +++ b/src/common/sender.h @@ -2,7 +2,7 @@ #ifdef HAVE_SDR #include "sdr.h" #endif -#include "wave.h" +#include "../libwave/wave.h" #include "../libsamplerate/samplerate.h" #include "jitter.h" #include "emphasis.h" diff --git a/src/common/wave.c b/src/common/wave.c deleted file mode 100644 index feae254..0000000 --- a/src/common/wave.c +++ /dev/null @@ -1,512 +0,0 @@ -/* wave recording and playback functions - * - * (C) 2016 by Andreas Eversberg - * All Rights Reserved - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include "sample.h" -#include "wave.h" - -/* NOTE: No locking required for writing and reading buffer pointers, since 'int' is atomic on >=32 bit machines */ - -static void *record_child(void *arg) -{ - wave_rec_t *rec = (wave_rec_t *)arg; - int to_write, to_end, len; - - while (!rec->finish || rec->buffer_writep != rec->buffer_readp) { - /* how much data is in buffer */ - to_write = (rec->buffer_size + rec->buffer_writep - rec->buffer_readp) % rec->buffer_size; - if (to_write == 0) { - usleep(10000); - continue; - } - /* only write up to the end of buffer */ - to_end = rec->buffer_size - rec->buffer_readp; - if (to_end < to_write) - to_write = to_end; - /* write */ - errno = 0; - len = fwrite(rec->buffer + rec->buffer_readp, 1, to_write, rec->fp); - /* quit on error */ - if (len < 0) { -error: - fprintf(stderr, "Failed to write to recording WAVE file! (errno %d)\n", errno); - rec->finish = 1; - return NULL; - } - /* increment read pointer */ - rec->buffer_readp += len; - if (rec->buffer_readp == rec->buffer_size) - rec->buffer_readp = 0; - /* quit on end of file */ - if (len != to_write) - goto error; - } - - return NULL; -} - -static void *playback_child(void *arg) -{ - wave_play_t *play = (wave_play_t *)arg; - int to_read, to_end, len; - - while(!play->finish) { - /* how much space is in buffer */ - to_read = (play->buffer_size + play->buffer_readp - play->buffer_writep - 1) % play->buffer_size; - if (to_read == 0) { - usleep(10000); - continue; - } - /* only read up to the end of buffer */ - to_end = play->buffer_size - play->buffer_writep; - if (to_end < to_read) - to_read = to_end; - /* read */ - len = fread(play->buffer + play->buffer_writep, 1, to_read, play->fp); - /* quit on error */ - if (len < 0) { - fprintf(stderr, "Failed to read from playback WAVE file! (errno %d)\n", errno); - play->finish = 1; - return NULL; - } - /* increment write pointer */ - play->buffer_writep += len; - if (play->buffer_writep == play->buffer_size) - play->buffer_writep = 0; - /* quit on end of file */ - if (len != to_read) { - play->finish = 1; - return NULL; - } - } - - return NULL; -} - -struct fmt { - uint16_t format; /* 1 = pcm, 2 = adpcm */ - uint16_t channels; /* number of channels */ - uint32_t sample_rate; /* sample rate */ - uint32_t data_rate; /* data rate */ - uint16_t bytes_sample; /* bytes per sample (all channels) */ - uint16_t bits_sample; /* bits per sample (one channel) */ -}; - -int wave_create_record(wave_rec_t *rec, const char *filename, int samplerate, int channels, double max_deviation) -{ - /* RIFFxxxxWAVEfmt xxxx(fmt size)dataxxxx... */ - char dummyheader[4 + 4 + 4 + 4 + 4 + sizeof(struct fmt) + 4 + 4]; - int __attribute__((__unused__)) len; - int rc; - - memset(rec, 0, sizeof(*rec)); - rec->samplerate = samplerate; - rec->channels = channels; - rec->max_deviation = max_deviation; - - rec->fp = fopen(filename, "w"); - if (!rec->fp) { - fprintf(stderr, "Failed to open recording file '%s'! (errno %d)\n", filename, errno); - return -errno; - } - - memset(&dummyheader, 0, sizeof(dummyheader)); - len = fwrite(dummyheader, 1, sizeof(dummyheader), rec->fp); - - rec->buffer_size = samplerate * 2 * channels; - rec->buffer = calloc(rec->buffer_size, 1); - if (!rec->buffer) { - fprintf(stderr, "No mem!\n"); - rc = ENOMEM; - goto error; - } - - rc = pthread_create(&rec->tid, NULL, record_child, rec); - if (rc < 0) { - fprintf(stderr, "Failed to create thread to record WAVE file! (errno %d)\n", errno); - goto error; - } - - printf("*** Writing WAVE file to %s.\n", filename); - - return 0; - -error: - if (rec->buffer) { - free(rec->buffer); - rec->buffer = NULL; - } - if (rec->fp) { - fclose(rec->fp); - rec->fp = NULL; - } - return rc; -} - -int wave_create_playback(wave_play_t *play, const char *filename, int samplerate, int channels, double max_deviation) -{ - uint8_t buffer[256]; - struct fmt fmt; - int32_t size, chunk, len; - int gotfmt = 0, gotdata = 0; - int rc = -EINVAL; - - memset(&fmt, 0, sizeof(fmt)); - memset(play, 0, sizeof(*play)); - play->channels = channels; - play->max_deviation = max_deviation; - - play->fp = fopen(filename, "r"); - if (!play->fp) { - fprintf(stderr, "Failed to open playback file '%s'! (errno %d)\n", filename, errno); - return -errno; - } - - len = fread(buffer, 1, 12, play->fp); - if (len != 12) { - fprintf(stderr, "Failed to read RIFF header!\n"); - rc = -EIO; - goto error; - } - if (!!strncmp((char *)buffer, "RIFF", 4)) { - fprintf(stderr, "Missing RIFF header, seems that this is no WAVE file!\n"); - rc = -EINVAL; - goto error; - } - size = buffer[4] + (buffer[5] << 8) + (buffer[6] << 16) + (buffer[7] << 24); - if (!!strncmp((char *)buffer + 8, "WAVE", 4)) { - fprintf(stderr, "Missing WAVE header, seems that this is no WAVE file!\n"); - rc = -EINVAL; - goto error; - } - size -= 4; - while (size) { - if (size < 8) { - fprintf(stderr, "Short read of WAVE file!\n"); - rc = -EINVAL; - goto error; - } - len = fread(buffer, 1, 8, play->fp); - if (len != 8) { - fprintf(stderr, "Failed to read chunk of WAVE file!\n"); - rc = -EIO; - goto error; - } - chunk = buffer[4] + (buffer[5] << 8) + (buffer[6] << 16) + (buffer[7] << 24); - size -= 8 + chunk; - if (size < 0) { - fprintf(stderr, "WAVE error: Chunk '%c%c%c%c' overflows file size!\n", buffer[4], buffer[5], buffer[6], buffer[7]); - rc = -EIO; - goto error; - } - if (!strncmp((char *)buffer, "fmt ", 4)) { - if (chunk < 16 || chunk > (int)sizeof(buffer)) { - fprintf(stderr, "WAVE error: Short or corrupt 'fmt' chunk!\n"); - rc = -EINVAL; - goto error; - } - len = fread(buffer, 1, chunk, play->fp); - fmt.format = buffer[0] + (buffer[1] << 8); - fmt.channels = buffer[2] + (buffer[3] << 8); - fmt.sample_rate = buffer[4] + (buffer[5] << 8) + (buffer[6] << 16) + (buffer[7] << 24); - fmt.data_rate = buffer[8] + (buffer[9] << 8) + (buffer[10] << 16) + (buffer[11] << 24); - fmt.bytes_sample = buffer[12] + (buffer[13] << 8); - fmt.bits_sample = buffer[14] + (buffer[15] << 8); - gotfmt = 1; - } else - if (!strncmp((char *)buffer, "data", 4)) { - if (!gotfmt) { - fprintf(stderr, "WAVE error: 'data' without 'fmt' chunk!\n"); - rc = -EINVAL; - goto error; - } - gotdata = 1; - break; - } else { - while(chunk > (int)sizeof(buffer)) { - len = fread(buffer, 1, sizeof(buffer), play->fp); - chunk -= sizeof(buffer); - } - if (chunk) - len = fread(buffer, 1, chunk, play->fp); - } - } - - if (!gotfmt || !gotdata) { - fprintf(stderr, "WAVE error: Missing 'data' or 'fmt' chunk!\n"); - rc = -EINVAL; - goto error; - } - - if (fmt.format != 1) { - fprintf(stderr, "WAVE error: We support only PCM files!\n"); - rc = -EINVAL; - goto error; - } - if (fmt.channels !=channels) { - fprintf(stderr, "WAVE error: We expect %d cannel(s), but wave file only has %d channel(s)\n", channels, fmt.channels); - rc = -EINVAL; - goto error; - } - if ((int)fmt.sample_rate != samplerate) { - fprintf(stderr, "WAVE error: The WAVE file's sample rate (%d) does not match our sample rate (%d)!\n", fmt.sample_rate, samplerate); - rc = -EINVAL; - goto error; - } - if ((int)fmt.data_rate != 2 * channels * samplerate) { - fprintf(stderr, "WAVE error: The WAVE file's data rate is only %d bytes per second, but we expect %d bytes per second (2 bytes per sample * channels * samplerate)!\n", fmt.data_rate, 2 * channels * samplerate); - rc = -EINVAL; - goto error; - } - if (fmt.bytes_sample != 2 * channels) { - fprintf(stderr, "WAVE error: The WAVE file's bytes per sample is only %d, but we expect %d bytes sample (2 bytes per sample * channels)!\n", fmt.bytes_sample, 2 * channels); - rc = -EINVAL; - goto error; - } - if (fmt.bits_sample != 16) { - fprintf(stderr, "WAVE error: We support only 16 bit files!\n"); - rc = -EINVAL; - goto error; - } - - play->left = chunk / 2 / channels; - - play->buffer_size = samplerate * 2 * channels; - play->buffer = calloc(play->buffer_size, 1); - if (!play->buffer) { - fprintf(stderr, "No mem!\n"); - rc = -ENOMEM; - goto error; - } - - rc = pthread_create(&play->tid, NULL, playback_child, play); - if (rc < 0) { - fprintf(stderr, "Failed to create thread to playback WAVE file! (errno %d)\n", errno); - goto error; - } - - printf("*** Reading WAVE file from %s.\n", filename); - - return 0; - -error: - if (play->buffer) { - free(play->buffer); - play->buffer = NULL; - } - if (play->fp) { - fclose(play->fp); - play->fp = NULL; - } - return rc; -} - -int wave_write(wave_rec_t *rec, sample_t **samples, int length) -{ - double max_deviation = rec->max_deviation; - int32_t value; - int __attribute__((__unused__)) len; - int i, c; - int to_write; - - /* on error, don't write more */ - if (rec->finish) - return 0; - - /* how much space is in buffer */ - to_write = (rec->buffer_size + rec->buffer_readp - rec->buffer_writep - 1) % rec->buffer_size; - to_write /= 2 * rec->channels; - if (to_write < length) - fprintf(stderr, "Record WAVE buffer overflow.\n"); - else - to_write = length; - if (to_write == 0) - return 0; - - for (i = 0; i < to_write; i++) { - for (c = 0; c < rec->channels; c++) { - value = samples[c][i] / max_deviation * 32767.0; - if (value > 32767) - value = 32767; - else if (value < -32767) - value = -32767; - rec->buffer[rec->buffer_writep] = value; - rec->buffer_writep = (rec->buffer_writep + 1) % rec->buffer_size; - rec->buffer[rec->buffer_writep] = value >> 8; - rec->buffer_writep = (rec->buffer_writep + 1) % rec->buffer_size; - } - } - rec->written += to_write; - - return to_write; -} - -int wave_read(wave_play_t *play, sample_t **samples, int length) -{ - double max_deviation = play->max_deviation; - int16_t value; /* must be int16, so assembling bytes work */ - int __attribute__((__unused__)) len; - int i, c; - int to_read; - - /* we have finished */ - if (play->left == 0) { - to_read = 0; -read_empty: - for (i = to_read; i < length; i++) { - for (c = 0; c < play->channels; c++) - samples[c][i] = 0; - } - return length; - } - - /* how much do we read from buffer */ - to_read = (play->buffer_size + play->buffer_writep - play->buffer_readp) % play->buffer_size; - to_read /= 2 * play->channels; - if (to_read > (int)play->left) - to_read = play->left; - if (to_read > length) - to_read = length; - - if (to_read == 0 && play->finish) { - if (play->left) { - printf("*** Finished reading WAVE file. (short read)\n"); - play->left = 0; - } - goto read_empty; - } - - /* read from buffer */ - for (i = 0; i < to_read; i++) { - for (c = 0; c < play->channels; c++) { - value = play->buffer[play->buffer_readp]; - play->buffer_readp = (play->buffer_readp + 1) % play->buffer_size; - value |= play->buffer[play->buffer_readp] << 8; - play->buffer_readp = (play->buffer_readp + 1) % play->buffer_size; - samples[c][i] = (double)value / 32767.0 * max_deviation; - } - } - play->left -= to_read; - - if (!play->left) - printf("*** Finished reading WAVE file.\n"); - - if (to_read < length) - goto read_empty; - - return length; -} - -void wave_destroy_record(wave_rec_t *rec) -{ - uint8_t buffer[256]; - uint32_t size, wsize; - struct fmt fmt; - int __attribute__((__unused__)) len; - - if (!rec->fp) - return; - - /* on error, thread has terminated */ - if (rec->finish) { - fclose(rec->fp); - rec->fp = NULL; - return; - } - - /* finish thread */ - rec->finish = 1; - pthread_join(rec->tid, NULL); - - /* cue */ - fprintf(rec->fp, "cue %c%c%c%c%c%c%c%c", 4, 0, 0, 0, 0,0,0,0); - - /* LIST */ - fprintf(rec->fp, "LIST%c%c%c%cadtl", 4, 0, 0, 0); - - /* go to header */ - fseek(rec->fp, 0, SEEK_SET); - - size = 2 * rec->written * rec->channels; - wsize = 4 + 8 + sizeof(fmt) + 8 + size + 8 + 4 + 8 + 4; - - /* RIFF */ - fprintf(rec->fp, "RIFF%c%c%c%c", wsize & 0xff, (wsize >> 8) & 0xff, (wsize >> 16) & 0xff, wsize >> 24); - - /* WAVE */ - fprintf(rec->fp, "WAVE"); - - /* fmt */ - fprintf(rec->fp, "fmt %c%c%c%c", (uint8_t)sizeof(fmt), 0, 0, 0); - fmt.format = 1; - fmt.channels = rec->channels; - fmt.sample_rate = rec->samplerate; /* samples/sec */ - fmt.data_rate = rec->samplerate * 2 * rec->channels; /* full data rate */ - fmt.bytes_sample = 2 * rec->channels; /* all channels */ - fmt.bits_sample = 16; /* one channel */ - buffer[0] = fmt.format; - buffer[1] = fmt.format >> 8; - buffer[2] = fmt.channels; - buffer[3] = fmt.channels >> 8; - buffer[4] = fmt.sample_rate; - buffer[5] = fmt.sample_rate >> 8; - buffer[6] = fmt.sample_rate >> 16; - buffer[7] = fmt.sample_rate >> 24; - buffer[8] = fmt.data_rate; - buffer[9] = fmt.data_rate >> 8; - buffer[10] = fmt.data_rate >> 16; - buffer[11] = fmt.data_rate >> 24; - buffer[12] = fmt.bytes_sample; - buffer[13] = fmt.bytes_sample >> 8; - buffer[14] = fmt.bits_sample; - buffer[15] = fmt.bits_sample >> 8; - len = fwrite(buffer, 1, sizeof(fmt), rec->fp); - - /* data */ - fprintf(rec->fp, "data%c%c%c%c", size & 0xff, (size >> 8) & 0xff, (size >> 16) & 0xff, size >> 24); - - free(rec->buffer); - rec->buffer = NULL; - fclose(rec->fp); - rec->fp = NULL; - - printf("*** WAVE file written.\n"); -} - -void wave_destroy_playback(wave_play_t *play) -{ - if (!play->fp) - return; - - /* finish thread if not already */ - play->finish = 1; - pthread_join(play->tid, NULL); - - free(play->buffer); - play->buffer = NULL; - fclose(play->fp); - play->fp = NULL; -} - diff --git a/src/common/wave.h b/src/common/wave.h deleted file mode 100644 index b7dc34a..0000000 --- a/src/common/wave.h +++ /dev/null @@ -1,37 +0,0 @@ - -typedef struct wave_rec { - FILE *fp; - int channels; - double max_deviation; - int samplerate; - uint32_t written; /* how much samples written */ - /* thread stuff */ - pthread_t tid; /* file io thread id */ - int finish; /* indicates end of thread */ - uint8_t *buffer; /* buffer to store sample data */ - int buffer_size; /* size of buffer in bytes */ - int buffer_readp; /* read pointer to next byte in buffer */ - int buffer_writep; /* write pointer to next byte in buffer */ -} wave_rec_t; - -typedef struct wave_play { - FILE *fp; - int channels; - double max_deviation; - uint32_t left; /* how much samples left */ - /* thread stuff */ - pthread_t tid; /* file io thread id */ - int finish; /* indicates end of thread */ - uint8_t *buffer; /* buffer to store sample data */ - int buffer_size; /* size of buffer in bytes */ - int buffer_readp; /* read pointer to next byte in buffer */ - int buffer_writep; /* write pointer to next byte in buffer */ -} wave_play_t; - -int wave_create_record(wave_rec_t *rec, const char *filename, int samplerate, int channels, double max_deviation); -int wave_create_playback(wave_play_t *play, const char *filename, int samplerate, int channels, double max_deviation); -int wave_read(wave_play_t *play, sample_t **samples, int length); -int wave_write(wave_rec_t *rec, sample_t **samples, int length); -void wave_destroy_record(wave_rec_t *rec); -void wave_destroy_playback(wave_play_t *play); - diff --git a/src/jtacs/Makefile.am b/src/jtacs/Makefile.am index 651d2b9..cc17138 100644 --- a/src/jtacs/Makefile.am +++ b/src/jtacs/Makefile.am @@ -17,6 +17,7 @@ jtacs_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libwave/libwave.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ diff --git a/src/libwave/Makefile.am b/src/libwave/Makefile.am new file mode 100644 index 0000000..c573515 --- /dev/null +++ b/src/libwave/Makefile.am @@ -0,0 +1,6 @@ +AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) + +noinst_LIBRARIES = libwave.a + +libwave_a_SOURCES = \ + wave.c diff --git a/src/libwave/wave.c b/src/libwave/wave.c new file mode 100644 index 0000000..a358068 --- /dev/null +++ b/src/libwave/wave.c @@ -0,0 +1,512 @@ +/* wave recording and playback functions + * + * (C) 2016 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../common/sample.h" +#include "wave.h" + +/* NOTE: No locking required for writing and reading buffer pointers, since 'int' is atomic on >=32 bit machines */ + +static void *record_child(void *arg) +{ + wave_rec_t *rec = (wave_rec_t *)arg; + int to_write, to_end, len; + + while (!rec->finish || rec->buffer_writep != rec->buffer_readp) { + /* how much data is in buffer */ + to_write = (rec->buffer_size + rec->buffer_writep - rec->buffer_readp) % rec->buffer_size; + if (to_write == 0) { + usleep(10000); + continue; + } + /* only write up to the end of buffer */ + to_end = rec->buffer_size - rec->buffer_readp; + if (to_end < to_write) + to_write = to_end; + /* write */ + errno = 0; + len = fwrite(rec->buffer + rec->buffer_readp, 1, to_write, rec->fp); + /* quit on error */ + if (len < 0) { +error: + fprintf(stderr, "Failed to write to recording WAVE file! (errno %d)\n", errno); + rec->finish = 1; + return NULL; + } + /* increment read pointer */ + rec->buffer_readp += len; + if (rec->buffer_readp == rec->buffer_size) + rec->buffer_readp = 0; + /* quit on end of file */ + if (len != to_write) + goto error; + } + + return NULL; +} + +static void *playback_child(void *arg) +{ + wave_play_t *play = (wave_play_t *)arg; + int to_read, to_end, len; + + while(!play->finish) { + /* how much space is in buffer */ + to_read = (play->buffer_size + play->buffer_readp - play->buffer_writep - 1) % play->buffer_size; + if (to_read == 0) { + usleep(10000); + continue; + } + /* only read up to the end of buffer */ + to_end = play->buffer_size - play->buffer_writep; + if (to_end < to_read) + to_read = to_end; + /* read */ + len = fread(play->buffer + play->buffer_writep, 1, to_read, play->fp); + /* quit on error */ + if (len < 0) { + fprintf(stderr, "Failed to read from playback WAVE file! (errno %d)\n", errno); + play->finish = 1; + return NULL; + } + /* increment write pointer */ + play->buffer_writep += len; + if (play->buffer_writep == play->buffer_size) + play->buffer_writep = 0; + /* quit on end of file */ + if (len != to_read) { + play->finish = 1; + return NULL; + } + } + + return NULL; +} + +struct fmt { + uint16_t format; /* 1 = pcm, 2 = adpcm */ + uint16_t channels; /* number of channels */ + uint32_t sample_rate; /* sample rate */ + uint32_t data_rate; /* data rate */ + uint16_t bytes_sample; /* bytes per sample (all channels) */ + uint16_t bits_sample; /* bits per sample (one channel) */ +}; + +int wave_create_record(wave_rec_t *rec, const char *filename, int samplerate, int channels, double max_deviation) +{ + /* RIFFxxxxWAVEfmt xxxx(fmt size)dataxxxx... */ + char dummyheader[4 + 4 + 4 + 4 + 4 + sizeof(struct fmt) + 4 + 4]; + int __attribute__((__unused__)) len; + int rc; + + memset(rec, 0, sizeof(*rec)); + rec->samplerate = samplerate; + rec->channels = channels; + rec->max_deviation = max_deviation; + + rec->fp = fopen(filename, "w"); + if (!rec->fp) { + fprintf(stderr, "Failed to open recording file '%s'! (errno %d)\n", filename, errno); + return -errno; + } + + memset(&dummyheader, 0, sizeof(dummyheader)); + len = fwrite(dummyheader, 1, sizeof(dummyheader), rec->fp); + + rec->buffer_size = samplerate * 2 * channels; + rec->buffer = calloc(rec->buffer_size, 1); + if (!rec->buffer) { + fprintf(stderr, "No mem!\n"); + rc = ENOMEM; + goto error; + } + + rc = pthread_create(&rec->tid, NULL, record_child, rec); + if (rc < 0) { + fprintf(stderr, "Failed to create thread to record WAVE file! (errno %d)\n", errno); + goto error; + } + + printf("*** Writing WAVE file to %s.\n", filename); + + return 0; + +error: + if (rec->buffer) { + free(rec->buffer); + rec->buffer = NULL; + } + if (rec->fp) { + fclose(rec->fp); + rec->fp = NULL; + } + return rc; +} + +int wave_create_playback(wave_play_t *play, const char *filename, int samplerate, int channels, double max_deviation) +{ + uint8_t buffer[256]; + struct fmt fmt; + int32_t size, chunk, len; + int gotfmt = 0, gotdata = 0; + int rc = -EINVAL; + + memset(&fmt, 0, sizeof(fmt)); + memset(play, 0, sizeof(*play)); + play->channels = channels; + play->max_deviation = max_deviation; + + play->fp = fopen(filename, "r"); + if (!play->fp) { + fprintf(stderr, "Failed to open playback file '%s'! (errno %d)\n", filename, errno); + return -errno; + } + + len = fread(buffer, 1, 12, play->fp); + if (len != 12) { + fprintf(stderr, "Failed to read RIFF header!\n"); + rc = -EIO; + goto error; + } + if (!!strncmp((char *)buffer, "RIFF", 4)) { + fprintf(stderr, "Missing RIFF header, seems that this is no WAVE file!\n"); + rc = -EINVAL; + goto error; + } + size = buffer[4] + (buffer[5] << 8) + (buffer[6] << 16) + (buffer[7] << 24); + if (!!strncmp((char *)buffer + 8, "WAVE", 4)) { + fprintf(stderr, "Missing WAVE header, seems that this is no WAVE file!\n"); + rc = -EINVAL; + goto error; + } + size -= 4; + while (size) { + if (size < 8) { + fprintf(stderr, "Short read of WAVE file!\n"); + rc = -EINVAL; + goto error; + } + len = fread(buffer, 1, 8, play->fp); + if (len != 8) { + fprintf(stderr, "Failed to read chunk of WAVE file!\n"); + rc = -EIO; + goto error; + } + chunk = buffer[4] + (buffer[5] << 8) + (buffer[6] << 16) + (buffer[7] << 24); + size -= 8 + chunk; + if (size < 0) { + fprintf(stderr, "WAVE error: Chunk '%c%c%c%c' overflows file size!\n", buffer[4], buffer[5], buffer[6], buffer[7]); + rc = -EIO; + goto error; + } + if (!strncmp((char *)buffer, "fmt ", 4)) { + if (chunk < 16 || chunk > (int)sizeof(buffer)) { + fprintf(stderr, "WAVE error: Short or corrupt 'fmt' chunk!\n"); + rc = -EINVAL; + goto error; + } + len = fread(buffer, 1, chunk, play->fp); + fmt.format = buffer[0] + (buffer[1] << 8); + fmt.channels = buffer[2] + (buffer[3] << 8); + fmt.sample_rate = buffer[4] + (buffer[5] << 8) + (buffer[6] << 16) + (buffer[7] << 24); + fmt.data_rate = buffer[8] + (buffer[9] << 8) + (buffer[10] << 16) + (buffer[11] << 24); + fmt.bytes_sample = buffer[12] + (buffer[13] << 8); + fmt.bits_sample = buffer[14] + (buffer[15] << 8); + gotfmt = 1; + } else + if (!strncmp((char *)buffer, "data", 4)) { + if (!gotfmt) { + fprintf(stderr, "WAVE error: 'data' without 'fmt' chunk!\n"); + rc = -EINVAL; + goto error; + } + gotdata = 1; + break; + } else { + while(chunk > (int)sizeof(buffer)) { + len = fread(buffer, 1, sizeof(buffer), play->fp); + chunk -= sizeof(buffer); + } + if (chunk) + len = fread(buffer, 1, chunk, play->fp); + } + } + + if (!gotfmt || !gotdata) { + fprintf(stderr, "WAVE error: Missing 'data' or 'fmt' chunk!\n"); + rc = -EINVAL; + goto error; + } + + if (fmt.format != 1) { + fprintf(stderr, "WAVE error: We support only PCM files!\n"); + rc = -EINVAL; + goto error; + } + if (fmt.channels !=channels) { + fprintf(stderr, "WAVE error: We expect %d cannel(s), but wave file only has %d channel(s)\n", channels, fmt.channels); + rc = -EINVAL; + goto error; + } + if ((int)fmt.sample_rate != samplerate) { + fprintf(stderr, "WAVE error: The WAVE file's sample rate (%d) does not match our sample rate (%d)!\n", fmt.sample_rate, samplerate); + rc = -EINVAL; + goto error; + } + if ((int)fmt.data_rate != 2 * channels * samplerate) { + fprintf(stderr, "WAVE error: The WAVE file's data rate is only %d bytes per second, but we expect %d bytes per second (2 bytes per sample * channels * samplerate)!\n", fmt.data_rate, 2 * channels * samplerate); + rc = -EINVAL; + goto error; + } + if (fmt.bytes_sample != 2 * channels) { + fprintf(stderr, "WAVE error: The WAVE file's bytes per sample is only %d, but we expect %d bytes sample (2 bytes per sample * channels)!\n", fmt.bytes_sample, 2 * channels); + rc = -EINVAL; + goto error; + } + if (fmt.bits_sample != 16) { + fprintf(stderr, "WAVE error: We support only 16 bit files!\n"); + rc = -EINVAL; + goto error; + } + + play->left = chunk / 2 / channels; + + play->buffer_size = samplerate * 2 * channels; + play->buffer = calloc(play->buffer_size, 1); + if (!play->buffer) { + fprintf(stderr, "No mem!\n"); + rc = -ENOMEM; + goto error; + } + + rc = pthread_create(&play->tid, NULL, playback_child, play); + if (rc < 0) { + fprintf(stderr, "Failed to create thread to playback WAVE file! (errno %d)\n", errno); + goto error; + } + + printf("*** Reading WAVE file from %s.\n", filename); + + return 0; + +error: + if (play->buffer) { + free(play->buffer); + play->buffer = NULL; + } + if (play->fp) { + fclose(play->fp); + play->fp = NULL; + } + return rc; +} + +int wave_write(wave_rec_t *rec, sample_t **samples, int length) +{ + double max_deviation = rec->max_deviation; + int32_t value; + int __attribute__((__unused__)) len; + int i, c; + int to_write; + + /* on error, don't write more */ + if (rec->finish) + return 0; + + /* how much space is in buffer */ + to_write = (rec->buffer_size + rec->buffer_readp - rec->buffer_writep - 1) % rec->buffer_size; + to_write /= 2 * rec->channels; + if (to_write < length) + fprintf(stderr, "Record WAVE buffer overflow.\n"); + else + to_write = length; + if (to_write == 0) + return 0; + + for (i = 0; i < to_write; i++) { + for (c = 0; c < rec->channels; c++) { + value = samples[c][i] / max_deviation * 32767.0; + if (value > 32767) + value = 32767; + else if (value < -32767) + value = -32767; + rec->buffer[rec->buffer_writep] = value; + rec->buffer_writep = (rec->buffer_writep + 1) % rec->buffer_size; + rec->buffer[rec->buffer_writep] = value >> 8; + rec->buffer_writep = (rec->buffer_writep + 1) % rec->buffer_size; + } + } + rec->written += to_write; + + return to_write; +} + +int wave_read(wave_play_t *play, sample_t **samples, int length) +{ + double max_deviation = play->max_deviation; + int16_t value; /* must be int16, so assembling bytes work */ + int __attribute__((__unused__)) len; + int i, c; + int to_read; + + /* we have finished */ + if (play->left == 0) { + to_read = 0; +read_empty: + for (i = to_read; i < length; i++) { + for (c = 0; c < play->channels; c++) + samples[c][i] = 0; + } + return length; + } + + /* how much do we read from buffer */ + to_read = (play->buffer_size + play->buffer_writep - play->buffer_readp) % play->buffer_size; + to_read /= 2 * play->channels; + if (to_read > (int)play->left) + to_read = play->left; + if (to_read > length) + to_read = length; + + if (to_read == 0 && play->finish) { + if (play->left) { + printf("*** Finished reading WAVE file. (short read)\n"); + play->left = 0; + } + goto read_empty; + } + + /* read from buffer */ + for (i = 0; i < to_read; i++) { + for (c = 0; c < play->channels; c++) { + value = play->buffer[play->buffer_readp]; + play->buffer_readp = (play->buffer_readp + 1) % play->buffer_size; + value |= play->buffer[play->buffer_readp] << 8; + play->buffer_readp = (play->buffer_readp + 1) % play->buffer_size; + samples[c][i] = (double)value / 32767.0 * max_deviation; + } + } + play->left -= to_read; + + if (!play->left) + printf("*** Finished reading WAVE file.\n"); + + if (to_read < length) + goto read_empty; + + return length; +} + +void wave_destroy_record(wave_rec_t *rec) +{ + uint8_t buffer[256]; + uint32_t size, wsize; + struct fmt fmt; + int __attribute__((__unused__)) len; + + if (!rec->fp) + return; + + /* on error, thread has terminated */ + if (rec->finish) { + fclose(rec->fp); + rec->fp = NULL; + return; + } + + /* finish thread */ + rec->finish = 1; + pthread_join(rec->tid, NULL); + + /* cue */ + fprintf(rec->fp, "cue %c%c%c%c%c%c%c%c", 4, 0, 0, 0, 0,0,0,0); + + /* LIST */ + fprintf(rec->fp, "LIST%c%c%c%cadtl", 4, 0, 0, 0); + + /* go to header */ + fseek(rec->fp, 0, SEEK_SET); + + size = 2 * rec->written * rec->channels; + wsize = 4 + 8 + sizeof(fmt) + 8 + size + 8 + 4 + 8 + 4; + + /* RIFF */ + fprintf(rec->fp, "RIFF%c%c%c%c", wsize & 0xff, (wsize >> 8) & 0xff, (wsize >> 16) & 0xff, wsize >> 24); + + /* WAVE */ + fprintf(rec->fp, "WAVE"); + + /* fmt */ + fprintf(rec->fp, "fmt %c%c%c%c", (uint8_t)sizeof(fmt), 0, 0, 0); + fmt.format = 1; + fmt.channels = rec->channels; + fmt.sample_rate = rec->samplerate; /* samples/sec */ + fmt.data_rate = rec->samplerate * 2 * rec->channels; /* full data rate */ + fmt.bytes_sample = 2 * rec->channels; /* all channels */ + fmt.bits_sample = 16; /* one channel */ + buffer[0] = fmt.format; + buffer[1] = fmt.format >> 8; + buffer[2] = fmt.channels; + buffer[3] = fmt.channels >> 8; + buffer[4] = fmt.sample_rate; + buffer[5] = fmt.sample_rate >> 8; + buffer[6] = fmt.sample_rate >> 16; + buffer[7] = fmt.sample_rate >> 24; + buffer[8] = fmt.data_rate; + buffer[9] = fmt.data_rate >> 8; + buffer[10] = fmt.data_rate >> 16; + buffer[11] = fmt.data_rate >> 24; + buffer[12] = fmt.bytes_sample; + buffer[13] = fmt.bytes_sample >> 8; + buffer[14] = fmt.bits_sample; + buffer[15] = fmt.bits_sample >> 8; + len = fwrite(buffer, 1, sizeof(fmt), rec->fp); + + /* data */ + fprintf(rec->fp, "data%c%c%c%c", size & 0xff, (size >> 8) & 0xff, (size >> 16) & 0xff, size >> 24); + + free(rec->buffer); + rec->buffer = NULL; + fclose(rec->fp); + rec->fp = NULL; + + printf("*** WAVE file written.\n"); +} + +void wave_destroy_playback(wave_play_t *play) +{ + if (!play->fp) + return; + + /* finish thread if not already */ + play->finish = 1; + pthread_join(play->tid, NULL); + + free(play->buffer); + play->buffer = NULL; + fclose(play->fp); + play->fp = NULL; +} + diff --git a/src/libwave/wave.h b/src/libwave/wave.h new file mode 100644 index 0000000..b7dc34a --- /dev/null +++ b/src/libwave/wave.h @@ -0,0 +1,37 @@ + +typedef struct wave_rec { + FILE *fp; + int channels; + double max_deviation; + int samplerate; + uint32_t written; /* how much samples written */ + /* thread stuff */ + pthread_t tid; /* file io thread id */ + int finish; /* indicates end of thread */ + uint8_t *buffer; /* buffer to store sample data */ + int buffer_size; /* size of buffer in bytes */ + int buffer_readp; /* read pointer to next byte in buffer */ + int buffer_writep; /* write pointer to next byte in buffer */ +} wave_rec_t; + +typedef struct wave_play { + FILE *fp; + int channels; + double max_deviation; + uint32_t left; /* how much samples left */ + /* thread stuff */ + pthread_t tid; /* file io thread id */ + int finish; /* indicates end of thread */ + uint8_t *buffer; /* buffer to store sample data */ + int buffer_size; /* size of buffer in bytes */ + int buffer_readp; /* read pointer to next byte in buffer */ + int buffer_writep; /* write pointer to next byte in buffer */ +} wave_play_t; + +int wave_create_record(wave_rec_t *rec, const char *filename, int samplerate, int channels, double max_deviation); +int wave_create_playback(wave_play_t *play, const char *filename, int samplerate, int channels, double max_deviation); +int wave_read(wave_play_t *play, sample_t **samples, int length); +int wave_write(wave_rec_t *rec, sample_t **samples, int length); +void wave_destroy_record(wave_rec_t *rec); +void wave_destroy_playback(wave_play_t *play); + diff --git a/src/nmt/Makefile.am b/src/nmt/Makefile.am index ddaabd9..041d9b3 100644 --- a/src/nmt/Makefile.am +++ b/src/nmt/Makefile.am @@ -28,6 +28,7 @@ nmt_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libwave/libwave.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ diff --git a/src/r2000/Makefile.am b/src/r2000/Makefile.am index a5e1585..8352e9f 100644 --- a/src/r2000/Makefile.am +++ b/src/r2000/Makefile.am @@ -17,6 +17,7 @@ radiocom2000_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libwave/libwave.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ diff --git a/src/tacs/Makefile.am b/src/tacs/Makefile.am index 1b392d3..2221706 100644 --- a/src/tacs/Makefile.am +++ b/src/tacs/Makefile.am @@ -18,6 +18,7 @@ tacs_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libwave/libwave.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 37632cf..38bb717 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -18,6 +18,7 @@ test_filter_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libwave/libwave.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ @@ -43,6 +44,7 @@ test_emphasis_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libwave/libwave.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ @@ -58,6 +60,7 @@ test_dms_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libwave/libwave.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ @@ -73,6 +76,7 @@ test_sms_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libwave/libwave.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ diff --git a/src/tv/Makefile.am b/src/tv/Makefile.am index 1b984d8..b4c068d 100644 --- a/src/tv/Makefile.am +++ b/src/tv/Makefile.am @@ -18,6 +18,7 @@ osmotv_LDADD = \ $(top_builddir)/src/common/libcommon.a \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libfilter/libfilter.a \ + $(top_builddir)/src/libwave/libwave.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ diff --git a/src/tv/main.c b/src/tv/main.c index 115d97d..7440d0b 100644 --- a/src/tv/main.c +++ b/src/tv/main.c @@ -30,7 +30,7 @@ enum paging_signal; #include "../common/sample.h" #include "../libfilter/iir_filter.h" #include "../common/fm_modulation.h" -#include "../common/wave.h" +#include "../libwave/wave.h" #include "../common/img.h" #include "../common/debug.h" #ifdef HAVE_SDR -- cgit v1.2.3