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, ¶meterID) == 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