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