view pcm/pcm_tx.c @ 28:fa341317c844

pcm: fix bug in play-stop command
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 24 Jun 2024 20:38:58 +0000
parents 27ca01bb5b11
children
line wrap: on
line source

/*
 * In this module we implement PCM Tx toward the TRAU.
 */

#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <ctype.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <osmocom/core/select.h>

#include "globals.h"

static const uint8_t dmw_alaw[8] =
	{0x34, 0x21, 0x21, 0x34, 0xB4, 0xA1, 0xA1, 0xB4};

static uint8_t pcm_fill_octet = 0x54;
static bool dmw_active;
static uint8_t *play_buffer;
static unsigned play_buf_nframes, play_buf_ptr;

static void fill_with_dmw(uint8_t *buf)
{
	unsigned n;

	for (n = 0; n < 20; n++) {
		memcpy(buf, dmw_alaw, 8);
		buf += 8;
	}
}

static void fill_with_play(uint8_t *outbuf)
{
	memcpy(outbuf, play_buffer + play_buf_ptr * 160, 160);
	play_buf_ptr++;
	if (play_buf_ptr < play_buf_nframes)
		return;
	free(play_buffer);
	play_buffer = NULL;
	printf("file play finished\n");
}

void transmit_pcm_20ms(void)
{
	uint8_t buf[160];

	if (play_buffer)
		fill_with_play(buf);
	else if (dmw_active)
		fill_with_dmw(buf);
	else
		memset(buf, pcm_fill_octet, 160);
	write(ts_fd, buf, 160);
}

void cmd_pcm_fill(int argc, char **argv)
{
	u_long val;
	char *cp;

	if (argc != 2) {
		printf("error: pcm-fill command needs 1 argument\n");
		return;
	}
	if (!isxdigit(argv[1][0])) {
inv_arg:	printf("error: argument is not a valid hex octet\n");
		return;
	}
	val = strtoul(argv[1], &cp, 16);
	if (*cp)
		goto inv_arg;
	if (val > 0xFF)
		goto inv_arg;
	pcm_fill_octet = val;
}

void cmd_dmw_on(int argc, char **argv)
{
	dmw_active = true;
}

void cmd_dmw_off(int argc, char **argv)
{
	dmw_active = false;
}

void cmd_play_file(int argc, char **argv)
{
	int fd;
	struct stat st;

	if (argc != 2) {
		printf("error: play command needs 1 argument\n");
		return;
	}
	if (play_buffer) {
		printf("error: file play already in progress\n");
		return;
	}
	fd = open(argv[1], O_RDONLY);
	if (fd < 0) {
		perror(argv[1]);
		return;
	}
	fstat(fd, &st);
	if (!S_ISREG(st.st_mode)) {
		close(fd);
		fprintf(stderr, "error: %s is not a regular file\n", argv[1]);
		return;
	}
	if (!st.st_size) {
		close(fd);
		fprintf(stderr, "error: %s is an empty file\n", argv[1]);
		return;
	}
	if (st.st_size % 160) {
		close(fd);
		fprintf(stderr,
			"error: size of %s is not a multiple of 160 bytes\n",
			argv[1]);
		return;
	}
	play_buffer = malloc(st.st_size);
	if (!play_buffer) {
		close(fd);
		fprintf(stderr, "unable to malloc buffer for %s\n", argv[1]);
		return;
	}
	read(fd, play_buffer, st.st_size);
	close(fd);
	play_buf_nframes = st.st_size / 160;
	play_buf_ptr = 0;
}

void cmd_play_offset(int argc, char **argv)
{
	int fd;
	struct stat st;
	unsigned offset, pre_offset;

	if (argc != 3) {
		printf("error: play-offset command needs 2 arguments\n");
		return;
	}
	if (play_buffer) {
		printf("error: file play already in progress\n");
		return;
	}
	offset = strtoul(argv[2], NULL, 0);
	if (offset < 1 || offset > 159) {
		printf("error: offset argument out of range\n");
		return;
	}
	fd = open(argv[1], O_RDONLY);
	if (fd < 0) {
		perror(argv[1]);
		return;
	}
	fstat(fd, &st);
	if (!S_ISREG(st.st_mode)) {
		close(fd);
		fprintf(stderr, "error: %s is not a regular file\n", argv[1]);
		return;
	}
	if (!st.st_size) {
		close(fd);
		fprintf(stderr, "error: %s is an empty file\n", argv[1]);
		return;
	}
	if (st.st_size % 160) {
		close(fd);
		fprintf(stderr,
			"error: size of %s is not a multiple of 160 bytes\n",
			argv[1]);
		return;
	}
	play_buffer = malloc(st.st_size + 160);
	if (!play_buffer) {
		close(fd);
		fprintf(stderr, "unable to malloc buffer for %s\n", argv[1]);
		return;
	}
	pre_offset = 160 - offset;
	memset(play_buffer, pcm_fill_octet, pre_offset);
	read(fd, play_buffer + pre_offset, st.st_size);
	close(fd);
	memset(play_buffer + pre_offset + st.st_size, pcm_fill_octet, offset);
	play_buf_nframes = st.st_size / 160 + 1;
	play_buf_ptr = 0;
}

void cmd_play_stop(int argc, char **argv)
{
	if (!play_buffer) {
		printf("error: no file play in progress\n");
		return;
	}
	free(play_buffer);
	play_buffer = NULL;
}