aboutsummaryrefslogtreecommitdiffstats
path: root/src/send.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@member.fsf.org>2015-12-19 22:03:01 +0100
committerMattias Andrée <maandree@member.fsf.org>2015-12-19 22:03:01 +0100
commit6944594bfabe9fd3eee7e53381048b491acf6dbb (patch)
tree6c776e0db51fa9d88d74524daf87916a5e6c117b /src/send.c
parenta nice readme (diff)
downloadfodtmf-6944594bfabe9fd3eee7e53381048b491acf6dbb.tar.gz
fodtmf-6944594bfabe9fd3eee7e53381048b491acf6dbb.tar.bz2
fodtmf-6944594bfabe9fd3eee7e53381048b491acf6dbb.tar.xz
m
Signed-off-by: Mattias Andrée <maandree@member.fsf.org>
Diffstat (limited to 'src/send.c')
-rw-r--r--src/send.c503
1 files changed, 0 insertions, 503 deletions
diff --git a/src/send.c b/src/send.c
deleted file mode 100644
index b841e1f..0000000
--- a/src/send.c
+++ /dev/null
@@ -1,503 +0,0 @@
-/**
- * Copyright © 2015 Mattias Andrée (maandree@member.fsf.org)
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-#include "common.h"
-
-#include <stdio.h>
-#include <stdint.h>
-#include <signal.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <alloca.h>
-#include <getopt.h>
-#include <math.h> /* -lm */
-#include <alsa/asoundlib.h> /* dep: alsa-lib (pkg-config alsa) */
-
-
-
-#define UTYPE uint32_t
-#define SMIN INT32_MIN
-#define SMAX INT32_MAX
-#define FORMAT SND_PCM_FORMAT_U32
-
-#define SAMPLE_RATE 52000 /* Hz */
-#define DURATION 100 /* ms */ /* 100 ms → 10 Bd → 5 B/s */
-#define LATENCY 100000 /* µs */
-
-
-
-/**
- * Number of samples per tone.
- */
-#define N (SAMPLE_RATE / 1000 * DURATION)
-
-
-
-/**
- * The process's name.
- */
-static const char* argv0;
-
-/**
- * Audio descriptor.
- */
-static snd_pcm_t* sound_handle = NULL;
-
-/**
- * Audio playback buffer for each nibble.
- */
-static UTYPE buffers[16][N];
-
-/**
- * The number of parity tones in the hamming code.
- */
-static int parity = 3;
-
-/**
- * Whether to add an additional parity tone (of all
- * data tones) in addition to those numerated by `parity`.
- */
-static int use_extra_parity = 0;
-
-/**
- * Whether to add redundancy frequencies in the tones.
- */
-static int use_redundant_freq = 0;
-
-/**
- * Frequencies multiple for the redundant frequencies.
- */
-static double redundant_freq_mul = 0;
-
-/**
- * Data buffer used for error correcting code support.
- */
-static int* data;
-
-/**
- * Code buffer used for error correcting code support.
- */
-static int* code;
-
-/**
- * The number elements that can be stored in `data`.
- */
-static int data_n;
-
-/**
- * The number elements that can be stored in `code`.
- */
-static int code_n;
-
-/**
- * File descriptor to write the audio to (with any metadata)
- * rather than sending it to the audio subsystem.
- *
- * This is available for developers of the receiver.
- * It is not intend to be used for anything else.
- */
-static int output_fd = -1;
-
-
-
-/**
- * Use to accept signals, without doing anything
- * about them, just be interrupted.
- *
- * @param signo The received signal.
- */
-static void signoop(int signo)
-{
- (void) signo;
-}
-
-
-/**
- * Get the tones used to transmit a nibble.
- *
- * @param nibble The nibble.
- * @param low Output parameter for the lower frequency.
- * @param high Output parameter for the higher frequency.
- */
-static void get_freq(int nibble, int* low, int* high)
-{
- static const int LOW[] = { 697, 770, 852, 941 };
- static const int HIGH[] = { 1209, 1336, 1477, 1663 };
-
- *low = LOW[(nibble >> 0) & 0x03];
- *high = HIGH[(nibble >> 2) & 0x03];
-}
-
-
-/**
- * Initialise to before for each nibble.
- */
-static void init_buffers(void)
-{
-#define GENERATE_TONE(tone) sin(2 * M_PI * ((double)i / (SAMPLE_RATE / (tone))))
-
- size_t i;
- UTYPE* buffer;
- int j, low, high;
-
- for (j = 0; j < 16; j++)
- {
- buffer = buffers[j];
- get_freq(j, &low, &high);
-
- if (use_redundant_freq)
- for (i = 0; i < N; i++)
- buffer[i] = (GENERATE_TONE(low) +
- GENERATE_TONE(high) +
- GENERATE_TONE(low * redundant_freq_mul) +
- GENERATE_TONE(high * redundant_freq_mul)) *
- (SMAX / 4) - SMIN;
- else
- for (i = 0; i < N; i++)
- buffer[i] = (GENERATE_TONE(low) + GENERATE_TONE(high)) *
- (SMAX / 2) - SMIN;
- }
-}
-
-
-/**
- * Transmit a nibble.
- *
- * @param n The nibble.
- * @return 0 on success, -1 on error.
- */
-static int send_nibble(int n)
-{
- UTYPE* buffer = buffers[n];
- snd_pcm_sframes_t frames;
- int r;
-
- if (output_fd >= 0)
- {
- char* buf = (char*)buffer;
- size_t ptr = 0;
- ssize_t wrote;
- while (ptr < N)
- {
- wrote = write(output_fd, buf + ptr, N - ptr);
- if (wrote < 0)
- return -1;
- ptr += (size_t)write;
- }
- return 0;
- }
-
- r = frames = snd_pcm_writei(sound_handle, buffer, N);
- if (frames < 0)
- r = frames = snd_pcm_recover(sound_handle, r, 0 /* do not print error reason? */);
- if (r < 0)
- {
- fprintf(stderr, "%s: snd_pcm_writei: %s\n", argv0, snd_strerror(r));
- errno = 0;
- return -1;
- }
- if ((r > 0) && ((size_t)r < N))
- printf("%s: short write: expected %zu, wrote %zu\n", argv0, N, (size_t)frames);
-
- return 0;
-}
-
-
-/**
- * Transmit a byte.
- *
- * @param c The byte.
- * @return 0 on success, -1 on error.
- */
-static int send_byte(int c)
-{
-#ifdef DEBUG
- fprintf(stderr, "%s: sending byte: %i\n", argv0, c);
-#endif
- if (send_nibble((c >> 0) & 0x0F)) return -1;
- if (send_nibble((c >> 4) & 0x0F)) return -1;
- return 0;
-}
-
-
-/**
- * Transmit a byte with error correcting code.
- *
- * This function uses a buffer so that it can
- * create an error correcting code. The bytes
- * are not transmitted until this buffer has
- * been filled.
- *
- * @param c The byte, the negative of that byte (intended
- * only for `CHAR_END` and `CHAR_CANCEL`) to fill
- * the remained of the buffer with the byte.
- * Note that if a negative value is used, it is
- * no necessary that anything will happen.
- * @return 0 on success, -1 on error.
- */
-static int send_byte_with_ecc(int c)
-{
- static int ptr = 0;
- int i, j, d, p;
-
- if (parity < 2)
- {
- if (c < 0)
- return 0;
- if (send_byte(c))
- return -1;
- if (parity)
- if (send_byte(c))
- return -1;
- if (use_extra_parity)
- if (send_byte(c))
- return -1;
- return 0;
- }
-
- /* Fill buffer. */
- if (c < 0)
- {
- if (ptr > 0)
- while (ptr < data_n)
- data[ptr++] = -c;
- }
- else
- data[ptr++] = c;
-
- /* Is it full? */
- if (ptr < data_n)
- return 0;
- ptr = 0;
-
- /* Hamming code. */
- memset(code, 0, code_n * sizeof(*code));
- for (i = 1, j = 0; i <= (1 << parity) - 1; i++)
- {
- if ((i & -i) == i)
- continue;
- for (d = i, p = 0; d; d >>= 1, p++)
- if (d & 1)
- code[(1 << p) - 1] ^= data[j];
- code[i - 1] = data[j];
- j++;
- }
- if (use_extra_parity)
- for (i = 0; i < data_n; i++)
- code[(1 << parity) - 1] ^= data[i];
-
- /* Transmit. */
- for (i = 0; i < code_n; i++)
- if (send_byte(code[i]))
- return -1;
-
- return 0;
-}
-
-
-/**
- * Transmit a chunk of bytes.
- *
- * @param buf The chunk to transmit.
- * @param n The number of bytes in the chunk.
- * @return 0 on success, -1 on failure.
- */
-static int send_chunk(char* buf, size_t n)
-{
- size_t i;
- int c;
-
- for (i = 0; i < n; i++)
- {
- c = buf[i];
- if ((c == CHAR_ESCAPE) || (c == CHAR_CANCEL) || (c == CHAR_END))
- if (send_byte_with_ecc(CHAR_ESCAPE))
- goto qfile;
- if (send_byte_with_ecc(c))
- goto qfile;
- }
-
- return 0;
- qfile:
- errno = 0;
- fail:
- return -1;
-}
-
-
-/**
- * Read all input from stdin and transmit it as it is being read.
- *
- * @return 0 on success, -1 on failure.
- */
-static int send_file(void)
-{
- char buf[1024];
- ssize_t n;
-
- for (;;)
- {
- n = read(STDIN_FILENO, buf, sizeof(buf));
- if (n < 0) goto fail;
- if (n == 0) break;
- if (send_chunk(buf, (size_t)n))
- goto fail;
- }
-
- return 0;
- fail:
- return -1;
-}
-
-
-/**
- * Read all input from stdin, and then transmit it.
- *
- * @return 0 on success, -1 on failure.
- */
-static int send_term(void)
-{
- char* buf = NULL;
- size_t size = 0;
- size_t ptr = 0;
- ssize_t n;
- void* new;
- int saved_errno;
-
- for (;;)
- {
- if (ptr == size)
- {
- size = size ? (size << 1) : 128;
- new = realloc(buf, size);
- if (new == NULL)
- goto fail;
- buf = new;
- }
- n = read(STDIN_FILENO, buf + ptr, size - ptr);
- if (n < 0) goto fail;
- if (n == 0) break;
- ptr += (size_t)n;
- }
- if (send_chunk(buf, ptr))
- goto fail;
-
- return 0;
- fail:
- saved_errno = errno;
- free(buf);
- errno = saved_errno;
- return -1;
-}
-
-
-/**
- * Transmit a file over audio.
- *
- * @param argc The number of elements in `argv`.
- * @param argv Command line arguments.
- * @return 0 on success, 1 on failure.
- */
-int main(int argc, char* argv[])
-{
- struct sigaction act;
- int r, rc = 1;
-
- /* Parse command line. */
- argv0 = argc ? argv[0] : "";
- for (;;)
- {
- r = getopt (argc, argv, "f:pr:o:");
- if (r == -1)
- break;
- else if (r == 'f') use_redundant_freq = 1, redundant_freq_mul = atof(optarg);
- else if (r == 'p') use_extra_parity = 1;
- else if (r == 'r') parity = atoi(optarg);
- else if (r == 'o') output_fd = atoi(optarg);
- else if (r != '?')
- abort();
- }
-
- /* Set up signal handling. */
- siginterrupt(SIGTERM, 1);
- siginterrupt(SIGQUIT, 1);
- siginterrupt(SIGINT, 1);
- siginterrupt(SIGHUP, 1);
- sigemptyset(&(act.sa_mask));
- act.sa_handler = signoop;
- act.sa_flags = 0;
- sigaction(SIGTERM, &act, NULL);
- sigaction(SIGQUIT, &act, NULL);
- sigaction(SIGHUP, &act, NULL);
- sigprocmask(SIG_SETMASK, &(act.sa_mask), NULL);
-
- /* Generate the tones to play. */
- init_buffers();
- /* Generate buffers for error correcting code. */
- data_n = (1 << parity) - parity - 1;
- code_n = (1 << parity) - 1 + use_extra_parity;
- data = alloca(data_n * sizeof(*data));
- code = alloca(code_n * sizeof(*code));
-
- /* Set up audio. */
- if (output_fd >= 0)
- goto no_audio;
- r = snd_pcm_open(&sound_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
- if (r < 0)
- return fprintf(stderr, "%s: snd_pcm_open: %s\n", *argv, snd_strerror(r)), 1;
- if (sound_handle == NULL)
- perror("snd_pcm_open");
- /* Configure audio. */
- r = snd_pcm_set_params(sound_handle, FORMAT, SND_PCM_ACCESS_RW_INTERLEAVED, 1 /* channels */,
- SAMPLE_RATE, 1 /* allow resampling? */, LATENCY);
- if (r < 0)
- return fprintf(stderr, "%s: snd_pcm_set_params: %s\n", *argv, snd_strerror(r)), 1;
- no_audio:
-
- /* Send message. */
- if (isatty(STDIN_FILENO))
- {
- if (send_term())
- goto fail;
- }
- else
- if (send_file())
- goto fail;
-
- /* Mark end of transmission. */
- if (send_byte_with_ecc(CHAR_END)) goto cleanup;
- if (send_byte_with_ecc(-CHAR_END)) goto cleanup;
-
- /* Done! */
- rc = 0;
- goto cleanup;
- fail:
- if (errno)
- perror(argv0);
- cleanup:
- if (output_fd >= 0)
- snd_pcm_close(sound_handle);
- /* Mark aborted transmission. */
- if (rc)
- {
- send_byte_with_ecc(CHAR_CANCEL);
- send_byte_with_ecc(-CHAR_CANCEL);
- }
- return rc;
-}
-