/*
 * This file is a part of VyQChat.
 *
 * Copyright (C) 2002-2004 Pawel Stolowski <yogin@linux.bydg.org>
 *
 * VyQChat is free software; you can redestribute it and/or modify it
 * under terms of GNU General Public License by Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY. See GPL for more details.
 */

#include "config.h"
#include "sound.h"
#include "settings.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <qapplication.h>
#include <qfile.h>
#include <qprocess.h>
#include <qglobal.h>

Sound::SoundType Sound::sndtype;
SoundSystem *Sound::sndsys = NULL;

static void sigchld_handler(int s)
{
	wait(NULL);
}

bool Sound::reload_samples(Settings &settings)/*{{{*/
{
	return sndsys->reload_samples(settings);
}/*}}}*/

bool Sound::init(Settings *settings)/*{{{*/
{
	sndtype = settings->getSoundType();
			
	switch (sndtype)
	{
		case NoSound:
			sndsys = new SoundSystem();
			break;
		case PCSpeaker:
			sndsys = new SoundPCSpeaker();
			break;
		case Command:
			sndsys = new SoundCommand();
			break;
		case Arts:
			sndsys = new SoundArts();
			break;
		case LibAo:
			sndsys = new SoundLibAo();
			break;
		default: return false;
	}
	if (!sndsys)
		return false;
	bool status = sndsys->init();
	if (!status)
	{
		delete sndsys;
		sndsys = new SoundPCSpeaker();
		status = sndsys->init();
	}
	if (status)
	{
		sndsys->reload_samples(*settings);
		signal(SIGCHLD, sigchld_handler);
	}
	return status;
}/*}}}*/

void Sound::close()/*{{{*/
{
	if (sndsys)
		sndsys->close();
}/*}}}*/

void Sound::play(VyEvent evt)/*{{{*/
{
	if (sndsys)
		sndsys->play(evt);
}/*}}}*/

const QString& Sound::error()/*{{{*/
{
	return sndsys->errstr;
}/*}}}*/

/*
 * Sound systems base
 */
SoundSystem::SoundSystem()/*{{{*/
{
	for (int i=0; i<EventDummy; i++)
		sample[i] = new QByteArray(0);
}/*}}}*/

SoundSystem::~SoundSystem()/*{{{*/
{
	for (int i=0; i<EventDummy; i++)
		delete sample[i];
}/*}}}*/

bool SoundSystem::reload_samples(Settings &settings)/*{{{*/
{
#	ifdef HAVE_SNDFILE
	bool status = true;

	for (int i=0; i<EventDummy; i++)
	{
		sfinf[i].format = 0;
		SNDFILE *f = sf_open(settings.getSampleFileName(static_cast<VyEvent>(i)).latin1(), SFM_READ, &sfinf[i]);
		if (f)
		{
			sample[i]->resize(sfinf[i].channels * sfinf[i].frames * sizeof(short));
			sf_readf_short(f, reinterpret_cast<short *>(sample[i]->data()), sfinf[i].frames);
			sf_close(f);
		}
		else
		{
			status = false;
		}
	}
	return status;
#	else
	return false;
#	endif
}/*}}}*/

/*
 * PC Speaker
 */
bool SoundPCSpeaker::init()/*{{{*/
{
	return true;
}/*}}}*/

void SoundPCSpeaker::play(VyEvent evt)/*{{{*/
{
	QApplication::beep();
}/*}}}*/

void SoundPCSpeaker::close()/*{{{*/
{
}/*}}}*/

/*
 * LibAo 
 */
bool SoundLibAo::init()/*{{{*/
{
#	ifdef HAVE_LIBAO 
	ao_initialize();
	drv = ao_default_driver_id();
	return true;
#	else
	errstr = QString("No libao support compiled");
	return false;
#	endif
}/*}}}*/

void SoundLibAo::play(VyEvent evt)/*{{{*/
{
#	ifdef HAVE_LIBAO
#		ifdef HAVE_SNDFILE
	if (fork() == 0)
	{
		ao_sample_format frmt;
		
		frmt.bits = sizeof(short) << 3;
		frmt.channels = sfinf[evt].channels;
		frmt.rate = sfinf[evt].samplerate;
		frmt.byte_format = AO_FMT_NATIVE;

		ao_device* dev = ao_open_live(drv, &frmt, NULL);
		ao_play(dev, sample[evt]->data(), sample[evt]->size());
		ao_close(dev);
		ao_shutdown();

		_exit(0);
	}
#		endif
#	endif
}/*}}}*/

void SoundLibAo::close()/*{{{*/
{
#	ifdef HAVE_LIBAO 
	ao_shutdown();
#	endif
}/*}}}*/

/*
 * ARTS
 */
bool SoundArts::init()/*{{{*/
{
#	ifdef HAVE_ARTS
/*	int err;
	if ((err = arts_init()) < 0)
	{
		errstr = arts_error_text(err);
		return false;
	}*/
#	else
	errstr = "no aRtS support compiled";
	return false;
#	endif
	return true;
}/*}}}*/

void SoundArts::play(VyEvent evt)/*{{{*/
{
#	ifdef HAVE_ARTS
#		ifdef HAVE_SNDFILE
	if (fork() == 0)
	{
		arts_init();
		
		arts_stream_t str = arts_play_stream(sfinf[evt].samplerate, sizeof(short)<<3, sfinf[evt].channels, "vyqchat");// + QString::number(getpid()));
		arts_write(str, sample[evt]->data(), sample[evt]->size());
		//if (status<0)
		//	qWarning("arts_write error: %s",  arts_error_text(status));
		arts_close_stream(str);
		arts_free();
		_exit(0);
	}
#		endif
#	endif
}/*}}}*/

void SoundArts::close()/*{{{*/
{
#	ifdef HAVE_ARTS
	//arts_free();
#	endif
}/*}}}*/

/*
 * COMMAND
 */
bool SoundCommand::reload_samples(Settings &settings)/*{{{*/
{
	const QStringList cmd = QStringList::split(' ', settings.getPlayCommand());
	for (int i=0; i<EventDummy; i++)
	{
		arg[i].clear();
		arg[i] = cmd;
		arg[i].append(settings.getSampleFileName(static_cast<VyEvent>(i)));
	}
	return true;
}/*}}}*/

bool SoundCommand::init()/*{{{*/
{
	return true;
}/*}}}*/

void SoundCommand::play(VyEvent evt)/*{{{*/
{
	QProcess *proc = new QProcess(arg[evt], NULL);
	QObject::connect(proc, SIGNAL(processExited()), proc, SLOT(deleteLater()));
	if (!proc->start())
		qWarning("cannot run play command");
}/*}}}*/



syntax highlighted by Code2HTML, v. 0.9.1