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 %lld card %lld\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=%lld, 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=%lld, "
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%08" B_PRIx32 "\n",
544			__FUNCTION__, fmt, raw.format));
545		// if specified, try it
546		if (raw.format)
547			afmt = OpenSoundDevice::convert_media_format_to_oss_format(raw.format);
548		afmt &= fmt;
549		PRINT(("%s:step2 afmt=0x%08x\n", __FUNCTION__, afmt));
550		// select the best as default
551		if (afmt == 0) {
552			afmt = OpenSoundDevice::select_oss_format(fmt);
553			raw.format = OpenSoundDevice::convert_oss_format_to_media_format(afmt);
554			//Close();
555			//return B_MEDIA_BAD_FORMAT;
556		}
557		PRINT(("%s:step3 afmt=0x%08x\n", __FUNCTION__, afmt));
558		// convert back
559		raw.format = OpenSoundDevice::convert_oss_format_to_media_format(afmt);
560		PRINT(("%s:step4 afmt=0x%08x, raw.format=0x%08" B_PRIx32 "\n",
561			__FUNCTION__, afmt, raw.format));
562		raw.valid_bits = OpenSoundDevice::convert_oss_format_to_valid_bits(afmt);
563
564		err = SetFormat(afmt);
565		if (err < B_OK) {
566			Close();
567			return err;
568		}
569
570		// endianness
571		raw.byte_order = OpenSoundDevice::convert_oss_format_to_endian(afmt);
572
573		// sample rate
574		raw.frame_rate = OpenSoundDevice::select_oss_rate(Info(), raw.frame_rate);		// measured in Hertz
575		//raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
576		err = SetSpeed(OpenSoundDevice::convert_media_rate_to_oss_rate(raw.frame_rate));
577		if (err < B_OK) {
578			Close();
579			return err;
580		}
581
582		// retrieve the driver buffer size (it's important to do this
583		// after all the other setup, since OSS may have adjusted it, and
584		// also weird things happen if this ioctl() is done before other
585		// setup itctl()s)
586		audio_buf_info abinfo;
587		memset(&abinfo, 0, sizeof(audio_buf_info));
588		if (ioctl(fFD, SNDCTL_DSP_GETOSPACE, &abinfo, sizeof(audio_buf_info)) < 0) {
589			fprintf(stderr, "failed to retrieve driver buffer size!\n");
590			abinfo.bytes = 0;
591		}
592		fDriverBufferSize = abinfo.bytes;
593
594		raw.buffer_size = fDriverBufferSize;
595
596	} else if (format.type == B_MEDIA_ENCODED_AUDIO) {
597		media_raw_audio_format &raw = format.u.encoded_audio.output;
598		// XXX: do we really have to do this ?
599		raw.channel_count = MAX((unsigned)(Info()->min_channels), MIN((unsigned)(Info()->max_channels), raw.channel_count));
600		raw.byte_order = B_MEDIA_HOST_ENDIAN;
601		raw.frame_rate = OpenSoundDevice::select_oss_rate(Info(), raw.frame_rate);		// measured in Hertz
602		//raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
603		raw.buffer_size = DEFAULT_BUFFER_SIZE;
604
605	} else {
606		PRINT(("%s: unknown media type\n", __FUNCTION__));
607		Close();
608		return EINVAL;
609	}
610	// cache it
611	fMediaFormat = format;
612
613	string_for_format(format, buf, 1024);
614	PRINT(("%s: %s\n", __FUNCTION__, buf));
615	return B_OK;
616}
617
618
619status_t OpenSoundDeviceEngine::SpecializeFormatFor(int fmt, media_format &format, bool rec)
620{
621	status_t err;
622	int afmt = 0;
623	CALLED();
624	fmt &= rec ? Info()->iformats : Info()->oformats;
625	if (fmt == 0)
626		return B_MEDIA_BAD_FORMAT;
627	media_format wc;
628	err = WildcardFormatFor(fmt, wc);
629	if (err < B_OK)
630		return err;
631
632	err = Open(rec ? OPEN_READ : OPEN_WRITE);
633	if (err < B_OK)
634		return err;
635
636	if (format.type == B_MEDIA_RAW_AUDIO) {
637		media_multi_audio_format &raw = format.u.raw_audio;
638
639		PRINT(("%s:step1  fmt=0x%08x, raw.format=0x%08" B_PRIx32 "\n",
640			__FUNCTION__, fmt, raw.format));
641		// select the best as default
642		if (!raw.format) {
643			afmt = OpenSoundDevice::select_oss_format(fmt);
644			raw.format = OpenSoundDevice::convert_oss_format_to_media_format(afmt);
645		}
646		// if specified, try it
647		if (raw.format)
648			afmt = OpenSoundDevice::convert_media_format_to_oss_format(raw.format);
649		afmt &= fmt;
650		PRINT(("%s:step2 afmt=0x%08x\n", __FUNCTION__, afmt));
651		if (afmt == 0) {
652			Close();
653			return B_MEDIA_BAD_FORMAT;
654		}
655		// convert back
656		raw.format = OpenSoundDevice::convert_oss_format_to_media_format(afmt);
657		PRINT(("%s:step4 afmt=0x%08x, raw.format=0x%08" B_PRIx32 "\n",
658			__FUNCTION__, afmt, raw.format));
659		if (!raw.valid_bits)
660			raw.valid_bits = OpenSoundDevice::convert_oss_format_to_valid_bits(afmt);
661		if (raw.valid_bits != OpenSoundDevice::convert_oss_format_to_valid_bits(afmt)) {
662			Close();
663			return B_MEDIA_BAD_FORMAT;
664		}
665
666		err = SetFormat(afmt);
667		if (err < B_OK) {
668			Close();
669			return err;
670		}
671
672		// endianness
673		if (!raw.byte_order)
674			raw.byte_order = OpenSoundDevice::convert_oss_format_to_endian(afmt);
675		if ((int)raw.byte_order != OpenSoundDevice::convert_oss_format_to_endian(afmt)) {
676			Close();
677			return B_MEDIA_BAD_FORMAT;
678		}
679
680		// channel count
681		if (raw.channel_count == 0)
682			raw.channel_count = (unsigned)Info()->min_channels;
683		if ((int)raw.channel_count < Info()->min_channels
684			|| (int)raw.channel_count > Info()->max_channels)
685			return B_MEDIA_BAD_FORMAT;
686		err = SetChannels(raw.channel_count);
687		if (err < B_OK) {
688			Close();
689			return err;
690		}
691
692		// sample rate
693		if (!raw.frame_rate)
694			raw.frame_rate = Info()->max_rate;
695		//raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
696		err = SetSpeed(OpenSoundDevice::convert_media_rate_to_oss_rate(raw.frame_rate));
697		if (err < B_OK) {
698			Close();
699			return err;
700		}
701
702#if 0
703		raw.buffer_size = DEFAULT_BUFFER_SIZE
704						* (raw.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
705						* raw.channel_count;
706#endif
707		audio_buf_info abinfo;
708		if (ioctl(fFD, rec?SNDCTL_DSP_GETISPACE:SNDCTL_DSP_GETOSPACE, &abinfo, sizeof(abinfo)) < 0) {
709			PRINT(("OpenSoundDeviceEngine::%s: %s: %s\n",
710				__FUNCTION__, "SNDCTL_DSP_GET?SPACE", strerror(errno)));
711			return -1;
712		}
713		PRINT(("OSS: %cSPACE: { bytes=%d, fragments=%d, fragsize=%d, fragstotal=%d }\n", rec?'I':'O', abinfo.bytes, abinfo.fragments, abinfo.fragsize, abinfo.fragstotal));
714		// cache the first one in the Device
715		// so StartThread() knows the number of frags
716		//if (!fFragments.fragstotal)
717		//	memcpy(&fFragments, &abinfo, sizeof(abinfo));
718
719		// make sure buffer size is less than the driver's own buffer ( /2 to keep some margin )
720		if (/*rec && raw.buffer_size &&*/ (int)raw.buffer_size > abinfo.fragsize * abinfo.fragstotal / 4)
721			return B_MEDIA_BAD_FORMAT;
722		if (!raw.buffer_size)
723			raw.buffer_size = abinfo.fragsize;//XXX
724/*						* (raw.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
725						* raw.channel_count;*/
726	} else if (format.type == B_MEDIA_ENCODED_AUDIO) {
727		media_raw_audio_format &raw = format.u.encoded_audio.output;
728		// XXX: do we really have to do this ?
729		raw.channel_count = MAX((unsigned)(Info()->min_channels), MIN((unsigned)(Info()->max_channels), raw.channel_count));
730		raw.byte_order = B_MEDIA_HOST_ENDIAN;
731		raw.frame_rate = OpenSoundDevice::select_oss_rate(Info(), raw.frame_rate);		// measured in Hertz
732		//raw.frame_rate = OpenSoundDevice::convert_oss_rate_to_media_rate(Info()->max_rate);		// measured in Hertz
733		raw.buffer_size = DEFAULT_BUFFER_SIZE;
734
735	} else {
736		Close();
737		return EINVAL;
738	}
739	// cache it
740	fMediaFormat = format;
741	char buf[1024];
742	string_for_format(format, buf, 1024);
743	PRINT(("%s: %s\n", __FUNCTION__, buf));
744	return B_OK;
745}
746
747