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