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