/*  $Id: soundprovider_recorder_oss.cpp,v 1.4 2003/09/22 22:15:13 mbn Exp $
**
**  ClanLib Game SDK
**  Copyright (C) 2003  The ClanLib Team
**  For a total list of contributers see the file CREDITS.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This library 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
**  Lesser General Public License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*/

#include "Sound/precomp.h"
#include "API/Sound/soundformat.h"
#include <API/Core/System/error.h>
#include "API/Core/System/cl_assert.h"
#include "API/Core/System/system.h"
#include "API/Core/System/log.h"
#include "soundprovider_recorder_oss.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#ifdef __CYGWIN__
#include <sys/select.h>
#endif


/////////////////////////////////////////////////////////////////////////////
// CL_SoundProvider_Recorder_OSS_Session construction:

CL_SoundProvider_Recorder_OSS_Session::CL_SoundProvider_Recorder_OSS_Session(int frequency) :
	frequency(frequency), position(0)
{

	dev_dsp_fd = open("/dev/dsp", O_RDONLY);
	if (dev_dsp_fd == -1)
	{
		frag_size = frequency/2;
		return;
//		throw CL_Error("Could not open /dev/dsp. No sound will be available.");
	}

#ifndef USE_DRIVER_FRAGSIZE
	int frag_settings = 0x0003000b; // 0xMMMMSSSS
		// (where MMMM = num fragments, SSSS = fragment size)

	if (ioctl(dev_dsp_fd, SNDCTL_DSP_SETFRAGMENT, &frag_settings))
	{
		CL_Log::log("debug", "ClanSound: Failed to set soundcard fragment size. Sound may have a long latency.");
	}

#endif

	format = AFMT_S16_NE;
	ioctl(dev_dsp_fd, SNDCTL_DSP_SETFMT, &format);
	if (format != AFMT_S16_NE)
	{
		close(dev_dsp_fd);
		throw CL_Error("Requires 16 bit soundcard. No sound input will be available.");
	}

	int stereo = 0;
	ioctl(dev_dsp_fd, SNDCTL_DSP_STEREO, &stereo);
	if (stereo != 0)
	{
		close(dev_dsp_fd);
		throw CL_Error("ClanSound: Requires 16 bit mono input. No sound will be available.");
	}

	int speed = frequency;
	ioctl(dev_dsp_fd, SNDCTL_DSP_SPEED, &speed);

	float percent_wrong = speed / (float) frequency;
	if (percent_wrong < 0.90 || percent_wrong > 1.10)
	{
		close(dev_dsp_fd);
		throw CL_Error("ClanSound: Mixing rate is not supported by soundcard.");
	}

	// Try to improve mixing performance by using the same mixing buffer size
	// as the sound device does:
	int err = ioctl(dev_dsp_fd, SNDCTL_DSP_GETBLKSIZE, &frag_size);
	if (err == -1)
	{
		CL_Log::log("debug", "ClanSound: Warning, Couldn't get sound device blocksize. Using 0.25 sec mixing buffer.");
		frag_size = frequency/4; // 0.25 sec mixing buffer used.
	}

}

CL_SoundProvider_Recorder_OSS_Session::~CL_SoundProvider_Recorder_OSS_Session()
{
}

/////////////////////////////////////////////////////////////////////////////
// CL_SoundProvider_Recorder_OSS_Session attributes:

int CL_SoundProvider_Recorder_OSS_Session::get_num_samples() const
{
	return 1;
}

int CL_SoundProvider_Recorder_OSS_Session::get_frequency() const
{

	return frequency;
}

CL_SoundFormat CL_SoundProvider_Recorder_OSS_Session::get_format() const
{
	return (CL_SoundFormat)format;
}

int CL_SoundProvider_Recorder_OSS_Session::get_num_channels() const
{
	return 1;
}

int CL_SoundProvider_Recorder_OSS_Session::get_position() const
{
	return position;
}

/////////////////////////////////////////////////////////////////////////////
// CL_SoundProvider_Recorder_OSS_Session operations:

bool CL_SoundProvider_Recorder_OSS_Session::eof() const
{
	return false;
}

void CL_SoundProvider_Recorder_OSS_Session::stop()
{
}

bool CL_SoundProvider_Recorder_OSS_Session::play()
{
	return true;
}

bool CL_SoundProvider_Recorder_OSS_Session::set_position(int pos)
{
	position = pos;
	return true;
}

int CL_SoundProvider_Recorder_OSS_Session::get_data(void **data_ptr, int data_requested)
{
	int len;
	audio_buf_info info;
	// todo: read samples, and only travel position by amount read.
	int err = ioctl(dev_dsp_fd, SNDCTL_DSP_GETISPACE, &info);
	if (err == -1)
	{
		CL_Log::log("debug", "ClanSound: fragments free not supported by device!?");
		position = 0;
	}
	if(info.fragments == 0)
	{
		return 0;
	}
	else
	{
		len = read(dev_dsp_fd, data_ptr[0], data_requested);
	}

	position += len;
	return position;
}

/////////////////////////////////////////////////////////////////////////////
// CL_SoundProvider_Recorder_OSS_Session implementation:
