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(NULL), 112 fBufferGroup(NULL), 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 status_t status = B_OK; 577 BBufferGroup *group = NULL; 578 MixerOutput *output = fCore->Output(); 579 if (!output) { 580 ERROR("AudioMixer::FormatChangeRequested: no output\n"); 581 goto err; 582 } 583 if (source != output->MediaOutput().source) { 584 ERROR("AudioMixer::FormatChangeRequested: wrong output source\n"); 585 goto err; 586 } 587 if (destination != output->MediaOutput().destination) { 588 ERROR("AudioMixer::FormatChangeRequested: wrong output destination " 589 "(port %ld, id %ld), our is (port %ld, id %ld)\n", destination.port, 590 destination.id, output->MediaOutput().destination.port, 591 output->MediaOutput().destination.id); 592 if (destination.port == output->MediaOutput().destination.port 593 && destination.id == output->MediaOutput().destination.id + 1) { 594 ERROR("AudioMixer::FormatChangeRequested: this might be the broken " 595 "R5 multi audio add-on\n"); 596 } 597 goto err; 598 } 599 if (io_format->type != B_MEDIA_RAW_AUDIO 600 && io_format->type != B_MEDIA_UNKNOWN_TYPE) { 601 ERROR("AudioMixer::FormatChangeRequested: wrong format type\n"); 602 goto err; 603 } 604 605 /* remove wildcards */ 606 #if USE_MEDIA_FORMAT_WORKAROUND 607 multi_audio_format_specialize(&io_format->u.raw_audio, 608 &fDefaultFormat.u.raw_audio); 609 #else 610 io_format->SpecializeTo(&fDefaultFormat); 611 #endif 612 613 media_node_id id; 614 FindLatencyFor(destination, &fDownstreamLatency, &id); 615 TRACE("AudioMixer: Downstream Latency is %" B_PRIdBIGTIME " usecs\n", 616 fDownstreamLatency); 617 618 // SetDuration of one buffer 619 SetBufferDuration(buffer_duration(io_format->u.raw_audio)); 620 TRACE("AudioMixer: buffer duration is %" B_PRIdBIGTIME " usecs\n", 621 BufferDuration()); 622 623 // Our internal latency is at least the length of a full output buffer 624 fInternalLatency = BufferDuration() 625 + max((bigtime_t)4500, bigtime_t(0.5 * BufferDuration())); 626 TRACE("AudioMixer: Internal latency is %" B_PRIdBIGTIME " usecs\n", 627 fInternalLatency); 628 629 SetEventLatency(fDownstreamLatency + fInternalLatency); 630 631 // we need to inform all connected *inputs* about *our* change in latency 632 PublishEventLatencyChange(); 633 634 // TODO: we might need to create more buffers, to span a larger downstream 635 // latency 636 637 // apply latency change 638 fCore->SetTimingInfo(TimeSource(), fDownstreamLatency); 639 640 // apply format change 641 fCore->OutputFormatChanged(io_format->u.raw_audio); 642 643 status = CreateBufferGroup(&group); 644 if (status != B_OK) 645 return status; 646 else { 647 delete fBufferGroup; 648 fBufferGroup = group; 649 fCore->SetOutputBufferGroup(fBufferGroup); 650 } 651 652 err: 653 fCore->Unlock(); 654 return status; 655 } 656 657 658 status_t 659 AudioMixer::GetNextOutput(int32 *cookie, media_output *out_output) 660 { 661 TRACE("AudioMixer::GetNextOutput\n"); 662 663 if (*cookie != 0) 664 return B_BAD_INDEX; 665 666 fCore->Lock(); 667 MixerOutput *output = fCore->Output(); 668 if (output) { 669 *out_output = output->MediaOutput(); 670 } else { 671 out_output->node = Node(); 672 out_output->source.port = ControlPort(); 673 out_output->source.id = 0; 674 out_output->destination = media_destination::null; 675 memset(&out_output->format, 0, sizeof(out_output->format)); 676 out_output->format.type = B_MEDIA_RAW_AUDIO; 677 strcpy(out_output->name, "Mixer Output"); 678 } 679 fCore->Unlock(); 680 681 *cookie += 1; 682 return B_OK; 683 } 684 685 686 status_t 687 AudioMixer::DisposeOutputCookie(int32 cookie) 688 { 689 // nothing to do 690 return B_OK; 691 } 692 693 694 status_t 695 AudioMixer::SetBufferGroup(const media_source &for_source, 696 BBufferGroup *newGroup) 697 { 698 TRACE("AudioMixer::SetBufferGroup\n"); 699 // the downstream consumer (soundcard) node asks us to use another 700 // BBufferGroup (might be NULL). We only have one output (id 0) 701 if (for_source.port != ControlPort() || for_source.id != 0) 702 return B_MEDIA_BAD_SOURCE; 703 704 if (newGroup == fBufferGroup) { 705 // we're already using this buffergroup 706 return B_OK; 707 } 708 709 fCore->Lock(); 710 if (!newGroup) { 711 status_t status = CreateBufferGroup(&newGroup); 712 if (status != B_OK) 713 return status; 714 } 715 fCore->SetOutputBufferGroup(newGroup); 716 delete fBufferGroup; 717 fBufferGroup = newGroup; 718 fCore->Unlock(); 719 720 return B_OK; 721 } 722 723 724 status_t 725 AudioMixer::GetLatency(bigtime_t *out_latency) 726 { 727 // report our *total* latency: internal plus downstream plus scheduling 728 *out_latency = EventLatency() + SchedulingLatency(); 729 730 TRACE("AudioMixer::GetLatency %Ld\n", *out_latency); 731 732 return B_OK; 733 } 734 735 736 void 737 AudioMixer::LatencyChanged(const media_source& source, 738 const media_destination& destination, bigtime_t new_latency, uint32 flags) 739 { 740 if (source.port != ControlPort() || source.id != 0) { 741 ERROR("AudioMixer::LatencyChanged: received but has wrong source " 742 "%ld/%ld\n", source.port, source.id); 743 return; 744 } 745 746 TRACE("AudioMixer::LatencyChanged: downstream latency from %ld/%ld to " 747 "%ld/%ld changed from %Ld to %Ld\n", source.port, source.id, 748 destination.port, destination.id, fDownstreamLatency, new_latency); 749 750 #if DEBUG 751 { 752 media_node_id id; 753 bigtime_t l; 754 FindLatencyFor(destination, &l, &id); 755 TRACE("AudioMixer: Reported downstream Latency is %Ld usecs\n", l); 756 } 757 #endif 758 759 fDownstreamLatency = new_latency; 760 SetEventLatency(fDownstreamLatency + fInternalLatency); 761 762 // XXX we might need to create more buffers, to span a larger downstream 763 // latency 764 765 fCore->Lock(); 766 fCore->SetTimingInfo(TimeSource(), fDownstreamLatency); 767 PublishEventLatencyChange(); 768 fCore->Unlock(); 769 } 770 771 status_t 772 AudioMixer::PrepareToConnect(const media_source &what, 773 const media_destination &where, media_format *format, 774 media_source *out_source, char *out_name) 775 { 776 TRACE("AudioMixer::PrepareToConnect\n"); 777 // PrepareToConnect() is the second stage of format negotiations that 778 // happens inside BMediaRoster::Connect(). At this point, the consumer's 779 // AcceptFormat() method has been called, and that node has potentially 780 // changed the proposed format. 781 // It may also have left wildcards in the format. PrepareToConnect() 782 // *must* fully specialize the format before returning! 783 // we also create the new output connection and return it in out_source. 784 785 PRINT_FORMAT("AudioMixer::PrepareToConnect: suggested format", *format); 786 787 // avoid loop connections 788 if (where.port == ControlPort()) 789 return B_MEDIA_BAD_SOURCE; 790 791 // is the source valid? 792 if (what.port != ControlPort() || what.id != 0) 793 return B_MEDIA_BAD_SOURCE; 794 795 // is the format acceptable? 796 if (format->type != B_MEDIA_RAW_AUDIO 797 && format->type != B_MEDIA_UNKNOWN_TYPE) { 798 PRINT_FORMAT("AudioMixer::PrepareToConnect: bad format", *format); 799 return B_MEDIA_BAD_FORMAT; 800 } 801 802 fCore->Lock(); 803 804 // are we already connected? 805 if (fCore->Output() != 0) { 806 fCore->Unlock(); 807 ERROR("AudioMixer::PrepareToConnect: already connected\n"); 808 return B_MEDIA_ALREADY_CONNECTED; 809 } 810 811 // It is possible that another mixer is connecting. 812 // To avoid using the default format, we use one of 813 // a) the format that it indicated as hint in the user_data, 814 // b) the output format of the system audio mixer 815 // c) the input format of the system DAC device 816 // d) if everything failes, keep the wildcard 817 if (format->u.raw_audio.channel_count == 0 818 && format->u.raw_audio.frame_rate < 1 819 && format->user_data_type == FORMAT_USER_DATA_TYPE 820 && *(uint32 *)&format->user_data[0] == FORMAT_USER_DATA_MAGIC_1 821 && *(uint32 *)&format->user_data[44] == FORMAT_USER_DATA_MAGIC_2) { 822 // ok, a mixer is connecting 823 uint32 channel_count = *(uint32 *)&format->user_data[4]; 824 float frame_rate = *(float *)&format->user_data[20]; 825 if (channel_count > 0 && frame_rate > 0) { 826 // format is good, use it 827 format->u.raw_audio.channel_count = channel_count; 828 format->u.raw_audio.frame_rate = frame_rate; 829 } else { 830 // other mixer's output is probably not connected 831 media_node node; 832 BMediaRoster *roster = BMediaRoster::Roster(); 833 media_output out; 834 media_input in; 835 int32 count; 836 if (roster->GetAudioMixer(&node) == B_OK 837 && roster->GetConnectedOutputsFor(node, &out, 1, &count) 838 == B_OK 839 && count == 1) { 840 // use mixer output format 841 format->u.raw_audio.channel_count 842 = out.format.u.raw_audio.channel_count; 843 format->u.raw_audio.frame_rate 844 = out.format.u.raw_audio.frame_rate; 845 } else if (roster->GetAudioOutput(&node) == B_OK 846 && roster->GetAllInputsFor(node, &in, 1, &count) == B_OK 847 && count == 1) { 848 // use DAC input format 849 format->u.raw_audio.channel_count 850 = in.format.u.raw_audio.channel_count; 851 format->u.raw_audio.frame_rate 852 = in.format.u.raw_audio.frame_rate; 853 } 854 } 855 } 856 857 /* set source and suggest a name */ 858 *out_source = what; 859 strcpy(out_name, "Mixer Output"); 860 861 /* remove wildcards */ 862 #if USE_MEDIA_FORMAT_WORKAROUND 863 multi_audio_format_specialize(&format->u.raw_audio, 864 &fDefaultFormat.u.raw_audio); 865 #else 866 format->SpecializeTo(&fDefaultFormat); 867 #endif 868 869 PRINT_FORMAT("AudioMixer::PrepareToConnect: final format", *format); 870 871 /* add output to core */ 872 media_output output; 873 output.node = Node(); 874 output.source = *out_source; 875 output.destination = where; 876 output.format = *format; 877 strcpy(output.name, out_name); 878 879 fCore->EnableOutput(false); 880 fCore->AddOutput(output); 881 882 fCore->Unlock(); 883 return B_OK; 884 } 885 886 887 void 888 AudioMixer::Connect(status_t error, const media_source &source, 889 const media_destination &dest, const media_format &format, char *io_name) 890 { 891 TRACE("AudioMixer::Connect\n"); 892 893 fCore->Lock(); 894 // are we still connected? 895 if (fCore->Output() == 0) { 896 fCore->Unlock(); 897 ERROR("AudioMixer::Connect: no longer connected\n"); 898 return; 899 } 900 fCore->Unlock(); 901 902 if (error != B_OK) { 903 // if an error occured, remove output from core 904 ERROR("AudioMixer::Connect failed with error 0x%08lX, removing " 905 "connection\n", error); 906 fCore->Lock(); 907 fCore->RemoveOutput(); 908 fCore->Unlock(); 909 return; 910 } 911 912 // Switch our prefered format to have the same 913 // frame_rate and channel count as the output. 914 fDefaultFormat.u.raw_audio.frame_rate = format.u.raw_audio.frame_rate; 915 fDefaultFormat.u.raw_audio.channel_count = format.u.raw_audio.channel_count; 916 917 // if the connection has no name, we set it now 918 if (strlen(io_name) == 0) 919 strcpy(io_name, "Mixer Output"); 920 921 // Now that we're connected, we can determine our downstream latency. 922 media_node_id id; 923 FindLatencyFor(dest, &fDownstreamLatency, &id); 924 TRACE("AudioMixer: Downstream Latency is %Ld usecs\n", fDownstreamLatency); 925 926 // SetDuration of one buffer 927 SetBufferDuration(buffer_duration(format.u.raw_audio)); 928 TRACE("AudioMixer: buffer duration is %Ld usecs\n", BufferDuration()); 929 930 // Our internal latency is at least the length of a full output buffer 931 // plus mixing time, plus jitter 932 fInternalLatency = BufferDuration() 933 + max(kMinMixingTime, bigtime_t(0.5 * BufferDuration())) + kMaxJitter; 934 TRACE("AudioMixer: Internal latency is %Ld usecs\n", fInternalLatency); 935 936 SetEventLatency(fDownstreamLatency + fInternalLatency); 937 938 // we need to inform all connected *inputs* about *our* change in latency 939 PublishEventLatencyChange(); 940 941 fCore->Lock(); 942 943 // Set up the buffer group for our connection, as long as nobody handed 944 // us a buffer group (via SetBufferGroup()) prior to this. That can 945 // happen, for example, if the consumer calls SetOutputBuffersFor() on 946 // us from within its Connected() method. 947 if (!fBufferGroup) { 948 BBufferGroup *group = NULL; 949 if (CreateBufferGroup(&group) != B_OK) 950 return; 951 fBufferGroup = group; 952 } 953 954 ASSERT(fCore->Output() != 0); 955 956 // our source should still be valid, too 957 ASSERT(fCore->Output()->MediaOutput().source.id == 0); 958 ASSERT(fCore->Output()->MediaOutput().source.port == ControlPort()); 959 960 // BBufferConsumer::Connected() may return a different input for the 961 // newly created connection. The destination can have changed since 962 // AudioMixer::PrepareToConnect() and we need to update it. 963 fCore->Output()->MediaOutput().destination = dest; 964 965 fCore->EnableOutput(true); 966 fCore->SetTimingInfo(TimeSource(), fDownstreamLatency); 967 fCore->SetOutputBufferGroup(fBufferGroup); 968 969 fCore->Settings()->LoadConnectionSettings(fCore->Output()); 970 971 fCore->Unlock(); 972 UpdateParameterWeb(); 973 } 974 975 976 void 977 AudioMixer::Disconnect(const media_source& what, const media_destination& where) 978 { 979 TRACE("AudioMixer::Disconnect\n"); 980 fCore->Lock(); 981 982 // Make sure that our connection is the one being disconnected 983 MixerOutput* output = fCore->Output(); 984 if (!output 985 || output->MediaOutput().node != Node() 986 || output->MediaOutput().source != what 987 || output->MediaOutput().destination != where) { 988 ERROR("AudioMixer::Disconnect can't disconnect (wrong connection)\n"); 989 fCore->Unlock(); 990 return; 991 } 992 993 // Switch our prefered format back to default 994 // frame rate and channel count. 995 fDefaultFormat.u.raw_audio.frame_rate = 96000; 996 fDefaultFormat.u.raw_audio.channel_count = 2; 997 998 // force a stop 999 fCore->Stop(); 1000 1001 fCore->RemoveOutput(); 1002 1003 // destroy buffer group 1004 delete fBufferGroup; 1005 fBufferGroup = NULL; 1006 fCore->SetOutputBufferGroup(0); 1007 1008 fCore->Unlock(); 1009 UpdateParameterWeb(); 1010 } 1011 1012 1013 void 1014 AudioMixer::LateNoticeReceived(const media_source& what, bigtime_t howMuch, 1015 bigtime_t performanceTime) 1016 { 1017 // We've produced some late buffers... Increase Latency 1018 // is the only runmode in which we can do anything about this 1019 // TODO: quality could be decreased, too 1020 1021 ERROR("AudioMixer::LateNoticeReceived, %Ld too late at %Ld\n", howMuch, 1022 performanceTime); 1023 1024 if (what == fCore->Output()->MediaOutput().source 1025 && RunMode() == B_INCREASE_LATENCY) { 1026 // We need to ignore subsequent notices whose arrival time here 1027 // lies within the last lateness, because queued-up buffers will all be 'late' 1028 if (performanceTime < fLastLateNotification) 1029 return; 1030 1031 fInternalLatency += howMuch; 1032 1033 // At some point a too large latency can get annoying 1034 // (actually more than annoying, as there won't be enough buffers long before this!) 1035 if (fInternalLatency > kMaxLatency) 1036 fInternalLatency = kMaxLatency; 1037 1038 fLastLateNotification = TimeSource()->Now() + howMuch; 1039 1040 debug_printf("AudioMixer: increasing internal latency to %" 1041 B_PRIdBIGTIME " usec\n", fInternalLatency); 1042 SetEventLatency(fDownstreamLatency + fInternalLatency); 1043 1044 PublishEventLatencyChange(); 1045 } 1046 } 1047 1048 1049 void 1050 AudioMixer::EnableOutput(const media_source& what, bool enabled, 1051 int32 */*deprecated*/) 1052 { 1053 // we only have one output 1054 if (what.id != 0 || what.port != ControlPort()) 1055 return; 1056 1057 fCore->Lock(); 1058 fCore->EnableOutput(enabled); 1059 fCore->Unlock(); 1060 } 1061 1062 1063 // #pragma mark - BMediaEventLooper methods 1064 1065 1066 void 1067 AudioMixer::NodeRegistered() 1068 { 1069 Run(); 1070 SetPriority(120); 1071 UpdateParameterWeb(); 1072 } 1073 1074 1075 void 1076 AudioMixer::SetTimeSource(BTimeSource* timeSource) 1077 { 1078 TRACE("AudioMixer::SetTimeSource: timesource is now %ld\n", 1079 timeSource->ID()); 1080 fCore->Lock(); 1081 fCore->SetTimingInfo(timeSource, fDownstreamLatency); 1082 fCore->Unlock(); 1083 } 1084 1085 1086 void 1087 AudioMixer::HandleEvent(const media_timed_event *event, bigtime_t lateness, 1088 bool realTimeEvent) 1089 { 1090 switch (event->type) { 1091 case BTimedEventQueue::B_HANDLE_BUFFER: 1092 { 1093 HandleInputBuffer((BBuffer *)event->pointer, lateness); 1094 ((BBuffer *)event->pointer)->Recycle(); 1095 break; 1096 } 1097 1098 case BTimedEventQueue::B_START: 1099 { 1100 TRACE("AudioMixer::HandleEvent: B_START\n"); 1101 if (RunState() != B_STARTED) { 1102 fCore->Lock(); 1103 fCore->Start(); 1104 fCore->Unlock(); 1105 } 1106 break; 1107 } 1108 1109 case BTimedEventQueue::B_STOP: 1110 { 1111 TRACE("AudioMixer::HandleEvent: B_STOP\n"); 1112 // stopped - don't process any more buffers, flush all buffers 1113 // from event queue 1114 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, 1115 BTimedEventQueue::B_HANDLE_BUFFER); 1116 fCore->Lock(); 1117 fCore->Stop(); 1118 fCore->Unlock(); 1119 break; 1120 } 1121 1122 case BTimedEventQueue::B_DATA_STATUS: 1123 { 1124 ERROR("DataStatus message\n"); 1125 break; 1126 } 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 1919 1920 #if USE_MEDIA_FORMAT_WORKAROUND 1921 static void 1922 raw_audio_format_specialize(media_raw_audio_format *format, 1923 const media_raw_audio_format *other) 1924 { 1925 if (format->frame_rate == 0) 1926 format->frame_rate = other->frame_rate; 1927 if (format->channel_count == 0) 1928 format->channel_count = other->channel_count; 1929 if (format->format == 0) 1930 format->format = other->format; 1931 if (format->byte_order == 0) 1932 format->byte_order = other->byte_order; 1933 if (format->buffer_size == 0) 1934 format->buffer_size = other->buffer_size; 1935 if (format->frame_rate == 0) 1936 format->frame_rate = other->frame_rate; 1937 } 1938 1939 1940 static void 1941 multi_audio_info_specialize(media_multi_audio_info *format, 1942 const media_multi_audio_info *other) 1943 { 1944 if (format->channel_mask == 0) 1945 format->channel_mask = other->channel_mask; 1946 if (format->valid_bits == 0) 1947 format->valid_bits = other->valid_bits; 1948 if (format->matrix_mask == 0) 1949 format->matrix_mask = other->matrix_mask; 1950 } 1951 1952 1953 static void 1954 multi_audio_format_specialize(media_multi_audio_format *format, 1955 const media_multi_audio_format *other) 1956 { 1957 raw_audio_format_specialize(format, other); 1958 multi_audio_info_specialize(format, other); 1959 } 1960 #endif 1961