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