1 /* 2 * Copyright 2002 David Shipman, 3 * Copyright 2003-2007 Marcus Overhagen 4 * Copyright 2007-2010 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_TRANSLATE_CONTEXT 33 #define B_TRANSLATE_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 { 118 BMediaNode::AddNodeKind(B_SYSTEM_MIXER); 119 120 // this is the default format used for all wildcard format SpecializeTo()s 121 fDefaultFormat.type = B_MEDIA_RAW_AUDIO; 122 fDefaultFormat.u.raw_audio.frame_rate = 96000; 123 fDefaultFormat.u.raw_audio.channel_count = 2; 124 fDefaultFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; 125 fDefaultFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN; 126 fDefaultFormat.u.raw_audio.buffer_size = 4096; 127 fDefaultFormat.u.raw_audio.channel_mask = 0; 128 fDefaultFormat.u.raw_audio.valid_bits = 0; 129 fDefaultFormat.u.raw_audio.matrix_mask = 0; 130 131 if (isSystemMixer) { 132 // to get persistent settings, assign a settings file 133 BPath path; 134 if (B_OK != find_directory (B_USER_SETTINGS_DIRECTORY, &path)) 135 path.SetTo("/boot/home/config/settings/"); 136 path.Append("System Audio Mixer"); 137 fCore->Settings()->SetSettingsFile(path.Path()); 138 139 // disable stop on the auto started (system) mixer 140 DisableNodeStop(); 141 } 142 143 ApplySettings(); 144 } 145 146 147 AudioMixer::~AudioMixer() 148 { 149 BMediaEventLooper::Quit(); 150 SetParameterWeb(NULL); 151 152 // stop the mixer 153 fCore->Lock(); 154 fCore->Stop(); 155 fCore->Unlock(); 156 157 // disconnect all nodes from the mixer 158 // XXX todo 159 160 delete fCore; 161 delete fBufferGroup; 162 163 DEBUG_ONLY(fCore = 0; fBufferGroup = 0; fWeb = 0); 164 } 165 166 167 void 168 AudioMixer::ApplySettings() 169 { 170 fCore->Lock(); 171 fCore->SetOutputAttenuation(fCore->Settings()->AttenuateOutput() ? 0.708 : 1.0); 172 fCore->Unlock(); 173 } 174 175 176 void 177 AudioMixer::DisableNodeStop() 178 { 179 fDisableStop = true; 180 } 181 182 183 // #pragma mark - BMediaNode methods 184 185 186 void 187 AudioMixer::Stop(bigtime_t performance_time, bool immediate) 188 { 189 if (fDisableStop) { 190 TRACE("AudioMixer STOP is disabled\n"); 191 return; 192 } else { 193 BMediaEventLooper::Stop(performance_time, immediate); 194 } 195 } 196 197 198 BMediaAddOn* 199 AudioMixer::AddOn(int32 *internal_id) const 200 { 201 *internal_id = 0; 202 return fAddOn; 203 } 204 205 206 // #pragma mark - BBufferConsumer methods 207 208 209 status_t 210 AudioMixer::HandleMessage(int32 message, const void *data, size_t size) 211 { 212 // since we're using a mediaeventlooper, there shouldn't be any messages 213 return B_ERROR; 214 } 215 216 217 status_t 218 AudioMixer::AcceptFormat(const media_destination &dest, media_format *ioFormat) 219 { 220 PRINT_FORMAT("AudioMixer::AcceptFormat: ", *ioFormat); 221 222 // check that the specified format is reasonable for the specified destination, and 223 // fill in any wildcard fields for which our BBufferConsumer has specific requirements. 224 225 // we have multiple inputs with different IDs, but 226 // the port number must match our ControlPort() 227 if (dest.port != ControlPort()) 228 return B_MEDIA_BAD_DESTINATION; 229 230 // specialize to raw audio format if necessary 231 if (ioFormat->type == B_MEDIA_UNKNOWN_TYPE) 232 ioFormat->type = B_MEDIA_RAW_AUDIO; 233 234 // we require a raw audio format 235 if (ioFormat->type != B_MEDIA_RAW_AUDIO) 236 return B_MEDIA_BAD_FORMAT; 237 238 // We do not have special requirements, but just in case 239 // another mixer is connecting to us and may need a hint 240 // to create a connection at optimal frame rate and 241 // channel count, we place this information in the user_data 242 fCore->Lock(); 243 MixerOutput *output = fCore->Output(); 244 uint32 channel_count = output ? output->MediaOutput().format.u.raw_audio.channel_count : 0; 245 float frame_rate = output ? output->MediaOutput().format.u.raw_audio.frame_rate : 0.0; 246 fCore->Unlock(); 247 ioFormat->user_data_type = FORMAT_USER_DATA_TYPE; 248 *(uint32 *)&ioFormat->user_data[0] = FORMAT_USER_DATA_MAGIC_1; 249 *(uint32 *)&ioFormat->user_data[4] = channel_count; 250 *(float *)&ioFormat->user_data[20] = frame_rate; 251 *(uint32 *)&ioFormat->user_data[44] = FORMAT_USER_DATA_MAGIC_2; 252 253 return B_OK; 254 } 255 256 257 status_t 258 AudioMixer::GetNextInput(int32 *cookie, media_input *out_input) 259 { 260 TRACE("AudioMixer::GetNextInput\n"); 261 262 // our 0th input is always a wildcard and free one 263 if (*cookie == 0) { 264 out_input->node = Node(); 265 out_input->source = media_source::null; 266 out_input->destination.port = ControlPort(); 267 out_input->destination.id = 0; 268 memset(&out_input->format, 0, sizeof(out_input->format)); 269 out_input->format.type = B_MEDIA_RAW_AUDIO; 270 strcpy(out_input->name, "Free Input"); 271 *cookie += 1; 272 return B_OK; 273 } 274 275 // the other inputs are the currently connected ones 276 fCore->Lock(); 277 MixerInput *input; 278 input = fCore->Input(*cookie - 1); 279 if (!input) { 280 fCore->Unlock(); 281 return B_BAD_INDEX; 282 } 283 *out_input = input->MediaInput(); 284 *cookie += 1; 285 fCore->Unlock(); 286 return B_OK; 287 } 288 289 290 void 291 AudioMixer::DisposeInputCookie(int32 cookie) 292 { 293 // nothing to do 294 } 295 296 297 void 298 AudioMixer::BufferReceived(BBuffer *buffer) 299 { 300 301 if (buffer->Header()->type == B_MEDIA_PARAMETERS) { 302 TRACE("Control Buffer Received\n"); 303 ApplyParameterData(buffer->Data(), buffer->SizeUsed()); 304 buffer->Recycle(); 305 return; 306 } 307 308 //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); 309 310 // to receive the buffer at the right time, 311 // push it through the event looper 312 media_timed_event event(buffer->Header()->start_time, 313 BTimedEventQueue::B_HANDLE_BUFFER, buffer, 314 BTimedEventQueue::B_RECYCLE_BUFFER); 315 EventQueue()->AddEvent(event); 316 } 317 318 319 void 320 AudioMixer::HandleInputBuffer(BBuffer* buffer, bigtime_t lateness) 321 { 322 if (lateness > kMaxJitter) { 323 debug_printf("Received buffer %Ld usec late\n", lateness); 324 if (RunMode() == B_DROP_DATA || RunMode() == B_DECREASE_PRECISION 325 || RunMode() == B_INCREASE_LATENCY) { 326 debug_printf("sending notify\n"); 327 328 // Build a media_source out of the header data 329 media_source source = media_source::null; 330 source.port = buffer->Header()->source_port; 331 source.id = buffer->Header()->source; 332 333 NotifyLateProducer(source, lateness, TimeSource()->Now()); 334 335 if (RunMode() == B_DROP_DATA) { 336 debug_printf("dropping buffer\n"); 337 return; 338 } 339 } 340 } 341 342 // printf("Received buffer with lateness %Ld\n", 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 performance time 1010 // lies before the performance time of the last notification 1011 if (performanceTime < fLastLateNotification) 1012 return; 1013 1014 fInternalLatency += howMuch; 1015 1016 // At some point a too large latency can get annoying 1017 if (fInternalLatency > kMaxLatency) 1018 fInternalLatency = kMaxLatency; 1019 1020 fLastLateNotification = TimeSource()->Now(); 1021 1022 debug_printf("AudioMixer: increasing internal latency to %Ld usec\n", fInternalLatency); 1023 SetEventLatency(fDownstreamLatency + fInternalLatency); 1024 1025 PublishEventLatencyChange(); 1026 } 1027 } 1028 1029 1030 void 1031 AudioMixer::EnableOutput(const media_source& what, bool enabled, 1032 int32 */*deprecated*/) 1033 { 1034 // we only have one output 1035 if (what.id != 0 || what.port != ControlPort()) 1036 return; 1037 1038 fCore->Lock(); 1039 fCore->EnableOutput(enabled); 1040 fCore->Unlock(); 1041 } 1042 1043 1044 // #pragma mark - BMediaEventLooper methods 1045 1046 1047 void 1048 AudioMixer::NodeRegistered() 1049 { 1050 Run(); 1051 SetPriority(120); 1052 UpdateParameterWeb(); 1053 } 1054 1055 1056 void 1057 AudioMixer::SetTimeSource(BTimeSource* timeSource) 1058 { 1059 TRACE("AudioMixer::SetTimeSource: timesource is now %ld\n", 1060 timeSource->ID()); 1061 fCore->Lock(); 1062 fCore->SetTimingInfo(timeSource, fDownstreamLatency); 1063 fCore->Unlock(); 1064 } 1065 1066 1067 void 1068 AudioMixer::HandleEvent(const media_timed_event *event, bigtime_t lateness, 1069 bool realTimeEvent) 1070 { 1071 switch (event->type) { 1072 case BTimedEventQueue::B_HANDLE_BUFFER: 1073 { 1074 HandleInputBuffer((BBuffer *)event->pointer, lateness); 1075 ((BBuffer *)event->pointer)->Recycle(); 1076 break; 1077 } 1078 1079 case BTimedEventQueue::B_START: 1080 { 1081 TRACE("AudioMixer::HandleEvent: B_START\n"); 1082 if (RunState() != B_STARTED) { 1083 fCore->Lock(); 1084 fCore->Start(); 1085 fCore->Unlock(); 1086 } 1087 break; 1088 } 1089 1090 case BTimedEventQueue::B_STOP: 1091 { 1092 TRACE("AudioMixer::HandleEvent: B_STOP\n"); 1093 // stopped - don't process any more buffers, flush all buffers 1094 // from event queue 1095 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, 1096 BTimedEventQueue::B_HANDLE_BUFFER); 1097 fCore->Lock(); 1098 fCore->Stop(); 1099 fCore->Unlock(); 1100 break; 1101 } 1102 1103 case BTimedEventQueue::B_DATA_STATUS: 1104 { 1105 ERROR("DataStatus message\n"); 1106 break; 1107 } 1108 1109 default: 1110 break; 1111 } 1112 } 1113 1114 1115 // #pragma mark - AudioMixer methods 1116 1117 1118 void 1119 AudioMixer::PublishEventLatencyChange() 1120 { 1121 // our event (processing + downstream) latency has changed, 1122 // and we need tell all inputs about this 1123 1124 TRACE("AudioMixer::PublishEventLatencyChange\n"); 1125 1126 fCore->Lock(); 1127 1128 MixerInput *input; 1129 for (int i = 0; (input = fCore->Input(i)) != 0; i++) { 1130 TRACE("AudioMixer::PublishEventLatencyChange: SendLatencyChange, " 1131 "connection %ld/%ld to %ld/%ld event latency is now %Ld\n", 1132 input->MediaInput().source.port, input->MediaInput().source.id, 1133 input->MediaInput().destination.port, 1134 input->MediaInput().destination.id, EventLatency()); 1135 SendLatencyChange(input->MediaInput().source, 1136 input->MediaInput().destination, EventLatency()); 1137 } 1138 1139 fCore->Unlock(); 1140 } 1141 1142 1143 BBufferGroup* 1144 AudioMixer::CreateBufferGroup() 1145 { 1146 // allocate enough buffers to span our downstream latency 1147 // (plus one for rounding up), plus one extra 1148 int32 count = int32(fDownstreamLatency / BufferDuration()) + 2; 1149 1150 TRACE("AudioMixer::CreateBufferGroup: fDownstreamLatency %Ld, " 1151 "BufferDuration %Ld, buffer count = %ld\n", fDownstreamLatency, 1152 BufferDuration(), count); 1153 1154 if (count < 3) 1155 count = 3; 1156 1157 fCore->Lock(); 1158 uint32 size = fCore->Output()->MediaOutput().format.u.raw_audio.buffer_size; 1159 fCore->Unlock(); 1160 1161 TRACE("AudioMixer: allocating %ld buffers of %ld bytes each\n", 1162 count, size); 1163 return new BBufferGroup(size, count); 1164 } 1165 1166 1167 status_t 1168 AudioMixer::SendBuffer(BBuffer* buffer, MixerOutput* output) 1169 { 1170 return BBufferProducer::SendBuffer(buffer, output->MediaOutput().source, 1171 output->MediaOutput().destination); 1172 } 1173 1174 1175 float 1176 AudioMixer::dB_to_Gain(float db) 1177 { 1178 TRACE("dB_to_Gain: dB in: %01.2f ", db); 1179 if (fCore->Settings()->NonLinearGainSlider()) { 1180 if (db > 0) { 1181 db = db * (pow(abs(DB_MAX), (1.0 / DB_EXPONENT_POSITIVE)) 1182 / abs(DB_MAX)); 1183 db = pow(db, DB_EXPONENT_POSITIVE); 1184 } else { 1185 db = -db; 1186 db = db * (pow(abs(DB_MIN), (1.0 / DB_EXPONENT_NEGATIVE)) 1187 / abs(DB_MIN)); 1188 db = pow(db, DB_EXPONENT_NEGATIVE); 1189 db = -db; 1190 } 1191 } 1192 TRACE("dB out: %01.2f\n", db); 1193 return pow(10.0, db / 20.0); 1194 } 1195 1196 1197 float 1198 AudioMixer::Gain_to_dB(float gain) 1199 { 1200 float db; 1201 db = 20.0 * log10(gain); 1202 if (fCore->Settings()->NonLinearGainSlider()) { 1203 if (db > 0) { 1204 db = pow(db, (1.0 / DB_EXPONENT_POSITIVE)); 1205 db = db * (abs(DB_MAX) / pow(abs(DB_MAX), 1206 (1.0 / DB_EXPONENT_POSITIVE))); 1207 } else { 1208 db = -db; 1209 db = pow(db, (1.0 / DB_EXPONENT_NEGATIVE)); 1210 db = db * (abs(DB_MIN) / pow(abs(DB_MIN), 1211 (1.0 / DB_EXPONENT_NEGATIVE))); 1212 db = -db; 1213 } 1214 } 1215 return db; 1216 } 1217 1218 1219 // #pragma mark - BControllable methods 1220 1221 1222 status_t 1223 AudioMixer::GetParameterValue(int32 id, bigtime_t *last_change, void *value, 1224 size_t *ioSize) 1225 { 1226 TRACE("GetParameterValue: id 0x%08lx, ioSize %ld\n", id, *ioSize); 1227 int param = PARAM(id); 1228 fCore->Lock(); 1229 if (PARAM_IS_ETC(id)) { 1230 switch (ETC(id)) { 1231 case 10: // Attenuate mixer output by 3dB 1232 *ioSize = sizeof(int32); 1233 static_cast<int32 *>(value)[0] = fCore->Settings()->AttenuateOutput(); 1234 break; 1235 case 20: // Use non linear gain sliders 1236 *ioSize = sizeof(int32); 1237 static_cast<int32 *>(value)[0] = fCore->Settings()->NonLinearGainSlider(); 1238 break; 1239 case 30: // Display balance control for stereo connections 1240 *ioSize = sizeof(int32); 1241 static_cast<int32 *>(value)[0] = fCore->Settings()->UseBalanceControl(); 1242 break; 1243 case 40: // Allow output channel remapping 1244 *ioSize = sizeof(int32); 1245 static_cast<int32 *>(value)[0] = fCore->Settings()->AllowOutputChannelRemapping(); 1246 break; 1247 case 50: // Allow input channel remapping 1248 *ioSize = sizeof(int32); 1249 static_cast<int32 *>(value)[0] = fCore->Settings()->AllowInputChannelRemapping(); 1250 break; 1251 case 60: // Input gain controls 1252 *ioSize = sizeof(int32); 1253 static_cast<int32 *>(value)[0] = fCore->Settings()->InputGainControls(); 1254 break; 1255 case 70: // Resampling algorithm 1256 *ioSize = sizeof(int32); 1257 static_cast<int32 *>(value)[0] = fCore->Settings()->ResamplingAlgorithm(); 1258 break; 1259 case 80: // Refuse output format changes 1260 *ioSize = sizeof(int32); 1261 static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseOutputFormatChange(); 1262 break; 1263 case 90: // Refuse input format changes 1264 *ioSize = sizeof(int32); 1265 static_cast<int32 *>(value)[0] = fCore->Settings()->RefuseInputFormatChange(); 1266 break; 1267 default: 1268 ERROR("unhandled ETC 0x%08lx\n", id); 1269 break; 1270 } 1271 } else if (param == 0) { 1272 MixerOutput *output = fCore->Output(); 1273 if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id))) 1274 goto err; 1275 if (PARAM_IS_MUTE(id)) { 1276 // output mute control 1277 if (*ioSize < sizeof(int32)) 1278 goto err; 1279 *ioSize = sizeof(int32); 1280 static_cast<int32 *>(value)[0] = output->IsMuted(); 1281 } 1282 if (PARAM_IS_GAIN(id)) { 1283 // output gain control 1284 if (fCore->Settings()->UseBalanceControl() && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1285 // single channel control + balance 1286 if (*ioSize < sizeof(float)) 1287 goto err; 1288 *ioSize = sizeof(float); 1289 static_cast<float *>(value)[0] = GAIN_TO_DB((output->GetOutputChannelGain(0) + output->GetOutputChannelGain(1)) / 2); 1290 } else { 1291 // multi channel control 1292 if (*ioSize == sizeof(float)) { 1293 // get combined gain for all controls 1294 float gain = 0; 1295 for (int channel = 0; 1296 channel < output->GetOutputChannelCount(); 1297 channel++) { 1298 gain += GAIN_TO_DB( 1299 output->GetOutputChannelGain(channel)); 1300 } 1301 static_cast<float *>(value)[0] = gain 1302 / output->GetOutputChannelCount(); 1303 } else { 1304 if (*ioSize < output->GetOutputChannelCount() 1305 * sizeof(float)) 1306 goto err; 1307 1308 *ioSize = output->GetOutputChannelCount() * sizeof(float); 1309 1310 for (int channel = 0; 1311 channel < output->GetOutputChannelCount(); 1312 channel++) { 1313 static_cast<float *>(value)[channel] 1314 = GAIN_TO_DB(output->GetOutputChannelGain(channel)); 1315 } 1316 } 1317 } 1318 } 1319 if (PARAM_IS_BALANCE(id)) { 1320 float l = output->GetOutputChannelGain(0); 1321 float r = output->GetOutputChannelGain(1); 1322 float v = r / (l+r); 1323 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v); 1324 if (*ioSize < sizeof(float)) 1325 goto err; 1326 *ioSize = sizeof(float); 1327 static_cast<float *>(value)[0] = v * 100; 1328 } 1329 if (PARAM_IS_SRC_ENABLE(id)) { 1330 if (*ioSize < sizeof(int32)) 1331 goto err; 1332 *ioSize = sizeof(int32); 1333 static_cast<int32 *>(value)[0] = output->HasOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id)); 1334 } 1335 if (PARAM_IS_SRC_GAIN(id)) { 1336 if (*ioSize < sizeof(float)) 1337 goto err; 1338 *ioSize = sizeof(float); 1339 static_cast<float *>(value)[0] = GAIN_TO_PERCENT(output->GetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id))); 1340 } 1341 } else { 1342 MixerInput *input; 1343 for (int i = 0; (input = fCore->Input(i)); i++) 1344 if (input->ID() == param) 1345 break; 1346 if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id))) 1347 goto err; 1348 if (PARAM_IS_MUTE(id)) { 1349 // input mute control 1350 if (*ioSize < sizeof(int32)) 1351 goto err; 1352 *ioSize = sizeof(int32); 1353 static_cast<int32 *>(value)[0] = !input->IsEnabled(); 1354 } 1355 if (PARAM_IS_GAIN(id)) { 1356 // input gain control 1357 if (fCore->Settings()->InputGainControls() == 0) { 1358 // Physical input channels 1359 if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1360 // single channel control + balance 1361 if (*ioSize < sizeof(float)) 1362 goto err; 1363 *ioSize = sizeof(float); 1364 static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetInputChannelGain(0) + input->GetInputChannelGain(1)) / 2); 1365 } else { 1366 // multi channel control 1367 if (*ioSize < input->GetInputChannelCount() * sizeof(float)) 1368 goto err; 1369 *ioSize = input->GetInputChannelCount() * sizeof(float); 1370 for (int chan = 0; chan < input->GetInputChannelCount(); chan++) 1371 static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetInputChannelGain(chan)); 1372 } 1373 } else { 1374 // Virtual output channels 1375 if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) { 1376 // single channel control + balance 1377 if (*ioSize < sizeof(float)) 1378 goto err; 1379 *ioSize = sizeof(float); 1380 static_cast<float *>(value)[0] = GAIN_TO_DB((input->GetMixerChannelGain(0) + input->GetMixerChannelGain(1)) / 2); 1381 } else { 1382 // multi channel control 1383 if (*ioSize < input->GetMixerChannelCount() * sizeof(float)) 1384 goto err; 1385 *ioSize = input->GetMixerChannelCount() * sizeof(float); 1386 for (int chan = 0; chan < input->GetMixerChannelCount(); chan++) 1387 static_cast<float *>(value)[chan] = GAIN_TO_DB(input->GetMixerChannelGain(chan)); 1388 } 1389 } 1390 } 1391 if (PARAM_IS_BALANCE(id)) { 1392 if (fCore->Settings()->InputGainControls() == 0) { 1393 // Physical input channels 1394 float l = input->GetInputChannelGain(0); 1395 float r = input->GetInputChannelGain(1); 1396 float v = r / (l+r); 1397 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v); 1398 if (*ioSize < sizeof(float)) 1399 goto err; 1400 *ioSize = sizeof(float); 1401 static_cast<float *>(value)[0] = v * 100; 1402 } else { 1403 // Virtual output channels 1404 float l = input->GetMixerChannelGain(0); 1405 float r = input->GetMixerChannelGain(1); 1406 float v = r / (l+r); 1407 TRACE("balance l %1.3f, r %1.3f, v %1.3f\n",l,r,v); 1408 if (*ioSize < sizeof(float)) 1409 goto err; 1410 *ioSize = sizeof(float); 1411 static_cast<float *>(value)[0] = v * 100; 1412 } 1413 } 1414 if (PARAM_IS_DST_ENABLE(id)) { 1415 if (*ioSize < sizeof(int32)) 1416 goto err; 1417 *ioSize = sizeof(int32); 1418 static_cast<int32 *>(value)[0] = input->HasInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id)); 1419 } 1420 } 1421 *last_change = TimeSource()->Now(); // XXX we could do better 1422 fCore->Unlock(); 1423 return B_OK; 1424 err: 1425 fCore->Unlock(); 1426 return B_ERROR; 1427 } 1428 1429 1430 void 1431 AudioMixer::SetParameterValue(int32 id, bigtime_t when, const void *value, 1432 size_t size) 1433 { 1434 TRACE("SetParameterValue: id 0x%08lx, size %ld\n", id, size); 1435 bool update = false; 1436 int param = PARAM(id); 1437 fCore->Lock(); 1438 if (PARAM_IS_ETC(id)) { 1439 switch (ETC(id)) { 1440 case 10: // Attenuate mixer output by 3dB 1441 if (size != sizeof(int32)) 1442 goto err; 1443 fCore->Settings()->SetAttenuateOutput(static_cast<const int32 *>(value)[0]); 1444 // this value is special (see MixerCore.h) and we need to notify the core 1445 fCore->SetOutputAttenuation((static_cast<const int32 *>(value)[0]) ? 0.708 : 1.0); 1446 break; 1447 case 20: // Use non linear gain sliders 1448 if (size != sizeof(int32)) 1449 goto err; 1450 fCore->Settings()->SetNonLinearGainSlider(static_cast<const int32 *>(value)[0]); 1451 update = true; // XXX should use BroadcastChangedParameter() 1452 break; 1453 case 30: // Display balance control for stereo connections 1454 if (size != sizeof(int32)) 1455 goto err; 1456 fCore->Settings()->SetUseBalanceControl(static_cast<const int32 *>(value)[0]); 1457 update = true; 1458 break; 1459 case 40: // Allow output channel remapping 1460 if (size != sizeof(int32)) 1461 goto err; 1462 fCore->Settings()->SetAllowOutputChannelRemapping(static_cast<const int32 *>(value)[0]); 1463 update = true; 1464 break; 1465 case 50: // Allow input channel remapping 1466 if (size != sizeof(int32)) 1467 goto err; 1468 fCore->Settings()->SetAllowInputChannelRemapping(static_cast<const int32 *>(value)[0]); 1469 update = true; 1470 break; 1471 case 60: // Input gain controls represent 1472 // (0, "Physical input channels") 1473 // (1, "Virtual output channels") 1474 if (size != sizeof(int32)) 1475 goto err; 1476 fCore->Settings()->SetInputGainControls(static_cast<const int32 *>(value)[0]); 1477 update = true; // XXX should use BroadcastChangedParameter() 1478 break; 1479 case 70: // Resampling algorithm 1480 if (size != sizeof(int32)) 1481 goto err; 1482 fCore->Settings()->SetResamplingAlgorithm(static_cast<const int32 *>(value)[0]); 1483 fCore->UpdateResamplingAlgorithm(); 1484 break; 1485 case 80: // Refuse output format changes 1486 if (size != sizeof(int32)) 1487 goto err; 1488 fCore->Settings()->SetRefuseOutputFormatChange(static_cast<const int32 *>(value)[0]); 1489 break; 1490 case 90: // Refuse input format changes 1491 if (size != sizeof(int32)) 1492 goto err; 1493 fCore->Settings()->SetRefuseInputFormatChange(static_cast<const int32 *>(value)[0]); 1494 break; 1495 default: 1496 ERROR("unhandled ETC 0x%08lx\n", id); 1497 break; 1498 } 1499 } else if (param == 0) { 1500 MixerOutput *output = fCore->Output(); 1501 if (!output || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_SRC_ENABLE(id) && !PARAM_IS_SRC_GAIN(id) && !PARAM_IS_BALANCE(id))) 1502 goto err; 1503 if (PARAM_IS_MUTE(id)) { 1504 // output mute control 1505 if (size != sizeof(int32)) 1506 goto err; 1507 output->SetMuted(static_cast<const int32 *>(value)[0]); 1508 } 1509 if (PARAM_IS_GAIN(id)) { 1510 // output gain control 1511 if (fCore->Settings()->UseBalanceControl() 1512 && output->GetOutputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1513 // single channel control + balance 1514 float l = output->GetOutputChannelGain(0); 1515 float r = output->GetOutputChannelGain(1); 1516 float m = (l + r) / 2; // master volume 1517 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]); 1518 float f = v / m; // factor for both channels 1519 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f); 1520 output->SetOutputChannelGain(0, output->GetOutputChannelGain(0) * f); 1521 output->SetOutputChannelGain(1, output->GetOutputChannelGain(1) * f); 1522 } else { 1523 // multi channel control 1524 if (size == sizeof(float)) { 1525 // set same volume for all channels 1526 float gain = static_cast<const float *>(value)[0]; 1527 for (int channel = 0; 1528 channel < output->GetOutputChannelCount(); 1529 channel++) { 1530 output->SetOutputChannelGain(channel, 1531 DB_TO_GAIN(gain)); 1532 } 1533 } else { 1534 if (size < output->GetOutputChannelCount() * sizeof(float)) 1535 goto err; 1536 for (int channel = 0; 1537 channel < output->GetOutputChannelCount(); 1538 channel++) { 1539 output->SetOutputChannelGain(channel, 1540 DB_TO_GAIN(static_cast<const float *>( 1541 value)[channel])); 1542 } 1543 } 1544 } 1545 } 1546 if (PARAM_IS_BALANCE(id)) { 1547 float l = output->GetOutputChannelGain(0); 1548 float r = output->GetOutputChannelGain(1); 1549 float m = (l + r) / 2; // master volume 1550 float v = static_cast<const float *>(value)[0] / 100; // current balance value 1551 float fl = 2 * (1 - v); // left channel factor of master volume 1552 float fr = 2 * v; // right channel factor of master volume 1553 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); 1554 output->SetOutputChannelGain(0, m * fl); 1555 output->SetOutputChannelGain(1, m * fr); 1556 } 1557 if (PARAM_IS_SRC_ENABLE(id)) { 1558 if (size != sizeof(int32)) 1559 goto err; 1560 if (static_cast<const int32 *>(value)[0]) { 1561 output->AddOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id)); 1562 } else { 1563 output->RemoveOutputChannelSource(PARAM_CHAN(id), PARAM_SRC(id)); 1564 } 1565 } 1566 if (PARAM_IS_SRC_GAIN(id)) { 1567 if (size != sizeof(float)) 1568 goto err; 1569 output->SetOutputChannelSourceGain(PARAM_CHAN(id), PARAM_SRC(id), PERCENT_TO_GAIN(static_cast<const float *>(value)[0])); 1570 } 1571 fCore->Settings()->SaveConnectionSettings(output); 1572 } else { 1573 MixerInput *input; 1574 for (int i = 0; (input = fCore->Input(i)); i++) 1575 if (input->ID() == param) 1576 break; 1577 if (!input || (!PARAM_IS_MUTE(id) && !PARAM_IS_GAIN(id) && !PARAM_IS_DST_ENABLE(id) && !PARAM_IS_BALANCE(id))) 1578 goto err; 1579 if (PARAM_IS_MUTE(id)) { 1580 // input mute control 1581 if (size != sizeof(int32)) 1582 goto err; 1583 input->SetEnabled(!static_cast<const int32 *>(value)[0]); 1584 } 1585 if (PARAM_IS_GAIN(id)) { 1586 // input gain control 1587 if (fCore->Settings()->InputGainControls() == 0) { 1588 // Physical input channels 1589 if (fCore->Settings()->UseBalanceControl() && input->GetInputChannelCount() == 2 && 1 /*channel mask is stereo */) { 1590 // single channel control + balance 1591 float l = input->GetInputChannelGain(0); 1592 float r = input->GetInputChannelGain(1); 1593 float m = (l + r) / 2; // master volume 1594 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]); 1595 float f = v / m; // factor for both channels 1596 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f); 1597 input->SetInputChannelGain(0, input->GetInputChannelGain(0) * f); 1598 input->SetInputChannelGain(1, input->GetInputChannelGain(1) * f); 1599 } else { 1600 // multi channel control 1601 if (size < input->GetInputChannelCount() * sizeof(float)) 1602 goto err; 1603 for (int chan = 0; chan < input->GetInputChannelCount(); chan++) 1604 input->SetInputChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan])); 1605 } 1606 } else { 1607 // Virtual output channels 1608 if (fCore->Settings()->UseBalanceControl() && input->GetMixerChannelCount() == 2 && 1 /*channel mask is stereo */) { 1609 // single channel control + balance 1610 float l = input->GetMixerChannelGain(0); 1611 float r = input->GetMixerChannelGain(1); 1612 float m = (l + r) / 2; // master volume 1613 float v = DB_TO_GAIN(static_cast<const float *>(value)[0]); 1614 float f = v / m; // factor for both channels 1615 TRACE("gain set l %1.3f, r %1.3f, m %1.3f, v %1.3f, f %1.3f\n",l,r,m,v,f); 1616 input->SetMixerChannelGain(0, input->GetMixerChannelGain(0) * f); 1617 input->SetMixerChannelGain(1, input->GetMixerChannelGain(1) * f); 1618 } else { 1619 // multi channel control 1620 if (size < input->GetMixerChannelCount() * sizeof(float)) 1621 goto err; 1622 for (int chan = 0; chan < input->GetMixerChannelCount(); chan++) 1623 input->SetMixerChannelGain(chan, DB_TO_GAIN(static_cast<const float *>(value)[chan])); 1624 } 1625 } 1626 } 1627 if (PARAM_IS_BALANCE(id)) { 1628 if (fCore->Settings()->InputGainControls() == 0) { 1629 // Physical input channels 1630 float l = input->GetInputChannelGain(0); 1631 float r = input->GetInputChannelGain(1); 1632 float m = (l + r) / 2; // master volume 1633 float v = static_cast<const float *>(value)[0] / 100; // current balance value 1634 float fl = 2 * (1 - v); // left channel factor of master volume 1635 float fr = 2 * v; // right channel factor of master volume 1636 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); 1637 input->SetInputChannelGain(0, m * fl); 1638 input->SetInputChannelGain(1, m * fr); 1639 } else { 1640 // Virtual output channels 1641 float l = input->GetMixerChannelGain(0); 1642 float r = input->GetMixerChannelGain(1); 1643 float m = (l + r) / 2; // master volume 1644 float v = static_cast<const float *>(value)[0] / 100; // current balance value 1645 float fl = 2 * (1 - v); // left channel factor of master volume 1646 float fr = 2 * v; // right channel factor of master volume 1647 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); 1648 input->SetMixerChannelGain(0, m * fl); 1649 input->SetMixerChannelGain(1, m * fr); 1650 } 1651 } 1652 if (PARAM_IS_DST_ENABLE(id)) { 1653 if (size != sizeof(int32)) 1654 goto err; 1655 if (static_cast<const int32 *>(value)[0]) { 1656 int oldchan = input->GetInputChannelForDestination(PARAM_DST(id)); 1657 if (oldchan != -1) { 1658 input->RemoveInputChannelDestination(oldchan, PARAM_DST(id)); 1659 int32 null = 0; 1660 BroadcastNewParameterValue(when, PARAM_DST_ENABLE(PARAM(id), oldchan, PARAM_DST(id)), &null, sizeof(null)); 1661 } 1662 input->AddInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id)); 1663 } else { 1664 input->RemoveInputChannelDestination(PARAM_CHAN(id), PARAM_DST(id)); 1665 } 1666 // TODO: this is really annoying 1667 // The slider count of the gain control needs to be changed, 1668 // but calling SetChannelCount(input->GetMixerChannelCount()) 1669 // on it has no effect on remote BParameterWebs in other apps. 1670 // BroadcastChangedParameter() should be correct, but doesn't work 1671 BroadcastChangedParameter(PARAM_GAIN(PARAM(id))); 1672 // We trigger a complete ParameterWeb update as workaround 1673 // but it will change the focus from tab 3 to tab 1 1674 update = true; 1675 } 1676 fCore->Settings()->SaveConnectionSettings(input); 1677 } 1678 1679 BroadcastNewParameterValue(when, id, const_cast<void *>(value), size); 1680 1681 err: 1682 fCore->Unlock(); 1683 if (update) 1684 UpdateParameterWeb(); 1685 } 1686 1687 1688 void 1689 AudioMixer::UpdateParameterWeb() 1690 { 1691 fCore->Lock(); 1692 BParameterWeb *web = new BParameterWeb(); 1693 BParameterGroup *top; 1694 BParameterGroup *outputchannels; 1695 BParameterGroup *inputchannels; 1696 BParameterGroup *group; 1697 BParameterGroup *subgroup; 1698 BParameterGroup *subsubgroup; 1699 BDiscreteParameter *dp; 1700 MixerInput *in; 1701 MixerOutput *out; 1702 char buf[50]; 1703 1704 top = web->MakeGroup(B_TRANSLATE("Gain controls")); 1705 1706 out = fCore->Output(); 1707 group = top->MakeGroup(""); 1708 group->MakeNullParameter(PARAM_STR1(0), B_MEDIA_RAW_AUDIO, 1709 B_TRANSLATE("Master output"), B_WEB_BUFFER_INPUT); 1710 if (!out) { 1711 group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO, 1712 B_TRANSLATE("not connected"), B_GENERIC); 1713 } else { 1714 group->MakeNullParameter(PARAM_STR2(0), B_MEDIA_RAW_AUDIO, 1715 StringForFormat(buf, out), B_GENERIC); 1716 group->MakeDiscreteParameter(PARAM_MUTE(0), B_MEDIA_RAW_AUDIO, 1717 B_TRANSLATE("Mute"), B_MUTE); 1718 if (fCore->Settings()->UseBalanceControl() 1719 && out->GetOutputChannelCount() == 2 && 1 1720 /*channel mask is stereo */) { 1721 // single channel control + balance 1722 group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO, 1723 B_TRANSLATE("Gain"), B_MASTER_GAIN, B_TRANSLATE("dB"), 1724 DB_MIN, DB_MAX, 0.1); 1725 group->MakeContinuousParameter(PARAM_BALANCE(0), B_MEDIA_RAW_AUDIO, 1726 "", B_BALANCE, "", 0, 100, 1); 1727 } else { 1728 // multi channel control 1729 group->MakeContinuousParameter(PARAM_GAIN(0), B_MEDIA_RAW_AUDIO, 1730 B_TRANSLATE("Gain"), B_MASTER_GAIN, B_TRANSLATE("dB"), 1731 DB_MIN, DB_MAX, 0.1) 1732 ->SetChannelCount(out->GetOutputChannelCount()); 1733 } 1734 group->MakeNullParameter(PARAM_STR3(0), B_MEDIA_RAW_AUDIO, 1735 B_TRANSLATE("To output"), B_WEB_BUFFER_OUTPUT); 1736 } 1737 1738 for (int i = 0; (in = fCore->Input(i)); i++) { 1739 group = top->MakeGroup(""); 1740 group->MakeNullParameter(PARAM_STR1(in->ID()), B_MEDIA_RAW_AUDIO, 1741 in->MediaInput().name, B_WEB_BUFFER_INPUT); 1742 group->MakeNullParameter(PARAM_STR2(in->ID()), B_MEDIA_RAW_AUDIO, 1743 StringForFormat(buf, in), B_GENERIC); 1744 group->MakeDiscreteParameter(PARAM_MUTE(in->ID()), B_MEDIA_RAW_AUDIO, 1745 B_TRANSLATE("Mute"), B_MUTE); 1746 // XXX the gain control is ugly once you have more than two channels, 1747 // as you don't know what channel each slider controls. Tooltips might help... 1748 if (fCore->Settings()->InputGainControls() == 0) { 1749 // Physical input channels 1750 if (fCore->Settings()->UseBalanceControl() 1751 && in->GetInputChannelCount() == 2 && 1 1752 /*channel mask is stereo */) { 1753 // single channel control + balance 1754 group->MakeContinuousParameter(PARAM_GAIN(in->ID()), 1755 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN, 1756 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1); 1757 group->MakeContinuousParameter(PARAM_BALANCE(in->ID()), 1758 B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1); 1759 } else { 1760 // multi channel control 1761 group->MakeContinuousParameter(PARAM_GAIN(in->ID()), 1762 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN, 1763 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1) 1764 ->SetChannelCount(in->GetInputChannelCount()); 1765 } 1766 } else { 1767 // Virtual output channels 1768 if (fCore->Settings()->UseBalanceControl() 1769 && in->GetMixerChannelCount() == 2 && 1 1770 /*channel mask is stereo */) { 1771 // single channel control + balance 1772 group->MakeContinuousParameter(PARAM_GAIN(in->ID()), 1773 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN, 1774 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1); 1775 group->MakeContinuousParameter(PARAM_BALANCE(in->ID()), 1776 B_MEDIA_RAW_AUDIO, "", B_BALANCE, "", 0, 100, 1); 1777 } else { 1778 // multi channel control 1779 group->MakeContinuousParameter(PARAM_GAIN(in->ID()), 1780 B_MEDIA_RAW_AUDIO, B_TRANSLATE("Gain"), B_GAIN, 1781 B_TRANSLATE("dB"), DB_MIN, DB_MAX, 0.1) 1782 ->SetChannelCount(in->GetMixerChannelCount()); 1783 } 1784 } 1785 group->MakeNullParameter(PARAM_STR3(in->ID()), B_MEDIA_RAW_AUDIO, 1786 B_TRANSLATE("To master"), B_WEB_BUFFER_OUTPUT); 1787 } 1788 1789 if (fCore->Settings()->AllowOutputChannelRemapping()) { 1790 top = web->MakeGroup(B_TRANSLATE("Output mapping")); // top level group 1791 outputchannels = top->MakeGroup(""); 1792 outputchannels->MakeNullParameter(PARAM_STR4(0), B_MEDIA_RAW_AUDIO, 1793 B_TRANSLATE("Output channel sources"), B_GENERIC); 1794 1795 group = outputchannels->MakeGroup(""); 1796 group->MakeNullParameter(PARAM_STR5(0), B_MEDIA_RAW_AUDIO, 1797 B_TRANSLATE("Master output"), B_GENERIC); 1798 group = group->MakeGroup(""); 1799 if (!out) { 1800 group->MakeNullParameter(PARAM_STR6(0), B_MEDIA_RAW_AUDIO, 1801 B_TRANSLATE("not connected"), B_GENERIC); 1802 } else { 1803 for (int chan = 0; chan < out->GetOutputChannelCount(); chan++) { 1804 subgroup = group->MakeGroup(""); 1805 subgroup->MakeNullParameter(PARAM_SRC_STR(0, chan), 1806 B_MEDIA_RAW_AUDIO, StringForChannelType(buf, 1807 out->GetOutputChannelType(chan)), B_GENERIC); 1808 for (int src = 0; src < MAX_CHANNEL_TYPES; src++) { 1809 subsubgroup = subgroup->MakeGroup(""); 1810 subsubgroup->MakeDiscreteParameter( 1811 PARAM_SRC_ENABLE(0, chan, src), B_MEDIA_RAW_AUDIO, "", 1812 B_ENABLE); 1813 subsubgroup->MakeContinuousParameter( 1814 PARAM_SRC_GAIN(0, chan, src), B_MEDIA_RAW_AUDIO, 1815 StringForChannelType(buf, src), B_GAIN, "%", 0.0, 1816 100.0, 0.1); 1817 } 1818 } 1819 } 1820 } 1821 1822 if (fCore->Settings()->AllowInputChannelRemapping()) { 1823 top = web->MakeGroup(B_TRANSLATE("Input mapping")); // top level group 1824 inputchannels = top->MakeGroup(""); 1825 inputchannels->MakeNullParameter(PARAM_STR7(0), B_MEDIA_RAW_AUDIO, 1826 B_TRANSLATE("Input channel destinations"), B_GENERIC); 1827 1828 for (int i = 0; (in = fCore->Input(i)); i++) { 1829 group = inputchannels->MakeGroup(""); 1830 group->MakeNullParameter(PARAM_STR4(in->ID()), B_MEDIA_RAW_AUDIO, 1831 in->MediaInput().name, B_GENERIC); 1832 group = group->MakeGroup(""); 1833 1834 for (int chan = 0; chan < in->GetInputChannelCount(); chan++) { 1835 subgroup = group->MakeGroup(""); 1836 subgroup->MakeNullParameter(PARAM_DST_STR(in->ID(), chan), 1837 B_MEDIA_RAW_AUDIO, StringForChannelType(buf, 1838 in->GetInputChannelType(chan)), B_GENERIC); 1839 for (int dst = 0; dst < MAX_CHANNEL_TYPES; dst++) { 1840 subgroup->MakeDiscreteParameter(PARAM_DST_ENABLE(in->ID(), 1841 chan, dst), B_MEDIA_RAW_AUDIO, StringForChannelType(buf, dst), 1842 B_ENABLE); 1843 } 1844 } 1845 } 1846 } 1847 1848 top = web->MakeGroup(B_TRANSLATE("Setup")); // top level group 1849 group = top->MakeGroup(""); 1850 1851 group->MakeDiscreteParameter(PARAM_ETC(10), B_MEDIA_RAW_AUDIO, 1852 B_TRANSLATE("Attenuate mixer output by 3dB (like BeOS R5)"), B_ENABLE); 1853 group->MakeDiscreteParameter(PARAM_ETC(20), B_MEDIA_RAW_AUDIO, 1854 B_TRANSLATE("Use non linear gain sliders (like BeOS R5)"), B_ENABLE); 1855 group->MakeDiscreteParameter(PARAM_ETC(30), B_MEDIA_RAW_AUDIO, 1856 B_TRANSLATE("Display balance control for stereo connections"), 1857 B_ENABLE); 1858 1859 group->MakeDiscreteParameter(PARAM_ETC(40), B_MEDIA_RAW_AUDIO, 1860 B_TRANSLATE("Allow output channel remapping"), B_ENABLE); 1861 group->MakeDiscreteParameter(PARAM_ETC(50), B_MEDIA_RAW_AUDIO, 1862 B_TRANSLATE("Allow input channel remapping"), B_ENABLE); 1863 1864 dp = group->MakeDiscreteParameter(PARAM_ETC(60), B_MEDIA_RAW_AUDIO, 1865 B_TRANSLATE("Input gain controls represent"), B_INPUT_MUX); 1866 dp->AddItem(0, B_TRANSLATE("Physical input channels")); 1867 dp->AddItem(1, B_TRANSLATE("Virtual output channels")); 1868 1869 dp = group->MakeDiscreteParameter(PARAM_ETC(70), B_MEDIA_RAW_AUDIO, 1870 B_TRANSLATE("Resampling algorithm"), B_INPUT_MUX); 1871 dp->AddItem(0, B_TRANSLATE("Drop/repeat samples")); 1872 dp->AddItem(2, B_TRANSLATE("Linear interpolation")); 1873 1874 // Note: The following code is outcommented on purpose 1875 // and is about to be modified at a later point 1876 /* 1877 dp->AddItem(1, B_TRANSLATE("Drop/repeat samples (template based)")); 1878 dp->AddItem(3, B_TRANSLATE("17th order filtering")); 1879 */ 1880 group->MakeDiscreteParameter(PARAM_ETC(80), B_MEDIA_RAW_AUDIO, 1881 B_TRANSLATE("Refuse output format changes"), B_ENABLE); 1882 group->MakeDiscreteParameter(PARAM_ETC(90), B_MEDIA_RAW_AUDIO, 1883 B_TRANSLATE("Refuse input format changes"), B_ENABLE); 1884 1885 fCore->Unlock(); 1886 SetParameterWeb(web); 1887 } 1888 1889 1890 #if USE_MEDIA_FORMAT_WORKAROUND 1891 static void 1892 raw_audio_format_specialize(media_raw_audio_format *format, 1893 const media_raw_audio_format *other) 1894 { 1895 if (format->frame_rate == 0) 1896 format->frame_rate = other->frame_rate; 1897 if (format->channel_count == 0) 1898 format->channel_count = other->channel_count; 1899 if (format->format == 0) 1900 format->format = other->format; 1901 if (format->byte_order == 0) 1902 format->byte_order = other->byte_order; 1903 if (format->buffer_size == 0) 1904 format->buffer_size = other->buffer_size; 1905 if (format->frame_rate == 0) 1906 format->frame_rate = other->frame_rate; 1907 } 1908 1909 1910 static void 1911 multi_audio_info_specialize(media_multi_audio_info *format, 1912 const media_multi_audio_info *other) 1913 { 1914 if (format->channel_mask == 0) 1915 format->channel_mask = other->channel_mask; 1916 if (format->valid_bits == 0) 1917 format->valid_bits = other->valid_bits; 1918 if (format->matrix_mask == 0) 1919 format->matrix_mask = other->matrix_mask; 1920 } 1921 1922 1923 static void 1924 multi_audio_format_specialize(media_multi_audio_format *format, 1925 const media_multi_audio_format *other) 1926 { 1927 raw_audio_format_specialize(format, other); 1928 multi_audio_info_specialize(format, other); 1929 } 1930 #endif 1931