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