/***************************************************************************
    mmusbrecord  -  A simple command line .wav file recorder

    begin                : July 26, 2003
    copyright            : (C) 2003 by Michael Minn
    email                : See www.michaelminn.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of version 2 of the GNU General Public License as 
    published by the Free Software Foundation (see COPYING or www.gnu.org).

***************************************************************************/

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/cdrom.h> // /usr/include/linux/cdrom.h
#include <linux/soundcard.h>
#include "mmusbaudio.h"
#include "mmuwave_file_header.h"

static int descriptor = 0;
static mmuwave_file_header header;

void terminate(int signal)
{
	write(1, "Terminating\n", 12);
	if (descriptor > 0)
		header.write_header(descriptor);
	exit(0);
}

void print_syntax()
{
	fprintf(stderr, "SYNTAX: mmusbrecord [ -d dsp_device ] [ -r sample_rate] [ -b bit_resolution ] output_file\n");
	exit(0);
}

int main(int argc, char **argv)
{
	const char *banner = "\x1b[2J\x1b[Hmmusbrecord v2005.12.09 (c) 2005 by Michael Minn\n";
	const char *device_name = "/dev/dsp";
	char *file_name = NULL;

	write(1, banner, strlen(banner));

	for (int scan = 1; scan < argc; ++scan)
		if (strcasecmp(argv[scan], "-d") == 0)
		{
			if (argc < (scan + 1))
				print_syntax();
			++scan;
			device_name = argv[scan];
		}

		else if (strcasecmp(argv[scan], "-r") == 0)
		{
			if (argc < (scan + 1))
				print_syntax();
			++scan;
			header.sample_rate = atoi(argv[scan]);
			header.bytes_per_second = header.sample_rate * header.channel_numbers * (header.bits_per_sample / 8);
		}

		else if (strcasecmp(argv[scan], "-b") == 0)
		{
			if (argc < (scan + 1))
				print_syntax();
			++scan;
			header.bits_per_sample = atoi(argv[scan]);
			header.bytes_per_second = header.sample_rate * header.channel_numbers * (header.bits_per_sample / 8);
		}

		else if (argv[scan][0] == '-')
			print_syntax();

		else
			file_name = argv[scan];

	if (!file_name)
		print_syntax();

	int device = open(device_name, O_RDONLY);
	if (device <= 0)
		return fprintf(stderr, "Failure opening sound device %s: %s\n", device_name, strerror(errno));

	int audio_format = AFMT_S16_LE;
	if (header.bits_per_sample == 8)
		audio_format = AFMT_U8;
	else if (header.bits_per_sample == 24)
		audio_format = AFMT_S24_LE;
	else if (header.bits_per_sample != 16)
		return fprintf(stderr, "%d bits per sample unsupported\n", header.bits_per_sample);
	
	if (ioctl(device, SNDCTL_DSP_SETFMT, &audio_format) < 0)
		return fprintf(stderr, "Failure setting device %s to %d bits per sample: %s", 
			device_name, header.bits_per_sample, strerror(errno));

	int channels = header.channel_numbers;
	if (ioctl(device, SNDCTL_DSP_CHANNELS, &channels) < 0)
		return fprintf(stderr, "Failure setting device %s to %d channels: %s", 
			device_name, channels, strerror(errno));

	if (ioctl(device, SNDCTL_DSP_SPEED, &header.sample_rate) < 0)
		return fprintf(stderr, "Failure setting device %s to %d samples/sec: %s", 
			device_name, header.sample_rate, strerror(errno));

	if ((descriptor = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) <= 0)
		return fprintf(stderr, "Failure opening %s: %s\n", file_name, strerror(errno));

	if (write(descriptor, &header, sizeof(header)) < (int) sizeof(header))
		return fprintf(stderr, "Failure writing .wav file header to %s: %s\n", file_name, strerror(errno));

	char text_buffer[256];
	sprintf(text_buffer, "Recording %.128s. Press ctrl-C to stop\n", file_name);
	write(1, text_buffer, strlen(text_buffer));

	signal(SIGINT, terminate);

	int status = 0;
	int byte_count = 0;
	int buffer_bytes = header.bytes_per_second / 4;
	char buffer[buffer_bytes];

	while ((status = read(device, buffer, buffer_bytes)) > 0)
		if ((status = write(descriptor, buffer, status)) < 0)
			return fprintf(stderr, "Failure writing to file %s: %s\n", file_name, strerror(errno));
		else
		{
			int seconds = (byte_count / header.bytes_per_second) % 60;
			int minutes = (byte_count / header.bytes_per_second) / 60;
			sprintf(text_buffer, "\x1b[3H%03d:%02d", minutes, seconds);

			for (int channel = 0; channel < header.channel_numbers; ++channel)
			{
				int peak = 0; // 0 - 15
				int bytes_per_sample = header.bits_per_sample * header.channel_numbers / 8;
				if (header.bits_per_sample == 8)
				{
					for (int x = channel; x < buffer_bytes; x += bytes_per_sample)
						if (peak < buffer[x])
							peak = buffer[x];
						else if (peak < -buffer[x])
							peak = -buffer[x];
					peak >>= 4;
				}

				else if (header.bits_per_sample == 24)
				{
					for (int x = channel * 3; x < buffer_bytes; x += bytes_per_sample)
						if (peak < *((short*)&buffer[x]))
							peak = *((short*)&buffer[x]);
						else if (peak < -*((short*)&buffer[x]))
							peak = -*((short*)&buffer[x]);
					peak >>= 11;
				}

				else // assume 16-bit
				{
					for (int x = channel * 2; x < buffer_bytes; x += bytes_per_sample)
						if (peak < *((short*)&buffer[x]))
							peak = *((short*)&buffer[x]);
						else if (peak < -*((short*)&buffer[x]))
							peak = -*((short*)&buffer[x]);
					peak >>= 11;
				}

				sprintf(text_buffer + strlen(text_buffer), " |%.*s%.*s|",
					peak, "****************", 16 - peak, "----------------");
			}

			write(1, text_buffer, strlen(text_buffer));
			byte_count += buffer_bytes;
		}

	if (status <= 0)
			return fprintf(stderr, "Failure reading from device %d %s: %s\n", device, device_name, strerror(errno));

	return 0;
};
