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