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