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