2 * Copyright 2002-2014 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Christopher ML Zumwalt May (zummy@users.sf.net)
10 /* A MediaKit producer node which mixes sound from the GameKit
11 and sends them to the audio mixer
15 #include "GameProducer.h"
21 #include <BufferGroup.h>
22 #include <ByteOrder.h>
24 #include <MediaDefs.h>
25 #include <TimeSource.h>
27 #include "GameSoundBuffer.h"
28 #include "GameSoundDevice.h"
29 #include "GSUtility.h"
41 GameProducer::GameProducer(GameSoundBuffer
* object
,
42 const gs_audio_format
* format
)
44 BMediaNode("GameProducer.h"),
45 BBufferProducer(B_MEDIA_RAW_AUDIO
),
52 // initialize our preferred format object
53 fPreferredFormat
.type
= B_MEDIA_RAW_AUDIO
;
54 fPreferredFormat
.u
.raw_audio
.format
= format
->format
;
55 fPreferredFormat
.u
.raw_audio
.channel_count
= format
->channel_count
;
56 fPreferredFormat
.u
.raw_audio
.frame_rate
= format
->frame_rate
; // Hertz
57 fPreferredFormat
.u
.raw_audio
.byte_order
= format
->byte_order
;
58 // fPreferredFormat.u.raw_audio.channel_mask
59 // = B_CHANNEL_LEFT | B_CHANNEL_RIGHT;
60 // fPreferredFormat.u.raw_audio.valid_bits = 32;
61 // fPreferredFormat.u.raw_audio.matrix_mask = B_MATRIX_AMBISONIC_WXYZ;
63 // we'll use the consumer's preferred buffer size, if any
64 fPreferredFormat
.u
.raw_audio
.buffer_size
65 = media_raw_audio_format::wildcard
.buffer_size
;
67 // we're not connected yet
68 fOutput
.destination
= media_destination::null
;
69 fOutput
.format
= fPreferredFormat
;
71 fFrameSize
= get_sample_size(format
->format
) * format
->channel_count
;
76 GameProducer::~GameProducer()
78 // Stop the BMediaEventLooper thread
85 GameProducer::AddOn(int32
* internal_id
) const
91 // BBufferProducer methods
93 GameProducer::GetNextOutput(int32
* cookie
, media_output
* _output
)
95 // we currently support only one output
106 GameProducer::DisposeOutputCookie(int32 cookie
)
108 // do nothing because our cookie is only an integer
114 GameProducer::EnableOutput(const media_source
& what
, bool enabled
,
117 // If I had more than one output, I'd have to walk my list of output records
118 // to see which one matched the given source, and then enable/disable that
119 // one. But this node only has one output, so I just make sure the given
120 // source matches, then set the enable state accordingly.
121 if (what
== fOutput
.source
)
123 fOutputEnabled
= enabled
;
129 GameProducer::FormatSuggestionRequested(media_type type
, int32
/*quality*/,
130 media_format
* format
)
132 // insure that we received a format
136 // returning our preferred format
137 *format
= fPreferredFormat
;
139 // our format is supported
140 if (type
== B_MEDIA_UNKNOWN_TYPE
)
143 // we only support raw audo
144 return (type
!= B_MEDIA_RAW_AUDIO
) ? B_MEDIA_BAD_FORMAT
: B_OK
;
149 GameProducer::FormatProposal(const media_source
& output
, media_format
* format
)
151 // doest the proposed output match our output?
152 if (output
!= fOutput
.source
)
153 return B_MEDIA_BAD_SOURCE
;
155 // return our preferred format
156 *format
= fPreferredFormat
;
158 // we will reject the proposal if the format is not audio
159 media_type requestedType
= format
->type
;
160 if ((requestedType
!= B_MEDIA_UNKNOWN_TYPE
)
161 && (requestedType
!= B_MEDIA_RAW_AUDIO
)) {
162 return B_MEDIA_BAD_FORMAT
;
165 return B_OK
; // raw audio or wildcard type, either is okay by us
170 GameProducer::PrepareToConnect(const media_source
& what
,
171 const media_destination
& where
, media_format
* format
,
172 media_source
* _source
, char* out_name
)
174 // The format has been processed by the consumer at this point. We need
175 // to insure the format is still acceptable and any wild care are filled in.
177 // trying to connect something that isn't our source?
178 if (what
!= fOutput
.source
)
179 return B_MEDIA_BAD_SOURCE
;
181 // are we already connected?
182 if (fOutput
.destination
!= media_destination::null
)
183 return B_MEDIA_ALREADY_CONNECTED
;
185 // the format may not yet be fully specialized (the consumer might have
186 // passed back some wildcards). Finish specializing it now, and return an
187 // error if we don't support the requested format.
188 if (format
->type
!= B_MEDIA_RAW_AUDIO
)
189 return B_MEDIA_BAD_FORMAT
;
191 if (format
->u
.raw_audio
.format
!= fPreferredFormat
.u
.raw_audio
.format
)
192 return B_MEDIA_BAD_FORMAT
;
194 // check the buffer size, which may still be wildcarded
195 if (format
->u
.raw_audio
.buffer_size
196 == media_raw_audio_format::wildcard
.buffer_size
) {
197 format
->u
.raw_audio
.buffer_size
= 4096;
198 // pick something comfortable to suggest
201 // Now reserve the connection, and return information about it
202 fOutput
.destination
= where
;
203 fOutput
.format
= *format
;
204 *_source
= fOutput
.source
;
205 strlcpy(out_name
, fOutput
.name
, B_MEDIA_NAME_LENGTH
);
211 GameProducer::Connect(status_t error
, const media_source
& source
,
212 const media_destination
& destination
, const media_format
& format
,
215 // If something earlier failed, Connect() might still be called, but with a
216 // non-zero error code. When that happens we simply unreserve the
217 // connection and do nothing else.
219 fOutput
.destination
= media_destination::null
;
220 fOutput
.format
= fPreferredFormat
;
224 // Okay, the connection has been confirmed. Record the destination and
225 // format that we agreed on, and report our connection name again.
226 fOutput
.destination
= destination
;
227 fOutput
.format
= format
;
228 strlcpy(ioName
, fOutput
.name
, B_MEDIA_NAME_LENGTH
);
230 // Now that we're connected, we can determine our downstream latency.
231 // Do so, then make sure we get our events early enough.
233 FindLatencyFor(fOutput
.destination
, &fLatency
, &id
);
236 fBufferSize
= fOutput
.format
.u
.raw_audio
.buffer_size
;
237 // Have to set it before latency calculating
239 // Use a dry run to see how long it takes me to fill a buffer of data
241 // The first step to setup the buffer
242 bigtime_t start
, produceLatency
;
243 int32 frames
= int32(fBufferSize
/ fFrameSize
);
244 float* data
= new float[frames
* 2];
246 // Second, fill the buffer
247 start
= ::system_time();
248 for (int32 i
= 0; i
< frames
; i
++) {
249 data
[i
* 2] = 0.8 * float(i
/ frames
);
250 data
[i
* 2 + 1] = 0.8 * float(i
/ frames
);
252 produceLatency
= ::system_time();
254 // Third, calculate the latency
255 fInternalLatency
= produceLatency
- start
;
256 SetEventLatency(fLatency
+ fInternalLatency
);
261 // reset our buffer duration, etc. to avoid later calculations
262 bigtime_t duration
= bigtime_t(1000000) * frames
263 / bigtime_t(fOutput
.format
.u
.raw_audio
.frame_rate
);
264 SetBufferDuration(duration
);
266 // Set up the buffer group for our connection, as long as nobody handed us a
267 // buffer group (via SetBufferGroup()) prior to this.
269 int32 count
= int32(fLatency
/ BufferDuration() + 2);
270 fBufferGroup
= new BBufferGroup(fBufferSize
, count
);
276 GameProducer::Disconnect(const media_source
& what
,
277 const media_destination
& where
)
279 // Make sure that our connection is the one being disconnected
280 if ((where
== fOutput
.destination
) && (what
== fOutput
.source
)) {
281 fOutput
.destination
= media_destination::null
;
282 fOutput
.format
= fPreferredFormat
;
290 GameProducer::FormatChangeRequested(const media_source
& source
,
291 const media_destination
& destination
, media_format
* io_format
,
294 // we don't support any other formats, so we just reject any format changes.
300 GameProducer::SetBufferGroup(const media_source
& forSource
,
301 BBufferGroup
* newGroup
)
303 // verify that we didn't get bogus arguments before we proceed
304 if (forSource
!= fOutput
.source
)
305 return B_MEDIA_BAD_SOURCE
;
307 // Are we being passed the buffer group we're already using?
308 if (newGroup
== fBufferGroup
)
311 // Ahh, someone wants us to use a different buffer group. At this point we
312 // delete the one we are using and use the specified one instead. If the
313 // specified group is NULL, we need to recreate one ourselves, and use
314 // *that*. Note that if we're caching a BBuffer that we requested earlier,
315 // we have to Recycle() that buffer *before* deleting the buffer group,
316 // otherwise we'll deadlock waiting for that buffer to be recycled!
317 delete fBufferGroup
; // waits for all buffers to recycle
318 if (newGroup
!= NULL
) {
319 // we were given a valid group; just use that one from now on
320 fBufferGroup
= newGroup
;
322 // get buffer length from the first buffer
324 if (newGroup
->GetBufferList(1, buffers
) != B_OK
)
326 fBufferSize
= buffers
[0]->SizeAvailable();
328 // we were passed a NULL group pointer; that means we construct
329 // our own buffer group to use from now on
330 fBufferSize
= fOutput
.format
.u
.raw_audio
.buffer_size
;
331 int32 count
= int32(fLatency
/ BufferDuration() + 2);
332 fBufferGroup
= new BBufferGroup(fBufferSize
, count
);
340 GameProducer::GetLatency(bigtime_t
* _latency
)
342 // report our *total* latency: internal plus downstream plus scheduling
343 *_latency
= EventLatency() + SchedulingLatency();
349 GameProducer::LateNoticeReceived(const media_source
& what
, bigtime_t howMuch
,
350 bigtime_t performanceDuration
)
352 // If we're late, we need to catch up. Respond in a manner appropriate to
353 // our current run mode.
354 if (what
== fOutput
.source
) {
355 if (RunMode() == B_RECORDING
) {
356 // A hardware capture node can't adjust; it simply emits buffers at
357 // appropriate points. We (partially) simulate this by not
358 // adjusting our behavior upon receiving late notices -- after all,
359 // the hardware can't choose to capture "sooner"...
360 } else if (RunMode() == B_INCREASE_LATENCY
) {
361 // We're late, and our run mode dictates that we try to produce
362 // buffers earlier in order to catch up. This argues that the
363 // downstream nodes are not properly reporting their latency, but
364 // there's not much we can do about that at the moment, so we try
365 // to start producing buffers earlier to compensate.
366 fInternalLatency
+= howMuch
;
367 SetEventLatency(fLatency
+ fInternalLatency
);
369 // The other run modes dictate various strategies for sacrificing
370 // data quality in the interests of timely data delivery. The way we
371 // do this is to skip a buffer, which catches us up in time by one
373 size_t nSamples
= fBufferSize
/ fFrameSize
;
374 fFramesSent
+= nSamples
;
381 GameProducer::LatencyChanged(const media_source
& source
,
382 const media_destination
& destination
, bigtime_t new_latency
, uint32 flags
)
384 // something downstream changed latency, so we need to start producing
385 // buffers earlier (or later) than we were previously. Make sure that the
386 // connection that changed is ours, and adjust to the new downstream
388 if ((source
== fOutput
.source
) && (destination
== fOutput
.destination
)) {
389 fLatency
= new_latency
;
390 SetEventLatency(fLatency
+ fInternalLatency
);
396 GameProducer::SetPlayRate(int32 numerator
, int32 denominator
)
398 // Play rates are weird. We don't support them
404 GameProducer::HandleMessage(int32 message
, const void* data
, size_t size
)
406 // We currently do not handle private messages
412 GameProducer::AdditionalBufferRequested(const media_source
& source
,
413 media_buffer_id prev_buffer
, bigtime_t prev_time
,
414 const media_seek_tag
* prev_tag
)
416 // we don't support offline mode (yet...)
421 // BMediaEventLooper methods
423 GameProducer::NodeRegistered()
425 // set up as much information about our output as we can
426 fOutput
.source
.port
= ControlPort();
427 fOutput
.source
.id
= 0;
428 fOutput
.node
= Node();
429 strlcpy(fOutput
.name
, "GameProducer Output", B_MEDIA_NAME_LENGTH
);
431 // Start the BMediaEventLooper thread
432 SetPriority(B_REAL_TIME_PRIORITY
);
438 GameProducer::SetRunMode(run_mode mode
)
440 // We don't support offline run mode, so broadcast an error if we're set to
441 // B_OFFLINE. Unfortunately, we can't actually reject the mode change...
442 if (B_OFFLINE
== mode
) {
443 ReportError(B_NODE_FAILED_SET_RUN_MODE
);
449 GameProducer::HandleEvent(const media_timed_event
* event
, bigtime_t lateness
,
452 // FPRINTF(stderr, "ToneProducer::HandleEvent\n");
455 case BTimedEventQueue::B_START
:
456 // don't do anything if we're already running
457 if (RunState() != B_STARTED
) {
458 // Going to start sending buffers so setup the needed bookkeeping
460 fStartTime
= event
->event_time
;
461 media_timed_event
firstBufferEvent(fStartTime
,
462 BTimedEventQueue::B_HANDLE_BUFFER
);
464 // Alternatively, we could call HandleEvent() directly with this
465 // event, to avoid a trip through the event queue like this:
466 // this->HandleEvent(&firstBufferEvent, 0, false);
467 EventQueue()->AddEvent(firstBufferEvent
);
471 case BTimedEventQueue::B_STOP
:
472 // When we handle a stop, we must ensure that downstream consumers don't
473 // get any more buffers from us. This means we have to flush any
474 // pending buffer-producing events from the queue.
475 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS
, true,
476 BTimedEventQueue::B_HANDLE_BUFFER
);
479 case BTimedEventQueue::B_HANDLE_BUFFER
:
481 // Ensure we're both started and connected before delivering buffer
482 if ((RunState() == BMediaEventLooper::B_STARTED
)
483 && (fOutput
.destination
!= media_destination::null
)) {
484 // Get the next buffer of data
485 BBuffer
* buffer
= FillNextBuffer(event
->event_time
);
487 // Send the buffer downstream if output is enabled
488 status_t err
= B_ERROR
;
489 if (fOutputEnabled
) {
490 err
= SendBuffer(buffer
, fOutput
.source
,
491 fOutput
.destination
);
494 // we need to recycle the buffer ourselves if output is
495 // disabled or if the call to SendBuffer() fails
500 // track how much media we've delivered so far
501 size_t nFrames
= fBufferSize
/ fFrameSize
;
502 fFramesSent
+= nFrames
;
504 // The buffer is on its way; now schedule the next one to go
505 bigtime_t nextEvent
= fStartTime
+ bigtime_t(double(fFramesSent
)
506 / double(fOutput
.format
.u
.raw_audio
.frame_rate
)
508 media_timed_event
nextBufferEvent(nextEvent
,
509 BTimedEventQueue::B_HANDLE_BUFFER
);
510 EventQueue()->AddEvent(nextBufferEvent
);
522 GameProducer::FillNextBuffer(bigtime_t event_time
)
524 // get a buffer from our buffer group
525 BBuffer
* buf
= fBufferGroup
->RequestBuffer(fBufferSize
, BufferDuration());
527 // if we fail to get a buffer (for example, if the request times out), we
528 // skip this buffer and go on to the next, to avoid locking up the control
533 // we need to discribe the buffer
534 int64 frames
= int64(fBufferSize
/ fFrameSize
);
535 memset(buf
->Data(), 0, fBufferSize
);
537 // now fill the buffer with data, continuing where the last buffer left off
538 fObject
->Play(buf
->Data(), frames
);
540 // fill in the buffer header
541 media_header
* hdr
= buf
->Header();
542 hdr
->type
= B_MEDIA_RAW_AUDIO
;
543 hdr
->size_used
= fBufferSize
;
544 hdr
->time_source
= TimeSource()->ID();
547 if (RunMode() == B_RECORDING
) {
548 // In B_RECORDING mode, we stamp with the capture time. We're not
549 // really a hardware capture node, but we simulate it by using the
550 // (precalculated) time at which this buffer "should" have been created.
553 // okay, we're in one of the "live" performance run modes. in these
554 // modes, we stamp the buffer with the time at which the buffer should
555 // be rendered to the output, not with the capture time. fStartTime is
556 // the cached value of the first buffer's performance time; we calculate
557 // this buffer's performance time as an offset from that time, based on
558 // the amount of media we've created so far.
559 // Recalculating every buffer like this avoids accumulation of error.
560 stamp
= fStartTime
+ bigtime_t(double(fFramesSent
)
561 / double(fOutput
.format
.u
.raw_audio
.frame_rate
) * 1000000.0);
563 hdr
->start_time
= stamp
;