/*
 * OpenSound media addon for BeOS and Haiku
 *
 * Copyright (c) 2007, François Revol (revol@free.fr)
 * Distributed under the terms of the MIT License.
 */
 
#include "OpenSoundDeviceEngine.h"
 
#include "debug.h"
#include "driver_io.h"
#include <MediaDefs.h>
#include <Debug.h>
#include <errno.h>
#include <string.h>
 
#include "SupportFunctions.h"
 
OpenSoundDeviceEngine::~OpenSoundDeviceEngine()
{
	CALLED();
	if (fFD != 0) {
		close(fFD);
	}
}
 
OpenSoundDeviceEngine::OpenSoundDeviceEngine(oss_audioinfo *info)
	: fNextPlay(NULL)
	, fNextRec(NULL)
	, fOpenMode(0)
	, fFD(-1)
	, fMediaFormat()
	, fDriverBufferSize(0)
{
	CALLED();
	fInitCheckStatus = B_NO_INIT;
	memcpy(&fAudioInfo, info, sizeof(oss_audioinfo));
 
	// XXX:REMOVEME
	// set default format
/*
	SetFormat(OpenSoundDevice::select_oss_format(info->oformats));
	SetChannels(info->max_channels);
	SetSpeed(info->max_rate);
*/
	fInitCheckStatus = B_OK;
}
 
 
status_t OpenSoundDeviceEngine::InitCheck(void) const
{
	CALLED();
	return fInitCheckStatus;
}
 
 
status_t OpenSoundDeviceEngine::Open(int mode)
{
	int omode, v;
	CALLED();
 
	switch (mode) {
	case OPEN_READ:
		if (!(Caps() & DSP_CAP_INPUT))
			return EINVAL;
		omode = O_RDONLY;
		break;
	case OPEN_WRITE:
		if (!(Caps() & DSP_CAP_OUTPUT))
			return EINVAL;
		omode = O_WRONLY;
		break;
	case OPEN_READWRITE:
		if (!(Caps() & DSP_CAP_OUTPUT) || !(Caps() & DSP_CAP_INPUT))
			return EINVAL;
		omode = O_RDWR;
		break;
	default:
		return EINVAL;
	}
	// O_EXCL = bypass soft mixer = direct access
	omode |= O_EXCL;
 
	Close();
	fOpenMode = mode;
	fFD = open(fAudioInfo.devnode, omode);
	if (fFD < 0) {
		fInitCheckStatus = errno;
		return EIO;
	}
	// disable format convertions
	v = 0;
	if (ioctl(fFD, SNDCTL_DSP_COOKEDMODE, &v, sizeof(int)) < 0) {
		fInitCheckStatus = errno;
		Close();
		return EIO;
	}
 
	// set driver buffer size by using the "fragments" API
	// TODO: export this setting as a BParameter?
	uint32 bufferCount = 4;
	uint32 bufferSize = 0x000b; // 1024 bytes
	v = (bufferCount << 16) | bufferSize;
	if (ioctl(fFD, SNDCTL_DSP_SETFRAGMENT, &v, sizeof(int)) < 0) {
		fInitCheckStatus = errno;
		Close();
		return EIO;
	}
 
	fDriverBufferSize = 2048;
		// preliminary, adjusted in AcceptFormat()
	return B_OK;
}
 
 
status_t OpenSoundDeviceEngine::Close(void)
{
	CALLED();
	if (fFD > -1)
		close(fFD);
	fFD = -1;
	fOpenMode = 0;
	fMediaFormat = media_format();
	return B_OK;
}
 
 
ssize_t OpenSoundDeviceEngine::Read(void *buffer, size_t size)
{
	ssize_t done;
	CALLED();
	done = read(fFD, buffer, size);
	if (done < 0)
		return errno;
	return done;
}
 
 
ssize_t
OpenSoundDeviceEngine::Write(const void *buffer, size_t size)
{
	CALLED();
 
	ASSERT(size > 0);
 
	ssize_t done = write(fFD, buffer, size);
	if (done < 0)
		return errno;
 
	return done;
}
 
 
status_t
OpenSoundDeviceEngine::UpdateInfo()
{
	CALLED();
 
	if (fFD < 0)
		return ENODEV;
 
	if (ioctl(fFD, SNDCTL_ENGINEINFO, &fAudioInfo, sizeof(oss_audioinfo)) < 0)
		return errno;
 
	return B_OK;
}
 
 
bigtime_t
OpenSoundDeviceEngine::PlaybackLatency()
{
	bigtime_t latency = time_for_buffer(fDriverBufferSize, fMediaFormat);
	bigtime_t cardLatency = CardLatency();
	if (cardLatency == 0) {
		// that's unrealistic, take matters into own hands
		cardLatency = latency / 3;
	}
	latency += cardLatency;
//	PRINT(("PlaybackLatency: odelay %d latency %Ld card %Ld\n",
//		fDriverBufferSize, latency, CardLatency()));
	return latency;
}
 
 
bigtime_t
OpenSoundDeviceEngine::RecordingLatency()
{
	return 0LL; //XXX
}
 
 
int OpenSoundDeviceEngine::GetChannels(void)
{
	int chans = -1;
	CALLED();
	if (ioctl(fFD, SNDCTL_DSP_CHANNELS, &chans, sizeof(int)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_CHANNELS", strerror(errno)));
		return -1;
	}
	return chans;
}
 
status_t OpenSoundDeviceEngine::SetChannels(int chans)
{
	CALLED();
	if (ioctl(fFD, SNDCTL_DSP_CHANNELS, &chans, sizeof(int)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_CHANNELS", strerror(errno)));
		return EIO;
	}
	PRINT(("OpenSoundDeviceEngine::%s: %d\n", __FUNCTION__, chans));
	return B_OK;
}
 
//XXX: either GetFormat*s*() or cache SetFormat()!
int OpenSoundDeviceEngine::GetFormat(void)
{
	int fmt = -1;
	CALLED();
	if (ioctl(fFD, SNDCTL_DSP_GETFMTS, &fmt, sizeof(int)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_GETFMTS", strerror(errno)));
		return -1;
	}
	return fmt;
}
 
status_t OpenSoundDeviceEngine::SetFormat(int fmt)
{
	CALLED();
	if (ioctl(fFD, SNDCTL_DSP_SETFMT, &fmt, sizeof(int)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_SETFMT", strerror(errno)));
		return EIO;
	}
	PRINT(("OpenSoundDeviceEngine::%s: 0x%08x\n", __FUNCTION__, fmt));
	return B_OK;
}
 
int OpenSoundDeviceEngine::GetSpeed(void)
{
	int speed = -1;
	CALLED();
	if (ioctl(fFD, SNDCTL_DSP_SPEED, &speed, sizeof(int)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_SPEED", strerror(errno)));
		return -1;
	}
	return speed;
}
 
status_t OpenSoundDeviceEngine::SetSpeed(int speed)
{
	CALLED();
	if (ioctl(fFD, SNDCTL_DSP_SPEED, &speed, sizeof(int)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_SPEED", strerror(errno)));
		return EIO;
	}
	PRINT(("OpenSoundDeviceEngine::%s: %d\n", __FUNCTION__, speed));
	return B_OK;
}
 
 
size_t OpenSoundDeviceEngine::GetISpace(audio_buf_info *info)
{
	audio_buf_info abinfo;
	CALLED();
	if (!info)
		info = &abinfo;
	memset(info, 0, sizeof(audio_buf_info));
	if (!(fOpenMode & OPEN_READ))
		return 0;
	if (ioctl(fFD, SNDCTL_DSP_GETISPACE, info, sizeof(audio_buf_info)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_GETISPACE", strerror(errno)));
		return EIO;
	}
	//PRINT(("OpenSoundDeviceEngine::%s: ISPACE: { bytes=%d, fragments=%d, fragsize=%d, fragstotal=%d }\n", __FUNCTION__, info->bytes, info->fragments, info->fragsize, info->fragstotal));
	return info->bytes;
}
 
 
size_t OpenSoundDeviceEngine::GetOSpace(audio_buf_info *info)
{
	audio_buf_info abinfo;
	//CALLED();
	if (!info)
		info = &abinfo;
	memset(info, 0, sizeof(audio_buf_info));
	if (!(fOpenMode & OPEN_WRITE))
		return 0;
	if (ioctl(fFD, SNDCTL_DSP_GETOSPACE, info, sizeof(audio_buf_info)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_GETOSPACE", strerror(errno)));
		return EIO;
	}
	//PRINT(("OpenSoundDeviceEngine::%s: OSPACE: { bytes=%d, fragments=%d, fragsize=%d, fragstotal=%d }\n", __FUNCTION__, info->bytes, info->fragments, info->fragsize, info->fragstotal));
	return info->bytes;
}
 
 
int64
OpenSoundDeviceEngine::GetCurrentIPtr(int32 *fifoed, oss_count_t *info)
{
	oss_count_t ocount;
	count_info cinfo;
	CALLED();
	if (!info)
		info = &ocount;
	memset(info, 0, sizeof(oss_count_t));
	if (!(fOpenMode & OPEN_READ))
		return 0;
	if (ioctl(fFD, SNDCTL_DSP_CURRENT_IPTR, info, sizeof(oss_count_t)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_CURRENT_IPTR", strerror(errno)));
		//return EIO;
		// fallback: try GET*PTR
		if (ioctl(fFD, SNDCTL_DSP_GETIPTR, &cinfo, sizeof(count_info)) < 0) {
			PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
					__FUNCTION__, "SNDCTL_DSP_GETIPTR", strerror(errno)));
			return 0;
		}
		// it's probably wrong...
		info->samples = cinfo.bytes / (fMediaFormat.u.raw_audio.channel_count
						 		* (fMediaFormat.AudioFormat() & media_raw_audio_format::B_AUDIO_SIZE_MASK));
		info->fifo_samples = 0;
	}
	PRINT(("OpenSoundDeviceEngine::%s: IPTR: { samples=%Ld, fifo_samples=%d }\n", __FUNCTION__, info->samples, info->fifo_samples));
	if (fifoed)
		*fifoed = info->fifo_samples;
	return info->samples;
}
 
 
int64
OpenSoundDeviceEngine::GetCurrentOPtr(int32* fifoed, size_t* fragmentPos)
{
	CALLED();
 
	if (!(fOpenMode & OPEN_WRITE)) {
		if (fifoed != NULL)
			*fifoed = 0;
		return 0;
	}
 
	oss_count_t info;
	memset(&info, 0, sizeof(oss_count_t));
 
	if (ioctl(fFD, SNDCTL_DSP_CURRENT_OPTR, &info, sizeof(oss_count_t)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_CURRENT_OPTR", strerror(errno)));
 
		return 0;
	}
 
	if (fragmentPos != NULL) {
		count_info cinfo;
		if (ioctl(fFD, SNDCTL_DSP_GETOPTR, &cinfo, sizeof(count_info)) < 0) {
			PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
					__FUNCTION__, "SNDCTL_DSP_GETOPTR", strerror(errno)));
			return 0;
		}
		*fragmentPos = cinfo.ptr;
	}
 
//	PRINT(("OpenSoundDeviceEngine::%s: OPTR: { samples=%Ld, "
//		"fifo_samples=%d }\n", __FUNCTION__, info->samples,
//		info->fifo_samples));
	if (fifoed != NULL)
		*fifoed = info.fifo_samples;
	return info.samples;
}
 
 
int32
OpenSoundDeviceEngine::GetIOverruns()
{
	audio_errinfo info;
	CALLED();
	memset(&info, 0, sizeof(info));
	if (!(fOpenMode & OPEN_WRITE))
		return 0;
	if (ioctl(fFD, SNDCTL_DSP_GETERROR, &info, sizeof(info)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_GETERROR", strerror(errno)));
		return 0;
	}
	PRINT(("OpenSoundDeviceEngine::%s: IOVERRUNS: { overruns=%d }\n", __FUNCTION__, info.rec_overruns));
	return info.rec_overruns;
}
 
 
int32
OpenSoundDeviceEngine::GetOUnderruns()
{
	audio_errinfo info;
	CALLED();
	memset(&info, 0, sizeof(info));
	if (!(fOpenMode & OPEN_WRITE))
		return 0;
	if (ioctl(fFD, SNDCTL_DSP_GETERROR, &info, sizeof(info)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_GETERROR", strerror(errno)));
		return 0;
	}
	//PRINT(("OpenSoundDeviceEngine::%s: OUNDERRUNS: { underruns=%d }\n", __FUNCTION__, info.play_underruns));
	return info.play_underruns;
}
 
 
size_t
OpenSoundDeviceEngine::DriverBufferSize() const
{
	return fDriverBufferSize;
}
 
 
status_t OpenSoundDeviceEngine::StartRecording(void)
{
	CALLED();
	oss_syncgroup group;
	group.id = 0;
	group.mode = PCM_ENABLE_INPUT;
	if (ioctl(fFD, SNDCTL_DSP_SYNCGROUP, &group, sizeof(group)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_SYNCGROUP", strerror(errno)));
		return EIO;
	}
	if (ioctl(fFD, SNDCTL_DSP_SYNCSTART, &group.id, sizeof(group.id)) < 0) {
		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_SYNCSTART", strerror(errno)));
		return EIO;
	}
	return B_OK;
}
 
 
status_t
OpenSoundDeviceEngine::WildcardFormatFor(int fmt, media_format &format,
	bool rec)
{
	status_t err;
	CALLED();
	fmt &= rec ? Info()->iformats : Info()->oformats;
	if (fmt == 0)
		return B_MEDIA_BAD_FORMAT;
	err = OpenSoundDevice::get_media_format_for(fmt, format);
	if (err < B_OK)
		return err;
	char buf[1024];
	string_for_format(format, buf, 1024);
	if (format.type == B_MEDIA_RAW_AUDIO) {
		format.u.raw_audio = media_multi_audio_format::wildcard;
		format.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
		// single rate supported
		if (Info()->min_rate == Info()->max_rate)
			format.u.raw_audio.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
	} else if (format.type == B_MEDIA_ENCODED_AUDIO) {
		format.u.encoded_audio.output = media_multi_audio_format::wildcard;
		//format.u.encoded_audio.output.byte_order = B_MEDIA_HOST_ENDIAN;
		// single rate supported
		//if (Info()->min_rate == Info()->max_rate)
		//	format.u.encoded_audio.output.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
	} else
		return EINVAL;
	PRINT(("%s: %s\n", __FUNCTION__, buf));
	return B_OK;
}
 
 
status_t OpenSoundDeviceEngine::PreferredFormatFor(int fmt, media_format &format, bool rec)
{
	status_t err;
	CALLED();
	fmt &= rec ? Info()->iformats : Info()->oformats;
	if (fmt == 0)
		return B_MEDIA_BAD_FORMAT;
	err = WildcardFormatFor(fmt, format);
	if (err < B_OK)
		return err;
	if (format.type == B_MEDIA_RAW_AUDIO) {
		media_multi_audio_format &raw = format.u.raw_audio;
		//format.u.raw_audio.channel_count = Info()->max_channels;
		raw.byte_order = B_MEDIA_HOST_ENDIAN;
		raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
		raw.buffer_size = DEFAULT_BUFFER_SIZE;
		/*if (rec)
			raw.buffer_size = 2048;*/
/*
		format.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
		format.u.raw_audio.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
		format.u.raw_audio.buffer_size = DEFAULT_BUFFER_SIZE;
*/
	} else if (format.type == B_MEDIA_ENCODED_AUDIO) {
		media_raw_audio_format &raw = format.u.encoded_audio.output;
		//format.u.encoded_audio.output.channel_count = Info()->max_channels;
		raw.byte_order = B_MEDIA_HOST_ENDIAN;
		// single rate supported
		if (Info()->min_rate == Info()->max_rate)
			raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
		raw.buffer_size = DEFAULT_BUFFER_SIZE;
	} else
		return EINVAL;
	char buf[1024];
	string_for_format(format, buf, 1024);
	PRINT(("%s: %s\n", __FUNCTION__, buf));
	return B_OK;
}
 
 
status_t OpenSoundDeviceEngine::AcceptFormatFor(int fmt, media_format &format, bool rec)
{
	status_t err;
	int afmt = 0;
	char buf[1024];
	CALLED();
	fmt &= rec ? Info()->iformats : Info()->oformats;
 
	if (fmt == 0)
		return B_MEDIA_BAD_FORMAT;
	media_format wc;
	err = WildcardFormatFor(fmt, wc);
	if (err < B_OK)
		return err;
 
	err = Open(rec ? OPEN_READ : OPEN_WRITE);
	if (err < B_OK)
		return err;
 
	if (format.type == B_MEDIA_RAW_AUDIO) {
		media_multi_audio_format &raw = format.u.raw_audio;
 
		// channel count
		raw.channel_count = MAX((unsigned)(Info()->min_channels), MIN((unsigned)(Info()->max_channels), raw.channel_count));
		err = SetChannels(raw.channel_count);
		if (err < B_OK) {
			Close();
			return err;
		}
 
		PRINT(("%s:step1  fmt=0x%08x, raw.format=0x%08" B_PRIx32 "\n",
			__FUNCTION__, fmt, raw.format));
		// if specified, try it
		if (raw.format)
			afmt = OpenSoundDevice::convert_media_format_to_oss_format(raw.format);
		afmt &= fmt;
		PRINT(("%s:step2 afmt=0x%08x\n", __FUNCTION__, afmt));
		// select the best as default
		if (afmt == 0) {
			afmt = OpenSoundDevice::select_oss_format(fmt);
			raw.format = OpenSoundDevice::convert_oss_format_to_media_format(afmt);
			//Close();
			//return B_MEDIA_BAD_FORMAT;
		}
		PRINT(("%s:step3 afmt=0x%08x\n", __FUNCTION__, afmt));
		// convert back
		raw.format = OpenSoundDevice::convert_oss_format_to_media_format(afmt);
		PRINT(("%s:step4 afmt=0x%08x, raw.format=0x%08" B_PRIx32 "\n",
			__FUNCTION__, afmt, raw.format));
		raw.valid_bits = OpenSoundDevice::convert_oss_format_to_valid_bits(afmt);
 
		err = SetFormat(afmt);
		if (err < B_OK) {
			Close();
			return err;
		}
 
		// endianness
		raw.byte_order = OpenSoundDevice::convert_oss_format_to_endian(afmt);
 
		// sample rate
		raw.frame_rate = OpenSoundDevice::select_oss_rate(Info(), raw.frame_rate);		// measured in Hertz
		//raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
		err = SetSpeed(OpenSoundDevice::convert_media_rate_to_oss_rate(raw.frame_rate));
		if (err < B_OK) {
			Close();
			return err;
		}
 
		// retrieve the driver buffer size (it's important to do this
		// after all the other setup, since OSS may have adjusted it, and
		// also weird things happen if this ioctl() is done before other
		// setup itctl()s)
		audio_buf_info abinfo;
		memset(&abinfo, 0, sizeof(audio_buf_info));
		if (ioctl(fFD, SNDCTL_DSP_GETOSPACE, &abinfo, sizeof(audio_buf_info)) < 0) {
			fprintf(stderr, "failed to retrieve driver buffer size!\n");
			abinfo.bytes = 0;
		}
		fDriverBufferSize = abinfo.bytes;
 
		raw.buffer_size = fDriverBufferSize;
 
	} else if (format.type == B_MEDIA_ENCODED_AUDIO) {
		media_raw_audio_format &raw = format.u.encoded_audio.output;
		// XXX: do we really have to do this ?
		raw.channel_count = MAX((unsigned)(Info()->min_channels), MIN((unsigned)(Info()->max_channels), raw.channel_count));
		raw.byte_order = B_MEDIA_HOST_ENDIAN;
		raw.frame_rate = OpenSoundDevice::select_oss_rate(Info(), raw.frame_rate);		// measured in Hertz
		//raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
		raw.buffer_size = DEFAULT_BUFFER_SIZE;
 
	} else {
		PRINT(("%s: unknown media type\n", __FUNCTION__));
		Close();
		return EINVAL;
	}
	// cache it
	fMediaFormat = format;
 
	string_for_format(format, buf, 1024);
	PRINT(("%s: %s\n", __FUNCTION__, buf));
	return B_OK;
}
 
 
status_t OpenSoundDeviceEngine::SpecializeFormatFor(int fmt, media_format &format, bool rec)
{
	status_t err;
	int afmt = 0;
	CALLED();
	fmt &= rec ? Info()->iformats : Info()->oformats;
	if (fmt == 0)
		return B_MEDIA_BAD_FORMAT;
	media_format wc;
	err = WildcardFormatFor(fmt, wc);
	if (err < B_OK)
		return err;
 
	err = Open(rec ? OPEN_READ : OPEN_WRITE);
	if (err < B_OK)
		return err;
 
	if (format.type == B_MEDIA_RAW_AUDIO) {
		media_multi_audio_format &raw = format.u.raw_audio;
 
		PRINT(("%s:step1  fmt=0x%08x, raw.format=0x%08" B_PRIx32 "\n",
			__FUNCTION__, fmt, raw.format));
		// select the best as default
		if (!raw.format) {
			afmt = OpenSoundDevice::select_oss_format(fmt);
			raw.format = OpenSoundDevice::convert_oss_format_to_media_format(afmt);
		}
		// if specified, try it
		if (raw.format)
			afmt = OpenSoundDevice::convert_media_format_to_oss_format(raw.format);
		afmt &= fmt;
		PRINT(("%s:step2 afmt=0x%08x\n", __FUNCTION__, afmt));
		if (afmt == 0) {
			Close();
			return B_MEDIA_BAD_FORMAT;
		}
		// convert back
		raw.format = OpenSoundDevice::convert_oss_format_to_media_format(afmt);
		PRINT(("%s:step4 afmt=0x%08x, raw.format=0x%08" B_PRIx32 "\n",
			__FUNCTION__, afmt, raw.format));
		if (!raw.valid_bits)
			raw.valid_bits = OpenSoundDevice::convert_oss_format_to_valid_bits(afmt);
		if (raw.valid_bits != OpenSoundDevice::convert_oss_format_to_valid_bits(afmt)) {
			Close();
			return B_MEDIA_BAD_FORMAT;
		}
 
		err = SetFormat(afmt);
		if (err < B_OK) {
			Close();
			return err;
		}
 
		// endianness
		if (!raw.byte_order)
			raw.byte_order = OpenSoundDevice::convert_oss_format_to_endian(afmt);
		if ((int)raw.byte_order != OpenSoundDevice::convert_oss_format_to_endian(afmt)) {
			Close();
			return B_MEDIA_BAD_FORMAT;
		}
 
		// channel count
		if (raw.channel_count == 0)
			raw.channel_count = (unsigned)Info()->min_channels;
		if ((int)raw.channel_count < Info()->min_channels
			|| (int)raw.channel_count > Info()->max_channels)
			return B_MEDIA_BAD_FORMAT;
		err = SetChannels(raw.channel_count);
		if (err < B_OK) {
			Close();
			return err;
		}
 
		// sample rate
		if (!raw.frame_rate)
			raw.frame_rate = Info()->max_rate;
		//raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
		err = SetSpeed(OpenSoundDevice::convert_media_rate_to_oss_rate(raw.frame_rate));
		if (err < B_OK) {
			Close();
			return err;
		}
 
#if 0
		raw.buffer_size = DEFAULT_BUFFER_SIZE
						* (raw.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
						* raw.channel_count;
#endif
		audio_buf_info abinfo;
		if (ioctl(fFD, rec?SNDCTL_DSP_GETISPACE:SNDCTL_DSP_GETOSPACE, &abinfo, sizeof(abinfo)) < 0) {
			PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
				__FUNCTION__, "SNDCTL_DSP_GET?SPACE", strerror(errno)));
			return -1;
		}
		PRINT(("OSS: %cSPACE: { bytes=%d, fragments=%d, fragsize=%d, fragstotal=%d }\n", rec?'I':'O', abinfo.bytes, abinfo.fragments, abinfo.fragsize, abinfo.fragstotal));
		// cache the first one in the Device
		// so StartThread() knows the number of frags
		//if (!fFragments.fragstotal)
		//	memcpy(&fFragments, &abinfo, sizeof(abinfo));
 
		// make sure buffer size is less than the driver's own buffer ( /2 to keep some margin )
		if (/*rec && raw.buffer_size &&*/ (int)raw.buffer_size > abinfo.fragsize * abinfo.fragstotal / 4)
			return B_MEDIA_BAD_FORMAT;
		if (!raw.buffer_size)
			raw.buffer_size = abinfo.fragsize;//XXX
/*						* (raw.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
						* raw.channel_count;*/
	} else if (format.type == B_MEDIA_ENCODED_AUDIO) {
		media_raw_audio_format &raw = format.u.encoded_audio.output;
		// XXX: do we really have to do this ?
		raw.channel_count = MAX((unsigned)(Info()->min_channels), MIN((unsigned)(Info()->max_channels), raw.channel_count));
		raw.byte_order = B_MEDIA_HOST_ENDIAN;
		raw.frame_rate = OpenSoundDevice::select_oss_rate(Info(), raw.frame_rate);		// measured in Hertz
		//raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
		raw.buffer_size = DEFAULT_BUFFER_SIZE;
 
	} else {
		Close();
		return EINVAL;
	}
	// cache it
	fMediaFormat = format;
	char buf[1024];
	string_for_format(format, buf, 1024);
	PRINT(("%s: %s\n", __FUNCTION__, buf));
	return B_OK;
}
 

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fPlayedFramesCount, fPlayedRealTime.

V576 Potentially incorrect format string is passed to the '_debugPrintf' function. Prefix 'L' is not applicable to conversion specifier 'd'.