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
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				"%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
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	delete fPlayerNode;
148
149	// do not delete fVolumeSlider, it belongs to the parameter web
150	delete fParameterWeb;
151}
152
153
154status_t
155BSoundPlayer::InitCheck()
156{
157	CALLED();
158	return fInitStatus;
159}
160
161
162media_raw_audio_format
163BSoundPlayer::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
174status_t
175BSoundPlayer::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
218void
219BSoundPlayer::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
262bigtime_t
263BSoundPlayer::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
290void
291BSoundPlayer::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
301bool
302BSoundPlayer::HasData()
303{
304	CALLED();
305	return (atomic_get(&fFlags) & F_HAS_DATA) != 0;
306}
307
308
309BSoundPlayer::BufferPlayerFunc
310BSoundPlayer::BufferPlayer() const
311{
312	CALLED();
313	return fPlayBufferFunc;
314}
315
316
317void
318BSoundPlayer::SetBufferPlayer(BufferPlayerFunc playerFunction)
319{
320	CALLED();
321	BAutolock _(fLocker);
322
323	fPlayBufferFunc = playerFunction;
324}
325
326
327BSoundPlayer::EventNotifierFunc
328BSoundPlayer::EventNotifier() const
329{
330	CALLED();
331	return fNotifierFunc;
332}
333
334
335void
336BSoundPlayer::SetNotifier(EventNotifierFunc eventNotifierFunction)
337{
338	CALLED();
339	BAutolock _(fLocker);
340
341	fNotifierFunc = eventNotifierFunction;
342}
343
344
345void*
346BSoundPlayer::Cookie() const
347{
348	CALLED();
349	return fCookie;
350}
351
352
353void
354BSoundPlayer::SetCookie(void *cookie)
355{
356	CALLED();
357	BAutolock _(fLocker);
358
359	fCookie = cookie;
360}
361
362
363void
364BSoundPlayer::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*/
381bigtime_t
382BSoundPlayer::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*/
395bigtime_t
396BSoundPlayer::PerformanceTime()
397{
398	if ((fFlags & F_NODES_CONNECTED) == 0)
399		return (bigtime_t) B_ERROR;
400
401	return fPlayerNode->TimeSource()->Now();
402}
403
404
405status_t
406BSoundPlayer::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
430BSoundPlayer::play_id
431BSoundPlayer::StartPlaying(BSound* sound, bigtime_t atTime)
432{
433	return StartPlaying(sound, atTime, 1.0);
434}
435
436
437BSoundPlayer::play_id
438BSoundPlayer::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
469status_t
470BSoundPlayer::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
492bool
493BSoundPlayer::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
514status_t
515BSoundPlayer::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
548status_t
549BSoundPlayer::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
574float
575BSoundPlayer::Volume()
576{
577	CALLED();
578	return pow(10.0, VolumeDB(true) / 20.0);
579}
580
581
582void
583BSoundPlayer::SetVolume(float newVolume)
584{
585	CALLED();
586	SetVolumeDB(20.0 * log10(newVolume));
587}
588
589
590float
591BSoundPlayer::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
611void
612BSoundPlayer::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
636status_t
637BSoundPlayer::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
660void
661BSoundPlayer::SetInitError(status_t error)
662{
663	CALLED();
664	fInitStatus = error;
665}
666
667
668// #pragma mark - private BSoundPlayer
669
670
671void
672BSoundPlayer::_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
707status_t BSoundPlayer::_Reserved_SoundPlayer_0(void*, ...) { return B_ERROR; }
708status_t BSoundPlayer::_Reserved_SoundPlayer_1(void*, ...) { return B_ERROR; }
709status_t BSoundPlayer::_Reserved_SoundPlayer_2(void*, ...) { return B_ERROR; }
710status_t BSoundPlayer::_Reserved_SoundPlayer_3(void*, ...) { return B_ERROR; }
711status_t BSoundPlayer::_Reserved_SoundPlayer_4(void*, ...) { return B_ERROR; }
712status_t BSoundPlayer::_Reserved_SoundPlayer_5(void*, ...) { return B_ERROR; }
713status_t BSoundPlayer::_Reserved_SoundPlayer_6(void*, ...) { return B_ERROR; }
714status_t BSoundPlayer::_Reserved_SoundPlayer_7(void*, ...) { return B_ERROR; }
715
716
717void
718BSoundPlayer::_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
866void
867BSoundPlayer::_NotifySoundDone(play_id id, bool gotToPlay)
868{
869	CALLED();
870	Notify(B_SOUND_DONE, id, gotToPlay);
871}
872
873
874void
875BSoundPlayer::_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
913void
914BSoundPlayer::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
925void
926BSoundPlayer::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
940sound_error::sound_error(const char* string)
941{
942	m_str_const = string;
943}
944
945
946const char*
947sound_error::what() const throw()
948{
949	return m_str_const;
950}
951
952