1 /* 2 * Copyright 2002-2009, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marcus Overhagen 7 * Jérôme Duval 8 */ 9 10 11 #include <SoundPlayer.h> 12 13 #include <math.h> 14 #include <string.h> 15 16 #include <Autolock.h> 17 #include <MediaRoster.h> 18 #include <ParameterWeb.h> 19 #include <Sound.h> 20 #include <TimeSource.h> 21 22 #include "SoundPlayNode.h" 23 24 #include "debug.h" 25 26 27 // Flags used internally in BSoundPlayer 28 enum { 29 F_NODES_CONNECTED = (1 << 0), 30 F_HAS_DATA = (1 << 1), 31 F_IS_STARTED = (1 << 2), 32 F_MUST_RELEASE_MIXER = (1 << 3), 33 }; 34 35 36 static BSoundPlayer::play_id sCurrentPlayID = 1; 37 38 39 BSoundPlayer::BSoundPlayer(const char* name, BufferPlayerFunc playerFunction, 40 EventNotifierFunc eventNotifierFunction, void* cookie) 41 { 42 CALLED(); 43 44 TRACE("BSoundPlayer::BSoundPlayer: default constructor used\n"); 45 46 media_multi_audio_format format = media_multi_audio_format::wildcard; 47 48 _Init(NULL, &format, name, NULL, playerFunction, eventNotifierFunction, 49 cookie); 50 } 51 52 53 BSoundPlayer::BSoundPlayer(const media_raw_audio_format* _format, 54 const char* name, BufferPlayerFunc playerFunction, 55 EventNotifierFunc eventNotifierFunction, void* cookie) 56 { 57 CALLED(); 58 59 TRACE("BSoundPlayer::BSoundPlayer: raw audio format constructor used\n"); 60 61 media_multi_audio_format format = media_multi_audio_format::wildcard; 62 *(media_raw_audio_format*)&format = *_format; 63 64 #if DEBUG > 0 65 char buf[100]; 66 media_format tmp; tmp.type = B_MEDIA_RAW_AUDIO; tmp.u.raw_audio = format; 67 string_for_format(tmp, buf, sizeof(buf)); 68 TRACE("BSoundPlayer::BSoundPlayer: format %s\n", buf); 69 #endif 70 71 _Init(NULL, &format, name, NULL, playerFunction, eventNotifierFunction, 72 cookie); 73 } 74 75 76 BSoundPlayer::BSoundPlayer(const media_node& toNode, 77 const media_multi_audio_format* format, const char* name, 78 const media_input* input, BufferPlayerFunc playerFunction, 79 EventNotifierFunc eventNotifierFunction, void* cookie) 80 { 81 CALLED(); 82 83 TRACE("BSoundPlayer::BSoundPlayer: multi audio format constructor used\n"); 84 85 if ((toNode.kind & B_BUFFER_CONSUMER) == 0) 86 debugger("BSoundPlayer: toNode must have B_BUFFER_CONSUMER kind!\n"); 87 88 #if DEBUG > 0 89 char buf[100]; 90 media_format tmp; tmp.type = B_MEDIA_RAW_AUDIO; tmp.u.raw_audio = *format; 91 string_for_format(tmp, buf, sizeof(buf)); 92 TRACE("BSoundPlayer::BSoundPlayer: format %s\n", buf); 93 #endif 94 95 _Init(&toNode, format, name, input, playerFunction, eventNotifierFunction, 96 cookie); 97 } 98 99 100 BSoundPlayer::~BSoundPlayer() 101 { 102 CALLED(); 103 104 if ((fFlags & F_IS_STARTED) != 0) { 105 // block, but don't flush 106 Stop(true, false); 107 } 108 109 status_t err; 110 BMediaRoster* roster = BMediaRoster::Roster(); 111 if (roster == NULL) { 112 TRACE("BSoundPlayer::~BSoundPlayer: Couldn't get BMediaRoster\n"); 113 goto cleanup; 114 } 115 116 if ((fFlags & F_NODES_CONNECTED) != 0) { 117 // Ordinarily we'd stop *all* of the nodes in the chain before 118 // disconnecting. However, our node is already stopped, and we can't 119 // stop the System Mixer. 120 // So, we just disconnect from it, and release our references to the 121 // nodes that we're using. We *are* supposed to do that even for global 122 // nodes like the Mixer. 123 err = roster->Disconnect(fMediaOutput, fMediaInput); 124 if (err != B_OK) { 125 TRACE("BSoundPlayer::~BSoundPlayer: Error disconnecting nodes: " 126 "%" B_PRId32 " (%s)\n", err, strerror(err)); 127 } 128 } 129 130 if ((fFlags & F_MUST_RELEASE_MIXER) != 0) { 131 // Release the mixer as it was acquired 132 // through BMediaRoster::GetAudioMixer() 133 err = roster->ReleaseNode(fMediaInput.node); 134 if (err != B_OK) { 135 TRACE("BSoundPlayer::~BSoundPlayer: Error releasing input node: " 136 "%" B_PRId32 " (%s)\n", err, strerror(err)); 137 } 138 } 139 140 cleanup: 141 // Dispose of the player node 142 143 // We do not call BMediaRoster::ReleaseNode(), since 144 // the player was created by using "new". We could 145 // call BMediaRoster::UnregisterNode(), but this is 146 // supposed to be done by BMediaNode destructor automatically. 147 148 // The node is deleted by the Release() when ref count reach 0. 149 // Since we are the sole owners, and no one acquired it 150 // this should be the case. The Quit() synchronization 151 // is handled by the DeleteHook inheritance. 152 // NOTE: this might be crucial when using a BMediaEventLooper. 153 if (fPlayerNode->Release() != NULL) { 154 TRACE("BSoundPlayer::~BSoundPlayer: Error the producer node " 155 "appears to be acquired by someone else than us!"); 156 } 157 158 // do not delete fVolumeSlider, it belongs to the parameter web 159 delete fParameterWeb; 160 } 161 162 163 status_t 164 BSoundPlayer::InitCheck() 165 { 166 CALLED(); 167 return fInitStatus; 168 } 169 170 171 media_raw_audio_format 172 BSoundPlayer::Format() const 173 { 174 CALLED(); 175 176 if ((fFlags & F_NODES_CONNECTED) == 0) 177 return media_raw_audio_format::wildcard; 178 179 return fPlayerNode->Format(); 180 } 181 182 183 status_t 184 BSoundPlayer::Start() 185 { 186 CALLED(); 187 188 if ((fFlags & F_NODES_CONNECTED) == 0) 189 return B_NO_INIT; 190 191 if ((fFlags & F_IS_STARTED) != 0) 192 return B_OK; 193 194 BMediaRoster* roster = BMediaRoster::Roster(); 195 if (!roster) { 196 TRACE("BSoundPlayer::Start: Couldn't get BMediaRoster\n"); 197 return B_ERROR; 198 } 199 200 if (!fPlayerNode->TimeSource()->IsRunning()) { 201 roster->StartTimeSource(fPlayerNode->TimeSource()->Node(), 202 fPlayerNode->TimeSource()->RealTime()); 203 } 204 205 // Add latency and a few ms to the nodes current time to 206 // make sure that we give the producer enough time to run 207 // buffers through the node chain, otherwise it'll start 208 // up already late 209 210 status_t err = roster->StartNode(fPlayerNode->Node(), 211 fPlayerNode->TimeSource()->Now() + Latency() + 5000); 212 if (err != B_OK) { 213 TRACE("BSoundPlayer::Start: StartNode failed, %" B_PRId32, err); 214 return err; 215 } 216 217 if (fNotifierFunc != NULL) 218 fNotifierFunc(fCookie, B_STARTED, this); 219 220 SetHasData(true); 221 atomic_or(&fFlags, F_IS_STARTED); 222 223 return B_OK; 224 } 225 226 227 void 228 BSoundPlayer::Stop(bool block, bool flush) 229 { 230 CALLED(); 231 232 TRACE("BSoundPlayer::Stop: block %d, flush %d\n", (int)block, (int)flush); 233 234 if ((fFlags & F_NODES_CONNECTED) == 0) 235 return; 236 237 // TODO: flush is ignored 238 239 if ((fFlags & F_IS_STARTED) != 0) { 240 BMediaRoster* roster = BMediaRoster::Roster(); 241 if (roster == NULL) { 242 TRACE("BSoundPlayer::Stop: Couldn't get BMediaRoster\n"); 243 return; 244 } 245 246 roster->StopNode(fPlayerNode->Node(), 0, true); 247 248 atomic_and(&fFlags, ~F_IS_STARTED); 249 } 250 251 if (block) { 252 // wait until the node is stopped 253 int tries; 254 for (tries = 250; fPlayerNode->IsPlaying() && tries != 0; tries--) 255 snooze(2000); 256 257 DEBUG_ONLY(if (tries == 0) 258 TRACE("BSoundPlayer::Stop: waiting for node stop failed\n")); 259 260 // Wait until all buffers on the way to the physical output have been 261 // played 262 snooze(Latency() + 2000); 263 } 264 265 if (fNotifierFunc) 266 fNotifierFunc(fCookie, B_STOPPED, this); 267 268 } 269 270 271 bigtime_t 272 BSoundPlayer::Latency() 273 { 274 CALLED(); 275 276 if ((fFlags & F_NODES_CONNECTED) == 0) 277 return 0; 278 279 BMediaRoster *roster = BMediaRoster::Roster(); 280 if (!roster) { 281 TRACE("BSoundPlayer::Latency: Couldn't get BMediaRoster\n"); 282 return 0; 283 } 284 285 bigtime_t latency; 286 status_t err = roster->GetLatencyFor(fMediaOutput.node, &latency); 287 if (err != B_OK) { 288 TRACE("BSoundPlayer::Latency: GetLatencyFor failed %" B_PRId32 289 " (%s)\n", err, strerror(err)); 290 return 0; 291 } 292 293 TRACE("BSoundPlayer::Latency: latency is %" B_PRId64 "\n", latency); 294 295 return latency; 296 } 297 298 299 void 300 BSoundPlayer::SetHasData(bool hasData) 301 { 302 CALLED(); 303 if (hasData) 304 atomic_or(&fFlags, F_HAS_DATA); 305 else 306 atomic_and(&fFlags, ~F_HAS_DATA); 307 } 308 309 310 bool 311 BSoundPlayer::HasData() 312 { 313 CALLED(); 314 return (atomic_get(&fFlags) & F_HAS_DATA) != 0; 315 } 316 317 318 BSoundPlayer::BufferPlayerFunc 319 BSoundPlayer::BufferPlayer() const 320 { 321 CALLED(); 322 return fPlayBufferFunc; 323 } 324 325 326 void 327 BSoundPlayer::SetBufferPlayer(BufferPlayerFunc playerFunction) 328 { 329 CALLED(); 330 BAutolock _(fLocker); 331 332 fPlayBufferFunc = playerFunction; 333 } 334 335 336 BSoundPlayer::EventNotifierFunc 337 BSoundPlayer::EventNotifier() const 338 { 339 CALLED(); 340 return fNotifierFunc; 341 } 342 343 344 void 345 BSoundPlayer::SetNotifier(EventNotifierFunc eventNotifierFunction) 346 { 347 CALLED(); 348 BAutolock _(fLocker); 349 350 fNotifierFunc = eventNotifierFunction; 351 } 352 353 354 void* 355 BSoundPlayer::Cookie() const 356 { 357 CALLED(); 358 return fCookie; 359 } 360 361 362 void 363 BSoundPlayer::SetCookie(void *cookie) 364 { 365 CALLED(); 366 BAutolock _(fLocker); 367 368 fCookie = cookie; 369 } 370 371 372 void 373 BSoundPlayer::SetCallbacks(BufferPlayerFunc playerFunction, 374 EventNotifierFunc eventNotifierFunction, void* cookie) 375 { 376 CALLED(); 377 BAutolock _(fLocker); 378 379 SetBufferPlayer(playerFunction); 380 SetNotifier(eventNotifierFunction); 381 SetCookie(cookie); 382 } 383 384 385 /*! The BeBook is inaccurate about the meaning of this function. 386 The probably best interpretation is to return the time that 387 has elapsed since playing was started, whichs seems to match 388 "CurrentTime() returns the current media time" 389 */ 390 bigtime_t 391 BSoundPlayer::CurrentTime() 392 { 393 if ((fFlags & F_NODES_CONNECTED) == 0) 394 return 0; 395 396 return fPlayerNode->CurrentTime(); 397 } 398 399 400 /*! Returns the current performance time of the sound player node 401 being used by the BSoundPlayer. Will return B_ERROR if the 402 BSoundPlayer object hasn't been properly initialized. 403 */ 404 bigtime_t 405 BSoundPlayer::PerformanceTime() 406 { 407 if ((fFlags & F_NODES_CONNECTED) == 0) 408 return (bigtime_t) B_ERROR; 409 410 return fPlayerNode->TimeSource()->Now(); 411 } 412 413 414 status_t 415 BSoundPlayer::Preroll() 416 { 417 CALLED(); 418 419 if ((fFlags & F_NODES_CONNECTED) == 0) 420 return B_NO_INIT; 421 422 BMediaRoster* roster = BMediaRoster::Roster(); 423 if (roster == NULL) { 424 TRACE("BSoundPlayer::Preroll: Couldn't get BMediaRoster\n"); 425 return B_ERROR; 426 } 427 428 status_t err = roster->PrerollNode(fMediaOutput.node); 429 if (err != B_OK) { 430 TRACE("BSoundPlayer::Preroll: Error while PrerollNode: %" 431 B_PRId32 " (%s)\n", err, strerror(err)); 432 return err; 433 } 434 435 return B_OK; 436 } 437 438 439 BSoundPlayer::play_id 440 BSoundPlayer::StartPlaying(BSound* sound, bigtime_t atTime) 441 { 442 return StartPlaying(sound, atTime, 1.0); 443 } 444 445 446 BSoundPlayer::play_id 447 BSoundPlayer::StartPlaying(BSound* sound, bigtime_t atTime, float withVolume) 448 { 449 CALLED(); 450 451 // TODO: support the at_time and with_volume parameters 452 playing_sound* item = (playing_sound*)malloc(sizeof(playing_sound)); 453 if (item == NULL) 454 return B_NO_MEMORY; 455 456 item->current_offset = 0; 457 item->sound = sound; 458 item->id = atomic_add(&sCurrentPlayID, 1); 459 item->delta = 0; 460 item->rate = 0; 461 item->volume = withVolume; 462 463 if (!fLocker.Lock()) { 464 free(item); 465 return B_ERROR; 466 } 467 468 sound->AcquireRef(); 469 item->next = fPlayingSounds; 470 fPlayingSounds = item; 471 fLocker.Unlock(); 472 473 SetHasData(true); 474 return item->id; 475 } 476 477 478 status_t 479 BSoundPlayer::SetSoundVolume(play_id id, float newVolume) 480 { 481 CALLED(); 482 if (!fLocker.Lock()) 483 return B_ERROR; 484 485 playing_sound *item = fPlayingSounds; 486 while (item) { 487 if (item->id == id) { 488 item->volume = newVolume; 489 fLocker.Unlock(); 490 return B_OK; 491 } 492 493 item = item->next; 494 } 495 496 fLocker.Unlock(); 497 return B_ENTRY_NOT_FOUND; 498 } 499 500 501 bool 502 BSoundPlayer::IsPlaying(play_id id) 503 { 504 CALLED(); 505 if (!fLocker.Lock()) 506 return B_ERROR; 507 508 playing_sound *item = fPlayingSounds; 509 while (item) { 510 if (item->id == id) { 511 fLocker.Unlock(); 512 return true; 513 } 514 515 item = item->next; 516 } 517 518 fLocker.Unlock(); 519 return false; 520 } 521 522 523 status_t 524 BSoundPlayer::StopPlaying(play_id id) 525 { 526 CALLED(); 527 if (!fLocker.Lock()) 528 return B_ERROR; 529 530 playing_sound** link = &fPlayingSounds; 531 playing_sound* item = fPlayingSounds; 532 533 while (item != NULL) { 534 if (item->id == id) { 535 *link = item->next; 536 sem_id waitSem = item->wait_sem; 537 item->sound->ReleaseRef(); 538 free(item); 539 fLocker.Unlock(); 540 541 _NotifySoundDone(id, true); 542 if (waitSem >= 0) 543 release_sem(waitSem); 544 545 return B_OK; 546 } 547 548 link = &item->next; 549 item = item->next; 550 } 551 552 fLocker.Unlock(); 553 return B_ENTRY_NOT_FOUND; 554 } 555 556 557 status_t 558 BSoundPlayer::WaitForSound(play_id id) 559 { 560 CALLED(); 561 if (!fLocker.Lock()) 562 return B_ERROR; 563 564 playing_sound* item = fPlayingSounds; 565 while (item != NULL) { 566 if (item->id == id) { 567 sem_id waitSem = item->wait_sem; 568 if (waitSem < 0) 569 waitSem = item->wait_sem = create_sem(0, "wait for sound"); 570 571 fLocker.Unlock(); 572 return acquire_sem(waitSem); 573 } 574 575 item = item->next; 576 } 577 578 fLocker.Unlock(); 579 return B_ENTRY_NOT_FOUND; 580 } 581 582 583 float 584 BSoundPlayer::Volume() 585 { 586 CALLED(); 587 return pow(10.0, VolumeDB(true) / 20.0); 588 } 589 590 591 void 592 BSoundPlayer::SetVolume(float newVolume) 593 { 594 CALLED(); 595 SetVolumeDB(20.0 * log10(newVolume)); 596 } 597 598 599 float 600 BSoundPlayer::VolumeDB(bool forcePoll) 601 { 602 CALLED(); 603 if (!fVolumeSlider) 604 return -94.0f; // silence 605 606 if (!forcePoll && system_time() - fLastVolumeUpdate < 500000) 607 return fVolumeDB; 608 609 int32 count = fVolumeSlider->CountChannels(); 610 float values[count]; 611 size_t size = count * sizeof(float); 612 fVolumeSlider->GetValue(&values, &size, NULL); 613 fLastVolumeUpdate = system_time(); 614 fVolumeDB = values[0]; 615 616 return values[0]; 617 } 618 619 620 void 621 BSoundPlayer::SetVolumeDB(float volumeDB) 622 { 623 CALLED(); 624 if (!fVolumeSlider) 625 return; 626 627 float minDB = fVolumeSlider->MinValue(); 628 float maxDB = fVolumeSlider->MaxValue(); 629 if (volumeDB < minDB) 630 volumeDB = minDB; 631 if (volumeDB > maxDB) 632 volumeDB = maxDB; 633 634 int count = fVolumeSlider->CountChannels(); 635 float values[count]; 636 for (int i = 0; i < count; i++) 637 values[i] = volumeDB; 638 fVolumeSlider->SetValue(values, sizeof(float) * count, 0); 639 640 fVolumeDB = volumeDB; 641 fLastVolumeUpdate = system_time(); 642 } 643 644 645 status_t 646 BSoundPlayer::GetVolumeInfo(media_node* _node, int32* _parameterID, 647 float* _minDB, float* _maxDB) 648 { 649 CALLED(); 650 if (fVolumeSlider == NULL) 651 return B_NO_INIT; 652 653 if (_node != NULL) 654 *_node = fMediaInput.node; 655 if (_parameterID != NULL) 656 *_parameterID = fVolumeSlider->ID(); 657 if (_minDB != NULL) 658 *_minDB = fVolumeSlider->MinValue(); 659 if (_maxDB != NULL) 660 *_maxDB = fVolumeSlider->MaxValue(); 661 662 return B_OK; 663 } 664 665 666 // #pragma mark - protected BSoundPlayer 667 668 669 void 670 BSoundPlayer::SetInitError(status_t error) 671 { 672 CALLED(); 673 fInitStatus = error; 674 } 675 676 677 // #pragma mark - private BSoundPlayer 678 679 680 void 681 BSoundPlayer::_SoundPlayBufferFunc(void *cookie, void *buffer, size_t size, 682 const media_raw_audio_format &format) 683 { 684 // TODO: support more than one sound and make use of the format parameter 685 BSoundPlayer *player = (BSoundPlayer *)cookie; 686 if (!player->fLocker.Lock()) { 687 memset(buffer, 0, size); 688 return; 689 } 690 691 playing_sound *sound = player->fPlayingSounds; 692 if (sound == NULL) { 693 player->SetHasData(false); 694 player->fLocker.Unlock(); 695 memset(buffer, 0, size); 696 return; 697 } 698 699 size_t used = 0; 700 if (!sound->sound->GetDataAt(sound->current_offset, buffer, size, &used)) { 701 // will take care of removing the item and notifying others 702 player->StopPlaying(sound->id); 703 player->fLocker.Unlock(); 704 memset(buffer, 0, size); 705 return; 706 } 707 708 sound->current_offset += used; 709 player->fLocker.Unlock(); 710 711 if (used < size) 712 memset((uint8 *)buffer + used, 0, size - used); 713 } 714 715 716 status_t BSoundPlayer::_Reserved_SoundPlayer_0(void*, ...) { return B_ERROR; } 717 status_t BSoundPlayer::_Reserved_SoundPlayer_1(void*, ...) { return B_ERROR; } 718 status_t BSoundPlayer::_Reserved_SoundPlayer_2(void*, ...) { return B_ERROR; } 719 status_t BSoundPlayer::_Reserved_SoundPlayer_3(void*, ...) { return B_ERROR; } 720 status_t BSoundPlayer::_Reserved_SoundPlayer_4(void*, ...) { return B_ERROR; } 721 status_t BSoundPlayer::_Reserved_SoundPlayer_5(void*, ...) { return B_ERROR; } 722 status_t BSoundPlayer::_Reserved_SoundPlayer_6(void*, ...) { return B_ERROR; } 723 status_t BSoundPlayer::_Reserved_SoundPlayer_7(void*, ...) { return B_ERROR; } 724 725 726 void 727 BSoundPlayer::_Init(const media_node* node, 728 const media_multi_audio_format* format, const char* name, 729 const media_input* input, BufferPlayerFunc playerFunction, 730 EventNotifierFunc eventNotifierFunction, void* cookie) 731 { 732 CALLED(); 733 fPlayingSounds = NULL; 734 fWaitingSounds = NULL; 735 736 fPlayerNode = NULL; 737 if (playerFunction == NULL) { 738 fPlayBufferFunc = _SoundPlayBufferFunc; 739 fCookie = this; 740 } else { 741 fPlayBufferFunc = playerFunction; 742 fCookie = cookie; 743 } 744 745 fNotifierFunc = eventNotifierFunction; 746 fVolumeDB = 0.0f; 747 fFlags = 0; 748 fInitStatus = B_ERROR; 749 fParameterWeb = NULL; 750 fVolumeSlider = NULL; 751 fLastVolumeUpdate = 0; 752 753 BMediaRoster* roster = BMediaRoster::Roster(); 754 if (roster == NULL) { 755 TRACE("BSoundPlayer::_Init: Couldn't get BMediaRoster\n"); 756 return; 757 } 758 759 // The inputNode that our player node will be 760 // connected with is either supplied by the user 761 // or the system audio mixer 762 media_node inputNode; 763 if (node) { 764 inputNode = *node; 765 } else { 766 fInitStatus = roster->GetAudioMixer(&inputNode); 767 if (fInitStatus != B_OK) { 768 TRACE("BSoundPlayer::_Init: Couldn't GetAudioMixer\n"); 769 return; 770 } 771 fFlags |= F_MUST_RELEASE_MIXER; 772 } 773 774 media_output _output; 775 media_input _input; 776 int32 inputCount; 777 int32 outputCount; 778 media_format tryFormat; 779 780 // Create the player node and register it 781 fPlayerNode = new BPrivate::SoundPlayNode(name, this); 782 fInitStatus = roster->RegisterNode(fPlayerNode); 783 if (fInitStatus != B_OK) { 784 TRACE("BSoundPlayer::_Init: Couldn't RegisterNode: %s\n", 785 strerror(fInitStatus)); 786 return; 787 } 788 789 // set the producer's time source to be the "default" time source, 790 // which the system audio mixer uses too. 791 media_node timeSource; 792 fInitStatus = roster->GetTimeSource(&timeSource); 793 if (fInitStatus != B_OK) { 794 TRACE("BSoundPlayer::_Init: Couldn't GetTimeSource: %s\n", 795 strerror(fInitStatus)); 796 return; 797 } 798 fInitStatus = roster->SetTimeSourceFor(fPlayerNode->Node().node, 799 timeSource.node); 800 if (fInitStatus != B_OK) { 801 TRACE("BSoundPlayer::_Init: Couldn't SetTimeSourceFor: %s\n", 802 strerror(fInitStatus)); 803 return; 804 } 805 806 // find a free media_input 807 if (!input) { 808 fInitStatus = roster->GetFreeInputsFor(inputNode, &_input, 1, 809 &inputCount, B_MEDIA_RAW_AUDIO); 810 if (fInitStatus != B_OK) { 811 TRACE("BSoundPlayer::_Init: Couldn't GetFreeInputsFor: %s\n", 812 strerror(fInitStatus)); 813 return; 814 } 815 if (inputCount < 1) { 816 TRACE("BSoundPlayer::_Init: Couldn't find a free input\n"); 817 fInitStatus = B_ERROR; 818 return; 819 } 820 } else { 821 _input = *input; 822 } 823 824 // find a free media_output 825 fInitStatus = roster->GetFreeOutputsFor(fPlayerNode->Node(), &_output, 1, 826 &outputCount, B_MEDIA_RAW_AUDIO); 827 if (fInitStatus != B_OK) { 828 TRACE("BSoundPlayer::_Init: Couldn't GetFreeOutputsFor: %s\n", 829 strerror(fInitStatus)); 830 return; 831 } 832 if (outputCount < 1) { 833 TRACE("BSoundPlayer::_Init: Couldn't find a free output\n"); 834 fInitStatus = B_ERROR; 835 return; 836 } 837 838 // Set an appropriate run mode for the producer 839 fInitStatus = roster->SetRunModeNode(fPlayerNode->Node(), 840 BMediaNode::B_INCREASE_LATENCY); 841 if (fInitStatus != B_OK) { 842 TRACE("BSoundPlayer::_Init: Couldn't SetRunModeNode: %s\n", 843 strerror(fInitStatus)); 844 return; 845 } 846 847 // setup our requested format (can still have many wildcards) 848 tryFormat.type = B_MEDIA_RAW_AUDIO; 849 tryFormat.u.raw_audio = *format; 850 851 #if DEBUG > 0 852 char buf[100]; 853 string_for_format(tryFormat, buf, sizeof(buf)); 854 TRACE("BSoundPlayer::_Init: trying to connect with format %s\n", buf); 855 #endif 856 857 // and connect the nodes 858 fInitStatus = roster->Connect(_output.source, _input.destination, 859 &tryFormat, &fMediaOutput, &fMediaInput); 860 if (fInitStatus != B_OK) { 861 TRACE("BSoundPlayer::_Init: Couldn't Connect: %s\n", 862 strerror(fInitStatus)); 863 return; 864 } 865 866 fFlags |= F_NODES_CONNECTED; 867 868 _GetVolumeSlider(); 869 870 TRACE("BSoundPlayer node %" B_PRId32 " has timesource %" B_PRId32 "\n", 871 fPlayerNode->Node().node, fPlayerNode->TimeSource()->Node().node); 872 } 873 874 875 void 876 BSoundPlayer::_NotifySoundDone(play_id id, bool gotToPlay) 877 { 878 CALLED(); 879 Notify(B_SOUND_DONE, id, gotToPlay); 880 } 881 882 883 void 884 BSoundPlayer::_GetVolumeSlider() 885 { 886 CALLED(); 887 888 ASSERT(fVolumeSlider == NULL); 889 890 BMediaRoster *roster = BMediaRoster::CurrentRoster(); 891 if (!roster) { 892 TRACE("BSoundPlayer::_GetVolumeSlider failed to get BMediaRoster"); 893 return; 894 } 895 896 if (!fParameterWeb && roster->GetParameterWebFor(fMediaInput.node, &fParameterWeb) < B_OK) { 897 TRACE("BSoundPlayer::_GetVolumeSlider couldn't get parameter web"); 898 return; 899 } 900 901 int count = fParameterWeb->CountParameters(); 902 for (int i = 0; i < count; i++) { 903 BParameter *parameter = fParameterWeb->ParameterAt(i); 904 if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER) 905 continue; 906 if ((parameter->ID() >> 16) != fMediaInput.destination.id) 907 continue; 908 if (strcmp(parameter->Kind(), B_GAIN) != 0) 909 continue; 910 fVolumeSlider = (BContinuousParameter *)parameter; 911 break; 912 } 913 914 #if DEBUG >0 915 if (!fVolumeSlider) { 916 TRACE("BSoundPlayer::_GetVolumeSlider couldn't find volume control"); 917 } 918 #endif 919 } 920 921 922 void 923 BSoundPlayer::Notify(sound_player_notification what, ...) 924 { 925 CALLED(); 926 if (fLocker.Lock()) { 927 if (fNotifierFunc) 928 (*fNotifierFunc)(fCookie, what); 929 fLocker.Unlock(); 930 } 931 } 932 933 934 void 935 BSoundPlayer::PlayBuffer(void* buffer, size_t size, 936 const media_raw_audio_format& format) 937 { 938 if (fLocker.Lock()) { 939 if (fPlayBufferFunc) 940 (*fPlayBufferFunc)(fCookie, buffer, size, format); 941 fLocker.Unlock(); 942 } 943 } 944 945 946 // #pragma mark - public sound_error 947 948 949 sound_error::sound_error(const char* string) 950 { 951 m_str_const = string; 952 } 953 954 955 const char* 956 sound_error::what() const throw() 957 { 958 return m_str_const; 959 } 960 961