xref: /haiku/src/kits/game/GameProducer.cpp (revision 9f66f05b58e3a033984f1271ac986a2c99226103)
1 /*
2  * Copyright 2002-2010 Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Christopher ML Zumwalt May (zummy@users.sf.net)
7  */
8 
9 
10 /*!	A MediaKit producer node which mixes sound from the GameKit
11 	and sends them to the audio mixer
12 */
13 
14 
15 #include <string.h>
16 #include <stdio.h>
17 
18 #include <Buffer.h>
19 #include <BufferGroup.h>
20 #include <ByteOrder.h>
21 #include <List.h>
22 #include <MediaDefs.h>
23 #include <TimeSource.h>
24 
25 #include "GameSoundBuffer.h"
26 #include "GameSoundDevice.h"
27 #include "GSUtility.h"
28 
29 #include "GameProducer.h"
30 
31 
32 struct _gs_play  {
33 	gs_id		sound;
34 	bool*		hook;
35 
36 	_gs_play*	next;
37 	_gs_play*	previous;
38 };
39 
40 
41 GameProducer::GameProducer(GameSoundBuffer* object,
42 		const gs_audio_format* format)
43 	:
44 	BMediaNode("GameProducer.h"),
45 	BBufferProducer(B_MEDIA_RAW_AUDIO),
46 	BMediaEventLooper(),
47 	fBufferGroup(NULL),
48 	fLatency(0),
49 	fInternalLatency(0),
50 	fOutputEnabled(true)
51 {
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;		// measured in Hertz
57 	fPreferredFormat.u.raw_audio.byte_order = format->byte_order;
58 //	fPreferredFormat.u.raw_audio.channel_mask = B_CHANNEL_LEFT | B_CHANNEL_RIGHT;
59 //	fPreferredFormat.u.raw_audio.valid_bits = 32;
60 //	fPreferredFormat.u.raw_audio.matrix_mask = B_MATRIX_AMBISONIC_WXYZ;
61 
62 	// we'll use the consumer's preferred buffer size, if any
63 	fPreferredFormat.u.raw_audio.buffer_size = media_raw_audio_format::wildcard.buffer_size;
64 
65 	// we're not connected yet
66 	fOutput.destination = media_destination::null;
67 	fOutput.format = fPreferredFormat;
68 
69 	fFrameSize = get_sample_size(format->format) * format->channel_count;
70 	fObject = object;
71 }
72 
73 
74 GameProducer::~GameProducer()
75 {
76 	// Stop the BMediaEventLooper thread
77 	Quit();
78 }
79 
80 
81 // BMediaNode methods
82 BMediaAddOn *
83 GameProducer::AddOn(int32 *internal_id) const
84 {
85 	return NULL;
86 }
87 
88 
89 // BBufferProducer methods
90 status_t
91 GameProducer::GetNextOutput(int32* cookie, media_output* out_output)
92 {
93 	// we currently support only one output
94 	if (0 != *cookie) return B_BAD_INDEX;
95 
96 	*out_output = fOutput;
97 	*cookie += 1;
98 	return B_OK;
99 }
100 
101 
102 status_t
103 GameProducer::DisposeOutputCookie(int32 cookie)
104 {
105 	// do nothing because our cookie is only an integer
106 	return B_OK;
107 }
108 
109 
110 void
111 GameProducer::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_)
112 {
113 	// If I had more than one output, I'd have to walk my list of output records to see
114 	// which one matched the given source, and then enable/disable that one.  But this
115 	// node only has one output, so I just make sure the given source matches, then set
116 	// the enable state accordingly.
117 	if (what == fOutput.source)
118 	{
119 		fOutputEnabled = enabled;
120 	}
121 }
122 
123 
124 status_t
125 GameProducer::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format)
126 {
127 	// insure that we received a format
128 	if (!format)
129 		return B_BAD_VALUE;
130 
131 	// returning our preferred format
132 	*format = fPreferredFormat;
133 
134 	// our format is supported
135 	if (type == B_MEDIA_UNKNOWN_TYPE) return B_OK;
136 
137 	// we only support raw audo
138 	return (type != B_MEDIA_RAW_AUDIO) ? B_MEDIA_BAD_FORMAT : B_OK;
139 }
140 
141 
142 status_t
143 GameProducer::FormatProposal(const media_source& output, media_format* format)
144 {
145 	// doest the proposed output match our output?
146 	if (output != fOutput.source) return B_MEDIA_BAD_SOURCE;
147 
148 	// return our preferred format
149 	*format = fPreferredFormat;
150 
151 	// we will reject the proposal if the format is not audio
152 	media_type requestedType = format->type;
153 	if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO))
154 		return B_MEDIA_BAD_FORMAT;
155 
156 	return B_OK;		// raw audio or wildcard type, either is okay by us
157 }
158 
159 
160 status_t
161 GameProducer::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name)
162 {
163 	// The format has been processed by the consumer at this point. We need
164 	// to insure the format is still acceptable and any wild care are filled in.
165 
166 	// trying to connect something that isn't our source?
167 	if (what != fOutput.source) return B_MEDIA_BAD_SOURCE;
168 
169 	// are we already connected?
170 	if (fOutput.destination != media_destination::null) return B_MEDIA_ALREADY_CONNECTED;
171 
172 	// the format may not yet be fully specialized (the consumer might have
173 	// passed back some wildcards).  Finish specializing it now, and return an
174 	// error if we don't support the requested format.
175 	if (format->type != B_MEDIA_RAW_AUDIO)
176 		return B_MEDIA_BAD_FORMAT;
177 
178 	if (format->u.raw_audio.format != fPreferredFormat.u.raw_audio.format)
179 		return B_MEDIA_BAD_FORMAT;
180 
181 	// check the buffer size, which may still be wildcarded
182 	if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size)
183 		format->u.raw_audio.buffer_size = 4096;		// pick something comfortable to suggest
184 
185 	// Now reserve the connection, and return information about it
186 	fOutput.destination = where;
187 	fOutput.format = *format;
188 	*out_source = fOutput.source;
189 	strlcpy(out_name, fOutput.name, B_MEDIA_NAME_LENGTH);
190 	return B_OK;
191 }
192 
193 
194 void
195 GameProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name)
196 {
197 	// If something earlier failed, Connect() might still be called, but with a non-zero
198 	// error code.  When that happens we simply unreserve the connection and do
199 	// nothing else.
200 	if (error) {
201 		fOutput.destination = media_destination::null;
202 		fOutput.format = fPreferredFormat;
203 		return;
204 	}
205 
206 	// Okay, the connection has been confirmed.  Record the destination and format
207 	// that we agreed on, and report our connection name again.
208 	fOutput.destination = destination;
209 	fOutput.format = format;
210 	strlcpy(io_name, fOutput.name, B_MEDIA_NAME_LENGTH);
211 
212 	// Now that we're connected, we can determine our downstream latency.
213 	// Do so, then make sure we get our events early enough.
214 	media_node_id id;
215 	FindLatencyFor(fOutput.destination, &fLatency, &id);
216 
217 	if (!fBufferGroup)
218 		fBufferSize = fOutput.format.u.raw_audio.buffer_size;
219 			// Have to set it before latency calculating
220 
221 	// Use a dry run to see how long it takes me to fill a buffer of data
222 
223 	// The first step to setup the buffer
224 	bigtime_t start, produceLatency;
225 	int32 frames = int32(fBufferSize / fFrameSize);
226 	float* data = new float[frames * 2];
227 
228 	// Second, fill the buffer
229 	start = ::system_time();
230 	for (int32 i = 0; i < frames; i++) {
231 		data[i*2] = 0.8 * float(i/frames);
232 		data[i*2+1] = 0.8 * float(i/frames);
233 	}
234 	produceLatency = ::system_time();
235 
236 	// Third, calculate the latency
237 	fInternalLatency = produceLatency - start;
238 	SetEventLatency(fLatency + fInternalLatency);
239 
240 	// Finaily, clean up
241 	delete [] data;
242 
243 	// reset our buffer duration, etc. to avoid later calculations
244 	bigtime_t duration = bigtime_t(1000000) * frames / bigtime_t(fOutput.format.u.raw_audio.frame_rate);
245 	SetBufferDuration(duration);
246 
247 	// Set up the buffer group for our connection, as long as nobody handed us a
248 	// buffer group (via SetBufferGroup()) prior to this.
249 	if (!fBufferGroup) {
250 		int32 count = int32(fLatency / BufferDuration() + 2);
251 		fBufferGroup = new BBufferGroup(fBufferSize, count);
252 	}
253 }
254 
255 
256 void
257 GameProducer::Disconnect(const media_source& what, const media_destination& where)
258 {
259 	// Make sure that our connection is the one being disconnected
260 	if ((where == fOutput.destination) && (what == fOutput.source)) {
261 		fOutput.destination = media_destination::null;
262 		fOutput.format = fPreferredFormat;
263 		delete fBufferGroup;
264 		fBufferGroup = NULL;
265 	}
266 }
267 
268 
269 status_t
270 GameProducer::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_)
271 {
272 	// we don't support any other formats, so we just reject any format changes.
273 	return B_ERROR;
274 }
275 
276 
277 status_t
278 GameProducer::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup)
279 {
280 	// verify that we didn't get bogus arguments before we proceed
281 	if (for_source != fOutput.source)
282 		return B_MEDIA_BAD_SOURCE;
283 
284 	// Are we being passed the buffer group we're already using?
285 	if (newGroup == fBufferGroup)
286 		return B_OK;
287 
288 	// Ahh, someone wants us to use a different buffer group.  At this point we delete
289 	// the one we are using and use the specified one instead.  If the specified group is
290 	// NULL, we need to recreate one ourselves, and use *that*.  Note that if we're
291 	// caching a BBuffer that we requested earlier, we have to Recycle() that buffer
292 	// *before* deleting the buffer group, otherwise we'll deadlock waiting for that
293 	// buffer to be recycled!
294 	delete fBufferGroup;		// waits for all buffers to recycle
295 	if (newGroup != NULL) {
296 		// we were given a valid group; just use that one from now on
297 		fBufferGroup = newGroup;
298 
299 		// get buffer length from the first buffer
300 		BBuffer *buffers[1];
301 		if (newGroup->GetBufferList(1, buffers) != B_OK)
302 			return B_BAD_VALUE;
303 		fBufferSize = buffers[0]->SizeAvailable();
304 	} else {
305 		// we were passed a NULL group pointer; that means we construct
306 		// our own buffer group to use from now on
307 		fBufferSize = fOutput.format.u.raw_audio.buffer_size;
308 		int32 count = int32(fLatency / BufferDuration() + 2);
309 		fBufferGroup = new BBufferGroup(fBufferSize, count);
310 	}
311 
312 	return B_OK;
313 }
314 
315 
316 status_t
317 GameProducer::GetLatency(bigtime_t* out_latency)
318 {
319 	// report our *total* latency:  internal plus downstream plus scheduling
320 	*out_latency = EventLatency() + SchedulingLatency();
321 	return B_OK;
322 }
323 
324 
325 void
326 GameProducer::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time)
327 {
328 	// If we're late, we need to catch up.  Respond in a manner appropriate to our
329 	// current run mode.
330 	if (what == fOutput.source) {
331 		if (RunMode() == B_RECORDING) {
332 			// A hardware capture node can't adjust; it simply emits buffers at
333 			// appropriate points.  We (partially) simulate this by not adjusting
334 			// our behavior upon receiving late notices -- after all, the hardware
335 			// can't choose to capture "sooner"....
336 		} else if (RunMode() == B_INCREASE_LATENCY) {
337 			// We're late, and our run mode dictates that we try to produce buffers
338 			// earlier in order to catch up.  This argues that the downstream nodes are
339 			// not properly reporting their latency, but there's not much we can do about
340 			// that at the moment, so we try to start producing buffers earlier to
341 			// compensate.
342 			fInternalLatency += how_much;
343 			SetEventLatency(fLatency + fInternalLatency);
344 		} else {
345 			// The other run modes dictate various strategies for sacrificing data quality
346 			// in the interests of timely data delivery.  The way *we* do this is to skip
347 			// a buffer, which catches us up in time by one buffer duration.
348 			size_t nSamples = fBufferSize / fFrameSize;
349 			fFramesSent += nSamples;
350 		}
351 	}
352 }
353 
354 
355 void
356 GameProducer::LatencyChanged(const media_source& source, const media_destination& destination, bigtime_t new_latency, uint32 flags)
357 {
358 	// something downstream changed latency, so we need to start producing
359 	// buffers earlier (or later) than we were previously.  Make sure that the
360 	// connection that changed is ours, and adjust to the new downstream
361 	// latency if so.
362 	if ((source == fOutput.source) && (destination == fOutput.destination)) {
363 		fLatency = new_latency;
364 		SetEventLatency(fLatency + fInternalLatency);
365 	}
366 }
367 
368 
369 status_t
370 GameProducer::SetPlayRate(int32 numer, int32 denom)
371 {
372 	// Play rates are weird.  We don't support them
373 	return B_ERROR;
374 }
375 
376 
377 status_t
378 GameProducer::HandleMessage(int32 message, const void* data, size_t size)
379 {
380 	// We currently do not handle private messages
381 	return B_ERROR;
382 }
383 
384 
385 void
386 GameProducer::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag)
387 {
388 	// we don't support offline mode (yet...)
389 	return;
390 }
391 
392 
393 // BMediaEventLooper methods
394 void
395 GameProducer::NodeRegistered()
396 {
397 	// Start the BMediaEventLooper thread
398 	SetPriority(B_REAL_TIME_PRIORITY);
399 	Run();
400 
401 	// set up as much information about our output as we can
402 	fOutput.source.port = ControlPort();
403 	fOutput.source.id = 0;
404 	fOutput.node = Node();
405 	strlcpy(fOutput.name, "GameProducer Output", B_MEDIA_NAME_LENGTH);
406 }
407 
408 
409 void
410 GameProducer::SetRunMode(run_mode mode)
411 {
412 	// We don't support offline run mode, so broadcast an error if we're set to
413 	// B_OFFLINE.  Unfortunately, we can't actually reject the mode change...
414 	if (B_OFFLINE == mode) {
415 		ReportError(B_NODE_FAILED_SET_RUN_MODE);
416 	}
417 }
418 
419 
420 void
421 GameProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent)
422 {
423 //	FPRINTF(stderr, "ToneProducer::HandleEvent\n");
424 	switch (event->type)
425 	{
426 	case BTimedEventQueue::B_START:
427 		// don't do anything if we're already running
428 		if (RunState() != B_STARTED) {
429 			// We are going to start sending buffers so setup the needed bookkeeping
430 			fFramesSent = 0;
431 			fStartTime = event->event_time;
432 			media_timed_event firstBufferEvent(fStartTime, BTimedEventQueue::B_HANDLE_BUFFER);
433 
434 			// Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through
435 			// the event queue, like this:
436 			//
437 			//		this->HandleEvent(&firstBufferEvent, 0, false);
438 			//
439 			EventQueue()->AddEvent(firstBufferEvent);
440 		}
441 		break;
442 
443 	case BTimedEventQueue::B_STOP:
444 		// When we handle a stop, we must ensure that downstream consumers don't
445 		// get any more buffers from us.  This means we have to flush any pending
446 		// buffer-producing events from the queue.
447 		EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
448 		break;
449 
450 	case BTimedEventQueue::B_HANDLE_BUFFER:
451 		{
452 			// make sure we're both started *and* connected before delivering a buffer
453 			if ((RunState() == BMediaEventLooper::B_STARTED)
454 				&& (fOutput.destination != media_destination::null)) {
455 				// Get the next buffer of data
456 				BBuffer* buffer = FillNextBuffer(event->event_time);
457 				if (buffer) {
458 					// send the buffer downstream if and only if output is enabled
459 					status_t err = B_ERROR;
460 					if (fOutputEnabled) {
461 						err = SendBuffer(buffer, fOutput.source,
462 							fOutput.destination);
463 					}
464 					if (err) {
465 						// we need to recycle the buffer ourselves if output is disabled or
466 						// if the call to SendBuffer() fails
467 						buffer->Recycle();
468 					}
469 				}
470 
471 				// track how much media we've delivered so far
472 				size_t nFrames = fBufferSize / fFrameSize;
473 				fFramesSent += nFrames;
474 
475 				// The buffer is on its way; now schedule the next one to go
476 				bigtime_t nextEvent = fStartTime + bigtime_t(double(fFramesSent) / double(fOutput.format.u.raw_audio.frame_rate) * 1000000.0);
477 				media_timed_event nextBufferEvent(nextEvent, BTimedEventQueue::B_HANDLE_BUFFER);
478 				EventQueue()->AddEvent(nextBufferEvent);
479 			}
480 		}
481 		break;
482 
483 	default:
484 		break;
485 	}
486 }
487 
488 
489 BBuffer*
490 GameProducer::FillNextBuffer(bigtime_t event_time)
491 {
492 	// get a buffer from our buffer group
493 	BBuffer* buf = fBufferGroup->RequestBuffer(fBufferSize, BufferDuration());
494 
495 	// if we fail to get a buffer (for example, if the request times out), we skip this
496 	// buffer and go on to the next, to avoid locking up the control thread
497 	if (!buf)
498 		return NULL;
499 
500 	// we need to discribe the buffer
501 	int64 frames = int64(fBufferSize / fFrameSize);
502 	memset(buf->Data(), 0, fBufferSize);
503 
504 	// now fill the buffer with data, continuing where the last buffer left off
505 	fObject->Play(buf->Data(), frames);
506 
507 	// fill in the buffer header
508 	media_header* hdr = buf->Header();
509 	hdr->type = B_MEDIA_RAW_AUDIO;
510 	hdr->size_used = fBufferSize;
511 	hdr->time_source = TimeSource()->ID();
512 
513 	bigtime_t stamp;
514 	if (RunMode() == B_RECORDING) {
515 		// In B_RECORDING mode, we stamp with the capture time.  We're not
516 		// really a hardware capture node, but we simulate it by using the (precalculated)
517 		// time at which this buffer "should" have been created.
518 		stamp = event_time;
519 	} else {
520 		// okay, we're in one of the "live" performance run modes.  in these modes, we
521 		// stamp the buffer with the time at which the buffer should be rendered to the
522 		// output, not with the capture time.  fStartTime is the cached value of the
523 		// first buffer's performance time; we calculate this buffer's performance time as
524 		// an offset from that time, based on the amount of media we've created so far.
525 		// Recalculating every buffer like this avoids accumulation of error.
526 		stamp = fStartTime + bigtime_t(double(fFramesSent) / double(fOutput.format.u.raw_audio.frame_rate) * 1000000.0);
527 	}
528 	hdr->start_time = stamp;
529 
530 	return buf;
531 }
532 
533