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