xref: /haiku/src/kits/media/SoundPlayer.cpp (revision 58481f0f6ef1a61ba07283f012cafbc2ed874ead)
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 <Sound.h>
10 #include <math.h>
11 #include <string.h>
12 
13 #include "debug.h"
14 #include "SoundPlayNode.h"
15 #include "SoundPlayer.h"
16 
17 #define atomic_read(a)	atomic_or(a, 0)
18 
19 // Flags used internally in BSoundPlayer
20 enum {
21 	F_NODES_CONNECTED	= (1 << 0),
22 	F_HAS_DATA			= (1 << 1),
23 	F_IS_STARTED		= (1 << 2),
24 	F_MUST_RELEASE_MIXER = (1 << 3),
25 };
26 
27 
28 static BSoundPlayer::play_id sCurrentPlayID = 1;
29 
30 
31 /*************************************************************
32  * public BSoundPlayer
33  *************************************************************/
34 
35 
36 BSoundPlayer::BSoundPlayer(const char * name,
37 						   void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format),
38 						   void (*Notifier)(void *, sound_player_notification what, ...),
39 						   void * cookie)
40 {
41 	CALLED();
42 
43 	TRACE("BSoundPlayer::BSoundPlayer: default constructor used\n");
44 
45 	media_multi_audio_format fmt = media_multi_audio_format::wildcard;
46 
47 	Init(NULL, &fmt, name, NULL, PlayBuffer, Notifier, cookie);
48 }
49 
50 
51 BSoundPlayer::BSoundPlayer(const media_raw_audio_format * format,
52 						   const char * name,
53 						   void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format),
54 						   void (*Notifier)(void *, sound_player_notification what, ...),
55 						   void * cookie)
56 {
57 	CALLED();
58 
59 	TRACE("BSoundPlayer::BSoundPlayer: raw audio format constructor used\n");
60 
61 	media_multi_audio_format fmt = media_multi_audio_format::wildcard;
62 	*(media_raw_audio_format *)&fmt = *format;
63 
64 #if DEBUG > 0
65 	char buf[100];
66 	media_format tmp; tmp.type = B_MEDIA_RAW_AUDIO; tmp.u.raw_audio = fmt;
67 	string_for_format(tmp, buf, sizeof(buf));
68 	TRACE("BSoundPlayer::BSoundPlayer: format %s\n", buf);
69 #endif
70 
71 	Init(NULL, &fmt, name, NULL, PlayBuffer, Notifier, cookie);
72 }
73 
74 
75 BSoundPlayer::BSoundPlayer(const media_node & toNode,
76 						   const media_multi_audio_format * format,
77 						   const char * name,
78 						   const media_input * input,
79 						   void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format),
80 						   void (*Notifier)(void *, sound_player_notification what, ...),
81 						   void * cookie)
82 {
83 	CALLED();
84 
85 	TRACE("BSoundPlayer::BSoundPlayer: multi audio format constructor used\n");
86 
87 	if ((toNode.kind & B_BUFFER_CONSUMER) == 0)
88 		debugger("BSoundPlayer: toNode must have B_BUFFER_CONSUMER kind!\n");
89 
90 #if DEBUG > 0
91 	char buf[100];
92 	media_format tmp; tmp.type = B_MEDIA_RAW_AUDIO; tmp.u.raw_audio = *format;
93 	string_for_format(tmp, buf, sizeof(buf));
94 	TRACE("BSoundPlayer::BSoundPlayer: format %s\n", buf);
95 #endif
96 
97 	Init(&toNode, format, name, input, PlayBuffer, Notifier, cookie);
98 }
99 
100 
101 /*************************************************************
102  * private BSoundPlayer
103  *************************************************************/
104 
105 
106 void
107 BSoundPlayer::Init(	const media_node * node,
108 					const media_multi_audio_format * format,
109 					const char * name,
110 					const media_input * input,
111 					void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format),
112 					void (*Notifier)(void *, sound_player_notification what, ...),
113 					void * cookie)
114 {
115 	CALLED();
116 	fPlayingSounds = NULL;
117 	fWaitingSounds = NULL;
118 
119 	fPlayerNode = NULL;
120 	fPlayBufferFunc = PlayBuffer;
121 	if (fPlayBufferFunc == NULL) {
122 		fPlayBufferFunc = _SoundPlayBufferFunc;
123 		fCookie = this;
124 	} else
125 		fCookie = cookie;
126 
127 	fNotifierFunc = Notifier;
128 	fVolumeDB = 0.0f;
129 	fFlags = 0;
130 	fInitStatus = B_ERROR;
131 	fParameterWeb = NULL;
132 	fVolumeSlider = NULL;
133 	fLastVolumeUpdate = 0;
134 
135 	status_t 		err;
136 	media_node		timeSource;
137 	media_node		inputNode;
138 	media_output	_output;
139 	media_input		_input;
140 	int32 			inputCount;
141 	int32			outputCount;
142 	media_format 	tryFormat;
143 
144 	BMediaRoster *roster = BMediaRoster::Roster();
145 	if (!roster) {
146 		TRACE("BSoundPlayer::Init: Couldn't get BMediaRoster\n");
147 		return;
148 	}
149 
150 	// The inputNode that our player node will be
151 	// connected with is either supplied by the user
152 	// or the system audio mixer
153 	if (node) {
154 		inputNode = *node;
155 	} else {
156 		err = roster->GetAudioMixer(&inputNode);
157 		if (err != B_OK) {
158 			TRACE("BSoundPlayer::Init: Couldn't GetAudioMixer\n");
159 			goto the_end;
160 		}
161 		fFlags |= F_MUST_RELEASE_MIXER;
162 	}
163 
164 	// Create the player node and register it
165 	fPlayerNode = new _SoundPlayNode(name, this);
166 	err = roster->RegisterNode(fPlayerNode);
167 	if (err != B_OK) {
168 		TRACE("BSoundPlayer::Init: Couldn't RegisterNode\n");
169 		goto the_end;
170 	}
171 
172 	// set the producer's time source to be the "default" time source,
173 	// which the system audio mixer uses too.
174 	err = roster->GetTimeSource(&timeSource);
175 	if (err != B_OK) {
176 		TRACE("BSoundPlayer::Init: Couldn't GetTimeSource\n");
177 		goto the_end;
178 	}
179 	err = roster->SetTimeSourceFor(fPlayerNode->Node().node, timeSource.node);
180 	if (err != B_OK) {
181 		TRACE("BSoundPlayer::Init: Couldn't SetTimeSourceFor\n");
182 		goto the_end;
183 	}
184 
185 	// find a free media_input
186 	if (!input) {
187 		err = roster->GetFreeInputsFor(inputNode, &_input, 1, &inputCount, B_MEDIA_RAW_AUDIO);
188 		if (err != B_OK) {
189 			TRACE("BSoundPlayer::Init: Couldn't GetFreeInputsFor\n");
190 			goto the_end;
191 		}
192 		if (inputCount < 1) {
193 			TRACE("BSoundPlayer::Init: Couldn't find a free input\n");
194 			err = B_ERROR;
195 			goto the_end;
196 		}
197 	} else {
198 		_input = *input;
199 	}
200 
201 	// find a free media_output
202 	err = roster->GetFreeOutputsFor(fPlayerNode->Node(), &_output, 1, &outputCount, B_MEDIA_RAW_AUDIO);
203 	if (err != B_OK) {
204 		TRACE("BSoundPlayer::Init: Couldn't GetFreeOutputsFor\n");
205 		goto the_end;
206 	}
207 	if (outputCount < 1) {
208 		TRACE("BSoundPlayer::Init: Couldn't find a free output\n");
209 		err = B_ERROR;
210 		goto the_end;
211 	}
212 
213 	// Set an appropriate run mode for the producer
214 	err = roster->SetRunModeNode(fPlayerNode->Node(), BMediaNode::B_INCREASE_LATENCY);
215 	if (err != B_OK) {
216 		TRACE("BSoundPlayer::Init: Couldn't SetRunModeNode\n");
217 		goto the_end;
218 	}
219 
220 	// setup our requested format (can still have many wildcards)
221 	tryFormat.type = B_MEDIA_RAW_AUDIO;
222 	tryFormat.u.raw_audio = *format;
223 
224 #if DEBUG > 0
225 	char buf[100];
226 	string_for_format(tryFormat, buf, sizeof(buf));
227 	TRACE("BSoundPlayer::Init: trying to connect with format %s\n", buf);
228 #endif
229 
230 	// and connect the nodes
231 	err = roster->Connect(_output.source, _input.destination, &tryFormat, &fMediaOutput, &fMediaInput);
232 	if (err != B_OK) {
233 		TRACE("BSoundPlayer::Init: Couldn't Connect\n");
234 		goto the_end;
235 	}
236 
237 	fFlags |= F_NODES_CONNECTED;
238 
239 	get_volume_slider();
240 
241 	TRACE("BSoundPlayer node %ld has timesource %ld\n", fPlayerNode->Node().node, fPlayerNode->TimeSource()->Node().node);
242 
243 the_end:
244 	TRACE("BSoundPlayer::Init: %s\n", strerror(err));
245 	SetInitError(err);
246 }
247 
248 
249 /*************************************************************
250  * public BSoundPlayer
251  *************************************************************/
252 
253 
254 /* virtual */
255 BSoundPlayer::~BSoundPlayer()
256 {
257 	CALLED();
258 
259 	if (fFlags & F_IS_STARTED) {
260 		Stop(true, false); // block, but don't flush
261 	}
262 
263 	status_t err;
264 	BMediaRoster *roster = BMediaRoster::Roster();
265 	if (!roster) {
266 		TRACE("BSoundPlayer::~BSoundPlayer: Couldn't get BMediaRoster\n");
267 		goto cleanup;
268 	}
269 
270 	if (fFlags & F_NODES_CONNECTED) {
271 		// Ordinarily we'd stop *all* of the nodes in the chain before disconnecting. However,
272 		// our node is already stopped, and we can't stop the System Mixer.
273 		// So, we just disconnect from it, and release our references to the nodes that
274 		// we're using.  We *are* supposed to do that even for global nodes like the Mixer.
275 		err = roster->Disconnect(fMediaOutput, fMediaInput);
276 #if DEBUG >0
277 		if (err) {
278 			TRACE("BSoundPlayer::~BSoundPlayer: Error disconnecting nodes:  %ld (%s)\n", err, strerror(err));
279 		}
280 #endif
281 	}
282 
283 	if (fFlags & F_MUST_RELEASE_MIXER) {
284 		// Release the mixer as it was acquired
285 		// through BMediaRoster::GetAudioMixer()
286 		err = roster->ReleaseNode(fMediaInput.node);
287 #if DEBUG >0
288 		if (err) {
289 			TRACE("BSoundPlayer::~BSoundPlayer: Error releasing input node:  %ld (%s)\n", err, strerror(err));
290 		}
291 #endif
292 	}
293 
294 cleanup:
295 
296 	// Dispose of the player node
297 	if (fPlayerNode) {
298 		// We do not call BMediaRoster::ReleaseNode(), since
299 		// the player was created by using "new". We could
300 		// call BMediaRoster::UnregisterNode(), but this is
301 		// supposed to be done by BMediaNode destructor automatically
302 		delete fPlayerNode;
303 	}
304 
305 	delete fParameterWeb;
306 	// do not delete fVolumeSlider, it belonged to the parameter web
307 }
308 
309 
310 status_t
311 BSoundPlayer::InitCheck()
312 {
313 	CALLED();
314 	return fInitStatus;
315 }
316 
317 
318 media_raw_audio_format
319 BSoundPlayer::Format() const
320 {
321 	CALLED();
322 
323 	if ((fFlags & F_NODES_CONNECTED) == 0)
324 		return media_raw_audio_format::wildcard;
325 
326 	return fPlayerNode->Format();
327 }
328 
329 
330 status_t
331 BSoundPlayer::Start()
332 {
333 	CALLED();
334 
335 	if ((fFlags & F_NODES_CONNECTED) == 0)
336 		return B_NO_INIT;
337 
338 	if (fFlags & F_IS_STARTED)
339 		return B_OK;
340 
341 	BMediaRoster *roster = BMediaRoster::Roster();
342 	if (!roster) {
343 		TRACE("BSoundPlayer::Start: Couldn't get BMediaRoster\n");
344 		return B_ERROR;
345 	}
346 
347 	if (!fPlayerNode->TimeSource()->IsRunning()) {
348 		roster->StartTimeSource(fPlayerNode->TimeSource()->Node(),
349 			fPlayerNode->TimeSource()->RealTime());
350 	}
351 
352 	// Add latency and a few ms to the nodes current time to
353 	// make sure that we give the producer enough time to run
354 	// buffers through the node chain, otherwise it'll start
355 	// up already late
356 
357 	status_t err = roster->StartNode(fPlayerNode->Node(), fPlayerNode->TimeSource()->Now() + Latency() + 5000);
358 	if (err != B_OK) {
359 		TRACE("BSoundPlayer::Start: StartNode failed, %ld", err);
360 		return err;
361 	}
362 
363 	if (fNotifierFunc)
364 		fNotifierFunc(fCookie, B_STARTED, this);
365 
366 	SetHasData(true);
367 	atomic_or(&fFlags, F_IS_STARTED);
368 
369 	return B_OK;
370 }
371 
372 
373 void
374 BSoundPlayer::Stop(bool block,
375 				   bool flush)
376 {
377 	CALLED();
378 
379 	TRACE("BSoundPlayer::Stop: block %d, flush %d\n", (int)block, (int)flush);
380 
381 	if ((fFlags & F_NODES_CONNECTED) == 0)
382 		return;
383 
384 	// XXX flush is ignored
385 
386 	if (fFlags & F_IS_STARTED) {
387 
388 		BMediaRoster *roster = BMediaRoster::Roster();
389 		if (!roster) {
390 			TRACE("BSoundPlayer::Stop: Couldn't get BMediaRoster\n");
391 			return;
392 		}
393 
394 		roster->StopNode(fPlayerNode->Node(), 0, true);
395 
396 		atomic_and(&fFlags, ~F_IS_STARTED);
397 	}
398 
399 	if (block) {
400 		// wait until the node is stopped
401 		int maxtrys;
402 		for (maxtrys = 250; fPlayerNode->IsPlaying() && maxtrys != 0; maxtrys--)
403 			snooze(2000);
404 
405 		DEBUG_ONLY(if (maxtrys == 0) TRACE("BSoundPlayer::Stop: waiting for node stop failed\n"));
406 
407 		// wait until all buffers on the way to the physical output have been played
408 		snooze(Latency() + 2000);
409 	}
410 
411 	if (fNotifierFunc)
412 		fNotifierFunc(fCookie, B_STOPPED, this);
413 
414 }
415 
416 
417 bigtime_t
418 BSoundPlayer::Latency()
419 {
420 	CALLED();
421 
422 	if ((fFlags & F_NODES_CONNECTED) == 0)
423 		return 0;
424 
425 	BMediaRoster *roster = BMediaRoster::Roster();
426 	if (!roster) {
427 		TRACE("BSoundPlayer::Latency: Couldn't get BMediaRoster\n");
428 		return 0;
429 	}
430 
431 	bigtime_t latency;
432 	status_t err = roster->GetLatencyFor(fMediaOutput.node, &latency);
433 	if (err != B_OK) {
434 		TRACE("BSoundPlayer::Latency: GetLatencyFor failed %ld (%s)\n", err, strerror(err));
435 		return 0;
436 	}
437 
438 	TRACE("BSoundPlayer::Latency: latency is %Ld\n", latency);
439 
440 	return latency;
441 }
442 
443 
444 void
445 BSoundPlayer::SetHasData(bool has_data)
446 {
447 	CALLED();
448 	if (has_data)
449 		atomic_or(&fFlags, F_HAS_DATA);
450 	else
451 		atomic_and(&fFlags, ~F_HAS_DATA);
452 }
453 
454 
455 /* virtual */ bool
456 BSoundPlayer::HasData()
457 {
458 	CALLED();
459 	return (atomic_read(&fFlags) & F_HAS_DATA) != 0;
460 }
461 
462 
463 BSoundPlayer::BufferPlayerFunc
464 BSoundPlayer::BufferPlayer() const
465 {
466 	CALLED();
467 	return fPlayBufferFunc;
468 }
469 
470 
471 void
472 BSoundPlayer::SetBufferPlayer(void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format))
473 {
474 	CALLED();
475 	fLocker.Lock();
476 	fPlayBufferFunc = PlayBuffer;
477 	fLocker.Unlock();
478 }
479 
480 
481 BSoundPlayer::EventNotifierFunc
482 BSoundPlayer::EventNotifier() const
483 {
484 	CALLED();
485 	return fNotifierFunc;
486 }
487 
488 
489 void BSoundPlayer::SetNotifier(void (*Notifier)(void *, sound_player_notification what, ...))
490 {
491 	CALLED();
492 	fLocker.Lock();
493 	fNotifierFunc = Notifier;
494 	fLocker.Unlock();
495 }
496 
497 
498 void *
499 BSoundPlayer::Cookie() const
500 {
501 	CALLED();
502 	return fCookie;
503 }
504 
505 
506 void
507 BSoundPlayer::SetCookie(void *cookie)
508 {
509 	CALLED();
510 	fLocker.Lock();
511 	fCookie = cookie;
512 	fLocker.Unlock();
513 }
514 
515 
516 void
517 BSoundPlayer::SetCallbacks(void (*PlayBuffer)(void *, void * buffer, size_t size, const media_raw_audio_format & format),
518 						   void (*Notifier)(void *, sound_player_notification what, ...),
519 						   void * cookie)
520 {
521 	CALLED();
522 	fLocker.Lock();
523 	SetBufferPlayer(PlayBuffer);
524 	SetNotifier(Notifier);
525 	SetCookie(cookie);
526 	fLocker.Unlock();
527 }
528 
529 
530 /* The BeBook is inaccurate about the meaning of this function.
531  * The probably best interpretation is to return the time that
532  * has elapsed since playing was started, whichs seems to match
533  * " CurrentTime() returns the current media time "
534  */
535 bigtime_t
536 BSoundPlayer::CurrentTime()
537 {
538 	if ((fFlags & F_NODES_CONNECTED) == 0)
539 		return 0;
540 
541 	return fPlayerNode->CurrentTime();
542 }
543 
544 
545 /* Returns the current performance time of the sound player node
546  * being used by the BSoundPlayer. Will return B_ERROR if the
547  * BSoundPlayer object hasn't been properly initialized.
548  */
549 bigtime_t
550 BSoundPlayer::PerformanceTime()
551 {
552 	if ((fFlags & F_NODES_CONNECTED) == 0)
553 		return (bigtime_t) B_ERROR;
554 
555 	return fPlayerNode->TimeSource()->Now();
556 }
557 
558 
559 status_t
560 BSoundPlayer::Preroll()
561 {
562 	CALLED();
563 
564 	if ((fFlags & F_NODES_CONNECTED) == 0)
565 		return B_NO_INIT;
566 
567 	BMediaRoster *roster = BMediaRoster::Roster();
568 	if (!roster) {
569 		TRACE("BSoundPlayer::Preroll: Couldn't get BMediaRoster\n");
570 		return B_ERROR;
571 	}
572 
573 	status_t err = roster->PrerollNode(fMediaOutput.node);
574 	if (err != B_OK) {
575 		TRACE("BSoundPlayer::Preroll: Error while PrerollNode:  %ld (%s)\n", err, strerror(err));
576 		return err;
577 	}
578 
579 	return B_OK;
580 }
581 
582 
583 BSoundPlayer::play_id
584 BSoundPlayer::StartPlaying(BSound *sound, bigtime_t atTime)
585 {
586 	return StartPlaying(sound, atTime, 1.0);
587 }
588 
589 
590 BSoundPlayer::play_id
591 BSoundPlayer::StartPlaying(BSound *sound, bigtime_t atTime, float withVolume)
592 {
593 	CALLED();
594 
595 	// TODO: support the at_time and with_volume parameters
596 	_playing_sound *item = (_playing_sound *)malloc(sizeof(_playing_sound));
597 	if (item == NULL)
598 		return B_NO_MEMORY;
599 
600 	item->current_offset = 0;
601 	item->sound = sound;
602 	item->id = atomic_add(&sCurrentPlayID, 1);
603 	item->delta = 0;
604 	item->rate = 0;
605 	item->volume = withVolume;
606 
607 	if (!fLocker.Lock()) {
608 		free(item);
609 		return B_ERROR;
610 	}
611 
612 	item->next = fPlayingSounds;
613 	fPlayingSounds = item;
614 	fLocker.Unlock();
615 
616 	SetHasData(true);
617 	return item->id;
618 }
619 
620 
621 status_t
622 BSoundPlayer::SetSoundVolume(play_id id, float newVolume)
623 {
624 	CALLED();
625 	if (!fLocker.Lock())
626 		return B_ERROR;
627 
628 	_playing_sound *item = fPlayingSounds;
629 	while (item) {
630 		if (item->id == id) {
631 			item->volume = newVolume;
632 			fLocker.Unlock();
633 			return B_OK;
634 		}
635 
636 		item = item->next;
637 	}
638 
639 	fLocker.Unlock();
640 	return B_ENTRY_NOT_FOUND;
641 }
642 
643 
644 bool
645 BSoundPlayer::IsPlaying(play_id id)
646 {
647 	CALLED();
648 	if (!fLocker.Lock())
649 		return B_ERROR;
650 
651 	_playing_sound *item = fPlayingSounds;
652 	while (item) {
653 		if (item->id == id) {
654 			fLocker.Unlock();
655 			return true;
656 		}
657 
658 		item = item->next;
659 	}
660 
661 	fLocker.Unlock();
662 	return false;
663 }
664 
665 
666 status_t
667 BSoundPlayer::StopPlaying(play_id id)
668 {
669 	CALLED();
670 	if (!fLocker.Lock())
671 		return B_ERROR;
672 
673 	_playing_sound **link = &fPlayingSounds;
674 	_playing_sound *item = fPlayingSounds;
675 	while (item) {
676 		if (item->id == id) {
677 			*link = item->next;
678 			sem_id waitSem = item->wait_sem;
679 			free(item);
680 			fLocker.Unlock();
681 
682 			NotifySoundDone(id, true);
683 			if (waitSem >= 0)
684 				release_sem(waitSem);
685 
686 			return B_OK;
687 		}
688 
689 		link = &item->next;
690 		item = item->next;
691 	}
692 
693 	fLocker.Unlock();
694 	return B_ENTRY_NOT_FOUND;
695 }
696 
697 
698 status_t
699 BSoundPlayer::WaitForSound(play_id id)
700 {
701 	CALLED();
702 	if (!fLocker.Lock())
703 		return B_ERROR;
704 
705 	_playing_sound *item = fPlayingSounds;
706 	while (item) {
707 		if (item->id == id) {
708 			sem_id waitSem = item->wait_sem;
709 			if (waitSem < 0)
710 				waitSem = item->wait_sem = create_sem(0, "wait for sound");
711 
712 			fLocker.Unlock();
713 			return acquire_sem(waitSem);
714 		}
715 
716 		item = item->next;
717 	}
718 
719 	fLocker.Unlock();
720 	return B_ENTRY_NOT_FOUND;
721 }
722 
723 
724 float
725 BSoundPlayer::Volume()
726 {
727 	CALLED();
728 	return pow(10.0, VolumeDB(true) / 20.0);
729 }
730 
731 
732 void
733 BSoundPlayer::SetVolume(float new_volume)
734 {
735 	CALLED();
736 	SetVolumeDB(20.0 * log10(new_volume));
737 }
738 
739 
740 float
741 BSoundPlayer::VolumeDB(bool forcePoll)
742 {
743 	CALLED();
744 	if (!fVolumeSlider)
745 		return -94.0f; // silence
746 
747 	if (!forcePoll && (system_time() - fLastVolumeUpdate < 500000))
748 		return fVolumeDB;
749 
750 	int32 count = fVolumeSlider->CountChannels();
751 	float values[count];
752 	size_t size = count * sizeof(float);
753 	fVolumeSlider->GetValue(&values, &size, NULL);
754 	fLastVolumeUpdate = system_time();
755 	fVolumeDB = values[0];
756 
757 	return values[0];
758 }
759 
760 
761 void
762 BSoundPlayer::SetVolumeDB(float volume_dB)
763 {
764 	CALLED();
765 	if (!fVolumeSlider)
766 		return;
767 
768 	float min_dB = fVolumeSlider->MinValue();
769 	float max_dB = fVolumeSlider->MaxValue();
770 	if (volume_dB < min_dB)
771 		volume_dB = min_dB;
772 	if (volume_dB > max_dB)
773 		volume_dB = max_dB;
774 
775 	int count = fVolumeSlider->CountChannels();
776 	float values[count];
777 	for (int i = 0; i < count; i++)
778 		values[i] = volume_dB;
779 	fVolumeSlider->SetValue(values, sizeof(float) * count, 0);
780 
781 	fVolumeDB = volume_dB;
782 	fLastVolumeUpdate = system_time();
783 }
784 
785 
786 status_t
787 BSoundPlayer::GetVolumeInfo(media_node *out_node,
788 							int32 *out_parameter_id,
789 							float *out_min_dB,
790 							float *out_max_dB)
791 {
792 	CALLED();
793 	if (!fVolumeSlider)
794 		return B_NO_INIT;
795 
796 	if (out_node)
797 		*out_node = fMediaInput.node;
798 	if (out_parameter_id)
799 		*out_parameter_id = fVolumeSlider->ID();
800 	if (out_min_dB)
801 		*out_min_dB = fVolumeSlider->MinValue();
802 	if (out_max_dB)
803 		*out_max_dB = fVolumeSlider->MaxValue();
804 	return B_OK;
805 }
806 
807 
808 
809 /*************************************************************
810  * protected BSoundPlayer
811  *************************************************************/
812 
813 
814 void
815 BSoundPlayer::SetInitError(status_t in_error)
816 {
817 	CALLED();
818 	fInitStatus = in_error;
819 }
820 
821 
822 /*************************************************************
823  * private BSoundPlayer
824  *************************************************************/
825 
826 void
827 BSoundPlayer::_SoundPlayBufferFunc(void *cookie, void *buffer, size_t size,
828 	const media_raw_audio_format &format)
829 {
830 	// TODO: support more than one sound and make use of the format parameter
831 	BSoundPlayer *player = (BSoundPlayer *)cookie;
832 	if (!player->fLocker.Lock()) {
833 		memset(buffer, 0, size);
834 		return;
835 	}
836 
837 	_playing_sound *sound = player->fPlayingSounds;
838 	if (sound == NULL) {
839 		player->SetHasData(false);
840 		player->fLocker.Unlock();
841 		memset(buffer, 0, size);
842 		return;
843 	}
844 
845 	size_t used = 0;
846 	if (!sound->sound->GetDataAt(sound->current_offset, buffer, size, &used)) {
847 		// will take care of removing the item and notifying others
848 		player->StopPlaying(sound->id);
849 		player->fLocker.Unlock();
850 		memset(buffer, 0, size);
851 		return;
852 	}
853 
854 	sound->current_offset += used;
855 	player->fLocker.Unlock();
856 
857 	if (used < size)
858 		memset((uint8 *)buffer + used, 0, size - used);
859 }
860 
861 
862 status_t BSoundPlayer::_Reserved_SoundPlayer_0(void *, ...) { return B_ERROR; }
863 status_t BSoundPlayer::_Reserved_SoundPlayer_1(void *, ...) { return B_ERROR; }
864 status_t BSoundPlayer::_Reserved_SoundPlayer_2(void *, ...) { return B_ERROR; }
865 status_t BSoundPlayer::_Reserved_SoundPlayer_3(void *, ...) { return B_ERROR; }
866 status_t BSoundPlayer::_Reserved_SoundPlayer_4(void *, ...) { return B_ERROR; }
867 status_t BSoundPlayer::_Reserved_SoundPlayer_5(void *, ...) { return B_ERROR; }
868 status_t BSoundPlayer::_Reserved_SoundPlayer_6(void *, ...) { return B_ERROR; }
869 status_t BSoundPlayer::_Reserved_SoundPlayer_7(void *, ...) { return B_ERROR; }
870 
871 
872 void
873 BSoundPlayer::NotifySoundDone(play_id id, bool gotToPlay)
874 {
875 	CALLED();
876 	Notify(B_SOUND_DONE, id, gotToPlay);
877 }
878 
879 
880 void
881 BSoundPlayer::get_volume_slider()
882 {
883 	CALLED();
884 
885 	ASSERT(fVolumeSlider == NULL);
886 
887 	BMediaRoster *roster = BMediaRoster::CurrentRoster();
888 	if (!roster) {
889 		TRACE("BSoundPlayer::get_volume_slider failed to get BMediaRoster");
890 		return;
891 	}
892 
893 	if (!fParameterWeb && roster->GetParameterWebFor(fMediaInput.node, &fParameterWeb) < B_OK) {
894 		TRACE("BSoundPlayer::get_volume_slider couldn't get parameter web");
895 		return;
896 	}
897 
898 	int count = fParameterWeb->CountParameters();
899 	for (int i = 0; i < count; i++) {
900 		BParameter *parameter = fParameterWeb->ParameterAt(i);
901 		if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
902 			continue;
903 		if ((parameter->ID() >> 16) != fMediaInput.destination.id)
904 			continue;
905 		if  (strcmp(parameter->Kind(), B_GAIN) != 0)
906 			continue;
907 		fVolumeSlider = (BContinuousParameter *)parameter;
908 		break;
909 	}
910 
911 #if DEBUG >0
912 	if (!fVolumeSlider) {
913 		TRACE("BSoundPlayer::get_volume_slider couldn't find volume control");
914 	}
915 #endif
916 }
917 
918 
919 /* virtual */ void
920 BSoundPlayer::Notify(sound_player_notification what, ...)
921 {
922 	CALLED();
923 	if (fLocker.Lock()) {
924 		if (fNotifierFunc)
925 			(*fNotifierFunc)(fCookie, what);
926 		fLocker.Unlock();
927 	}
928 }
929 
930 
931 /* virtual */ void
932 BSoundPlayer::PlayBuffer(void *buffer, size_t size, const media_raw_audio_format &format)
933 {
934 	if (fLocker.Lock()) {
935 		if (fPlayBufferFunc)
936 			(*fPlayBufferFunc)(fCookie, buffer, size, format);
937 		fLocker.Unlock();
938 	}
939 }
940 
941 
942 /*************************************************************
943  * public sound_error
944  *************************************************************/
945 
946 
947 sound_error::sound_error(const char *str)
948 {
949 	m_str_const = str;
950 }
951 
952 
953 const char *
954 sound_error::what() const throw ()
955 {
956 	return m_str_const;
957 }
958 
959