1/*
2 * OpenSound media addon for BeOS and Haiku
3 *
4 * Copyright (c) 2007, François Revol (revol@free.fr)
5 * Distributed under the terms of the MIT License.
6 */
7
8#include "OpenSoundDeviceEngine.h"
9
10#include "debug.h"
11#include "driver_io.h"
12#include <MediaDefs.h>
13#include <Debug.h>
14#include <errno.h>
15#include <string.h>
16
17#include "SupportFunctions.h"
18
19OpenSoundDeviceEngine::~OpenSoundDeviceEngine()
20{
21	CALLED();
22	if (fFD != 0) {
23		close(fFD);
24	}
25}
26
27OpenSoundDeviceEngine::OpenSoundDeviceEngine(oss_audioinfo *info)
28	: fNextPlay(NULL)
29	, fNextRec(NULL)
30	, fOpenMode(0)
31	, fFD(-1)
32	, fMediaFormat()
33	, fDriverBufferSize(0)
34{
35	CALLED();
36	fInitCheckStatus = B_NO_INIT;
37	memcpy(&fAudioInfo, info, sizeof(oss_audioinfo));
38
39	// XXX:REMOVEME
40	// set default format
41/*
42	SetFormat(OpenSoundDevice::select_oss_format(info->oformats));
43	SetChannels(info->max_channels);
44	SetSpeed(info->max_rate);
45*/
46	fInitCheckStatus = B_OK;
47}
48
49
50status_t OpenSoundDeviceEngine::InitCheck(void) const
51{
52	CALLED();
53	return fInitCheckStatus;
54}
55
56
57status_t OpenSoundDeviceEngine::Open(int mode)
58{
59	int omode, v;
60	CALLED();
61
62	switch (mode) {
63	case OPEN_READ:
64		if (!(Caps() & DSP_CAP_INPUT))
65			return EINVAL;
66		omode = O_RDONLY;
67		break;
68	case OPEN_WRITE:
69		if (!(Caps() & DSP_CAP_OUTPUT))
70			return EINVAL;
71		omode = O_WRONLY;
72		break;
73	case OPEN_READWRITE:
74		if (!(Caps() & DSP_CAP_OUTPUT) || !(Caps() & DSP_CAP_INPUT))
75			return EINVAL;
76		omode = O_RDWR;
77		break;
78	default:
79		return EINVAL;
80	}
81	// O_EXCL = bypass soft mixer = direct access
82	omode |= O_EXCL;
83
84	Close();
85	fOpenMode = mode;
86	fFD = open(fAudioInfo.devnode, omode);
87	if (fFD < 0) {
88		fInitCheckStatus = errno;
89		return EIO;
90	}
91	// disable format convertions
92	v = 0;
93	if (ioctl(fFD, SNDCTL_DSP_COOKEDMODE, &v, sizeof(int)) < 0) {
94		fInitCheckStatus = errno;
95		Close();
96		return EIO;
97	}
98
99	// set driver buffer size by using the "fragments" API
100	// TODO: export this setting as a BParameter?
101	uint32 bufferCount = 4;
102	uint32 bufferSize = 0x000b; // 1024 bytes
103	v = (bufferCount << 16) | bufferSize;
104	if (ioctl(fFD, SNDCTL_DSP_SETFRAGMENT, &v, sizeof(int)) < 0) {
105		fInitCheckStatus = errno;
106		Close();
107		return EIO;
108	}
109
110	fDriverBufferSize = 2048;
111		// preliminary, adjusted in AcceptFormat()
112	return B_OK;
113}
114
115
116status_t OpenSoundDeviceEngine::Close(void)
117{
118	CALLED();
119	if (fFD > -1)
120		close(fFD);
121	fFD = -1;
122	fOpenMode = 0;
123	fMediaFormat = media_format();
124	return B_OK;
125}
126
127
128ssize_t OpenSoundDeviceEngine::Read(void *buffer, size_t size)
129{
130	ssize_t done;
131	CALLED();
132	done = read(fFD, buffer, size);
133	if (done < 0)
134		return errno;
135	return done;
136}
137
138
139ssize_t
140OpenSoundDeviceEngine::Write(const void *buffer, size_t size)
141{
142	CALLED();
143
144	ASSERT(size > 0);
145
146	ssize_t done = write(fFD, buffer, size);
147	if (done < 0)
148		return errno;
149
150	return done;
151}
152
153
154status_t
155OpenSoundDeviceEngine::UpdateInfo()
156{
157	CALLED();
158
159	if (fFD < 0)
160		return ENODEV;
161
162	if (ioctl(fFD, SNDCTL_ENGINEINFO, &fAudioInfo, sizeof(oss_audioinfo)) < 0)
163		return errno;
164
165	return B_OK;
166}
167
168
169bigtime_t
170OpenSoundDeviceEngine::PlaybackLatency()
171{
172	bigtime_t latency = time_for_buffer(fDriverBufferSize, fMediaFormat);
173	bigtime_t cardLatency = CardLatency();
174	if (cardLatency == 0) {
175		// that's unrealistic, take matters into own hands
176		cardLatency = latency / 3;
177	}
178	latency += cardLatency;
179//	PRINT(("PlaybackLatency: odelay %d latency %Ld card %Ld\n",
180//		fDriverBufferSize, latency, CardLatency()));
181	return latency;
182}
183
184
185bigtime_t
186OpenSoundDeviceEngine::RecordingLatency()
187{
188	return 0LL; //XXX
189}
190
191
192int OpenSoundDeviceEngine::GetChannels(void)
193{
194	int chans = -1;
195	CALLED();
196	if (ioctl(fFD, SNDCTL_DSP_CHANNELS, &chans, sizeof(int)) < 0) {
197		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
198				__FUNCTION__, "SNDCTL_DSP_CHANNELS", strerror(errno)));
199		return -1;
200	}
201	return chans;
202}
203
204status_t OpenSoundDeviceEngine::SetChannels(int chans)
205{
206	CALLED();
207	if (ioctl(fFD, SNDCTL_DSP_CHANNELS, &chans, sizeof(int)) < 0) {
208		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
209				__FUNCTION__, "SNDCTL_DSP_CHANNELS", strerror(errno)));
210		return EIO;
211	}
212	PRINT(("OpenSoundDeviceEngine::%s: %d\n", __FUNCTION__, chans));
213	return B_OK;
214}
215
216//XXX: either GetFormat*s*() or cache SetFormat()!
217int OpenSoundDeviceEngine::GetFormat(void)
218{
219	int fmt = -1;
220	CALLED();
221	if (ioctl(fFD, SNDCTL_DSP_GETFMTS, &fmt, sizeof(int)) < 0) {
222		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
223				__FUNCTION__, "SNDCTL_DSP_GETFMTS", strerror(errno)));
224		return -1;
225	}
226	return fmt;
227}
228
229status_t OpenSoundDeviceEngine::SetFormat(int fmt)
230{
231	CALLED();
232	if (ioctl(fFD, SNDCTL_DSP_SETFMT, &fmt, sizeof(int)) < 0) {
233		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
234				__FUNCTION__, "SNDCTL_DSP_SETFMT", strerror(errno)));
235		return EIO;
236	}
237	PRINT(("OpenSoundDeviceEngine::%s: 0x%08x\n", __FUNCTION__, fmt));
238	return B_OK;
239}
240
241int OpenSoundDeviceEngine::GetSpeed(void)
242{
243	int speed = -1;
244	CALLED();
245	if (ioctl(fFD, SNDCTL_DSP_SPEED, &speed, sizeof(int)) < 0) {
246		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
247				__FUNCTION__, "SNDCTL_DSP_SPEED", strerror(errno)));
248		return -1;
249	}
250	return speed;
251}
252
253status_t OpenSoundDeviceEngine::SetSpeed(int speed)
254{
255	CALLED();
256	if (ioctl(fFD, SNDCTL_DSP_SPEED, &speed, sizeof(int)) < 0) {
257		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
258				__FUNCTION__, "SNDCTL_DSP_SPEED", strerror(errno)));
259		return EIO;
260	}
261	PRINT(("OpenSoundDeviceEngine::%s: %d\n", __FUNCTION__, speed));
262	return B_OK;
263}
264
265
266size_t OpenSoundDeviceEngine::GetISpace(audio_buf_info *info)
267{
268	audio_buf_info abinfo;
269	CALLED();
270	if (!info)
271		info = &abinfo;
272	memset(info, 0, sizeof(audio_buf_info));
273	if (!(fOpenMode & OPEN_READ))
274		return 0;
275	if (ioctl(fFD, SNDCTL_DSP_GETISPACE, info, sizeof(audio_buf_info)) < 0) {
276		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
277				__FUNCTION__, "SNDCTL_DSP_GETISPACE", strerror(errno)));
278		return EIO;
279	}
280	//PRINT(("OpenSoundDeviceEngine::%s: ISPACE: { bytes=%d, fragments=%d, fragsize=%d, fragstotal=%d }\n", __FUNCTION__, info->bytes, info->fragments, info->fragsize, info->fragstotal));
281	return info->bytes;
282}
283
284
285size_t OpenSoundDeviceEngine::GetOSpace(audio_buf_info *info)
286{
287	audio_buf_info abinfo;
288	//CALLED();
289	if (!info)
290		info = &abinfo;
291	memset(info, 0, sizeof(audio_buf_info));
292	if (!(fOpenMode & OPEN_WRITE))
293		return 0;
294	if (ioctl(fFD, SNDCTL_DSP_GETOSPACE, info, sizeof(audio_buf_info)) < 0) {
295		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
296				__FUNCTION__, "SNDCTL_DSP_GETOSPACE", strerror(errno)));
297		return EIO;
298	}
299	//PRINT(("OpenSoundDeviceEngine::%s: OSPACE: { bytes=%d, fragments=%d, fragsize=%d, fragstotal=%d }\n", __FUNCTION__, info->bytes, info->fragments, info->fragsize, info->fragstotal));
300	return info->bytes;
301}
302
303
304int64
305OpenSoundDeviceEngine::GetCurrentIPtr(int32 *fifoed, oss_count_t *info)
306{
307	oss_count_t ocount;
308	count_info cinfo;
309	CALLED();
310	if (!info)
311		info = &ocount;
312	memset(info, 0, sizeof(oss_count_t));
313	if (!(fOpenMode & OPEN_READ))
314		return 0;
315	if (ioctl(fFD, SNDCTL_DSP_CURRENT_IPTR, info, sizeof(oss_count_t)) < 0) {
316		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
317				__FUNCTION__, "SNDCTL_DSP_CURRENT_IPTR", strerror(errno)));
318		//return EIO;
319		// fallback: try GET*PTR
320		if (ioctl(fFD, SNDCTL_DSP_GETIPTR, &cinfo, sizeof(count_info)) < 0) {
321			PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
322					__FUNCTION__, "SNDCTL_DSP_GETIPTR", strerror(errno)));
323			return 0;
324		}
325		// it's probably wrong...
326		info->samples = cinfo.bytes / (fMediaFormat.u.raw_audio.channel_count
327						 		* (fMediaFormat.AudioFormat() & media_raw_audio_format::B_AUDIO_SIZE_MASK));
328		info->fifo_samples = 0;
329	}
330	PRINT(("OpenSoundDeviceEngine::%s: IPTR: { samples=%Ld, fifo_samples=%d }\n", __FUNCTION__, info->samples, info->fifo_samples));
331	if (fifoed)
332		*fifoed = info->fifo_samples;
333	return info->samples;
334}
335
336
337int64
338OpenSoundDeviceEngine::GetCurrentOPtr(int32* fifoed, size_t* fragmentPos)
339{
340	CALLED();
341
342	if (!(fOpenMode & OPEN_WRITE)) {
343		if (fifoed != NULL)
344			*fifoed = 0;
345		return 0;
346	}
347
348	oss_count_t info;
349	memset(&info, 0, sizeof(oss_count_t));
350
351	if (ioctl(fFD, SNDCTL_DSP_CURRENT_OPTR, &info, sizeof(oss_count_t)) < 0) {
352		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
353				__FUNCTION__, "SNDCTL_DSP_CURRENT_OPTR", strerror(errno)));
354
355		return 0;
356	}
357
358	if (fragmentPos != NULL) {
359		count_info cinfo;
360		if (ioctl(fFD, SNDCTL_DSP_GETOPTR, &cinfo, sizeof(count_info)) < 0) {
361			PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
362					__FUNCTION__, "SNDCTL_DSP_GETOPTR", strerror(errno)));
363			return 0;
364		}
365		*fragmentPos = cinfo.ptr;
366	}
367
368//	PRINT(("OpenSoundDeviceEngine::%s: OPTR: { samples=%Ld, "
369//		"fifo_samples=%d }\n", __FUNCTION__, info->samples,
370//		info->fifo_samples));
371	if (fifoed != NULL)
372		*fifoed = info.fifo_samples;
373	return info.samples;
374}
375
376
377int32
378OpenSoundDeviceEngine::GetIOverruns()
379{
380	audio_errinfo info;
381	CALLED();
382	memset(&info, 0, sizeof(info));
383	if (!(fOpenMode & OPEN_WRITE))
384		return 0;
385	if (ioctl(fFD, SNDCTL_DSP_GETERROR, &info, sizeof(info)) < 0) {
386		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
387				__FUNCTION__, "SNDCTL_DSP_GETERROR", strerror(errno)));
388		return 0;
389	}
390	PRINT(("OpenSoundDeviceEngine::%s: IOVERRUNS: { overruns=%d }\n", __FUNCTION__, info.rec_overruns));
391	return info.rec_overruns;
392}
393
394
395int32
396OpenSoundDeviceEngine::GetOUnderruns()
397{
398	audio_errinfo info;
399	CALLED();
400	memset(&info, 0, sizeof(info));
401	if (!(fOpenMode & OPEN_WRITE))
402		return 0;
403	if (ioctl(fFD, SNDCTL_DSP_GETERROR, &info, sizeof(info)) < 0) {
404		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
405				__FUNCTION__, "SNDCTL_DSP_GETERROR", strerror(errno)));
406		return 0;
407	}
408	//PRINT(("OpenSoundDeviceEngine::%s: OUNDERRUNS: { underruns=%d }\n", __FUNCTION__, info.play_underruns));
409	return info.play_underruns;
410}
411
412
413size_t
414OpenSoundDeviceEngine::DriverBufferSize() const
415{
416	return fDriverBufferSize;
417}
418
419
420status_t OpenSoundDeviceEngine::StartRecording(void)
421{
422	CALLED();
423	oss_syncgroup group;
424	group.id = 0;
425	group.mode = PCM_ENABLE_INPUT;
426	if (ioctl(fFD, SNDCTL_DSP_SYNCGROUP, &group, sizeof(group)) < 0) {
427		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
428				__FUNCTION__, "SNDCTL_DSP_SYNCGROUP", strerror(errno)));
429		return EIO;
430	}
431	if (ioctl(fFD, SNDCTL_DSP_SYNCSTART, &group.id, sizeof(group.id)) < 0) {
432		PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
433				__FUNCTION__, "SNDCTL_DSP_SYNCSTART", strerror(errno)));
434		return EIO;
435	}
436	return B_OK;
437}
438
439
440status_t
441OpenSoundDeviceEngine::WildcardFormatFor(int fmt, media_format &format,
442	bool rec)
443{
444	status_t err;
445	CALLED();
446	fmt &= rec ? Info()->iformats : Info()->oformats;
447	if (fmt == 0)
448		return B_MEDIA_BAD_FORMAT;
449	err = OpenSoundDevice::get_media_format_for(fmt, format);
450	if (err < B_OK)
451		return err;
452	char buf[1024];
453	string_for_format(format, buf, 1024);
454	if (format.type == B_MEDIA_RAW_AUDIO) {
455		format.u.raw_audio = media_multi_audio_format::wildcard;
456		format.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
457		// single rate supported
458		if (Info()->min_rate == Info()->max_rate)
459			format.u.raw_audio.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
460	} else if (format.type == B_MEDIA_ENCODED_AUDIO) {
461		format.u.encoded_audio.output = media_multi_audio_format::wildcard;
462		//format.u.encoded_audio.output.byte_order = B_MEDIA_HOST_ENDIAN;
463		// single rate supported
464		//if (Info()->min_rate == Info()->max_rate)
465		//	format.u.encoded_audio.output.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
466	} else
467		return EINVAL;
468	PRINT(("%s: %s\n", __FUNCTION__, buf));
469	return B_OK;
470}
471
472
473status_t OpenSoundDeviceEngine::PreferredFormatFor(int fmt, media_format &format, bool rec)
474{
475	status_t err;
476	CALLED();
477	fmt &= rec ? Info()->iformats : Info()->oformats;
478	if (fmt == 0)
479		return B_MEDIA_BAD_FORMAT;
480	err = WildcardFormatFor(fmt, format);
481	if (err < B_OK)
482		return err;
483	if (format.type == B_MEDIA_RAW_AUDIO) {
484		media_multi_audio_format &raw = format.u.raw_audio;
485		//format.u.raw_audio.channel_count = Info()->max_channels;
486		raw.byte_order = B_MEDIA_HOST_ENDIAN;
487		raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
488		raw.buffer_size = DEFAULT_BUFFER_SIZE;
489		/*if (rec)
490			raw.buffer_size = 2048;*/
491/*
492		format.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
493		format.u.raw_audio.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
494		format.u.raw_audio.buffer_size = DEFAULT_BUFFER_SIZE;
495*/
496	} else if (format.type == B_MEDIA_ENCODED_AUDIO) {
497		media_raw_audio_format &raw = format.u.encoded_audio.output;
498		//format.u.encoded_audio.output.channel_count = Info()->max_channels;
499		raw.byte_order = B_MEDIA_HOST_ENDIAN;
500		// single rate supported
501		if (Info()->min_rate == Info()->max_rate)
502			raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
503		raw.buffer_size = DEFAULT_BUFFER_SIZE;
504	} else
505		return EINVAL;
506	char buf[1024];
507	string_for_format(format, buf, 1024);
508	PRINT(("%s: %s\n", __FUNCTION__, buf));
509	return B_OK;
510}
511
512
513status_t OpenSoundDeviceEngine::AcceptFormatFor(int fmt, media_format &format, bool rec)
514{
515	status_t err;
516	int afmt = 0;
517	char buf[1024];
518	CALLED();
519	fmt &= rec ? Info()->iformats : Info()->oformats;
520
521	if (fmt == 0)
522		return B_MEDIA_BAD_FORMAT;
523	media_format wc;
524	err = WildcardFormatFor(fmt, wc);
525	if (err < B_OK)
526		return err;
527
528	err = Open(rec ? OPEN_READ : OPEN_WRITE);
529	if (err < B_OK)
530		return err;
531
532	if (format.type == B_MEDIA_RAW_AUDIO) {
533		media_multi_audio_format &raw = format.u.raw_audio;
534
535		// channel count
536		raw.channel_count = MAX((unsigned)(Info()->min_channels), MIN((unsigned)(Info()->max_channels), raw.channel_count));
537		err = SetChannels(raw.channel_count);
538		if (err < B_OK) {
539			Close();
540			return err;
541		}
542
543		PRINT(("%s:step1  fmt=0x%08x, raw.format=0x%08lx\n", __FUNCTION__, fmt, raw.format));
544		// if specified, try it
545		if (raw.format)
546			afmt = OpenSoundDevice::convert_media_format_to_oss_format(raw.format);
547		afmt &= fmt;
548		PRINT(("%s:step2 afmt=0x%08x\n", __FUNCTION__, afmt));
549		// select the best as default
550		if (afmt == 0) {
551			afmt = OpenSoundDevice::select_oss_format(fmt);
552			raw.format = OpenSoundDevice::convert_oss_format_to_media_format(afmt);
553			//Close();
554			//return B_MEDIA_BAD_FORMAT;
555		}
556		PRINT(("%s:step3 afmt=0x%08x\n", __FUNCTION__, afmt));
557		// convert back
558		raw.format = OpenSoundDevice::convert_oss_format_to_media_format(afmt);
559		PRINT(("%s:step4 afmt=0x%08x, raw.format=0x%08lx\n", __FUNCTION__, afmt, raw.format));
560		raw.valid_bits = OpenSoundDevice::convert_oss_format_to_valid_bits(afmt);
561
562		err = SetFormat(afmt);
563		if (err < B_OK) {
564			Close();
565			return err;
566		}
567
568		// endianness
569		raw.byte_order = OpenSoundDevice::convert_oss_format_to_endian(afmt);
570
571		// sample rate
572		raw.frame_rate = OpenSoundDevice::select_oss_rate(Info(), raw.frame_rate);		// measured in Hertz
573		//raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
574		err = SetSpeed(OpenSoundDevice::convert_media_rate_to_oss_rate(raw.frame_rate));
575		if (err < B_OK) {
576			Close();
577			return err;
578		}
579
580		// retrieve the driver buffer size (it's important to do this
581		// after all the other setup, since OSS may have adjusted it, and
582		// also weird things happen if this ioctl() is done before other
583		// setup itctl()s)
584		audio_buf_info abinfo;
585		memset(&abinfo, 0, sizeof(audio_buf_info));
586		if (ioctl(fFD, SNDCTL_DSP_GETOSPACE, &abinfo, sizeof(audio_buf_info)) < 0) {
587			fprintf(stderr, "failed to retrieve driver buffer size!\n");
588			abinfo.bytes = 0;
589		}
590		fDriverBufferSize = abinfo.bytes;
591
592		raw.buffer_size = fDriverBufferSize;
593
594	} else if (format.type == B_MEDIA_ENCODED_AUDIO) {
595		media_raw_audio_format &raw = format.u.encoded_audio.output;
596		// XXX: do we really have to do this ?
597		raw.channel_count = MAX((unsigned)(Info()->min_channels), MIN((unsigned)(Info()->max_channels), raw.channel_count));
598		raw.byte_order = B_MEDIA_HOST_ENDIAN;
599		raw.frame_rate = OpenSoundDevice::select_oss_rate(Info(), raw.frame_rate);		// measured in Hertz
600		//raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
601		raw.buffer_size = DEFAULT_BUFFER_SIZE;
602
603	} else {
604		PRINT(("%s: unknown media type\n", __FUNCTION__));
605		Close();
606		return EINVAL;
607	}
608	// cache it
609	fMediaFormat = format;
610
611	string_for_format(format, buf, 1024);
612	PRINT(("%s: %s\n", __FUNCTION__, buf));
613	return B_OK;
614}
615
616
617status_t OpenSoundDeviceEngine::SpecializeFormatFor(int fmt, media_format &format, bool rec)
618{
619	status_t err;
620	int afmt = 0;
621	CALLED();
622	fmt &= rec ? Info()->iformats : Info()->oformats;
623	if (fmt == 0)
624		return B_MEDIA_BAD_FORMAT;
625	media_format wc;
626	err = WildcardFormatFor(fmt, wc);
627	if (err < B_OK)
628		return err;
629
630	err = Open(rec ? OPEN_READ : OPEN_WRITE);
631	if (err < B_OK)
632		return err;
633
634	if (format.type == B_MEDIA_RAW_AUDIO) {
635		media_multi_audio_format &raw = format.u.raw_audio;
636
637		PRINT(("%s:step1  fmt=0x%08x, raw.format=0x%08lx\n", __FUNCTION__, fmt, raw.format));
638		// select the best as default
639		if (!raw.format) {
640			afmt = OpenSoundDevice::select_oss_format(fmt);
641			raw.format = OpenSoundDevice::convert_oss_format_to_media_format(afmt);
642		}
643		// if specified, try it
644		if (raw.format)
645			afmt = OpenSoundDevice::convert_media_format_to_oss_format(raw.format);
646		afmt &= fmt;
647		PRINT(("%s:step2 afmt=0x%08x\n", __FUNCTION__, afmt));
648		if (afmt == 0) {
649			Close();
650			return B_MEDIA_BAD_FORMAT;
651		}
652		// convert back
653		raw.format = OpenSoundDevice::convert_oss_format_to_media_format(afmt);
654		PRINT(("%s:step4 afmt=0x%08x, raw.format=0x%08lx\n", __FUNCTION__, afmt, raw.format));
655		if (!raw.valid_bits)
656			raw.valid_bits = OpenSoundDevice::convert_oss_format_to_valid_bits(afmt);
657		if (raw.valid_bits != OpenSoundDevice::convert_oss_format_to_valid_bits(afmt)) {
658			Close();
659			return B_MEDIA_BAD_FORMAT;
660		}
661
662		err = SetFormat(afmt);
663		if (err < B_OK) {
664			Close();
665			return err;
666		}
667
668		// endianness
669		if (!raw.byte_order)
670			raw.byte_order = OpenSoundDevice::convert_oss_format_to_endian(afmt);
671		if ((int)raw.byte_order != OpenSoundDevice::convert_oss_format_to_endian(afmt)) {
672			Close();
673			return B_MEDIA_BAD_FORMAT;
674		}
675
676		// channel count
677		if (raw.channel_count == 0)
678			raw.channel_count = (unsigned)Info()->min_channels;
679		if ((int)raw.channel_count < Info()->min_channels
680			|| (int)raw.channel_count > Info()->max_channels)
681			return B_MEDIA_BAD_FORMAT;
682		err = SetChannels(raw.channel_count);
683		if (err < B_OK) {
684			Close();
685			return err;
686		}
687
688		// sample rate
689		if (!raw.frame_rate)
690			raw.frame_rate = Info()->max_rate;
691		//raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
692		err = SetSpeed(OpenSoundDevice::convert_media_rate_to_oss_rate(raw.frame_rate));
693		if (err < B_OK) {
694			Close();
695			return err;
696		}
697
698#if 0
699		raw.buffer_size = DEFAULT_BUFFER_SIZE
700						* (raw.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
701						* raw.channel_count;
702#endif
703		audio_buf_info abinfo;
704		if (ioctl(fFD, rec?SNDCTL_DSP_GETISPACE:SNDCTL_DSP_GETOSPACE, &abinfo, sizeof(abinfo)) < 0) {
705			PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
706				__FUNCTION__, "SNDCTL_DSP_GET?SPACE", strerror(errno)));
707			return -1;
708		}
709		PRINT(("OSS: %cSPACE: { bytes=%d, fragments=%d, fragsize=%d, fragstotal=%d }\n", rec?'I':'O', abinfo.bytes, abinfo.fragments, abinfo.fragsize, abinfo.fragstotal));
710		// cache the first one in the Device
711		// so StartThread() knows the number of frags
712		//if (!fFragments.fragstotal)
713		//	memcpy(&fFragments, &abinfo, sizeof(abinfo));
714
715		// make sure buffer size is less than the driver's own buffer ( /2 to keep some margin )
716		if (/*rec && raw.buffer_size &&*/ (int)raw.buffer_size > abinfo.fragsize * abinfo.fragstotal / 4)
717			return B_MEDIA_BAD_FORMAT;
718		if (!raw.buffer_size)
719			raw.buffer_size = abinfo.fragsize;//XXX
720/*						* (raw.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
721						* raw.channel_count;*/
722	} else if (format.type == B_MEDIA_ENCODED_AUDIO) {
723		media_raw_audio_format &raw = format.u.encoded_audio.output;
724		// XXX: do we really have to do this ?
725		raw.channel_count = MAX((unsigned)(Info()->min_channels), MIN((unsigned)(Info()->max_channels), raw.channel_count));
726		raw.byte_order = B_MEDIA_HOST_ENDIAN;
727		raw.frame_rate = OpenSoundDevice::select_oss_rate(Info(), raw.frame_rate);		// measured in Hertz
728		//raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
729		raw.buffer_size = DEFAULT_BUFFER_SIZE;
730
731	} else {
732		Close();
733		return EINVAL;
734	}
735	// cache it
736	fMediaFormat = format;
737	char buf[1024];
738	string_for_format(format, buf, 1024);
739	PRINT(("%s: %s\n", __FUNCTION__, buf));
740	return B_OK;
741}
742
743