1 /* 2 * Copyright 2002 David Shipman, 3 * Copyright 2003-2007 Marcus Overhagen 4 * Copyright 2007 Haiku Inc. All rights reserved. 5 * Distributed under the terms of the MIT License. 6 */ 7 #include "AudioMixer.h" 8 #include "MixerCore.h" 9 #include "MixerInput.h" 10 #include "MixerOutput.h" 11 #include "MixerUtils.h" 12 13 #include <Buffer.h> 14 #include <FindDirectory.h> 15 #include <math.h> 16 #include <MediaRoster.h> 17 #include <ParameterWeb.h> 18 #include <Path.h> 19 #include <RealtimeAlloc.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <TimeSource.h> 24 25 // the range of the gain sliders (in dB) 26 #define DB_MAX 18.0 27 #define DB_MIN -60.0 28 // when using non linear sliders, we use a power function with 29 #define DB_EXPONENT_POSITIVE 1.4 // for dB values > 0 30 #define DB_EXPONENT_NEGATIVE 1.8 // for dB values < 0 31 32 #define USE_MEDIA_FORMAT_WORKAROUND 1 33 34 #if USE_MEDIA_FORMAT_WORKAROUND 35 static void 36 multi_audio_format_specialize(media_multi_audio_format *format, 37 const media_multi_audio_format *other); 38 #endif 39 40 #define FORMAT_USER_DATA_TYPE 0x7294a8f3 41 #define FORMAT_USER_DATA_MAGIC_1 0xc84173bd 42 #define FORMAT_USER_DATA_MAGIC_2 0x4af62b7d 43 44 AudioMixer::AudioMixer(BMediaAddOn *addOn, bool isSystemMixer) 45 : BMediaNode("Audio Mixer"), 46 BBufferConsumer(B_MEDIA_RAW_AUDIO), 47 BBufferProducer(B_MEDIA_RAW_AUDIO), 48 BControllable(), 49 BMediaEventLooper(), 50 fAddOn(addOn), 51 fCore(new MixerCore(this)), 52 fWeb(0), 53 fBufferGroup(0), 54 fDownstreamLatency(1), 55 fInternalLatency(1), 56 fDisableStop(false) 57 { 58 BMediaNode::AddNodeKind(B_SYSTEM_MIXER); 59 60 // this is the default format used for all wildcard format SpecializeTo()s 61 fDefaultFormat.type = B_MEDIA_RAW_AUDIO; 62 fDefaultFormat.u.raw_audio.frame_rate = 96000; 63 fDefaultFormat.u.raw_audio.channel_count = 2; 64 fDefaultFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; 65 fDefaultFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN; 66 fDefaultFormat.u.raw_audio.buffer_size = 4096; 67 fDefaultFormat.u.raw_audio.channel_mask = 0; 68 fDefaultFormat.u.raw_audio.valid_bits = 0; 69 fDefaultFormat.u.raw_audio.matrix_mask = 0; 70 71 if (isSystemMixer) { 72 // to get persistent settings, assign a settings file 73 BPath path; 74 if (B_OK != find_directory (B_USER_SETTINGS_DIRECTORY, &path)) 75 path.SetTo("/boot/home/config/settings/"); 76 path.Append("System Audio Mixer"); 77 fCore->Settings()->SetSettingsFile(path.Path()); 78 79 // disable stop on the auto started (system) mixer 80 DisableNodeStop(); 81 } 82 83 ApplySettings(); 84 } 85 86 AudioMixer::~AudioMixer() 87 { 88 BMediaEventLooper::Quit(); 89 SetParameterWeb(NULL); 90 91 // stop the mixer 92 fCore->Lock(); 93 fCore->Stop(); 94 fCore->Unlock(); 95 96 // disconnect all nodes from the mixer 97 // XXX todo 98 99 delete fCore; 100 delete fBufferGroup; 101 102 DEBUG_ONLY(fCore = 0; fBufferGroup = 0; fWeb = 0); 103 } 104 105 void 106 AudioMixer::ApplySettings() 107 { 108 fCore->Lock(); 109 fCore->SetOutputAttenuation(fCore->Settings()->AttenuateOutput() ? 0.708 : 1.0); 110 fCore->Unlock(); 111 } 112 113 void 114 AudioMixer::DisableNodeStop() 115 { 116 fDisableStop = true; 117 } 118 119 // 120 // BMediaNode methods 121 // 122 123 void AudioMixer::Stop(bigtime_t performance_time, bool immediate) 124 { 125 if (fDisableStop) { 126 TRACE("AudioMixer STOP is disabled\n"); 127 return; 128 } else { 129 BMediaEventLooper::Stop(performance_time, immediate); 130 } 131 } 132 133 BMediaAddOn * 134 AudioMixer::AddOn(int32 *internal_id) const 135 { 136 *internal_id = 0; 137 return fAddOn; 138 } 139 140 // 141 // BBufferConsumer methods 142 // 143 144 status_t 145 AudioMixer::HandleMessage(int32 message, const void *data, size_t size) 146 { 147 // since we're using a mediaeventlooper, there shouldn't be any messages 148 return B_ERROR; 149 } 150 151 status_t 152 AudioMixer::AcceptFormat(const media_destination &dest, media_format *ioFormat) 153 { 154 PRINT_FORMAT("AudioMixer::AcceptFormat: ", *ioFormat); 155 156 // check that the specified format is reasonable for the specified destination, and 157 // fill in any wildcard fields for which our BBufferConsumer has specific requirements. 158 159 // we have multiple inputs with different IDs, but 160 // the port number must match our ControlPort() 161 if (dest.port != ControlPort()) 162 return B_MEDIA_BAD_DESTINATION; 163 164 // specialize to raw audio format if necessary 165 if (ioFormat->type == B_MEDIA_UNKNOWN_TYPE) 166 ioFormat->type = B_MEDIA_RAW_AUDIO; 167 168 // we require a raw audio format 169 if (ioFormat->type != B_MEDIA_RAW_AUDIO) 170 return B_MEDIA_BAD_FORMAT; 171 172 // We do not have special requirements, but just in case 173 // another mixer is connecting to us and may need a hint 174 // to create a connection at optimal frame rate and 175 // channel count, we place this information in the user_data 176 fCore->Lock(); 177 MixerOutput *output = fCore->Output(); 178 uint32 channel_count = output ? output->MediaOutput().format.u.raw_audio.channel_count : 0; 179 float frame_rate = output ? output->MediaOutput().format.u.raw_audio.frame_rate : 0.0; 180 fCore->Unlock(); 181 ioFormat->user_data_type = FORMAT_USER_DATA_TYPE; 182 *(uint32 *)&ioFormat->user_data[0] = FORMAT_USER_DATA_MAGIC_1; 183 *(uint32 *)&ioFormat->user_data[4] = channel_count; 184 *(float *)&ioFormat->user_data[20] = frame_rate; 185 *(uint32 *)&ioFormat->user_data[44] = FORMAT_USER_DATA_MAGIC_2; 186 187 return B_OK; 188 } 189 190 status_t 191 AudioMixer::GetNextInput(int32 *cookie, media_input *out_input) 192 { 193 TRACE("AudioMixer::GetNextInput\n"); 194 195 // our 0th input is always a wildcard and free one 196 if (*cookie == 0) { 197 out_input->node = Node(); 198 out_input->source = media_source::null; 199 out_input->destination.port = ControlPort(); 200 out_input->destination.id = 0; 201 memset(&out_input->format, 0, sizeof(out_input->format)); 202 out_input->format.type = B_MEDIA_RAW_AUDIO; 203 strcpy(out_input->name, "Free Input"); 204 *cookie += 1; 205 return B_OK; 206 } 207 208 // the other inputs are the currently connected ones 209 fCore->Lock(); 210 MixerInput *input; 211 input = fCore->Input(*cookie - 1); 212 if (!input) { 213 fCore->Unlock(); 214 return B_BAD_INDEX; 215 } 216 *out_input = input->MediaInput(); 217 *cookie += 1; 218 fCore->Unlock(); 219 return B_OK; 220 } 221 222 void 223 AudioMixer::DisposeInputCookie(int32 cookie) 224 { 225 // nothing to do 226 } 227 228 void 229 AudioMixer::BufferReceived(BBuffer *buffer) 230 { 231 232 if (buffer->Header()->type == B_MEDIA_PARAMETERS) { 233 TRACE("Control Buffer Received\n"); 234 ApplyParameterData(buffer->Data(), buffer->SizeUsed()); 235 buffer->Recycle(); 236 return; 237 } 238 239 //PRINT(4, "buffer received at %12Ld, should arrive at %12Ld, delta %12Ld\n", TimeSource()->Now(), buffer->Header()->start_time, TimeSource()->Now() - buffer->Header()->start_time); 240 241 // Note: The following code is outcommented on purpose 242 // and is about to be modified at a later point 243 // HandleInputBuffer(buffer, 0); 244 // buffer->Recycle(); 245 // return; 246 247 248 // to receive the buffer at the right time, 249 // push it through the event looper 250 media_timed_event event(buffer->Header()->start_time, 251 BTimedEventQueue::B_HANDLE_BUFFER, 252 buffer, 253 BTimedEventQueue::B_RECYCLE_BUFFER); 254 EventQueue()->AddEvent(event); 255 } 256 257 258 void 259 AudioMixer::HandleInputBuffer(BBuffer *buffer, bigtime_t lateness) 260 { 261 // Note: The following code is outcommented on purpose 262 // and is about to be modified at a later point 263 /* 264 if (lateness > 5000) { 265 printf("Received buffer with way to high lateness %Ld\n", lateness); 266 if (RunMode() != B_DROP_DATA) { 267 printf("sending notify\n"); 268 NotifyLateProducer(channel->fInput.source, lateness / 2, TimeSource()->Now()); 269 } else if (RunMode() == B_DROP_DATA) { 270 printf("dropping buffer\n"); 271 return; 272 } 273 } 274 */ 275 276 // printf("Received buffer with lateness %Ld\n", lateness); 277 278 fCore->Lock(); 279 fCore->BufferReceived(buffer, lateness); 280 fCore->Unlock(); 281 282 // Note: The following code is outcommented on purpose 283 // and is about to be modified at a later point 284 /* 285 if ((B_OFFLINE == RunMode()) && (B_DATA_AVAILABLE == channel->fProducerDataStatus)) 286 { 287 RequestAdditionalBuffer(channel->fInput.source, buffer); 288 } 289 */ 290 } 291 292 void 293 AudioMixer::ProducerDataStatus( const media_destination &for_whom, 294 int32 status, bigtime_t at_performance_time) 295 { 296 /* 297 if (IsValidDest(for_whom)) 298 { 299 media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS, 300 (void *)(&for_whom), BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL); 301 EventQueue()->AddEvent(event); 302 303 // FIX_THIS 304 // the for_whom destination is not being sent correctly - verify in HandleEvent loop 305 306 } 307 */ 308 } 309 310 status_t 311 AudioMixer::GetLatencyFor(const media_destination &for_whom, 312 bigtime_t *out_latency, 313 media_node_id *out_timesource) 314 { 315 // we have multiple inputs with different IDs, but 316 // the port number must match our ControlPort() 317 if (for_whom.port != ControlPort()) 318 return B_MEDIA_BAD_DESTINATION; 319 320 // return our event latency - this includes our internal + downstream 321 // latency, but _not_ the scheduling latency 322 *out_latency = EventLatency(); 323 *out_timesource = TimeSource()->ID(); 324 325 printf("AudioMixer::GetLatencyFor %Ld, timesource is %ld\n", 326 *out_latency, 327 *out_timesource); 328 329 return B_OK; 330 } 331 332 status_t 333 AudioMixer::Connected(const media_source &producer, const media_destination &where, 334 const media_format &with_format, media_input *out_input) 335 { 336 PRINT_FORMAT("AudioMixer::Connected: ", with_format); 337 338 // workaround for a crashing bug in RealPlayer. to be proper, RealPlayer's 339 // BBufferProducer::PrepareToConnect() should have removed all wildcards. 340 if (out_input->format.u.raw_audio.frame_rate == 0) { 341 fprintf(stderr, "Audio Mixer Warning: " 342 "Producer (port %ld, id %ld) connected with frame_rate=0\n", 343 producer.port, 344 producer.id); 345 MixerOutput *output = fCore->Output(); 346 float frame_rate = output ? output->MediaOutput().format.u.raw_audio.frame_rate : 44100.0f; 347 out_input->format.u.raw_audio.frame_rate = frame_rate; 348 } 349 350 // a BBufferProducer is connecting to our BBufferConsumer 351 352 // incoming connections should always have an incoming ID=0, 353 // and the port number must match our ControlPort() 354 if (where.id != 0 || where.port != ControlPort()) 355 return B_MEDIA_BAD_DESTINATION; 356 357 fCore->Lock(); 358 359 // we assign a new id (!= 0) to the newly created input 360 out_input->destination.id = fCore->CreateInputID(); 361 362 // We need to make sure that the outInput's name field contains a valid name, 363 // the name given the connection by the producer may still be an empty string. 364 // if the input has no name, assign one 365 if (strlen(out_input->name) == 0) 366 sprintf(out_input->name, "Input %ld", out_input->destination.id); 367 368 // add a new input to the mixer engine 369 MixerInput *input; 370 input = fCore->AddInput(*out_input); 371 372 fCore->Settings()->LoadConnectionSettings(input); 373 374 fCore->Unlock(); 375 376 // If we want the producer to use a specific BBufferGroup, we now need to call 377 // BMediaRoster::SetOutputBuffersFor() here to set the producer's buffer group. 378 // But we have no special buffer requirements anyway... 379 380 UpdateParameterWeb(); 381 382 return B_OK; 383 } 384 385 void 386 AudioMixer::Disconnected(const media_source &producer, const media_destination &where) 387 { 388 // One of our inputs has been disconnected 389 390 // check if it is really belongs to us 391 if (where.port != ControlPort()) { 392 TRACE("AudioMixer::Disconnected wrong input port\n"); 393 return; 394 } 395 396 fCore->Lock(); 397 398 if (!fCore->RemoveInput(where.id)) { 399 TRACE("AudioMixer::Disconnected can't remove input\n"); 400 } 401 402 fCore->Unlock(); 403 UpdateParameterWeb(); 404 } 405 406 status_t 407 AudioMixer::FormatChanged(const media_source &producer, const media_destination &consumer, 408 int32 change_tag, const media_format &format) 409 { 410 // at some point in the future (indicated by change_tag and RequestCompleted()), 411 // we will receive buffers in a different format 412 413 TRACE("AudioMixer::FormatChanged\n"); 414 415 if (consumer.port != ControlPort() || consumer.id == 0) 416 return B_MEDIA_BAD_DESTINATION; 417 418 if (fCore->Settings()->RefuseInputFormatChange()) { 419 TRACE("AudioMixer::FormatChanged: input format change refused\n"); 420 return B_ERROR; 421 } 422 423 // XXX we should not apply the format change at this point 424 425 // tell core about format change 426 fCore->Lock(); 427 fCore->InputFormatChanged(consumer.id, format.u.raw_audio); 428 fCore->Unlock(); 429 430 return B_OK; 431 } 432 433 // 434 // BBufferProducer methods 435 // 436 437 status_t 438 AudioMixer::FormatSuggestionRequested(media_type type, int32 quality, media_format *format) 439 { 440 TRACE("AudioMixer::FormatSuggestionRequested\n"); 441 442 // BBufferProducer function, a downstream consumer 443 // is asking for our output format 444 445 if (type != B_MEDIA_RAW_AUDIO && type != B_MEDIA_UNKNOWN_TYPE) 446 return B_MEDIA_BAD_FORMAT; 447 448 // we can produce any (wildcard) raw audio format 449 memset(format, 0, sizeof(*format)); 450 format->type = B_MEDIA_RAW_AUDIO; 451 return B_OK; 452 } 453 454 status_t 455 AudioMixer::FormatProposal(const media_source &output, media_format *ioFormat) 456 { 457 // BBufferProducer function, we implement this function to verify that the 458 // proposed media_format is suitable for the specified output. If any fields 459 // in the format are wildcards, and we have a specific requirement, adjust 460 // those fields to match our requirements before returning. 461 462 TRACE("AudioMixer::FormatProposal\n"); 463 464 // we only have one output (id=0, port=ControlPort()) 465 if (output.id != 0 || output.port != ControlPort()) 466 return B_MEDIA_BAD_SOURCE; 467 468 // specialize to raw audio format if necessary 469 if (ioFormat->type == B_MEDIA_UNKNOWN_TYPE) 470 ioFormat->type = B_MEDIA_RAW_AUDIO; 471 472 // we require a raw audio format 473 if (ioFormat->type != B_MEDIA_RAW_AUDIO) 474 return B_MEDIA_BAD_FORMAT; 475 476 return B_OK; 477 } 478 479 // If the format isn't good, put a good format into *io_format and return error 480 // If format has wildcard, specialize to what you can do (and change). 481 // If you can change the format, return OK. 482 // The request comes from your destination sychronously, so you cannot ask it 483 // whether it likes it -- you should assume it will since it asked. 484 status_t 485 AudioMixer::FormatChangeRequested(const media_source &source, const media_destination &destination, 486 media_format *io_format, int32 *_deprecated_) 487 { 488 // the downstream consumer node (soundcard) requested that we produce 489 // another format, we need to check if the format is acceptable and 490 // remove any wildcards before returning OK. 491 492 TRACE("AudioMixer::FormatChangeRequested\n"); 493 494 if (fCore->Settings()->RefuseOutputFormatChange()) { 495 TRACE("AudioMixer::FormatChangeRequested: output format change refused\n"); 496 return B_ERROR; 497 } 498 499 fCore->Lock(); 500 501 MixerOutput *output = fCore->Output(); 502 if (!output) { 503 ERROR("AudioMixer::FormatChangeRequested: no output\n"); 504 goto err; 505 } 506 if (source != output->MediaOutput().source) { 507 ERROR("AudioMixer::FormatChangeRequested: wrong output source\n"); 508 goto err; 509 } 510 if (destination != output->MediaOutput().destination) { 511 ERROR("AudioMixer::FormatChangeRequested: wrong output destination (port %ld, id %ld), our is (port %ld, id %ld)\n", destination.port, destination.id, output->MediaOutput().destination.port, output->MediaOutput().destination.id); 512 if (destination.port == output->MediaOutput().destination.port && destination.id == output->MediaOutput().destination.id + 1) { 513 ERROR("AudioMixer::FormatChangeRequested: this might be the broken R5 multi audio add-on\n"); 514 goto err; 515 } else { 516 goto err; 517 } 518 } 519 if (io_format->type != B_MEDIA_RAW_AUDIO && io_format->type != B_MEDIA_UNKNOWN_TYPE) { 520 ERROR("AudioMixer::FormatChangeRequested: wrong format type\n"); 521 goto err; 522 } 523 524 /* remove wildcards */ 525 #if USE_MEDIA_FORMAT_WORKAROUND 526 multi_audio_format_specialize(&io_format->u.raw_audio, &fDefaultFormat.u.raw_audio); 527 #else 528 io_format->SpecializeTo(&fDefaultFormat); 529 #endif 530 531 media_node_id id; 532 FindLatencyFor(destination, &fDownstreamLatency, &id); 533 TRACE("AudioMixer: Downstream Latency is %Ld usecs\n", fDownstreamLatency); 534 535 // SetDuration of one buffer 536 SetBufferDuration(buffer_duration(io_format->u.raw_audio)); 537 TRACE("AudioMixer: buffer duration is %Ld usecs\n", BufferDuration()); 538 539 // Our internal latency is at least the length of a full output buffer 540 fInternalLatency = BufferDuration() + max(4500LL, bigtime_t(0.5 * BufferDuration())); 541 TRACE("AudioMixer: Internal latency is %Ld usecs\n", fInternalLatency); 542 543 SetEventLatency(fDownstreamLatency + fInternalLatency); 544 545 // we need to inform all connected *inputs* about *our* change in latency 546 PublishEventLatencyChange(); 547 548 // XXX we might need to create more buffers, to span a larger downstream latency 549 550 // apply latency change 551 fCore->SetTimingInfo(TimeSource(), fDownstreamLatency); 552 553 // apply format change 554 fCore->OutputFormatChanged(io_format->u.raw_audio); 555 556 delete fBufferGroup; 557 fBufferGroup = CreateBufferGroup(); 558 fCore->SetOutputBufferGroup(fBufferGroup); 559 560 fCore->Unlock(); 561 return B_OK; 562 563 err: 564 fCore->Unlock(); 565 return B_ERROR; 566 } 567 568 status_t 569 AudioMixer::GetNextOutput(int32 *cookie, media_output *out_output) 570 { 571 TRACE("AudioMixer::GetNextOutput\n"); 572 573 if (*cookie != 0) 574 return B_BAD_INDEX; 575 576 fCore->Lock(); 577 MixerOutput *output = fCore->Output(); 578 if (output) { 579 *out_output = output->MediaOutput(); 580 } else { 581 out_output->node = Node(); 582 out_output->source.port = ControlPort(); 583 out_output->source.id = 0; 584 out_output->destination = media_destination::null; 585 memset(&out_output->format, 0, sizeof(out_output->format)); 586 out_output->format.type = B_MEDIA_RAW_AUDIO; 587 strcpy(out_output->name, "Mixer Output"); 588 } 589 fCore->Unlock(); 590 591 *cookie += 1; 592 return B_OK; 593 } 594 595 status_t 596 AudioMixer::DisposeOutputCookie(int32 cookie) 597 { 598 // nothing to do 599 return B_OK; 600 } 601 602 status_t 603 AudioMixer::SetBufferGroup(const media_source &for_source, BBufferGroup *newGroup) 604 { 605 PRINT("AudioMixer::SetBufferGroup\n"); 606 // the downstream consumer (soundcard) node asks us to use another 607 // BBufferGroup (might be NULL). We only have one output (id 0) 608 if (for_source.port != ControlPort() || for_source.id != 0) 609 return B_MEDIA_BAD_SOURCE; 610 611 if (newGroup == fBufferGroup) { 612 // we're already using this buffergroup 613 return B_OK; 614 } 615 616 fCore->Lock(); 617 if (!newGroup) 618 newGroup = CreateBufferGroup(); 619 fCore->SetOutputBufferGroup(newGroup); 620 delete fBufferGroup; 621 fBufferGroup = newGroup; 622 fCore->Unlock(); 623 624 return B_OK; 625 } 626 627 status_t 628 AudioMixer::GetLatency(bigtime_t *out_latency) 629 { 630 // report our *total* latency: internal plus downstream plus scheduling 631 *out_latency = EventLatency() + SchedulingLatency(); 632 633 TRACE("AudioMixer::GetLatency %Ld\n", *out_latency); 634 635 return B_OK; 636 } 637 638 void 639 AudioMixer::LatencyChanged(const media_source & source, const media_destination & destination, 640 bigtime_t new_latency, uint32 flags) 641 { 642 if (source.port != ControlPort() || source.id != 0) { 643 ERROR("AudioMixer::LatencyChanged: received but has wrong source %ld/%ld\n", source.port, source.id); 644 return; 645 } 646 647 TRACE("AudioMixer::LatencyChanged: downstream latency from %ld/%ld to %ld/%ld changed from %Ld to %Ld\n", 648 source.port, source.id, destination.port, destination.id, fDownstreamLatency, new_latency); 649 650 #if DEBUG 651 { 652 media_node_id id; 653 bigtime_t l; 654 FindLatencyFor(destination, &l, &id); 655 TRACE("AudioMixer: Reported downstream Latency is %Ld usecs\n", l); 656 } 657 #endif 658 659 fDownstreamLatency = new_latency; 660 SetEventLatency(fDownstreamLatency + fInternalLatency); 661 662 // XXX we might need to create more buffers, to span a larger downstream latency 663 664 fCore->Lock(); 665 fCore->SetTimingInfo(TimeSource(), fDownstreamLatency); 666 PublishEventLatencyChange(); 667 fCore->Unlock(); 668 } 669 670 status_t 671 AudioMixer::PrepareToConnect(const media_source &what, 672 const media_destination &where, 673 media_format *format, 674 media_source *out_source, 675 char *out_name) 676 { 677 TRACE("AudioMixer::PrepareToConnect\n"); 678 // PrepareToConnect() is the second stage of format negotiations that happens 679 // inside BMediaRoster::Connect(). At this point, the consumer's AcceptFormat() 680 // method has been called, and that node has potentially changed the proposed 681 // format. It may also have left wildcards in the format. PrepareToConnect() 682 // *must* fully specialize the format before returning! 683 // we also create the new output connection and return it in out_source. 684 685 PRINT_FORMAT("AudioMixer::PrepareToConnect: suggested format", *format); 686 687 // avoid loop connections 688 if (where.port == ControlPort()) 689 return B_MEDIA_BAD_SOURCE; 690 691 // is the source valid? 692 if (what.port != ControlPort() || what.id != 0) 693 return B_MEDIA_BAD_SOURCE; 694 695 // is the format acceptable? 696 if (format->type != B_MEDIA_RAW_AUDIO && format->type != B_MEDIA_UNKNOWN_TYPE) { 697 PRINT_FORMAT("AudioMixer::PrepareToConnect: bad format", *format); 698 return B_MEDIA_BAD_FORMAT; 699 } 700 701 fCore->Lock(); 702 703 // are we already connected? 704 if (fCore->Output() != 0) { 705 fCore->Unlock(); 706 ERROR("AudioMixer::PrepareToConnect: already connected\n"); 707 return B_MEDIA_ALREADY_CONNECTED; 708 } 709 710 // It is possible that another mixer is connecting. 711 // To avoid using the default format, we use one of 712 // a) the format that it indicated as hint in the user_data, 713 // b) the output format of the system audio mixer 714 // c) the input format of the system DAC device 715 // d) if everything failes, keep the wildcard 716 if (format->u.raw_audio.channel_count == 0 717 && format->u.raw_audio.frame_rate < 1 718 && format->user_data_type == FORMAT_USER_DATA_TYPE 719 && *(uint32 *)&format->user_data[0] == FORMAT_USER_DATA_MAGIC_1 720 && *(uint32 *)&format->user_data[44] == FORMAT_USER_DATA_MAGIC_2) { 721 // ok, a mixer is connecting 722 uint32 channel_count = *(uint32 *)&format->user_data[4]; 723 float frame_rate = *(float *)&format->user_data[20]; 724 if (channel_count > 0 && frame_rate > 0) { 725 // format is good, use it 726 format->u.raw_audio.channel_count = channel_count; 727 format->u.raw_audio.frame_rate = frame_rate; 728 } else { 729 // other mixer's output is probably not connected 730 media_node node; 731 BMediaRoster *roster = BMediaRoster::Roster(); 732 media_output out; 733 media_input in; 734 int32 count; 735 if (B_OK == roster->GetAudioMixer(&node) && B_OK == roster->GetConnectedOutputsFor(node, &out, 1, &count) && count == 1) { 736 // use mixer output format 737 format->u.raw_audio.channel_count = out.format.u.raw_audio.channel_count; 738 format->u.raw_audio.frame_rate = out.format.u.raw_audio.frame_rate; 739 } else if (B_OK == roster->GetAudioOutput(&node) && B_OK == roster->GetAllInputsFor(node, &in, 1, &count) && count == 1) { 740 // use DAC input format 741 format->u.raw_audio.channel_count = in.format.u.raw_audio.channel_count; 742 format->u.raw_audio.frame_rate = in.format.u.raw_audio.frame_rate; 743 } 744 } 745 } 746 747 /* set source and suggest a name */ 748 *out_source = what; 749 strcpy(out_name, "Mixer Output"); 750 751 /* remove wildcards */ 752 #if USE_MEDIA_FORMAT_WORKAROUND 753 multi_audio_format_specialize(&format->u.raw_audio, &fDefaultFormat.u.raw_audio); 754 #else 755 format->SpecializeTo(&fDefaultFormat); 756 #endif 757 758 PRINT_FORMAT("AudioMixer::PrepareToConnect: final format", *format); 759 760 /* add output to core */ 761 media_output output; 762 output.node = Node(); 763 output.source = *out_source; 764 output.destination = where; 765 output.format = *format; 766 strcpy(output.name, out_name); 767 768 fCore->EnableOutput(false); 769 fCore->AddOutput(output); 770 771 fCore->Unlock(); 772 return B_OK; 773 } 774 775 void 776 AudioMixer::Connect(status_t error, const media_source &source, 777 const media_destination &dest, 778 const media_format &format, 779 char *io_name) 780 { 781 TRACE("AudioMixer::Connect\n"); 782 783 fCore->Lock(); 784 // are we still connected? 785 if (fCore->Output() == 0) { 786 fCore->Unlock(); 787 ERROR("AudioMixer::Connect: no longer connected\n"); 788 return; 789 } 790 fCore->Unlock(); 791 792 if (error != B_OK) { 793 // if an error occured, remove output from core 794 ERROR("AudioMixer::Connect failed with error 0x%08lX, removing connection\n", error); 795 fCore->Lock(); 796 fCore->RemoveOutput(); 797 fCore->Unlock(); 798 return; 799 } 800 801 /* Switch our prefered format to have the same 802 * frame_rate and channel count as the output. 803 */ 804 fDefaultFormat.u.raw_audio.frame_rate = format.u.raw_audio.frame_rate; 805 fDefaultFormat.u.raw_audio.channel_count = format.u.raw_audio.channel_count; 806 807 // if the connection has no name, we set it now 808 if (strlen(io_name) == 0) 809 strcpy(io_name, "Mixer Output"); 810 811 // Now that we're connected, we can determine our downstream latency. 812 media_node_id id; 813 FindLatencyFor(dest, &fDownstreamLatency, &id); 814 TRACE("AudioMixer: Downstream Latency is %Ld usecs\n", fDownstreamLatency); 815 816 // SetDuration of one buffer 817 SetBufferDuration(buffer_duration(format.u.raw_audio)); 818 TRACE("AudioMixer: buffer duration is %Ld usecs\n", BufferDuration()); 819 820 // Our internal latency is at least the length of a full output buffer 821 fInternalLatency = BufferDuration() + max(4500LL, bigtime_t(0.5 * BufferDuration())); 822 TRACE("AudioMixer: Internal latency is %Ld usecs\n", fInternalLatency); 823 824 SetEventLatency(fDownstreamLatency + fInternalLatency); 825 826 // we need to inform all connected *inputs* about *our* change in latency 827 PublishEventLatencyChange(); 828 829 // Set up the buffer group for our connection, as long as nobody handed us a 830 // buffer group (via SetBufferGroup()) prior to this. That can happen, for example, 831 // if the consumer calls SetOutputBuffersFor() on us from within its Connected() 832 // method. 833 if (!fBufferGroup) 834 fBufferGroup = CreateBufferGroup(); 835 836 fCore->Lock(); 837 838 ASSERT(fCore->Output() != 0); 839 840 // our source should still be valid, too 841 ASSERT(fCore->Output()->MediaOutput().source.id == 0); 842 ASSERT(fCore->Output()->MediaOutput().source.port == ControlPort()); 843 844 // BBufferConsumer::Connected() may return a different input for the 845 // newly created connection. The destination can have changed since 846 // AudioMixer::PrepareToConnect() and we need to update it. 847 fCore->Output()->MediaOutput().destination = dest; 848 849 fCore->EnableOutput(true); 850 fCore->SetTimingInfo(TimeSource(), fDownstreamLatency); 851 fCore->SetOutputBufferGroup(fBufferGroup); 852 853 fCore->Settings()->LoadConnectionSettings(fCore->Output()); 854 855 fCore->Unlock(); 856 UpdateParameterWeb(); 857 } 858 859 void 860 AudioMixer::Disconnect(const media_source &what, const media_destination &where) 861 { 862 TRACE("AudioMixer::Disconnect\n"); 863 fCore->Lock(); 864 865 // Make sure that our connection is the one being disconnected 866 MixerOutput * output = fCore->Output(); 867 if (!output || output->MediaOutput().node != Node() || output->MediaOutput().source != what || output->MediaOutput().destination != where) { 868 ERROR("AudioMixer::Disconnect can't disconnect (wrong connection)\n"); 869 fCore->Unlock(); 870 return; 871 } 872 873 // Switch our prefered format back to default 874 // frame rate and channel count. 875 fDefaultFormat.u.raw_audio.frame_rate = 96000; 876 fDefaultFormat.u.raw_audio.channel_count = 2; 877 878 // force a stop 879 fCore->Stop(); 880 881 fCore->RemoveOutput(); 882 883 // destroy buffer group 884 delete fBufferGroup; 885 fBufferGroup = 0; 886 fCore->SetOutputBufferGroup(0); 887 888 fCore->Unlock(); 889 UpdateParameterWeb(); 890 } 891 892 893 void 894 AudioMixer::LateNoticeReceived(const media_source &what, bigtime_t how_much, bigtime_t performance_time) 895 { 896 // We've produced some late buffers... Increase Latency 897 // is the only runmode in which we can do anything about this 898 899 ERROR("AudioMixer::LateNoticeReceived, %Ld too late at %Ld\n", how_much, performance_time); 900 // Note: The following code is outcommented on purpose 901 // and is about to be modified at a later point 902 /* 903 if (what == fOutput.source) { 904 if (RunMode() == B_INCREASE_LATENCY) { 905 fInternalLatency += how_much; 906 907 if (fInternalLatency > 50000) 908 fInternalLatency = 50000; 909 910 printf("AudioMixer: increasing internal latency to %Ld usec\n", fInternalLatency); 911 SetEventLatency(fDownstreamLatency + fInternalLatency); 912 913 PublishEventLatencyChange(); 914 } 915 } 916 */ 917 } 918 919 920 void 921 AudioMixer::EnableOutput(const media_source &what, bool enabled, int32 *_deprecated_) 922 { 923 // we only have one output 924 if (what.id != 0 || what.port != ControlPort()) 925 return; 926 927 fCore->Lock(); 928 fCore->EnableOutput(enabled); 929 fCore->Unlock(); 930 } 931 932 933 // 934 // BMediaEventLooper methods 935 // 936 937 void 938 AudioMixer::NodeRegistered() 939 { 940 Run(); 941 SetPriority(120); 942 UpdateParameterWeb(); 943 } 944 945 void 946 AudioMixer::SetTimeSource(BTimeSource * time_source) 947 { 948 TRACE("AudioMixer::SetTimeSource: timesource is now %ld\n", time_source->ID()); 949 fCore->Lock(); 950 fCore->SetTimingInfo(time_source, fDownstreamLatency); 951 fCore->Unlock(); 952 } 953 954 void 955 AudioMixer::HandleEvent(const media_timed_event *event, bigtime_t lateness, bool realTimeEvent) 956 { 957 switch (event->type) 958 { 959 960 case BTimedEventQueue::B_HANDLE_BUFFER: 961 { 962 HandleInputBuffer((BBuffer *)event->pointer, lateness); 963 ((BBuffer *)event->pointer)->Recycle(); 964 break; 965 } 966 967 case BTimedEventQueue::B_START: 968 { 969 TRACE("AudioMixer::HandleEvent: B_START\n"); 970 if (RunState() != B_STARTED) { 971 fCore->Lock(); 972 fCore->Start(); 973 fCore->Unlock(); 974 } 975 break; 976 } 977 978 case BTimedEventQueue::B_STOP: 979 { 980 TRACE("AudioMixer::HandleEvent: B_STOP\n"); 981 // stopped - don't process any more buffers, flush all buffers from eventqueue 982 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); 983 fCore->Lock(); 984 fCore->Stop(); 985 fCore->Unlock(); 986 break; 987 } 988 989 case BTimedEventQueue::B_DATA_STATUS: 990 { 991 ERROR("DataStatus message\n"); 992 break; 993 } 994 995 default: 996 break; 997 998 } 999 } 1000 1001 // 1002 // AudioMixer methods 1003 // 1004 1005 void 1006 AudioMixer::PublishEventLatencyChange() 1007 { 1008 // our event (processing + downstream) latency has changed, 1009 // and we need tell all inputs about this 1010 1011 TRACE("AudioMixer::PublishEventLatencyChange\n"); 1012 1013 fCore->Lock(); 1014 1015 MixerInput *input; 1016 for (int i = 0; (input = fCore->Input(i)) != 0; i++) { 1017 TRACE("AudioMixer::PublishEventLatencyChange: SendLatencyChange, connection %ld/%ld to %ld/%ld event latency is now %Ld\n", 1018 input->MediaInput().source.port, input->MediaInput().source.id, 1019 input->MediaInput().destination.port, input->MediaInput().destination.id, 1020 EventLatency()); 1021 SendLatencyChange(input->MediaInput().source, input->MediaInput().destination, EventLatency()); 1022 } 1023 1024 fCore->Unlock(); 1025 } 1026 1027 BBufferGroup * 1028 AudioMixer::CreateBufferGroup() 1029 { 1030 // allocate enough buffers to span our downstream latency 1031 // (plus one for rounding up), plus one extra 1032 int32 count = int32(fDownstreamLatency / BufferDuration()) + 2; 1033 1034 TRACE("AudioMixer::CreateBufferGroup: fDownstreamLatency %Ld, BufferDuration %Ld, buffer count = %ld\n", fDownstreamLatency, BufferDuration(), count); 1035 1036 if (count < 3) 1037 count = 3; 1038 1039 fCore->Lock(); 1040 uint32 size = fCore->Output()->MediaOutput().format.u.raw_audio.buffer_size; 1041 fCore->Unlock(); 1042 1043 TRACE("AudioMixer: allocating %ld buffers of %ld bytes each\n", count, size); 1044 return new BBufferGroup(size, count); 1045 } 1046 1047 float 1048 AudioMixer::dB_to_Gain(float db) 1049 { 1050 TRACE("dB_to_Gain: dB in: %01.2f ", db); 1051 if (fCore->Settings()->NonLinearGainSlider()) { 1052 if (db > 0) { 1053 db = db * (pow(abs(DB_MAX), (1.0 / DB_EXPONENT_POSITIVE)) / abs(DB_MAX)); 1054 db = pow(db, DB_EXPONENT_POSITIVE); 1055 } else { 1056 db = -db; 1057 db = db * (pow(abs(DB_MIN), (1.0 / DB_EXPONENT_NEGATIVE)) / abs(DB_MIN)); 1058 db = pow(db, DB_EXPONENT_NEGATIVE); 1059 db = -db; 1060 } 1061 } 1062 TRACE("dB out: %01.2f\n", db); 1063 return pow(10.0, db / 20.0); 1064 } 1065 1066 float 1067 AudioMixer::Gain_to_dB(float gain) 1068 { 1069 float db; 1070 db = 20.0 * log10(gain); 1071 if (fCore->Settings()->NonLinearGainSlider()) { 1072 if (db > 0) { 1073 db = pow(db, (1.0 / DB_EXPONENT_POSITIVE)); 1074 db = db * (abs(DB_MAX) / pow(abs(DB_MAX), (1.0 / DB_EXPONENT_POSITIVE))); 1075 } else { 1076 db = -db; 1077 db = pow(db, (1.0 / DB_EXPONENT_NEGATIVE)); 1078 db = db * (abs(DB_MIN) / pow(abs(DB_MIN), (1.0 / DB_EXPONENT_NEGATIVE))); 1079 db = -db; 1080 } 1081 } 1082 return db; 1083 } 1084 1085 // 1086 // BControllable methods 1087 // 1088 1089 #define DB_TO_GAIN(db) dB_to_Gain((db)) 1090 #define GAIN_TO_DB(gain) Gain_to_dB((gain)) 1091 #define PERCENT_TO_GAIN(pct) ((pct) / 100.0) 1092 #define GAIN_TO_PERCENT(gain) ((gain) * 100.0) 1093 1094 // the id is encoded with 16 bits 1095 // then chan and src (or dst) are encoded with 6 bits 1096 // the unique number with 4 bits 1097 // the PARAM_ETC etc is encoded with 26 bits 1098 #define PARAM_SRC_ENABLE(id, chan, src) (((id) << 16) | ((chan) << 10) | ((src) << 4) | 0x1) 1099 #define PARAM_SRC_GAIN(id, chan, src) (((id) << 16) | ((chan) << 10) | ((src) << 4) | 0x2) 1100 #define PARAM_DST_ENABLE(id, chan, dst) (((id) << 16) | ((chan) << 10) | ((dst) << 4) | 0x3) 1101 #define PARAM_ETC(etc) (((etc) << 16) | 0x4) 1102 #define PARAM_SRC_STR(id, chan) (((id) << 16) | ((chan) << 10) | 0x5) 1103 #define PARAM_DST_STR(id, chan) (((id) << 16) | ((chan) << 10) | 0x6) 1104 #define PARAM_MUTE(id) (((id) << 16) | 0x7) 1105 #define PARAM_GAIN(id) (((id) << 16) | 0x8) 1106 #define PARAM_BALANCE(id) (((id) << 16) | 0x9) 1107 #define PARAM_STR1(id) (((id) << 16) | ((1) << 10) | 0xa) 1108 #define PARAM_STR2(id) (((id) << 16) | ((2) << 10) | 0xa) 1109 #define PARAM_STR3(id) (((id) << 16) | ((3) << 10) | 0xa) 1110 #define PARAM_STR4(id) (((id) << 16) | ((4) << 10) | 0xa) 1111 #define PARAM_STR5(id) (((id) << 16) | ((5) << 10) | 0xa) 1112 #define PARAM_STR6(id) (((id) << 16) | ((6) << 10) | 0xa) 1113 #define PARAM_STR7(id) (((id) << 16) | ((7) << 10) | 0xa) 1114 1115 #define PARAM(id) ((id) >> 16) 1116 #define ETC(id) ((id) >> 16) 1117 #define PARAM_CHAN(id) (((id) >> 10) & 0x3f) 1118 #define PARAM_SRC(id) (((id) >> 4) & 0x3f) 1119 #define PARAM_DST(id) (((id) >> 4) & 0x3f) 1120 #define PARAM_IS_SRC_ENABLE(id) (((id) & 0xf) == 0x1) 1121 #define PARAM_IS_SRC_GAIN(id) (((id) & 0xf) == 0x2) 1122 #define PARAM_IS_DST_ENABLE(id) (((id) & 0xf) == 0x3) 1123 #define PARAM_IS_ETC(id) (((id) & 0xf) == 0x4) 1124 #define PARAM_IS_MUTE(id) (((id) & 0xf) == 0x7) 1125 #define PARAM_IS_GAIN(id) (((id) & 0xf) == 0x8) 1126 #define PARAM_IS_BALANCE(id) (((id) & 0xf) == 0x9) 1127 1128 1129 status_t 1130 AudioMixer::GetParameterValue(int32 id, bigtime_t *last_change, 1131 void *value, size_t *ioSize) 1132 { 1133 TRACE("GetParameterValue: id 0x%08lx, ioSize %ld\n", id, *ioSize); 1134 int param = PARAM(id); 1135 fCore->Lock(); 1136 if (PARAM_IS_ETC(id)) { 1137 switch (ETC(id)) { 1138 case 10: // Attenuate mixer output by 3dB 1139 *ioSize = sizeof(int32); 1140 static_cast<int32 *>(value)[0] = fCore->Settings()->AttenuateOutput(); 1141 break; 1142 case 20: // Use non linear gain sliders 1143 *ioSize = sizeof(int32); 1144 static_cast<int32 *>(value)[0] = fCore->Settings()->NonLinearGainSlider(); 1145 break; 1146 case 30: // Display balance control for stereo connections 1147 *ioSize = sizeof(int32); 1148 static_cast<int32 *>(value)[0] = fCore->Settings()->UseBalanceControl(); 1149 break; 1150 case 40: // Allow output channel remapping 1151 *ioSize = sizeof(int32); 1152 static_cast<int32 *>(value)[0] = fCore->Settings()->AllowOutputChannelRemapping(); 1153 break; 1154 case 50: // Allow input channel remapping 1155 *ioSize = sizeof(int32); 1156 static_cast<int32 *>(value)[0] = fCore->Settings()->AllowInputChannelRemapping(); 1157 break; 1158 case 60: // Input gain controls 1159 *ioSize = sizeof(int32); 1160 static_cast<int32 *>(value)[0] = fCore->Settings()->InputGainControls(); 1161 break; 1162 case 70: // Resampling algorithm 1163 *ioSize = sizeof(int32); 1164 static_cast<int32 *>(value)[0] = fCore->Settings()->ResamplingAlgorithm(); 1165 break; 1166 case 80: // Refuse output format changes 1167 *ioSize = sizeof(int32); 1168 static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseOutputFormatChange(); 1169 break; 1170 case 90: // Refuse input format changes 1171 *ioSize = sizeof(int32); 1172 static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseInputFormatChange(); 1173 break; 1174 default: 1175 ERROR("unhandled ETC 0x%08lx\n", id); 1176 break; 1177 } 1178 } else if (param == 0) { 1179 MixerOutput *output = fCore->Output(); 1180 if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id))) 1181 goto err; 1182 if (PARAM_IS_MUTE(id)) { 1183 // output mute control 1184 if (*ioSize < sizeof(int32)) 1185 goto err; 1186 *ioSize = sizeof(int32); 1187 static_cast<int32 *>(value)[0] = output->IsMuted(); 1188 } 1189 if (PARAM_IS_GAIN(id)) { 1190 // output gain control 1191 if (fCore->Settings()->UseBalanceControl() && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1192 // single channel control + balance 1193 if (*ioSize < sizeof(float)) 1194 goto err; 1195 *ioSize = sizeof(float); 1196 static_cast<float *>(value)[0] = GAIN_TO_DB((output->GetOutputChannelGain(0) + output->GetOutputChannelGain(1)) / 2); 1197 } else { 1198 // multi channel control 1199 if (*ioSize < output->GetOutputChannelCount() * sizeof(float)) 1200 goto err; 1201 *ioSize = output->GetOutputChannelCount() * sizeof(float); 1202 for (int chan = 0; chan < output->GetOutputChannelCount(); chan++) 1203 static_cast<float *>(value)[chan] = GAIN_TO_DB(output->GetOutputChannelGain(chan)); 1204 } 1205 } 1206 if (PARAM_IS_BALANCE(id)) { 1207 float l = output->GetOutputChannelGain(0); 1208 float r = output->GetOutputChannelGain(1); 1209 float v = r / (l+r); 1210 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v); 1211 if (*ioSize < sizeof(float)) 1212 goto err; 1213 *ioSize = sizeof(float); 1214 static_cast<float *>(value)[0] = v * 100; 1215 } 1216 if (PARAM_IS_SRC_ENABLE(id)) { 1217 if (*ioSize < sizeof(int32)) 1218 goto err; 1219 *ioSize = sizeof(int32); 1220 static_cast<int32 *>(value)[0] = output->HasOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id)); 1221 } 1222 if (PARAM_IS_SRC_GAIN(id)) { 1223 if (*ioSize < sizeof(float)) 1224 goto err; 1225 *ioSize = sizeof(float); 1226 static_cast<float *>(value)[0] = GAIN_TO_PERCENT(output->GetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id))); 1227 } 1228 } else { 1229 MixerInput *input; 1230 for (int i = 0; (input = fCore->Input(i)); i++) 1231 if (input->ID() == param) 1232 break; 1233 if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id))) 1234 goto err; 1235 if (PARAM_IS_MUTE(id)) { 1236 // input mute control 1237 if (*ioSize < sizeof(int32)) 1238 goto err; 1239 *ioSize = sizeof(int32); 1240 static_cast<int32 *>(value)[0] = !input->IsEnabled(); 1241 } 1242 if (PARAM_IS_GAIN(id)) { 1243 // input gain control 1244 if (fCore->Settings()->InputGainControls() == 0) { 1245 // Physical input channels 1246 if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1247 // single channel control + balance 1248 if (*ioSize < sizeof(float)) 1249 goto err; 1250 *ioSize = sizeof(float); 1251 static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetInputChannelGain(0) + input->GetInputChannelGain(1)) / 2); 1252 } else { 1253 // multi channel control 1254 if (*ioSize < input->GetInputChannelCount() * sizeof(float)) 1255 goto err; 1256 *ioSize = input->GetInputChannelCount() * sizeof(float); 1257 for (int chan = 0; chan < input->GetInputChannelCount(); chan++) 1258 static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetInputChannelGain(chan)); 1259 } 1260 } else { 1261 // Virtual output channels 1262 if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) { 1263 // single channel control + balance 1264 if (*ioSize < sizeof(float)) 1265 goto err; 1266 *ioSize = sizeof(float); 1267 static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetMixerChannelGain(0) + input->GetMixerChannelGain(1)) / 2); 1268 } else { 1269 // multi channel control 1270 if (*ioSize < input->GetMixerChannelCount() * sizeof(float)) 1271 goto err; 1272 *ioSize = input->GetMixerChannelCount() * sizeof(float); 1273 for (int chan = 0; chan < input->GetMixerChannelCount(); chan++) 1274 static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetMixerChannelGain(chan)); 1275 } 1276 } 1277 } 1278 if (PARAM_IS_BALANCE(id)) { 1279 if (fCore->Settings()->InputGainControls() == 0) { 1280 // Physical input channels 1281 float l = input->GetInputChannelGain(0); 1282 float r = input->GetInputChannelGain(1); 1283 float v = r / (l+r); 1284 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v); 1285 if (*ioSize < sizeof(float)) 1286 goto err; 1287 *ioSize = sizeof(float); 1288 static_cast<float *>(value)[0] = v * 100; 1289 } else { 1290 // Virtual output channels 1291 float l = input->GetMixerChannelGain(0); 1292 float r = input->GetMixerChannelGain(1); 1293 float v = r / (l+r); 1294 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v); 1295 if (*ioSize < sizeof(float)) 1296 goto err; 1297 *ioSize = sizeof(float); 1298 static_cast<float *>(value)[0] = v * 100; 1299 } 1300 } 1301 if (PARAM_IS_DST_ENABLE(id)) { 1302 if (*ioSize < sizeof(int32)) 1303 goto err; 1304 *ioSize = sizeof(int32); 1305 static_cast<int32 *>(value)[0] = input->HasInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id)); 1306 } 1307 } 1308 *last_change = TimeSource()->Now(); // XXX we could do better 1309 fCore->Unlock(); 1310 return B_OK; 1311 err: 1312 fCore->Unlock(); 1313 return B_ERROR; 1314 } 1315 1316 void 1317 AudioMixer::SetParameterValue(int32 id, bigtime_t when, 1318 const void *value, size_t size) 1319 { 1320 TRACE("SetParameterValue: id 0x%08lx, size %ld\n", id, size); 1321 bool update = false; 1322 int param = PARAM(id); 1323 fCore->Lock(); 1324 if (PARAM_IS_ETC(id)) { 1325 switch (ETC(id)) { 1326 case 10: // Attenuate mixer output by 3dB 1327 if (size != sizeof(int32)) 1328 goto err; 1329 fCore->Settings()->SetAttenuateOutput(static_cast<const int32 *>(value)[0]); 1330 // this value is special (see MixerCore.h) and we need to notify the core 1331 fCore->SetOutputAttenuation((static_cast<const int32 *>(value)[0]) ? 0.708 : 1.0); 1332 break; 1333 case 20: // Use non linear gain sliders 1334 if (size != sizeof(int32)) 1335 goto err; 1336 fCore->Settings()->SetNonLinearGainSlider(static_cast<const int32 *>(value)[0]); 1337 update = true; // XXX should use BroadcastChangedParameter() 1338 break; 1339 case 30: // Display balance control for stereo connections 1340 if (size != sizeof(int32)) 1341 goto err; 1342 fCore->Settings()->SetUseBalanceControl(static_cast<const int32 *>(value)[0]); 1343 update = true; 1344 break; 1345 case 40: // Allow output channel remapping 1346 if (size != sizeof(int32)) 1347 goto err; 1348 fCore->Settings()->SetAllowOutputChannelRemapping(static_cast<const int32 *>(value)[0]); 1349 update = true; 1350 break; 1351 case 50: // Allow input channel remapping 1352 if (size != sizeof(int32)) 1353 goto err; 1354 fCore->Settings()->SetAllowInputChannelRemapping(static_cast<const int32 *>(value)[0]); 1355 update = true; 1356 break; 1357 case 60: // Input gain controls represent 1358 // (0, "Physical input channels") 1359 // (1, "Virtual output channels") 1360 if (size != sizeof(int32)) 1361 goto err; 1362 fCore->Settings()->SetInputGainControls(static_cast<const int32 *>(value)[0]); 1363 update = true; // XXX should use BroadcastChangedParameter() 1364 break; 1365 case 70: // Resampling algorithm 1366 if (size != sizeof(int32)) 1367 goto err; 1368 fCore->Settings()->SetResamplingAlgorithm(static_cast<const int32 *>(value)[0]); 1369 // XXX tell the core to change the algorithm 1370 break; 1371 case 80: // Refuse output format changes 1372 if (size != sizeof(int32)) 1373 goto err; 1374 fCore->Settings()->SetRefuseOutputFormatChange(static_cast<const int32 *>(value)[0]); 1375 break; 1376 case 90: // Refuse input format changes 1377 if (size != sizeof(int32)) 1378 goto err; 1379 fCore->Settings()->SetRefuseInputFormatChange(static_cast<const int32 *>(value)[0]); 1380 break; 1381 default: 1382 ERROR("unhandled ETC 0x%08lx\n", id); 1383 break; 1384 } 1385 } else if (param == 0) { 1386 MixerOutput *output = fCore->Output(); 1387 if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id))) 1388 goto err; 1389 if (PARAM_IS_MUTE(id)) { 1390 // output mute control 1391 if (size != sizeof(int32)) 1392 goto err; 1393 output->SetMuted(static_cast<const int32 *>(value)[0]); 1394 } 1395 if (PARAM_IS_GAIN(id)) { 1396 // output gain control 1397 if (fCore->Settings()->UseBalanceControl() && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1398 // single channel control + balance 1399 float l = output->GetOutputChannelGain(0); 1400 float r = output->GetOutputChannelGain(1); 1401 float m = (l + r) / 2; // master volume 1402 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]); 1403 float f = v / m; // factor for both channels 1404 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f); 1405 output->SetOutputChannelGain(0, output->GetOutputChannelGain(0) * f); 1406 output->SetOutputChannelGain(1, output->GetOutputChannelGain(1) * f); 1407 } else { 1408 // multi channel control 1409 if (size < output->GetOutputChannelCount() * sizeof(float)) 1410 goto err; 1411 for (int chan = 0; chan < output->GetOutputChannelCount(); chan++) 1412 output->SetOutputChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan])); 1413 } 1414 } 1415 if (PARAM_IS_BALANCE(id)) { 1416 float l = output->GetOutputChannelGain(0); 1417 float r = output->GetOutputChannelGain(1); 1418 float m = (l + r) / 2; // master volume 1419 float v = static_cast<const float *>(value)[0] / 100; // current balance value 1420 float fl = 2 * (1 - v); // left channel factor of master volume 1421 float fr = 2 * v; // right channel factor of master volume 1422 TRACE("balance set l %1.3f, r %1.3f, m %1.3f, v %1.3f, fl %1.3f, fr %1.3f\n",l,r,m,v,fl,fr); 1423 output->SetOutputChannelGain(0, m * fl); 1424 output->SetOutputChannelGain(1, m * fr); 1425 } 1426 if (PARAM_IS_SRC_ENABLE(id)) { 1427 if (size != sizeof(int32)) 1428 goto err; 1429 if (static_cast<const int32 *>(value)[0]) { 1430 output->AddOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id)); 1431 } else { 1432 output->RemoveOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id)); 1433 } 1434 } 1435 if (PARAM_IS_SRC_GAIN(id)) { 1436 if (size != sizeof(float)) 1437 goto err; 1438 output->SetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id), PERCENT_TO_GAIN(static_cast<const float *>(value)[0])); 1439 } 1440 fCore->Settings()->SaveConnectionSettings(output); 1441 } else { 1442 MixerInput *input; 1443 for (int i = 0; (input = fCore->Input(i)); i++) 1444 if (input->ID() == param) 1445 break; 1446 if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id))) 1447 goto err; 1448 if (PARAM_IS_MUTE(id)) { 1449 // input mute control 1450 if (size != sizeof(int32)) 1451 goto err; 1452 input->SetEnabled(!static_cast<const int32 *>(value)[0]); 1453 } 1454 if (PARAM_IS_GAIN(id)) { 1455 // input gain control 1456 if (fCore->Settings()->InputGainControls() == 0) { 1457 // Physical input channels 1458 if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1459 // single channel control + balance 1460 float l = input->GetInputChannelGain(0); 1461 float r = input->GetInputChannelGain(1); 1462 float m = (l + r) / 2; // master volume 1463 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]); 1464 float f = v / m; // factor for both channels 1465 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f); 1466 input->SetInputChannelGain(0, input->GetInputChannelGain(0) * f); 1467 input->SetInputChannelGain(1, input->GetInputChannelGain(1) * f); 1468 } else { 1469 // multi channel control 1470 if (size < input->GetInputChannelCount() * sizeof(float)) 1471 goto err; 1472 for (int chan = 0; chan < input->GetInputChannelCount(); chan++) 1473 input->SetInputChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan])); 1474 } 1475 } else { 1476 // Virtual output channels 1477 if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) { 1478 // single channel control + balance 1479 float l = input->GetMixerChannelGain(0); 1480 float r = input->GetMixerChannelGain(1); 1481 float m = (l + r) / 2; // master volume 1482 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]); 1483 float f = v / m; // factor for both channels 1484 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f); 1485 input->SetMixerChannelGain(0, input->GetMixerChannelGain(0) * f); 1486 input->SetMixerChannelGain(1, input->GetMixerChannelGain(1) * f); 1487 } else { 1488 // multi channel control 1489 if (size < input->GetMixerChannelCount() * sizeof(float)) 1490 goto err; 1491 for (int chan = 0; chan < input->GetMixerChannelCount(); chan++) 1492 input->SetMixerChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan])); 1493 } 1494 } 1495 } 1496 if (PARAM_IS_BALANCE(id)) { 1497 if (fCore->Settings()->InputGainControls() == 0) { 1498 // Physical input channels 1499 float l = input->GetInputChannelGain(0); 1500 float r = input->GetInputChannelGain(1); 1501 float m = (l + r) / 2; // master volume 1502 float v = static_cast<const float *>(value)[0] / 100; // current balance value 1503 float fl = 2 * (1 - v); // left channel factor of master volume 1504 float fr = 2 * v; // right channel factor of master volume 1505 TRACE("balance set l %1.3f, r %1.3f, m %1.3f, v %1.3f, fl %1.3f, fr %1.3f\n",l,r,m,v,fl,fr); 1506 input->SetInputChannelGain(0, m * fl); 1507 input->SetInputChannelGain(1, m * fr); 1508 } else { 1509 // Virtual output channels 1510 float l = input->GetMixerChannelGain(0); 1511 float r = input->GetMixerChannelGain(1); 1512 float m = (l + r) / 2; // master volume 1513 float v = static_cast<const float *>(value)[0] / 100; // current balance value 1514 float fl = 2 * (1 - v); // left channel factor of master volume 1515 float fr = 2 * v; // right channel factor of master volume 1516 TRACE("balance set l %1.3f, r %1.3f, m %1.3f, v %1.3f, fl %1.3f, fr %1.3f\n",l,r,m,v,fl,fr); 1517 input->SetMixerChannelGain(0, m * fl); 1518 input->SetMixerChannelGain(1, m * fr); 1519 } 1520 } 1521 if (PARAM_IS_DST_ENABLE(id)) { 1522 if (size != sizeof(int32)) 1523 goto err; 1524 if (static_cast<const int32 *>(value)[0]) { 1525 int oldchan = input->GetInputChannelForDestination(PARAM_DST(id)); 1526 if (oldchan != -1) { 1527 input->RemoveInputChannelDestination(oldchan, PARAM_DST(id)); 1528 int32 null = 0; 1529 BroadcastNewParameterValue(when, PARAM_DST_ENABLE(PARAM(id), oldchan, PARAM_DST(id)), &null, sizeof(null)); 1530 } 1531 input->AddInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id)); 1532 } else { 1533 input->RemoveInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id)); 1534 } 1535 // XXX this is really annoying 1536 // The slider count of the gain control needs to be changed, 1537 // but calling SetChannelCount(input->GetMixerChannelCount()) 1538 // on it has no effect on remote BParameterWebs in other apps. 1539 // BroadcastChangedParameter() should be correct, but doesn't work 1540 BroadcastChangedParameter(PARAM_GAIN(PARAM(id))); 1541 // We trigger a complete ParameterWeb update as workaround 1542 // but it will change the focus from tab 3 to tab 1 1543 update = true; 1544 } 1545 fCore->Settings()->SaveConnectionSettings(input); 1546 } 1547 BroadcastNewParameterValue(when, id, const_cast<void *>(value), size); 1548 err: 1549 fCore->Unlock(); 1550 if (update) 1551 UpdateParameterWeb(); 1552 } 1553 1554 void 1555 AudioMixer::UpdateParameterWeb() 1556 { 1557 fCore->Lock(); 1558 BParameterWeb *web = new BParameterWeb(); 1559 BParameterGroup *top; 1560 BParameterGroup *outputchannels; 1561 BParameterGroup *inputchannels; 1562 BParameterGroup *group; 1563 BParameterGroup *subgroup; 1564 BParameterGroup *subsubgroup; 1565 BDiscreteParameter *dp; 1566 MixerInput *in; 1567 MixerOutput *out; 1568 char buf[50]; 1569 1570 top = web->MakeGroup("Gain Controls"); 1571 1572 out = fCore->Output(); 1573 group = top->MakeGroup(""); 1574 group->MakeNullParameter(PARAM_STR1(0), B_MEDIA_RAW_AUDIO, "Master Output", B_WEB_BUFFER_INPUT); 1575 if (!out) { 1576 group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO, "not connected", B_GENERIC); 1577 } else { 1578 group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO, StringForFormat(buf, out), B_GENERIC); 1579 group->MakeDiscreteParameter(PARAM_MUTE(0), B_MEDIA_RAW_AUDIO, "Mute", B_MUTE); 1580 if (fCore->Settings()->UseBalanceControl() && out->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1581 // single channel control + balance 1582 group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO, "Gain", B_MASTER_GAIN, "dB", DB_MIN, DB_MAX, 0.1); 1583 group->MakeContinuousParameter(PARAM_BALANCE(0), B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1); 1584 } else { 1585 // multi channel control 1586 group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO, "Gain", B_MASTER_GAIN, "dB", DB_MIN, DB_MAX, 0.1) 1587 ->SetChannelCount(out->GetOutputChannelCount()); 1588 } 1589 group->MakeNullParameter(PARAM_STR3(0), B_MEDIA_RAW_AUDIO, "To Output", B_WEB_BUFFER_OUTPUT); 1590 } 1591 1592 for (int i = 0; (in = fCore->Input(i)); i++) { 1593 group = top->MakeGroup(""); 1594 group->MakeNullParameter(PARAM_STR1(in->ID()), B_MEDIA_RAW_AUDIO, in->MediaInput().name, B_WEB_BUFFER_INPUT); 1595 group->MakeNullParameter(PARAM_STR2(in->ID()), B_MEDIA_RAW_AUDIO, StringForFormat(buf, in), B_GENERIC); 1596 group->MakeDiscreteParameter(PARAM_MUTE(in->ID()), B_MEDIA_RAW_AUDIO, "Mute", B_MUTE); 1597 // XXX the gain control is ugly once you have more than two channels, 1598 // as you don't know what channel each slider controls. Tooltips might help... 1599 if (fCore->Settings()->InputGainControls() == 0) { 1600 // Physical input channels 1601 if (fCore->Settings()->UseBalanceControl() && in->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1602 // single channel control + balance 1603 group->MakeContinuousParameter(PARAM_GAIN(in->ID()), B_MEDIA_RAW_AUDIO, "Gain", B_GAIN, "dB", DB_MIN, DB_MAX, 0.1); 1604 group->MakeContinuousParameter(PARAM_BALANCE(in->ID()), B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1); 1605 } else { 1606 // multi channel control 1607 group->MakeContinuousParameter(PARAM_GAIN(in->ID()), B_MEDIA_RAW_AUDIO, "Gain", B_GAIN, "dB", DB_MIN, DB_MAX, 0.1) 1608 ->SetChannelCount(in->GetInputChannelCount()); 1609 } 1610 } else { 1611 // Virtual output channels 1612 if (fCore->Settings()->UseBalanceControl() && in->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) { 1613 // single channel control + balance 1614 group->MakeContinuousParameter(PARAM_GAIN(in->ID()), B_MEDIA_RAW_AUDIO, "Gain", B_GAIN, "dB", DB_MIN, DB_MAX, 0.1); 1615 group->MakeContinuousParameter(PARAM_BALANCE(in->ID()), B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1); 1616 } else { 1617 // multi channel control 1618 group->MakeContinuousParameter(PARAM_GAIN(in->ID()), B_MEDIA_RAW_AUDIO, "Gain", B_GAIN, "dB", DB_MIN, DB_MAX, 0.1) 1619 ->SetChannelCount(in->GetMixerChannelCount()); 1620 } 1621 } 1622 group->MakeNullParameter(PARAM_STR3(in->ID()), B_MEDIA_RAW_AUDIO, "To Master", B_WEB_BUFFER_OUTPUT); 1623 } 1624 1625 if (fCore->Settings()->AllowOutputChannelRemapping()) { 1626 top = web->MakeGroup("Output Mapping"); // top level group 1627 outputchannels = top->MakeGroup(""); 1628 outputchannels->MakeNullParameter(PARAM_STR4(0), B_MEDIA_RAW_AUDIO, "Output Channel Sources", B_GENERIC); 1629 1630 group = outputchannels->MakeGroup(""); 1631 group->MakeNullParameter(PARAM_STR5(0), B_MEDIA_RAW_AUDIO, "Master Output", B_GENERIC); 1632 group = group->MakeGroup(""); 1633 if (!out) { 1634 group->MakeNullParameter(PARAM_STR6(0), B_MEDIA_RAW_AUDIO, "not connected", B_GENERIC); 1635 } else { 1636 for (int chan = 0; chan < out->GetOutputChannelCount(); chan++) { 1637 subgroup = group->MakeGroup(""); 1638 subgroup->MakeNullParameter(PARAM_SRC_STR(0, chan), B_MEDIA_RAW_AUDIO, 1639 StringForChannelType(buf, out->GetOutputChannelType(chan)), B_GENERIC); 1640 for (int src = 0; src < MAX_CHANNEL_TYPES; src++) { 1641 subsubgroup = subgroup->MakeGroup(""); 1642 subsubgroup->MakeDiscreteParameter(PARAM_SRC_ENABLE(0, chan, src), B_MEDIA_RAW_AUDIO, "", B_ENABLE); 1643 subsubgroup->MakeContinuousParameter(PARAM_SRC_GAIN(0, chan, src), B_MEDIA_RAW_AUDIO, 1644 StringForChannelType(buf, src), B_GAIN, "%", 0.0, 100.0, 0.1); 1645 } 1646 } 1647 } 1648 } 1649 1650 if (fCore->Settings()->AllowInputChannelRemapping()) { 1651 top = web->MakeGroup("Input Mapping"); // top level group 1652 inputchannels = top->MakeGroup(""); 1653 inputchannels->MakeNullParameter(PARAM_STR7(0), B_MEDIA_RAW_AUDIO, "Input Channel Destinations", B_GENERIC); 1654 1655 for (int i = 0; (in = fCore->Input(i)); i++) { 1656 group = inputchannels->MakeGroup(""); 1657 group->MakeNullParameter(PARAM_STR4(in->ID()), B_MEDIA_RAW_AUDIO, in->MediaInput().name, B_GENERIC); 1658 group = group->MakeGroup(""); 1659 1660 for (int chan = 0; chan < in->GetInputChannelCount(); chan++) { 1661 subgroup = group->MakeGroup(""); 1662 subgroup->MakeNullParameter(PARAM_DST_STR(in->ID(), chan), B_MEDIA_RAW_AUDIO, 1663 StringForChannelType(buf, in->GetInputChannelType(chan)), B_GENERIC); 1664 for (int dst = 0; dst < MAX_CHANNEL_TYPES; dst++) { 1665 subgroup->MakeDiscreteParameter(PARAM_DST_ENABLE(in->ID(), chan, dst), B_MEDIA_RAW_AUDIO, StringForChannelType(buf, dst), B_ENABLE); 1666 } 1667 } 1668 } 1669 } 1670 1671 top = web->MakeGroup("Setup"); // top level group 1672 group = top->MakeGroup(""); 1673 1674 group->MakeDiscreteParameter(PARAM_ETC(10), B_MEDIA_RAW_AUDIO, "Attenuate mixer output by 3dB (like BeOS R5)", B_ENABLE); 1675 group->MakeDiscreteParameter(PARAM_ETC(20), B_MEDIA_RAW_AUDIO, "Use non linear gain sliders (like BeOS R5)", B_ENABLE); 1676 group->MakeDiscreteParameter(PARAM_ETC(30), B_MEDIA_RAW_AUDIO, "Display balance control for stereo connections", B_ENABLE); 1677 1678 group->MakeDiscreteParameter(PARAM_ETC(40), B_MEDIA_RAW_AUDIO, "Allow output channel remapping", B_ENABLE); 1679 group->MakeDiscreteParameter(PARAM_ETC(50), B_MEDIA_RAW_AUDIO, "Allow input channel remapping", B_ENABLE); 1680 1681 dp = group->MakeDiscreteParameter(PARAM_ETC(60), B_MEDIA_RAW_AUDIO, "Input gain controls represent", B_INPUT_MUX); 1682 dp->AddItem(0, "Physical input channels"); 1683 dp->AddItem(1, "Virtual output channels"); 1684 1685 dp = group->MakeDiscreteParameter(PARAM_ETC(70), B_MEDIA_RAW_AUDIO, "Resampling algorithm", B_INPUT_MUX); 1686 dp->AddItem(0, "Drop/repeat samples"); 1687 1688 // Note: The following code is outcommented on purpose 1689 // and is about to be modified at a later point 1690 /* 1691 dp->AddItem(1, "Drop/repeat samples (template based)"); 1692 dp->AddItem(2, "Linear interpolation"); 1693 dp->AddItem(3, "17th order filtering"); 1694 */ 1695 group->MakeDiscreteParameter(PARAM_ETC(80), B_MEDIA_RAW_AUDIO, "Refuse output format changes", B_ENABLE); 1696 group->MakeDiscreteParameter(PARAM_ETC(90), B_MEDIA_RAW_AUDIO, "Refuse input format changes", B_ENABLE); 1697 1698 fCore->Unlock(); 1699 SetParameterWeb(web); 1700 } 1701 1702 #if USE_MEDIA_FORMAT_WORKAROUND 1703 static void 1704 raw_audio_format_specialize(media_raw_audio_format *format, const media_raw_audio_format *other) 1705 { 1706 if (format->frame_rate == 0) 1707 format->frame_rate = other->frame_rate; 1708 if (format->channel_count == 0) 1709 format->channel_count = other->channel_count; 1710 if (format->format == 0) 1711 format->format = other->format; 1712 if (format->byte_order == 0) 1713 format->byte_order = other->byte_order; 1714 if (format->buffer_size == 0) 1715 format->buffer_size = other->buffer_size; 1716 if (format->frame_rate == 0) 1717 format->frame_rate = other->frame_rate; 1718 } 1719 1720 static void 1721 multi_audio_info_specialize(media_multi_audio_info *format, const media_multi_audio_info *other) 1722 { 1723 if (format->channel_mask == 0) 1724 format->channel_mask = other->channel_mask; 1725 if (format->valid_bits == 0) 1726 format->valid_bits = other->valid_bits; 1727 if (format->matrix_mask == 0) 1728 format->matrix_mask = other->matrix_mask; 1729 } 1730 1731 static void 1732 multi_audio_format_specialize(media_multi_audio_format *format, const media_multi_audio_format *other) 1733 { 1734 raw_audio_format_specialize(format, other); 1735 multi_audio_info_specialize(format, other); 1736 } 1737 #endif 1738