/*

	MakeUEF by Thomas Harte 2000

	Distributed under the GPL - see 'Copying' for details

	Contains the code for reading from the sound card on various platforms

*/
#include "defines.h"
#include <malloc.h>

#define BUF_SIZE 4096

#if TARGET_ALLEGRO || TARGET_OSS
#include <signal.h>

bool quit;

void set_quit(int signum)
{
	quit = true;
}

int signals[] =
{
	SIGINT,
	SIGTERM,
	SIGKILL,
	-1
};

bool C_sndcard::Finished(void)
{
	return quit;
}

#endif

#ifdef TARGET_OSS

#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/soundcard.h>

int audio_fd;
unsigned char audio_buffer[BUF_SIZE];

void C_sndcard::NewBlock(void)
{
	while(!(bufsize = read(audio_fd, data, BUF_SIZE)));
	pos = 0;
}

bool C_sndcard::Open(char *name)
{
	int speed = 22050;
	int *sigs;

	quit = false;

	if((audio_fd = open("/dev/dsp", O_RDONLY, 0)) == -1)
	{
		error = "could not open /dev/dsp";
		return false;
	}

	if(ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed) == -1)
	{
		error = "unable to select a suitable sampling frequency";
		return false;
	}

	freq = speed;

	if(speed < MIN_WAVE_FREQ)
	{
		error = "selected sampling frequency too low";
		return false;
	}

	data = (char *)malloc(BUF_SIZE);
	NewBlock();

	sigs = signals;
	while(*sigs != -1)
	{
		signal(*sigs, set_quit);
		sigs++;
	}

	return true;
}

bool C_sndcard::Close(void)
{
	free(data);
	close(audio_fd);
}

#endif

#ifdef TARGET_ALLEGRO

#include <allegro.h>

void C_sndcard::NewBlock(void)
{
	while(read_sound_input(data) < 1);
	pos = 0;
}

bool C_sndcard::Open(char *name)
{
	int *sigs;

	allegro_init();
	install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL);
	install_sound_input(DIGI_AUTODETECT, MIDI_NONE);

	freq = get_sound_input_cap_rate(8, 0);

	if(get_sound_input_cap_parm(freq, 8, 0) < 1)
	{
		error = "Could not open a mono 8bit recording stream";
		return false;
	}

	set_sound_input_source(SOUND_INPUT_LINE);
	bufsize = start_sound_input(freq, 8, 0);
	data = (char *)malloc(bufsize);
	NewBlock();

	sigs = signals;
	while(*sigs != -1)
	{
		signal(*sigs, set_quit);
		sigs++;
	}

	return true;
}

bool C_sndcard::Close(void)
{
	free(data);
}
#endif

#ifdef TARGET_WIN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <mmsystem.h>
#include <conio.h>

HWAVEIN dev;
volatile WAVEHDR hdr1, hdr2, *chdr;
char *inbuf1, *inbuf2;
bool quit;

void C_sndcard::NewBlock(void)
{
	while(!(chdr->dwFlags&WHDR_DONE));
	waveInAddBuffer(dev, (WAVEHDR *)chdr, sizeof(WAVEHDR));

	data = chdr->lpData;
	pos = 0;
	chdr = chdr == &hdr1 ? &hdr2 : &hdr1;
}

bool C_sndcard::Open(char *name)
{
	WAVEFORMATEX devformat;
	WAVEINCAPS devcaps;
	MMRESULT res;
	int numdevs, seldev;
	int slevel = 0, nlevel;

	if((numdevs = waveInGetNumDevs()) < 1)
	{
		error = "No wave input devices installed";
		return false;
	}

	seldev = -1;
	while(numdevs--)
	{
		waveInGetDevCaps(numdevs, &devcaps, sizeof(WAVEINCAPS));

		nlevel = 0;
		if(devcaps.dwFormats & WAVE_FORMAT_2M08) //8bit mono, 22050Hz
			nlevel = 1;

		if(devcaps.dwFormats & WAVE_FORMAT_4M08) //8bit mono, 44100Hz
			nlevel = 2;

		if(nlevel > slevel)
		{
			slevel = nlevel;
			seldev = numdevs;
		}
	}

	if(seldev == -1)
	{
		error = "No suitable devices found (1)";
		return false;
	}

	freq = (slevel == 2) ? 44100 : 22050;

	devformat.wFormatTag = WAVE_FORMAT_PCM;
	devformat.nChannels = 1;
	devformat.nSamplesPerSec = 
	devformat.nAvgBytesPerSec = freq;
	devformat.nBlockAlign = 1;
	devformat.wBitsPerSample = 8;
	devformat.cbSize = 0;

	if((res = waveInOpen(&dev, WAVE_MAPPER, &devformat, 0, NULL, CALLBACK_NULL)) != MMSYSERR_NOERROR)
	{
		error = "No suitable wave devices found (2)";
		return false;
	}

	waveInStart(dev);

	inbuf1 = (char *)malloc(BUF_SIZE);
	inbuf2 = (char *)malloc(BUF_SIZE);
	quit = false;

	bufsize = BUF_SIZE;

	hdr1.lpData = inbuf1;
	hdr1.dwBufferLength = BUF_SIZE;
	hdr1.dwFlags = 0;
	hdr2.lpData = inbuf2;
	hdr2.dwBufferLength = BUF_SIZE;
	hdr2.dwFlags = 0;

	waveInPrepareHeader(dev, (WAVEHDR *)&hdr1, sizeof(WAVEHDR));
	waveInPrepareHeader(dev, (WAVEHDR *)&hdr2, sizeof(WAVEHDR));

	waveInAddBuffer(dev, (WAVEHDR *)&hdr1, sizeof(WAVEHDR));
	waveInAddBuffer(dev, (WAVEHDR *)&hdr2, sizeof(WAVEHDR));
	chdr = &hdr1;

	NewBlock();

	return true;
}

bool C_sndcard::Close(void)
{
	while(!(chdr->dwFlags&WHDR_DONE));
	chdr = chdr == &hdr1 ? &hdr2 : &hdr1;
	while(!(chdr->dwFlags&WHDR_DONE));

	waveInUnprepareHeader(dev, (WAVEHDR *)&hdr2, sizeof(WAVEHDR));
	waveInUnprepareHeader(dev, (WAVEHDR *)&hdr2, sizeof(WAVEHDR));

	waveInStop(dev);
	waveInClose(dev);

	free(inbuf1);
	free(inbuf2);
	return true;
}

bool C_sndcard::Finished(void)
{
	if(_kbhit())
	{
		switch(_getch())
		{
			default :
			break;

			case 0x1b :
			case 0x51 :
			case 0x71 :
				quit = true;
			break;
		}
	}

	return quit;
}
#endif

char C_sndcard::GetNextValue(void)
{
	char ret = -1; 

	ret = data[pos] - 128;
	pos++;

	if(pos == bufsize)
	{
		NewBlock();
	}

	return ret;
}
