xref: /haiku/src/add-ons/media/media-add-ons/opensound/OpenSoundNode.cpp (revision 959ff00ddee8411dabb09211f3bfbd52d87229da)
1 /*
2  * OpenSound media addon for BeOS and Haiku
3  *
4  * Copyright (c) 2007, François Revol (revol@free.fr)
5  * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr)
6  * Distributed under the terms of the MIT License.
7  */
8 #include "OpenSoundNode.h"
9 
10 #include <Autolock.h>
11 #include <Buffer.h>
12 #include <BufferGroup.h>
13 #include <Debug.h>
14 #include <MediaAddOn.h>
15 #include <MediaRoster.h>
16 #include <scheduler.h>
17 #include <String.h>
18 
19 #include <new>
20 #include <limits.h>
21 #include <signal.h>
22 #include <stdio.h>
23 #include <string.h>
24 
25 #ifdef DEBUG
26   #define PRINTING
27 #endif
28 #include "debug.h"
29 
30 #include "OpenSoundDevice.h"
31 #include "OpenSoundDeviceEngine.h"
32 #include "OpenSoundDeviceMixer.h"
33 #include "SupportFunctions.h"
34 
35 using std::nothrow;
36 
37 
38 class FunctionTracer {
39 public:
40 	FunctionTracer(const char* functionName)
41 		: fFunctionName(functionName)
42 	{
43 		printf("OpenSoundNode::%s()\n", fFunctionName.String());
44 	}
45 	 ~FunctionTracer()
46 	{
47 		printf("OpenSoundNode::%s() - leave\n", fFunctionName.String());
48 	}
49 	BString	fFunctionName;
50 };
51 
52 
53 // debugging
54 #ifdef TRACE
55 #	undef TRACE
56 #endif
57 #ifdef CALLED
58 #	undef CALLED
59 #endif
60 //#define TRACE_OSS_NODE
61 #ifdef TRACE_OSS_NODE
62 #	define TRACE(x...)		printf(x)
63 #	define CALLED(x...)		FunctionTracer _ft(__FUNCTION__)
64 #	define PRINTING
65 #else
66 #	define TRACE(x...)
67 #	define CALLED(x...)
68 #endif
69 
70 
71 class OpenSoundNode::NodeInput {
72 public:
73 	NodeInput(const media_input& input, int engineIndex, int ossFormatFlags,
74 			OpenSoundNode* node)
75 		: fNode(node),
76 		  fEngineIndex(engineIndex),
77 		  fRealEngine(NULL),
78 		  fOSSFormatFlags(ossFormatFlags),
79 
80 		  fInput(input),
81 		  fPreferredFormat(input.format),
82 		  	// Keep a version of the original preferred format,
83 		  	// in case we are re-connected and need to start "clean"
84 
85 		  fThread(-1),
86 		  fBuffers(4),
87 
88 		  fTestTonePhase(0)
89 	{
90 		CALLED();
91 
92 		fInput.format.u.raw_audio.format
93 			= media_raw_audio_format::wildcard.format;
94 	}
95 
96 	~NodeInput()
97 	{
98 		CALLED();
99 
100 		RecycleAllBuffers();
101 	}
102 
103 	status_t Write(void* data, size_t size)
104 	{
105 		CALLED();
106 
107 		ssize_t written = fRealEngine->Write(data, size);
108 
109 		if (written < 0)
110 			return (status_t)written;
111 		if (written < (ssize_t)size)
112 			return B_IO_ERROR;
113 
114 		return B_OK;
115 	}
116 
117 	void WriteTestTone(size_t bytes)
118 	{
119 		// phase of the sine wave
120 		uint8 buffer[bytes];
121 		float sampleRate = fInput.format.u.raw_audio.frame_rate;
122 
123 		const static int kSineBuffer[48] = {
124 			0, 4276, 8480, 12539, 16383, 19947, 23169, 25995,
125 			28377, 30272, 31650, 32486, 32767, 32486, 31650, 30272,
126 			28377, 25995, 23169, 19947, 16383, 12539, 8480, 4276,
127 			0, -4276, -8480, -12539, -16383, -19947, -23169, -25995,
128 			-28377, -30272, -31650, -32486, -32767, -32486, -31650, -30272,
129 			-28377, -25995, -23169, -19947, -16383, -12539, -8480, -4276
130 		};
131 
132 		short* b = (short*)buffer;
133 			// TODO: assumes 16 bit samples!
134 		int32 channels = fInput.format.u.raw_audio.channel_count;
135 		int32 frames = bytes / bytes_per_frame(fInput.format);
136 		for (int32 i = 0; i < frames; i ++) {
137 			// convert sample rate from 48000 to connected format
138 			uint32 p = (uint32)((fTestTonePhase * sampleRate) / 48000);
139 
140 			// prevent phase from integer overflow
141 			fTestTonePhase = (fTestTonePhase + 1) % 4800;
142 			for (int32 k = 0; k < channels; k++)
143 				b[k] = kSineBuffer[p % 48];
144 			b += channels;
145 		}
146 
147 		ssize_t written = fRealEngine->Write(buffer, bytes);
148 		if (written != (ssize_t)bytes) {
149 			// error
150 		}
151 	}
152 
153 	void RecycleAllBuffers()
154 	{
155 		CALLED();
156 
157 		// make sure all buffers are recycled, or we might hang
158 		// when told to quit
159 		while (BBuffer* buffer = (BBuffer*)fBuffers.RemoveItem(0L))
160 			buffer->Recycle();
161 	}
162 
163 	OpenSoundNode*			fNode;
164 	int32					fEngineIndex;
165 	OpenSoundDeviceEngine*	fRealEngine;
166 		// engine it's connected to. can be a shadow one (!= fEngineIndex)
167 	int						fOSSFormatFlags;
168 		// AFMT_* flags for this input
169 	media_input				fInput;
170 	media_format 			fPreferredFormat;
171 
172 	thread_id				fThread;
173 	BList					fBuffers;
174 		// contains BBuffer* pointers that have not yet played
175 
176 	uint32					fTestTonePhase;
177 };
178 
179 
180 class OpenSoundNode::NodeOutput {
181 public:
182 	NodeOutput(const media_output& output, const media_format& format)
183 		: fNode(NULL),
184 		  fEngineIndex(-1),
185 		  fRealEngine(NULL),
186 		  fOSSFormatFlags(0),
187 
188 		  fOutput(output),
189 		  fPreferredFormat(format),
190 
191 		  fThread(-1),
192 		  fBufferGroup(NULL),
193 		  fUsingOwnBufferGroup(true),
194 		  fOutputEnabled(true),
195 
196 		  fSamplesSent(0)
197 	{
198 		CALLED();
199 	}
200 
201 	~NodeOutput()
202 	{
203 		CALLED();
204 
205 		FreeBuffers();
206 	}
207 
208 	status_t AllocateBuffers(bigtime_t bufferDuration, bigtime_t latency)
209 	{
210 		TRACE("NodeOutput::AllocateBuffers(bufferDuration = %lld, "
211 			"latency = %lld)\n", bufferDuration, latency);
212 
213 		FreeBuffers();
214 
215 		// allocate enough buffers to span our downstream latency, plus one
216 		size_t size = fOutput.format.u.raw_audio.buffer_size;
217 		int32 count = int32(latency / bufferDuration + 1 + 1);
218 
219 		fBufferGroup = new (nothrow) BBufferGroup(size, count);
220 		fUsingOwnBufferGroup = true;
221 		return fBufferGroup != NULL ? fBufferGroup->InitCheck() : B_NO_MEMORY;
222 	}
223 
224 	status_t SetExternalBuffers(BBufferGroup* bufferGroup)
225 	{
226 		TRACE("NodeOutput::SetExternalBuffers(%p)\n", bufferGroup);
227 
228 		fBufferGroup = bufferGroup;
229 		fUsingOwnBufferGroup = false;
230 		return fBufferGroup->InitCheck();
231 	}
232 
233 	void FreeBuffers()
234 	{
235 		TRACE("NodeOutput::FreeBuffers(): %p (own %d)\n", fBufferGroup,
236 			fUsingOwnBufferGroup);
237 // TODO: it is not clear to me how buffer group responsibility is supposed
238 // to work properly. Appearantly, a consumer can use SetOutputBuffers(),
239 // which is a deprecated call in the BeOS API, with "willReclaim == true".
240 // In that case, we would not be responsible for deleting these buffers,
241 // but I don't understand what mechanism makes sure that we know about this.
242 // The documentation for SetBufferGroup() says you are supposed to delete
243 // the given buffer group. In any case, the fUsingOwnBufferGroup is correclty
244 // maintained as far as we are concerned, but I delete the buffers anyways,
245 // which is what the code was doing from the beginning and that worked. I
246 // have not tested yet, whether an external buffer group is passed to the node
247 // from the system mixer.
248 
249 //		if (fUsingOwnBufferGroup)
250 			delete fBufferGroup;
251 		fBufferGroup = NULL;
252 	}
253 
254 	BBuffer* FillNextBuffer(bigtime_t bufferDuration)
255 	{
256 		if (fBufferGroup == NULL)
257 			return NULL;
258 
259 		BBuffer* buffer = fBufferGroup->RequestBuffer(
260 			fOutput.format.u.raw_audio.buffer_size, bufferDuration);
261 
262 		// if we fail to get a buffer (for example, if the request times out),
263 		// we skip this buffer and go on to the next, to avoid locking up the
264 		// control thread
265 		if (!buffer)
266 			return NULL;
267 
268 		// now fill it with data
269 		ssize_t sizeUsed = fRealEngine->Read(buffer->Data(),
270 			fOutput.format.u.raw_audio.buffer_size);
271 		if (sizeUsed < 0) {
272 			TRACE("NodeOutput::%s: %s\n", __FUNCTION__,
273 				strerror(sizeUsed));
274 			buffer->Recycle();
275 			return NULL;
276 		}
277 		if (sizeUsed < (ssize_t)fOutput.format.u.raw_audio.buffer_size) {
278 			TRACE("NodeOutput::%s: requested %d, got %d\n", __FUNCTION__,
279 				fOutput.format.u.raw_audio.buffer_size, sizeUsed);
280 		}
281 
282 		media_header* hdr = buffer->Header();
283 		if (hdr != NULL) {
284 			hdr->type = B_MEDIA_RAW_AUDIO;
285 			hdr->size_used = sizeUsed;
286 		}
287 
288 		return buffer;
289 	}
290 
291 	OpenSoundNode*			fNode;
292 	int32					fEngineIndex;
293 	OpenSoundDeviceEngine*	fRealEngine;
294 		// engine it's connected to. can be a shadow one (!= fEngineIndex)
295 	int						fOSSFormatFlags;
296 		// AFMT_* flags for this output
297 	media_output			fOutput;
298 	media_format 			fPreferredFormat;
299 
300 	thread_id				fThread;
301 	BBufferGroup*			fBufferGroup;
302 	bool					fUsingOwnBufferGroup;
303 	bool 					fOutputEnabled;
304 	uint64 					fSamplesSent;
305 };
306 
307 
308 // #pragma mark - OpenSoundNode
309 
310 
311 OpenSoundNode::OpenSoundNode(BMediaAddOn* addon, const char* name,
312 		OpenSoundDevice* device, int32 internal_id, BMessage* config)
313 	: BMediaNode(name),
314 	  BBufferConsumer(B_MEDIA_RAW_AUDIO),
315 	  BBufferProducer(B_MEDIA_RAW_AUDIO),
316 	  BTimeSource(),
317 	  BMediaEventLooper(),
318 
319 	  fInitCheckStatus(B_NO_INIT),
320 	  fDevice(device),
321 
322 	  fTimeSourceStarted(false),
323 	  fTimeSourceStartTime(0),
324 
325 	  fWeb(NULL),
326 	  fConfig(0UL)
327 {
328 	CALLED();
329 
330 	if (fDevice == NULL)
331 		return;
332 
333 	fAddOn = addon;
334 	fId = internal_id;
335 
336 	AddNodeKind(B_PHYSICAL_OUTPUT);
337 	AddNodeKind(B_PHYSICAL_INPUT);
338 
339 	// initialize our preferred format object
340 	// TODO: this should go away! should use engine's preferred for each afmt.
341 #if 1
342 	memset(&fPreferredFormat, 0, sizeof(fPreferredFormat));
343 		// set everything to wildcard first
344 	fPreferredFormat.type = B_MEDIA_RAW_AUDIO;
345 	fPreferredFormat.u.raw_audio = media_multi_audio_format::wildcard;
346 	fPreferredFormat.u.raw_audio.channel_count = 2;
347 	fPreferredFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
348 	OpenSoundDeviceEngine *engine = fDevice->EngineAt(0);
349 	if (engine) {
350 		const oss_audioinfo* ai = engine->Info();
351 		int fmt = OpenSoundDevice::select_oss_format(ai->oformats);
352 		fPreferredFormat.u.raw_audio.format
353 			= OpenSoundDevice::convert_oss_format_to_media_format(fmt);
354 		fPreferredFormat.u.raw_audio.valid_bits
355 			= OpenSoundDevice::convert_oss_format_to_valid_bits(fmt);
356 		// TODO: engine->PreferredChannels() ? (caps & DSP_CH*)
357 		fPreferredFormat.u.raw_audio.frame_rate
358 			= OpenSoundDevice::convert_oss_rate_to_media_rate(ai->max_rate);		// measured in Hertz
359 	}
360 
361 	// TODO: Use the OSS suggested buffer size via SNDCTL_DSP_GETBLKSIZE ?
362 	fPreferredFormat.u.raw_audio.buffer_size = DEFAULT_BUFFER_SIZE
363 		* (fPreferredFormat.u.raw_audio.format
364 			& media_raw_audio_format::B_AUDIO_SIZE_MASK)
365 		* fPreferredFormat.u.raw_audio.channel_count;
366 #endif
367 
368 	if (config != NULL) {
369 		fConfig = *config;
370 		PRINT_OBJECT(fConfig);
371 	}
372 
373 	fInitCheckStatus = B_OK;
374 }
375 
376 
377 OpenSoundNode::~OpenSoundNode()
378 {
379 	CALLED();
380 
381 	fAddOn->GetConfigurationFor(this, NULL);
382 
383 	int32 count = fInputs.CountItems();
384 	for (int32 i = 0; i < count; i++)
385 		delete (NodeInput*)fInputs.ItemAtFast(i);
386 	count = fOutputs.CountItems();
387 	for (int32 i = 0; i < count; i++)
388 		delete (NodeOutput*)fOutputs.ItemAtFast(i);
389 
390 	BMediaEventLooper::Quit();
391 
392 	fWeb = NULL;
393 }
394 
395 
396 status_t
397 OpenSoundNode::InitCheck() const
398 {
399 	CALLED();
400 	return fInitCheckStatus;
401 }
402 
403 
404 // #pragma mark - BMediaNode
405 
406 
407 BMediaAddOn*
408 OpenSoundNode::AddOn(int32* internal_id) const
409 {
410 	CALLED();
411 	// BeBook says this only gets called if we were in an add-on.
412 	if (fAddOn != 0) {
413 		// If we get a null pointer then we just won't write.
414 		if (internal_id != 0) {
415 			*internal_id = fId;
416 		}
417 	}
418 	return fAddOn;
419 }
420 
421 
422 void
423 OpenSoundNode::Preroll()
424 {
425 	CALLED();
426 	// XXX:Performance opportunity
427 	BMediaNode::Preroll();
428 }
429 
430 
431 status_t
432 OpenSoundNode::HandleMessage(int32 message, const void* data, size_t size)
433 {
434 	CALLED();
435 	return B_ERROR;
436 }
437 
438 
439 void
440 OpenSoundNode::NodeRegistered()
441 {
442 	CALLED();
443 
444 	if (fInitCheckStatus != B_OK) {
445 		ReportError(B_NODE_IN_DISTRESS);
446 		return;
447 	}
448 
449 	SetPriority(B_REAL_TIME_PRIORITY);
450 
451 	Run();
452 
453 	TRACE("NodeRegistered: %d engines\n", fDevice->CountEngines());
454 	for (int32 i = 0; i < fDevice->CountEngines(); i++) {
455 		OpenSoundDeviceEngine* engine = fDevice->EngineAt(i);
456 		if (engine == NULL)
457 			continue;
458 		// skip shadow engines
459 		if (engine->Caps() & PCM_CAP_SHADOW)
460 			continue;
461 		// skip engines that don't have outputs
462 		if ((engine->Caps() & PCM_CAP_OUTPUT) == 0)
463 			continue;
464 
465 		TRACE("NodeRegistered: engine[%d]: .caps=0x%08x, .oformats=0x%08x\n",
466 			i, engine->Caps(), engine->Info()->oformats);
467 
468 		// iterate over all possible OSS formats/encodings and
469 		// create a NodeInput for each
470 		for (int32 f = 0; gSupportedFormats[f]; f++) {
471 			// figure out if the engine supports the given format
472 			int fmt = gSupportedFormats[f] & engine->Info()->oformats;
473 			if (fmt == 0)
474 				continue;
475 			TRACE("NodeRegistered() : creating an input for engine %i, "
476 				"format[%i]\n", i, f);
477 
478 			media_input mediaInput;
479 			status_t err = engine->PreferredFormatFor(fmt, mediaInput.format);
480 			if (err < B_OK)
481 				continue;
482 
483 			mediaInput.destination.port = ControlPort();
484 			mediaInput.destination.id = fInputs.CountItems();
485 			mediaInput.node = Node();
486 			char *prefix = "";
487 			if (strstr(engine->Info()->name, "SPDIF"))
488 				prefix = "S/PDIF ";
489 			sprintf(mediaInput.name, "%sOutput %ld (%s)", prefix,
490 				mediaInput.destination.id, gSupportedFormatsNames[f]);
491 
492 			NodeInput* input = new (nothrow) NodeInput(mediaInput, i, fmt,
493 				this);
494 			if (input == NULL || !fInputs.AddItem(input)) {
495 				delete input;
496 				continue;
497 			}
498 		}
499 	}
500 
501 	for (int32 i = 0; i < fDevice->CountEngines(); i++) {
502 		OpenSoundDeviceEngine* engine = fDevice->EngineAt(i);
503 		if (engine == NULL)
504 			continue;
505 		// skip shadow engines
506 		if (engine->Caps() & PCM_CAP_SHADOW)
507 			continue;
508 		// skip engines that don't have inputs
509 		if ((engine->Caps() & PCM_CAP_INPUT) == 0)
510 			continue;
511 
512 		TRACE("NodeRegistered: engine[%d]: .caps=0x%08x, .iformats=0x%08x\n",
513 			i, engine->Caps(), engine->Info()->iformats);
514 
515 		for (int32 f = 0; gSupportedFormats[f]; f++) {
516 			int fmt = gSupportedFormats[f] & engine->Info()->iformats;
517 			if (fmt == 0)
518 				continue;
519 			TRACE("NodeRegistered() : creating an output for engine %i, "
520 				"format[%i]\n", i, f);
521 
522 			media_format preferredFormat;
523 			status_t err = engine->PreferredFormatFor(fmt, preferredFormat);
524 			if (err < B_OK)
525 				continue;
526 
527 			media_output mediaOutput;
528 
529 			mediaOutput.format = preferredFormat;
530 			mediaOutput.destination = media_destination::null;
531 			mediaOutput.source.port = ControlPort();
532 			mediaOutput.source.id = fOutputs.CountItems();
533 			mediaOutput.node = Node();
534 			char *prefix = "";
535 			if (strstr(engine->Info()->name, "SPDIF"))
536 				prefix = "S/PDIF ";
537 			sprintf(mediaOutput.name, "%sInput %ld (%s)", prefix,
538 				mediaOutput.source.id, gSupportedFormatsNames[f]);
539 
540 			NodeOutput* output = new (nothrow) NodeOutput(mediaOutput,
541 				preferredFormat);
542 			if (output == NULL || !fOutputs.AddItem(output)) {
543 				delete output;
544 				continue;
545 			}
546 //			output->fPreferredFormat.u.raw_audio.channel_count
547 //				= engine->Info()->max_channels;
548 			output->fOutput.format = output->fPreferredFormat;
549 			output->fEngineIndex = i;
550 			output->fOSSFormatFlags = fmt;
551 			output->fNode = this;
552 		}
553 	}
554 
555 	// set up our parameter web
556 	fWeb = MakeParameterWeb();
557 	SetParameterWeb(fWeb);
558 
559 	// apply configuration
560 #ifdef PRINTING
561 	bigtime_t start = system_time();
562 #endif
563 
564 	int32 index = 0;
565 	int32 parameterID = 0;
566 	while (fConfig.FindInt32("parameterID", index, &parameterID) == B_OK) {
567 		const void* data;
568 		ssize_t size;
569 		if (fConfig.FindData("parameterData", B_RAW_TYPE, index, &data,
570 			&size) == B_OK) {
571 			SetParameterValue(parameterID, TimeSource()->Now(), data, size);
572 		}
573 		index++;
574 	}
575 
576 	TRACE("apply configuration in : %lldµs\n", system_time() - start);
577 }
578 
579 
580 status_t
581 OpenSoundNode::RequestCompleted(const media_request_info& info)
582 {
583 	CALLED();
584 	return B_OK;
585 }
586 
587 
588 void
589 OpenSoundNode::SetTimeSource(BTimeSource* timeSource)
590 {
591 	CALLED();
592 }
593 
594 
595 // #pragma mark - BBufferConsumer
596 
597 
598 //!	Someone, probably the producer, is asking you about this format.
599 //	Give your honest opinion, possibly modifying *format. Do not ask
600 //	upstream producer about the format, since he's synchronously
601 //	waiting for your reply.
602 status_t
603 OpenSoundNode::AcceptFormat(const media_destination& dest,
604 	media_format* format)
605 {
606 	// Check to make sure the format is okay, then remove
607 	// any wildcards corresponding to our requirements.
608 
609 	CALLED();
610 
611 	NodeInput* channel = _FindInput(dest);
612 
613 	if (channel == NULL) {
614 		fprintf(stderr, "OpenSoundNode::AcceptFormat()"
615 			" - B_MEDIA_BAD_DESTINATION");
616 		return B_MEDIA_BAD_DESTINATION;
617 			// we only have one input so that better be it
618 	}
619 
620 	if (format == NULL) {
621 		fprintf(stderr, "OpenSoundNode::AcceptFormat() - B_BAD_VALUE\n");
622 		return B_BAD_VALUE;
623 	}
624 
625 /*	media_format * myFormat = GetFormat();
626 	fprintf(stderr,"proposed format: ");
627 	print_media_format(format);
628 	fprintf(stderr,"\n");
629 	fprintf(stderr,"my format: ");
630 	print_media_format(myFormat);
631 	fprintf(stderr,"\n");*/
632 	// Be's format_is_compatible doesn't work.
633 //	if (!format_is_compatible(*format,*myFormat)) {
634 
635 	BAutolock L(fDevice->Locker());
636 
637 	OpenSoundDeviceEngine* engine = fDevice->NextFreeEngineAt(
638 		channel->fEngineIndex, false);
639 	if (!engine)
640 		return B_BUSY;
641 
642 	status_t err = engine->AcceptFormatFor(channel->fOSSFormatFlags, *format,
643 		false);
644 	if (err < B_OK)
645 		return err;
646 
647 	channel->fRealEngine = engine;
648 
649 /*
650 	if ( format->type != B_MEDIA_RAW_AUDIO ) {
651 		fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
652 		return B_MEDIA_BAD_FORMAT;
653 	}
654 */
655 
656 	//channel->fInput.format = channel->fPreferredFormat;
657 	channel->fInput.format = *format;
658 
659 	/*if(format->u.raw_audio.format == media_raw_audio_format::B_AUDIO_FLOAT
660 		&& channel->fPreferredFormat.u.raw_audio.format
661 			== media_raw_audio_format::B_AUDIO_SHORT)
662 		format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
663 	else*/
664 /*
665 	format->u.raw_audio.format = channel->fPreferredFormat.u.raw_audio.format;
666 	format->u.raw_audio.valid_bits
667 		= channel->fPreferredFormat.u.raw_audio.valid_bits;
668 
669 	format->u.raw_audio.frame_rate
670 		= channel->fPreferredFormat.u.raw_audio.frame_rate;
671 	format->u.raw_audio.channel_count
672 		= channel->fPreferredFormat.u.raw_audio.channel_count;
673 	format->u.raw_audio.byte_order
674 		= B_MEDIA_HOST_ENDIAN;
675 
676 	format->u.raw_audio.buffer_size
677 		= DEFAULT_BUFFER_SIZE
678 			* (format->u.raw_audio.format
679 				& media_raw_audio_format::B_AUDIO_SIZE_MASK)
680 			* format->u.raw_audio.channel_count;
681 */
682 
683 //	media_format myFormat;
684 //	GetFormat(&myFormat);
685 //	if (!format_is_acceptible(*format,myFormat)) {
686 //		fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
687 //		return B_MEDIA_BAD_FORMAT;
688 //	}
689 
690 	return B_OK;
691 }
692 
693 
694 status_t
695 OpenSoundNode::GetNextInput(int32* cookie, media_input* out_input)
696 {
697 	CALLED();
698 
699 	// let's not crash even if they are stupid
700 	if (out_input == NULL) {
701 		// no place to write!
702 		fprintf(stderr,"OpenSoundNode::GetNextInput() - B_BAD_VALUE\n");
703 		return B_BAD_VALUE;
704 	}
705 
706 	if (*cookie >= fInputs.CountItems() || *cookie < 0)
707 		return B_BAD_INDEX;
708 
709 	NodeInput* channel = (NodeInput*)fInputs.ItemAt(*cookie);
710 	*out_input = channel->fInput;
711 	*cookie += 1;
712 
713 	TRACE("input.format : %u\n", channel->fInput.format.u.raw_audio.format);
714 
715 	return B_OK;
716 }
717 
718 
719 void
720 OpenSoundNode::DisposeInputCookie(int32 cookie)
721 {
722 	CALLED();
723 	// nothing to do since our cookies are just integers
724 }
725 
726 
727 void
728 OpenSoundNode::BufferReceived(BBuffer* buffer)
729 {
730 	CALLED();
731 
732 	switch (buffer->Header()->type) {
733 //		case B_MEDIA_PARAMETERS:
734 //		{
735 //			status_t status = ApplyParameterData(buffer->Data(),
736 //				buffer->SizeUsed());
737 //			if (status != B_OK) {
738 //				fprintf(stderr, "ApplyParameterData() in "
739 //					"OpenSoundNode::BufferReceived() failed: %s\n",
740 //					strerror(status));
741 //			}
742 //			buffer->Recycle();
743 //			break;
744 //		}
745 		case B_MEDIA_RAW_AUDIO:
746 			if (buffer->Flags() & BBuffer::B_SMALL_BUFFER) {
747 				fprintf(stderr, "OpenSoundNode::BufferReceived() - "
748 					"B_SMALL_BUFFER not implemented\n");
749 				// TODO: implement this part
750 				buffer->Recycle();
751 			} else {
752 				media_timed_event event(buffer->Header()->start_time,
753 					BTimedEventQueue::B_HANDLE_BUFFER, buffer,
754 					BTimedEventQueue::B_RECYCLE_BUFFER);
755 				status_t status = EventQueue()->AddEvent(event);
756 				if (status != B_OK) {
757 					fprintf(stderr, "OpenSoundNode::BufferReceived() - "
758 						"EventQueue()->AddEvent() failed: %s\n",
759 						strerror(status));
760 					buffer->Recycle();
761 				}
762 			}
763 			break;
764 		default:
765 			fprintf(stderr, "OpenSoundNode::BufferReceived() - unexpected "
766 				"buffer type\n");
767 			buffer->Recycle();
768 			break;
769 	}
770 }
771 
772 
773 void
774 OpenSoundNode::ProducerDataStatus(const media_destination& for_whom,
775 	int32 status, bigtime_t at_performance_time)
776 {
777 	CALLED();
778 
779 	NodeInput* channel = _FindInput(for_whom);
780 
781 	if (channel == NULL) {
782 		fprintf(stderr,"OpenSoundNode::ProducerDataStatus() - "
783 			"invalid destination\n");
784 		return;
785 	}
786 
787 //	TRACE("************ ProducerDataStatus: queuing event ************\n");
788 //	TRACE("************ status=%d ************\n", status);
789 
790 	media_timed_event event(at_performance_time,
791 		BTimedEventQueue::B_DATA_STATUS, &channel->fInput,
792 		BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
793 	EventQueue()->AddEvent(event);
794 }
795 
796 
797 status_t
798 OpenSoundNode::GetLatencyFor(const media_destination& for_whom,
799 	bigtime_t* out_latency, media_node_id* out_timesource)
800 {
801 	CALLED();
802 
803 	if (out_latency == NULL || out_timesource == NULL) {
804 		fprintf(stderr,"OpenSoundNode::GetLatencyFor() - B_BAD_VALUE\n");
805 		return B_BAD_VALUE;
806 	}
807 
808 	NodeInput* channel = _FindInput(for_whom);
809 
810 	if (channel == NULL || channel->fRealEngine == NULL) {
811 		fprintf(stderr,"OpenSoundNode::GetLatencyFor() - "
812 			"B_MEDIA_BAD_DESTINATION\n");
813 		return B_MEDIA_BAD_DESTINATION;
814 	}
815 
816 	// start with the node latency (1 buffer duration)
817 	*out_latency = EventLatency();
818 
819 	// add the OSS driver buffer's latency as well
820 	bigtime_t bufferLatency = channel->fRealEngine->PlaybackLatency();
821 	*out_latency += bufferLatency;
822 
823 	TRACE("OpenSoundNode::GetLatencyFor() - EventLatency %lld, OSS %lld\n",
824 		EventLatency(), bufferLatency);
825 
826 	*out_timesource = TimeSource()->ID();
827 
828 	return B_OK;
829 }
830 
831 
832 status_t
833 OpenSoundNode::Connected(const media_source& producer,
834 	const media_destination& where, const media_format& with_format,
835 	media_input* out_input)
836 {
837 	CALLED();
838 
839 	if (out_input == NULL) {
840 		fprintf(stderr,"OpenSoundNode::Connected() - B_BAD_VALUE\n");
841 		return B_BAD_VALUE;
842 	}
843 
844 	NodeInput* channel = _FindInput(where);
845 
846 	if (channel == NULL) {
847 		fprintf(stderr,"OpenSoundNode::Connected() - "
848 			"B_MEDIA_BAD_DESTINATION\n");
849 		return B_MEDIA_BAD_DESTINATION;
850 	}
851 
852 	BAutolock L(fDevice->Locker());
853 
854 	// use one half buffer length latency
855 	size_t bufferSize = channel->fRealEngine->DriverBufferSize() / 2;
856 	fInternalLatency = time_for_buffer(bufferSize, with_format);
857 	TRACE("  internal latency = %lld\n", fInternalLatency);
858 
859 	// TODO: A global node value is assigned a channel specific value!
860 	// That can't be correct. For as long as there is only one output
861 	// in use at a time, this will not matter of course.
862 	SetEventLatency(fInternalLatency);
863 
864 	// record the agreed upon values
865 	channel->fInput.source = producer;
866 	channel->fInput.format = with_format;
867 
868 	*out_input = channel->fInput;
869 
870 	// we are sure the thread is started
871 	_StartPlayThread(channel);
872 
873 	return B_OK;
874 }
875 
876 
877 void
878 OpenSoundNode::Disconnected(const media_source& producer,
879 	const media_destination& where)
880 {
881 	CALLED();
882 
883 	NodeInput* channel = _FindInput(where);
884 	if (channel == NULL) {
885 		fprintf(stderr,"OpenSoundNode::Disconnected() - "
886 			"B_MEDIA_BAD_DESTINATION\n");
887 		return;
888 	}
889 	if (channel->fInput.source != producer) {
890 		fprintf(stderr,"OpenSoundNode::Disconnected() - "
891 			"B_MEDIA_BAD_SOURCE\n");
892 		return;
893 	}
894 
895 	_StopPlayThread(channel);
896 
897 	channel->RecycleAllBuffers();
898 
899 	channel->fInput.source = media_source::null;
900 	channel->fInput.format = channel->fPreferredFormat;
901 	if (channel->fRealEngine)
902 		channel->fRealEngine->Close();
903 	channel->fRealEngine = NULL;
904 }
905 
906 
907 //! The notification comes from the upstream producer, so he's
908 //	already cool with the format; you should not ask him about it
909 //	in here.
910 status_t
911 OpenSoundNode::FormatChanged(const media_source& producer,
912 	const media_destination& consumer,  int32 change_tag,
913 	const media_format& format)
914 {
915 	CALLED();
916 	NodeInput* channel = _FindInput(consumer);
917 
918 	if (channel == NULL) {
919 		fprintf(stderr,"OpenSoundNode::FormatChanged() - "
920 			"B_MEDIA_BAD_DESTINATION\n");
921 		return B_MEDIA_BAD_DESTINATION;
922 	}
923 
924 	if (channel->fInput.source != producer) {
925 		fprintf(stderr,"OpenSoundNode::FormatChanged() - "
926 			"B_MEDIA_BAD_SOURCE\n");
927 		return B_MEDIA_BAD_SOURCE;
928 	}
929 
930 	// currently not supported, TODO: implement?
931 	return B_ERROR;
932 }
933 
934 
935 //!	Given a performance time of some previous buffer, retrieve the
936 //	remembered tag of the closest (previous or exact) performance
937 //	time. Set *out_flags to 0; the idea being that flags can be
938 //	added later, and the understood flags returned in *out_flags.
939 status_t
940 OpenSoundNode::SeekTagRequested(const media_destination& destination,
941 	bigtime_t in_target_time, uint32 in_flags, media_seek_tag* out_seek_tag,
942 	bigtime_t* out_tagged_time, uint32* out_flags)
943 {
944 	CALLED();
945 	return BBufferConsumer::SeekTagRequested(destination, in_target_time,
946 		in_flags, out_seek_tag, out_tagged_time, out_flags);
947 }
948 
949 
950 // #pragma mark - BBufferProducer
951 
952 
953 //!	FormatSuggestionRequested() is not necessarily part of the format
954 //	negotiation process; it's simply an interrogation -- the caller wants
955 //	to see what the node's preferred data format is, given a suggestion by
956 //	the caller.
957 status_t
958 OpenSoundNode::FormatSuggestionRequested(media_type type, int32 /*quality*/,
959 	media_format* format)
960 {
961 	CALLED();
962 
963 	if (format == NULL) {
964 		fprintf(stderr, "\tERROR - NULL format pointer passed in!\n");
965 		return B_BAD_VALUE;
966 	}
967 
968 	// this is the format we'll be returning (our preferred format)
969 	*format = fPreferredFormat;
970 
971 	// a wildcard type is okay; we can specialize it
972 	if (type == B_MEDIA_UNKNOWN_TYPE)
973 		type = B_MEDIA_RAW_AUDIO;
974 
975 	// TODO: For OSS engines that support encoded formats, we could
976 	// handle this here. For the time being, we only support raw audio.
977 	if (type != B_MEDIA_RAW_AUDIO)
978 		return B_MEDIA_BAD_FORMAT;
979 
980 	return B_OK;
981 }
982 
983 
984 //!	FormatProposal() is the first stage in the BMediaRoster::Connect()
985 //	process.  We hand out a suggested format, with wildcards for any
986 //	variations we support.
987 status_t
988 OpenSoundNode::FormatProposal(const media_source& output, media_format* format)
989 {
990 	CALLED();
991 
992 	NodeOutput* channel = _FindOutput(output);
993 
994 	// is this a proposal for our select output?
995 	if (channel == NULL) {
996 		fprintf(stderr, "OpenSoundNode::FormatProposal returning "
997 			"B_MEDIA_BAD_SOURCE\n");
998 		return B_MEDIA_BAD_SOURCE;
999 	}
1000 
1001 	media_type requestedType = format->type;
1002 #ifdef ENABLE_REC
1003 	OpenSoundDeviceEngine* engine = fDevice->NextFreeEngineAt(
1004 		channel->fEngineIndex, true);
1005 
1006 	// We only support raw audio, so we always return that, but we supply an
1007 	// error code depending on whether we found the proposal acceptable.
1008 	status_t err = engine->PreferredFormatFor(channel->fOSSFormatFlags, *format, true);
1009 	if (err < B_OK)
1010 		return err;
1011 #else
1012 	*format = fPreferredFormat;
1013 #endif
1014 	if (requestedType != B_MEDIA_UNKNOWN_TYPE
1015 		&& requestedType != B_MEDIA_RAW_AUDIO
1016 #ifdef ENABLE_NON_RAW_SUPPORT
1017 		 && requestedType != B_MEDIA_ENCODED_AUDIO
1018 #endif
1019 		)
1020 	{
1021 		fprintf(stderr, "OpenSoundNode::FormatProposal returning "
1022 			"B_MEDIA_BAD_FORMAT\n");
1023 		return B_MEDIA_BAD_FORMAT;
1024 	}
1025 
1026 	// raw audio or wildcard type, either is okay by us
1027 	return B_OK;
1028 }
1029 
1030 
1031 status_t
1032 OpenSoundNode::FormatChangeRequested(const media_source& source,
1033 	const media_destination& destination, media_format* io_format,
1034 	int32* _deprecated_)
1035 {
1036 	CALLED();
1037 
1038 	// we don't support any other formats, so we just reject any format
1039 	// changes. TODO: implement?
1040 	return B_ERROR;
1041 }
1042 
1043 
1044 status_t
1045 OpenSoundNode::GetNextOutput(int32* cookie, media_output* out_output)
1046 {
1047 	CALLED();
1048 
1049 	if (*cookie >= fOutputs.CountItems() || *cookie < 0)
1050 		return B_BAD_INDEX;
1051 
1052 	NodeOutput *channel = (NodeOutput*)fOutputs.ItemAt(*cookie);
1053 	*out_output = channel->fOutput;
1054 	*cookie += 1;
1055 	return B_OK;
1056 }
1057 
1058 
1059 status_t
1060 OpenSoundNode::DisposeOutputCookie(int32 cookie)
1061 {
1062 	CALLED();
1063 	// do nothing because we don't use the cookie for anything special
1064 	return B_OK;
1065 }
1066 
1067 
1068 status_t
1069 OpenSoundNode::SetBufferGroup(const media_source& for_source,
1070 	BBufferGroup* newGroup)
1071 {
1072 	CALLED();
1073 
1074 	NodeOutput* channel = _FindOutput(for_source);
1075 
1076 	// is this our output?
1077 	if (channel == NULL) {
1078 		fprintf(stderr, "OpenSoundNode::SetBufferGroup() returning "
1079 			"B_MEDIA_BAD_SOURCE\n");
1080 		return B_MEDIA_BAD_SOURCE;
1081 	}
1082 
1083 	// Are we being passed the buffer group we're already using?
1084 	if (newGroup == channel->fBufferGroup)
1085 		return B_OK;
1086 
1087 	// Ahh, someone wants us to use a different buffer group.  At this point
1088 	// we delete the one we are using and use the specified one instead.  If
1089 	// the specified group is NULL, we need to recreate one ourselves, and
1090 	// use *that*.  Note that if we're caching a BBuffer that we requested
1091 	// earlier, we have to Recycle() that buffer *before* deleting the buffer
1092 	// group, otherwise we'll deadlock waiting for that buffer to be recycled!
1093 	channel->FreeBuffers();
1094 		// waits for all buffers to recycle
1095 	if (newGroup != NULL) {
1096 		// we were given a valid group; just use that one from now on
1097 		return channel->SetExternalBuffers(newGroup);
1098 	} else {
1099 		// we were passed a NULL group pointer; that means we construct
1100 		// our own buffer group to use from now on
1101 		return channel->AllocateBuffers(BufferDuration(), fLatency);
1102 	}
1103 }
1104 
1105 
1106 //!	PrepareToConnect() is the second stage of format negotiations that happens
1107 //	inside BMediaRoster::Connect().  At this point, the consumer's
1108 //	AcceptFormat() method has been called, and that node has potentially
1109 //	changed the proposed format.  It may also have left wildcards in the
1110 //	format.  PrepareToConnect() *must* fully specialize the format before
1111 //	returning!
1112 status_t
1113 OpenSoundNode::PrepareToConnect(const media_source& what,
1114 	const media_destination& where, media_format* format,
1115 	media_source* out_source, char* out_name)
1116 {
1117 	CALLED();
1118 
1119 	status_t err;
1120 	NodeOutput *channel = _FindOutput(what);
1121 
1122 	// is this our output?
1123 	if (channel == NULL) {
1124 		fprintf(stderr, "OpenSoundNode::PrepareToConnect returning "
1125 			"B_MEDIA_BAD_SOURCE\n");
1126 		return B_MEDIA_BAD_SOURCE;
1127 	}
1128 
1129 	// are we already connected?
1130 	if (channel->fOutput.destination != media_destination::null)
1131 		return B_MEDIA_ALREADY_CONNECTED;
1132 
1133 	BAutolock L(fDevice->Locker());
1134 
1135 	OpenSoundDeviceEngine *engine = fDevice->NextFreeEngineAt(
1136 		channel->fEngineIndex, false);
1137 	if (engine == NULL)
1138 		return B_BUSY;
1139 
1140 	// the format may not yet be fully specialized (the consumer might have
1141 	// passed back some wildcards).  Finish specializing it now, and return an
1142 	// error if we don't support the requested format.
1143 	if (format->type != B_MEDIA_RAW_AUDIO) {
1144 		fprintf(stderr, "\tnon-raw-audio format?!\n");
1145 		return B_MEDIA_BAD_FORMAT;
1146 	}
1147 
1148 	// !!! validate all other fields except for buffer_size here, because the
1149 	// consumer might have supplied different values from AcceptFormat()?
1150 	err = engine->SpecializeFormatFor(channel->fOSSFormatFlags, *format, true);
1151 	if (err < B_OK)
1152 		return err;
1153 
1154 	channel->fRealEngine = engine;
1155 
1156 #if 0
1157 	// check the buffer size, which may still be wildcarded
1158 	if (format->u.raw_audio.buffer_size
1159 		== media_raw_audio_format::wildcard.buffer_size) {
1160 		format->u.raw_audio.buffer_size = 2048;
1161 			// pick something comfortable to suggest
1162 		fprintf(stderr, "\tno buffer size provided, suggesting %lu\n",
1163 			format->u.raw_audio.buffer_size);
1164 	} else {
1165 		fprintf(stderr, "\tconsumer suggested buffer_size %lu\n",
1166 			format->u.raw_audio.buffer_size);
1167 	}
1168 #endif
1169 
1170 	// Now reserve the connection, and return information about it
1171 	channel->fOutput.destination = where;
1172 	channel->fOutput.format = *format;
1173 	*out_source = channel->fOutput.source;
1174 	strncpy(out_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
1175 	return B_OK;
1176 }
1177 
1178 
1179 void
1180 OpenSoundNode::Connect(status_t error, const media_source& source,
1181 	const media_destination& destination, const media_format& format,
1182 	char* io_name)
1183 {
1184 	CALLED();
1185 
1186 	NodeOutput *channel = _FindOutput(source);
1187 
1188 	// is this our output?
1189 	if (channel == NULL) {
1190 		fprintf(stderr, "OpenSoundNode::Connect returning (cause : "
1191 			"B_MEDIA_BAD_SOURCE)\n");
1192 		return;
1193 	}
1194 
1195 	OpenSoundDeviceEngine *engine = channel->fRealEngine;
1196 	if (engine == NULL)
1197 		return;
1198 
1199 	// If something earlier failed, Connect() might still be called, but with
1200 	// a non-zero error code.  When that happens we simply unreserve the
1201 	// connection and do nothing else.
1202 	if (error) {
1203 		channel->fOutput.destination = media_destination::null;
1204 		channel->fOutput.format = channel->fPreferredFormat;
1205 		engine->Close();
1206 		channel->fRealEngine = NULL;
1207 		return;
1208 	}
1209 
1210 	// Okay, the connection has been confirmed.  Record the destination and
1211 	// format that we agreed on, and report our connection name again.
1212 	channel->fOutput.destination = destination;
1213 	channel->fOutput.format = format;
1214 	strncpy(io_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
1215 
1216 	// reset our buffer duration, etc. to avoid later calculations
1217 	bigtime_t duration = channel->fOutput.format.u.raw_audio.buffer_size
1218 		* 10000
1219 		/ ( (channel->fOutput.format.u.raw_audio.format
1220 				& media_raw_audio_format::B_AUDIO_SIZE_MASK)
1221 			* channel->fOutput.format.u.raw_audio.channel_count)
1222 		/ ((int32)(channel->fOutput.format.u.raw_audio.frame_rate / 100));
1223 
1224 	SetBufferDuration(duration);
1225 
1226 	// Now that we're connected, we can determine our downstream latency.
1227 	// Do so, then make sure we get our events early enough.
1228 	media_node_id id;
1229 	FindLatencyFor(channel->fOutput.destination, &fLatency, &id);
1230 	TRACE("\tdownstream latency = %Ld\n", fLatency);
1231 
1232 	fInternalLatency = BufferDuration();
1233 	TRACE("\tbuffer-filling took %Ld usec on this machine\n",
1234 		fInternalLatency);
1235 	//SetEventLatency(fLatency + fInternalLatency);
1236 
1237 	// Set up the buffer group for our connection, as long as nobody handed us
1238 	// a buffer group (via SetBufferGroup()) prior to this.  That can happen,
1239 	// for example, if the consumer calls SetOutputBuffersFor() on us from
1240 	// within its Connected() method.
1241 	if (channel->fBufferGroup == NULL)
1242 		channel->AllocateBuffers(BufferDuration(), fLatency);
1243 
1244 	engine->StartRecording();
1245 
1246 	// we are sure the thread is started
1247 	_StartRecThread(channel);
1248 }
1249 
1250 
1251 void
1252 OpenSoundNode::Disconnect(const media_source& what,
1253 	const media_destination& where)
1254 {
1255 	CALLED();
1256 
1257 	NodeOutput *channel = _FindOutput(what);
1258 
1259 	// is this our output?
1260 	if (channel == NULL) {
1261 		fprintf(stderr, "OpenSoundNode::Disconnect() returning (cause : "
1262 			"B_MEDIA_BAD_SOURCE)\n");
1263 		return;
1264 	}
1265 
1266 	_StopRecThread(channel);
1267 
1268 	BAutolock L(fDevice->Locker());
1269 
1270 	OpenSoundDeviceEngine* engine = channel->fRealEngine;
1271 
1272 	// Make sure that our connection is the one being disconnected
1273 	if (where == channel->fOutput.destination
1274 		&& what == channel->fOutput.source) {
1275 		if (engine)
1276 			engine->Close();
1277 		channel->fRealEngine = NULL;
1278 		channel->fOutput.destination = media_destination::null;
1279 		channel->fOutput.format = channel->fPreferredFormat;
1280 		channel->FreeBuffers();
1281 	} else {
1282 		fprintf(stderr, "\tDisconnect() called with wrong source/destination "
1283 			"(%ld/%ld), ours is (%ld/%ld)\n", what.id, where.id,
1284 			channel->fOutput.source.id, channel->fOutput.destination.id);
1285 	}
1286 }
1287 
1288 
1289 void
1290 OpenSoundNode::LateNoticeReceived(const media_source& what, bigtime_t how_much,
1291 	bigtime_t performance_time)
1292 {
1293 	CALLED();
1294 
1295 	// is this our output?
1296 	NodeOutput *channel = _FindOutput(what);
1297 	if (channel == NULL)
1298 		return;
1299 
1300 	// If we're late, we need to catch up.  Respond in a manner appropriate
1301 	// to our current run mode.
1302 	if (RunMode() == B_RECORDING) {
1303 		// A hardware capture node can't adjust; it simply emits buffers at
1304 		// appropriate points.  We (partially) simulate this by not adjusting
1305 		// our behavior upon receiving late notices -- after all, the hardware
1306 		// can't choose to capture "sooner"....
1307 	} else if (RunMode() == B_INCREASE_LATENCY) {
1308 		// We're late, and our run mode dictates that we try to produce buffers
1309 		// earlier in order to catch up.  This argues that the downstream nodes
1310 		// are not properly reporting their latency, but there's not much we
1311 		// can do about that at the moment, so we try to start producing
1312 		// buffers earlier to compensate.
1313 		fInternalLatency += how_much;
1314 		SetEventLatency(fLatency + fInternalLatency);
1315 
1316 		fprintf(stderr, "\tincreasing latency to %Ld\n",
1317 			fLatency + fInternalLatency);
1318 	} else {
1319 		// The other run modes dictate various strategies for sacrificing data
1320 		// quality in the interests of timely data delivery.  The way *we* do
1321 		// this is to skip a buffer, which catches us up in time by one buffer
1322 		// duration.
1323 //		size_t nSamples = fOutput.format.u.raw_audio.buffer_size
1324 //			/ sizeof(float);
1325 //		mSamplesSent += nSamples;
1326 
1327 		fprintf(stderr, "\tskipping a buffer to try to catch up\n");
1328 	}
1329 }
1330 
1331 
1332 void
1333 OpenSoundNode::EnableOutput(const media_source& what, bool enabled,
1334 	int32* _deprecated_)
1335 {
1336 	CALLED();
1337 
1338 	// If I had more than one output, I'd have to walk my list of output records to see
1339 	// which one matched the given source, and then enable/disable that one.  But this
1340 	// node only has one output, so I just make sure the given source matches, then set
1341 	// the enable state accordingly.
1342 	NodeOutput *channel = _FindOutput(what);
1343 
1344 	if (channel != NULL)
1345 	{
1346 		channel->fOutputEnabled = enabled;
1347 	}
1348 }
1349 
1350 void
1351 OpenSoundNode::AdditionalBufferRequested(const media_source& source,
1352 	media_buffer_id prev_buffer, bigtime_t prev_time,
1353 	const media_seek_tag* prev_tag)
1354 {
1355 	CALLED();
1356 	// we don't support offline mode
1357 	return;
1358 }
1359 
1360 
1361 // #pragma mark - BMediaEventLooper
1362 
1363 
1364 void
1365 OpenSoundNode::HandleEvent(const media_timed_event* event, bigtime_t lateness,
1366 	bool realTimeEvent)
1367 {
1368 	CALLED();
1369 
1370 	switch (event->type) {
1371 		case BTimedEventQueue::B_START:
1372 			HandleStart(event,lateness,realTimeEvent);
1373 			break;
1374 		case BTimedEventQueue::B_SEEK:
1375 			HandleSeek(event,lateness,realTimeEvent);
1376 			break;
1377 		case BTimedEventQueue::B_WARP:
1378 			HandleWarp(event,lateness,realTimeEvent);
1379 			break;
1380 		case BTimedEventQueue::B_STOP:
1381 			HandleStop(event,lateness,realTimeEvent);
1382 			break;
1383 		case BTimedEventQueue::B_HANDLE_BUFFER:
1384 //			TRACE("HandleEvent: B_HANDLE_BUFFER, RunState= %d\n",
1385 //				RunState());
1386 			if (RunState() == BMediaEventLooper::B_STARTED) {
1387 				HandleBuffer(event,lateness,realTimeEvent);
1388 			}
1389 			break;
1390 		case BTimedEventQueue::B_DATA_STATUS:
1391 			HandleDataStatus(event,lateness,realTimeEvent);
1392 			break;
1393 		case BTimedEventQueue::B_PARAMETER:
1394 			HandleParameter(event,lateness,realTimeEvent);
1395 			break;
1396 		default:
1397 			fprintf(stderr,"  unknown event type: %li\n",event->type);
1398 			break;
1399 	}
1400 }
1401 
1402 
1403 // protected
1404 status_t
1405 OpenSoundNode::HandleBuffer(const media_timed_event* event,
1406 	bigtime_t lateness, bool realTimeEvent)
1407 {
1408 	CALLED();
1409 
1410 	// TODO: How should we handle late buffers? Drop them?
1411 	// Notify the producer?
1412 
1413 	BBuffer* buffer = const_cast<BBuffer*>((BBuffer*)event->pointer);
1414 	if (buffer == NULL) {
1415 		fprintf(stderr,"OpenSoundNode::HandleBuffer() - B_BAD_VALUE\n");
1416 		return B_BAD_VALUE;
1417 	}
1418 
1419 	NodeInput *channel = _FindInput(buffer->Header()->destination);
1420 //	TRACE("buffer->Header()->destination : %i\n",
1421 //		buffer->Header()->destination);
1422 
1423 	if (channel == NULL) {
1424 		buffer->Recycle();
1425 		fprintf(stderr,"OpenSoundNode::HandleBuffer() - "
1426 			"B_MEDIA_BAD_DESTINATION\n");
1427 		return B_MEDIA_BAD_DESTINATION;
1428 	}
1429 
1430 	media_header* hdr = buffer->Header();
1431 	bigtime_t now = TimeSource()->Now();
1432 	bigtime_t perf_time = hdr->start_time;
1433 
1434 	// the how_early calculated here doesn't include scheduling latency
1435 	// because we've already been scheduled to handle the buffer
1436 	bigtime_t how_early = perf_time - EventLatency() - now;
1437 
1438 	// if the buffer is late, we ignore it and report the fact to the producer
1439 	// who sent it to us
1440 	if (RunMode() != B_OFFLINE
1441 			// lateness doesn't matter in offline mode...
1442 		&& RunMode() != B_RECORDING
1443 			// ...or in recording mode
1444 		&& how_early < 0LL
1445 		&& false) {
1446 			// TODO: Debug
1447 		//mLateBuffers++;
1448 		NotifyLateProducer(channel->fInput.source, -how_early, perf_time);
1449 		fprintf(stderr,"	<- LATE BUFFER : %lli\n", how_early);
1450 		buffer->Recycle();
1451 	} else {
1452 		fDevice->Locker()->Lock();
1453 		if (channel->fBuffers.CountItems() > 10) {
1454 			fDevice->Locker()->Unlock();
1455 			TRACE("OpenSoundNode::HandleBuffer too many buffers, "
1456 				"recycling\n");
1457 			buffer->Recycle();
1458 		} else {
1459 //			TRACE("OpenSoundNode::HandleBuffer writing channelId : %i,
1460 //				how_early:%lli\n", channel->fEngineIndex, how_early);
1461 			if (!channel->fBuffers.AddItem(buffer))
1462 				buffer->Recycle();
1463 			fDevice->Locker()->Unlock();
1464 		}
1465 	}
1466 	return B_OK;
1467 }
1468 
1469 
1470 status_t
1471 OpenSoundNode::HandleDataStatus(const media_timed_event* event,
1472 	bigtime_t lateness, bool realTimeEvent)
1473 {
1474 //	CALLED();
1475 
1476 	// TODO: this is called mostly whenever the system mixer
1477 	// switches from not sending us buffers (no client connected)
1478 	// to sending buffers, and vice versa. In a Terminal, this
1479 	// can be nicely demonstrated by provoking a system beep while
1480 	// nothing else is using audio playback. Any first beep will
1481 	// start with a small glitch, while more beeps before the last
1482 	// one finished will not have the glitch. I am not sure, but
1483 	// I seem to remember that other audio nodes have the same
1484 	// problem, so it might not be a problem of this implementation.
1485 
1486 	BString message("OpenSoundNode::HandleDataStatus status: ");
1487 
1488 	switch(event->data) {
1489 		case B_DATA_NOT_AVAILABLE:
1490 			message << "No data";
1491 			break;
1492 		case B_DATA_AVAILABLE:
1493 			message << "Data";
1494 			break;
1495 		case B_PRODUCER_STOPPED:
1496 			message << "Stopped";
1497 			break;
1498 		default:
1499 			message << "???";
1500 			break;
1501 	}
1502 
1503 	message << ", lateness: " << lateness;
1504 	printf("%s\n", message.String());
1505 
1506 	return B_OK;
1507 }
1508 
1509 
1510 status_t
1511 OpenSoundNode::HandleStart(const media_timed_event* event, bigtime_t lateness,
1512 	bool realTimeEvent)
1513 {
1514 	CALLED();
1515 	if (RunState() != B_STARTED) {
1516 		// TODO: What should happen here?
1517 	}
1518 	return B_OK;
1519 }
1520 
1521 
1522 status_t
1523 OpenSoundNode::HandleSeek(const media_timed_event* event, bigtime_t lateness,
1524 	bool realTimeEvent)
1525 {
1526 	CALLED();
1527 	TRACE("OpenSoundNode::HandleSeek(t=%lld, d=%li, bd=%lld)\n",
1528 		event->event_time,event->data,event->bigdata);
1529 	return B_OK;
1530 }
1531 
1532 
1533 status_t
1534 OpenSoundNode::HandleWarp(const media_timed_event* event,
1535 	bigtime_t lateness, bool realTimeEvent)
1536 {
1537 	CALLED();
1538 	return B_OK;
1539 }
1540 
1541 
1542 status_t
1543 OpenSoundNode::HandleStop(const media_timed_event* event, bigtime_t lateness,
1544 	bool realTimeEvent)
1545 {
1546 	CALLED();
1547 	// flush the queue so downstreamers don't get any more
1548 	EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
1549 		BTimedEventQueue::B_HANDLE_BUFFER);
1550 
1551 	return B_OK;
1552 }
1553 
1554 
1555 status_t
1556 OpenSoundNode::HandleParameter(const media_timed_event* event,
1557 	bigtime_t lateness, bool realTimeEvent)
1558 {
1559 	CALLED();
1560 	return B_OK;
1561 }
1562 
1563 
1564 // #pragma mark - BTimeSource
1565 
1566 
1567 void
1568 OpenSoundNode::SetRunMode(run_mode mode)
1569 {
1570 	CALLED();
1571 	TRACE("OpenSoundNode::SetRunMode(%d)\n", mode);
1572 	//BTimeSource::SetRunMode(mode);
1573 }
1574 
1575 
1576 status_t
1577 OpenSoundNode::TimeSourceOp(const time_source_op_info& op, void* _reserved)
1578 {
1579 	CALLED();
1580 	switch(op.op) {
1581 		case B_TIMESOURCE_START:
1582 			TRACE("TimeSourceOp op B_TIMESOURCE_START\n");
1583 			if (RunState() != BMediaEventLooper::B_STARTED) {
1584 				fTimeSourceStarted = true;
1585 				fTimeSourceStartTime = RealTime();
1586 
1587 				media_timed_event startEvent(0, BTimedEventQueue::B_START);
1588 				EventQueue()->AddEvent(startEvent);
1589 			}
1590 			break;
1591 		case B_TIMESOURCE_STOP:
1592 			TRACE("TimeSourceOp op B_TIMESOURCE_STOP\n");
1593 			if (RunState() == BMediaEventLooper::B_STARTED) {
1594 				media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1595 				EventQueue()->AddEvent(stopEvent);
1596 				fTimeSourceStarted = false;
1597 				PublishTime(0, 0, 0);
1598 			}
1599 			break;
1600 		case B_TIMESOURCE_STOP_IMMEDIATELY:
1601 			TRACE("TimeSourceOp op B_TIMESOURCE_STOP_IMMEDIATELY\n");
1602 			if (RunState() == BMediaEventLooper::B_STARTED) {
1603 				media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1604 				EventQueue()->AddEvent(stopEvent);
1605 				fTimeSourceStarted = false;
1606 				PublishTime(0, 0, 0);
1607 			}
1608 			break;
1609 		case B_TIMESOURCE_SEEK:
1610 //			TRACE("TimeSourceOp op B_TIMESOURCE_SEEK\n");
1611 printf("TimeSourceOp op B_TIMESOURCE_SEEK, real %lld, "
1612 	"perf %lld\n", op.real_time, op.performance_time);
1613 			BroadcastTimeWarp(op.real_time, op.performance_time);
1614 			break;
1615 		default:
1616 			break;
1617 	}
1618 	return B_OK;
1619 }
1620 
1621 
1622 // #pragma mark - BControllable
1623 
1624 
1625 status_t
1626 OpenSoundNode::GetParameterValue(int32 id, bigtime_t* last_change, void* value,
1627 	size_t* ioSize)
1628 {
1629 	CALLED();
1630 
1631 	int channelCount = 1;
1632 	int sliderShift = 8;
1633 
1634 	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1635 	if (!mixer)
1636 		return ENODEV;
1637 
1638 	TRACE("id : %i, *ioSize=%d\n", id, *ioSize);
1639 
1640 	oss_mixext mixext;
1641 	status_t err = mixer->GetExtInfo(id, &mixext);
1642 	if (err < B_OK)
1643 		return err;
1644 
1645 	oss_mixer_value mixval;
1646 	mixval.ctrl = mixext.ctrl;
1647 	mixval.timestamp = mixext.timestamp;
1648 
1649 	err = mixer->GetMixerValue(&mixval);
1650 	if (err < B_OK)
1651 		return err;
1652 
1653 	if (!(mixext.flags & MIXF_READABLE))
1654 		return EINVAL;
1655 
1656 	BParameter *parameter = NULL;
1657 	for (int32 i = 0; i < fWeb->CountParameters(); i++) {
1658 		parameter = fWeb->ParameterAt(i);
1659 		if(parameter->ID() == id)
1660 			break;
1661 	}
1662 
1663 	if (!parameter)
1664 		return ENODEV;
1665 
1666 	TRACE("%s: value = 0x%08x\n", __FUNCTION__, mixval.value);
1667 
1668 	*last_change = system_time();//??
1669 
1670 	switch (mixext.type) {
1671 	case MIXT_DEVROOT:
1672 	case MIXT_GROUP:
1673 		break;
1674 	case MIXT_ONOFF:
1675 		if (*ioSize < sizeof(bool))
1676 			return EINVAL;
1677 		*(int32 *)value = mixval.value?true:false;
1678 		*ioSize = sizeof(bool);
1679 		return B_OK;
1680 	case MIXT_ENUM:
1681 		if (*ioSize < sizeof(int32))
1682 			return EINVAL;
1683 		*(int32 *)value = mixval.value;
1684 		*ioSize = sizeof(int32);
1685 		return B_OK;
1686 		break;
1687 	case MIXT_STEREODB:
1688 	case MIXT_STEREOSLIDER16:
1689 	case MIXT_STEREOSLIDER:
1690 		channelCount = 2;
1691 	case MIXT_SLIDER:
1692 	case MIXT_MONODB:
1693 	case MIXT_MONOSLIDER16:
1694 	case MIXT_MONOSLIDER:
1695 		if (*ioSize < channelCount * sizeof(float))
1696 			return EINVAL;
1697 		if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
1698 			return EINVAL;
1699 		if (mixext.type == MIXT_STEREOSLIDER16 ||
1700 			mixext.type == MIXT_MONOSLIDER16)
1701 			sliderShift = 16;
1702 		*ioSize = channelCount * sizeof(float);
1703 		((float *)value)[0] = (float)(mixval.value & ((1 << sliderShift) - 1));
1704 		TRACE("%s: value[O] = %f\n", __FUNCTION__, ((float *)value)[0]);
1705 		if (channelCount < 2)
1706 			return B_OK;
1707 		((float *)value)[1] = (float)((mixval.value >> sliderShift)
1708 			& ((1 << sliderShift) - 1));
1709 		TRACE("%s: value[1] = %f\n", __FUNCTION__, ((float *)value)[1]);
1710 		return B_OK;
1711 		break;
1712 	case MIXT_MESSAGE:
1713 		break;
1714 	case MIXT_MONOVU:
1715 		break;
1716 	case MIXT_STEREOVU:
1717 		break;
1718 	case MIXT_MONOPEAK:
1719 		break;
1720 	case MIXT_STEREOPEAK:
1721 		break;
1722 	case MIXT_RADIOGROUP:
1723 		break;//??
1724 	case MIXT_MARKER:
1725 		break;// separator item: ignore
1726 	case MIXT_VALUE:
1727 		break;
1728 	case MIXT_HEXVALUE:
1729 		break;
1730 /*	case MIXT_MONODB:
1731 		break;
1732 	case MIXT_STEREODB:
1733 		break;*/
1734 	case MIXT_3D:
1735 		break;
1736 /*	case MIXT_MONOSLIDER16:
1737 		break;
1738 	case MIXT_STEREOSLIDER16:
1739 		break;*/
1740 	default:
1741 		TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
1742 			__FUNCTION__, mixext.type);
1743 	}
1744 	*ioSize = 0;
1745 	return EINVAL;
1746 }
1747 
1748 
1749 void
1750 OpenSoundNode::SetParameterValue(int32 id, bigtime_t performance_time,
1751 	const void* value, size_t size)
1752 {
1753 	CALLED();
1754 
1755 	TRACE("id : %i, performance_time : %lld, size : %i\n", id,
1756 		performance_time, size);
1757 
1758 	OpenSoundDeviceMixer *mixer = fDevice->MixerAt(0);
1759 	if (mixer == NULL)
1760 		return;
1761 
1762 	oss_mixext mixext;
1763 	if (mixer->GetExtInfo(id, &mixext) < B_OK)
1764 		return;
1765 	if (!(mixext.flags & MIXF_WRITEABLE))
1766 		return;
1767 
1768 	oss_mixer_value mixval;
1769 	mixval.ctrl = mixext.ctrl;
1770 	mixval.timestamp = mixext.timestamp;
1771 
1772 	status_t err = mixer->GetMixerValue(&mixval);
1773 	if (err < B_OK)
1774 		return;
1775 
1776 	mixval.ctrl = mixext.ctrl;
1777 	mixval.timestamp = mixext.timestamp;
1778 
1779 	BParameter *parameter = NULL;
1780 	for(int32 i=0; i<fWeb->CountParameters(); i++) {
1781 		parameter = fWeb->ParameterAt(i);
1782 		if(parameter->ID() == id)
1783 			break;
1784 	}
1785 
1786 	if (!parameter)
1787 		return;
1788 
1789 	int channelCount = 1;
1790 	int sliderShift = 8;
1791 
1792 	switch (mixext.type) {
1793 		case MIXT_DEVROOT:
1794 		case MIXT_GROUP:
1795 			break;
1796 		case MIXT_ONOFF:
1797 			if (size < sizeof(bool))
1798 				return;
1799 			mixval.value = (int)*(int32 *)value;
1800 			mixer->SetMixerValue(&mixval);
1801 			// At least on my ATI IXP, recording selection can't be set to OFF,
1802 			// you have to set another one to ON to actually do it,
1803 			// and setting to ON changes others to OFF
1804 			// So we have to let users know about it.
1805 			// XXX: find something better, doesn't work correctly here.
1806 			// XXX: try a timed event ?
1807 			_PropagateParameterChanges(mixext.ctrl, mixext.type, mixext.id);
1808 
1809 			return;
1810 		case MIXT_ENUM:
1811 			if (size < sizeof(int32))
1812 				return;
1813 			mixval.value = (int)*(int32 *)value;
1814 			mixer->SetMixerValue(&mixval);
1815 			break;
1816 		case MIXT_STEREODB:
1817 		case MIXT_STEREOSLIDER16:
1818 		case MIXT_STEREOSLIDER:
1819 			channelCount = 2;
1820 		case MIXT_SLIDER:
1821 		case MIXT_MONODB:
1822 		case MIXT_MONOSLIDER16:
1823 		case MIXT_MONOSLIDER:
1824 			if (size < channelCount * sizeof(float))
1825 				return;
1826 			if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
1827 				return;
1828 			if (mixext.type == MIXT_STEREOSLIDER16 ||
1829 				mixext.type == MIXT_MONOSLIDER16)
1830 				sliderShift = 16;
1831 			mixval.value = 0;
1832 
1833 			TRACE("-------- sliderShift=%d, v = %08x, v & %08x = %08x\n",
1834 				sliderShift, mixval.value, ((1 << sliderShift) - 1),
1835 				mixval.value & ((1 << sliderShift) - 1));
1836 
1837 			mixval.value |= ((int)(((float *)value)[0]))
1838 				& ((1 << sliderShift) - 1);
1839 			if (channelCount > 1) {
1840 				mixval.value |= (((int)(((float *)value)[1]))
1841 					& ((1 << sliderShift) - 1)) << sliderShift;
1842 			}
1843 
1844 			TRACE("%s: value = 0x%08x\n", __FUNCTION__, mixval.value);
1845 			mixer->SetMixerValue(&mixval);
1846 			return;
1847 			break;
1848 		case MIXT_MESSAGE:
1849 			break;
1850 		case MIXT_MONOVU:
1851 			break;
1852 		case MIXT_STEREOVU:
1853 			break;
1854 		case MIXT_MONOPEAK:
1855 			break;
1856 		case MIXT_STEREOPEAK:
1857 			break;
1858 		case MIXT_RADIOGROUP:
1859 			break;//??
1860 		case MIXT_MARKER:
1861 			break;// separator item: ignore
1862 		case MIXT_VALUE:
1863 			break;
1864 		case MIXT_HEXVALUE:
1865 			break;
1866 //		case MIXT_MONODB:
1867 //			break;
1868 //		case MIXT_STEREODB:
1869 //			break;
1870 		case MIXT_3D:
1871 			break;
1872 //		case MIXT_MONOSLIDER16:
1873 //			break;
1874 //		case MIXT_STEREOSLIDER16:
1875 //			break;
1876 		default:
1877 			TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
1878 				__FUNCTION__, mixext.type);
1879 	}
1880 
1881 	return;
1882 }
1883 
1884 
1885 BParameterWeb*
1886 OpenSoundNode::MakeParameterWeb()
1887 {
1888 	CALLED();
1889 	BParameterWeb* web = new BParameterWeb;
1890 
1891 	// TODO: the card might change the mixer controls at some point,
1892 	// we should detect it (poll) and recreate the parameter web and
1893 	// re-set it.
1894 
1895 	// TODO: cache mixext[...] and poll for changes in their update_counter.
1896 
1897 	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1898 	if (mixer == NULL) {
1899 		// some cards don't have a mixer, just put a placeholder then
1900 		BParameterGroup* child = web->MakeGroup("No mixer");
1901 		child->MakeNullParameter(1, B_MEDIA_UNKNOWN_TYPE, "No Mixer",
1902 			B_GENERIC);
1903 		return web;
1904 	}
1905 
1906 	int mixext_count = mixer->CountExtInfos();
1907 	TRACE("OpenSoundNode::MakeParameterWeb %i ExtInfos\n", mixext_count);
1908 
1909 	for (int32 i = 0; i < mixext_count; i++) {
1910 		oss_mixext mixext;
1911 		if (mixer->GetExtInfo(i, &mixext) < B_OK)
1912 			continue;
1913 
1914 		if (mixext.type == MIXT_DEVROOT) {
1915 			oss_mixext_root* extroot = (oss_mixext_root*)mixext.data;
1916 			TRACE("OpenSoundNode: mixext[%d]: ROOT\n", i);
1917 			int32 nb = 0;
1918 			const char* childName = mixext.extname;
1919 			childName = extroot->id; // extroot->name;
1920 			BParameterGroup *child = web->MakeGroup(childName);
1921 			_ProcessGroup(child, i, nb);
1922 		}
1923 	}
1924 
1925 	return web;
1926 }
1927 
1928 
1929 // #pragma mark - OpenSoundNode specific
1930 
1931 
1932 void
1933 OpenSoundNode::_ProcessGroup(BParameterGroup *group, int32 index,
1934 	int32& nbParameters)
1935 {
1936 	CALLED();
1937 	// TODO: It looks wrong to use the same mixer in a recursive function!
1938 	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1939 
1940 	int mixext_count = mixer->CountExtInfos();
1941 	for (int32 i = 0; i < mixext_count; i++) {
1942 		oss_mixext mixext;
1943 		if (mixer->GetExtInfo(i, &mixext) < B_OK)
1944 			continue;
1945 		// only keep direct children of that group
1946 		if (mixext.parent != index)
1947 			continue;
1948 
1949 		int32 nb = 1;
1950 
1951 		TRACE("OpenSoundNode: mixext[%d]: { %s/%s, type=%d, parent=%d, "
1952 			"min=%d, max=%d, flags=0x%08x, control_no=%d, desc=%d, "
1953 			"update_counter=%d }\n", i,
1954 			(mixext.type != MIXT_MARKER) ? mixext.id : "",
1955 			(mixext.type != MIXT_MARKER) ? mixext.extname : "",
1956 			mixext.type, mixext.parent,
1957 			mixext.minvalue, mixext.maxvalue,
1958 			mixext.flags, mixext.control_no,
1959 			mixext.desc, mixext.update_counter);
1960 
1961 		// should actually rename the whole group but it's too late there.
1962 		const char *childName = mixext.extname;
1963 		if (mixext.flags & MIXF_MAINVOL)
1964 			childName = "Master Gain";
1965 
1966 		const char *sliderUnit = "";//"(linear)";
1967 		if (mixext.flags & MIXF_HZ)
1968 			sliderUnit = "Hz";
1969 
1970 		const char *continuousKind = B_GAIN;
1971 		BParameterGroup* child;
1972 
1973 		switch (mixext.type) {
1974 		case MIXT_DEVROOT:
1975 			// root item, should be done already
1976 			break;
1977 		case MIXT_GROUP:
1978 			TRACE("OpenSoundNode: mixext[%d]: GROUP\n", i);
1979 			child = group->MakeGroup(childName);
1980 			child->MakeNullParameter(i, B_MEDIA_RAW_AUDIO, childName,
1981 				B_WEB_BUFFER_OUTPUT);
1982 			_ProcessGroup(child, i, nb);
1983 			break;
1984 		case MIXT_ONOFF:
1985 			TRACE("OpenSoundNode: mixext[%d]: ONOFF\n", i);
1986 			// multiaudio node adds 100 to IDs !?
1987 			if (0/*MMC[i].string == S_MUTE*/) {
1988 				group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
1989 					B_MUTE);
1990 			} else {
1991 				group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
1992 					B_ENABLE);
1993 			}
1994 			if (nbParameters > 0) {
1995 				(group->ParameterAt(nbParameters - 1))->AddOutput(
1996 					group->ParameterAt(nbParameters));
1997 				nbParameters++;
1998 			}
1999 			break;
2000 		case MIXT_ENUM:
2001 		{
2002 			TRACE("OpenSoundNode: mixext[%d]: ENUM\n", i);
2003 			BDiscreteParameter *parameter =
2004 				group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
2005 					B_INPUT_MUX);
2006 			if (nbParameters > 0) {
2007 				(group->ParameterAt(nbParameters - 1))->AddOutput(
2008 					group->ParameterAt(nbParameters));
2009 				nbParameters++;
2010 			}
2011 			_ProcessMux(parameter, i);
2012 			break;
2013 		}
2014 		case MIXT_MONODB:
2015 		case MIXT_STEREODB:
2016 			sliderUnit = "dB";
2017 		case MIXT_SLIDER:
2018 		case MIXT_MONOSLIDER16:
2019 		case MIXT_STEREOSLIDER16:
2020 		case MIXT_MONOSLIDER:
2021 			//TRACE("OpenSoundNode: mixext[%d]: MONOSLIDER\n", i);
2022 			//break;
2023 			// fall through
2024 		case MIXT_STEREOSLIDER:
2025 			TRACE("OpenSoundNode: mixext[%d]: [MONO|STEREO]SLIDER\n", i);
2026 
2027 			if (mixext.flags & MIXF_MAINVOL)
2028 				continuousKind = B_MASTER_GAIN;
2029 
2030 // TODO: find out what this was supposed to do:
2031 //			if (mixext.flags & MIXF_CENTIBEL)
2032 //				true;//step size
2033 //			if (mixext.flags & MIXF_DECIBEL)
2034 //				true;//step size
2035 
2036 			group->MakeContinuousParameter(i, B_MEDIA_RAW_AUDIO, childName,
2037 				continuousKind,  sliderUnit, mixext.minvalue, mixext.maxvalue,
2038 				/*TODO: should be "granularity"*/1);
2039 
2040 			if (mixext.type == MIXT_STEREOSLIDER ||
2041 				mixext.type == MIXT_STEREOSLIDER16 ||
2042 				mixext.type == MIXT_STEREODB)
2043 				group->ParameterAt(nbParameters)->SetChannelCount(2);
2044 
2045 			TRACE("nb parameters : %d\n", nbParameters);
2046 			if (nbParameters > 0) {
2047 				(group->ParameterAt(nbParameters - 1))->AddOutput(
2048 					group->ParameterAt(nbParameters));
2049 				nbParameters++;
2050 			}
2051 
2052 			break;
2053 		case MIXT_MESSAGE:
2054 			break;
2055 		case MIXT_MONOVU:
2056 			break;
2057 		case MIXT_STEREOVU:
2058 			break;
2059 		case MIXT_MONOPEAK:
2060 			break;
2061 		case MIXT_STEREOPEAK:
2062 			break;
2063 		case MIXT_RADIOGROUP:
2064 			break;//??
2065 		case MIXT_MARKER:
2066 			break;// separator item: ignore
2067 		case MIXT_VALUE:
2068 			break;
2069 		case MIXT_HEXVALUE:
2070 			break;
2071 //		case MIXT_MONODB:
2072 //			TRACE("OpenSoundNode::_ProcessGroup: Skipping obsolete "
2073 //				"MIXT_MONODB\n");
2074 //			break;
2075 //		case MIXT_STEREODB:
2076 //			TRACE("OpenSoundNode::_ProcessGroup: Skipping obsolete "
2077 //				"MIXT_STEREODB\n");
2078 //			break;
2079 //		case MIXT_SLIDER:
2080 //			break;
2081 		case MIXT_3D:
2082 			break;
2083 //		case MIXT_MONOSLIDER16:
2084 //			break;
2085 //		case MIXT_STEREOSLIDER16:
2086 //			break;
2087 		default:
2088 			TRACE("OpenSoundNode::_ProcessGroup: unknown mixer control "
2089 				"type %d\n", mixext.type);
2090 		}
2091 	}
2092 
2093 }
2094 
2095 
2096 void
2097 OpenSoundNode::_ProcessMux(BDiscreteParameter* parameter, int32 index)
2098 {
2099 	CALLED();
2100 	OpenSoundDeviceMixer *mixer = fDevice->MixerAt(0);
2101 	oss_mixer_enuminfo enuminfo;
2102 	status_t err = mixer->GetEnumInfo(index, &enuminfo);
2103 	if (err < B_OK) {
2104 		// maybe there is no list.
2105 		// generate a count form 0
2106 		oss_mixext mixext;
2107 		if (mixer->GetExtInfo(index, &mixext) < B_OK)
2108 			return;
2109 
2110 		for (int32 i = 0; i < mixext.maxvalue; i++) {
2111 			BString s;
2112 			s << i;
2113 			parameter->AddItem(i, s.String());
2114 		}
2115 		return;
2116 	}
2117 
2118 	for (int32 i = 0; i < enuminfo.nvalues; i++) {
2119 		parameter->AddItem(i, &enuminfo.strings[enuminfo.strindex[i]]);
2120 	}
2121 	return;
2122 }
2123 
2124 
2125 status_t
2126 OpenSoundNode::_PropagateParameterChanges(int from, int type, const char* id)
2127 {
2128 	CALLED();
2129 
2130 	TRACE("OpenSoundNode::_PropagateParameterChanges(from %i, type %i, "
2131 		"id %s)\n", from, type, id);
2132 
2133 	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
2134 	if (mixer == NULL)
2135 		return ENODEV;
2136 
2137 // TODO: Cortex doesn't like that!
2138 // try timed event
2139 // try checking update_counter+caching
2140 return B_OK;
2141 
2142 //	char oldValues[128];
2143 	char newValues[128];
2144 	size_t oldValuesSize;
2145 	size_t newValuesSize;
2146 
2147 	for (int i = 0; i < mixer->CountExtInfos(); i++) {
2148 		oss_mixext mixext;
2149 		status_t err = mixer->GetExtInfo(i, &mixext);
2150 		if (err < B_OK)
2151 			continue;
2152 
2153 		// skip the caller
2154 		//if (mixext.ctrl == from)
2155 		//	continue;
2156 
2157 		if (!(mixext.flags & MIXF_READABLE))
2158 			continue;
2159 
2160 		// match type ?
2161 		if (type > -1 && mixext.type != type)
2162 			continue;
2163 
2164 		// match internal ID string
2165 		if (id && strncmp(mixext.id, id, 16))
2166 			continue;
2167 
2168 //		BParameter *parameter = NULL;
2169 //		for(int32 i=0; i<fWeb->CountParameters(); i++) {
2170 //			parameter = fWeb->ParameterAt(i);
2171 //			if(parameter->ID() == mixext.ctrl)
2172 //				break;
2173 //		}
2174 //
2175 //		if (!parameter)
2176 //			continue;
2177 
2178 		oldValuesSize = 128;
2179 		newValuesSize = 128;
2180 		bigtime_t last;
2181 //		TRACE("OpenSoundNode::%s: comparing mixer control %d\n",
2182 //			__FUNCTION__, mixext.ctrl);
2183 //		if (parameter->GetValue(oldValues, &oldValuesSize, &last) < B_OK)
2184 //			continue;
2185 		if (GetParameterValue(mixext.ctrl, &last, newValues,
2186 				&newValuesSize) < B_OK) {
2187 			continue;
2188 		}
2189 //		if (oldValuesSize != newValuesSize || memcmp(oldValues, newValues,
2190 //				MIN(oldValuesSize, newValuesSize))) {
2191 			TRACE("OpenSoundNode::%s: updating mixer control %d\n",
2192 				__FUNCTION__, mixext.ctrl);
2193 			BroadcastNewParameterValue(last, mixext.ctrl, newValues,
2194 				newValuesSize);
2195 //			BroadcastChangedParameter(mixext.ctrl);
2196 //		}
2197 	}
2198 	return B_OK;
2199 }
2200 
2201 
2202 int32
2203 OpenSoundNode::_PlayThread(NodeInput* input)
2204 {
2205 	CALLED();
2206 	//set_thread_priority(find_thread(NULL), 5);// TODO:DEBUG
2207 	signal(SIGUSR1, &_SignalHandler);
2208 
2209 	OpenSoundDeviceEngine* engine = input->fRealEngine;
2210 	if (!engine || !engine->InUse())
2211 		return B_NO_INIT;
2212 	// skip unconnected or non-busy engines
2213 	if (input->fInput.source == media_source::null
2214 		&& input->fEngineIndex == 0)
2215 		return B_NO_INIT;
2216 	// must be open for write
2217 	ASSERT(engine->OpenMode() & OPEN_WRITE);
2218 
2219 	// make writing actually block until the previous buffer played
2220 	size_t driverBufferSize = engine->DriverBufferSize();
2221 	size_t bufferSize = input->fInput.format.u.raw_audio.buffer_size;
2222 	if (driverBufferSize != bufferSize) {
2223 		printf("warning, OSS driver buffer size: %ld, audio buffer "
2224 			"size: %ld", driverBufferSize, bufferSize);
2225 	}
2226 
2227 	// cache a silence buffer
2228 	uint8 silenceBuffer[bufferSize];
2229 	uint8 formatSilence = 0;
2230 	if (input->fInput.format.u.raw_audio.format
2231 			== media_raw_audio_format::B_AUDIO_UCHAR)
2232 		formatSilence = 128;
2233 
2234 	memset(silenceBuffer, formatSilence, bufferSize);
2235 
2236 	// start by writing the OSS driver buffer size of silence
2237 	// so that the first call to write() already blocks for (almost) the
2238 	// buffer duration
2239 	input->Write(silenceBuffer, bufferSize);
2240 
2241 	int64 bytesWritten = 0;
2242 	bigtime_t lastRealTime = RealTime();
2243 	bigtime_t lastPerformanceTime = 0;
2244 
2245 	const int32 driftValueCount = 64;
2246 	int32 currentDriftValueIndex = 0;
2247 	float driftValues[driftValueCount];
2248 	for (int32 i = 0; i < driftValueCount; i++)
2249 		driftValues[i] = 1.0;
2250 
2251 	do {
2252 		if (!fDevice->Locker()->Lock())
2253 			break;
2254 
2255 		TRACE("OpenSoundNode::_PlayThread: buffers: %ld\n",
2256 			input->fBuffers.CountItems());
2257 
2258 		BBuffer* buffer = (BBuffer*)input->fBuffers.RemoveItem(0L);
2259 
2260 		fDevice->Locker()->Unlock();
2261 
2262 		if (input->fThread < 0) {
2263 			if (buffer)
2264 				buffer->Recycle();
2265 			break;
2266 		}
2267 
2268 //input->WriteTestTone();
2269 //if (buffer)
2270 //	buffer->Recycle();
2271 //continue;
2272 
2273 		int32 additionalBytesWritten = 0;
2274 		if (buffer != NULL) {
2275 			if (input->Write(buffer->Data(), buffer->SizeUsed()) == B_OK)
2276 				additionalBytesWritten = buffer->SizeUsed();
2277 			buffer->Recycle();
2278 		} else {
2279 			input->Write(silenceBuffer, bufferSize);
2280 			additionalBytesWritten = bufferSize;
2281 		}
2282 
2283 		// TODO: do not assume channel 0 will always be running!
2284 		// update the timesource
2285 		if (input->fEngineIndex == 0 && input->fThread >= 0) {
2286 
2287 			bigtime_t realTime = RealTime();
2288 			bigtime_t realPlaybackDuration = realTime - lastRealTime;
2289 			bigtime_t performanceTime
2290 				= time_for_buffer(bytesWritten, input->fInput.format);
2291 			float drift = (double)(performanceTime
2292 				- lastPerformanceTime) / realPlaybackDuration;
2293 
2294 			lastPerformanceTime = performanceTime;
2295 			lastRealTime = realTime;
2296 
2297 			driftValues[currentDriftValueIndex++] = drift;
2298 			if (currentDriftValueIndex == driftValueCount)
2299 				currentDriftValueIndex = 0;
2300 			drift = 0.0;
2301 			for (int32 i = 0; i < driftValueCount; i++)
2302 				drift += driftValues[i];
2303 			drift /= driftValueCount;
2304 
2305 			if (fDevice->Locker()->Lock()) {
2306 				if (input->fThread >= 0)
2307 					_UpdateTimeSource(performanceTime, realTime, drift);
2308 				fDevice->Locker()->Unlock();
2309 			}
2310 		}
2311 		bytesWritten += additionalBytesWritten;
2312 
2313 	} while (input->fThread > -1);
2314 
2315 	return 0;
2316 }
2317 
2318 
2319 int32
2320 OpenSoundNode::_RecThread(NodeOutput* output)
2321 {
2322 	CALLED();
2323 
2324 	//set_thread_priority(find_thread(NULL), 5);// TODO:DEBUG
2325 	signal(SIGUSR1, &_SignalHandler);
2326 
2327 	OpenSoundDeviceEngine *engine = output->fRealEngine;
2328 	if (!engine || !engine->InUse())
2329 		return B_NO_INIT;
2330 	// make sure we're both started *and* connected before delivering a buffer
2331 	if ((RunState() != BMediaEventLooper::B_STARTED)
2332 		|| (output->fOutput.destination == media_destination::null)) {
2333 		return B_NO_INIT;
2334 	}
2335 
2336 	// must be open for read
2337 	ASSERT(engine->OpenMode() & OPEN_READ);
2338 
2339 #ifdef ENABLE_REC
2340 
2341 	fDevice->Locker()->Lock();
2342 	do {
2343 		audio_buf_info abinfo;
2344 //		size_t avail = engine->GetISpace(&abinfo);
2345 //		TRACE("OpenSoundNode::_RunThread: I avail: %d\n", avail);
2346 //
2347 //		// skip if less than 1 buffer
2348 //		if (avail < output->fOutput.format.u.raw_audio.buffer_size)
2349 //			continue;
2350 
2351 		fDevice->Locker()->Unlock();
2352 		// Get the next buffer of data
2353 		BBuffer* buffer = _FillNextBuffer(&abinfo, *output);
2354 		fDevice->Locker()->Lock();
2355 
2356 		if (buffer) {
2357 			// send the buffer downstream if and only if output is enabled
2358 			status_t err = B_ERROR;
2359 			if (output->fOutputEnabled)
2360 				err = SendBuffer(buffer, output->fOutput.destination);
2361 //			TRACE("OpenSoundNode::_RunThread: I avail: %d, OE %d, %s\n",
2362 //				avail, output->fOutputEnabled, strerror(err));
2363 			if (err != B_OK) {
2364 				buffer->Recycle();
2365 			} else {
2366 				// track how much media we've delivered so far
2367 				size_t nSamples = buffer->SizeUsed()
2368 					/ (output->fOutput.format.u.raw_audio.format
2369 						& media_raw_audio_format::B_AUDIO_SIZE_MASK);
2370 				output->fSamplesSent += nSamples;
2371 //				TRACE("OpenSoundNode::%s: sent %d samples\n",
2372 //					__FUNCTION__, nSamples);
2373 			}
2374 
2375 		}
2376 	} while (output->fThread > -1);
2377 	fDevice->Locker()->Unlock();
2378 
2379 #endif
2380 	return 0;
2381 }
2382 
2383 
2384 status_t
2385 OpenSoundNode::_StartPlayThread(NodeInput* input)
2386 {
2387 	CALLED();
2388 	BAutolock L(fDevice->Locker());
2389 	// the thread is already started ?
2390 	if (input->fThread > B_OK)
2391 		return B_OK;
2392 
2393 	//allocate buffer free semaphore
2394 //	int bufferCount = MAX(fDevice->fFragments.fragstotal, 2); // XXX
2395 
2396 //	fBufferAvailableSem = create_sem(fDevice->MBL.return_playback_buffers - 1,
2397 //		"multi_audio out buffer free");
2398 //	fBufferAvailableSem = create_sem(bufferCount - 1,
2399 //		"OpenSound out buffer free");
2400 
2401 //	if (fBufferAvailableSem < B_OK)
2402 //		return B_ERROR;
2403 
2404 	input->fThread = spawn_thread(_PlayThreadEntry,
2405 		"OpenSound audio output", B_REAL_TIME_PRIORITY, input);
2406 
2407 	if (input->fThread < B_OK) {
2408 //		delete_sem(fBufferAvailableSem);
2409 		return B_ERROR;
2410 	}
2411 
2412 	resume_thread(input->fThread);
2413 	return B_OK;
2414 }
2415 
2416 
2417 status_t
2418 OpenSoundNode::_StopPlayThread(NodeInput* input)
2419 {
2420 	if (input->fThread < 0)
2421 		return B_OK;
2422 
2423 	CALLED();
2424 
2425 	thread_id th;
2426 	{
2427 		BAutolock L(fDevice->Locker());
2428 		th = input->fThread;
2429 		input->fThread = -1;
2430 		//kill(th, SIGUSR1);
2431 	}
2432 	status_t ret;
2433 	wait_for_thread(th, &ret);
2434 
2435 	return B_OK;
2436 }
2437 
2438 
2439 status_t
2440 OpenSoundNode::_StartRecThread(NodeOutput* output)
2441 {
2442 	CALLED();
2443 	// the thread is already started ?
2444 	if (output->fThread > B_OK)
2445 		return B_OK;
2446 
2447 	//allocate buffer free semaphore
2448 //	int bufferCount = MAX(fDevice->fFragments.fragstotal, 2); // XXX
2449 
2450 //	fBufferAvailableSem = create_sem(fDevice->MBL.return_playback_buffers - 1,
2451 //		"multi_audio out buffer free");
2452 //	fBufferAvailableSem = create_sem(bufferCount - 1,
2453 //		"OpenSound out buffer free");
2454 
2455 //	if (fBufferAvailableSem < B_OK)
2456 //		return B_ERROR;
2457 
2458 	output->fThread = spawn_thread(_RecThreadEntry, "OpenSound audio input",
2459 		B_REAL_TIME_PRIORITY, output);
2460 
2461 	if (output->fThread < B_OK) {
2462 		//delete_sem(fBufferAvailableSem);
2463 		return B_ERROR;
2464 	}
2465 
2466 	resume_thread(output->fThread);
2467 	return B_OK;
2468 }
2469 
2470 
2471 status_t
2472 OpenSoundNode::_StopRecThread(NodeOutput* output)
2473 {
2474 	if (output->fThread < 0)
2475 		return B_OK;
2476 
2477 	CALLED();
2478 
2479 	thread_id th = output->fThread;
2480 	output->fThread = -1;
2481 	{
2482 		BAutolock L(fDevice->Locker());
2483 		//kill(th, SIGUSR1);
2484 	}
2485 	status_t ret;
2486 	wait_for_thread(th, &ret);
2487 
2488 	return B_OK;
2489 }
2490 
2491 
2492 void
2493 OpenSoundNode::_UpdateTimeSource(bigtime_t performanceTime,
2494 	bigtime_t realTime, float drift)
2495 {
2496 //	CALLED();
2497 
2498 	if (!fTimeSourceStarted)
2499 		return;
2500 
2501 	PublishTime(performanceTime, realTime, drift);
2502 
2503 //	TRACE("_UpdateTimeSource() perfTime : %lli, realTime : %lli, "
2504 //		"drift : %f\n", perfTime, realTime, drift);
2505 }
2506 
2507 
2508 BBuffer*
2509 OpenSoundNode::_FillNextBuffer(audio_buf_info* abinfo, NodeOutput& channel)
2510 {
2511 	CALLED();
2512 
2513 	BBuffer* buffer = channel.FillNextBuffer(BufferDuration());
2514 	if (!buffer)
2515 		return NULL;
2516 
2517 	if (fDevice == NULL)
2518 		fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - fDevice NULL\n");
2519 	if (buffer->Header() == NULL) {
2520 		fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - "
2521 			"buffer->Header() NULL\n");
2522 	}
2523 	if (TimeSource() == NULL) {
2524 		fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - "
2525 			"TimeSource() NULL\n");
2526 	}
2527 
2528 	// fill in the buffer header
2529 	media_header* hdr = buffer->Header();
2530 	if (hdr != NULL) {
2531 		hdr->time_source = TimeSource()->ID();
2532 		// TODO: should be system_time() - latency_as_per(abinfo)
2533 		hdr->start_time = PerformanceTimeFor(system_time());
2534 	}
2535 
2536 	return buffer;
2537 }
2538 
2539 
2540 status_t
2541 OpenSoundNode::GetConfigurationFor(BMessage* into_message)
2542 {
2543 	CALLED();
2544 
2545 	if (!into_message)
2546 		return B_BAD_VALUE;
2547 
2548 	size_t size = 128;
2549 	void* buffer = malloc(size);
2550 
2551 	for (int32 i = 0; i < fWeb->CountParameters(); i++) {
2552 		BParameter* parameter = fWeb->ParameterAt(i);
2553 		if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER
2554 			&& parameter->Type() != BParameter::B_DISCRETE_PARAMETER)
2555 			continue;
2556 
2557 		TRACE("getting parameter %i\n", parameter->ID());
2558 		size = 128;
2559 		bigtime_t last_change;
2560 		status_t err;
2561 		while ((err = GetParameterValue(parameter->ID(), &last_change, buffer,
2562 				&size)) == B_NO_MEMORY) {
2563 			size += 128;
2564 			free(buffer);
2565 			buffer = malloc(size);
2566 		}
2567 
2568 		if (err == B_OK && size > 0) {
2569 			into_message->AddInt32("parameterID", parameter->ID());
2570 			into_message->AddData("parameterData", B_RAW_TYPE, buffer, size,
2571 				false);
2572 		} else {
2573 			TRACE("parameter err : %s\n", strerror(err));
2574 		}
2575 	}
2576 
2577 	free(buffer);
2578 
2579 	PRINT_OBJECT(*into_message);
2580 
2581 	return B_OK;
2582 }
2583 
2584 
2585 OpenSoundNode::NodeOutput*
2586 OpenSoundNode::_FindOutput(const media_source& source) const
2587 {
2588 	int32 count = fOutputs.CountItems();
2589 	for (int32 i = 0; i < count; i++) {
2590 		NodeOutput* channel = (NodeOutput*)fOutputs.ItemAtFast(i);
2591 		if (source == channel->fOutput.source)
2592 			return channel;
2593 	}
2594 	return NULL;
2595 }
2596 
2597 
2598 OpenSoundNode::NodeInput*
2599 OpenSoundNode::_FindInput(const media_destination& dest) const
2600 {
2601 	int32 count = fInputs.CountItems();
2602 	for (int32 i = 0; i < count; i++) {
2603 		NodeInput* channel = (NodeInput*)fInputs.ItemAtFast(i);
2604 		if (dest == channel->fInput.destination)
2605 			return channel;
2606 	}
2607 	return NULL;
2608 }
2609 
2610 
2611 OpenSoundNode::NodeInput*
2612 OpenSoundNode::_FindInput(int32 destinationId)
2613 {
2614 	int32 count = fInputs.CountItems();
2615 	for (int32 i = 0; i < count; i++) {
2616 		NodeInput* channel = (NodeInput*)fInputs.ItemAtFast(i);
2617 		if (destinationId == channel->fInput.destination.id)
2618 			return channel;
2619 	}
2620 	return NULL;
2621 }
2622 
2623 
2624 // pragma mark - static
2625 
2626 
2627 void
2628 OpenSoundNode::_SignalHandler(int sig)
2629 {
2630 	// TODO: what was this intended for, just stopping the threads?
2631 	// (see _StopThreadXXX(), there is a kill call commented out there)
2632 }
2633 
2634 
2635 int32
2636 OpenSoundNode::_PlayThreadEntry(void* data)
2637 {
2638 	CALLED();
2639 	NodeInput* channel = static_cast<NodeInput*>(data);
2640 	return channel->fNode->_PlayThread(channel);
2641 }
2642 
2643 
2644 int32
2645 OpenSoundNode::_RecThreadEntry(void* data)
2646 {
2647 	CALLED();
2648 	NodeOutput* channel = static_cast<NodeOutput*>(data);
2649 	return channel->fNode->_RecThread(channel);
2650 }
2651 
2652 
2653 void
2654 OpenSoundNode::GetFlavor(flavor_info* outInfo, int32 id)
2655 {
2656 	CALLED();
2657 	if (outInfo == NULL)
2658 		return;
2659 
2660 	outInfo->flavor_flags = 0;
2661 	outInfo->possible_count = 1;
2662 		// one flavor at a time
2663 	outInfo->in_format_count = 0;
2664 		// no inputs
2665 	outInfo->in_formats = 0;
2666 	outInfo->out_format_count = 0;
2667 		// no outputs
2668 	outInfo->out_formats = 0;
2669 	outInfo->internal_id = id;
2670 
2671 	outInfo->name = "OpenSoundNode Node";
2672 	outInfo->info = "The OpenSoundNode outputs to OpenSound System v4 "
2673 		"drivers.";
2674 	outInfo->kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER | B_TIME_SOURCE
2675 		| B_PHYSICAL_OUTPUT | B_PHYSICAL_INPUT | B_CONTROLLABLE;
2676 	// TODO: If the OSS engine supports outputing encoded audio,
2677 	// we would need to setup a B_MEDIA_ENCODED_AUDIO format here
2678 	outInfo->in_format_count = 1;
2679 		// 1 input
2680 	media_format * informats = new media_format[outInfo->in_format_count];
2681 	GetFormat(&informats[0]);
2682 	outInfo->in_formats = informats;
2683 
2684 	outInfo->out_format_count = 1;
2685 		// 1 output
2686 	media_format * outformats = new media_format[outInfo->out_format_count];
2687 	GetFormat(&outformats[0]);
2688 	outInfo->out_formats = outformats;
2689 }
2690 
2691 
2692 void
2693 OpenSoundNode::GetFormat(media_format* outFormat)
2694 {
2695 	CALLED();
2696 	if (outFormat == NULL)
2697 		return;
2698 
2699 	outFormat->type = B_MEDIA_RAW_AUDIO;
2700 	outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
2701 	outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
2702 	outFormat->u.raw_audio = media_raw_audio_format::wildcard;
2703 }
2704