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