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