xref: /haiku/src/add-ons/media/media-add-ons/opensound/OpenSoundNode.cpp (revision 579f1dbca962a2a03df54f69fdc6e9423f91f20e)
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 	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 %" B_PRId32 " (%s)", prefix,
492 				mediaInput.destination.id, gSupportedFormatsNames[f]);
493 
494 			NodeInput* input = new (nothrow) NodeInput(mediaInput, i, fmt,
495 				this);
496 			if (input == NULL || !fInputs.AddItem(input)) {
497 				delete input;
498 				continue;
499 			}
500 		}
501 	}
502 
503 	for (int32 i = 0; i < fDevice->CountEngines(); i++) {
504 		OpenSoundDeviceEngine* engine = fDevice->EngineAt(i);
505 		if (engine == NULL)
506 			continue;
507 		// skip shadow engines
508 		if (engine->Caps() & PCM_CAP_SHADOW)
509 			continue;
510 		// skip engines that don't have inputs
511 		if ((engine->Caps() & PCM_CAP_INPUT) == 0)
512 			continue;
513 
514 		TRACE("NodeRegistered: engine[%d]: .caps=0x%08x, .iformats=0x%08x\n",
515 			i, engine->Caps(), engine->Info()->iformats);
516 
517 		for (int32 f = 0; gSupportedFormats[f]; f++) {
518 			int fmt = gSupportedFormats[f] & engine->Info()->iformats;
519 			if (fmt == 0)
520 				continue;
521 			TRACE("NodeRegistered() : creating an output for engine %i, "
522 				"format[%i]\n", i, f);
523 
524 			media_format preferredFormat;
525 			status_t err = engine->PreferredFormatFor(fmt, preferredFormat);
526 			if (err < B_OK)
527 				continue;
528 
529 			media_output mediaOutput;
530 
531 			mediaOutput.format = preferredFormat;
532 			mediaOutput.destination = media_destination::null;
533 			mediaOutput.source.port = ControlPort();
534 			mediaOutput.source.id = fOutputs.CountItems();
535 			mediaOutput.node = Node();
536 			const char *prefix = "";
537 			if (strstr(engine->Info()->name, "SPDIF"))
538 				prefix = "S/PDIF ";
539 			sprintf(mediaOutput.name, "%sInput %" B_PRId32 " (%s)", prefix,
540 				mediaOutput.source.id, gSupportedFormatsNames[f]);
541 
542 			NodeOutput* output = new (nothrow) NodeOutput(mediaOutput,
543 				preferredFormat);
544 			if (output == NULL || !fOutputs.AddItem(output)) {
545 				delete output;
546 				continue;
547 			}
548 //			output->fPreferredFormat.u.raw_audio.channel_count
549 //				= engine->Info()->max_channels;
550 			output->fOutput.format = output->fPreferredFormat;
551 			output->fEngineIndex = i;
552 			output->fOSSFormatFlags = fmt;
553 			output->fNode = this;
554 		}
555 	}
556 
557 	// set up our parameter web
558 	fWeb = MakeParameterWeb();
559 	SetParameterWeb(fWeb);
560 
561 	// apply configuration
562 #ifdef TRACE_OSS_NODE
563 	bigtime_t start = system_time();
564 #endif
565 
566 	int32 index = 0;
567 	int32 parameterID = 0;
568 	while (fConfig.FindInt32("parameterID", index, &parameterID) == B_OK) {
569 		const void* data;
570 		ssize_t size;
571 		if (fConfig.FindData("parameterData", B_RAW_TYPE, index, &data,
572 			&size) == B_OK) {
573 			SetParameterValue(parameterID, TimeSource()->Now(), data, size);
574 		}
575 		index++;
576 	}
577 
578 	TRACE("apply configuration in : %lldµs\n", system_time() - start);
579 }
580 
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 			"(%" B_PRId32 "/%" B_PRId32 "), ours is (%" B_PRId32 "/%" B_PRId32
1286 			")\n", what.id, where.id, channel->fOutput.source.id,
1287 			channel->fOutput.destination.id);
1288 	}
1289 }
1290 
1291 
1292 void
1293 OpenSoundNode::LateNoticeReceived(const media_source& what, bigtime_t how_much,
1294 	bigtime_t performance_time)
1295 {
1296 	CALLED();
1297 
1298 	// is this our output?
1299 	NodeOutput *channel = _FindOutput(what);
1300 	if (channel == NULL)
1301 		return;
1302 
1303 	// If we're late, we need to catch up.  Respond in a manner appropriate
1304 	// to our current run mode.
1305 	if (RunMode() == B_RECORDING) {
1306 		// A hardware capture node can't adjust; it simply emits buffers at
1307 		// appropriate points.  We (partially) simulate this by not adjusting
1308 		// our behavior upon receiving late notices -- after all, the hardware
1309 		// can't choose to capture "sooner"....
1310 	} else if (RunMode() == B_INCREASE_LATENCY) {
1311 		// We're late, and our run mode dictates that we try to produce buffers
1312 		// earlier in order to catch up.  This argues that the downstream nodes
1313 		// are not properly reporting their latency, but there's not much we
1314 		// can do about that at the moment, so we try to start producing
1315 		// buffers earlier to compensate.
1316 		fInternalLatency += how_much;
1317 		SetEventLatency(fLatency + fInternalLatency);
1318 
1319 		fprintf(stderr, "\tincreasing latency to %" B_PRIdBIGTIME "\n",
1320 			fLatency + fInternalLatency);
1321 	} else {
1322 		// The other run modes dictate various strategies for sacrificing data
1323 		// quality in the interests of timely data delivery.  The way *we* do
1324 		// this is to skip a buffer, which catches us up in time by one buffer
1325 		// duration.
1326 //		size_t nSamples = fOutput.format.u.raw_audio.buffer_size
1327 //			/ sizeof(float);
1328 //		mSamplesSent += nSamples;
1329 
1330 		fprintf(stderr, "\tskipping a buffer to try to catch up\n");
1331 	}
1332 }
1333 
1334 
1335 void
1336 OpenSoundNode::EnableOutput(const media_source& what, bool enabled,
1337 	int32* _deprecated_)
1338 {
1339 	CALLED();
1340 
1341 	// If I had more than one output, I'd have to walk my list of output records to see
1342 	// which one matched the given source, and then enable/disable that one.  But this
1343 	// node only has one output, so I just make sure the given source matches, then set
1344 	// the enable state accordingly.
1345 	NodeOutput *channel = _FindOutput(what);
1346 
1347 	if (channel != NULL)
1348 	{
1349 		channel->fOutputEnabled = enabled;
1350 	}
1351 }
1352 
1353 void
1354 OpenSoundNode::AdditionalBufferRequested(const media_source& source,
1355 	media_buffer_id prev_buffer, bigtime_t prev_time,
1356 	const media_seek_tag* prev_tag)
1357 {
1358 	CALLED();
1359 	// we don't support offline mode
1360 	return;
1361 }
1362 
1363 
1364 // #pragma mark - BMediaEventLooper
1365 
1366 
1367 void
1368 OpenSoundNode::HandleEvent(const media_timed_event* event, bigtime_t lateness,
1369 	bool realTimeEvent)
1370 {
1371 	CALLED();
1372 
1373 	switch (event->type) {
1374 		case BTimedEventQueue::B_START:
1375 			HandleStart(event,lateness,realTimeEvent);
1376 			break;
1377 		case BTimedEventQueue::B_SEEK:
1378 			HandleSeek(event,lateness,realTimeEvent);
1379 			break;
1380 		case BTimedEventQueue::B_WARP:
1381 			HandleWarp(event,lateness,realTimeEvent);
1382 			break;
1383 		case BTimedEventQueue::B_STOP:
1384 			HandleStop(event,lateness,realTimeEvent);
1385 			break;
1386 		case BTimedEventQueue::B_HANDLE_BUFFER:
1387 //			TRACE("HandleEvent: B_HANDLE_BUFFER, RunState= %d\n",
1388 //				RunState());
1389 			if (RunState() == BMediaEventLooper::B_STARTED) {
1390 				HandleBuffer(event,lateness,realTimeEvent);
1391 			}
1392 			break;
1393 		case BTimedEventQueue::B_DATA_STATUS:
1394 			HandleDataStatus(event,lateness,realTimeEvent);
1395 			break;
1396 		case BTimedEventQueue::B_PARAMETER:
1397 			HandleParameter(event,lateness,realTimeEvent);
1398 			break;
1399 		default:
1400 			fprintf(stderr,"  unknown event type: %" B_PRId32 "\n",
1401 				event->type);
1402 			break;
1403 	}
1404 }
1405 
1406 
1407 // protected
1408 status_t
1409 OpenSoundNode::HandleBuffer(const media_timed_event* event,
1410 	bigtime_t lateness, bool realTimeEvent)
1411 {
1412 	CALLED();
1413 
1414 	// TODO: How should we handle late buffers? Drop them?
1415 	// Notify the producer?
1416 
1417 	BBuffer* buffer = const_cast<BBuffer*>((BBuffer*)event->pointer);
1418 	if (buffer == NULL) {
1419 		fprintf(stderr,"OpenSoundNode::HandleBuffer() - B_BAD_VALUE\n");
1420 		return B_BAD_VALUE;
1421 	}
1422 
1423 	NodeInput *channel = _FindInput(buffer->Header()->destination);
1424 //	TRACE("buffer->Header()->destination : %i\n",
1425 //		buffer->Header()->destination);
1426 
1427 	if (channel == NULL) {
1428 		buffer->Recycle();
1429 		fprintf(stderr,"OpenSoundNode::HandleBuffer() - "
1430 			"B_MEDIA_BAD_DESTINATION\n");
1431 		return B_MEDIA_BAD_DESTINATION;
1432 	}
1433 
1434 	media_header* hdr = buffer->Header();
1435 	bigtime_t now = TimeSource()->Now();
1436 	bigtime_t perf_time = hdr->start_time;
1437 
1438 	// the how_early calculated here doesn't include scheduling latency
1439 	// because we've already been scheduled to handle the buffer
1440 	bigtime_t how_early = perf_time - EventLatency() - now;
1441 
1442 	// if the buffer is late, we ignore it and report the fact to the producer
1443 	// who sent it to us
1444 	if (RunMode() != B_OFFLINE
1445 			// lateness doesn't matter in offline mode...
1446 		&& RunMode() != B_RECORDING
1447 			// ...or in recording mode
1448 		&& how_early < 0LL
1449 		&& false) {
1450 			// TODO: Debug
1451 		//mLateBuffers++;
1452 		NotifyLateProducer(channel->fInput.source, -how_early, perf_time);
1453 		fprintf(stderr,"	<- LATE BUFFER : %" B_PRIdBIGTIME "\n", how_early);
1454 		buffer->Recycle();
1455 	} else {
1456 		fDevice->Locker()->Lock();
1457 		if (channel->fBuffers.CountItems() > 10) {
1458 			fDevice->Locker()->Unlock();
1459 			TRACE("OpenSoundNode::HandleBuffer too many buffers, "
1460 				"recycling\n");
1461 			buffer->Recycle();
1462 		} else {
1463 //			TRACE("OpenSoundNode::HandleBuffer writing channelId : %i,
1464 //				how_early:%lli\n", channel->fEngineIndex, how_early);
1465 			if (!channel->fBuffers.AddItem(buffer))
1466 				buffer->Recycle();
1467 			fDevice->Locker()->Unlock();
1468 		}
1469 	}
1470 	return B_OK;
1471 }
1472 
1473 
1474 status_t
1475 OpenSoundNode::HandleDataStatus(const media_timed_event* event,
1476 	bigtime_t lateness, bool realTimeEvent)
1477 {
1478 //	CALLED();
1479 
1480 	// TODO: this is called mostly whenever the system mixer
1481 	// switches from not sending us buffers (no client connected)
1482 	// to sending buffers, and vice versa. In a Terminal, this
1483 	// can be nicely demonstrated by provoking a system beep while
1484 	// nothing else is using audio playback. Any first beep will
1485 	// start with a small glitch, while more beeps before the last
1486 	// one finished will not have the glitch. I am not sure, but
1487 	// I seem to remember that other audio nodes have the same
1488 	// problem, so it might not be a problem of this implementation.
1489 
1490 	BString message("OpenSoundNode::HandleDataStatus status: ");
1491 
1492 	switch(event->data) {
1493 		case B_DATA_NOT_AVAILABLE:
1494 			message << "No data";
1495 			break;
1496 		case B_DATA_AVAILABLE:
1497 			message << "Data";
1498 			break;
1499 		case B_PRODUCER_STOPPED:
1500 			message << "Stopped";
1501 			break;
1502 		default:
1503 			message << "???";
1504 			break;
1505 	}
1506 
1507 	message << ", lateness: " << lateness;
1508 	printf("%s\n", message.String());
1509 
1510 	return B_OK;
1511 }
1512 
1513 
1514 status_t
1515 OpenSoundNode::HandleStart(const media_timed_event* event, bigtime_t lateness,
1516 	bool realTimeEvent)
1517 {
1518 	CALLED();
1519 	if (RunState() != B_STARTED) {
1520 		// TODO: What should happen here?
1521 	}
1522 	return B_OK;
1523 }
1524 
1525 
1526 status_t
1527 OpenSoundNode::HandleSeek(const media_timed_event* event, bigtime_t lateness,
1528 	bool realTimeEvent)
1529 {
1530 	CALLED();
1531 	TRACE("OpenSoundNode::HandleSeek(t=%lld, d=%li, bd=%lld)\n",
1532 		event->event_time,event->data,event->bigdata);
1533 	return B_OK;
1534 }
1535 
1536 
1537 status_t
1538 OpenSoundNode::HandleWarp(const media_timed_event* event,
1539 	bigtime_t lateness, bool realTimeEvent)
1540 {
1541 	CALLED();
1542 	return B_OK;
1543 }
1544 
1545 
1546 status_t
1547 OpenSoundNode::HandleStop(const media_timed_event* event, bigtime_t lateness,
1548 	bool realTimeEvent)
1549 {
1550 	CALLED();
1551 	// flush the queue so downstreamers don't get any more
1552 	EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
1553 		BTimedEventQueue::B_HANDLE_BUFFER);
1554 
1555 	return B_OK;
1556 }
1557 
1558 
1559 status_t
1560 OpenSoundNode::HandleParameter(const media_timed_event* event,
1561 	bigtime_t lateness, bool realTimeEvent)
1562 {
1563 	CALLED();
1564 	return B_OK;
1565 }
1566 
1567 
1568 // #pragma mark - BTimeSource
1569 
1570 
1571 void
1572 OpenSoundNode::SetRunMode(run_mode mode)
1573 {
1574 	CALLED();
1575 	TRACE("OpenSoundNode::SetRunMode(%d)\n", mode);
1576 	//BTimeSource::SetRunMode(mode);
1577 }
1578 
1579 
1580 status_t
1581 OpenSoundNode::TimeSourceOp(const time_source_op_info& op, void* _reserved)
1582 {
1583 	CALLED();
1584 	switch(op.op) {
1585 		case B_TIMESOURCE_START:
1586 			TRACE("TimeSourceOp op B_TIMESOURCE_START\n");
1587 			if (RunState() != BMediaEventLooper::B_STARTED) {
1588 				fTimeSourceStarted = true;
1589 				fTimeSourceStartTime = RealTime();
1590 
1591 				media_timed_event startEvent(0, BTimedEventQueue::B_START);
1592 				EventQueue()->AddEvent(startEvent);
1593 			}
1594 			break;
1595 		case B_TIMESOURCE_STOP:
1596 			TRACE("TimeSourceOp op B_TIMESOURCE_STOP\n");
1597 			if (RunState() == BMediaEventLooper::B_STARTED) {
1598 				media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1599 				EventQueue()->AddEvent(stopEvent);
1600 				fTimeSourceStarted = false;
1601 				PublishTime(0, 0, 0);
1602 			}
1603 			break;
1604 		case B_TIMESOURCE_STOP_IMMEDIATELY:
1605 			TRACE("TimeSourceOp op B_TIMESOURCE_STOP_IMMEDIATELY\n");
1606 			if (RunState() == BMediaEventLooper::B_STARTED) {
1607 				media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1608 				EventQueue()->AddEvent(stopEvent);
1609 				fTimeSourceStarted = false;
1610 				PublishTime(0, 0, 0);
1611 			}
1612 			break;
1613 		case B_TIMESOURCE_SEEK:
1614 //			TRACE("TimeSourceOp op B_TIMESOURCE_SEEK\n");
1615 printf("TimeSourceOp op B_TIMESOURCE_SEEK, real %" B_PRIdBIGTIME ", "
1616 	"perf %" B_PRIdBIGTIME "\n", op.real_time, op.performance_time);
1617 			BroadcastTimeWarp(op.real_time, op.performance_time);
1618 			break;
1619 		default:
1620 			break;
1621 	}
1622 	return B_OK;
1623 }
1624 
1625 
1626 // #pragma mark - BControllable
1627 
1628 
1629 status_t
1630 OpenSoundNode::GetParameterValue(int32 id, bigtime_t* last_change, void* value,
1631 	size_t* ioSize)
1632 {
1633 	CALLED();
1634 
1635 	int channelCount = 1;
1636 	int sliderShift = 8;
1637 
1638 	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1639 	if (!mixer)
1640 		return ENODEV;
1641 
1642 	TRACE("id : %i, *ioSize=%d\n", id, *ioSize);
1643 
1644 	oss_mixext mixext;
1645 	status_t err = mixer->GetExtInfo(id, &mixext);
1646 	if (err < B_OK)
1647 		return err;
1648 
1649 	oss_mixer_value mixval;
1650 	mixval.ctrl = mixext.ctrl;
1651 	mixval.timestamp = mixext.timestamp;
1652 
1653 	err = mixer->GetMixerValue(&mixval);
1654 	if (err < B_OK)
1655 		return err;
1656 
1657 	if (!(mixext.flags & MIXF_READABLE))
1658 		return EINVAL;
1659 
1660 	BParameter *parameter = NULL;
1661 	for (int32 i = 0; i < fWeb->CountParameters(); i++) {
1662 		parameter = fWeb->ParameterAt(i);
1663 		if(parameter->ID() == id)
1664 			break;
1665 	}
1666 
1667 	if (!parameter)
1668 		return ENODEV;
1669 
1670 	TRACE("%s: value = 0x%08x\n", __FUNCTION__, mixval.value);
1671 
1672 	*last_change = system_time();//??
1673 
1674 	switch (mixext.type) {
1675 	case MIXT_DEVROOT:
1676 	case MIXT_GROUP:
1677 		break;
1678 	case MIXT_ONOFF:
1679 		if (*ioSize < sizeof(bool))
1680 			return EINVAL;
1681 		*(int32 *)value = mixval.value?true:false;
1682 		*ioSize = sizeof(bool);
1683 		return B_OK;
1684 	case MIXT_ENUM:
1685 		if (*ioSize < sizeof(int32))
1686 			return EINVAL;
1687 		*(int32 *)value = mixval.value;
1688 		*ioSize = sizeof(int32);
1689 		return B_OK;
1690 		break;
1691 	case MIXT_STEREODB:
1692 	case MIXT_STEREOSLIDER16:
1693 	case MIXT_STEREOSLIDER:
1694 		channelCount = 2;
1695 	case MIXT_SLIDER:
1696 	case MIXT_MONODB:
1697 	case MIXT_MONOSLIDER16:
1698 	case MIXT_MONOSLIDER:
1699 		if (*ioSize < channelCount * sizeof(float))
1700 			return EINVAL;
1701 		if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
1702 			return EINVAL;
1703 		if (mixext.type == MIXT_STEREOSLIDER16 ||
1704 			mixext.type == MIXT_MONOSLIDER16)
1705 			sliderShift = 16;
1706 		*ioSize = channelCount * sizeof(float);
1707 		((float *)value)[0] = (float)(mixval.value & ((1 << sliderShift) - 1));
1708 		TRACE("%s: value[O] = %f\n", __FUNCTION__, ((float *)value)[0]);
1709 		if (channelCount < 2)
1710 			return B_OK;
1711 		((float *)value)[1] = (float)((mixval.value >> sliderShift)
1712 			& ((1 << sliderShift) - 1));
1713 		TRACE("%s: value[1] = %f\n", __FUNCTION__, ((float *)value)[1]);
1714 		return B_OK;
1715 		break;
1716 	case MIXT_MESSAGE:
1717 		break;
1718 	case MIXT_MONOVU:
1719 		break;
1720 	case MIXT_STEREOVU:
1721 		break;
1722 	case MIXT_MONOPEAK:
1723 		break;
1724 	case MIXT_STEREOPEAK:
1725 		break;
1726 	case MIXT_RADIOGROUP:
1727 		break;//??
1728 	case MIXT_MARKER:
1729 		break;// separator item: ignore
1730 	case MIXT_VALUE:
1731 		break;
1732 	case MIXT_HEXVALUE:
1733 		break;
1734 /*	case MIXT_MONODB:
1735 		break;
1736 	case MIXT_STEREODB:
1737 		break;*/
1738 	case MIXT_3D:
1739 		break;
1740 /*	case MIXT_MONOSLIDER16:
1741 		break;
1742 	case MIXT_STEREOSLIDER16:
1743 		break;*/
1744 	default:
1745 		TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
1746 			__FUNCTION__, mixext.type);
1747 	}
1748 	*ioSize = 0;
1749 	return EINVAL;
1750 }
1751 
1752 
1753 void
1754 OpenSoundNode::SetParameterValue(int32 id, bigtime_t performance_time,
1755 	const void* value, size_t size)
1756 {
1757 	CALLED();
1758 
1759 	TRACE("id : %i, performance_time : %lld, size : %i\n", id,
1760 		performance_time, size);
1761 
1762 	OpenSoundDeviceMixer *mixer = fDevice->MixerAt(0);
1763 	if (mixer == NULL)
1764 		return;
1765 
1766 	oss_mixext mixext;
1767 	if (mixer->GetExtInfo(id, &mixext) < B_OK)
1768 		return;
1769 	if (!(mixext.flags & MIXF_WRITEABLE))
1770 		return;
1771 
1772 	oss_mixer_value mixval;
1773 	mixval.ctrl = mixext.ctrl;
1774 	mixval.timestamp = mixext.timestamp;
1775 
1776 	status_t err = mixer->GetMixerValue(&mixval);
1777 	if (err < B_OK)
1778 		return;
1779 
1780 	mixval.ctrl = mixext.ctrl;
1781 	mixval.timestamp = mixext.timestamp;
1782 
1783 	BParameter *parameter = NULL;
1784 	for(int32 i=0; i<fWeb->CountParameters(); i++) {
1785 		parameter = fWeb->ParameterAt(i);
1786 		if(parameter->ID() == id)
1787 			break;
1788 	}
1789 
1790 	if (!parameter)
1791 		return;
1792 
1793 	int channelCount = 1;
1794 	int sliderShift = 8;
1795 
1796 	switch (mixext.type) {
1797 		case MIXT_DEVROOT:
1798 		case MIXT_GROUP:
1799 			break;
1800 		case MIXT_ONOFF:
1801 			if (size < sizeof(bool))
1802 				return;
1803 			mixval.value = (int)*(int32 *)value;
1804 			mixer->SetMixerValue(&mixval);
1805 			// At least on my ATI IXP, recording selection can't be set to OFF,
1806 			// you have to set another one to ON to actually do it,
1807 			// and setting to ON changes others to OFF
1808 			// So we have to let users know about it.
1809 			// XXX: find something better, doesn't work correctly here.
1810 			// XXX: try a timed event ?
1811 			_PropagateParameterChanges(mixext.ctrl, mixext.type, mixext.id);
1812 
1813 			return;
1814 		case MIXT_ENUM:
1815 			if (size < sizeof(int32))
1816 				return;
1817 			mixval.value = (int)*(int32 *)value;
1818 			mixer->SetMixerValue(&mixval);
1819 			break;
1820 		case MIXT_STEREODB:
1821 		case MIXT_STEREOSLIDER16:
1822 		case MIXT_STEREOSLIDER:
1823 			channelCount = 2;
1824 		case MIXT_SLIDER:
1825 		case MIXT_MONODB:
1826 		case MIXT_MONOSLIDER16:
1827 		case MIXT_MONOSLIDER:
1828 			if (size < channelCount * sizeof(float))
1829 				return;
1830 			if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
1831 				return;
1832 			if (mixext.type == MIXT_STEREOSLIDER16 ||
1833 				mixext.type == MIXT_MONOSLIDER16)
1834 				sliderShift = 16;
1835 			mixval.value = 0;
1836 
1837 			TRACE("-------- sliderShift=%d, v = %08x, v & %08x = %08x\n",
1838 				sliderShift, mixval.value, ((1 << sliderShift) - 1),
1839 				mixval.value & ((1 << sliderShift) - 1));
1840 
1841 			mixval.value |= ((int)(((float *)value)[0]))
1842 				& ((1 << sliderShift) - 1);
1843 			if (channelCount > 1) {
1844 				mixval.value |= (((int)(((float *)value)[1]))
1845 					& ((1 << sliderShift) - 1)) << sliderShift;
1846 			}
1847 
1848 			TRACE("%s: value = 0x%08x\n", __FUNCTION__, mixval.value);
1849 			mixer->SetMixerValue(&mixval);
1850 			return;
1851 			break;
1852 		case MIXT_MESSAGE:
1853 			break;
1854 		case MIXT_MONOVU:
1855 			break;
1856 		case MIXT_STEREOVU:
1857 			break;
1858 		case MIXT_MONOPEAK:
1859 			break;
1860 		case MIXT_STEREOPEAK:
1861 			break;
1862 		case MIXT_RADIOGROUP:
1863 			break;//??
1864 		case MIXT_MARKER:
1865 			break;// separator item: ignore
1866 		case MIXT_VALUE:
1867 			break;
1868 		case MIXT_HEXVALUE:
1869 			break;
1870 //		case MIXT_MONODB:
1871 //			break;
1872 //		case MIXT_STEREODB:
1873 //			break;
1874 		case MIXT_3D:
1875 			break;
1876 //		case MIXT_MONOSLIDER16:
1877 //			break;
1878 //		case MIXT_STEREOSLIDER16:
1879 //			break;
1880 		default:
1881 			TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
1882 				__FUNCTION__, mixext.type);
1883 	}
1884 
1885 	return;
1886 }
1887 
1888 
1889 BParameterWeb*
1890 OpenSoundNode::MakeParameterWeb()
1891 {
1892 	CALLED();
1893 	BParameterWeb* web = new BParameterWeb;
1894 
1895 	// TODO: the card might change the mixer controls at some point,
1896 	// we should detect it (poll) and recreate the parameter web and
1897 	// re-set it.
1898 
1899 	// TODO: cache mixext[...] and poll for changes in their update_counter.
1900 
1901 	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1902 	if (mixer == NULL) {
1903 		// some cards don't have a mixer, just put a placeholder then
1904 		BParameterGroup* child = web->MakeGroup("No mixer");
1905 		child->MakeNullParameter(1, B_MEDIA_UNKNOWN_TYPE, "No Mixer",
1906 			B_GENERIC);
1907 		return web;
1908 	}
1909 
1910 	int mixext_count = mixer->CountExtInfos();
1911 	TRACE("OpenSoundNode::MakeParameterWeb %i ExtInfos\n", mixext_count);
1912 
1913 	for (int32 i = 0; i < mixext_count; i++) {
1914 		oss_mixext mixext;
1915 		if (mixer->GetExtInfo(i, &mixext) < B_OK)
1916 			continue;
1917 
1918 		if (mixext.type == MIXT_DEVROOT) {
1919 			oss_mixext_root* extroot = (oss_mixext_root*)mixext.data;
1920 			TRACE("OpenSoundNode: mixext[%d]: ROOT\n", i);
1921 			int32 nb = 0;
1922 			const char* childName = mixext.extname;
1923 			childName = extroot->id; // extroot->name;
1924 			BParameterGroup *child = web->MakeGroup(childName);
1925 			_ProcessGroup(child, i, nb);
1926 		}
1927 	}
1928 
1929 	return web;
1930 }
1931 
1932 
1933 // #pragma mark - OpenSoundNode specific
1934 
1935 
1936 void
1937 OpenSoundNode::_ProcessGroup(BParameterGroup *group, int32 index,
1938 	int32& nbParameters)
1939 {
1940 	CALLED();
1941 	// TODO: It looks wrong to use the same mixer in a recursive function!
1942 	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1943 
1944 	int mixext_count = mixer->CountExtInfos();
1945 	for (int32 i = 0; i < mixext_count; i++) {
1946 		oss_mixext mixext;
1947 		if (mixer->GetExtInfo(i, &mixext) < B_OK)
1948 			continue;
1949 		// only keep direct children of that group
1950 		if (mixext.parent != index)
1951 			continue;
1952 
1953 		int32 nb = 1;
1954 
1955 		TRACE("OpenSoundNode: mixext[%d]: { %s/%s, type=%d, parent=%d, "
1956 			"min=%d, max=%d, flags=0x%08x, control_no=%d, desc=%d, "
1957 			"update_counter=%d }\n", i,
1958 			(mixext.type != MIXT_MARKER) ? mixext.id : "",
1959 			(mixext.type != MIXT_MARKER) ? mixext.extname : "",
1960 			mixext.type, mixext.parent,
1961 			mixext.minvalue, mixext.maxvalue,
1962 			mixext.flags, mixext.control_no,
1963 			mixext.desc, mixext.update_counter);
1964 
1965 		// should actually rename the whole group but it's too late there.
1966 		const char *childName = mixext.extname;
1967 		if (mixext.flags & MIXF_MAINVOL)
1968 			childName = "Master Gain";
1969 
1970 		const char *sliderUnit = "";//"(linear)";
1971 		if (mixext.flags & MIXF_HZ)
1972 			sliderUnit = "Hz";
1973 
1974 		const char *continuousKind = B_GAIN;
1975 		BParameterGroup* child;
1976 
1977 		switch (mixext.type) {
1978 		case MIXT_DEVROOT:
1979 			// root item, should be done already
1980 			break;
1981 		case MIXT_GROUP:
1982 			TRACE("OpenSoundNode: mixext[%d]: GROUP\n", i);
1983 			child = group->MakeGroup(childName);
1984 			child->MakeNullParameter(i, B_MEDIA_RAW_AUDIO, childName,
1985 				B_WEB_BUFFER_OUTPUT);
1986 			_ProcessGroup(child, i, nb);
1987 			break;
1988 		case MIXT_ONOFF:
1989 			TRACE("OpenSoundNode: mixext[%d]: ONOFF\n", i);
1990 			// multiaudio node adds 100 to IDs !?
1991 			if (0/*MMC[i].string == S_MUTE*/) {
1992 				group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
1993 					B_MUTE);
1994 			} else {
1995 				group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
1996 					B_ENABLE);
1997 			}
1998 			if (nbParameters > 0) {
1999 				(group->ParameterAt(nbParameters - 1))->AddOutput(
2000 					group->ParameterAt(nbParameters));
2001 				nbParameters++;
2002 			}
2003 			break;
2004 		case MIXT_ENUM:
2005 		{
2006 			TRACE("OpenSoundNode: mixext[%d]: ENUM\n", i);
2007 			BDiscreteParameter *parameter =
2008 				group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
2009 					B_INPUT_MUX);
2010 			if (nbParameters > 0) {
2011 				(group->ParameterAt(nbParameters - 1))->AddOutput(
2012 					group->ParameterAt(nbParameters));
2013 				nbParameters++;
2014 			}
2015 			_ProcessMux(parameter, i);
2016 			break;
2017 		}
2018 		case MIXT_MONODB:
2019 		case MIXT_STEREODB:
2020 			sliderUnit = "dB";
2021 		case MIXT_SLIDER:
2022 		case MIXT_MONOSLIDER16:
2023 		case MIXT_STEREOSLIDER16:
2024 		case MIXT_MONOSLIDER:
2025 			//TRACE("OpenSoundNode: mixext[%d]: MONOSLIDER\n", i);
2026 			//break;
2027 			// fall through
2028 		case MIXT_STEREOSLIDER:
2029 			TRACE("OpenSoundNode: mixext[%d]: [MONO|STEREO]SLIDER\n", i);
2030 
2031 			if (mixext.flags & MIXF_MAINVOL)
2032 				continuousKind = B_MASTER_GAIN;
2033 
2034 // TODO: find out what this was supposed to do:
2035 //			if (mixext.flags & MIXF_CENTIBEL)
2036 //				true;//step size
2037 //			if (mixext.flags & MIXF_DECIBEL)
2038 //				true;//step size
2039 
2040 			group->MakeContinuousParameter(i, B_MEDIA_RAW_AUDIO, childName,
2041 				continuousKind,  sliderUnit, mixext.minvalue, mixext.maxvalue,
2042 				/*TODO: should be "granularity"*/1);
2043 
2044 			if (mixext.type == MIXT_STEREOSLIDER ||
2045 				mixext.type == MIXT_STEREOSLIDER16 ||
2046 				mixext.type == MIXT_STEREODB)
2047 				group->ParameterAt(nbParameters)->SetChannelCount(2);
2048 
2049 			TRACE("nb parameters : %d\n", nbParameters);
2050 			if (nbParameters > 0) {
2051 				(group->ParameterAt(nbParameters - 1))->AddOutput(
2052 					group->ParameterAt(nbParameters));
2053 				nbParameters++;
2054 			}
2055 
2056 			break;
2057 		case MIXT_MESSAGE:
2058 			break;
2059 		case MIXT_MONOVU:
2060 			break;
2061 		case MIXT_STEREOVU:
2062 			break;
2063 		case MIXT_MONOPEAK:
2064 			break;
2065 		case MIXT_STEREOPEAK:
2066 			break;
2067 		case MIXT_RADIOGROUP:
2068 			break;//??
2069 		case MIXT_MARKER:
2070 			break;// separator item: ignore
2071 		case MIXT_VALUE:
2072 			break;
2073 		case MIXT_HEXVALUE:
2074 			break;
2075 //		case MIXT_MONODB:
2076 //			TRACE("OpenSoundNode::_ProcessGroup: Skipping obsolete "
2077 //				"MIXT_MONODB\n");
2078 //			break;
2079 //		case MIXT_STEREODB:
2080 //			TRACE("OpenSoundNode::_ProcessGroup: Skipping obsolete "
2081 //				"MIXT_STEREODB\n");
2082 //			break;
2083 //		case MIXT_SLIDER:
2084 //			break;
2085 		case MIXT_3D:
2086 			break;
2087 //		case MIXT_MONOSLIDER16:
2088 //			break;
2089 //		case MIXT_STEREOSLIDER16:
2090 //			break;
2091 		default:
2092 			TRACE("OpenSoundNode::_ProcessGroup: unknown mixer control "
2093 				"type %d\n", mixext.type);
2094 		}
2095 	}
2096 
2097 }
2098 
2099 
2100 void
2101 OpenSoundNode::_ProcessMux(BDiscreteParameter* parameter, int32 index)
2102 {
2103 	CALLED();
2104 	OpenSoundDeviceMixer *mixer = fDevice->MixerAt(0);
2105 	oss_mixer_enuminfo enuminfo;
2106 	status_t err = mixer->GetEnumInfo(index, &enuminfo);
2107 	if (err < B_OK) {
2108 		// maybe there is no list.
2109 		// generate a count form 0
2110 		oss_mixext mixext;
2111 		if (mixer->GetExtInfo(index, &mixext) < B_OK)
2112 			return;
2113 
2114 		for (int32 i = 0; i < mixext.maxvalue; i++) {
2115 			BString s;
2116 			s << i;
2117 			parameter->AddItem(i, s.String());
2118 		}
2119 		return;
2120 	}
2121 
2122 	for (int32 i = 0; i < enuminfo.nvalues; i++) {
2123 		parameter->AddItem(i, &enuminfo.strings[enuminfo.strindex[i]]);
2124 	}
2125 	return;
2126 }
2127 
2128 
2129 status_t
2130 OpenSoundNode::_PropagateParameterChanges(int from, int type, const char* id)
2131 {
2132 	CALLED();
2133 
2134 	TRACE("OpenSoundNode::_PropagateParameterChanges(from %i, type %i, "
2135 		"id %s)\n", from, type, id);
2136 
2137 	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
2138 	if (mixer == NULL)
2139 		return ENODEV;
2140 
2141 // TODO: Cortex doesn't like that!
2142 // try timed event
2143 // try checking update_counter+caching
2144 return B_OK;
2145 
2146 //	char oldValues[128];
2147 	char newValues[128];
2148 //	size_t oldValuesSize;
2149 	size_t newValuesSize;
2150 
2151 	for (int i = 0; i < mixer->CountExtInfos(); i++) {
2152 		oss_mixext mixext;
2153 		status_t err = mixer->GetExtInfo(i, &mixext);
2154 		if (err < B_OK)
2155 			continue;
2156 
2157 		// skip the caller
2158 		//if (mixext.ctrl == from)
2159 		//	continue;
2160 
2161 		if (!(mixext.flags & MIXF_READABLE))
2162 			continue;
2163 
2164 		// match type ?
2165 		if (type > -1 && mixext.type != type)
2166 			continue;
2167 
2168 		// match internal ID string
2169 		if (id && strncmp(mixext.id, id, 16))
2170 			continue;
2171 
2172 //		BParameter *parameter = NULL;
2173 //		for(int32 i=0; i<fWeb->CountParameters(); i++) {
2174 //			parameter = fWeb->ParameterAt(i);
2175 //			if(parameter->ID() == mixext.ctrl)
2176 //				break;
2177 //		}
2178 //
2179 //		if (!parameter)
2180 //			continue;
2181 
2182 //		oldValuesSize = 128;
2183 		newValuesSize = 128;
2184 		bigtime_t last;
2185 //		TRACE("OpenSoundNode::%s: comparing mixer control %d\n",
2186 //			__FUNCTION__, mixext.ctrl);
2187 //		if (parameter->GetValue(oldValues, &oldValuesSize, &last) < B_OK)
2188 //			continue;
2189 		if (GetParameterValue(mixext.ctrl, &last, newValues,
2190 				&newValuesSize) < B_OK) {
2191 			continue;
2192 		}
2193 //		if (oldValuesSize != newValuesSize || memcmp(oldValues, newValues,
2194 //				MIN(oldValuesSize, newValuesSize))) {
2195 			TRACE("OpenSoundNode::%s: updating mixer control %d\n",
2196 				__FUNCTION__, mixext.ctrl);
2197 			BroadcastNewParameterValue(last, mixext.ctrl, newValues,
2198 				newValuesSize);
2199 //			BroadcastChangedParameter(mixext.ctrl);
2200 //		}
2201 	}
2202 	return B_OK;
2203 }
2204 
2205 
2206 int32
2207 OpenSoundNode::_PlayThread(NodeInput* input)
2208 {
2209 	CALLED();
2210 	//set_thread_priority(find_thread(NULL), 5);// TODO:DEBUG
2211 	signal(SIGUSR1, &_SignalHandler);
2212 
2213 	OpenSoundDeviceEngine* engine = input->fRealEngine;
2214 	if (!engine || !engine->InUse())
2215 		return B_NO_INIT;
2216 	// skip unconnected or non-busy engines
2217 	if (input->fInput.source == media_source::null
2218 		&& input->fEngineIndex == 0)
2219 		return B_NO_INIT;
2220 	// must be open for write
2221 	ASSERT(engine->OpenMode() & OPEN_WRITE);
2222 
2223 	// make writing actually block until the previous buffer played
2224 	size_t driverBufferSize = engine->DriverBufferSize();
2225 	size_t bufferSize = input->fInput.format.u.raw_audio.buffer_size;
2226 	if (driverBufferSize != bufferSize) {
2227 		printf("warning, OSS driver buffer size: %ld, audio buffer "
2228 			"size: %ld", driverBufferSize, bufferSize);
2229 	}
2230 
2231 	// cache a silence buffer
2232 	uint8 silenceBuffer[bufferSize];
2233 	uint8 formatSilence = 0;
2234 	if (input->fInput.format.u.raw_audio.format
2235 			== media_raw_audio_format::B_AUDIO_UCHAR)
2236 		formatSilence = 128;
2237 
2238 	memset(silenceBuffer, formatSilence, bufferSize);
2239 
2240 	// start by writing the OSS driver buffer size of silence
2241 	// so that the first call to write() already blocks for (almost) the
2242 	// buffer duration
2243 	input->Write(silenceBuffer, bufferSize);
2244 
2245 	int64 bytesWritten = 0;
2246 	bigtime_t lastRealTime = RealTime();
2247 	bigtime_t lastPerformanceTime = 0;
2248 
2249 	const int32 driftValueCount = 64;
2250 	int32 currentDriftValueIndex = 0;
2251 	float driftValues[driftValueCount];
2252 	for (int32 i = 0; i < driftValueCount; i++)
2253 		driftValues[i] = 1.0;
2254 
2255 	do {
2256 		if (!fDevice->Locker()->Lock())
2257 			break;
2258 
2259 		TRACE("OpenSoundNode::_PlayThread: buffers: %ld\n",
2260 			input->fBuffers.CountItems());
2261 
2262 		BBuffer* buffer = (BBuffer*)input->fBuffers.RemoveItem((int32)0);
2263 
2264 		fDevice->Locker()->Unlock();
2265 
2266 		if (input->fThread < 0) {
2267 			if (buffer)
2268 				buffer->Recycle();
2269 			break;
2270 		}
2271 
2272 //input->WriteTestTone();
2273 //if (buffer)
2274 //	buffer->Recycle();
2275 //continue;
2276 
2277 		int32 additionalBytesWritten = 0;
2278 		if (buffer != NULL) {
2279 			if (input->Write(buffer->Data(), buffer->SizeUsed()) == B_OK)
2280 				additionalBytesWritten = buffer->SizeUsed();
2281 			buffer->Recycle();
2282 		} else {
2283 			input->Write(silenceBuffer, bufferSize);
2284 			additionalBytesWritten = bufferSize;
2285 		}
2286 
2287 		// TODO: do not assume channel 0 will always be running!
2288 		// update the timesource
2289 		if (input->fEngineIndex == 0 && input->fThread >= 0) {
2290 
2291 			bigtime_t realTime = RealTime();
2292 			bigtime_t realPlaybackDuration = realTime - lastRealTime;
2293 			bigtime_t performanceTime
2294 				= time_for_buffer(bytesWritten, input->fInput.format);
2295 			float drift = (double)(performanceTime
2296 				- lastPerformanceTime) / realPlaybackDuration;
2297 
2298 			lastPerformanceTime = performanceTime;
2299 			lastRealTime = realTime;
2300 
2301 			driftValues[currentDriftValueIndex++] = drift;
2302 			if (currentDriftValueIndex == driftValueCount)
2303 				currentDriftValueIndex = 0;
2304 			drift = 0.0;
2305 			for (int32 i = 0; i < driftValueCount; i++)
2306 				drift += driftValues[i];
2307 			drift /= driftValueCount;
2308 
2309 			if (fDevice->Locker()->Lock()) {
2310 				if (input->fThread >= 0)
2311 					_UpdateTimeSource(performanceTime, realTime, drift);
2312 				fDevice->Locker()->Unlock();
2313 			}
2314 		}
2315 		bytesWritten += additionalBytesWritten;
2316 
2317 	} while (input->fThread > -1);
2318 
2319 	return 0;
2320 }
2321 
2322 
2323 int32
2324 OpenSoundNode::_RecThread(NodeOutput* output)
2325 {
2326 	CALLED();
2327 
2328 	//set_thread_priority(find_thread(NULL), 5);// TODO:DEBUG
2329 	signal(SIGUSR1, &_SignalHandler);
2330 
2331 	OpenSoundDeviceEngine *engine = output->fRealEngine;
2332 	if (!engine || !engine->InUse())
2333 		return B_NO_INIT;
2334 	// make sure we're both started *and* connected before delivering a buffer
2335 	if ((RunState() != BMediaEventLooper::B_STARTED)
2336 		|| (output->fOutput.destination == media_destination::null)) {
2337 		return B_NO_INIT;
2338 	}
2339 
2340 	// must be open for read
2341 	ASSERT(engine->OpenMode() & OPEN_READ);
2342 
2343 #ifdef ENABLE_REC
2344 
2345 	fDevice->Locker()->Lock();
2346 	do {
2347 		audio_buf_info abinfo;
2348 //		size_t avail = engine->GetISpace(&abinfo);
2349 //		TRACE("OpenSoundNode::_RunThread: I avail: %d\n", avail);
2350 //
2351 //		// skip if less than 1 buffer
2352 //		if (avail < output->fOutput.format.u.raw_audio.buffer_size)
2353 //			continue;
2354 
2355 		fDevice->Locker()->Unlock();
2356 		// Get the next buffer of data
2357 		BBuffer* buffer = _FillNextBuffer(&abinfo, *output);
2358 		fDevice->Locker()->Lock();
2359 
2360 		if (buffer) {
2361 			// send the buffer downstream if and only if output is enabled
2362 			status_t err = B_ERROR;
2363 			if (output->fOutputEnabled) {
2364 				err = SendBuffer(buffer, output->fOutput.source,
2365 					output->fOutput.destination);
2366 			}
2367 //			TRACE("OpenSoundNode::_RunThread: I avail: %d, OE %d, %s\n",
2368 //				avail, output->fOutputEnabled, strerror(err));
2369 			if (err != B_OK) {
2370 				buffer->Recycle();
2371 			} else {
2372 				// track how much media we've delivered so far
2373 				size_t nSamples = buffer->SizeUsed()
2374 					/ (output->fOutput.format.u.raw_audio.format
2375 						& media_raw_audio_format::B_AUDIO_SIZE_MASK);
2376 				output->fSamplesSent += nSamples;
2377 //				TRACE("OpenSoundNode::%s: sent %d samples\n",
2378 //					__FUNCTION__, nSamples);
2379 			}
2380 
2381 		}
2382 	} while (output->fThread > -1);
2383 	fDevice->Locker()->Unlock();
2384 
2385 #endif
2386 	return 0;
2387 }
2388 
2389 
2390 status_t
2391 OpenSoundNode::_StartPlayThread(NodeInput* input)
2392 {
2393 	CALLED();
2394 	BAutolock L(fDevice->Locker());
2395 	// the thread is already started ?
2396 	if (input->fThread > B_OK)
2397 		return B_OK;
2398 
2399 	//allocate buffer free semaphore
2400 //	int bufferCount = MAX(fDevice->fFragments.fragstotal, 2); // XXX
2401 
2402 //	fBufferAvailableSem = create_sem(fDevice->MBL.return_playback_buffers - 1,
2403 //		"multi_audio out buffer free");
2404 //	fBufferAvailableSem = create_sem(bufferCount - 1,
2405 //		"OpenSound out buffer free");
2406 
2407 //	if (fBufferAvailableSem < B_OK)
2408 //		return B_ERROR;
2409 
2410 	input->fThread = spawn_thread(_PlayThreadEntry,
2411 		"OpenSound audio output", B_REAL_TIME_PRIORITY, input);
2412 
2413 	if (input->fThread < B_OK) {
2414 //		delete_sem(fBufferAvailableSem);
2415 		return B_ERROR;
2416 	}
2417 
2418 	resume_thread(input->fThread);
2419 	return B_OK;
2420 }
2421 
2422 
2423 status_t
2424 OpenSoundNode::_StopPlayThread(NodeInput* input)
2425 {
2426 	if (input->fThread < 0)
2427 		return B_OK;
2428 
2429 	CALLED();
2430 
2431 	thread_id th;
2432 	{
2433 		BAutolock L(fDevice->Locker());
2434 		th = input->fThread;
2435 		input->fThread = -1;
2436 		//kill(th, SIGUSR1);
2437 	}
2438 	status_t ret;
2439 	wait_for_thread(th, &ret);
2440 
2441 	return B_OK;
2442 }
2443 
2444 
2445 status_t
2446 OpenSoundNode::_StartRecThread(NodeOutput* output)
2447 {
2448 	CALLED();
2449 	// the thread is already started ?
2450 	if (output->fThread > B_OK)
2451 		return B_OK;
2452 
2453 	//allocate buffer free semaphore
2454 //	int bufferCount = MAX(fDevice->fFragments.fragstotal, 2); // XXX
2455 
2456 //	fBufferAvailableSem = create_sem(fDevice->MBL.return_playback_buffers - 1,
2457 //		"multi_audio out buffer free");
2458 //	fBufferAvailableSem = create_sem(bufferCount - 1,
2459 //		"OpenSound out buffer free");
2460 
2461 //	if (fBufferAvailableSem < B_OK)
2462 //		return B_ERROR;
2463 
2464 	output->fThread = spawn_thread(_RecThreadEntry, "OpenSound audio input",
2465 		B_REAL_TIME_PRIORITY, output);
2466 
2467 	if (output->fThread < B_OK) {
2468 		//delete_sem(fBufferAvailableSem);
2469 		return B_ERROR;
2470 	}
2471 
2472 	resume_thread(output->fThread);
2473 	return B_OK;
2474 }
2475 
2476 
2477 status_t
2478 OpenSoundNode::_StopRecThread(NodeOutput* output)
2479 {
2480 	if (output->fThread < 0)
2481 		return B_OK;
2482 
2483 	CALLED();
2484 
2485 	thread_id th = output->fThread;
2486 	output->fThread = -1;
2487 	{
2488 		BAutolock L(fDevice->Locker());
2489 		//kill(th, SIGUSR1);
2490 	}
2491 	status_t ret;
2492 	wait_for_thread(th, &ret);
2493 
2494 	return B_OK;
2495 }
2496 
2497 
2498 void
2499 OpenSoundNode::_UpdateTimeSource(bigtime_t performanceTime,
2500 	bigtime_t realTime, float drift)
2501 {
2502 //	CALLED();
2503 
2504 	if (!fTimeSourceStarted)
2505 		return;
2506 
2507 	PublishTime(performanceTime, realTime, drift);
2508 
2509 //	TRACE("_UpdateTimeSource() perfTime : %lli, realTime : %lli, "
2510 //		"drift : %f\n", perfTime, realTime, drift);
2511 }
2512 
2513 
2514 BBuffer*
2515 OpenSoundNode::_FillNextBuffer(audio_buf_info* abinfo, NodeOutput& channel)
2516 {
2517 	CALLED();
2518 
2519 	BBuffer* buffer = channel.FillNextBuffer(BufferDuration());
2520 	if (!buffer)
2521 		return NULL;
2522 
2523 	if (fDevice == NULL)
2524 		fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - fDevice NULL\n");
2525 	if (buffer->Header() == NULL) {
2526 		fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - "
2527 			"buffer->Header() NULL\n");
2528 	}
2529 	if (TimeSource() == NULL) {
2530 		fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - "
2531 			"TimeSource() NULL\n");
2532 	}
2533 
2534 	// fill in the buffer header
2535 	media_header* hdr = buffer->Header();
2536 	if (hdr != NULL) {
2537 		hdr->time_source = TimeSource()->ID();
2538 		// TODO: should be system_time() - latency_as_per(abinfo)
2539 		hdr->start_time = PerformanceTimeFor(system_time());
2540 	}
2541 
2542 	return buffer;
2543 }
2544 
2545 
2546 status_t
2547 OpenSoundNode::GetConfigurationFor(BMessage* into_message)
2548 {
2549 	CALLED();
2550 
2551 	if (!into_message)
2552 		return B_BAD_VALUE;
2553 
2554 	size_t size = 128;
2555 	void* buffer = malloc(size);
2556 
2557 	for (int32 i = 0; i < fWeb->CountParameters(); i++) {
2558 		BParameter* parameter = fWeb->ParameterAt(i);
2559 		if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER
2560 			&& parameter->Type() != BParameter::B_DISCRETE_PARAMETER)
2561 			continue;
2562 
2563 		TRACE("getting parameter %i\n", parameter->ID());
2564 		size = 128;
2565 		bigtime_t last_change;
2566 		status_t err;
2567 		while ((err = GetParameterValue(parameter->ID(), &last_change, buffer,
2568 				&size)) == B_NO_MEMORY) {
2569 			size += 128;
2570 			free(buffer);
2571 			buffer = malloc(size);
2572 		}
2573 
2574 		if (err == B_OK && size > 0) {
2575 			into_message->AddInt32("parameterID", parameter->ID());
2576 			into_message->AddData("parameterData", B_RAW_TYPE, buffer, size,
2577 				false);
2578 		} else {
2579 			TRACE("parameter err : %s\n", strerror(err));
2580 		}
2581 	}
2582 
2583 	free(buffer);
2584 
2585 	PRINT_OBJECT(*into_message);
2586 
2587 	return B_OK;
2588 }
2589 
2590 
2591 OpenSoundNode::NodeOutput*
2592 OpenSoundNode::_FindOutput(const media_source& source) const
2593 {
2594 	int32 count = fOutputs.CountItems();
2595 	for (int32 i = 0; i < count; i++) {
2596 		NodeOutput* channel = (NodeOutput*)fOutputs.ItemAtFast(i);
2597 		if (source == channel->fOutput.source)
2598 			return channel;
2599 	}
2600 	return NULL;
2601 }
2602 
2603 
2604 OpenSoundNode::NodeInput*
2605 OpenSoundNode::_FindInput(const media_destination& dest) const
2606 {
2607 	int32 count = fInputs.CountItems();
2608 	for (int32 i = 0; i < count; i++) {
2609 		NodeInput* channel = (NodeInput*)fInputs.ItemAtFast(i);
2610 		if (dest == channel->fInput.destination)
2611 			return channel;
2612 	}
2613 	return NULL;
2614 }
2615 
2616 
2617 OpenSoundNode::NodeInput*
2618 OpenSoundNode::_FindInput(int32 destinationId)
2619 {
2620 	int32 count = fInputs.CountItems();
2621 	for (int32 i = 0; i < count; i++) {
2622 		NodeInput* channel = (NodeInput*)fInputs.ItemAtFast(i);
2623 		if (destinationId == channel->fInput.destination.id)
2624 			return channel;
2625 	}
2626 	return NULL;
2627 }
2628 
2629 
2630 // pragma mark - static
2631 
2632 
2633 void
2634 OpenSoundNode::_SignalHandler(int sig)
2635 {
2636 	// TODO: what was this intended for, just stopping the threads?
2637 	// (see _StopThreadXXX(), there is a kill call commented out there)
2638 }
2639 
2640 
2641 int32
2642 OpenSoundNode::_PlayThreadEntry(void* data)
2643 {
2644 	CALLED();
2645 	NodeInput* channel = static_cast<NodeInput*>(data);
2646 	return channel->fNode->_PlayThread(channel);
2647 }
2648 
2649 
2650 int32
2651 OpenSoundNode::_RecThreadEntry(void* data)
2652 {
2653 	CALLED();
2654 	NodeOutput* channel = static_cast<NodeOutput*>(data);
2655 	return channel->fNode->_RecThread(channel);
2656 }
2657 
2658 
2659 void
2660 OpenSoundNode::GetFlavor(flavor_info* outInfo, int32 id)
2661 {
2662 	CALLED();
2663 	if (outInfo == NULL)
2664 		return;
2665 
2666 	outInfo->flavor_flags = 0;
2667 	outInfo->possible_count = 1;
2668 		// one flavor at a time
2669 	outInfo->in_format_count = 0;
2670 		// no inputs
2671 	outInfo->in_formats = 0;
2672 	outInfo->out_format_count = 0;
2673 		// no outputs
2674 	outInfo->out_formats = 0;
2675 	outInfo->internal_id = id;
2676 
2677 	outInfo->name = (char *)"OpenSoundNode Node";
2678 	outInfo->info = (char *)"The OpenSoundNode outputs to OpenSound System v4 "
2679 		"drivers.";
2680 	outInfo->kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER | B_TIME_SOURCE
2681 		| B_PHYSICAL_OUTPUT | B_PHYSICAL_INPUT | B_CONTROLLABLE;
2682 	// TODO: If the OSS engine supports outputing encoded audio,
2683 	// we would need to setup a B_MEDIA_ENCODED_AUDIO format here
2684 	outInfo->in_format_count = 1;
2685 		// 1 input
2686 	media_format * informats = new media_format[outInfo->in_format_count];
2687 	GetFormat(&informats[0]);
2688 	outInfo->in_formats = informats;
2689 
2690 	outInfo->out_format_count = 1;
2691 		// 1 output
2692 	media_format * outformats = new media_format[outInfo->out_format_count];
2693 	GetFormat(&outformats[0]);
2694 	outInfo->out_formats = outformats;
2695 }
2696 
2697 
2698 void
2699 OpenSoundNode::GetFormat(media_format* outFormat)
2700 {
2701 	CALLED();
2702 	if (outFormat == NULL)
2703 		return;
2704 
2705 	outFormat->type = B_MEDIA_RAW_AUDIO;
2706 	outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
2707 	outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
2708 	outFormat->u.raw_audio = media_raw_audio_format::wildcard;
2709 }
2710