1 /*
2 * Copyright 2002-2010, 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 /*! This is the BBufferProducer used internally by BSoundPlayer.
12 */
13
14
15 #include "SoundPlayNode.h"
16
17 #include <string.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20
21 #include <TimeSource.h>
22 #include <MediaRoster.h>
23 #include "MediaDebug.h"
24
25
26 #define SEND_NEW_BUFFER_EVENT (BTimedEventQueue::B_USER_EVENT + 1)
27
28
29 namespace BPrivate {
30
31
SoundPlayNode(const char * name,BSoundPlayer * player)32 SoundPlayNode::SoundPlayNode(const char* name, BSoundPlayer* player)
33 :
34 BMediaNode(name),
35 BBufferProducer(B_MEDIA_RAW_AUDIO),
36 BMediaEventLooper(),
37 fPlayer(player),
38 fInitStatus(B_OK),
39 fOutputEnabled(true),
40 fBufferGroup(NULL),
41 fFramesSent(0),
42 fTooEarlyCount(0)
43 {
44 CALLED();
45 fOutput.format.type = B_MEDIA_RAW_AUDIO;
46 fOutput.format.u.raw_audio = media_multi_audio_format::wildcard;
47 }
48
49
~SoundPlayNode()50 SoundPlayNode::~SoundPlayNode()
51 {
52 CALLED();
53 Quit();
54 }
55
56
57 bool
IsPlaying()58 SoundPlayNode::IsPlaying()
59 {
60 return RunState() == B_STARTED;
61 }
62
63
64 bigtime_t
CurrentTime()65 SoundPlayNode::CurrentTime()
66 {
67 int frameRate = (int)fOutput.format.u.raw_audio.frame_rate;
68 return frameRate == 0 ? 0
69 : bigtime_t((1000000LL * fFramesSent) / frameRate);
70 }
71
72
73 media_multi_audio_format
Format() const74 SoundPlayNode::Format() const
75 {
76 return fOutput.format.u.raw_audio;
77 }
78
79
80 // #pragma mark - implementation of BMediaNode
81
82
83 BMediaAddOn*
AddOn(int32 * _internalID) const84 SoundPlayNode::AddOn(int32* _internalID) const
85 {
86 CALLED();
87 // This only gets called if we are in an add-on.
88 return NULL;
89 }
90
91
92 void
Preroll()93 SoundPlayNode::Preroll()
94 {
95 CALLED();
96 // TODO: Performance opportunity
97 BMediaNode::Preroll();
98 }
99
100
101 status_t
HandleMessage(int32 message,const void * data,size_t size)102 SoundPlayNode::HandleMessage(int32 message, const void* data, size_t size)
103 {
104 CALLED();
105 return B_ERROR;
106 }
107
108
109 void
NodeRegistered()110 SoundPlayNode::NodeRegistered()
111 {
112 CALLED();
113
114 if (fInitStatus != B_OK) {
115 ReportError(B_NODE_IN_DISTRESS);
116 return;
117 }
118
119 SetPriority(B_URGENT_PRIORITY);
120
121 fOutput.format.type = B_MEDIA_RAW_AUDIO;
122 fOutput.format.u.raw_audio = media_multi_audio_format::wildcard;
123 fOutput.destination = media_destination::null;
124 fOutput.source.port = ControlPort();
125 fOutput.source.id = 0;
126 fOutput.node = Node();
127 strcpy(fOutput.name, Name());
128
129 Run();
130 }
131
132
133 status_t
RequestCompleted(const media_request_info & info)134 SoundPlayNode::RequestCompleted(const media_request_info& info)
135 {
136 CALLED();
137 return B_OK;
138 }
139
140
141 void
SetTimeSource(BTimeSource * timeSource)142 SoundPlayNode::SetTimeSource(BTimeSource* timeSource)
143 {
144 CALLED();
145 BMediaNode::SetTimeSource(timeSource);
146 }
147
148
149 void
SetRunMode(run_mode mode)150 SoundPlayNode::SetRunMode(run_mode mode)
151 {
152 TRACE("SoundPlayNode::SetRunMode mode:%i\n", mode);
153 BMediaNode::SetRunMode(mode);
154 }
155
156
157 // #pragma mark - implementation for BBufferProducer
158
159
160 status_t
FormatSuggestionRequested(media_type type,int32,media_format * format)161 SoundPlayNode::FormatSuggestionRequested(media_type type, int32 /*quality*/,
162 media_format* format)
163 {
164 // FormatSuggestionRequested() is not necessarily part of the format
165 // negotiation process; it's simply an interrogation -- the caller wants
166 // to see what the node's preferred data format is, given a suggestion by
167 // the caller.
168 CALLED();
169
170 // a wildcard type is okay; but we only support raw audio
171 if (type != B_MEDIA_RAW_AUDIO && type != B_MEDIA_UNKNOWN_TYPE)
172 return B_MEDIA_BAD_FORMAT;
173
174 // this is the format we'll be returning (our preferred format)
175 format->type = B_MEDIA_RAW_AUDIO;
176 format->u.raw_audio = media_multi_audio_format::wildcard;
177
178 return B_OK;
179 }
180
181
182 status_t
FormatProposal(const media_source & output,media_format * format)183 SoundPlayNode::FormatProposal(const media_source& output, media_format* format)
184 {
185 // FormatProposal() is the first stage in the BMediaRoster::Connect()
186 // process. We hand out a suggested format, with wildcards for any
187 // variations we support.
188 CALLED();
189
190 // is this a proposal for our one output?
191 if (output != fOutput.source) {
192 TRACE("SoundPlayNode::FormatProposal returning B_MEDIA_BAD_SOURCE\n");
193 return B_MEDIA_BAD_SOURCE;
194 }
195
196 // if wildcard, change it to raw audio
197 if (format->type == B_MEDIA_UNKNOWN_TYPE)
198 format->type = B_MEDIA_RAW_AUDIO;
199
200 // if not raw audio, we can't support it
201 if (format->type != B_MEDIA_RAW_AUDIO) {
202 TRACE("SoundPlayNode::FormatProposal returning B_MEDIA_BAD_FORMAT\n");
203 return B_MEDIA_BAD_FORMAT;
204 }
205
206 #if DEBUG >0
207 char buf[100];
208 string_for_format(*format, buf, sizeof(buf));
209 TRACE("SoundPlayNode::FormatProposal: format %s\n", buf);
210 #endif
211
212 return B_OK;
213 }
214
215
216 status_t
FormatChangeRequested(const media_source & source,const media_destination & destination,media_format * _format,int32 *)217 SoundPlayNode::FormatChangeRequested(const media_source& source,
218 const media_destination& destination, media_format* _format,
219 int32* /* deprecated */)
220 {
221 CALLED();
222
223 // we don't support any other formats, so we just reject any format changes.
224 return B_ERROR;
225 }
226
227
228 status_t
GetNextOutput(int32 * cookie,media_output * _output)229 SoundPlayNode::GetNextOutput(int32* cookie, media_output* _output)
230 {
231 CALLED();
232
233 if (*cookie == 0) {
234 *_output = fOutput;
235 *cookie += 1;
236 return B_OK;
237 } else {
238 return B_BAD_INDEX;
239 }
240 }
241
242
243 status_t
DisposeOutputCookie(int32 cookie)244 SoundPlayNode::DisposeOutputCookie(int32 cookie)
245 {
246 CALLED();
247 // do nothing because we don't use the cookie for anything special
248 return B_OK;
249 }
250
251
252 status_t
SetBufferGroup(const media_source & forSource,BBufferGroup * newGroup)253 SoundPlayNode::SetBufferGroup(const media_source& forSource,
254 BBufferGroup* newGroup)
255 {
256 CALLED();
257
258 // is this our output?
259 if (forSource != fOutput.source) {
260 TRACE("SoundPlayNode::SetBufferGroup returning B_MEDIA_BAD_SOURCE\n");
261 return B_MEDIA_BAD_SOURCE;
262 }
263
264 // Are we being passed the buffer group we're already using?
265 if (newGroup == fBufferGroup)
266 return B_OK;
267
268 // Ahh, someone wants us to use a different buffer group. At this point we
269 // delete the one we are using and use the specified one instead.
270 // If the specified group is NULL, we need to recreate one ourselves, and
271 // use *that*. Note that if we're caching a BBuffer that we requested
272 // earlier, we have to Recycle() that buffer *before* deleting the buffer
273 // group, otherwise we'll deadlock waiting for that buffer to be recycled!
274 delete fBufferGroup;
275 // waits for all buffers to recycle
276
277 if (newGroup != NULL) {
278 // we were given a valid group; just use that one from now on
279 fBufferGroup = newGroup;
280 return B_OK;
281 }
282
283 // we were passed a NULL group pointer; that means we construct
284 // our own buffer group to use from now on
285 return AllocateBuffers();
286 }
287
288
289 status_t
GetLatency(bigtime_t * _latency)290 SoundPlayNode::GetLatency(bigtime_t* _latency)
291 {
292 CALLED();
293
294 // report our *total* latency: internal plus downstream plus scheduling
295 *_latency = EventLatency() + SchedulingLatency();
296 return B_OK;
297 }
298
299
300 status_t
PrepareToConnect(const media_source & what,const media_destination & where,media_format * format,media_source * _source,char * _name)301 SoundPlayNode::PrepareToConnect(const media_source& what,
302 const media_destination& where, media_format* format,
303 media_source* _source, char* _name)
304 {
305 // PrepareToConnect() is the second stage of format negotiations that
306 // happens inside BMediaRoster::Connect(). At this point, the consumer's
307 // AcceptFormat() method has been called, and that node has potentially
308 // changed the proposed format. It may also have left wildcards in the
309 // format. PrepareToConnect() *must* fully specialize the format before
310 // returning!
311 CALLED();
312
313 // is this our output?
314 if (what != fOutput.source) {
315 TRACE("SoundPlayNode::PrepareToConnect returning "
316 "B_MEDIA_BAD_SOURCE\n");
317 return B_MEDIA_BAD_SOURCE;
318 }
319
320 // are we already connected?
321 if (fOutput.destination != media_destination::null)
322 return B_MEDIA_ALREADY_CONNECTED;
323
324 // the format may not yet be fully specialized (the consumer might have
325 // passed back some wildcards). Finish specializing it now, and return an
326 // error if we don't support the requested format.
327
328 #if DEBUG > 0
329 char buf[100];
330 string_for_format(*format, buf, sizeof(buf));
331 TRACE("SoundPlayNode::PrepareToConnect: input format %s\n", buf);
332 #endif
333
334 // if not raw audio, we can't support it
335 if (format->type != B_MEDIA_UNKNOWN_TYPE
336 && format->type != B_MEDIA_RAW_AUDIO) {
337 TRACE("SoundPlayNode::PrepareToConnect: non raw format, returning "
338 "B_MEDIA_BAD_FORMAT\n");
339 return B_MEDIA_BAD_FORMAT;
340 }
341
342 // the haiku mixer might have a hint
343 // for us, so check for it
344 #define FORMAT_USER_DATA_TYPE 0x7294a8f3
345 #define FORMAT_USER_DATA_MAGIC_1 0xc84173bd
346 #define FORMAT_USER_DATA_MAGIC_2 0x4af62b7d
347 uint32 channel_count = 0;
348 float frame_rate = 0;
349 if (format->user_data_type == FORMAT_USER_DATA_TYPE
350 && *(uint32 *)&format->user_data[0] == FORMAT_USER_DATA_MAGIC_1
351 && *(uint32 *)&format->user_data[44] == FORMAT_USER_DATA_MAGIC_2) {
352 channel_count = *(uint32 *)&format->user_data[4];
353 frame_rate = *(float *)&format->user_data[20];
354 TRACE("SoundPlayNode::PrepareToConnect: found mixer info: "
355 "channel_count %" B_PRId32 " , frame_rate %.1f\n", channel_count, frame_rate);
356 }
357
358 media_format default_format;
359 default_format.type = B_MEDIA_RAW_AUDIO;
360 default_format.u.raw_audio.frame_rate = frame_rate > 0 ? frame_rate : 44100;
361 default_format.u.raw_audio.channel_count = channel_count > 0
362 ? channel_count : 2;
363 default_format.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
364 default_format.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
365 default_format.u.raw_audio.buffer_size = 0;
366 format->SpecializeTo(&default_format);
367
368 if (format->u.raw_audio.buffer_size == 0) {
369 format->u.raw_audio.buffer_size
370 = BMediaRoster::Roster()->AudioBufferSizeFor(
371 format->u.raw_audio.channel_count, format->u.raw_audio.format,
372 format->u.raw_audio.frame_rate);
373 }
374
375 #if DEBUG > 0
376 string_for_format(*format, buf, sizeof(buf));
377 TRACE("SoundPlayNode::PrepareToConnect: output format %s\n", buf);
378 #endif
379
380 // Now reserve the connection, and return information about it
381 fOutput.destination = where;
382 fOutput.format = *format;
383 *_source = fOutput.source;
384 strcpy(_name, Name());
385 return B_OK;
386 }
387
388
389 void
Connect(status_t error,const media_source & source,const media_destination & destination,const media_format & format,char * name)390 SoundPlayNode::Connect(status_t error, const media_source& source,
391 const media_destination& destination, const media_format& format,
392 char* name)
393 {
394 CALLED();
395
396 // is this our output?
397 if (source != fOutput.source) {
398 TRACE("SoundPlayNode::Connect returning\n");
399 return;
400 }
401
402 // If something earlier failed, Connect() might still be called, but with
403 // a non-zero error code. When that happens we simply unreserve the
404 // connection and do nothing else.
405 if (error) {
406 fOutput.destination = media_destination::null;
407 fOutput.format.type = B_MEDIA_RAW_AUDIO;
408 fOutput.format.u.raw_audio = media_multi_audio_format::wildcard;
409 return;
410 }
411
412 // Okay, the connection has been confirmed. Record the destination and
413 // format that we agreed on, and report our connection name again.
414 fOutput.destination = destination;
415 fOutput.format = format;
416 strcpy(name, Name());
417
418 // Now that we're connected, we can determine our downstream latency.
419 // Do so, then make sure we get our events early enough.
420 media_node_id id;
421 FindLatencyFor(fOutput.destination, &fLatency, &id);
422 TRACE("SoundPlayNode::Connect: downstream latency = %" B_PRId64 "\n",
423 fLatency);
424
425 // reset our buffer duration, etc. to avoid later calculations
426 bigtime_t duration = ((fOutput.format.u.raw_audio.buffer_size * 1000000LL)
427 / ((fOutput.format.u.raw_audio.format
428 & media_raw_audio_format::B_AUDIO_SIZE_MASK)
429 * fOutput.format.u.raw_audio.channel_count))
430 / (int32)fOutput.format.u.raw_audio.frame_rate;
431 SetBufferDuration(duration);
432 TRACE("SoundPlayNode::Connect: buffer duration is %" B_PRId64 "\n",
433 duration);
434
435 fInternalLatency = (3 * BufferDuration()) / 4;
436 TRACE("SoundPlayNode::Connect: using %" B_PRId64 " as internal latency\n",
437 fInternalLatency);
438 SetEventLatency(fLatency + fInternalLatency);
439
440 // Set up the buffer group for our connection, as long as nobody handed us
441 // a buffer group (via SetBufferGroup()) prior to this.
442 // That can happen, for example, if the consumer calls SetOutputBuffersFor()
443 // on us from within its Connected() method.
444 if (!fBufferGroup)
445 AllocateBuffers();
446 }
447
448
449 void
Disconnect(const media_source & what,const media_destination & where)450 SoundPlayNode::Disconnect(const media_source& what,
451 const media_destination& where)
452 {
453 CALLED();
454
455 // is this our output?
456 if (what != fOutput.source) {
457 TRACE("SoundPlayNode::Disconnect returning\n");
458 return;
459 }
460
461 // Make sure that our connection is the one being disconnected
462 if (where == fOutput.destination && what == fOutput.source) {
463 fOutput.destination = media_destination::null;
464 fOutput.format.type = B_MEDIA_RAW_AUDIO;
465 fOutput.format.u.raw_audio = media_multi_audio_format::wildcard;
466 delete fBufferGroup;
467 fBufferGroup = NULL;
468 } else {
469 fprintf(stderr, "\tDisconnect() called with wrong source/destination "
470 "(%" B_PRId32 "/%" B_PRId32 "), ours is (%" B_PRId32 "/%" B_PRId32
471 ")\n", what.id, where.id, fOutput.source.id,
472 fOutput.destination.id);
473 }
474 }
475
476
477 void
LateNoticeReceived(const media_source & what,bigtime_t howMuch,bigtime_t performanceTime)478 SoundPlayNode::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
479 bigtime_t performanceTime)
480 {
481 CALLED();
482
483 TRACE("SoundPlayNode::LateNoticeReceived, %" B_PRId64 " too late at %"
484 B_PRId64 "\n", howMuch, performanceTime);
485
486 // is this our output?
487 if (what != fOutput.source) {
488 TRACE("SoundPlayNode::LateNoticeReceived returning\n");
489 return;
490 }
491
492 if (RunMode() != B_DROP_DATA) {
493 // We're late, and our run mode dictates that we try to produce buffers
494 // earlier in order to catch up. This argues that the downstream nodes are
495 // not properly reporting their latency, but there's not much we can do about
496 // that at the moment, so we try to start producing buffers earlier to
497 // compensate.
498
499 fInternalLatency += howMuch;
500
501 if (fInternalLatency > 30000) // avoid getting a too high latency
502 fInternalLatency = 30000;
503
504 SetEventLatency(fLatency + fInternalLatency);
505 TRACE("SoundPlayNode::LateNoticeReceived: increasing latency to %"
506 B_PRId64 "\n", fLatency + fInternalLatency);
507 } else {
508 // The other run modes dictate various strategies for sacrificing data quality
509 // in the interests of timely data delivery. The way *we* do this is to skip
510 // a buffer, which catches us up in time by one buffer duration.
511
512 size_t nFrames = fOutput.format.u.raw_audio.buffer_size
513 / ((fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK)
514 * fOutput.format.u.raw_audio.channel_count);
515
516 fFramesSent += nFrames;
517
518 TRACE("SoundPlayNode::LateNoticeReceived: skipping a buffer to try to catch up\n");
519 }
520 }
521
522
523 void
EnableOutput(const media_source & what,bool enabled,int32 *)524 SoundPlayNode::EnableOutput(const media_source& what, bool enabled,
525 int32* /* deprecated */)
526 {
527 CALLED();
528
529 // If I had more than one output, I'd have to walk my list of output
530 // records to see which one matched the given source, and then
531 // enable/disable that one.
532 // But this node only has one output, so I just make sure the given source
533 // matches, then set the enable state accordingly.
534
535 // is this our output?
536 if (what != fOutput.source) {
537 fprintf(stderr, "SoundPlayNode::EnableOutput returning\n");
538 return;
539 }
540
541 fOutputEnabled = enabled;
542 }
543
544
545 void
AdditionalBufferRequested(const media_source & source,media_buffer_id previousBuffer,bigtime_t previousTime,const media_seek_tag * previousTag)546 SoundPlayNode::AdditionalBufferRequested(const media_source& source,
547 media_buffer_id previousBuffer, bigtime_t previousTime,
548 const media_seek_tag* previousTag)
549 {
550 CALLED();
551 // we don't support offline mode
552 return;
553 }
554
555
556 void
LatencyChanged(const media_source & source,const media_destination & destination,bigtime_t newLatency,uint32 flags)557 SoundPlayNode::LatencyChanged(const media_source& source,
558 const media_destination& destination, bigtime_t newLatency, uint32 flags)
559 {
560 CALLED();
561
562 TRACE("SoundPlayNode::LatencyChanged: new_latency %" B_PRId64 "\n",
563 newLatency);
564
565 // something downstream changed latency, so we need to start producing
566 // buffers earlier (or later) than we were previously. Make sure that the
567 // connection that changed is ours, and adjust to the new downstream
568 // latency if so.
569 if (source == fOutput.source && destination == fOutput.destination) {
570 fLatency = newLatency;
571 SetEventLatency(fLatency + fInternalLatency);
572 } else {
573 TRACE("SoundPlayNode::LatencyChanged: ignored\n");
574 }
575 }
576
577
578 // #pragma mark - implementation for BMediaEventLooper
579
580
581 void
HandleEvent(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)582 SoundPlayNode::HandleEvent(const media_timed_event* event, bigtime_t lateness,
583 bool realTimeEvent)
584 {
585 CALLED();
586 switch (event->type) {
587 case BTimedEventQueue::B_START:
588 HandleStart(event,lateness,realTimeEvent);
589 break;
590 case BTimedEventQueue::B_SEEK:
591 HandleSeek(event,lateness,realTimeEvent);
592 break;
593 case BTimedEventQueue::B_WARP:
594 HandleWarp(event,lateness,realTimeEvent);
595 break;
596 case BTimedEventQueue::B_STOP:
597 HandleStop(event,lateness,realTimeEvent);
598 break;
599 case BTimedEventQueue::B_HANDLE_BUFFER:
600 // we don't get any buffers
601 break;
602 case SEND_NEW_BUFFER_EVENT:
603 if (RunState() == BMediaEventLooper::B_STARTED)
604 SendNewBuffer(event, lateness, realTimeEvent);
605 break;
606 case BTimedEventQueue::B_DATA_STATUS:
607 HandleDataStatus(event,lateness,realTimeEvent);
608 break;
609 case BTimedEventQueue::B_PARAMETER:
610 HandleParameter(event,lateness,realTimeEvent);
611 break;
612 default:
613 fprintf(stderr," unknown event type: %" B_PRId32 "\n", event->type);
614 break;
615 }
616 }
617
618
619 // #pragma mark - protected methods
620
621
622 // how should we handle late buffers? drop them?
623 // notify the producer?
624 status_t
SendNewBuffer(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)625 SoundPlayNode::SendNewBuffer(const media_timed_event* event,
626 bigtime_t lateness, bool realTimeEvent)
627 {
628 CALLED();
629 // TRACE("latency = %12Ld, event = %12Ld, sched = %5Ld, arrive at %12Ld, now %12Ld, current lateness %12Ld\n", EventLatency() + SchedulingLatency(), EventLatency(), SchedulingLatency(), event->event_time, TimeSource()->Now(), lateness);
630
631 // make sure we're both started *and* connected before delivering a buffer
632 if (RunState() != BMediaEventLooper::B_STARTED
633 || fOutput.destination == media_destination::null)
634 return B_OK;
635
636 // The event->event_time is the time at which the buffer we are preparing
637 // here should arrive at it's destination. The MediaEventLooper should have
638 // scheduled us early enough (based on EventLatency() and the
639 // SchedulingLatency()) to make this possible.
640 // lateness is independent of EventLatency()!
641
642 if (lateness > (BufferDuration() / 3) ) {
643 TRACE("SoundPlayNode::SendNewBuffer, event scheduled much too late, "
644 "lateness is %" B_PRId64 "\n", lateness);
645 }
646
647 // skip buffer creation if output not enabled
648 if (fOutputEnabled) {
649
650 // Get the next buffer of data
651 BBuffer* buffer = FillNextBuffer(event->event_time);
652
653 if (buffer) {
654
655 // If we are ready way too early, decrase internal latency
656 /*
657 bigtime_t how_early = event->event_time - TimeSource()->Now() - fLatency - fInternalLatency;
658 if (how_early > 5000) {
659
660 TRACE("SoundPlayNode::SendNewBuffer, event scheduled too early, how_early is %lld\n", how_early);
661
662 if (fTooEarlyCount++ == 5) {
663 fInternalLatency -= how_early;
664 if (fInternalLatency < 500)
665 fInternalLatency = 500;
666 TRACE("SoundPlayNode::SendNewBuffer setting internal latency to %lld\n", fInternalLatency);
667 SetEventLatency(fLatency + fInternalLatency);
668 fTooEarlyCount = 0;
669 }
670 }
671 */
672 // send the buffer downstream if and only if output is enabled
673 if (SendBuffer(buffer, fOutput.source, fOutput.destination)
674 != B_OK) {
675 // we need to recycle the buffer
676 // if the call to SendBuffer() fails
677 TRACE("SoundPlayNode::SendNewBuffer: Buffer sending "
678 "failed\n");
679 buffer->Recycle();
680 }
681 }
682 }
683
684 // track how much media we've delivered so far
685 size_t nFrames = fOutput.format.u.raw_audio.buffer_size
686 / ((fOutput.format.u.raw_audio.format
687 & media_raw_audio_format::B_AUDIO_SIZE_MASK)
688 * fOutput.format.u.raw_audio.channel_count);
689 fFramesSent += nFrames;
690
691 // The buffer is on its way; now schedule the next one to go
692 // nextEvent is the time at which the buffer should arrive at it's
693 // destination
694 bigtime_t nextEvent = fStartTime + bigtime_t((1000000LL * fFramesSent)
695 / (int32)fOutput.format.u.raw_audio.frame_rate);
696 media_timed_event nextBufferEvent(nextEvent, SEND_NEW_BUFFER_EVENT);
697 EventQueue()->AddEvent(nextBufferEvent);
698
699 return B_OK;
700 }
701
702
703 status_t
HandleDataStatus(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)704 SoundPlayNode::HandleDataStatus(const media_timed_event* event,
705 bigtime_t lateness, bool realTimeEvent)
706 {
707 TRACE("SoundPlayNode::HandleDataStatus status: %" B_PRId32 ", lateness: %"
708 B_PRId64 "\n", event->data, lateness);
709
710 switch (event->data) {
711 case B_DATA_NOT_AVAILABLE:
712 break;
713 case B_DATA_AVAILABLE:
714 break;
715 case B_PRODUCER_STOPPED:
716 break;
717 default:
718 break;
719 }
720 return B_OK;
721 }
722
723
724 status_t
HandleStart(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)725 SoundPlayNode::HandleStart(const media_timed_event* event, bigtime_t lateness,
726 bool realTimeEvent)
727 {
728 CALLED();
729 // don't do anything if we're already running
730 if (RunState() != B_STARTED) {
731 // We want to start sending buffers now, so we set up the buffer-sending
732 // bookkeeping and fire off the first "produce a buffer" event.
733
734 fFramesSent = 0;
735 fStartTime = event->event_time;
736 media_timed_event firstBufferEvent(event->event_time,
737 SEND_NEW_BUFFER_EVENT);
738
739 // Alternatively, we could call HandleEvent() directly with this event,
740 // to avoid a trip through the event queue, like this:
741 //
742 // this->HandleEvent(&firstBufferEvent, 0, false);
743 //
744 EventQueue()->AddEvent(firstBufferEvent);
745 }
746 return B_OK;
747 }
748
749
750 status_t
HandleSeek(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)751 SoundPlayNode::HandleSeek(const media_timed_event* event, bigtime_t lateness,
752 bool realTimeEvent)
753 {
754 CALLED();
755 TRACE("SoundPlayNode::HandleSeek(t=%" B_PRId64 ", d=%" B_PRId32 ", bd=%"
756 B_PRId64 ")\n", event->event_time, event->data, event->bigdata);
757 return B_OK;
758 }
759
760
761 status_t
HandleWarp(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)762 SoundPlayNode::HandleWarp(const media_timed_event* event, bigtime_t lateness,
763 bool realTimeEvent)
764 {
765 CALLED();
766 return B_OK;
767 }
768
769
770 status_t
HandleStop(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)771 SoundPlayNode::HandleStop(const media_timed_event* event, bigtime_t lateness,
772 bool realTimeEvent)
773 {
774 CALLED();
775 // flush the queue so downstreamers don't get any more
776 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
777 SEND_NEW_BUFFER_EVENT);
778
779 return B_OK;
780 }
781
782
783 status_t
HandleParameter(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)784 SoundPlayNode::HandleParameter(const media_timed_event* event,
785 bigtime_t lateness, bool realTimeEvent)
786 {
787 CALLED();
788 return B_OK;
789 }
790
791
792 status_t
AllocateBuffers()793 SoundPlayNode::AllocateBuffers()
794 {
795 CALLED();
796
797 // allocate enough buffers to span our downstream latency, plus one
798 size_t size = fOutput.format.u.raw_audio.buffer_size;
799 int32 count = int32(fLatency / BufferDuration() + 1 + 1);
800
801 TRACE("SoundPlayNode::AllocateBuffers: latency = %" B_PRId64 ", buffer "
802 "duration = %" B_PRId64 ", count %" B_PRId32 "\n", fLatency,
803 BufferDuration(), count);
804
805 if (count < 3)
806 count = 3;
807
808 TRACE("SoundPlayNode::AllocateBuffers: creating group of %" B_PRId32
809 " buffers, size = %" B_PRIuSIZE "\n", count, size);
810
811 fBufferGroup = new BBufferGroup(size, count);
812 if (fBufferGroup->InitCheck() != B_OK) {
813 ERROR("SoundPlayNode::AllocateBuffers: BufferGroup::InitCheck() "
814 "failed\n");
815 }
816
817 return fBufferGroup->InitCheck();
818 }
819
820
821 BBuffer*
FillNextBuffer(bigtime_t eventTime)822 SoundPlayNode::FillNextBuffer(bigtime_t eventTime)
823 {
824 CALLED();
825
826 // get a buffer from our buffer group
827 BBuffer* buffer = fBufferGroup->RequestBuffer(
828 fOutput.format.u.raw_audio.buffer_size, BufferDuration() / 2);
829
830 // If we fail to get a buffer (for example, if the request times out), we
831 // skip this buffer and go on to the next, to avoid locking up the control
832 // thread
833 if (buffer == NULL) {
834 ERROR("SoundPlayNode::FillNextBuffer: RequestBuffer failed\n");
835 return NULL;
836 }
837
838 if (fPlayer->HasData()) {
839 fPlayer->PlayBuffer(buffer->Data(),
840 fOutput.format.u.raw_audio.buffer_size, fOutput.format.u.raw_audio);
841 } else
842 memset(buffer->Data(), 0, fOutput.format.u.raw_audio.buffer_size);
843
844 // fill in the buffer header
845 media_header* header = buffer->Header();
846 header->type = B_MEDIA_RAW_AUDIO;
847 header->size_used = fOutput.format.u.raw_audio.buffer_size;
848 header->time_source = TimeSource()->ID();
849 header->start_time = eventTime;
850
851 return buffer;
852 }
853
854
855 } // namespace BPrivate
856