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