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