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
28enum {
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
36static BSoundPlayer::play_id sCurrentPlayID = 1;
37
38
39BSoundPlayer::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
53BSoundPlayer::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
76BSoundPlayer::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
100BSoundPlayer::~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
140cleanup:
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
163status_t
164BSoundPlayer::InitCheck()
165{
166	CALLED();
167	return fInitStatus;
168}
169
170
171media_raw_audio_format
172BSoundPlayer::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
183status_t
184BSoundPlayer::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
227void
228BSoundPlayer::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
271bigtime_t
272BSoundPlayer::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
299void
300BSoundPlayer::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
310bool
311BSoundPlayer::HasData()
312{
313	CALLED();
314	return (atomic_get(&fFlags) & F_HAS_DATA) != 0;
315}
316
317
318BSoundPlayer::BufferPlayerFunc
319BSoundPlayer::BufferPlayer() const
320{
321	CALLED();
322	return fPlayBufferFunc;
323}
324
325
326void
327BSoundPlayer::SetBufferPlayer(BufferPlayerFunc playerFunction)
328{
329	CALLED();
330	BAutolock _(fLocker);
331
332	fPlayBufferFunc = playerFunction;
333}
334
335
336BSoundPlayer::EventNotifierFunc
337BSoundPlayer::EventNotifier() const
338{
339	CALLED();
340	return fNotifierFunc;
341}
342
343
344void
345BSoundPlayer::SetNotifier(EventNotifierFunc eventNotifierFunction)
346{
347	CALLED();
348	BAutolock _(fLocker);
349
350	fNotifierFunc = eventNotifierFunction;
351}
352
353
354void*
355BSoundPlayer::Cookie() const
356{
357	CALLED();
358	return fCookie;
359}
360
361
362void
363BSoundPlayer::SetCookie(void *cookie)
364{
365	CALLED();
366	BAutolock _(fLocker);
367
368	fCookie = cookie;
369}
370
371
372void
373BSoundPlayer::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*/
390bigtime_t
391BSoundPlayer::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*/
404bigtime_t
405BSoundPlayer::PerformanceTime()
406{
407	if ((fFlags & F_NODES_CONNECTED) == 0)
408		return (bigtime_t) B_ERROR;
409
410	return fPlayerNode->TimeSource()->Now();
411}
412
413
414status_t
415BSoundPlayer::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
439BSoundPlayer::play_id
440BSoundPlayer::StartPlaying(BSound* sound, bigtime_t atTime)
441{
442	return StartPlaying(sound, atTime, 1.0);
443}
444
445
446BSoundPlayer::play_id
447BSoundPlayer::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
478status_t
479BSoundPlayer::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
501bool
502BSoundPlayer::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
523status_t
524BSoundPlayer::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
557status_t
558BSoundPlayer::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
583float
584BSoundPlayer::Volume()
585{
586	CALLED();
587	return pow(10.0, VolumeDB(true) / 20.0);
588}
589
590
591void
592BSoundPlayer::SetVolume(float newVolume)
593{
594	CALLED();
595	SetVolumeDB(20.0 * log10(newVolume));
596}
597
598
599float
600BSoundPlayer::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
620void
621BSoundPlayer::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
645status_t
646BSoundPlayer::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
669void
670BSoundPlayer::SetInitError(status_t error)
671{
672	CALLED();
673	fInitStatus = error;
674}
675
676
677// #pragma mark - private BSoundPlayer
678
679
680void
681BSoundPlayer::_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
716status_t BSoundPlayer::_Reserved_SoundPlayer_0(void*, ...) { return B_ERROR; }
717status_t BSoundPlayer::_Reserved_SoundPlayer_1(void*, ...) { return B_ERROR; }
718status_t BSoundPlayer::_Reserved_SoundPlayer_2(void*, ...) { return B_ERROR; }
719status_t BSoundPlayer::_Reserved_SoundPlayer_3(void*, ...) { return B_ERROR; }
720status_t BSoundPlayer::_Reserved_SoundPlayer_4(void*, ...) { return B_ERROR; }
721status_t BSoundPlayer::_Reserved_SoundPlayer_5(void*, ...) { return B_ERROR; }
722status_t BSoundPlayer::_Reserved_SoundPlayer_6(void*, ...) { return B_ERROR; }
723status_t BSoundPlayer::_Reserved_SoundPlayer_7(void*, ...) { return B_ERROR; }
724
725
726void
727BSoundPlayer::_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
875void
876BSoundPlayer::_NotifySoundDone(play_id id, bool gotToPlay)
877{
878	CALLED();
879	Notify(B_SOUND_DONE, id, gotToPlay);
880}
881
882
883void
884BSoundPlayer::_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
922void
923BSoundPlayer::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
934void
935BSoundPlayer::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
949sound_error::sound_error(const char* string)
950{
951	m_str_const = string;
952}
953
954
955const char*
956sound_error::what() const throw()
957{
958	return m_str_const;
959}
960
961