2 * Copyright 2002-2009, Haiku.
3 * Distributed under the terms of the MIT License.
11 #include <SoundPlayer.h>
17 #include <MediaRoster.h>
18 #include <ParameterWeb.h>
20 #include <TimeSource.h>
22 #include "SoundPlayNode.h"
27 // Flags used internally in BSoundPlayer
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),
36 static BSoundPlayer::play_id sCurrentPlayID
= 1;
39 BSoundPlayer::BSoundPlayer(const char* name
, BufferPlayerFunc playerFunction
,
40 EventNotifierFunc eventNotifierFunction
, void* cookie
)
44 TRACE("BSoundPlayer::BSoundPlayer: default constructor used\n");
46 media_multi_audio_format format
= media_multi_audio_format::wildcard
;
48 _Init(NULL
, &format
, name
, NULL
, playerFunction
, eventNotifierFunction
,
53 BSoundPlayer::BSoundPlayer(const media_raw_audio_format
* _format
,
54 const char* name
, BufferPlayerFunc playerFunction
,
55 EventNotifierFunc eventNotifierFunction
, void* cookie
)
59 TRACE("BSoundPlayer::BSoundPlayer: raw audio format constructor used\n");
61 media_multi_audio_format format
= media_multi_audio_format::wildcard
;
62 *(media_raw_audio_format
*)&format
= *_format
;
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
);
71 _Init(NULL
, &format
, name
, NULL
, playerFunction
, eventNotifierFunction
,
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
)
83 TRACE("BSoundPlayer::BSoundPlayer: multi audio format constructor used\n");
85 if ((toNode
.kind
& B_BUFFER_CONSUMER
) == 0)
86 debugger("BSoundPlayer: toNode must have B_BUFFER_CONSUMER kind!\n");
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
);
95 _Init(&toNode
, format
, name
, input
, playerFunction
, eventNotifierFunction
,
100 BSoundPlayer::~BSoundPlayer()
104 if ((fFlags
& F_IS_STARTED
) != 0) {
105 // block, but don't flush
110 BMediaRoster
* roster
= BMediaRoster::Roster();
111 if (roster
== NULL
) {
112 TRACE("BSoundPlayer::~BSoundPlayer: Couldn't get BMediaRoster\n");
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
);
125 TRACE("BSoundPlayer::~BSoundPlayer: Error disconnecting nodes: "
126 "%ld (%s)\n", err
, strerror(err
));
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
);
135 TRACE("BSoundPlayer::~BSoundPlayer: Error releasing input node: "
136 "%ld (%s)\n", err
, strerror(err
));
141 // Dispose of the player node
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
149 // do not delete fVolumeSlider, it belongs to the parameter web
150 delete fParameterWeb
;
155 BSoundPlayer::InitCheck()
162 media_raw_audio_format
163 BSoundPlayer::Format() const
167 if ((fFlags
& F_NODES_CONNECTED
) == 0)
168 return media_raw_audio_format::wildcard
;
170 return fPlayerNode
->Format();
175 BSoundPlayer::Start()
179 if ((fFlags
& F_NODES_CONNECTED
) == 0)
182 if ((fFlags
& F_IS_STARTED
) != 0)
185 BMediaRoster
* roster
= BMediaRoster::Roster();
187 TRACE("BSoundPlayer::Start: Couldn't get BMediaRoster\n");
191 if (!fPlayerNode
->TimeSource()->IsRunning()) {
192 roster
->StartTimeSource(fPlayerNode
->TimeSource()->Node(),
193 fPlayerNode
->TimeSource()->RealTime());
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
201 status_t err
= roster
->StartNode(fPlayerNode
->Node(),
202 fPlayerNode
->TimeSource()->Now() + Latency() + 5000);
204 TRACE("BSoundPlayer::Start: StartNode failed, %ld", err
);
208 if (fNotifierFunc
!= NULL
)
209 fNotifierFunc(fCookie
, B_STARTED
, this);
212 atomic_or(&fFlags
, F_IS_STARTED
);
219 BSoundPlayer::Stop(bool block
, bool flush
)
223 TRACE("BSoundPlayer::Stop: block %d, flush %d\n", (int)block
, (int)flush
);
225 if ((fFlags
& F_NODES_CONNECTED
) == 0)
228 // TODO: flush is ignored
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");
237 roster
->StopNode(fPlayerNode
->Node(), 0, true);
239 atomic_and(&fFlags
, ~F_IS_STARTED
);
243 // wait until the node is stopped
245 for (tries
= 250; fPlayerNode
->IsPlaying() && tries
!= 0; tries
--)
248 DEBUG_ONLY(if (tries
== 0)
249 TRACE("BSoundPlayer::Stop: waiting for node stop failed\n"));
251 // Wait until all buffers on the way to the physical output have been
253 snooze(Latency() + 2000);
257 fNotifierFunc(fCookie
, B_STOPPED
, this);
263 BSoundPlayer::Latency()
267 if ((fFlags
& F_NODES_CONNECTED
) == 0)
270 BMediaRoster
*roster
= BMediaRoster::Roster();
272 TRACE("BSoundPlayer::Latency: Couldn't get BMediaRoster\n");
277 status_t err
= roster
->GetLatencyFor(fMediaOutput
.node
, &latency
);
279 TRACE("BSoundPlayer::Latency: GetLatencyFor failed %ld (%s)\n", err
,
284 TRACE("BSoundPlayer::Latency: latency is %Ld\n", latency
);
291 BSoundPlayer::SetHasData(bool hasData
)
295 atomic_or(&fFlags
, F_HAS_DATA
);
297 atomic_and(&fFlags
, ~F_HAS_DATA
);
302 BSoundPlayer::HasData()
305 return (atomic_get(&fFlags
) & F_HAS_DATA
) != 0;
309 BSoundPlayer::BufferPlayerFunc
310 BSoundPlayer::BufferPlayer() const
313 return fPlayBufferFunc
;
318 BSoundPlayer::SetBufferPlayer(BufferPlayerFunc playerFunction
)
321 BAutolock
_(fLocker
);
323 fPlayBufferFunc
= playerFunction
;
327 BSoundPlayer::EventNotifierFunc
328 BSoundPlayer::EventNotifier() const
331 return fNotifierFunc
;
336 BSoundPlayer::SetNotifier(EventNotifierFunc eventNotifierFunction
)
339 BAutolock
_(fLocker
);
341 fNotifierFunc
= eventNotifierFunction
;
346 BSoundPlayer::Cookie() const
354 BSoundPlayer::SetCookie(void *cookie
)
357 BAutolock
_(fLocker
);
364 BSoundPlayer::SetCallbacks(BufferPlayerFunc playerFunction
,
365 EventNotifierFunc eventNotifierFunction
, void* cookie
)
368 BAutolock
_(fLocker
);
370 SetBufferPlayer(playerFunction
);
371 SetNotifier(eventNotifierFunction
);
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"
382 BSoundPlayer::CurrentTime()
384 if ((fFlags
& F_NODES_CONNECTED
) == 0)
387 return fPlayerNode
->CurrentTime();
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.
396 BSoundPlayer::PerformanceTime()
398 if ((fFlags
& F_NODES_CONNECTED
) == 0)
399 return (bigtime_t
) B_ERROR
;
401 return fPlayerNode
->TimeSource()->Now();
406 BSoundPlayer::Preroll()
410 if ((fFlags
& F_NODES_CONNECTED
) == 0)
413 BMediaRoster
* roster
= BMediaRoster::Roster();
414 if (roster
== NULL
) {
415 TRACE("BSoundPlayer::Preroll: Couldn't get BMediaRoster\n");
419 status_t err
= roster
->PrerollNode(fMediaOutput
.node
);
421 TRACE("BSoundPlayer::Preroll: Error while PrerollNode: %ld (%s)\n",
430 BSoundPlayer::play_id
431 BSoundPlayer::StartPlaying(BSound
* sound
, bigtime_t atTime
)
433 return StartPlaying(sound
, atTime
, 1.0);
437 BSoundPlayer::play_id
438 BSoundPlayer::StartPlaying(BSound
* sound
, bigtime_t atTime
, float withVolume
)
442 // TODO: support the at_time and with_volume parameters
443 playing_sound
* item
= (playing_sound
*)malloc(sizeof(playing_sound
));
447 item
->current_offset
= 0;
449 item
->id
= atomic_add(&sCurrentPlayID
, 1);
452 item
->volume
= withVolume
;
454 if (!fLocker
.Lock()) {
460 item
->next
= fPlayingSounds
;
461 fPlayingSounds
= item
;
470 BSoundPlayer::SetSoundVolume(play_id id
, float newVolume
)
476 playing_sound
*item
= fPlayingSounds
;
478 if (item
->id
== id
) {
479 item
->volume
= newVolume
;
488 return B_ENTRY_NOT_FOUND
;
493 BSoundPlayer::IsPlaying(play_id id
)
499 playing_sound
*item
= fPlayingSounds
;
501 if (item
->id
== id
) {
515 BSoundPlayer::StopPlaying(play_id id
)
521 playing_sound
** link
= &fPlayingSounds
;
522 playing_sound
* item
= fPlayingSounds
;
524 while (item
!= NULL
) {
525 if (item
->id
== id
) {
527 sem_id waitSem
= item
->wait_sem
;
528 item
->sound
->ReleaseRef();
532 _NotifySoundDone(id
, true);
534 release_sem(waitSem
);
544 return B_ENTRY_NOT_FOUND
;
549 BSoundPlayer::WaitForSound(play_id id
)
555 playing_sound
* item
= fPlayingSounds
;
556 while (item
!= NULL
) {
557 if (item
->id
== id
) {
558 sem_id waitSem
= item
->wait_sem
;
560 waitSem
= item
->wait_sem
= create_sem(0, "wait for sound");
563 return acquire_sem(waitSem
);
570 return B_ENTRY_NOT_FOUND
;
575 BSoundPlayer::Volume()
578 return pow(10.0, VolumeDB(true) / 20.0);
583 BSoundPlayer::SetVolume(float newVolume
)
586 SetVolumeDB(20.0 * log10(newVolume
));
591 BSoundPlayer::VolumeDB(bool forcePoll
)
595 return -94.0f
; // silence
597 if (!forcePoll
&& system_time() - fLastVolumeUpdate
< 500000)
600 int32 count
= fVolumeSlider
->CountChannels();
602 size_t size
= count
* sizeof(float);
603 fVolumeSlider
->GetValue(&values
, &size
, NULL
);
604 fLastVolumeUpdate
= system_time();
605 fVolumeDB
= values
[0];
612 BSoundPlayer::SetVolumeDB(float volumeDB
)
618 float minDB
= fVolumeSlider
->MinValue();
619 float maxDB
= fVolumeSlider
->MaxValue();
620 if (volumeDB
< minDB
)
622 if (volumeDB
> maxDB
)
625 int count
= fVolumeSlider
->CountChannels();
627 for (int i
= 0; i
< count
; i
++)
628 values
[i
] = volumeDB
;
629 fVolumeSlider
->SetValue(values
, sizeof(float) * count
, 0);
631 fVolumeDB
= volumeDB
;
632 fLastVolumeUpdate
= system_time();
637 BSoundPlayer::GetVolumeInfo(media_node
* _node
, int32
* _parameterID
,
638 float* _minDB
, float* _maxDB
)
641 if (fVolumeSlider
== NULL
)
645 *_node
= fMediaInput
.node
;
646 if (_parameterID
!= NULL
)
647 *_parameterID
= fVolumeSlider
->ID();
649 *_minDB
= fVolumeSlider
->MinValue();
651 *_maxDB
= fVolumeSlider
->MaxValue();
657 // #pragma mark - protected BSoundPlayer
661 BSoundPlayer::SetInitError(status_t error
)
668 // #pragma mark - private BSoundPlayer
672 BSoundPlayer::_SoundPlayBufferFunc(void *cookie
, void *buffer
, size_t size
,
673 const media_raw_audio_format
&format
)
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
);
682 playing_sound
*sound
= player
->fPlayingSounds
;
684 player
->SetHasData(false);
685 player
->fLocker
.Unlock();
686 memset(buffer
, 0, size
);
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
);
699 sound
->current_offset
+= used
;
700 player
->fLocker
.Unlock();
703 memset((uint8
*)buffer
+ used
, 0, size
- used
);
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
; }
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
)
724 fPlayingSounds
= NULL
;
725 fWaitingSounds
= NULL
;
728 if (playerFunction
== NULL
) {
729 fPlayBufferFunc
= _SoundPlayBufferFunc
;
732 fPlayBufferFunc
= playerFunction
;
736 fNotifierFunc
= eventNotifierFunction
;
739 fInitStatus
= B_ERROR
;
740 fParameterWeb
= NULL
;
741 fVolumeSlider
= NULL
;
742 fLastVolumeUpdate
= 0;
744 BMediaRoster
* roster
= BMediaRoster::Roster();
745 if (roster
== NULL
) {
746 TRACE("BSoundPlayer::_Init: Couldn't get BMediaRoster\n");
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
;
757 fInitStatus
= roster
->GetAudioMixer(&inputNode
);
758 if (fInitStatus
!= B_OK
) {
759 TRACE("BSoundPlayer::_Init: Couldn't GetAudioMixer\n");
762 fFlags
|= F_MUST_RELEASE_MIXER
;
765 media_output _output
;
769 media_format tryFormat
;
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
));
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
));
789 fInitStatus
= roster
->SetTimeSourceFor(fPlayerNode
->Node().node
,
791 if (fInitStatus
!= B_OK
) {
792 TRACE("BSoundPlayer::_Init: Couldn't SetTimeSourceFor: %s\n",
793 strerror(fInitStatus
));
797 // find a free media_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
));
806 if (inputCount
< 1) {
807 TRACE("BSoundPlayer::_Init: Couldn't find a free input\n");
808 fInitStatus
= B_ERROR
;
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
));
823 if (outputCount
< 1) {
824 TRACE("BSoundPlayer::_Init: Couldn't find a free output\n");
825 fInitStatus
= B_ERROR
;
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
));
838 // setup our requested format (can still have many wildcards)
839 tryFormat
.type
= B_MEDIA_RAW_AUDIO
;
840 tryFormat
.u
.raw_audio
= *format
;
844 string_for_format(tryFormat
, buf
, sizeof(buf
));
845 TRACE("BSoundPlayer::_Init: trying to connect with format %s\n", buf
);
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
));
857 fFlags
|= F_NODES_CONNECTED
;
861 TRACE("BSoundPlayer node %ld has timesource %ld\n",
862 fPlayerNode
->Node().node
, fPlayerNode
->TimeSource()->Node().node
);
867 BSoundPlayer::_NotifySoundDone(play_id id
, bool gotToPlay
)
870 Notify(B_SOUND_DONE
, id
, gotToPlay
);
875 BSoundPlayer::_GetVolumeSlider()
879 ASSERT(fVolumeSlider
== NULL
);
881 BMediaRoster
*roster
= BMediaRoster::CurrentRoster();
883 TRACE("BSoundPlayer::_GetVolumeSlider failed to get BMediaRoster");
887 if (!fParameterWeb
&& roster
->GetParameterWebFor(fMediaInput
.node
, &fParameterWeb
) < B_OK
) {
888 TRACE("BSoundPlayer::_GetVolumeSlider couldn't get parameter web");
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
)
897 if ((parameter
->ID() >> 16) != fMediaInput
.destination
.id
)
899 if (strcmp(parameter
->Kind(), B_GAIN
) != 0)
901 fVolumeSlider
= (BContinuousParameter
*)parameter
;
906 if (!fVolumeSlider
) {
907 TRACE("BSoundPlayer::_GetVolumeSlider couldn't find volume control");
914 BSoundPlayer::Notify(sound_player_notification what
, ...)
917 if (fLocker
.Lock()) {
919 (*fNotifierFunc
)(fCookie
, what
);
926 BSoundPlayer::PlayBuffer(void* buffer
, size_t size
,
927 const media_raw_audio_format
& format
)
929 if (fLocker
.Lock()) {
931 (*fPlayBufferFunc
)(fCookie
, buffer
, size
, format
);
937 // #pragma mark - public sound_error
940 sound_error::sound_error(const char* string
)
942 m_str_const
= string
;
947 sound_error::what() const throw()