xref: /haiku/src/add-ons/media/media-add-ons/opensound/OpenSoundNode.cpp (revision f659f955ba5566abe8403a10a816eeaae14ef97b)
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(0L))
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(0UL)
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 	SetPriority(B_REAL_TIME_PRIORITY);
452 
453 	Run();
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 %ld (%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 %ld (%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 
581 
582 status_t
583 OpenSoundNode::RequestCompleted(const media_request_info& info)
584 {
585 	CALLED();
586 	return B_OK;
587 }
588 
589 
590 void
591 OpenSoundNode::SetTimeSource(BTimeSource* timeSource)
592 {
593 	CALLED();
594 }
595 
596 
597 // #pragma mark - BBufferConsumer
598 
599 
600 //!	Someone, probably the producer, is asking you about this format.
601 //	Give your honest opinion, possibly modifying *format. Do not ask
602 //	upstream producer about the format, since he's synchronously
603 //	waiting for your reply.
604 status_t
605 OpenSoundNode::AcceptFormat(const media_destination& dest,
606 	media_format* format)
607 {
608 	// Check to make sure the format is okay, then remove
609 	// any wildcards corresponding to our requirements.
610 
611 	CALLED();
612 
613 	NodeInput* channel = _FindInput(dest);
614 
615 	if (channel == NULL) {
616 		fprintf(stderr, "OpenSoundNode::AcceptFormat()"
617 			" - B_MEDIA_BAD_DESTINATION");
618 		return B_MEDIA_BAD_DESTINATION;
619 			// we only have one input so that better be it
620 	}
621 
622 	if (format == NULL) {
623 		fprintf(stderr, "OpenSoundNode::AcceptFormat() - B_BAD_VALUE\n");
624 		return B_BAD_VALUE;
625 	}
626 
627 /*	media_format * myFormat = GetFormat();
628 	fprintf(stderr,"proposed format: ");
629 	print_media_format(format);
630 	fprintf(stderr,"\n");
631 	fprintf(stderr,"my format: ");
632 	print_media_format(myFormat);
633 	fprintf(stderr,"\n");*/
634 	// Be's format_is_compatible doesn't work.
635 //	if (!format_is_compatible(*format,*myFormat)) {
636 
637 	BAutolock L(fDevice->Locker());
638 
639 	OpenSoundDeviceEngine* engine = fDevice->NextFreeEngineAt(
640 		channel->fEngineIndex, false);
641 	if (!engine)
642 		return B_BUSY;
643 
644 	status_t err = engine->AcceptFormatFor(channel->fOSSFormatFlags, *format,
645 		false);
646 	if (err < B_OK)
647 		return err;
648 
649 	channel->fRealEngine = engine;
650 
651 /*
652 	if ( format->type != B_MEDIA_RAW_AUDIO ) {
653 		fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
654 		return B_MEDIA_BAD_FORMAT;
655 	}
656 */
657 
658 	//channel->fInput.format = channel->fPreferredFormat;
659 	channel->fInput.format = *format;
660 
661 	/*if(format->u.raw_audio.format == media_raw_audio_format::B_AUDIO_FLOAT
662 		&& channel->fPreferredFormat.u.raw_audio.format
663 			== media_raw_audio_format::B_AUDIO_SHORT)
664 		format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
665 	else*/
666 /*
667 	format->u.raw_audio.format = channel->fPreferredFormat.u.raw_audio.format;
668 	format->u.raw_audio.valid_bits
669 		= channel->fPreferredFormat.u.raw_audio.valid_bits;
670 
671 	format->u.raw_audio.frame_rate
672 		= channel->fPreferredFormat.u.raw_audio.frame_rate;
673 	format->u.raw_audio.channel_count
674 		= channel->fPreferredFormat.u.raw_audio.channel_count;
675 	format->u.raw_audio.byte_order
676 		= B_MEDIA_HOST_ENDIAN;
677 
678 	format->u.raw_audio.buffer_size
679 		= DEFAULT_BUFFER_SIZE
680 			* (format->u.raw_audio.format
681 				& media_raw_audio_format::B_AUDIO_SIZE_MASK)
682 			* format->u.raw_audio.channel_count;
683 */
684 
685 //	media_format myFormat;
686 //	GetFormat(&myFormat);
687 //	if (!format_is_acceptible(*format,myFormat)) {
688 //		fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
689 //		return B_MEDIA_BAD_FORMAT;
690 //	}
691 
692 	return B_OK;
693 }
694 
695 
696 status_t
697 OpenSoundNode::GetNextInput(int32* cookie, media_input* out_input)
698 {
699 	CALLED();
700 
701 	// let's not crash even if they are stupid
702 	if (out_input == NULL) {
703 		// no place to write!
704 		fprintf(stderr,"OpenSoundNode::GetNextInput() - B_BAD_VALUE\n");
705 		return B_BAD_VALUE;
706 	}
707 
708 	if (*cookie >= fInputs.CountItems() || *cookie < 0)
709 		return B_BAD_INDEX;
710 
711 	NodeInput* channel = (NodeInput*)fInputs.ItemAt(*cookie);
712 	*out_input = channel->fInput;
713 	*cookie += 1;
714 
715 	TRACE("input.format : %u\n", channel->fInput.format.u.raw_audio.format);
716 
717 	return B_OK;
718 }
719 
720 
721 void
722 OpenSoundNode::DisposeInputCookie(int32 cookie)
723 {
724 	CALLED();
725 	// nothing to do since our cookies are just integers
726 }
727 
728 
729 void
730 OpenSoundNode::BufferReceived(BBuffer* buffer)
731 {
732 	CALLED();
733 
734 	switch (buffer->Header()->type) {
735 //		case B_MEDIA_PARAMETERS:
736 //		{
737 //			status_t status = ApplyParameterData(buffer->Data(),
738 //				buffer->SizeUsed());
739 //			if (status != B_OK) {
740 //				fprintf(stderr, "ApplyParameterData() in "
741 //					"OpenSoundNode::BufferReceived() failed: %s\n",
742 //					strerror(status));
743 //			}
744 //			buffer->Recycle();
745 //			break;
746 //		}
747 		case B_MEDIA_RAW_AUDIO:
748 			if (buffer->Flags() & BBuffer::B_SMALL_BUFFER) {
749 				fprintf(stderr, "OpenSoundNode::BufferReceived() - "
750 					"B_SMALL_BUFFER not implemented\n");
751 				// TODO: implement this part
752 				buffer->Recycle();
753 			} else {
754 				media_timed_event event(buffer->Header()->start_time,
755 					BTimedEventQueue::B_HANDLE_BUFFER, buffer,
756 					BTimedEventQueue::B_RECYCLE_BUFFER);
757 				status_t status = EventQueue()->AddEvent(event);
758 				if (status != B_OK) {
759 					fprintf(stderr, "OpenSoundNode::BufferReceived() - "
760 						"EventQueue()->AddEvent() failed: %s\n",
761 						strerror(status));
762 					buffer->Recycle();
763 				}
764 			}
765 			break;
766 		default:
767 			fprintf(stderr, "OpenSoundNode::BufferReceived() - unexpected "
768 				"buffer type\n");
769 			buffer->Recycle();
770 			break;
771 	}
772 }
773 
774 
775 void
776 OpenSoundNode::ProducerDataStatus(const media_destination& for_whom,
777 	int32 status, bigtime_t at_performance_time)
778 {
779 	CALLED();
780 
781 	NodeInput* channel = _FindInput(for_whom);
782 
783 	if (channel == NULL) {
784 		fprintf(stderr,"OpenSoundNode::ProducerDataStatus() - "
785 			"invalid destination\n");
786 		return;
787 	}
788 
789 //	TRACE("************ ProducerDataStatus: queuing event ************\n");
790 //	TRACE("************ status=%d ************\n", status);
791 
792 	media_timed_event event(at_performance_time,
793 		BTimedEventQueue::B_DATA_STATUS, &channel->fInput,
794 		BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
795 	EventQueue()->AddEvent(event);
796 }
797 
798 
799 status_t
800 OpenSoundNode::GetLatencyFor(const media_destination& for_whom,
801 	bigtime_t* out_latency, media_node_id* out_timesource)
802 {
803 	CALLED();
804 
805 	if (out_latency == NULL || out_timesource == NULL) {
806 		fprintf(stderr,"OpenSoundNode::GetLatencyFor() - B_BAD_VALUE\n");
807 		return B_BAD_VALUE;
808 	}
809 
810 	NodeInput* channel = _FindInput(for_whom);
811 
812 	if (channel == NULL || channel->fRealEngine == NULL) {
813 		fprintf(stderr,"OpenSoundNode::GetLatencyFor() - "
814 			"B_MEDIA_BAD_DESTINATION\n");
815 		return B_MEDIA_BAD_DESTINATION;
816 	}
817 
818 	// start with the node latency (1 buffer duration)
819 	*out_latency = EventLatency();
820 
821 	// add the OSS driver buffer's latency as well
822 	bigtime_t bufferLatency = channel->fRealEngine->PlaybackLatency();
823 	*out_latency += bufferLatency;
824 
825 	TRACE("OpenSoundNode::GetLatencyFor() - EventLatency %lld, OSS %lld\n",
826 		EventLatency(), bufferLatency);
827 
828 	*out_timesource = TimeSource()->ID();
829 
830 	return B_OK;
831 }
832 
833 
834 status_t
835 OpenSoundNode::Connected(const media_source& producer,
836 	const media_destination& where, const media_format& with_format,
837 	media_input* out_input)
838 {
839 	CALLED();
840 
841 	if (out_input == NULL) {
842 		fprintf(stderr,"OpenSoundNode::Connected() - B_BAD_VALUE\n");
843 		return B_BAD_VALUE;
844 	}
845 
846 	NodeInput* channel = _FindInput(where);
847 
848 	if (channel == NULL) {
849 		fprintf(stderr,"OpenSoundNode::Connected() - "
850 			"B_MEDIA_BAD_DESTINATION\n");
851 		return B_MEDIA_BAD_DESTINATION;
852 	}
853 
854 	BAutolock L(fDevice->Locker());
855 
856 	// use one half buffer length latency
857 	size_t bufferSize = channel->fRealEngine->DriverBufferSize() / 2;
858 	fInternalLatency = time_for_buffer(bufferSize, with_format);
859 	TRACE("  internal latency = %lld\n", fInternalLatency);
860 
861 	// TODO: A global node value is assigned a channel specific value!
862 	// That can't be correct. For as long as there is only one output
863 	// in use at a time, this will not matter of course.
864 	SetEventLatency(fInternalLatency);
865 
866 	// record the agreed upon values
867 	channel->fInput.source = producer;
868 	channel->fInput.format = with_format;
869 
870 	*out_input = channel->fInput;
871 
872 	// we are sure the thread is started
873 	_StartPlayThread(channel);
874 
875 	return B_OK;
876 }
877 
878 
879 void
880 OpenSoundNode::Disconnected(const media_source& producer,
881 	const media_destination& where)
882 {
883 	CALLED();
884 
885 	NodeInput* channel = _FindInput(where);
886 	if (channel == NULL) {
887 		fprintf(stderr,"OpenSoundNode::Disconnected() - "
888 			"B_MEDIA_BAD_DESTINATION\n");
889 		return;
890 	}
891 	if (channel->fInput.source != producer) {
892 		fprintf(stderr,"OpenSoundNode::Disconnected() - "
893 			"B_MEDIA_BAD_SOURCE\n");
894 		return;
895 	}
896 
897 	_StopPlayThread(channel);
898 
899 	channel->RecycleAllBuffers();
900 
901 	channel->fInput.source = media_source::null;
902 	channel->fInput.format = channel->fPreferredFormat;
903 	if (channel->fRealEngine)
904 		channel->fRealEngine->Close();
905 	channel->fRealEngine = NULL;
906 }
907 
908 
909 //! The notification comes from the upstream producer, so he's
910 //	already cool with the format; you should not ask him about it
911 //	in here.
912 status_t
913 OpenSoundNode::FormatChanged(const media_source& producer,
914 	const media_destination& consumer,  int32 change_tag,
915 	const media_format& format)
916 {
917 	CALLED();
918 	NodeInput* channel = _FindInput(consumer);
919 
920 	if (channel == NULL) {
921 		fprintf(stderr,"OpenSoundNode::FormatChanged() - "
922 			"B_MEDIA_BAD_DESTINATION\n");
923 		return B_MEDIA_BAD_DESTINATION;
924 	}
925 
926 	if (channel->fInput.source != producer) {
927 		fprintf(stderr,"OpenSoundNode::FormatChanged() - "
928 			"B_MEDIA_BAD_SOURCE\n");
929 		return B_MEDIA_BAD_SOURCE;
930 	}
931 
932 	// currently not supported, TODO: implement?
933 	return B_ERROR;
934 }
935 
936 
937 //!	Given a performance time of some previous buffer, retrieve the
938 //	remembered tag of the closest (previous or exact) performance
939 //	time. Set *out_flags to 0; the idea being that flags can be
940 //	added later, and the understood flags returned in *out_flags.
941 status_t
942 OpenSoundNode::SeekTagRequested(const media_destination& destination,
943 	bigtime_t in_target_time, uint32 in_flags, media_seek_tag* out_seek_tag,
944 	bigtime_t* out_tagged_time, uint32* out_flags)
945 {
946 	CALLED();
947 	return BBufferConsumer::SeekTagRequested(destination, in_target_time,
948 		in_flags, out_seek_tag, out_tagged_time, out_flags);
949 }
950 
951 
952 // #pragma mark - BBufferProducer
953 
954 
955 //!	FormatSuggestionRequested() is not necessarily part of the format
956 //	negotiation process; it's simply an interrogation -- the caller wants
957 //	to see what the node's preferred data format is, given a suggestion by
958 //	the caller.
959 status_t
960 OpenSoundNode::FormatSuggestionRequested(media_type type, int32 /*quality*/,
961 	media_format* format)
962 {
963 	CALLED();
964 
965 	if (format == NULL) {
966 		fprintf(stderr, "\tERROR - NULL format pointer passed in!\n");
967 		return B_BAD_VALUE;
968 	}
969 
970 	// this is the format we'll be returning (our preferred format)
971 	*format = fPreferredFormat;
972 
973 	// a wildcard type is okay; we can specialize it
974 	if (type == B_MEDIA_UNKNOWN_TYPE)
975 		type = B_MEDIA_RAW_AUDIO;
976 
977 	// TODO: For OSS engines that support encoded formats, we could
978 	// handle this here. For the time being, we only support raw audio.
979 	if (type != B_MEDIA_RAW_AUDIO)
980 		return B_MEDIA_BAD_FORMAT;
981 
982 	return B_OK;
983 }
984 
985 
986 //!	FormatProposal() is the first stage in the BMediaRoster::Connect()
987 //	process.  We hand out a suggested format, with wildcards for any
988 //	variations we support.
989 status_t
990 OpenSoundNode::FormatProposal(const media_source& output, media_format* format)
991 {
992 	CALLED();
993 
994 	NodeOutput* channel = _FindOutput(output);
995 
996 	// is this a proposal for our select output?
997 	if (channel == NULL) {
998 		fprintf(stderr, "OpenSoundNode::FormatProposal returning "
999 			"B_MEDIA_BAD_SOURCE\n");
1000 		return B_MEDIA_BAD_SOURCE;
1001 	}
1002 
1003 	media_type requestedType = format->type;
1004 #ifdef ENABLE_REC
1005 	OpenSoundDeviceEngine* engine = fDevice->NextFreeEngineAt(
1006 		channel->fEngineIndex, true);
1007 
1008 	// We only support raw audio, so we always return that, but we supply an
1009 	// error code depending on whether we found the proposal acceptable.
1010 	status_t err = engine->PreferredFormatFor(channel->fOSSFormatFlags, *format, true);
1011 	if (err < B_OK)
1012 		return err;
1013 #else
1014 	*format = fPreferredFormat;
1015 #endif
1016 	if (requestedType != B_MEDIA_UNKNOWN_TYPE
1017 		&& requestedType != B_MEDIA_RAW_AUDIO
1018 #ifdef ENABLE_NON_RAW_SUPPORT
1019 		 && requestedType != B_MEDIA_ENCODED_AUDIO
1020 #endif
1021 		)
1022 	{
1023 		fprintf(stderr, "OpenSoundNode::FormatProposal returning "
1024 			"B_MEDIA_BAD_FORMAT\n");
1025 		return B_MEDIA_BAD_FORMAT;
1026 	}
1027 
1028 	// raw audio or wildcard type, either is okay by us
1029 	return B_OK;
1030 }
1031 
1032 
1033 status_t
1034 OpenSoundNode::FormatChangeRequested(const media_source& source,
1035 	const media_destination& destination, media_format* io_format,
1036 	int32* _deprecated_)
1037 {
1038 	CALLED();
1039 
1040 	// we don't support any other formats, so we just reject any format
1041 	// changes. TODO: implement?
1042 	return B_ERROR;
1043 }
1044 
1045 
1046 status_t
1047 OpenSoundNode::GetNextOutput(int32* cookie, media_output* out_output)
1048 {
1049 	CALLED();
1050 
1051 	if (*cookie >= fOutputs.CountItems() || *cookie < 0)
1052 		return B_BAD_INDEX;
1053 
1054 	NodeOutput *channel = (NodeOutput*)fOutputs.ItemAt(*cookie);
1055 	*out_output = channel->fOutput;
1056 	*cookie += 1;
1057 	return B_OK;
1058 }
1059 
1060 
1061 status_t
1062 OpenSoundNode::DisposeOutputCookie(int32 cookie)
1063 {
1064 	CALLED();
1065 	// do nothing because we don't use the cookie for anything special
1066 	return B_OK;
1067 }
1068 
1069 
1070 status_t
1071 OpenSoundNode::SetBufferGroup(const media_source& for_source,
1072 	BBufferGroup* newGroup)
1073 {
1074 	CALLED();
1075 
1076 	NodeOutput* channel = _FindOutput(for_source);
1077 
1078 	// is this our output?
1079 	if (channel == NULL) {
1080 		fprintf(stderr, "OpenSoundNode::SetBufferGroup() returning "
1081 			"B_MEDIA_BAD_SOURCE\n");
1082 		return B_MEDIA_BAD_SOURCE;
1083 	}
1084 
1085 	// Are we being passed the buffer group we're already using?
1086 	if (newGroup == channel->fBufferGroup)
1087 		return B_OK;
1088 
1089 	// Ahh, someone wants us to use a different buffer group.  At this point
1090 	// we delete the one we are using and use the specified one instead.  If
1091 	// the specified group is NULL, we need to recreate one ourselves, and
1092 	// use *that*.  Note that if we're caching a BBuffer that we requested
1093 	// earlier, we have to Recycle() that buffer *before* deleting the buffer
1094 	// group, otherwise we'll deadlock waiting for that buffer to be recycled!
1095 	channel->FreeBuffers();
1096 		// waits for all buffers to recycle
1097 	if (newGroup != NULL) {
1098 		// we were given a valid group; just use that one from now on
1099 		return channel->SetExternalBuffers(newGroup);
1100 	} else {
1101 		// we were passed a NULL group pointer; that means we construct
1102 		// our own buffer group to use from now on
1103 		return channel->AllocateBuffers(BufferDuration(), fLatency);
1104 	}
1105 }
1106 
1107 
1108 //!	PrepareToConnect() is the second stage of format negotiations that happens
1109 //	inside BMediaRoster::Connect().  At this point, the consumer's
1110 //	AcceptFormat() method has been called, and that node has potentially
1111 //	changed the proposed format.  It may also have left wildcards in the
1112 //	format.  PrepareToConnect() *must* fully specialize the format before
1113 //	returning!
1114 status_t
1115 OpenSoundNode::PrepareToConnect(const media_source& what,
1116 	const media_destination& where, media_format* format,
1117 	media_source* out_source, char* out_name)
1118 {
1119 	CALLED();
1120 
1121 	status_t err;
1122 	NodeOutput *channel = _FindOutput(what);
1123 
1124 	// is this our output?
1125 	if (channel == NULL) {
1126 		fprintf(stderr, "OpenSoundNode::PrepareToConnect returning "
1127 			"B_MEDIA_BAD_SOURCE\n");
1128 		return B_MEDIA_BAD_SOURCE;
1129 	}
1130 
1131 	// are we already connected?
1132 	if (channel->fOutput.destination != media_destination::null)
1133 		return B_MEDIA_ALREADY_CONNECTED;
1134 
1135 	BAutolock L(fDevice->Locker());
1136 
1137 	OpenSoundDeviceEngine *engine = fDevice->NextFreeEngineAt(
1138 		channel->fEngineIndex, false);
1139 	if (engine == NULL)
1140 		return B_BUSY;
1141 
1142 	// the format may not yet be fully specialized (the consumer might have
1143 	// passed back some wildcards).  Finish specializing it now, and return an
1144 	// error if we don't support the requested format.
1145 	if (format->type != B_MEDIA_RAW_AUDIO) {
1146 		fprintf(stderr, "\tnon-raw-audio format?!\n");
1147 		return B_MEDIA_BAD_FORMAT;
1148 	}
1149 
1150 	// !!! validate all other fields except for buffer_size here, because the
1151 	// consumer might have supplied different values from AcceptFormat()?
1152 	err = engine->SpecializeFormatFor(channel->fOSSFormatFlags, *format, true);
1153 	if (err < B_OK)
1154 		return err;
1155 
1156 	channel->fRealEngine = engine;
1157 
1158 #if 0
1159 	// check the buffer size, which may still be wildcarded
1160 	if (format->u.raw_audio.buffer_size
1161 		== media_raw_audio_format::wildcard.buffer_size) {
1162 		format->u.raw_audio.buffer_size = 2048;
1163 			// pick something comfortable to suggest
1164 		fprintf(stderr, "\tno buffer size provided, suggesting %lu\n",
1165 			format->u.raw_audio.buffer_size);
1166 	} else {
1167 		fprintf(stderr, "\tconsumer suggested buffer_size %lu\n",
1168 			format->u.raw_audio.buffer_size);
1169 	}
1170 #endif
1171 
1172 	// Now reserve the connection, and return information about it
1173 	channel->fOutput.destination = where;
1174 	channel->fOutput.format = *format;
1175 	*out_source = channel->fOutput.source;
1176 	strncpy(out_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
1177 	return B_OK;
1178 }
1179 
1180 
1181 void
1182 OpenSoundNode::Connect(status_t error, const media_source& source,
1183 	const media_destination& destination, const media_format& format,
1184 	char* io_name)
1185 {
1186 	CALLED();
1187 
1188 	NodeOutput *channel = _FindOutput(source);
1189 
1190 	// is this our output?
1191 	if (channel == NULL) {
1192 		fprintf(stderr, "OpenSoundNode::Connect returning (cause : "
1193 			"B_MEDIA_BAD_SOURCE)\n");
1194 		return;
1195 	}
1196 
1197 	OpenSoundDeviceEngine *engine = channel->fRealEngine;
1198 	if (engine == NULL)
1199 		return;
1200 
1201 	// If something earlier failed, Connect() might still be called, but with
1202 	// a non-zero error code.  When that happens we simply unreserve the
1203 	// connection and do nothing else.
1204 	if (error) {
1205 		channel->fOutput.destination = media_destination::null;
1206 		channel->fOutput.format = channel->fPreferredFormat;
1207 		engine->Close();
1208 		channel->fRealEngine = NULL;
1209 		return;
1210 	}
1211 
1212 	// Okay, the connection has been confirmed.  Record the destination and
1213 	// format that we agreed on, and report our connection name again.
1214 	channel->fOutput.destination = destination;
1215 	channel->fOutput.format = format;
1216 	strncpy(io_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
1217 
1218 	// reset our buffer duration, etc. to avoid later calculations
1219 	bigtime_t duration = channel->fOutput.format.u.raw_audio.buffer_size
1220 		* 10000
1221 		/ ( (channel->fOutput.format.u.raw_audio.format
1222 				& media_raw_audio_format::B_AUDIO_SIZE_MASK)
1223 			* channel->fOutput.format.u.raw_audio.channel_count)
1224 		/ ((int32)(channel->fOutput.format.u.raw_audio.frame_rate / 100));
1225 
1226 	SetBufferDuration(duration);
1227 
1228 	// Now that we're connected, we can determine our downstream latency.
1229 	// Do so, then make sure we get our events early enough.
1230 	media_node_id id;
1231 	FindLatencyFor(channel->fOutput.destination, &fLatency, &id);
1232 	TRACE("\tdownstream latency = %Ld\n", fLatency);
1233 
1234 	fInternalLatency = BufferDuration();
1235 	TRACE("\tbuffer-filling took %Ld usec on this machine\n",
1236 		fInternalLatency);
1237 	//SetEventLatency(fLatency + fInternalLatency);
1238 
1239 	// Set up the buffer group for our connection, as long as nobody handed us
1240 	// a buffer group (via SetBufferGroup()) prior to this.  That can happen,
1241 	// for example, if the consumer calls SetOutputBuffersFor() on us from
1242 	// within its Connected() method.
1243 	if (channel->fBufferGroup == NULL)
1244 		channel->AllocateBuffers(BufferDuration(), fLatency);
1245 
1246 	engine->StartRecording();
1247 
1248 	// we are sure the thread is started
1249 	_StartRecThread(channel);
1250 }
1251 
1252 
1253 void
1254 OpenSoundNode::Disconnect(const media_source& what,
1255 	const media_destination& where)
1256 {
1257 	CALLED();
1258 
1259 	NodeOutput *channel = _FindOutput(what);
1260 
1261 	// is this our output?
1262 	if (channel == NULL) {
1263 		fprintf(stderr, "OpenSoundNode::Disconnect() returning (cause : "
1264 			"B_MEDIA_BAD_SOURCE)\n");
1265 		return;
1266 	}
1267 
1268 	_StopRecThread(channel);
1269 
1270 	BAutolock L(fDevice->Locker());
1271 
1272 	OpenSoundDeviceEngine* engine = channel->fRealEngine;
1273 
1274 	// Make sure that our connection is the one being disconnected
1275 	if (where == channel->fOutput.destination
1276 		&& what == channel->fOutput.source) {
1277 		if (engine)
1278 			engine->Close();
1279 		channel->fRealEngine = NULL;
1280 		channel->fOutput.destination = media_destination::null;
1281 		channel->fOutput.format = channel->fPreferredFormat;
1282 		channel->FreeBuffers();
1283 	} else {
1284 		fprintf(stderr, "\tDisconnect() called with wrong source/destination "
1285 			"(%ld/%ld), ours is (%ld/%ld)\n", what.id, where.id,
1286 			channel->fOutput.source.id, 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 %Ld\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: %li\n",event->type);
1400 			break;
1401 	}
1402 }
1403 
1404 
1405 // protected
1406 status_t
1407 OpenSoundNode::HandleBuffer(const media_timed_event* event,
1408 	bigtime_t lateness, bool realTimeEvent)
1409 {
1410 	CALLED();
1411 
1412 	// TODO: How should we handle late buffers? Drop them?
1413 	// Notify the producer?
1414 
1415 	BBuffer* buffer = const_cast<BBuffer*>((BBuffer*)event->pointer);
1416 	if (buffer == NULL) {
1417 		fprintf(stderr,"OpenSoundNode::HandleBuffer() - B_BAD_VALUE\n");
1418 		return B_BAD_VALUE;
1419 	}
1420 
1421 	NodeInput *channel = _FindInput(buffer->Header()->destination);
1422 //	TRACE("buffer->Header()->destination : %i\n",
1423 //		buffer->Header()->destination);
1424 
1425 	if (channel == NULL) {
1426 		buffer->Recycle();
1427 		fprintf(stderr,"OpenSoundNode::HandleBuffer() - "
1428 			"B_MEDIA_BAD_DESTINATION\n");
1429 		return B_MEDIA_BAD_DESTINATION;
1430 	}
1431 
1432 	media_header* hdr = buffer->Header();
1433 	bigtime_t now = TimeSource()->Now();
1434 	bigtime_t perf_time = hdr->start_time;
1435 
1436 	// the how_early calculated here doesn't include scheduling latency
1437 	// because we've already been scheduled to handle the buffer
1438 	bigtime_t how_early = perf_time - EventLatency() - now;
1439 
1440 	// if the buffer is late, we ignore it and report the fact to the producer
1441 	// who sent it to us
1442 	if (RunMode() != B_OFFLINE
1443 			// lateness doesn't matter in offline mode...
1444 		&& RunMode() != B_RECORDING
1445 			// ...or in recording mode
1446 		&& how_early < 0LL
1447 		&& false) {
1448 			// TODO: Debug
1449 		//mLateBuffers++;
1450 		NotifyLateProducer(channel->fInput.source, -how_early, perf_time);
1451 		fprintf(stderr,"	<- LATE BUFFER : %lli\n", how_early);
1452 		buffer->Recycle();
1453 	} else {
1454 		fDevice->Locker()->Lock();
1455 		if (channel->fBuffers.CountItems() > 10) {
1456 			fDevice->Locker()->Unlock();
1457 			TRACE("OpenSoundNode::HandleBuffer too many buffers, "
1458 				"recycling\n");
1459 			buffer->Recycle();
1460 		} else {
1461 //			TRACE("OpenSoundNode::HandleBuffer writing channelId : %i,
1462 //				how_early:%lli\n", channel->fEngineIndex, how_early);
1463 			if (!channel->fBuffers.AddItem(buffer))
1464 				buffer->Recycle();
1465 			fDevice->Locker()->Unlock();
1466 		}
1467 	}
1468 	return B_OK;
1469 }
1470 
1471 
1472 status_t
1473 OpenSoundNode::HandleDataStatus(const media_timed_event* event,
1474 	bigtime_t lateness, bool realTimeEvent)
1475 {
1476 //	CALLED();
1477 
1478 	// TODO: this is called mostly whenever the system mixer
1479 	// switches from not sending us buffers (no client connected)
1480 	// to sending buffers, and vice versa. In a Terminal, this
1481 	// can be nicely demonstrated by provoking a system beep while
1482 	// nothing else is using audio playback. Any first beep will
1483 	// start with a small glitch, while more beeps before the last
1484 	// one finished will not have the glitch. I am not sure, but
1485 	// I seem to remember that other audio nodes have the same
1486 	// problem, so it might not be a problem of this implementation.
1487 
1488 	BString message("OpenSoundNode::HandleDataStatus status: ");
1489 
1490 	switch(event->data) {
1491 		case B_DATA_NOT_AVAILABLE:
1492 			message << "No data";
1493 			break;
1494 		case B_DATA_AVAILABLE:
1495 			message << "Data";
1496 			break;
1497 		case B_PRODUCER_STOPPED:
1498 			message << "Stopped";
1499 			break;
1500 		default:
1501 			message << "???";
1502 			break;
1503 	}
1504 
1505 	message << ", lateness: " << lateness;
1506 	printf("%s\n", message.String());
1507 
1508 	return B_OK;
1509 }
1510 
1511 
1512 status_t
1513 OpenSoundNode::HandleStart(const media_timed_event* event, bigtime_t lateness,
1514 	bool realTimeEvent)
1515 {
1516 	CALLED();
1517 	if (RunState() != B_STARTED) {
1518 		// TODO: What should happen here?
1519 	}
1520 	return B_OK;
1521 }
1522 
1523 
1524 status_t
1525 OpenSoundNode::HandleSeek(const media_timed_event* event, bigtime_t lateness,
1526 	bool realTimeEvent)
1527 {
1528 	CALLED();
1529 	TRACE("OpenSoundNode::HandleSeek(t=%lld, d=%li, bd=%lld)\n",
1530 		event->event_time,event->data,event->bigdata);
1531 	return B_OK;
1532 }
1533 
1534 
1535 status_t
1536 OpenSoundNode::HandleWarp(const media_timed_event* event,
1537 	bigtime_t lateness, bool realTimeEvent)
1538 {
1539 	CALLED();
1540 	return B_OK;
1541 }
1542 
1543 
1544 status_t
1545 OpenSoundNode::HandleStop(const media_timed_event* event, bigtime_t lateness,
1546 	bool realTimeEvent)
1547 {
1548 	CALLED();
1549 	// flush the queue so downstreamers don't get any more
1550 	EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
1551 		BTimedEventQueue::B_HANDLE_BUFFER);
1552 
1553 	return B_OK;
1554 }
1555 
1556 
1557 status_t
1558 OpenSoundNode::HandleParameter(const media_timed_event* event,
1559 	bigtime_t lateness, bool realTimeEvent)
1560 {
1561 	CALLED();
1562 	return B_OK;
1563 }
1564 
1565 
1566 // #pragma mark - BTimeSource
1567 
1568 
1569 void
1570 OpenSoundNode::SetRunMode(run_mode mode)
1571 {
1572 	CALLED();
1573 	TRACE("OpenSoundNode::SetRunMode(%d)\n", mode);
1574 	//BTimeSource::SetRunMode(mode);
1575 }
1576 
1577 
1578 status_t
1579 OpenSoundNode::TimeSourceOp(const time_source_op_info& op, void* _reserved)
1580 {
1581 	CALLED();
1582 	switch(op.op) {
1583 		case B_TIMESOURCE_START:
1584 			TRACE("TimeSourceOp op B_TIMESOURCE_START\n");
1585 			if (RunState() != BMediaEventLooper::B_STARTED) {
1586 				fTimeSourceStarted = true;
1587 				fTimeSourceStartTime = RealTime();
1588 
1589 				media_timed_event startEvent(0, BTimedEventQueue::B_START);
1590 				EventQueue()->AddEvent(startEvent);
1591 			}
1592 			break;
1593 		case B_TIMESOURCE_STOP:
1594 			TRACE("TimeSourceOp op B_TIMESOURCE_STOP\n");
1595 			if (RunState() == BMediaEventLooper::B_STARTED) {
1596 				media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1597 				EventQueue()->AddEvent(stopEvent);
1598 				fTimeSourceStarted = false;
1599 				PublishTime(0, 0, 0);
1600 			}
1601 			break;
1602 		case B_TIMESOURCE_STOP_IMMEDIATELY:
1603 			TRACE("TimeSourceOp op B_TIMESOURCE_STOP_IMMEDIATELY\n");
1604 			if (RunState() == BMediaEventLooper::B_STARTED) {
1605 				media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1606 				EventQueue()->AddEvent(stopEvent);
1607 				fTimeSourceStarted = false;
1608 				PublishTime(0, 0, 0);
1609 			}
1610 			break;
1611 		case B_TIMESOURCE_SEEK:
1612 //			TRACE("TimeSourceOp op B_TIMESOURCE_SEEK\n");
1613 printf("TimeSourceOp op B_TIMESOURCE_SEEK, real %lld, "
1614 	"perf %lld\n", op.real_time, op.performance_time);
1615 			BroadcastTimeWarp(op.real_time, op.performance_time);
1616 			break;
1617 		default:
1618 			break;
1619 	}
1620 	return B_OK;
1621 }
1622 
1623 
1624 // #pragma mark - BControllable
1625 
1626 
1627 status_t
1628 OpenSoundNode::GetParameterValue(int32 id, bigtime_t* last_change, void* value,
1629 	size_t* ioSize)
1630 {
1631 	CALLED();
1632 
1633 	int channelCount = 1;
1634 	int sliderShift = 8;
1635 
1636 	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1637 	if (!mixer)
1638 		return ENODEV;
1639 
1640 	TRACE("id : %i, *ioSize=%d\n", id, *ioSize);
1641 
1642 	oss_mixext mixext;
1643 	status_t err = mixer->GetExtInfo(id, &mixext);
1644 	if (err < B_OK)
1645 		return err;
1646 
1647 	oss_mixer_value mixval;
1648 	mixval.ctrl = mixext.ctrl;
1649 	mixval.timestamp = mixext.timestamp;
1650 
1651 	err = mixer->GetMixerValue(&mixval);
1652 	if (err < B_OK)
1653 		return err;
1654 
1655 	if (!(mixext.flags & MIXF_READABLE))
1656 		return EINVAL;
1657 
1658 	BParameter *parameter = NULL;
1659 	for (int32 i = 0; i < fWeb->CountParameters(); i++) {
1660 		parameter = fWeb->ParameterAt(i);
1661 		if(parameter->ID() == id)
1662 			break;
1663 	}
1664 
1665 	if (!parameter)
1666 		return ENODEV;
1667 
1668 	TRACE("%s: value = 0x%08x\n", __FUNCTION__, mixval.value);
1669 
1670 	*last_change = system_time();//??
1671 
1672 	switch (mixext.type) {
1673 	case MIXT_DEVROOT:
1674 	case MIXT_GROUP:
1675 		break;
1676 	case MIXT_ONOFF:
1677 		if (*ioSize < sizeof(bool))
1678 			return EINVAL;
1679 		*(int32 *)value = mixval.value?true:false;
1680 		*ioSize = sizeof(bool);
1681 		return B_OK;
1682 	case MIXT_ENUM:
1683 		if (*ioSize < sizeof(int32))
1684 			return EINVAL;
1685 		*(int32 *)value = mixval.value;
1686 		*ioSize = sizeof(int32);
1687 		return B_OK;
1688 		break;
1689 	case MIXT_STEREODB:
1690 	case MIXT_STEREOSLIDER16:
1691 	case MIXT_STEREOSLIDER:
1692 		channelCount = 2;
1693 	case MIXT_SLIDER:
1694 	case MIXT_MONODB:
1695 	case MIXT_MONOSLIDER16:
1696 	case MIXT_MONOSLIDER:
1697 		if (*ioSize < channelCount * sizeof(float))
1698 			return EINVAL;
1699 		if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
1700 			return EINVAL;
1701 		if (mixext.type == MIXT_STEREOSLIDER16 ||
1702 			mixext.type == MIXT_MONOSLIDER16)
1703 			sliderShift = 16;
1704 		*ioSize = channelCount * sizeof(float);
1705 		((float *)value)[0] = (float)(mixval.value & ((1 << sliderShift) - 1));
1706 		TRACE("%s: value[O] = %f\n", __FUNCTION__, ((float *)value)[0]);
1707 		if (channelCount < 2)
1708 			return B_OK;
1709 		((float *)value)[1] = (float)((mixval.value >> sliderShift)
1710 			& ((1 << sliderShift) - 1));
1711 		TRACE("%s: value[1] = %f\n", __FUNCTION__, ((float *)value)[1]);
1712 		return B_OK;
1713 		break;
1714 	case MIXT_MESSAGE:
1715 		break;
1716 	case MIXT_MONOVU:
1717 		break;
1718 	case MIXT_STEREOVU:
1719 		break;
1720 	case MIXT_MONOPEAK:
1721 		break;
1722 	case MIXT_STEREOPEAK:
1723 		break;
1724 	case MIXT_RADIOGROUP:
1725 		break;//??
1726 	case MIXT_MARKER:
1727 		break;// separator item: ignore
1728 	case MIXT_VALUE:
1729 		break;
1730 	case MIXT_HEXVALUE:
1731 		break;
1732 /*	case MIXT_MONODB:
1733 		break;
1734 	case MIXT_STEREODB:
1735 		break;*/
1736 	case MIXT_3D:
1737 		break;
1738 /*	case MIXT_MONOSLIDER16:
1739 		break;
1740 	case MIXT_STEREOSLIDER16:
1741 		break;*/
1742 	default:
1743 		TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
1744 			__FUNCTION__, mixext.type);
1745 	}
1746 	*ioSize = 0;
1747 	return EINVAL;
1748 }
1749 
1750 
1751 void
1752 OpenSoundNode::SetParameterValue(int32 id, bigtime_t performance_time,
1753 	const void* value, size_t size)
1754 {
1755 	CALLED();
1756 
1757 	TRACE("id : %i, performance_time : %lld, size : %i\n", id,
1758 		performance_time, size);
1759 
1760 	OpenSoundDeviceMixer *mixer = fDevice->MixerAt(0);
1761 	if (mixer == NULL)
1762 		return;
1763 
1764 	oss_mixext mixext;
1765 	if (mixer->GetExtInfo(id, &mixext) < B_OK)
1766 		return;
1767 	if (!(mixext.flags & MIXF_WRITEABLE))
1768 		return;
1769 
1770 	oss_mixer_value mixval;
1771 	mixval.ctrl = mixext.ctrl;
1772 	mixval.timestamp = mixext.timestamp;
1773 
1774 	status_t err = mixer->GetMixerValue(&mixval);
1775 	if (err < B_OK)
1776 		return;
1777 
1778 	mixval.ctrl = mixext.ctrl;
1779 	mixval.timestamp = mixext.timestamp;
1780 
1781 	BParameter *parameter = NULL;
1782 	for(int32 i=0; i<fWeb->CountParameters(); i++) {
1783 		parameter = fWeb->ParameterAt(i);
1784 		if(parameter->ID() == id)
1785 			break;
1786 	}
1787 
1788 	if (!parameter)
1789 		return;
1790 
1791 	int channelCount = 1;
1792 	int sliderShift = 8;
1793 
1794 	switch (mixext.type) {
1795 		case MIXT_DEVROOT:
1796 		case MIXT_GROUP:
1797 			break;
1798 		case MIXT_ONOFF:
1799 			if (size < sizeof(bool))
1800 				return;
1801 			mixval.value = (int)*(int32 *)value;
1802 			mixer->SetMixerValue(&mixval);
1803 			// At least on my ATI IXP, recording selection can't be set to OFF,
1804 			// you have to set another one to ON to actually do it,
1805 			// and setting to ON changes others to OFF
1806 			// So we have to let users know about it.
1807 			// XXX: find something better, doesn't work correctly here.
1808 			// XXX: try a timed event ?
1809 			_PropagateParameterChanges(mixext.ctrl, mixext.type, mixext.id);
1810 
1811 			return;
1812 		case MIXT_ENUM:
1813 			if (size < sizeof(int32))
1814 				return;
1815 			mixval.value = (int)*(int32 *)value;
1816 			mixer->SetMixerValue(&mixval);
1817 			break;
1818 		case MIXT_STEREODB:
1819 		case MIXT_STEREOSLIDER16:
1820 		case MIXT_STEREOSLIDER:
1821 			channelCount = 2;
1822 		case MIXT_SLIDER:
1823 		case MIXT_MONODB:
1824 		case MIXT_MONOSLIDER16:
1825 		case MIXT_MONOSLIDER:
1826 			if (size < channelCount * sizeof(float))
1827 				return;
1828 			if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
1829 				return;
1830 			if (mixext.type == MIXT_STEREOSLIDER16 ||
1831 				mixext.type == MIXT_MONOSLIDER16)
1832 				sliderShift = 16;
1833 			mixval.value = 0;
1834 
1835 			TRACE("-------- sliderShift=%d, v = %08x, v & %08x = %08x\n",
1836 				sliderShift, mixval.value, ((1 << sliderShift) - 1),
1837 				mixval.value & ((1 << sliderShift) - 1));
1838 
1839 			mixval.value |= ((int)(((float *)value)[0]))
1840 				& ((1 << sliderShift) - 1);
1841 			if (channelCount > 1) {
1842 				mixval.value |= (((int)(((float *)value)[1]))
1843 					& ((1 << sliderShift) - 1)) << sliderShift;
1844 			}
1845 
1846 			TRACE("%s: value = 0x%08x\n", __FUNCTION__, mixval.value);
1847 			mixer->SetMixerValue(&mixval);
1848 			return;
1849 			break;
1850 		case MIXT_MESSAGE:
1851 			break;
1852 		case MIXT_MONOVU:
1853 			break;
1854 		case MIXT_STEREOVU:
1855 			break;
1856 		case MIXT_MONOPEAK:
1857 			break;
1858 		case MIXT_STEREOPEAK:
1859 			break;
1860 		case MIXT_RADIOGROUP:
1861 			break;//??
1862 		case MIXT_MARKER:
1863 			break;// separator item: ignore
1864 		case MIXT_VALUE:
1865 			break;
1866 		case MIXT_HEXVALUE:
1867 			break;
1868 //		case MIXT_MONODB:
1869 //			break;
1870 //		case MIXT_STEREODB:
1871 //			break;
1872 		case MIXT_3D:
1873 			break;
1874 //		case MIXT_MONOSLIDER16:
1875 //			break;
1876 //		case MIXT_STEREOSLIDER16:
1877 //			break;
1878 		default:
1879 			TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
1880 				__FUNCTION__, mixext.type);
1881 	}
1882 
1883 	return;
1884 }
1885 
1886 
1887 BParameterWeb*
1888 OpenSoundNode::MakeParameterWeb()
1889 {
1890 	CALLED();
1891 	BParameterWeb* web = new BParameterWeb;
1892 
1893 	// TODO: the card might change the mixer controls at some point,
1894 	// we should detect it (poll) and recreate the parameter web and
1895 	// re-set it.
1896 
1897 	// TODO: cache mixext[...] and poll for changes in their update_counter.
1898 
1899 	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1900 	if (mixer == NULL) {
1901 		// some cards don't have a mixer, just put a placeholder then
1902 		BParameterGroup* child = web->MakeGroup("No mixer");
1903 		child->MakeNullParameter(1, B_MEDIA_UNKNOWN_TYPE, "No Mixer",
1904 			B_GENERIC);
1905 		return web;
1906 	}
1907 
1908 	int mixext_count = mixer->CountExtInfos();
1909 	TRACE("OpenSoundNode::MakeParameterWeb %i ExtInfos\n", mixext_count);
1910 
1911 	for (int32 i = 0; i < mixext_count; i++) {
1912 		oss_mixext mixext;
1913 		if (mixer->GetExtInfo(i, &mixext) < B_OK)
1914 			continue;
1915 
1916 		if (mixext.type == MIXT_DEVROOT) {
1917 			oss_mixext_root* extroot = (oss_mixext_root*)mixext.data;
1918 			TRACE("OpenSoundNode: mixext[%d]: ROOT\n", i);
1919 			int32 nb = 0;
1920 			const char* childName = mixext.extname;
1921 			childName = extroot->id; // extroot->name;
1922 			BParameterGroup *child = web->MakeGroup(childName);
1923 			_ProcessGroup(child, i, nb);
1924 		}
1925 	}
1926 
1927 	return web;
1928 }
1929 
1930 
1931 // #pragma mark - OpenSoundNode specific
1932 
1933 
1934 void
1935 OpenSoundNode::_ProcessGroup(BParameterGroup *group, int32 index,
1936 	int32& nbParameters)
1937 {
1938 	CALLED();
1939 	// TODO: It looks wrong to use the same mixer in a recursive function!
1940 	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1941 
1942 	int mixext_count = mixer->CountExtInfos();
1943 	for (int32 i = 0; i < mixext_count; i++) {
1944 		oss_mixext mixext;
1945 		if (mixer->GetExtInfo(i, &mixext) < B_OK)
1946 			continue;
1947 		// only keep direct children of that group
1948 		if (mixext.parent != index)
1949 			continue;
1950 
1951 		int32 nb = 1;
1952 
1953 		TRACE("OpenSoundNode: mixext[%d]: { %s/%s, type=%d, parent=%d, "
1954 			"min=%d, max=%d, flags=0x%08x, control_no=%d, desc=%d, "
1955 			"update_counter=%d }\n", i,
1956 			(mixext.type != MIXT_MARKER) ? mixext.id : "",
1957 			(mixext.type != MIXT_MARKER) ? mixext.extname : "",
1958 			mixext.type, mixext.parent,
1959 			mixext.minvalue, mixext.maxvalue,
1960 			mixext.flags, mixext.control_no,
1961 			mixext.desc, mixext.update_counter);
1962 
1963 		// should actually rename the whole group but it's too late there.
1964 		const char *childName = mixext.extname;
1965 		if (mixext.flags & MIXF_MAINVOL)
1966 			childName = "Master Gain";
1967 
1968 		const char *sliderUnit = "";//"(linear)";
1969 		if (mixext.flags & MIXF_HZ)
1970 			sliderUnit = "Hz";
1971 
1972 		const char *continuousKind = B_GAIN;
1973 		BParameterGroup* child;
1974 
1975 		switch (mixext.type) {
1976 		case MIXT_DEVROOT:
1977 			// root item, should be done already
1978 			break;
1979 		case MIXT_GROUP:
1980 			TRACE("OpenSoundNode: mixext[%d]: GROUP\n", i);
1981 			child = group->MakeGroup(childName);
1982 			child->MakeNullParameter(i, B_MEDIA_RAW_AUDIO, childName,
1983 				B_WEB_BUFFER_OUTPUT);
1984 			_ProcessGroup(child, i, nb);
1985 			break;
1986 		case MIXT_ONOFF:
1987 			TRACE("OpenSoundNode: mixext[%d]: ONOFF\n", i);
1988 			// multiaudio node adds 100 to IDs !?
1989 			if (0/*MMC[i].string == S_MUTE*/) {
1990 				group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
1991 					B_MUTE);
1992 			} else {
1993 				group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
1994 					B_ENABLE);
1995 			}
1996 			if (nbParameters > 0) {
1997 				(group->ParameterAt(nbParameters - 1))->AddOutput(
1998 					group->ParameterAt(nbParameters));
1999 				nbParameters++;
2000 			}
2001 			break;
2002 		case MIXT_ENUM:
2003 		{
2004 			TRACE("OpenSoundNode: mixext[%d]: ENUM\n", i);
2005 			BDiscreteParameter *parameter =
2006 				group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
2007 					B_INPUT_MUX);
2008 			if (nbParameters > 0) {
2009 				(group->ParameterAt(nbParameters - 1))->AddOutput(
2010 					group->ParameterAt(nbParameters));
2011 				nbParameters++;
2012 			}
2013 			_ProcessMux(parameter, i);
2014 			break;
2015 		}
2016 		case MIXT_MONODB:
2017 		case MIXT_STEREODB:
2018 			sliderUnit = "dB";
2019 		case MIXT_SLIDER:
2020 		case MIXT_MONOSLIDER16:
2021 		case MIXT_STEREOSLIDER16:
2022 		case MIXT_MONOSLIDER:
2023 			//TRACE("OpenSoundNode: mixext[%d]: MONOSLIDER\n", i);
2024 			//break;
2025 			// fall through
2026 		case MIXT_STEREOSLIDER:
2027 			TRACE("OpenSoundNode: mixext[%d]: [MONO|STEREO]SLIDER\n", i);
2028 
2029 			if (mixext.flags & MIXF_MAINVOL)
2030 				continuousKind = B_MASTER_GAIN;
2031 
2032 // TODO: find out what this was supposed to do:
2033 //			if (mixext.flags & MIXF_CENTIBEL)
2034 //				true;//step size
2035 //			if (mixext.flags & MIXF_DECIBEL)
2036 //				true;//step size
2037 
2038 			group->MakeContinuousParameter(i, B_MEDIA_RAW_AUDIO, childName,
2039 				continuousKind,  sliderUnit, mixext.minvalue, mixext.maxvalue,
2040 				/*TODO: should be "granularity"*/1);
2041 
2042 			if (mixext.type == MIXT_STEREOSLIDER ||
2043 				mixext.type == MIXT_STEREOSLIDER16 ||
2044 				mixext.type == MIXT_STEREODB)
2045 				group->ParameterAt(nbParameters)->SetChannelCount(2);
2046 
2047 			TRACE("nb parameters : %d\n", nbParameters);
2048 			if (nbParameters > 0) {
2049 				(group->ParameterAt(nbParameters - 1))->AddOutput(
2050 					group->ParameterAt(nbParameters));
2051 				nbParameters++;
2052 			}
2053 
2054 			break;
2055 		case MIXT_MESSAGE:
2056 			break;
2057 		case MIXT_MONOVU:
2058 			break;
2059 		case MIXT_STEREOVU:
2060 			break;
2061 		case MIXT_MONOPEAK:
2062 			break;
2063 		case MIXT_STEREOPEAK:
2064 			break;
2065 		case MIXT_RADIOGROUP:
2066 			break;//??
2067 		case MIXT_MARKER:
2068 			break;// separator item: ignore
2069 		case MIXT_VALUE:
2070 			break;
2071 		case MIXT_HEXVALUE:
2072 			break;
2073 //		case MIXT_MONODB:
2074 //			TRACE("OpenSoundNode::_ProcessGroup: Skipping obsolete "
2075 //				"MIXT_MONODB\n");
2076 //			break;
2077 //		case MIXT_STEREODB:
2078 //			TRACE("OpenSoundNode::_ProcessGroup: Skipping obsolete "
2079 //				"MIXT_STEREODB\n");
2080 //			break;
2081 //		case MIXT_SLIDER:
2082 //			break;
2083 		case MIXT_3D:
2084 			break;
2085 //		case MIXT_MONOSLIDER16:
2086 //			break;
2087 //		case MIXT_STEREOSLIDER16:
2088 //			break;
2089 		default:
2090 			TRACE("OpenSoundNode::_ProcessGroup: unknown mixer control "
2091 				"type %d\n", mixext.type);
2092 		}
2093 	}
2094 
2095 }
2096 
2097 
2098 void
2099 OpenSoundNode::_ProcessMux(BDiscreteParameter* parameter, int32 index)
2100 {
2101 	CALLED();
2102 	OpenSoundDeviceMixer *mixer = fDevice->MixerAt(0);
2103 	oss_mixer_enuminfo enuminfo;
2104 	status_t err = mixer->GetEnumInfo(index, &enuminfo);
2105 	if (err < B_OK) {
2106 		// maybe there is no list.
2107 		// generate a count form 0
2108 		oss_mixext mixext;
2109 		if (mixer->GetExtInfo(index, &mixext) < B_OK)
2110 			return;
2111 
2112 		for (int32 i = 0; i < mixext.maxvalue; i++) {
2113 			BString s;
2114 			s << i;
2115 			parameter->AddItem(i, s.String());
2116 		}
2117 		return;
2118 	}
2119 
2120 	for (int32 i = 0; i < enuminfo.nvalues; i++) {
2121 		parameter->AddItem(i, &enuminfo.strings[enuminfo.strindex[i]]);
2122 	}
2123 	return;
2124 }
2125 
2126 
2127 status_t
2128 OpenSoundNode::_PropagateParameterChanges(int from, int type, const char* id)
2129 {
2130 	CALLED();
2131 
2132 	TRACE("OpenSoundNode::_PropagateParameterChanges(from %i, type %i, "
2133 		"id %s)\n", from, type, id);
2134 
2135 	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
2136 	if (mixer == NULL)
2137 		return ENODEV;
2138 
2139 // TODO: Cortex doesn't like that!
2140 // try timed event
2141 // try checking update_counter+caching
2142 return B_OK;
2143 
2144 //	char oldValues[128];
2145 	char newValues[128];
2146 //	size_t oldValuesSize;
2147 	size_t newValuesSize;
2148 
2149 	for (int i = 0; i < mixer->CountExtInfos(); i++) {
2150 		oss_mixext mixext;
2151 		status_t err = mixer->GetExtInfo(i, &mixext);
2152 		if (err < B_OK)
2153 			continue;
2154 
2155 		// skip the caller
2156 		//if (mixext.ctrl == from)
2157 		//	continue;
2158 
2159 		if (!(mixext.flags & MIXF_READABLE))
2160 			continue;
2161 
2162 		// match type ?
2163 		if (type > -1 && mixext.type != type)
2164 			continue;
2165 
2166 		// match internal ID string
2167 		if (id && strncmp(mixext.id, id, 16))
2168 			continue;
2169 
2170 //		BParameter *parameter = NULL;
2171 //		for(int32 i=0; i<fWeb->CountParameters(); i++) {
2172 //			parameter = fWeb->ParameterAt(i);
2173 //			if(parameter->ID() == mixext.ctrl)
2174 //				break;
2175 //		}
2176 //
2177 //		if (!parameter)
2178 //			continue;
2179 
2180 //		oldValuesSize = 128;
2181 		newValuesSize = 128;
2182 		bigtime_t last;
2183 //		TRACE("OpenSoundNode::%s: comparing mixer control %d\n",
2184 //			__FUNCTION__, mixext.ctrl);
2185 //		if (parameter->GetValue(oldValues, &oldValuesSize, &last) < B_OK)
2186 //			continue;
2187 		if (GetParameterValue(mixext.ctrl, &last, newValues,
2188 				&newValuesSize) < B_OK) {
2189 			continue;
2190 		}
2191 //		if (oldValuesSize != newValuesSize || memcmp(oldValues, newValues,
2192 //				MIN(oldValuesSize, newValuesSize))) {
2193 			TRACE("OpenSoundNode::%s: updating mixer control %d\n",
2194 				__FUNCTION__, mixext.ctrl);
2195 			BroadcastNewParameterValue(last, mixext.ctrl, newValues,
2196 				newValuesSize);
2197 //			BroadcastChangedParameter(mixext.ctrl);
2198 //		}
2199 	}
2200 	return B_OK;
2201 }
2202 
2203 
2204 int32
2205 OpenSoundNode::_PlayThread(NodeInput* input)
2206 {
2207 	CALLED();
2208 	//set_thread_priority(find_thread(NULL), 5);// TODO:DEBUG
2209 	signal(SIGUSR1, &_SignalHandler);
2210 
2211 	OpenSoundDeviceEngine* engine = input->fRealEngine;
2212 	if (!engine || !engine->InUse())
2213 		return B_NO_INIT;
2214 	// skip unconnected or non-busy engines
2215 	if (input->fInput.source == media_source::null
2216 		&& input->fEngineIndex == 0)
2217 		return B_NO_INIT;
2218 	// must be open for write
2219 	ASSERT(engine->OpenMode() & OPEN_WRITE);
2220 
2221 	// make writing actually block until the previous buffer played
2222 	size_t driverBufferSize = engine->DriverBufferSize();
2223 	size_t bufferSize = input->fInput.format.u.raw_audio.buffer_size;
2224 	if (driverBufferSize != bufferSize) {
2225 		printf("warning, OSS driver buffer size: %ld, audio buffer "
2226 			"size: %ld", driverBufferSize, bufferSize);
2227 	}
2228 
2229 	// cache a silence buffer
2230 	uint8 silenceBuffer[bufferSize];
2231 	uint8 formatSilence = 0;
2232 	if (input->fInput.format.u.raw_audio.format
2233 			== media_raw_audio_format::B_AUDIO_UCHAR)
2234 		formatSilence = 128;
2235 
2236 	memset(silenceBuffer, formatSilence, bufferSize);
2237 
2238 	// start by writing the OSS driver buffer size of silence
2239 	// so that the first call to write() already blocks for (almost) the
2240 	// buffer duration
2241 	input->Write(silenceBuffer, bufferSize);
2242 
2243 	int64 bytesWritten = 0;
2244 	bigtime_t lastRealTime = RealTime();
2245 	bigtime_t lastPerformanceTime = 0;
2246 
2247 	const int32 driftValueCount = 64;
2248 	int32 currentDriftValueIndex = 0;
2249 	float driftValues[driftValueCount];
2250 	for (int32 i = 0; i < driftValueCount; i++)
2251 		driftValues[i] = 1.0;
2252 
2253 	do {
2254 		if (!fDevice->Locker()->Lock())
2255 			break;
2256 
2257 		TRACE("OpenSoundNode::_PlayThread: buffers: %ld\n",
2258 			input->fBuffers.CountItems());
2259 
2260 		BBuffer* buffer = (BBuffer*)input->fBuffers.RemoveItem(0L);
2261 
2262 		fDevice->Locker()->Unlock();
2263 
2264 		if (input->fThread < 0) {
2265 			if (buffer)
2266 				buffer->Recycle();
2267 			break;
2268 		}
2269 
2270 //input->WriteTestTone();
2271 //if (buffer)
2272 //	buffer->Recycle();
2273 //continue;
2274 
2275 		int32 additionalBytesWritten = 0;
2276 		if (buffer != NULL) {
2277 			if (input->Write(buffer->Data(), buffer->SizeUsed()) == B_OK)
2278 				additionalBytesWritten = buffer->SizeUsed();
2279 			buffer->Recycle();
2280 		} else {
2281 			input->Write(silenceBuffer, bufferSize);
2282 			additionalBytesWritten = bufferSize;
2283 		}
2284 
2285 		// TODO: do not assume channel 0 will always be running!
2286 		// update the timesource
2287 		if (input->fEngineIndex == 0 && input->fThread >= 0) {
2288 
2289 			bigtime_t realTime = RealTime();
2290 			bigtime_t realPlaybackDuration = realTime - lastRealTime;
2291 			bigtime_t performanceTime
2292 				= time_for_buffer(bytesWritten, input->fInput.format);
2293 			float drift = (double)(performanceTime
2294 				- lastPerformanceTime) / realPlaybackDuration;
2295 
2296 			lastPerformanceTime = performanceTime;
2297 			lastRealTime = realTime;
2298 
2299 			driftValues[currentDriftValueIndex++] = drift;
2300 			if (currentDriftValueIndex == driftValueCount)
2301 				currentDriftValueIndex = 0;
2302 			drift = 0.0;
2303 			for (int32 i = 0; i < driftValueCount; i++)
2304 				drift += driftValues[i];
2305 			drift /= driftValueCount;
2306 
2307 			if (fDevice->Locker()->Lock()) {
2308 				if (input->fThread >= 0)
2309 					_UpdateTimeSource(performanceTime, realTime, drift);
2310 				fDevice->Locker()->Unlock();
2311 			}
2312 		}
2313 		bytesWritten += additionalBytesWritten;
2314 
2315 	} while (input->fThread > -1);
2316 
2317 	return 0;
2318 }
2319 
2320 
2321 int32
2322 OpenSoundNode::_RecThread(NodeOutput* output)
2323 {
2324 	CALLED();
2325 
2326 	//set_thread_priority(find_thread(NULL), 5);// TODO:DEBUG
2327 	signal(SIGUSR1, &_SignalHandler);
2328 
2329 	OpenSoundDeviceEngine *engine = output->fRealEngine;
2330 	if (!engine || !engine->InUse())
2331 		return B_NO_INIT;
2332 	// make sure we're both started *and* connected before delivering a buffer
2333 	if ((RunState() != BMediaEventLooper::B_STARTED)
2334 		|| (output->fOutput.destination == media_destination::null)) {
2335 		return B_NO_INIT;
2336 	}
2337 
2338 	// must be open for read
2339 	ASSERT(engine->OpenMode() & OPEN_READ);
2340 
2341 #ifdef ENABLE_REC
2342 
2343 	fDevice->Locker()->Lock();
2344 	do {
2345 		audio_buf_info abinfo;
2346 //		size_t avail = engine->GetISpace(&abinfo);
2347 //		TRACE("OpenSoundNode::_RunThread: I avail: %d\n", avail);
2348 //
2349 //		// skip if less than 1 buffer
2350 //		if (avail < output->fOutput.format.u.raw_audio.buffer_size)
2351 //			continue;
2352 
2353 		fDevice->Locker()->Unlock();
2354 		// Get the next buffer of data
2355 		BBuffer* buffer = _FillNextBuffer(&abinfo, *output);
2356 		fDevice->Locker()->Lock();
2357 
2358 		if (buffer) {
2359 			// send the buffer downstream if and only if output is enabled
2360 			status_t err = B_ERROR;
2361 			if (output->fOutputEnabled) {
2362 				err = SendBuffer(buffer, output->fOutput.source,
2363 					output->fOutput.destination);
2364 			}
2365 //			TRACE("OpenSoundNode::_RunThread: I avail: %d, OE %d, %s\n",
2366 //				avail, output->fOutputEnabled, strerror(err));
2367 			if (err != B_OK) {
2368 				buffer->Recycle();
2369 			} else {
2370 				// track how much media we've delivered so far
2371 				size_t nSamples = buffer->SizeUsed()
2372 					/ (output->fOutput.format.u.raw_audio.format
2373 						& media_raw_audio_format::B_AUDIO_SIZE_MASK);
2374 				output->fSamplesSent += nSamples;
2375 //				TRACE("OpenSoundNode::%s: sent %d samples\n",
2376 //					__FUNCTION__, nSamples);
2377 			}
2378 
2379 		}
2380 	} while (output->fThread > -1);
2381 	fDevice->Locker()->Unlock();
2382 
2383 #endif
2384 	return 0;
2385 }
2386 
2387 
2388 status_t
2389 OpenSoundNode::_StartPlayThread(NodeInput* input)
2390 {
2391 	CALLED();
2392 	BAutolock L(fDevice->Locker());
2393 	// the thread is already started ?
2394 	if (input->fThread > B_OK)
2395 		return B_OK;
2396 
2397 	//allocate buffer free semaphore
2398 //	int bufferCount = MAX(fDevice->fFragments.fragstotal, 2); // XXX
2399 
2400 //	fBufferAvailableSem = create_sem(fDevice->MBL.return_playback_buffers - 1,
2401 //		"multi_audio out buffer free");
2402 //	fBufferAvailableSem = create_sem(bufferCount - 1,
2403 //		"OpenSound out buffer free");
2404 
2405 //	if (fBufferAvailableSem < B_OK)
2406 //		return B_ERROR;
2407 
2408 	input->fThread = spawn_thread(_PlayThreadEntry,
2409 		"OpenSound audio output", B_REAL_TIME_PRIORITY, input);
2410 
2411 	if (input->fThread < B_OK) {
2412 //		delete_sem(fBufferAvailableSem);
2413 		return B_ERROR;
2414 	}
2415 
2416 	resume_thread(input->fThread);
2417 	return B_OK;
2418 }
2419 
2420 
2421 status_t
2422 OpenSoundNode::_StopPlayThread(NodeInput* input)
2423 {
2424 	if (input->fThread < 0)
2425 		return B_OK;
2426 
2427 	CALLED();
2428 
2429 	thread_id th;
2430 	{
2431 		BAutolock L(fDevice->Locker());
2432 		th = input->fThread;
2433 		input->fThread = -1;
2434 		//kill(th, SIGUSR1);
2435 	}
2436 	status_t ret;
2437 	wait_for_thread(th, &ret);
2438 
2439 	return B_OK;
2440 }
2441 
2442 
2443 status_t
2444 OpenSoundNode::_StartRecThread(NodeOutput* output)
2445 {
2446 	CALLED();
2447 	// the thread is already started ?
2448 	if (output->fThread > B_OK)
2449 		return B_OK;
2450 
2451 	//allocate buffer free semaphore
2452 //	int bufferCount = MAX(fDevice->fFragments.fragstotal, 2); // XXX
2453 
2454 //	fBufferAvailableSem = create_sem(fDevice->MBL.return_playback_buffers - 1,
2455 //		"multi_audio out buffer free");
2456 //	fBufferAvailableSem = create_sem(bufferCount - 1,
2457 //		"OpenSound out buffer free");
2458 
2459 //	if (fBufferAvailableSem < B_OK)
2460 //		return B_ERROR;
2461 
2462 	output->fThread = spawn_thread(_RecThreadEntry, "OpenSound audio input",
2463 		B_REAL_TIME_PRIORITY, output);
2464 
2465 	if (output->fThread < B_OK) {
2466 		//delete_sem(fBufferAvailableSem);
2467 		return B_ERROR;
2468 	}
2469 
2470 	resume_thread(output->fThread);
2471 	return B_OK;
2472 }
2473 
2474 
2475 status_t
2476 OpenSoundNode::_StopRecThread(NodeOutput* output)
2477 {
2478 	if (output->fThread < 0)
2479 		return B_OK;
2480 
2481 	CALLED();
2482 
2483 	thread_id th = output->fThread;
2484 	output->fThread = -1;
2485 	{
2486 		BAutolock L(fDevice->Locker());
2487 		//kill(th, SIGUSR1);
2488 	}
2489 	status_t ret;
2490 	wait_for_thread(th, &ret);
2491 
2492 	return B_OK;
2493 }
2494 
2495 
2496 void
2497 OpenSoundNode::_UpdateTimeSource(bigtime_t performanceTime,
2498 	bigtime_t realTime, float drift)
2499 {
2500 //	CALLED();
2501 
2502 	if (!fTimeSourceStarted)
2503 		return;
2504 
2505 	PublishTime(performanceTime, realTime, drift);
2506 
2507 //	TRACE("_UpdateTimeSource() perfTime : %lli, realTime : %lli, "
2508 //		"drift : %f\n", perfTime, realTime, drift);
2509 }
2510 
2511 
2512 BBuffer*
2513 OpenSoundNode::_FillNextBuffer(audio_buf_info* abinfo, NodeOutput& channel)
2514 {
2515 	CALLED();
2516 
2517 	BBuffer* buffer = channel.FillNextBuffer(BufferDuration());
2518 	if (!buffer)
2519 		return NULL;
2520 
2521 	if (fDevice == NULL)
2522 		fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - fDevice NULL\n");
2523 	if (buffer->Header() == NULL) {
2524 		fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - "
2525 			"buffer->Header() NULL\n");
2526 	}
2527 	if (TimeSource() == NULL) {
2528 		fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - "
2529 			"TimeSource() NULL\n");
2530 	}
2531 
2532 	// fill in the buffer header
2533 	media_header* hdr = buffer->Header();
2534 	if (hdr != NULL) {
2535 		hdr->time_source = TimeSource()->ID();
2536 		// TODO: should be system_time() - latency_as_per(abinfo)
2537 		hdr->start_time = PerformanceTimeFor(system_time());
2538 	}
2539 
2540 	return buffer;
2541 }
2542 
2543 
2544 status_t
2545 OpenSoundNode::GetConfigurationFor(BMessage* into_message)
2546 {
2547 	CALLED();
2548 
2549 	if (!into_message)
2550 		return B_BAD_VALUE;
2551 
2552 	size_t size = 128;
2553 	void* buffer = malloc(size);
2554 
2555 	for (int32 i = 0; i < fWeb->CountParameters(); i++) {
2556 		BParameter* parameter = fWeb->ParameterAt(i);
2557 		if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER
2558 			&& parameter->Type() != BParameter::B_DISCRETE_PARAMETER)
2559 			continue;
2560 
2561 		TRACE("getting parameter %i\n", parameter->ID());
2562 		size = 128;
2563 		bigtime_t last_change;
2564 		status_t err;
2565 		while ((err = GetParameterValue(parameter->ID(), &last_change, buffer,
2566 				&size)) == B_NO_MEMORY) {
2567 			size += 128;
2568 			free(buffer);
2569 			buffer = malloc(size);
2570 		}
2571 
2572 		if (err == B_OK && size > 0) {
2573 			into_message->AddInt32("parameterID", parameter->ID());
2574 			into_message->AddData("parameterData", B_RAW_TYPE, buffer, size,
2575 				false);
2576 		} else {
2577 			TRACE("parameter err : %s\n", strerror(err));
2578 		}
2579 	}
2580 
2581 	free(buffer);
2582 
2583 	PRINT_OBJECT(*into_message);
2584 
2585 	return B_OK;
2586 }
2587 
2588 
2589 OpenSoundNode::NodeOutput*
2590 OpenSoundNode::_FindOutput(const media_source& source) const
2591 {
2592 	int32 count = fOutputs.CountItems();
2593 	for (int32 i = 0; i < count; i++) {
2594 		NodeOutput* channel = (NodeOutput*)fOutputs.ItemAtFast(i);
2595 		if (source == channel->fOutput.source)
2596 			return channel;
2597 	}
2598 	return NULL;
2599 }
2600 
2601 
2602 OpenSoundNode::NodeInput*
2603 OpenSoundNode::_FindInput(const media_destination& dest) const
2604 {
2605 	int32 count = fInputs.CountItems();
2606 	for (int32 i = 0; i < count; i++) {
2607 		NodeInput* channel = (NodeInput*)fInputs.ItemAtFast(i);
2608 		if (dest == channel->fInput.destination)
2609 			return channel;
2610 	}
2611 	return NULL;
2612 }
2613 
2614 
2615 OpenSoundNode::NodeInput*
2616 OpenSoundNode::_FindInput(int32 destinationId)
2617 {
2618 	int32 count = fInputs.CountItems();
2619 	for (int32 i = 0; i < count; i++) {
2620 		NodeInput* channel = (NodeInput*)fInputs.ItemAtFast(i);
2621 		if (destinationId == channel->fInput.destination.id)
2622 			return channel;
2623 	}
2624 	return NULL;
2625 }
2626 
2627 
2628 // pragma mark - static
2629 
2630 
2631 void
2632 OpenSoundNode::_SignalHandler(int sig)
2633 {
2634 	// TODO: what was this intended for, just stopping the threads?
2635 	// (see _StopThreadXXX(), there is a kill call commented out there)
2636 }
2637 
2638 
2639 int32
2640 OpenSoundNode::_PlayThreadEntry(void* data)
2641 {
2642 	CALLED();
2643 	NodeInput* channel = static_cast<NodeInput*>(data);
2644 	return channel->fNode->_PlayThread(channel);
2645 }
2646 
2647 
2648 int32
2649 OpenSoundNode::_RecThreadEntry(void* data)
2650 {
2651 	CALLED();
2652 	NodeOutput* channel = static_cast<NodeOutput*>(data);
2653 	return channel->fNode->_RecThread(channel);
2654 }
2655 
2656 
2657 void
2658 OpenSoundNode::GetFlavor(flavor_info* outInfo, int32 id)
2659 {
2660 	CALLED();
2661 	if (outInfo == NULL)
2662 		return;
2663 
2664 	outInfo->flavor_flags = 0;
2665 	outInfo->possible_count = 1;
2666 		// one flavor at a time
2667 	outInfo->in_format_count = 0;
2668 		// no inputs
2669 	outInfo->in_formats = 0;
2670 	outInfo->out_format_count = 0;
2671 		// no outputs
2672 	outInfo->out_formats = 0;
2673 	outInfo->internal_id = id;
2674 
2675 	outInfo->name = (char *)"OpenSoundNode Node";
2676 	outInfo->info = (char *)"The OpenSoundNode outputs to OpenSound System v4 "
2677 		"drivers.";
2678 	outInfo->kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER | B_TIME_SOURCE
2679 		| B_PHYSICAL_OUTPUT | B_PHYSICAL_INPUT | B_CONTROLLABLE;
2680 	// TODO: If the OSS engine supports outputing encoded audio,
2681 	// we would need to setup a B_MEDIA_ENCODED_AUDIO format here
2682 	outInfo->in_format_count = 1;
2683 		// 1 input
2684 	media_format * informats = new media_format[outInfo->in_format_count];
2685 	GetFormat(&informats[0]);
2686 	outInfo->in_formats = informats;
2687 
2688 	outInfo->out_format_count = 1;
2689 		// 1 output
2690 	media_format * outformats = new media_format[outInfo->out_format_count];
2691 	GetFormat(&outformats[0]);
2692 	outInfo->out_formats = outformats;
2693 }
2694 
2695 
2696 void
2697 OpenSoundNode::GetFormat(media_format* outFormat)
2698 {
2699 	CALLED();
2700 	if (outFormat == NULL)
2701 		return;
2702 
2703 	outFormat->type = B_MEDIA_RAW_AUDIO;
2704 	outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
2705 	outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
2706 	outFormat->u.raw_audio = media_raw_audio_format::wildcard;
2707 }
2708