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 "MediaDebug.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
BSoundPlayer(const char * name,BufferPlayerFunc playerFunction,EventNotifierFunc eventNotifierFunction,void * cookie)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
BSoundPlayer(const media_raw_audio_format * _format,const char * name,BufferPlayerFunc playerFunction,EventNotifierFunc eventNotifierFunction,void * cookie)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
BSoundPlayer(const media_node & toNode,const media_multi_audio_format * format,const char * name,const media_input * input,BufferPlayerFunc playerFunction,EventNotifierFunc eventNotifierFunction,void * cookie)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
~BSoundPlayer()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 != NULL && 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
InitCheck()164 BSoundPlayer::InitCheck()
165 {
166 CALLED();
167 return fInitStatus;
168 }
169
170
171 media_raw_audio_format
Format() const172 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
Start()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
Stop(bool block,bool flush)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
Latency()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
SetHasData(bool hasData)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
HasData()311 BSoundPlayer::HasData()
312 {
313 CALLED();
314 return (atomic_get(&fFlags) & F_HAS_DATA) != 0;
315 }
316
317
318 BSoundPlayer::BufferPlayerFunc
BufferPlayer() const319 BSoundPlayer::BufferPlayer() const
320 {
321 CALLED();
322 return fPlayBufferFunc;
323 }
324
325
326 void
SetBufferPlayer(BufferPlayerFunc playerFunction)327 BSoundPlayer::SetBufferPlayer(BufferPlayerFunc playerFunction)
328 {
329 CALLED();
330 BAutolock _(fLocker);
331
332 fPlayBufferFunc = playerFunction;
333 }
334
335
336 BSoundPlayer::EventNotifierFunc
EventNotifier() const337 BSoundPlayer::EventNotifier() const
338 {
339 CALLED();
340 return fNotifierFunc;
341 }
342
343
344 void
SetNotifier(EventNotifierFunc eventNotifierFunction)345 BSoundPlayer::SetNotifier(EventNotifierFunc eventNotifierFunction)
346 {
347 CALLED();
348 BAutolock _(fLocker);
349
350 fNotifierFunc = eventNotifierFunction;
351 }
352
353
354 void*
Cookie() const355 BSoundPlayer::Cookie() const
356 {
357 CALLED();
358 return fCookie;
359 }
360
361
362 void
SetCookie(void * cookie)363 BSoundPlayer::SetCookie(void *cookie)
364 {
365 CALLED();
366 BAutolock _(fLocker);
367
368 fCookie = cookie;
369 }
370
371
372 void
SetCallbacks(BufferPlayerFunc playerFunction,EventNotifierFunc eventNotifierFunction,void * cookie)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
CurrentTime()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
PerformanceTime()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
Preroll()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
StartPlaying(BSound * sound,bigtime_t atTime)440 BSoundPlayer::StartPlaying(BSound* sound, bigtime_t atTime)
441 {
442 return StartPlaying(sound, atTime, 1.0);
443 }
444
445
446 BSoundPlayer::play_id
StartPlaying(BSound * sound,bigtime_t atTime,float withVolume)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
SetSoundVolume(play_id id,float newVolume)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
IsPlaying(play_id id)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
StopPlaying(play_id id)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
WaitForSound(play_id id)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
Volume()584 BSoundPlayer::Volume()
585 {
586 CALLED();
587 return pow(10.0, VolumeDB(true) / 20.0);
588 }
589
590
591 void
SetVolume(float newVolume)592 BSoundPlayer::SetVolume(float newVolume)
593 {
594 CALLED();
595 SetVolumeDB(20.0 * log10(newVolume));
596 }
597
598
599 float
VolumeDB(bool forcePoll)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
SetVolumeDB(float volumeDB)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
GetVolumeInfo(media_node * _node,int32 * _parameterID,float * _minDB,float * _maxDB)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
SetInitError(status_t error)670 BSoundPlayer::SetInitError(status_t error)
671 {
672 CALLED();
673 fInitStatus = error;
674 }
675
676
677 // #pragma mark - private BSoundPlayer
678
679
680 void
_SoundPlayBufferFunc(void * cookie,void * buffer,size_t size,const media_raw_audio_format & format)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
_Reserved_SoundPlayer_0(void *,...)716 status_t BSoundPlayer::_Reserved_SoundPlayer_0(void*, ...) { return B_ERROR; }
_Reserved_SoundPlayer_1(void *,...)717 status_t BSoundPlayer::_Reserved_SoundPlayer_1(void*, ...) { return B_ERROR; }
_Reserved_SoundPlayer_2(void *,...)718 status_t BSoundPlayer::_Reserved_SoundPlayer_2(void*, ...) { return B_ERROR; }
_Reserved_SoundPlayer_3(void *,...)719 status_t BSoundPlayer::_Reserved_SoundPlayer_3(void*, ...) { return B_ERROR; }
_Reserved_SoundPlayer_4(void *,...)720 status_t BSoundPlayer::_Reserved_SoundPlayer_4(void*, ...) { return B_ERROR; }
_Reserved_SoundPlayer_5(void *,...)721 status_t BSoundPlayer::_Reserved_SoundPlayer_5(void*, ...) { return B_ERROR; }
_Reserved_SoundPlayer_6(void *,...)722 status_t BSoundPlayer::_Reserved_SoundPlayer_6(void*, ...) { return B_ERROR; }
_Reserved_SoundPlayer_7(void *,...)723 status_t BSoundPlayer::_Reserved_SoundPlayer_7(void*, ...) { return B_ERROR; }
724
725
726 void
_Init(const media_node * node,const media_multi_audio_format * format,const char * name,const media_input * input,BufferPlayerFunc playerFunction,EventNotifierFunc eventNotifierFunction,void * cookie)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
_NotifySoundDone(play_id id,bool gotToPlay)876 BSoundPlayer::_NotifySoundDone(play_id id, bool gotToPlay)
877 {
878 CALLED();
879 Notify(B_SOUND_DONE, id, gotToPlay);
880 }
881
882
883 void
_GetVolumeSlider()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
Notify(sound_player_notification what,...)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
PlayBuffer(void * buffer,size_t size,const media_raw_audio_format & format)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
sound_error(const char * string)949 sound_error::sound_error(const char* string)
950 {
951 m_str_const = string;
952 }
953
954
955 const char*
what() const956 sound_error::what() const throw()
957 {
958 return m_str_const;
959 }
960
961