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 (fCore->Settings()->NonLinearGainSlider()) { 1210 if (db > 0) { 1211 db = db * (pow(abs(DB_MAX), (1.0 / DB_EXPONENT_POSITIVE)) 1212 / abs(DB_MAX)); 1213 db = pow(db, DB_EXPONENT_POSITIVE); 1214 } else { 1215 db = -db; 1216 db = db * (pow(abs(DB_MIN), (1.0 / DB_EXPONENT_NEGATIVE)) 1217 / abs(DB_MIN)); 1218 db = pow(db, DB_EXPONENT_NEGATIVE); 1219 db = -db; 1220 } 1221 } 1222 TRACE("dB out: %01.2f\n", db); 1223 return pow(10.0, db / 20.0); 1224 } 1225 1226 1227 float 1228 AudioMixer::Gain_to_dB(float gain) 1229 { 1230 float db; 1231 db = 20.0 * log10(gain); 1232 if (fCore->Settings()->NonLinearGainSlider()) { 1233 if (db > 0) { 1234 db = pow(db, (1.0 / DB_EXPONENT_POSITIVE)); 1235 db = db * (abs(DB_MAX) / pow(abs(DB_MAX), 1236 (1.0 / DB_EXPONENT_POSITIVE))); 1237 } else { 1238 db = -db; 1239 db = pow(db, (1.0 / DB_EXPONENT_NEGATIVE)); 1240 db = db * (abs(DB_MIN) / pow(abs(DB_MIN), 1241 (1.0 / DB_EXPONENT_NEGATIVE))); 1242 db = -db; 1243 } 1244 } 1245 return db; 1246 } 1247 1248 1249 // #pragma mark - BControllable methods 1250 1251 1252 status_t 1253 AudioMixer::GetParameterValue(int32 id, bigtime_t *last_change, void *value, 1254 size_t *ioSize) 1255 { 1256 TRACE("GetParameterValue: id 0x%08lx, ioSize %ld\n", id, *ioSize); 1257 int param = PARAM(id); 1258 fCore->Lock(); 1259 if (PARAM_IS_ETC(id)) { 1260 switch (ETC(id)) { 1261 case 10: // Attenuate mixer output by 3dB 1262 *ioSize = sizeof(int32); 1263 static_cast<int32 *>(value)[0] = fCore->Settings()->AttenuateOutput(); 1264 break; 1265 case 20: // Use non linear gain sliders 1266 *ioSize = sizeof(int32); 1267 static_cast<int32 *>(value)[0] = fCore->Settings()->NonLinearGainSlider(); 1268 break; 1269 case 30: // Display balance control for stereo connections 1270 *ioSize = sizeof(int32); 1271 static_cast<int32 *>(value)[0] = fCore->Settings()->UseBalanceControl(); 1272 break; 1273 case 40: // Allow output channel remapping 1274 *ioSize = sizeof(int32); 1275 static_cast<int32 *>(value)[0] = fCore->Settings()->AllowOutputChannelRemapping(); 1276 break; 1277 case 50: // Allow input channel remapping 1278 *ioSize = sizeof(int32); 1279 static_cast<int32 *>(value)[0] = fCore->Settings()->AllowInputChannelRemapping(); 1280 break; 1281 case 60: // Input gain controls 1282 *ioSize = sizeof(int32); 1283 static_cast<int32 *>(value)[0] = fCore->Settings()->InputGainControls(); 1284 break; 1285 case 70: // Resampling algorithm 1286 *ioSize = sizeof(int32); 1287 static_cast<int32 *>(value)[0] = fCore->Settings()->ResamplingAlgorithm(); 1288 break; 1289 case 80: // Refuse output format changes 1290 *ioSize = sizeof(int32); 1291 static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseOutputFormatChange(); 1292 break; 1293 case 90: // Refuse input format changes 1294 *ioSize = sizeof(int32); 1295 static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseInputFormatChange(); 1296 break; 1297 default: 1298 ERROR("unhandled ETC 0x%08lx\n", id); 1299 break; 1300 } 1301 } else if (param == 0) { 1302 MixerOutput *output = fCore->Output(); 1303 if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id))) 1304 goto err; 1305 if (PARAM_IS_MUTE(id)) { 1306 // output mute control 1307 if (*ioSize < sizeof(int32)) 1308 goto err; 1309 *ioSize = sizeof(int32); 1310 static_cast<int32 *>(value)[0] = output->IsMuted(); 1311 } 1312 if (PARAM_IS_GAIN(id)) { 1313 // output gain control 1314 if (fCore->Settings()->UseBalanceControl() && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1315 // single channel control + balance 1316 if (*ioSize < sizeof(float)) 1317 goto err; 1318 *ioSize = sizeof(float); 1319 static_cast<float *>(value)[0] = GAIN_TO_DB((output->GetOutputChannelGain(0) + output->GetOutputChannelGain(1)) / 2); 1320 } else { 1321 // multi channel control 1322 if (*ioSize == sizeof(float)) { 1323 // get combined gain for all controls 1324 float gain = 0; 1325 for (int channel = 0; 1326 channel < output->GetOutputChannelCount(); 1327 channel++) { 1328 gain += GAIN_TO_DB( 1329 output->GetOutputChannelGain(channel)); 1330 } 1331 static_cast<float *>(value)[0] = gain 1332 / output->GetOutputChannelCount(); 1333 } else { 1334 if (*ioSize < output->GetOutputChannelCount() 1335 * sizeof(float)) 1336 goto err; 1337 1338 *ioSize = output->GetOutputChannelCount() * sizeof(float); 1339 1340 for (int channel = 0; 1341 channel < output->GetOutputChannelCount(); 1342 channel++) { 1343 static_cast<float *>(value)[channel] 1344 = GAIN_TO_DB(output->GetOutputChannelGain(channel)); 1345 } 1346 } 1347 } 1348 } 1349 if (PARAM_IS_BALANCE(id)) { 1350 float l = output->GetOutputChannelGain(0); 1351 float r = output->GetOutputChannelGain(1); 1352 float v = r / (l+r); 1353 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v); 1354 if (*ioSize < sizeof(float)) 1355 goto err; 1356 *ioSize = sizeof(float); 1357 static_cast<float *>(value)[0] = v * 100; 1358 } 1359 if (PARAM_IS_SRC_ENABLE(id)) { 1360 if (*ioSize < sizeof(int32)) 1361 goto err; 1362 *ioSize = sizeof(int32); 1363 static_cast<int32 *>(value)[0] = output->HasOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id)); 1364 } 1365 if (PARAM_IS_SRC_GAIN(id)) { 1366 if (*ioSize < sizeof(float)) 1367 goto err; 1368 *ioSize = sizeof(float); 1369 static_cast<float *>(value)[0] = GAIN_TO_PERCENT(output->GetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id))); 1370 } 1371 } else { 1372 MixerInput *input; 1373 for (int i = 0; (input = fCore->Input(i)); i++) 1374 if (input->ID() == param) 1375 break; 1376 if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id))) 1377 goto err; 1378 if (PARAM_IS_MUTE(id)) { 1379 // input mute control 1380 if (*ioSize < sizeof(int32)) 1381 goto err; 1382 *ioSize = sizeof(int32); 1383 static_cast<int32 *>(value)[0] = !input->IsEnabled(); 1384 } 1385 if (PARAM_IS_GAIN(id)) { 1386 // input gain control 1387 if (fCore->Settings()->InputGainControls() == 0) { 1388 // Physical input channels 1389 if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1390 // single channel control + balance 1391 if (*ioSize < sizeof(float)) 1392 goto err; 1393 *ioSize = sizeof(float); 1394 static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetInputChannelGain(0) + input->GetInputChannelGain(1)) / 2); 1395 } else { 1396 // multi channel control 1397 if (*ioSize < input->GetInputChannelCount() * sizeof(float)) 1398 goto err; 1399 *ioSize = input->GetInputChannelCount() * sizeof(float); 1400 for (int chan = 0; chan < input->GetInputChannelCount(); chan++) 1401 static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetInputChannelGain(chan)); 1402 } 1403 } else { 1404 // Virtual output channels 1405 if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) { 1406 // single channel control + balance 1407 if (*ioSize < sizeof(float)) 1408 goto err; 1409 *ioSize = sizeof(float); 1410 static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetMixerChannelGain(0) + input->GetMixerChannelGain(1)) / 2); 1411 } else { 1412 // multi channel control 1413 if (*ioSize < input->GetMixerChannelCount() * sizeof(float)) 1414 goto err; 1415 *ioSize = input->GetMixerChannelCount() * sizeof(float); 1416 for (int chan = 0; chan < input->GetMixerChannelCount(); chan++) 1417 static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetMixerChannelGain(chan)); 1418 } 1419 } 1420 } 1421 if (PARAM_IS_BALANCE(id)) { 1422 if (fCore->Settings()->InputGainControls() == 0) { 1423 // Physical input channels 1424 float l = input->GetInputChannelGain(0); 1425 float r = input->GetInputChannelGain(1); 1426 float v = r / (l+r); 1427 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v); 1428 if (*ioSize < sizeof(float)) 1429 goto err; 1430 *ioSize = sizeof(float); 1431 static_cast<float *>(value)[0] = v * 100; 1432 } else { 1433 // Virtual output channels 1434 float l = input->GetMixerChannelGain(0); 1435 float r = input->GetMixerChannelGain(1); 1436 float v = r / (l+r); 1437 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v); 1438 if (*ioSize < sizeof(float)) 1439 goto err; 1440 *ioSize = sizeof(float); 1441 static_cast<float *>(value)[0] = v * 100; 1442 } 1443 } 1444 if (PARAM_IS_DST_ENABLE(id)) { 1445 if (*ioSize < sizeof(int32)) 1446 goto err; 1447 *ioSize = sizeof(int32); 1448 static_cast<int32 *>(value)[0] = input->HasInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id)); 1449 } 1450 } 1451 *last_change = TimeSource()->Now(); // XXX we could do better 1452 fCore->Unlock(); 1453 return B_OK; 1454 err: 1455 fCore->Unlock(); 1456 return B_ERROR; 1457 } 1458 1459 1460 void 1461 AudioMixer::SetParameterValue(int32 id, bigtime_t when, const void *value, 1462 size_t size) 1463 { 1464 TRACE("SetParameterValue: id 0x%08lx, size %ld\n", id, size); 1465 bool update = false; 1466 int param = PARAM(id); 1467 fCore->Lock(); 1468 if (PARAM_IS_ETC(id)) { 1469 switch (ETC(id)) { 1470 case 10: // Attenuate mixer output by 3dB 1471 if (size != sizeof(int32)) 1472 goto err; 1473 fCore->Settings()->SetAttenuateOutput(static_cast<const int32 *>(value)[0]); 1474 // this value is special (see MixerCore.h) and we need to notify the core 1475 fCore->SetOutputAttenuation((static_cast<const int32 *>(value)[0]) ? 0.708 : 1.0); 1476 break; 1477 case 20: // Use non linear gain sliders 1478 if (size != sizeof(int32)) 1479 goto err; 1480 fCore->Settings()->SetNonLinearGainSlider(static_cast<const int32 *>(value)[0]); 1481 update = true; // XXX should use BroadcastChangedParameter() 1482 break; 1483 case 30: // Display balance control for stereo connections 1484 if (size != sizeof(int32)) 1485 goto err; 1486 fCore->Settings()->SetUseBalanceControl(static_cast<const int32 *>(value)[0]); 1487 update = true; 1488 break; 1489 case 40: // Allow output channel remapping 1490 if (size != sizeof(int32)) 1491 goto err; 1492 fCore->Settings()->SetAllowOutputChannelRemapping(static_cast<const int32 *>(value)[0]); 1493 update = true; 1494 break; 1495 case 50: // Allow input channel remapping 1496 if (size != sizeof(int32)) 1497 goto err; 1498 fCore->Settings()->SetAllowInputChannelRemapping(static_cast<const int32 *>(value)[0]); 1499 update = true; 1500 break; 1501 case 60: // Input gain controls represent 1502 // (0, "Physical input channels") 1503 // (1, "Virtual output channels") 1504 if (size != sizeof(int32)) 1505 goto err; 1506 fCore->Settings()->SetInputGainControls(static_cast<const int32 *>(value)[0]); 1507 update = true; // XXX should use BroadcastChangedParameter() 1508 break; 1509 case 70: // Resampling algorithm 1510 if (size != sizeof(int32)) 1511 goto err; 1512 fCore->Settings()->SetResamplingAlgorithm(static_cast<const int32 *>(value)[0]); 1513 fCore->UpdateResamplingAlgorithm(); 1514 break; 1515 case 80: // Refuse output format changes 1516 if (size != sizeof(int32)) 1517 goto err; 1518 fCore->Settings()->SetRefuseOutputFormatChange(static_cast<const int32 *>(value)[0]); 1519 break; 1520 case 90: // Refuse input format changes 1521 if (size != sizeof(int32)) 1522 goto err; 1523 fCore->Settings()->SetRefuseInputFormatChange(static_cast<const int32 *>(value)[0]); 1524 break; 1525 default: 1526 ERROR("unhandled ETC 0x%08lx\n", id); 1527 break; 1528 } 1529 } else if (param == 0) { 1530 MixerOutput *output = fCore->Output(); 1531 if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id))) 1532 goto err; 1533 if (PARAM_IS_MUTE(id)) { 1534 // output mute control 1535 if (size != sizeof(int32)) 1536 goto err; 1537 output->SetMuted(static_cast<const int32 *>(value)[0]); 1538 } 1539 if (PARAM_IS_GAIN(id)) { 1540 // output gain control 1541 if (fCore->Settings()->UseBalanceControl() 1542 && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1543 // single channel control + balance 1544 float l = output->GetOutputChannelGain(0); 1545 float r = output->GetOutputChannelGain(1); 1546 float m = (l + r) / 2; // master volume 1547 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]); 1548 float f = v / m; // factor for both channels 1549 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f); 1550 output->SetOutputChannelGain(0, output->GetOutputChannelGain(0) * f); 1551 output->SetOutputChannelGain(1, output->GetOutputChannelGain(1) * f); 1552 } else { 1553 // multi channel control 1554 if (size == sizeof(float)) { 1555 // set same volume for all channels 1556 float gain = static_cast<const float *>(value)[0]; 1557 for (int channel = 0; 1558 channel < output->GetOutputChannelCount(); 1559 channel++) { 1560 output->SetOutputChannelGain(channel, 1561 DB_TO_GAIN(gain)); 1562 } 1563 } else { 1564 if (size < output->GetOutputChannelCount() * sizeof(float)) 1565 goto err; 1566 for (int channel = 0; 1567 channel < output->GetOutputChannelCount(); 1568 channel++) { 1569 output->SetOutputChannelGain(channel, 1570 DB_TO_GAIN(static_cast<const float *>( 1571 value)[channel])); 1572 } 1573 } 1574 } 1575 } 1576 if (PARAM_IS_BALANCE(id)) { 1577 float l = output->GetOutputChannelGain(0); 1578 float r = output->GetOutputChannelGain(1); 1579 float m = (l + r) / 2; // master volume 1580 float v = static_cast<const float *>(value)[0] / 100; // current balance value 1581 float fl = 2 * (1 - v); // left channel factor of master volume 1582 float fr = 2 * v; // right channel factor of master volume 1583 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); 1584 output->SetOutputChannelGain(0, m * fl); 1585 output->SetOutputChannelGain(1, m * fr); 1586 } 1587 if (PARAM_IS_SRC_ENABLE(id)) { 1588 if (size != sizeof(int32)) 1589 goto err; 1590 if (static_cast<const int32 *>(value)[0]) { 1591 output->AddOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id)); 1592 } else { 1593 output->RemoveOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id)); 1594 } 1595 } 1596 if (PARAM_IS_SRC_GAIN(id)) { 1597 if (size != sizeof(float)) 1598 goto err; 1599 output->SetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id), PERCENT_TO_GAIN(static_cast<const float *>(value)[0])); 1600 } 1601 fCore->Settings()->SaveConnectionSettings(output); 1602 } else { 1603 MixerInput *input; 1604 for (int i = 0; (input = fCore->Input(i)); i++) 1605 if (input->ID() == param) 1606 break; 1607 if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id))) 1608 goto err; 1609 if (PARAM_IS_MUTE(id)) { 1610 // input mute control 1611 if (size != sizeof(int32)) 1612 goto err; 1613 input->SetEnabled(!static_cast<const int32 *>(value)[0]); 1614 } 1615 if (PARAM_IS_GAIN(id)) { 1616 // input gain control 1617 if (fCore->Settings()->InputGainControls() == 0) { 1618 // Physical input channels 1619 if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1620 // single channel control + balance 1621 float l = input->GetInputChannelGain(0); 1622 float r = input->GetInputChannelGain(1); 1623 float m = (l + r) / 2; // master volume 1624 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]); 1625 float f = v / m; // factor for both channels 1626 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f); 1627 input->SetInputChannelGain(0, input->GetInputChannelGain(0) * f); 1628 input->SetInputChannelGain(1, input->GetInputChannelGain(1) * f); 1629 } else { 1630 // multi channel control 1631 if (size < input->GetInputChannelCount() * sizeof(float)) 1632 goto err; 1633 for (int chan = 0; chan < input->GetInputChannelCount(); chan++) 1634 input->SetInputChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan])); 1635 } 1636 } else { 1637 // Virtual output channels 1638 if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) { 1639 // single channel control + balance 1640 float l = input->GetMixerChannelGain(0); 1641 float r = input->GetMixerChannelGain(1); 1642 float m = (l + r) / 2; // master volume 1643 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]); 1644 float f = v / m; // factor for both channels 1645 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f); 1646 input->SetMixerChannelGain(0, input->GetMixerChannelGain(0) * f); 1647 input->SetMixerChannelGain(1, input->GetMixerChannelGain(1) * f); 1648 } else { 1649 // multi channel control 1650 if (size < input->GetMixerChannelCount() * sizeof(float)) 1651 goto err; 1652 for (int chan = 0; chan < input->GetMixerChannelCount(); chan++) 1653 input->SetMixerChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan])); 1654 } 1655 } 1656 } 1657 if (PARAM_IS_BALANCE(id)) { 1658 if (fCore->Settings()->InputGainControls() == 0) { 1659 // Physical input channels 1660 float l = input->GetInputChannelGain(0); 1661 float r = input->GetInputChannelGain(1); 1662 float m = (l + r) / 2; // master volume 1663 float v = static_cast<const float *>(value)[0] / 100; // current balance value 1664 float fl = 2 * (1 - v); // left channel factor of master volume 1665 float fr = 2 * v; // right channel factor of master volume 1666 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); 1667 input->SetInputChannelGain(0, m * fl); 1668 input->SetInputChannelGain(1, m * fr); 1669 } else { 1670 // Virtual output channels 1671 float l = input->GetMixerChannelGain(0); 1672 float r = input->GetMixerChannelGain(1); 1673 float m = (l + r) / 2; // master volume 1674 float v = static_cast<const float *>(value)[0] / 100; // current balance value 1675 float fl = 2 * (1 - v); // left channel factor of master volume 1676 float fr = 2 * v; // right channel factor of master volume 1677 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); 1678 input->SetMixerChannelGain(0, m * fl); 1679 input->SetMixerChannelGain(1, m * fr); 1680 } 1681 } 1682 if (PARAM_IS_DST_ENABLE(id)) { 1683 if (size != sizeof(int32)) 1684 goto err; 1685 if (static_cast<const int32 *>(value)[0]) { 1686 int oldchan = input->GetInputChannelForDestination(PARAM_DST(id)); 1687 if (oldchan != -1) { 1688 input->RemoveInputChannelDestination(oldchan, PARAM_DST(id)); 1689 int32 null = 0; 1690 BroadcastNewParameterValue(when, PARAM_DST_ENABLE(PARAM(id), oldchan, PARAM_DST(id)), &null, sizeof(null)); 1691 } 1692 input->AddInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id)); 1693 } else { 1694 input->RemoveInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id)); 1695 } 1696 // TODO: this is really annoying 1697 // The slider count of the gain control needs to be changed, 1698 // but calling SetChannelCount(input->GetMixerChannelCount()) 1699 // on it has no effect on remote BParameterWebs in other apps. 1700 // BroadcastChangedParameter() should be correct, but doesn't work 1701 BroadcastChangedParameter(PARAM_GAIN(PARAM(id))); 1702 // We trigger a complete ParameterWeb update as workaround 1703 // but it will change the focus from tab 3 to tab 1 1704 update = true; 1705 } 1706 fCore->Settings()->SaveConnectionSettings(input); 1707 } 1708 1709 BroadcastNewParameterValue(when, id, const_cast<void *>(value), size); 1710 1711 err: 1712 fCore->Unlock(); 1713 if (update) 1714 UpdateParameterWeb(); 1715 } 1716 1717 1718 void 1719 AudioMixer::UpdateParameterWeb() 1720 { 1721 fCore->Lock(); 1722 BParameterWeb *web = new BParameterWeb(); 1723 BParameterGroup *top; 1724 BParameterGroup *outputchannels; 1725 BParameterGroup *inputchannels; 1726 BParameterGroup *group; 1727 BParameterGroup *subgroup; 1728 BParameterGroup *subsubgroup; 1729 BDiscreteParameter *dp; 1730 MixerInput *in; 1731 MixerOutput *out; 1732 char buf[50]; 1733 1734 top = web->MakeGroup(B_TRANSLATE("Gain controls")); 1735 1736 out = fCore->Output(); 1737 group = top->MakeGroup(""); 1738 group->MakeNullParameter(PARAM_STR1(0), B_MEDIA_RAW_AUDIO, 1739 B_TRANSLATE("Master output"), B_WEB_BUFFER_INPUT); 1740 if (!out) { 1741 group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO, 1742 B_TRANSLATE("not connected"), B_GENERIC); 1743 } else { 1744 group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO, 1745 StringForFormat(buf, out), B_GENERIC); 1746 group->MakeDiscreteParameter(PARAM_MUTE(0), B_MEDIA_RAW_AUDIO, 1747 B_TRANSLATE("Mute"), B_MUTE); 1748 if (fCore->Settings()->UseBalanceControl() 1749 && out->GetOutputChannelCount() == 2 && 1 1750 /*channel mask is stereo */) { 1751 // single channel control + balance 1752 group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO, 1753 B_TRANSLATE("Gain"), B_MASTER_GAIN, B_TRANSLATE("dB"), 1754 DB_MIN, DB_MAX, 0.1); 1755 group->MakeContinuousParameter(PARAM_BALANCE(0), B_MEDIA_RAW_AUDIO, 1756 "", B_BALANCE, "", 0, 100, 1); 1757 } else { 1758 // multi channel control 1759 group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO, 1760 B_TRANSLATE("Gain"), B_MASTER_GAIN, B_TRANSLATE("dB"), 1761 DB_MIN, DB_MAX, 0.1) 1762 ->SetChannelCount(out->GetOutputChannelCount()); 1763 } 1764 group->MakeNullParameter(PARAM_STR3(0), B_MEDIA_RAW_AUDIO, 1765 B_TRANSLATE("To output"), B_WEB_BUFFER_OUTPUT); 1766 } 1767 1768 for (int i = 0; (in = fCore->Input(i)); i++) { 1769 group = top->MakeGroup(""); 1770 group->MakeNullParameter(PARAM_STR1(in->ID()), B_MEDIA_RAW_AUDIO, 1771 in->MediaInput().name, B_WEB_BUFFER_INPUT); 1772 group->MakeNullParameter(PARAM_STR2(in->ID()), B_MEDIA_RAW_AUDIO, 1773 StringForFormat(buf, in), B_GENERIC); 1774 group->MakeDiscreteParameter(PARAM_MUTE(in->ID()), B_MEDIA_RAW_AUDIO, 1775 B_TRANSLATE("Mute"), B_MUTE); 1776 // XXX the gain control is ugly once you have more than two channels, 1777 // as you don't know what channel each slider controls. Tooltips might help... 1778 if (fCore->Settings()->InputGainControls() == 0) { 1779 // Physical input channels 1780 if (fCore->Settings()->UseBalanceControl() 1781 && in->GetInputChannelCount() == 2 && 1 1782 /*channel mask is stereo */) { 1783 // single channel control + balance 1784 group->MakeContinuousParameter(PARAM_GAIN(in->ID()), 1785 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN, 1786 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1); 1787 group->MakeContinuousParameter(PARAM_BALANCE(in->ID()), 1788 B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1); 1789 } else { 1790 // multi channel control 1791 group->MakeContinuousParameter(PARAM_GAIN(in->ID()), 1792 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN, 1793 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1) 1794 ->SetChannelCount(in->GetInputChannelCount()); 1795 } 1796 } else { 1797 // Virtual output channels 1798 if (fCore->Settings()->UseBalanceControl() 1799 && in->GetMixerChannelCount() == 2 && 1 1800 /*channel mask is stereo */) { 1801 // single channel control + balance 1802 group->MakeContinuousParameter(PARAM_GAIN(in->ID()), 1803 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN, 1804 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1); 1805 group->MakeContinuousParameter(PARAM_BALANCE(in->ID()), 1806 B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1); 1807 } else { 1808 // multi channel control 1809 group->MakeContinuousParameter(PARAM_GAIN(in->ID()), 1810 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN, 1811 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1) 1812 ->SetChannelCount(in->GetMixerChannelCount()); 1813 } 1814 } 1815 group->MakeNullParameter(PARAM_STR3(in->ID()), B_MEDIA_RAW_AUDIO, 1816 B_TRANSLATE("To master"), B_WEB_BUFFER_OUTPUT); 1817 } 1818 1819 if (fCore->Settings()->AllowOutputChannelRemapping()) { 1820 top = web->MakeGroup(B_TRANSLATE("Output mapping")); // top level group 1821 outputchannels = top->MakeGroup(""); 1822 outputchannels->MakeNullParameter(PARAM_STR4(0), B_MEDIA_RAW_AUDIO, 1823 B_TRANSLATE("Output channel sources"), B_GENERIC); 1824 1825 group = outputchannels->MakeGroup(""); 1826 group->MakeNullParameter(PARAM_STR5(0), B_MEDIA_RAW_AUDIO, 1827 B_TRANSLATE("Master output"), B_GENERIC); 1828 group = group->MakeGroup(""); 1829 if (!out) { 1830 group->MakeNullParameter(PARAM_STR6(0), B_MEDIA_RAW_AUDIO, 1831 B_TRANSLATE("not connected"), B_GENERIC); 1832 } else { 1833 for (int chan = 0; chan < out->GetOutputChannelCount(); chan++) { 1834 subgroup = group->MakeGroup(""); 1835 subgroup->MakeNullParameter(PARAM_SRC_STR(0, chan), 1836 B_MEDIA_RAW_AUDIO, StringForChannelType(buf, 1837 out->GetOutputChannelType(chan)), B_GENERIC); 1838 for (int src = 0; src < MAX_CHANNEL_TYPES; src++) { 1839 subsubgroup = subgroup->MakeGroup(""); 1840 subsubgroup->MakeDiscreteParameter( 1841 PARAM_SRC_ENABLE(0, chan, src), B_MEDIA_RAW_AUDIO, "", 1842 B_ENABLE); 1843 subsubgroup->MakeContinuousParameter( 1844 PARAM_SRC_GAIN(0, chan, src), B_MEDIA_RAW_AUDIO, 1845 StringForChannelType(buf, src), B_GAIN, "%", 0.0, 1846 100.0, 0.1); 1847 } 1848 } 1849 } 1850 } 1851 1852 if (fCore->Settings()->AllowInputChannelRemapping()) { 1853 top = web->MakeGroup(B_TRANSLATE("Input mapping")); // top level group 1854 inputchannels = top->MakeGroup(""); 1855 inputchannels->MakeNullParameter(PARAM_STR7(0), B_MEDIA_RAW_AUDIO, 1856 B_TRANSLATE("Input channel destinations"), B_GENERIC); 1857 1858 for (int i = 0; (in = fCore->Input(i)); i++) { 1859 group = inputchannels->MakeGroup(""); 1860 group->MakeNullParameter(PARAM_STR4(in->ID()), B_MEDIA_RAW_AUDIO, 1861 in->MediaInput().name, B_GENERIC); 1862 group = group->MakeGroup(""); 1863 1864 for (int chan = 0; chan < in->GetInputChannelCount(); chan++) { 1865 subgroup = group->MakeGroup(""); 1866 subgroup->MakeNullParameter(PARAM_DST_STR(in->ID(), chan), 1867 B_MEDIA_RAW_AUDIO, StringForChannelType(buf, 1868 in->GetInputChannelType(chan)), B_GENERIC); 1869 for (int dst = 0; dst < MAX_CHANNEL_TYPES; dst++) { 1870 subgroup->MakeDiscreteParameter(PARAM_DST_ENABLE(in->ID(), 1871 chan, dst), B_MEDIA_RAW_AUDIO, StringForChannelType(buf, dst), 1872 B_ENABLE); 1873 } 1874 } 1875 } 1876 } 1877 1878 top = web->MakeGroup(B_TRANSLATE("Setup")); // top level group 1879 group = top->MakeGroup(""); 1880 1881 group->MakeDiscreteParameter(PARAM_ETC(10), B_MEDIA_RAW_AUDIO, 1882 B_TRANSLATE("Attenuate mixer output by 3dB (like BeOS R5)"), B_ENABLE); 1883 group->MakeDiscreteParameter(PARAM_ETC(20), B_MEDIA_RAW_AUDIO, 1884 B_TRANSLATE("Use non linear gain sliders (like BeOS R5)"), B_ENABLE); 1885 group->MakeDiscreteParameter(PARAM_ETC(30), B_MEDIA_RAW_AUDIO, 1886 B_TRANSLATE("Display balance control for stereo connections"), 1887 B_ENABLE); 1888 1889 group->MakeDiscreteParameter(PARAM_ETC(40), B_MEDIA_RAW_AUDIO, 1890 B_TRANSLATE("Allow output channel remapping"), B_ENABLE); 1891 group->MakeDiscreteParameter(PARAM_ETC(50), B_MEDIA_RAW_AUDIO, 1892 B_TRANSLATE("Allow input channel remapping"), B_ENABLE); 1893 1894 dp = group->MakeDiscreteParameter(PARAM_ETC(60), B_MEDIA_RAW_AUDIO, 1895 B_TRANSLATE("Input gain controls represent"), B_INPUT_MUX); 1896 dp->AddItem(0, B_TRANSLATE("Physical input channels")); 1897 dp->AddItem(1, B_TRANSLATE("Virtual output channels")); 1898 1899 dp = group->MakeDiscreteParameter(PARAM_ETC(70), B_MEDIA_RAW_AUDIO, 1900 B_TRANSLATE("Resampling algorithm"), B_INPUT_MUX); 1901 dp->AddItem(0, B_TRANSLATE("Drop/repeat samples")); 1902 dp->AddItem(2, B_TRANSLATE("Linear interpolation")); 1903 1904 // Note: The following code is outcommented on purpose 1905 // and is about to be modified at a later point 1906 /* 1907 dp->AddItem(1, B_TRANSLATE("Drop/repeat samples (template based)")); 1908 dp->AddItem(3, B_TRANSLATE("17th order filtering")); 1909 */ 1910 group->MakeDiscreteParameter(PARAM_ETC(80), B_MEDIA_RAW_AUDIO, 1911 B_TRANSLATE("Refuse output format changes"), B_ENABLE); 1912 group->MakeDiscreteParameter(PARAM_ETC(90), B_MEDIA_RAW_AUDIO, 1913 B_TRANSLATE("Refuse input format changes"), B_ENABLE); 1914 1915 fCore->Unlock(); 1916 SetParameterWeb(web); 1917 } 1918