xref: /haiku/src/add-ons/media/media-add-ons/esound_sink/ESDSinkNode.cpp (revision 0562493379cd52eb7103531f895f10bb8e77c085)
1 /*
2  * ESounD media addon for BeOS
3  *
4  * Copyright (c) 2006 François Revol (revol@free.fr)
5  *
6  * Based on Multi Audio addon for Haiku,
7  * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr)
8  *
9  * All rights reserved.
10  * Redistribution and use in source and binary forms, with or without modification,
11  * are permitted provided that the following conditions are met:
12  *
13  * - Redistributions of source code must retain the above copyright notice,
14  *   this list of conditions and the following disclaimer.
15  * - Redistributions in binary form must reproduce the above copyright notice,
16  *   this list of conditions and the following disclaimer in the documentation
17  *   and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  */
31 #include <MediaDefs.h>
32 #include <MediaNode.h>
33 #include <MediaAddOn.h>
34 #include <BufferConsumer.h>
35 #include <FileInterface.h>
36 #include <Controllable.h>
37 #include <MediaEventLooper.h>
38 #include <File.h>
39 #include <Errors.h>
40 #include <Entry.h>
41 #include <BufferGroup.h>
42 #include <TimeSource.h>
43 #include <Buffer.h>
44 #include <ParameterWeb.h>
45 #include <MediaRoster.h>
46 #include <limits.h>
47 #include <MediaDefs.h>
48 #include <Message.h>
49 
50 #include "ESDSinkNode.h"
51 #include "ESDEndpoint.h"
52 #ifdef DEBUG
53   #define PRINTING
54 #endif
55 #include "debug.h"
56 #include <Debug.h>
57 
58 #include <stdio.h>
59 #include <string.h>
60 
61 const char * multi_string[] =
62 {
63 	"NAME IS ATTACHED",
64 	"Output", "Input", "Setup", "Tone Control", "Extended Setup", "Enhanced Setup", "Master",
65 	"Beep", "Phone", "Mic", "Line", "CD", "Video", "Aux", "Wave", "Gain", "Level", "Volume",
66 	"Mute", "Enable", "Stereo Mix", "Mono Mix", "Output Stereo Mix", "Output Mono Mix", "Output Bass",
67 	"Output Treble", "Output 3D Center", "Output 3D Depth"
68 };
69 
70 
71 // -------------------------------------------------------- //
72 // ctor/dtor
73 // -------------------------------------------------------- //
74 
75 ESDSinkNode::~ESDSinkNode(void)
76 {
77 	CALLED();
78 	fAddOn->GetConfigurationFor(this, NULL);
79 
80 	BMediaEventLooper::Quit();
81 
82 	fWeb = NULL;
83 	delete fDevice;
84 }
85 
86 ESDSinkNode::ESDSinkNode(BMediaAddOn *addon, char* name, BMessage * config)
87 	: BMediaNode(name),
88 	  BBufferConsumer(B_MEDIA_RAW_AUDIO),
89 #if ENABLE_INPUT
90 	  BBufferProducer(B_MEDIA_RAW_AUDIO),
91 #endif
92 #ifdef ENABLE_TS
93 	  BTimeSource(),
94 #endif
95 	  BMediaEventLooper(),
96 	  fThread(-1),
97 	  fDevice(NULL),
98 	  fTimeSourceStarted(false),
99 	  fWeb(NULL),
100 	  fConfig(*config)
101 {
102 	CALLED();
103 	fInitCheckStatus = B_NO_INIT;
104 
105 	fAddOn = addon;
106 	fId = 0;
107 
108 	AddNodeKind( B_PHYSICAL_OUTPUT );
109 #if ENABLE_INPUT
110 	AddNodeKind( B_PHYSICAL_INPUT );
111 #endif
112 
113 	// initialize our preferred format object
114 	memset(&fPreferredFormat, 0, sizeof(fPreferredFormat)); // set everything to wildcard first
115 	fPreferredFormat.type = B_MEDIA_RAW_AUDIO;
116 #if ESD_FMT == 8
117 	fPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_UCHAR;
118 #else
119 	fPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
120 #endif
121 	fPreferredFormat.u.raw_audio.valid_bits = 0;
122 	fPreferredFormat.u.raw_audio.channel_count = 2;
123 	fPreferredFormat.u.raw_audio.frame_rate = ESD_DEFAULT_RATE;
124 	fPreferredFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
125 
126 	// we'll use the consumer's preferred buffer size, if any
127 	fPreferredFormat.u.raw_audio.buffer_size = ESD_MAX_BUF / 4
128 /*						* (fPreferredFormat.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
129 						* fPreferredFormat.u.raw_audio.channel_count*/;
130 
131 	if(config) {
132 		//PRINT_OBJECT(*config);
133 		config->FindString("hostname", &fHostname);
134 	}
135 	if (fHostname.Length() < 1)
136 		fHostname = "192.168.0.1";
137 	fPort = ESD_DEFAULT_PORT;
138 
139 	fDevice = new ESDEndpoint();
140 	/*
141 	if (fDevice) {
142 		if (fDevice->Connect(fHostname.String()) >= 0) {
143 			fDevice->SetCommand();
144 			fDevice->SetFormat(ESD_FMT, 2);
145 			//fDevice->GetServerInfo();
146 			fInitCheckStatus = fDevice->SendDefaultCommand();
147 		}
148 	}
149 	*/
150 	if (!fDevice)
151 		return;
152 	fInitCheckStatus = B_OK;
153 }
154 
155 status_t ESDSinkNode::InitCheck(void) const
156 {
157 	CALLED();
158 	return fInitCheckStatus;
159 }
160 
161 
162 // -------------------------------------------------------- //
163 // implementation of BMediaNode
164 // -------------------------------------------------------- //
165 
166 BMediaAddOn * ESDSinkNode::AddOn(
167 				int32 * internal_id) const
168 {
169 	CALLED();
170 	// BeBook says this only gets called if we were in an add-on.
171 	if (fAddOn != 0) {
172 		// If we get a null pointer then we just won't write.
173 		if (internal_id != 0) {
174 			*internal_id = fId;
175 		}
176 	}
177 	return fAddOn;
178 }
179 
180 void ESDSinkNode::Preroll(void)
181 {
182 	CALLED();
183 	// XXX:Performance opportunity
184 	BMediaNode::Preroll();
185 }
186 
187 status_t ESDSinkNode::HandleMessage(
188 				int32 message,
189 				const void * data,
190 				size_t size)
191 {
192 	CALLED();
193 	return B_ERROR;
194 }
195 
196 void ESDSinkNode::NodeRegistered(void)
197 {
198 	CALLED();
199 
200 	if (fInitCheckStatus != B_OK) {
201 		ReportError(B_NODE_IN_DISTRESS);
202 		return;
203 	}
204 
205 	SetPriority(B_REAL_TIME_PRIORITY);
206 
207 	Run();
208 
209 //	media_input *input = new media_input;
210 
211 	fInput.format = fPreferredFormat;
212 	fInput.destination.port = ControlPort();
213 	fInput.destination.id = 0;
214 	fInput.node = Node();
215 	sprintf(fInput.name, "output %ld", fInput.destination.id);
216 
217 	fOutput.format = fPreferredFormat;
218 	fOutput.destination = media_destination::null;
219 	fOutput.source.port = ControlPort();
220 	fOutput.source.id = 0;
221 	fOutput.node = Node();
222 	sprintf(fOutput.name, "input %ld", fOutput.source.id);
223 
224 	// Set up our parameter web
225 	fWeb = MakeParameterWeb();
226 	SetParameterWeb(fWeb);
227 
228 	/* apply configuration */
229 #ifdef PRINTING
230 	bigtime_t start = system_time();
231 #endif
232 
233 	int32 index = 0;
234 	int32 parameterID = 0;
235 	const void *data;
236 	ssize_t size;
237 	while(fConfig.FindInt32("parameterID", index, &parameterID) == B_OK) {
238 		if(fConfig.FindData("parameterData", B_RAW_TYPE, index, &data, &size) == B_OK)
239 			SetParameterValue(parameterID, TimeSource()->Now(), data, size);
240 		index++;
241 	}
242 
243 #ifdef PRINTING
244 	PRINT(("apply configuration in : %ld\n", system_time() - start));
245 #endif
246 }
247 
248 status_t ESDSinkNode::RequestCompleted(const media_request_info &info)
249 {
250 	CALLED();
251 	return B_OK;
252 }
253 
254 void ESDSinkNode::SetTimeSource(BTimeSource *timeSource)
255 {
256 	CALLED();
257 }
258 
259 // -------------------------------------------------------- //
260 // implemention of BBufferConsumer
261 // -------------------------------------------------------- //
262 
263 // Check to make sure the format is okay, then remove
264 // any wildcards corresponding to our requirements.
265 status_t ESDSinkNode::AcceptFormat(
266 				const media_destination & dest,
267 				media_format * format)
268 {
269 	CALLED();
270 
271 	if(fInput.destination != dest) {
272 		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION");
273 		return B_MEDIA_BAD_DESTINATION; // we only have one input so that better be it
274 	}
275 
276 /*	media_format * myFormat = GetFormat();
277 	fprintf(stderr,"proposed format: ");
278 	print_media_format(format);
279 	fprintf(stderr,"\n");
280 	fprintf(stderr,"my format: ");
281 	print_media_format(myFormat);
282 	fprintf(stderr,"\n");*/
283 	// Be's format_is_compatible doesn't work.
284 //	if (!format_is_compatible(*format,*myFormat)) {
285 
286 	if ( format->type != B_MEDIA_RAW_AUDIO ) {
287 		fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
288 		return B_MEDIA_BAD_FORMAT;
289 	}
290 
291 	/*if(format->u.raw_audio.format == media_raw_audio_format::B_AUDIO_FLOAT
292 		&& channel->fPreferredFormat.u.raw_audio.format == media_raw_audio_format::B_AUDIO_SHORT)
293 		format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
294 	else*/
295 	format->u.raw_audio.format = fPreferredFormat.u.raw_audio.format;
296 	format->u.raw_audio.valid_bits = fPreferredFormat.u.raw_audio.valid_bits;
297 
298 	format->u.raw_audio.frame_rate    = fPreferredFormat.u.raw_audio.frame_rate;
299 	format->u.raw_audio.channel_count = fPreferredFormat.u.raw_audio.channel_count;
300 	format->u.raw_audio.byte_order    = B_MEDIA_HOST_ENDIAN;
301 	format->u.raw_audio.buffer_size   = ESD_MAX_BUF / 4
302 /*						* (format->u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
303 						* format->u.raw_audio.channel_count*/;
304 
305 
306 	/*media_format myFormat;
307 	GetFormat(&myFormat);
308 	if (!format_is_acceptible(*format,myFormat)) {
309 		fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
310 		return B_MEDIA_BAD_FORMAT;
311 	}*/
312 	//AddRequirements(format);
313 	return B_OK;
314 }
315 
316 status_t ESDSinkNode::GetNextInput(
317 				int32 * cookie,
318 				media_input * out_input)
319 {
320 	CALLED();
321 
322 	if ((*cookie < 1) && (*cookie >= 0)) {
323 		*out_input = fInput;
324 		*cookie += 1;
325 		PRINT(("input.format : %u\n", fInput.format.u.raw_audio.format));
326 		return B_OK;
327 	} else
328 		return B_BAD_INDEX;
329 }
330 
331 void ESDSinkNode::DisposeInputCookie(
332 				int32 cookie)
333 {
334 	CALLED();
335 	// nothing to do since our cookies are just integers
336 }
337 
338 void ESDSinkNode::BufferReceived(
339 				BBuffer * buffer)
340 {
341 	CALLED();
342 	switch (buffer->Header()->type) {
343 		/*case B_MEDIA_PARAMETERS:
344 			{
345 			status_t status = ApplyParameterData(buffer->Data(),buffer->SizeUsed());
346 			if (status != B_OK) {
347 				fprintf(stderr,"ApplyParameterData in ESDSinkNode::BufferReceived failed\n");
348 			}
349 			buffer->Recycle();
350 			}
351 			break;*/
352 		case B_MEDIA_RAW_AUDIO:
353 #if 0
354 			if (buffer->Flags() & BBuffer::B_SMALL_BUFFER) {
355 				fprintf(stderr,"NOT IMPLEMENTED: B_SMALL_BUFFER in ESDSinkNode::BufferReceived\n");
356 				// XXX: implement this part
357 				buffer->Recycle();
358 			} else {
359 				media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER,
360 										buffer, BTimedEventQueue::B_RECYCLE_BUFFER);
361 				status_t status = EventQueue()->AddEvent(event);
362 				if (status != B_OK) {
363 					fprintf(stderr,"EventQueue()->AddEvent(event) in ESDSinkNode::BufferReceived failed\n");
364 					buffer->Recycle();
365 				}
366 			}
367 #endif
368 			if (fDevice->CanSend()) {
369 
370 				fDevice->Write(buffer->Data(), buffer->SizeUsed());
371 
372 			}
373 			buffer->Recycle();
374 			break;
375 		default:
376 			fprintf(stderr,"unexpected buffer type in ESDSinkNode::BufferReceived\n");
377 			buffer->Recycle();
378 			break;
379 	}
380 }
381 
382 void ESDSinkNode::ProducerDataStatus(
383 				const media_destination & for_whom,
384 				int32 status,
385 				bigtime_t at_performance_time)
386 {
387 	CALLED();
388 
389 	if(fInput.destination != for_whom) {
390 		fprintf(stderr,"invalid destination received in ESDSinkNode::ProducerDataStatus\n");
391 		return;
392 	}
393 
394 	media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS,
395 			&fInput, BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
396 	EventQueue()->AddEvent(event);
397 }
398 
399 status_t ESDSinkNode::GetLatencyFor(
400 				const media_destination & for_whom,
401 				bigtime_t * out_latency,
402 				media_node_id * out_timesource)
403 {
404 	CALLED();
405 	if ((out_latency == 0) || (out_timesource == 0)) {
406 		fprintf(stderr,"<- B_BAD_VALUE\n");
407 		return B_BAD_VALUE;
408 	}
409 
410 	if(fInput.destination != for_whom) {
411 		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
412 		return B_MEDIA_BAD_DESTINATION;
413 	}
414 
415 	bigtime_t intl = EventLatency();
416 	bigtime_t netl = 0LL;
417 	if (fDevice)
418 		netl = fDevice->GetLatency();
419 	// I don't want to swap
420 	if (netl > 500000)
421 		netl = 500000;
422 	*out_latency = intl + netl;
423 	fprintf(stderr, "int latency %Ld, net latency %Ld, total latency %Ld\n", intl, netl, *out_latency);
424 	*out_timesource = TimeSource()->ID();
425 	return B_OK;
426 }
427 
428 status_t ESDSinkNode::Connected(
429 				const media_source & producer,	/* here's a good place to request buffer group usage */
430 				const media_destination & where,
431 				const media_format & with_format,
432 				media_input * out_input)
433 {
434 	CALLED();
435 
436 	if(fInput.destination != where) {
437 		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
438 		return B_MEDIA_BAD_DESTINATION;
439 	}
440 
441 	//
442 	if (fDevice) {
443 		if (fDevice->Connect(fHostname.String(), fPort) >= 0) {
444 			fDevice->SetCommand();
445 			//fDevice->GetServerInfo();
446 			fDevice->SetFormat(ESD_FMT, 2);
447 			fInitCheckStatus = fDevice->SendDefaultCommand();
448 		}
449 	}
450 	// use one buffer length latency
451 	fInternalLatency = with_format.u.raw_audio.buffer_size * 10000 / 2
452 			/ ( (with_format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
453 				* with_format.u.raw_audio.channel_count)
454 			/ ((int32)(with_format.u.raw_audio.frame_rate / 100));
455 
456 	PRINT(("  internal latency = %lld\n",fInternalLatency));
457 
458 	SetEventLatency(fInternalLatency);
459 
460 	// record the agreed upon values
461 	fInput.source = producer;
462 	fInput.format = with_format;
463 	*out_input = fInput;
464 
465 	return B_OK;
466 }
467 
468 void ESDSinkNode::Disconnected(
469 				const media_source & producer,
470 				const media_destination & where)
471 {
472 	CALLED();
473 
474 	if(fInput.destination != where) {
475 		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
476 		return;
477 	}
478 	if (fInput.source != producer) {
479 		fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
480 		return;
481 	}
482 
483 	fInput.source = media_source::null;
484 	fInput.format = fPreferredFormat;
485 	//GetFormat(&channel->fInput.format);
486 }
487 
488 	/* The notification comes from the upstream producer, so he's already cool with */
489 	/* the format; you should not ask him about it in here. */
490 status_t ESDSinkNode::FormatChanged(
491 				const media_source & producer,
492 				const media_destination & consumer,
493 				int32 change_tag,
494 				const media_format & format)
495 {
496 	CALLED();
497 
498 	if(fInput.destination != consumer) {
499 		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
500 		return B_MEDIA_BAD_DESTINATION;
501 	}
502 	if (fInput.source != producer) {
503 		return B_MEDIA_BAD_SOURCE;
504 	}
505 
506 	return B_ERROR;
507 }
508 
509 	/* Given a performance time of some previous buffer, retrieve the remembered tag */
510 	/* of the closest (previous or exact) performance time. Set *out_flags to 0; the */
511 	/* idea being that flags can be added later, and the understood flags returned in */
512 	/* *out_flags. */
513 status_t ESDSinkNode::SeekTagRequested(
514 				const media_destination & destination,
515 				bigtime_t in_target_time,
516 				uint32 in_flags,
517 				media_seek_tag * out_seek_tag,
518 				bigtime_t * out_tagged_time,
519 				uint32 * out_flags)
520 {
521 	CALLED();
522 	return BBufferConsumer::SeekTagRequested(destination,in_target_time,in_flags,
523 											out_seek_tag,out_tagged_time,out_flags);
524 }
525 
526 // -------------------------------------------------------- //
527 // implementation for BBufferProducer
528 // -------------------------------------------------------- //
529 #if 0
530 status_t
531 ESDSinkNode::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format)
532 {
533 	// FormatSuggestionRequested() is not necessarily part of the format negotiation
534 	// process; it's simply an interrogation -- the caller wants to see what the node's
535 	// preferred data format is, given a suggestion by the caller.
536 	CALLED();
537 
538 	if (!format)
539 	{
540 		fprintf(stderr, "\tERROR - NULL format pointer passed in!\n");
541 		return B_BAD_VALUE;
542 	}
543 
544 	// this is the format we'll be returning (our preferred format)
545 	*format = fPreferredFormat;
546 
547 	// a wildcard type is okay; we can specialize it
548 	if (type == B_MEDIA_UNKNOWN_TYPE) type = B_MEDIA_RAW_AUDIO;
549 
550 	// we only support raw audio
551 	if (type != B_MEDIA_RAW_AUDIO) return B_MEDIA_BAD_FORMAT;
552 	else return B_OK;
553 }
554 
555 status_t
556 ESDSinkNode::FormatProposal(const media_source& output, media_format* format)
557 {
558 	// FormatProposal() is the first stage in the BMediaRoster::Connect() process.  We hand
559 	// out a suggested format, with wildcards for any variations we support.
560 	CALLED();
561 	node_output *channel = FindOutput(output);
562 
563 	// is this a proposal for our select output?
564 	if (channel == NULL)
565 	{
566 		fprintf(stderr, "ESDSinkNode::FormatProposal returning B_MEDIA_BAD_SOURCE\n");
567 		return B_MEDIA_BAD_SOURCE;
568 	}
569 
570 	// we only support floating-point raw audio, so we always return that, but we
571 	// supply an error code depending on whether we found the proposal acceptable.
572 	media_type requestedType = format->type;
573 	*format = channel->fPreferredFormat;
574 	if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO))
575 	{
576 		fprintf(stderr, "ESDSinkNode::FormatProposal returning B_MEDIA_BAD_FORMAT\n");
577 		return B_MEDIA_BAD_FORMAT;
578 	}
579 	else return B_OK;		// raw audio or wildcard type, either is okay by us
580 }
581 
582 status_t
583 ESDSinkNode::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_)
584 {
585 	CALLED();
586 
587 	// we don't support any other formats, so we just reject any format changes.
588 	return B_ERROR;
589 }
590 
591 status_t
592 ESDSinkNode::GetNextOutput(int32* cookie, media_output* out_output)
593 {
594 	CALLED();
595 
596 	if ((*cookie < fOutputs.CountItems()) && (*cookie >= 0)) {
597 		node_output *channel = (node_output *)fOutputs.ItemAt(*cookie);
598 		*out_output = channel->fOutput;
599 		*cookie += 1;
600 		return B_OK;
601 	} else
602 		return B_BAD_INDEX;
603 }
604 
605 status_t
606 ESDSinkNode::DisposeOutputCookie(int32 cookie)
607 {
608 	CALLED();
609 	// do nothing because we don't use the cookie for anything special
610 	return B_OK;
611 }
612 
613 status_t
614 ESDSinkNode::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup)
615 {
616 	CALLED();
617 
618 	node_output *channel = FindOutput(for_source);
619 
620 	// is this our output?
621 	if (channel == NULL)
622 	{
623 		fprintf(stderr, "ESDSinkNode::SetBufferGroup returning B_MEDIA_BAD_SOURCE\n");
624 		return B_MEDIA_BAD_SOURCE;
625 	}
626 
627 	// Are we being passed the buffer group we're already using?
628 	if (newGroup == channel->fBufferGroup) return B_OK;
629 
630 	// Ahh, someone wants us to use a different buffer group.  At this point we delete
631 	// the one we are using and use the specified one instead.  If the specified group is
632 	// NULL, we need to recreate one ourselves, and use *that*.  Note that if we're
633 	// caching a BBuffer that we requested earlier, we have to Recycle() that buffer
634 	// *before* deleting the buffer group, otherwise we'll deadlock waiting for that
635 	// buffer to be recycled!
636 	delete channel->fBufferGroup;		// waits for all buffers to recycle
637 	if (newGroup != NULL)
638 	{
639 		// we were given a valid group; just use that one from now on
640 		channel->fBufferGroup = newGroup;
641 	}
642 	else
643 	{
644 		// we were passed a NULL group pointer; that means we construct
645 		// our own buffer group to use from now on
646 		size_t size = channel->fOutput.format.u.raw_audio.buffer_size;
647 		int32 count = int32(fLatency / BufferDuration() + 1 + 1);
648 		channel->fBufferGroup = new BBufferGroup(size, count);
649 	}
650 
651 	return B_OK;
652 }
653 
654 status_t
655 ESDSinkNode::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name)
656 {
657 	// PrepareToConnect() is the second stage of format negotiations that happens
658 	// inside BMediaRoster::Connect().  At this point, the consumer's AcceptFormat()
659 	// method has been called, and that node has potentially changed the proposed
660 	// format.  It may also have left wildcards in the format.  PrepareToConnect()
661 	// *must* fully specialize the format before returning!
662 	CALLED();
663 
664 	node_output *channel = FindOutput(what);
665 
666 	// is this our output?
667 	if (channel == NULL)
668 	{
669 		fprintf(stderr, "ESDSinkNode::PrepareToConnect returning B_MEDIA_BAD_SOURCE\n");
670 		return B_MEDIA_BAD_SOURCE;
671 	}
672 
673 	// are we already connected?
674 	if (channel->fOutput.destination != media_destination::null)
675 		return B_MEDIA_ALREADY_CONNECTED;
676 
677 	// the format may not yet be fully specialized (the consumer might have
678 	// passed back some wildcards).  Finish specializing it now, and return an
679 	// error if we don't support the requested format.
680 	if (format->type != B_MEDIA_RAW_AUDIO)
681 	{
682 		fprintf(stderr, "\tnon-raw-audio format?!\n");
683 		return B_MEDIA_BAD_FORMAT;
684 	}
685 
686 	 // !!! validate all other fields except for buffer_size here, because the consumer might have
687 	// supplied different values from AcceptFormat()?
688 
689 	// check the buffer size, which may still be wildcarded
690 	if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size)
691 	{
692 		format->u.raw_audio.buffer_size = 2048;		// pick something comfortable to suggest
693 		fprintf(stderr, "\tno buffer size provided, suggesting %lu\n", format->u.raw_audio.buffer_size);
694 	}
695 	else
696 	{
697 		fprintf(stderr, "\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size);
698 	}
699 
700 	// Now reserve the connection, and return information about it
701 	channel->fOutput.destination = where;
702 	channel->fOutput.format = *format;
703 	*out_source = channel->fOutput.source;
704 	strncpy(out_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
705 	return B_OK;
706 }
707 
708 void
709 ESDSinkNode::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name)
710 {
711 	CALLED();
712 
713 	node_output *channel = FindOutput(source);
714 
715 	// is this our output?
716 	if (channel == NULL)
717 	{
718 		fprintf(stderr, "ESDSinkNode::Connect returning (cause : B_MEDIA_BAD_SOURCE)\n");
719 		return;
720 	}
721 
722 	// If something earlier failed, Connect() might still be called, but with a non-zero
723 	// error code.  When that happens we simply unreserve the connection and do
724 	// nothing else.
725 	if (error)
726 	{
727 		channel->fOutput.destination = media_destination::null;
728 		channel->fOutput.format = channel->fPreferredFormat;
729 		return;
730 	}
731 
732 	// Okay, the connection has been confirmed.  Record the destination and format
733 	// that we agreed on, and report our connection name again.
734 	channel->fOutput.destination = destination;
735 	channel->fOutput.format = format;
736 	strncpy(io_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
737 
738 	// reset our buffer duration, etc. to avoid later calculations
739 	bigtime_t duration = channel->fOutput.format.u.raw_audio.buffer_size * 10000
740 			/ ( (channel->fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
741 				* channel->fOutput.format.u.raw_audio.channel_count)
742 			/ ((int32)(channel->fOutput.format.u.raw_audio.frame_rate / 100));
743 
744 	SetBufferDuration(duration);
745 
746 	// Now that we're connected, we can determine our downstream latency.
747 	// Do so, then make sure we get our events early enough.
748 	media_node_id id;
749 	FindLatencyFor(channel->fOutput.destination, &fLatency, &id);
750 	PRINT(("\tdownstream latency = %Ld\n", fLatency));
751 
752 	fInternalLatency = BufferDuration();
753 	PRINT(("\tbuffer-filling took %Ld usec on this machine\n", fInternalLatency));
754 	//SetEventLatency(fLatency + fInternalLatency);
755 
756 	// Set up the buffer group for our connection, as long as nobody handed us a
757 	// buffer group (via SetBufferGroup()) prior to this.  That can happen, for example,
758 	// if the consumer calls SetOutputBuffersFor() on us from within its Connected()
759 	// method.
760 	if (!channel->fBufferGroup)
761 		AllocateBuffers(*channel);
762 
763 	// we are sure the thread is started
764 	StartThread();
765 }
766 
767 void
768 ESDSinkNode::Disconnect(const media_source& what, const media_destination& where)
769 {
770 	CALLED();
771 
772 	node_output *channel = FindOutput(what);
773 
774 	// is this our output?
775 	if (channel == NULL)
776 	{
777 		fprintf(stderr, "ESDSinkNode::Disconnect() returning (cause : B_MEDIA_BAD_SOURCE)\n");
778 		return;
779 	}
780 
781 	// Make sure that our connection is the one being disconnected
782 	if ((where == channel->fOutput.destination) && (what == channel->fOutput.source))
783 	{
784 		channel->fOutput.destination = media_destination::null;
785 		channel->fOutput.format = channel->fPreferredFormat;
786 		delete channel->fBufferGroup;
787 		channel->fBufferGroup = NULL;
788 	}
789 	else
790 	{
791 		fprintf(stderr, "\tDisconnect() called with wrong source/destination (%ld/%ld), ours is (%ld/%ld)\n",
792 			what.id, where.id, channel->fOutput.source.id, channel->fOutput.destination.id);
793 	}
794 }
795 
796 void
797 ESDSinkNode::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time)
798 {
799 	CALLED();
800 
801 	node_output *channel = FindOutput(what);
802 
803 	// is this our output?
804 	if (channel == NULL)
805 	{
806 		return;
807 	}
808 
809 	// If we're late, we need to catch up.  Respond in a manner appropriate to our
810 	// current run mode.
811 	if (RunMode() == B_RECORDING)
812 	{
813 		// A hardware capture node can't adjust; it simply emits buffers at
814 		// appropriate points.  We (partially) simulate this by not adjusting
815 		// our behavior upon receiving late notices -- after all, the hardware
816 		// can't choose to capture "sooner"....
817 	}
818 	else if (RunMode() == B_INCREASE_LATENCY)
819 	{
820 		// We're late, and our run mode dictates that we try to produce buffers
821 		// earlier in order to catch up.  This argues that the downstream nodes are
822 		// not properly reporting their latency, but there's not much we can do about
823 		// that at the moment, so we try to start producing buffers earlier to
824 		// compensate.
825 		fInternalLatency += how_much;
826 		SetEventLatency(fLatency + fInternalLatency);
827 
828 		fprintf(stderr, "\tincreasing latency to %Ld\n", fLatency + fInternalLatency);
829 	}
830 	else
831 	{
832 		// The other run modes dictate various strategies for sacrificing data quality
833 		// in the interests of timely data delivery.  The way *we* do this is to skip
834 		// a buffer, which catches us up in time by one buffer duration.
835 		/*size_t nSamples = fOutput.format.u.raw_audio.buffer_size / sizeof(float);
836 		mSamplesSent += nSamples;*/
837 
838 		fprintf(stderr, "\tskipping a buffer to try to catch up\n");
839 	}
840 }
841 
842 void
843 ESDSinkNode::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_)
844 {
845 	CALLED();
846 
847 	// If I had more than one output, I'd have to walk my list of output records to see
848 	// which one matched the given source, and then enable/disable that one.  But this
849 	// node only has one output, so I just make sure the given source matches, then set
850 	// the enable state accordingly.
851 	node_output *channel = FindOutput(what);
852 
853 	if (channel != NULL)
854 	{
855 		channel->fOutputEnabled = enabled;
856 	}
857 }
858 
859 void
860 ESDSinkNode::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag)
861 {
862 	CALLED();
863 	// we don't support offline mode
864 	return;
865 }
866 #endif
867 
868 // -------------------------------------------------------- //
869 // implementation for BMediaEventLooper
870 // -------------------------------------------------------- //
871 
872 void ESDSinkNode::HandleEvent(
873 				const media_timed_event *event,
874 				bigtime_t lateness,
875 				bool realTimeEvent)
876 {
877 	CALLED();
878 	switch (event->type) {
879 		case BTimedEventQueue::B_START:
880 			HandleStart(event,lateness,realTimeEvent);
881 			break;
882 		case BTimedEventQueue::B_SEEK:
883 			HandleSeek(event,lateness,realTimeEvent);
884 			break;
885 		case BTimedEventQueue::B_WARP:
886 			HandleWarp(event,lateness,realTimeEvent);
887 			break;
888 		case BTimedEventQueue::B_STOP:
889 			HandleStop(event,lateness,realTimeEvent);
890 			break;
891 		case BTimedEventQueue::B_HANDLE_BUFFER:
892 			if (RunState() == BMediaEventLooper::B_STARTED) {
893 				HandleBuffer(event,lateness,realTimeEvent);
894 			}
895 			break;
896 		case BTimedEventQueue::B_DATA_STATUS:
897 			HandleDataStatus(event,lateness,realTimeEvent);
898 			break;
899 		case BTimedEventQueue::B_PARAMETER:
900 			HandleParameter(event,lateness,realTimeEvent);
901 			break;
902 		default:
903 			fprintf(stderr,"  unknown event type: %li\n",event->type);
904 			break;
905 	}
906 }
907 
908 // protected:
909 
910 // how should we handle late buffers?  drop them?
911 // notify the producer?
912 status_t ESDSinkNode::HandleBuffer(
913 				const media_timed_event *event,
914 				bigtime_t lateness,
915 				bool realTimeEvent)
916 {
917 	CALLED();
918 	BBuffer * buffer = const_cast<BBuffer*>((BBuffer*)event->pointer);
919 	if (buffer == 0) {
920 		fprintf(stderr,"<- B_BAD_VALUE\n");
921 		return B_BAD_VALUE;
922 	}
923 
924 	if(fInput.destination.id != buffer->Header()->destination) {
925 		fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
926 		return B_MEDIA_BAD_DESTINATION;
927 	}
928 
929 	media_header* hdr = buffer->Header();
930 	bigtime_t now = TimeSource()->Now();
931 	bigtime_t perf_time = hdr->start_time;
932 
933 	// the how_early calculate here doesn't include scheduling latency because
934 	// we've already been scheduled to handle the buffer
935 	bigtime_t how_early = perf_time - EventLatency() - now;
936 
937 	// if the buffer is late, we ignore it and report the fact to the producer
938 	// who sent it to us
939 	if ((RunMode() != B_OFFLINE) &&				// lateness doesn't matter in offline mode...
940 		(RunMode() != B_RECORDING) &&		// ...or in recording mode
941 		(how_early < 0LL))
942 	{
943 		//mLateBuffers++;
944 		NotifyLateProducer(fInput.source, -how_early, perf_time);
945 		fprintf(stderr,"	<- LATE BUFFER : %lli\n", how_early);
946 		buffer->Recycle();
947 	} else {
948 		if (fDevice->CanSend())
949 			fDevice->Write(buffer->Data(), buffer->SizeUsed());
950 	}
951 	return B_OK;
952 }
953 
954 status_t ESDSinkNode::HandleDataStatus(
955 						const media_timed_event *event,
956 						bigtime_t lateness,
957 						bool realTimeEvent)
958 {
959 	CALLED();
960 	PRINT(("ESDSinkNode::HandleDataStatus status:%li, lateness:%li\n", event->data, lateness));
961 	switch(event->data) {
962 		case B_DATA_NOT_AVAILABLE:
963 			break;
964 		case B_DATA_AVAILABLE:
965 			break;
966 		case B_PRODUCER_STOPPED:
967 			break;
968 		default:
969 			break;
970 	}
971 	return B_OK;
972 }
973 
974 status_t ESDSinkNode::HandleStart(
975 						const media_timed_event *event,
976 						bigtime_t lateness,
977 						bool realTimeEvent)
978 {
979 	CALLED();
980 	if (RunState() != B_STARTED) {
981 
982 	}
983 	return B_OK;
984 }
985 
986 status_t ESDSinkNode::HandleSeek(
987 						const media_timed_event *event,
988 						bigtime_t lateness,
989 						bool realTimeEvent)
990 {
991 	CALLED();
992 	PRINT(("ESDSinkNode::HandleSeek(t=%lld,d=%li,bd=%lld)\n",event->event_time,event->data,event->bigdata));
993 	return B_OK;
994 }
995 
996 status_t ESDSinkNode::HandleWarp(
997 						const media_timed_event *event,
998 						bigtime_t lateness,
999 						bool realTimeEvent)
1000 {
1001 	CALLED();
1002 	return B_OK;
1003 }
1004 
1005 status_t ESDSinkNode::HandleStop(
1006 						const media_timed_event *event,
1007 						bigtime_t lateness,
1008 						bool realTimeEvent)
1009 {
1010 	CALLED();
1011 	// flush the queue so downstreamers don't get any more
1012 	EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
1013 
1014 	//StopThread();
1015 	return B_OK;
1016 }
1017 
1018 status_t ESDSinkNode::HandleParameter(
1019 				const media_timed_event *event,
1020 				bigtime_t lateness,
1021 				bool realTimeEvent)
1022 {
1023 	CALLED();
1024 	return B_OK;
1025 }
1026 
1027 // -------------------------------------------------------- //
1028 // implemention of BTimeSource
1029 // -------------------------------------------------------- //
1030 #ifdef ENABLE_TS
1031 
1032 void
1033 ESDSinkNode::SetRunMode(run_mode mode)
1034 {
1035 	CALLED();
1036 	PRINT(("ESDSinkNode::SetRunMode mode:%i\n", mode));
1037 	//BTimeSource::SetRunMode(mode);
1038 }
1039 
1040 status_t
1041 ESDSinkNode::TimeSourceOp(const time_source_op_info &op, void *_reserved)
1042 {
1043 	CALLED();
1044 	switch(op.op) {
1045 		case B_TIMESOURCE_START:
1046 			PRINT(("TimeSourceOp op B_TIMESOURCE_START\n"));
1047 			if (RunState() != BMediaEventLooper::B_STARTED) {
1048 				fTimeSourceStarted = true;
1049 
1050 				media_timed_event startEvent(0, BTimedEventQueue::B_START);
1051 				EventQueue()->AddEvent(startEvent);
1052 			}
1053 			break;
1054 		case B_TIMESOURCE_STOP:
1055 			PRINT(("TimeSourceOp op B_TIMESOURCE_STOP\n"));
1056 			if (RunState() == BMediaEventLooper::B_STARTED) {
1057 				media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1058 				EventQueue()->AddEvent(stopEvent);
1059 				fTimeSourceStarted = false;
1060 				PublishTime(0, 0, 0);
1061 			}
1062 			break;
1063 		case B_TIMESOURCE_STOP_IMMEDIATELY:
1064 			PRINT(("TimeSourceOp op B_TIMESOURCE_STOP_IMMEDIATELY\n"));
1065 			if (RunState() == BMediaEventLooper::B_STARTED) {
1066 				media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1067 				EventQueue()->AddEvent(stopEvent);
1068 				fTimeSourceStarted = false;
1069 				PublishTime(0, 0, 0);
1070 			}
1071 			break;
1072 		case B_TIMESOURCE_SEEK:
1073 			PRINT(("TimeSourceOp op B_TIMESOURCE_SEEK\n"));
1074 			BroadcastTimeWarp(op.real_time, op.performance_time);
1075 			break;
1076 		default:
1077 			break;
1078 	}
1079 	return B_OK;
1080 }
1081 #endif
1082 
1083 // -------------------------------------------------------- //
1084 // implemention of BControllable
1085 // -------------------------------------------------------- //
1086 
1087 status_t
1088 ESDSinkNode::GetParameterValue(int32 id, bigtime_t* last_change, void* value, size_t* ioSize)
1089 {
1090 	CALLED();
1091 	if (!fDevice)
1092 		return B_ERROR;
1093 	//PRINT(("id : %i\n", id));
1094 	switch (id) {
1095 		case PARAM_ENABLED:
1096 			// XXX
1097 			break;
1098 		case PARAM_HOST:
1099 		{
1100 			BString s = fDevice->Host();
1101 			*ioSize = MIN(*ioSize, s.Length());
1102 			memcpy(value, s.String(), *ioSize);
1103 			return B_OK;
1104 		}
1105 		case PARAM_PORT:
1106 		{
1107 			BString s;
1108 			s << fDevice->Port();
1109 			*ioSize = MIN(*ioSize, s.Length());
1110 			memcpy(value, s.String(), *ioSize);
1111 			return B_OK;
1112 		}
1113 		default:
1114 			break;
1115 	}
1116 #if 0
1117 	BParameter *parameter = NULL;
1118 	for(int32 i=0; i<fWeb->CountParameters(); i++) {
1119 		parameter = fWeb->ParameterAt(i);
1120 		if(parameter->ID() == id)
1121 			break;
1122 	}
1123 #endif
1124 
1125 return EINVAL;
1126 }
1127 
1128 void
1129 ESDSinkNode::SetParameterValue(int32 id, bigtime_t performance_time, const void* value, size_t size)
1130 {
1131 	CALLED();
1132 	PRINT(("id : %i, performance_time : %lld, size : %i\n", id, performance_time, size));
1133 	BParameter *parameter = NULL;
1134 	for(int32 i=0; i<fWeb->CountParameters(); i++) {
1135 		parameter = fWeb->ParameterAt(i);
1136 		if(parameter->ID() == id)
1137 			break;
1138 	}
1139 	switch (id) {
1140 		case PARAM_ENABLED:
1141 			break;
1142 		case PARAM_HOST:
1143 		{
1144 			fprintf(stderr, "set HOST: %s\n", (const char *)value);
1145 			fHostname = (const char *)value;
1146 			if (fDevice && fDevice->Connected()) {
1147 				if (fDevice->Connect(fHostname.String(), fPort) >= 0) {
1148 					fDevice->SetCommand();
1149 					fDevice->SetFormat(ESD_FMT, 2);
1150 					//fDevice->GetServerInfo();
1151 					fInitCheckStatus = fDevice->SendDefaultCommand();
1152 				}
1153 			}
1154 			return;
1155 		}
1156 		case PARAM_PORT:
1157 		{
1158 			fprintf(stderr, "set PORT: %s\n", (const char *)value);
1159 			fPort = atoi((const char *)value);
1160 			if (fDevice && fDevice->Connected()) {
1161 				if (fDevice->Connect(fHostname.String(), fPort) >= 0) {
1162 					fDevice->SetCommand();
1163 					fDevice->SetFormat(ESD_FMT, 2);
1164 					//fDevice->GetServerInfo();
1165 					fInitCheckStatus = fDevice->SendDefaultCommand();
1166 				}
1167 			}
1168 			return;
1169 		}
1170 		default:
1171 			break;
1172 	}
1173 }
1174 
1175 BParameterWeb*
1176 ESDSinkNode::MakeParameterWeb()
1177 {
1178 	CALLED();
1179 	BParameterWeb* web = new BParameterWeb;
1180 #if 0
1181 	PRINT(("MMCI.control_count : %i\n", fDevice->MMCI.control_count));
1182 	multi_mix_control		*MMC = fDevice->MMCI.controls;
1183 
1184 	for(int i=0; i<fDevice->MMCI.control_count; i++) {
1185 		if(MMC[i].flags & B_MULTI_MIX_GROUP && MMC[i].parent == 0) {
1186 				PRINT(("NEW_GROUP\n"));
1187 				int32 nb = 0;
1188 				const char* childName;
1189 				if(MMC[i].string != S_null)
1190 					childName = multi_string[MMC[i].string];
1191 				else
1192 					childName = MMC[i].name;
1193 				BParameterGroup *child = web->MakeGroup(childName);
1194 				ProcessGroup(child, i, nb);
1195 		}
1196 	}
1197 #endif
1198 	int id = 0;
1199 	BParameterGroup *group = web->MakeGroup("Server");
1200 	BParameter *p;
1201 	// XXX: use B_MEDIA_UNKNOWN_TYPE or _NO_TYPE ?
1202 	// keep in sync with enum { PARAM_* } !
1203 	p = group->MakeDiscreteParameter(PARAM_ENABLED, B_MEDIA_RAW_AUDIO, "Enable", B_ENABLE);
1204 #if defined(B_BEOS_VERSION_DANO) || defined(__HAIKU__)
1205 	p = group->MakeTextParameter(PARAM_HOST, B_MEDIA_RAW_AUDIO, "Hostname", B_GENERIC, 128);
1206 	p = group->MakeTextParameter(PARAM_PORT, B_MEDIA_RAW_AUDIO, "Port", B_GENERIC, 16);
1207 #endif
1208 	return web;
1209 }
1210 #if 0
1211 void
1212 ESDSinkNode::ProcessGroup(BParameterGroup *group, int32 index, int32 &nbParameters)
1213 {
1214 	CALLED();
1215 	multi_mix_control		*parent = &fDevice->MMCI.controls[index];
1216 	multi_mix_control		*MMC = fDevice->MMCI.controls;
1217 	for(int32 i=0; i<fDevice->MMCI.control_count; i++) {
1218 		if(MMC[i].parent != parent->id)
1219 			continue;
1220 
1221 		const char* childName;
1222 		if(MMC[i].string != S_null)
1223 			childName = multi_string[MMC[i].string];
1224 		else
1225 			childName = MMC[i].name;
1226 
1227 		if(MMC[i].flags & B_MULTI_MIX_GROUP) {
1228 			PRINT(("NEW_GROUP\n"));
1229 			int32 nb = 1;
1230 			BParameterGroup *child = group->MakeGroup(childName);
1231 			child->MakeNullParameter(MMC[i].id, B_MEDIA_RAW_AUDIO, childName, B_WEB_BUFFER_OUTPUT);
1232 			ProcessGroup(child, i, nb);
1233 		} else if(MMC[i].flags & B_MULTI_MIX_MUX) {
1234 			PRINT(("NEW_MUX\n"));
1235 			BDiscreteParameter *parameter =
1236 				group->MakeDiscreteParameter(100 + MMC[i].id, B_MEDIA_RAW_AUDIO, childName, B_INPUT_MUX);
1237 			if(nbParameters>0) {
1238 				(group->ParameterAt(nbParameters - 1))->AddOutput(group->ParameterAt(nbParameters));
1239 				nbParameters++;
1240 			}
1241 			ProcessMux(parameter, i);
1242 		} else if(MMC[i].flags & B_MULTI_MIX_GAIN) {
1243 			PRINT(("NEW_GAIN\n"));
1244 			group->MakeContinuousParameter(100 + MMC[i].id, B_MEDIA_RAW_AUDIO, "", B_MASTER_GAIN,
1245 				"dB", MMC[i].gain.min_gain, MMC[i].gain.max_gain, MMC[i].gain.granularity);
1246 
1247 			if(i+1 <fDevice->MMCI.control_count && MMC[i+1].master == MMC[i].id && MMC[i+1].flags & B_MULTI_MIX_GAIN) {
1248 				group->ParameterAt(nbParameters)->SetChannelCount(
1249 					group->ParameterAt(nbParameters)->CountChannels() + 1);
1250 				i++;
1251 			}
1252 
1253 			PRINT(("nb parameters : %d\n", nbParameters));
1254 			if (nbParameters > 0) {
1255 				(group->ParameterAt(nbParameters - 1))->AddOutput(group->ParameterAt(nbParameters));
1256 				nbParameters++;
1257 			}
1258 		} else if(MMC[i].flags & B_MULTI_MIX_ENABLE) {
1259 			PRINT(("NEW_ENABLE\n"));
1260 			if(MMC[i].string == S_MUTE)
1261 				group->MakeDiscreteParameter(100 + MMC[i].id, B_MEDIA_RAW_AUDIO, childName, B_MUTE);
1262 			else
1263 				group->MakeDiscreteParameter(100 + MMC[i].id, B_MEDIA_RAW_AUDIO, childName, B_ENABLE);
1264 			if(nbParameters>0) {
1265 				(group->ParameterAt(nbParameters - 1))->AddOutput(group->ParameterAt(nbParameters));
1266 				nbParameters++;
1267 			}
1268 		}
1269 	}
1270 }
1271 
1272 void
1273 ESDSinkNode::ProcessMux(BDiscreteParameter *parameter, int32 index)
1274 {
1275 	CALLED();
1276 	multi_mix_control		*parent = &fDevice->MMCI.controls[index];
1277 	multi_mix_control		*MMC = fDevice->MMCI.controls;
1278 	int32 	itemIndex = 0;
1279 	for(int32 i=0; i<fDevice->MMCI.control_count; i++) {
1280 		if(MMC[i].parent != parent->id)
1281 			continue;
1282 
1283 		const char* childName;
1284 		if(MMC[i].string != S_null)
1285 			childName = multi_string[MMC[i].string];
1286 		else
1287 			childName = MMC[i].name;
1288 
1289 		if(MMC[i].flags & B_MULTI_MIX_MUX_VALUE) {
1290 			PRINT(("NEW_MUX_VALUE\n"));
1291 			parameter->AddItem(itemIndex, childName);
1292 			itemIndex++;
1293 		}
1294 	}
1295 }
1296 #endif
1297 // -------------------------------------------------------- //
1298 // ESDSinkNode specific functions
1299 // -------------------------------------------------------- //
1300 
1301 status_t
1302 ESDSinkNode::GetConfigurationFor(BMessage * into_message)
1303 {
1304 	CALLED();
1305 
1306 	BParameter *parameter = NULL;
1307 	void *buffer;
1308 	size_t size = 128;
1309 	bigtime_t last_change;
1310 	status_t err;
1311 
1312 	buffer = malloc(size);
1313 
1314 	for(int32 i=0; i<fWeb->CountParameters(); i++) {
1315 		parameter = fWeb->ParameterAt(i);
1316 		if(parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER
1317 			&& parameter->Type() != BParameter::B_DISCRETE_PARAMETER)
1318 			continue;
1319 
1320 		PRINT(("getting parameter %i\n", parameter->ID()));
1321 		size = 128;
1322 		while((err = GetParameterValue(parameter->ID(), &last_change, buffer, &size))==B_NO_MEMORY) {
1323 			size += 128;
1324 			free(buffer);
1325 			buffer = malloc(size);
1326 		}
1327 
1328 		if(err == B_OK && size > 0) {
1329 			into_message->AddInt32("parameterID", parameter->ID());
1330 			into_message->AddData("parameterData", B_RAW_TYPE, buffer, size, false);
1331 		} else {
1332 			PRINT(("parameter err : %s\n", strerror(err)));
1333 		}
1334 	}
1335 
1336 	//PRINT_OBJECT(*into_message);
1337 
1338 	return B_OK;
1339 }
1340 
1341 // static:
1342 
1343 void ESDSinkNode::GetFlavor(flavor_info * outInfo, int32 id)
1344 {
1345 	CALLED();
1346 
1347 	outInfo->flavor_flags = B_FLAVOR_IS_GLOBAL;
1348 //	outInfo->possible_count = 0;	// any number
1349 	outInfo->possible_count = 1;	// only 1
1350 	outInfo->in_format_count = 0; // no inputs
1351 	outInfo->in_formats = 0;
1352 	outInfo->out_format_count = 0; // no outputs
1353 	outInfo->out_formats = 0;
1354 	outInfo->internal_id = id;
1355 
1356 	outInfo->name = new char[256];
1357 		strcpy(outInfo->name, "ESounD Out");
1358 	outInfo->info = new char[256];
1359 		strcpy(outInfo->info, "The ESounD Sink node outputs a network Enlightenment Sound Daemon.");
1360 	outInfo->kinds = /*B_TIME_SOURCE | *//*B_CONTROLLABLE | */ 0;
1361 
1362 #if ENABLE_INPUT
1363 	outInfo->kinds |= B_BUFFER_PRODUCER | B_PHYSICAL_INPUT;
1364 	outInfo->out_format_count = 1; // 1 output
1365 	media_format * outformats = new media_format[outInfo->out_format_count];
1366 	GetFormat(&outformats[0]);
1367 	outInfo->out_formats = outformats;
1368 #endif
1369 
1370 	outInfo->kinds |= B_BUFFER_CONSUMER | B_PHYSICAL_OUTPUT;
1371 	outInfo->in_format_count = 1; // 1 input
1372 	media_format * informats = new media_format[outInfo->in_format_count];
1373 	GetFormat(&informats[0]);
1374 	outInfo->in_formats = informats;
1375 }
1376 
1377 void ESDSinkNode::GetFormat(media_format * outFormat)
1378 {
1379 	CALLED();
1380 
1381 	outFormat->type = B_MEDIA_RAW_AUDIO;
1382 	outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
1383 	outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
1384 	outFormat->u.raw_audio = media_raw_audio_format::wildcard;
1385 }
1386