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