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