xref: /haiku/src/kits/media/SoundPlayer.cpp (revision ed6250c95736c0b55da79d6e9dd01369532260c0)
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(fMediaOutput, fMediaInput);
267 #if DEBUG >0
268 		if (err) {
269 			TRACE("BSoundPlayer::~BSoundPlayer: Error disconnecting nodes:  %ld (%s)\n", err, strerror(err));
270 		}
271 #endif
272 	}
273 
274 	if (fFlags & F_MUST_RELEASE_MIXER) {
275 		// Release the mixer as it was acquired
276 		// through BMediaRoster::GetAudioMixer()
277 		err = roster->ReleaseNode(fMediaInput.node);
278 #if DEBUG >0
279 		if (err) {
280 			TRACE("BSoundPlayer::~BSoundPlayer: Error releasing input node:  %ld (%s)\n", err, strerror(err));
281 		}
282 #endif
283 	}
284 
285 cleanup:
286 
287 	// Dispose of the player node
288 	if (fPlayerNode) {
289 		// We do not call BMediaRoster::ReleaseNode(), since
290 		// the player was created by using "new". We could
291 		// call BMediaRoster::UnregisterNode(), but this is
292 		// supposed to be done by BMediaNode destructor automatically
293 		delete fPlayerNode;
294 	}
295 
296 	delete fParameterWeb;
297 	// do not delete fVolumeSlider, it belonged to the parameter web
298 }
299 
300 
301 status_t
302 BSoundPlayer::InitCheck()
303 {
304 	CALLED();
305 	return fInitStatus;
306 }
307 
308 
309 media_raw_audio_format
310 BSoundPlayer::Format() const
311 {
312 	CALLED();
313 
314 	if ((fFlags & F_NODES_CONNECTED) == 0)
315 		return media_raw_audio_format::wildcard;
316 
317 	return fPlayerNode->Format();
318 }
319 
320 
321 status_t
322 BSoundPlayer::Start()
323 {
324 	CALLED();
325 
326 	if ((fFlags & F_NODES_CONNECTED) == 0)
327 		return B_NO_INIT;
328 
329 	if (fFlags & F_IS_STARTED)
330 		return B_OK;
331 
332 	BMediaRoster *roster = BMediaRoster::Roster();
333 	if (!roster) {
334 		TRACE("BSoundPlayer::Start: Couldn't get BMediaRoster\n");
335 		return B_ERROR;
336 	}
337 
338 	// Add latency and a few ms to the nodes current time to
339 	// make sure that we give the producer enough time to run
340 	// buffers through the node chain, otherwise it'll start
341 	// up already late
342 
343 	status_t err = roster->StartNode(fPlayerNode->Node(), fPlayerNode->TimeSource()->Now() + Latency() + 5000);
344 	if (err != B_OK) {
345 		TRACE("BSoundPlayer::Start: StartNode failed, %ld", err);
346 		return err;
347 	}
348 
349 	atomic_or(&fFlags, F_IS_STARTED);
350 
351 	return B_OK;
352 }
353 
354 
355 void
356 BSoundPlayer::Stop(bool block,
357 				   bool flush)
358 {
359 	CALLED();
360 
361 	TRACE("BSoundPlayer::Stop: block %d, flush %d\n", (int)block, (int)flush);
362 
363 	if ((fFlags & F_NODES_CONNECTED) == 0)
364 		return;
365 
366 	// XXX flush is ignored
367 
368 	if (fFlags & F_IS_STARTED) {
369 
370 		BMediaRoster *roster = BMediaRoster::Roster();
371 		if (!roster) {
372 			TRACE("BSoundPlayer::Stop: Couldn't get BMediaRoster\n");
373 			return;
374 		}
375 
376 		roster->StopNode(fPlayerNode->Node(), 0, true);
377 
378 		atomic_and(&fFlags, ~F_IS_STARTED);
379 	}
380 
381 	if (block) {
382 		// wait until the node is stopped
383 		int maxtrys;
384 		for (maxtrys = 250; fPlayerNode->IsPlaying() && maxtrys != 0; maxtrys--)
385 			snooze(2000);
386 
387 		DEBUG_ONLY(if (maxtrys == 0) TRACE("BSoundPlayer::Stop: waiting for node stop failed\n"));
388 
389 		// wait until all buffers on the way to the physical output have been played
390 		snooze(Latency() + 2000);
391 	}
392 }
393 
394 
395 bigtime_t
396 BSoundPlayer::Latency()
397 {
398 	CALLED();
399 
400 	if ((fFlags & F_NODES_CONNECTED) == 0)
401 		return 0;
402 
403 	BMediaRoster *roster = BMediaRoster::Roster();
404 	if (!roster) {
405 		TRACE("BSoundPlayer::Latency: Couldn't get BMediaRoster\n");
406 		return 0;
407 	}
408 
409 	bigtime_t latency;
410 	status_t err = roster->GetLatencyFor(fMediaOutput.node, &latency);
411 	if (err != B_OK) {
412 		TRACE("BSoundPlayer::Latency: GetLatencyFor failed %ld (%s)\n", err, strerror(err));
413 		return 0;
414 	}
415 
416 	TRACE("BSoundPlayer::Latency: latency is %Ld\n", latency);
417 
418 	return latency;
419 }
420 
421 
422 void
423 BSoundPlayer::SetHasData(bool has_data)
424 {
425 	CALLED();
426 	if (has_data)
427 		atomic_or(&fFlags, F_HAS_DATA);
428 	else
429 		atomic_and(&fFlags, ~F_HAS_DATA);
430 }
431 
432 
433 /* virtual */ bool
434 BSoundPlayer::HasData()
435 {
436 	CALLED();
437 	return (atomic_read(&fFlags) & F_HAS_DATA) != 0;
438 }
439 
440 
441 BSoundPlayer::BufferPlayerFunc
442 BSoundPlayer::BufferPlayer() const
443 {
444 	CALLED();
445 	return fPlayBufferFunc;
446 }
447 
448 
449 void
450 BSoundPlayer::SetBufferPlayer(void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format))
451 {
452 	CALLED();
453 	fLocker.Lock();
454 	fPlayBufferFunc = PlayBuffer;
455 	fLocker.Unlock();
456 }
457 
458 
459 BSoundPlayer::EventNotifierFunc
460 BSoundPlayer::EventNotifier() const
461 {
462 	CALLED();
463 	return fNotifierFunc;
464 }
465 
466 
467 void BSoundPlayer::SetNotifier(void (*Notifier)(void *, sound_player_notification what, ...))
468 {
469 	CALLED();
470 	fLocker.Lock();
471 	fNotifierFunc = Notifier;
472 	fLocker.Unlock();
473 }
474 
475 
476 void *
477 BSoundPlayer::Cookie() const
478 {
479 	CALLED();
480 	return fCookie;
481 }
482 
483 
484 void
485 BSoundPlayer::SetCookie(void *cookie)
486 {
487 	CALLED();
488 	fLocker.Lock();
489 	fCookie = cookie;
490 	fLocker.Unlock();
491 }
492 
493 
494 void
495 BSoundPlayer::SetCallbacks(void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format),
496 						   void (*Notifier)(void *, sound_player_notification what, ...),
497 						   void * cookie)
498 {
499 	CALLED();
500 	fLocker.Lock();
501 	SetBufferPlayer(PlayBuffer);
502 	SetNotifier(Notifier);
503 	SetCookie(cookie);
504 	fLocker.Unlock();
505 }
506 
507 
508 /* The BeBook is inaccurate about the meaning of this function.
509  * The probably best interpretation is to return the time that
510  * has elapsed since playing was started, whichs seems to match
511  * " CurrentTime() returns the current media time "
512  */
513 bigtime_t
514 BSoundPlayer::CurrentTime()
515 {
516 	if ((fFlags & F_NODES_CONNECTED) == 0)
517 		return 0;
518 
519 	return fPlayerNode->CurrentTime();
520 }
521 
522 
523 /* Returns the current performance time of the sound player node
524  * being used by the BSoundPlayer. Will return B_ERROR if the
525  * BSoundPlayer object hasn't been properly initialized.
526  */
527 bigtime_t
528 BSoundPlayer::PerformanceTime()
529 {
530 	if ((fFlags & F_NODES_CONNECTED) == 0)
531 		return (bigtime_t) B_ERROR;
532 
533 	return fPlayerNode->TimeSource()->Now();
534 }
535 
536 
537 status_t
538 BSoundPlayer::Preroll()
539 {
540 	CALLED();
541 
542 	if ((fFlags & F_NODES_CONNECTED) == 0)
543 		return B_NO_INIT;
544 
545 	BMediaRoster *roster = BMediaRoster::Roster();
546 	if (!roster) {
547 		TRACE("BSoundPlayer::Preroll: Couldn't get BMediaRoster\n");
548 		return B_ERROR;
549 	}
550 
551 	status_t err = roster->PrerollNode(fMediaOutput.node);
552 	if (err != B_OK) {
553 		TRACE("BSoundPlayer::Preroll: Error while PrerollNode:  %ld (%s)\n", err, strerror(err));
554 		return err;
555 	}
556 
557 	return B_OK;
558 }
559 
560 
561 BSoundPlayer::play_id
562 BSoundPlayer::StartPlaying(BSound *sound,
563 						   bigtime_t at_time)
564 {
565 	UNIMPLEMENTED();
566 	return 1;
567 }
568 
569 
570 BSoundPlayer::play_id
571 BSoundPlayer::StartPlaying(BSound *sound,
572 						   bigtime_t at_time,
573 						   float with_volume)
574 {
575 	UNIMPLEMENTED();
576 	return 1;
577 }
578 
579 
580 status_t
581 BSoundPlayer::SetSoundVolume(play_id sound,
582 							 float new_volume)
583 {
584 	UNIMPLEMENTED();
585 
586 	return B_OK;
587 }
588 
589 
590 bool
591 BSoundPlayer::IsPlaying(play_id id)
592 {
593 	UNIMPLEMENTED();
594 
595 	return true;
596 }
597 
598 
599 status_t
600 BSoundPlayer::StopPlaying(play_id id)
601 {
602 	UNIMPLEMENTED();
603 
604 	return B_OK;
605 }
606 
607 
608 status_t
609 BSoundPlayer::WaitForSound(play_id id)
610 {
611 	UNIMPLEMENTED();
612 
613 	return B_OK;
614 }
615 
616 
617 float
618 BSoundPlayer::Volume()
619 {
620 	CALLED();
621 
622 	return pow(10.0, VolumeDB(true) / 20.0);
623 }
624 
625 
626 void
627 BSoundPlayer::SetVolume(float new_volume)
628 {
629 	CALLED();
630 	SetVolumeDB(20.0 * log10(new_volume));
631 }
632 
633 
634 float
635 BSoundPlayer::VolumeDB(bool forcePoll)
636 {
637 	CALLED();
638 	if (!fVolumeSlider)
639 		return -94.0f; // silence
640 
641 	if (!forcePoll && (system_time() - fLastVolumeUpdate < 500000))
642 		return fVolumeDB;
643 
644 	int32 count = fVolumeSlider->CountChannels();
645 	float values[count];
646 	size_t size = count * sizeof(float);
647 	fVolumeSlider->GetValue(&values, &size, NULL);
648 	fLastVolumeUpdate = system_time();
649 	fVolumeDB = values[0];
650 
651 	return values[0];
652 }
653 
654 
655 void
656 BSoundPlayer::SetVolumeDB(float volume_dB)
657 {
658 	CALLED();
659 	if (!fVolumeSlider)
660 		return;
661 
662 	float min_dB = fVolumeSlider->MinValue();
663 	float max_dB = fVolumeSlider->MaxValue();
664 	if (volume_dB < min_dB)
665 		volume_dB = min_dB;
666 	if (volume_dB > max_dB)
667 		volume_dB = max_dB;
668 
669 	int count = fVolumeSlider->CountChannels();
670 	float values[count];
671 	for (int i = 0; i < count; i++)
672 		values[i] = volume_dB;
673 	fVolumeSlider->SetValue(values, sizeof(float) * count, 0);
674 
675 	fVolumeDB = volume_dB;
676 	fLastVolumeUpdate = system_time();
677 }
678 
679 
680 status_t
681 BSoundPlayer::GetVolumeInfo(media_node *out_node,
682 							int32 *out_parameter_id,
683 							float *out_min_dB,
684 							float *out_max_dB)
685 {
686 	CALLED();
687 	if (!fVolumeSlider)
688 		return B_NO_INIT;
689 
690 	if (out_node)
691 		*out_node = fMediaInput.node;
692 	if (out_parameter_id)
693 		*out_parameter_id = fVolumeSlider->ID();
694 	if (out_min_dB)
695 		*out_min_dB = fVolumeSlider->MinValue();
696 	if (out_max_dB)
697 		*out_max_dB = fVolumeSlider->MaxValue();
698 	return B_OK;
699 }
700 
701 
702 
703 /*************************************************************
704  * protected BSoundPlayer
705  *************************************************************/
706 
707 
708 void
709 BSoundPlayer::SetInitError(status_t in_error)
710 {
711 	CALLED();
712 	fInitStatus = in_error;
713 }
714 
715 
716 /*************************************************************
717  * private BSoundPlayer
718  *************************************************************/
719 
720 status_t BSoundPlayer::_Reserved_SoundPlayer_0(void *, ...) { return B_ERROR; }
721 status_t BSoundPlayer::_Reserved_SoundPlayer_1(void *, ...) { return B_ERROR; }
722 status_t BSoundPlayer::_Reserved_SoundPlayer_2(void *, ...) { return B_ERROR; }
723 status_t BSoundPlayer::_Reserved_SoundPlayer_3(void *, ...) { return B_ERROR; }
724 status_t BSoundPlayer::_Reserved_SoundPlayer_4(void *, ...) { return B_ERROR; }
725 status_t BSoundPlayer::_Reserved_SoundPlayer_5(void *, ...) { return B_ERROR; }
726 status_t BSoundPlayer::_Reserved_SoundPlayer_6(void *, ...) { return B_ERROR; }
727 status_t BSoundPlayer::_Reserved_SoundPlayer_7(void *, ...) { return B_ERROR; }
728 
729 
730 void
731 BSoundPlayer::NotifySoundDone(play_id sound,
732 							  bool got_to_play)
733 {
734 	UNIMPLEMENTED();
735 }
736 
737 
738 void
739 BSoundPlayer::get_volume_slider()
740 {
741 	CALLED();
742 
743 	ASSERT(fVolumeSlider == NULL);
744 
745 	BMediaRoster *roster = BMediaRoster::CurrentRoster();
746 	if (!roster) {
747 		TRACE("BSoundPlayer::get_volume_slider failed to get BMediaRoster");
748 		return;
749 	}
750 
751 	if (!fParameterWeb && roster->GetParameterWebFor(fMediaInput.node, &fParameterWeb) < B_OK) {
752 		TRACE("BSoundPlayer::get_volume_slider couldn't get parameter web");
753 		return;
754 	}
755 
756 	int count = fParameterWeb->CountParameters();
757 	for (int i = 0; i < count; i++) {
758 		BParameter *parameter = fParameterWeb->ParameterAt(i);
759 		if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
760 			continue;
761 		if ((parameter->ID() >> 16) != fMediaInput.destination.id)
762 			continue;
763 		if  (strcmp(parameter->Kind(), B_GAIN) != 0)
764 			continue;
765 		fVolumeSlider = (BContinuousParameter *)parameter;
766 		break;
767 	}
768 
769 #if DEBUG >0
770 	if (!fVolumeSlider) {
771 		TRACE("BSoundPlayer::get_volume_slider couldn't find volume control");
772 	}
773 #endif
774 }
775 
776 
777 /* virtual */ void
778 BSoundPlayer::Notify(sound_player_notification what, ...)
779 {
780 	CALLED();
781 	if (fLocker.Lock()) {
782 		if (fNotifierFunc)
783 			(*fNotifierFunc)(fCookie, what);
784 		fLocker.Unlock();
785 	}
786 }
787 
788 
789 /* virtual */ void
790 BSoundPlayer::PlayBuffer(void *buffer, size_t size, const media_raw_audio_format &format)
791 {
792 	if (fLocker.Lock()) {
793 		if (fPlayBufferFunc)
794 			(*fPlayBufferFunc)(fCookie, buffer, size, format);
795 		fLocker.Unlock();
796 	}
797 }
798 
799 
800 /*************************************************************
801  * public sound_error
802  *************************************************************/
803 
804 
805 sound_error::sound_error(const char *str)
806 {
807 	m_str_const = str;
808 }
809 
810 
811 const char *
812 sound_error::what() const throw ()
813 {
814 	return m_str_const;
815 }
816 
817