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