xref: /haiku/src/add-ons/media/media-add-ons/tone_producer_demo/ToneProducer.cpp (revision 040a81419dda83d1014e9dc94936a4cb3f027303)
1 /*
2 	ToneProducer.cpp
3 
4 	Copyright 1999, Be Incorporated.   All Rights Reserved.
5 	This file may be used under the terms of the Be Sample Code License.
6 
7 	NOTE:  to compile this code under Genki beta releases, do a search-
8 	and-replace to change "B_PARAMETER" to "B_USER_EVENT+1"
9 */
10 
11 #include "ToneProducer.h"
12 #include <support/ByteOrder.h>
13 #include <media/BufferGroup.h>
14 #include <media/Buffer.h>
15 #include <media/TimeSource.h>
16 #include <media/ParameterWeb.h>
17 #include <media/MediaDefs.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <math.h>
21 
22 #include <Messenger.h>
23 
24 #include <Debug.h>
25 #if DEBUG
26 	#define FPRINTF fprintf
27 #else
28 	static inline void FPRINTF(FILE*, const char*, ...) { }
29 #endif
30 
31 // parameter web handling
32 static BParameterWeb* make_parameter_web();
33 const int32 FREQUENCY_NULL_PARAM = 1;
34 const int32 FREQUENCY_PARAM = 2;
35 const int32 GAIN_NULL_PARAM = 11;
36 const int32 GAIN_PARAM = 12;
37 const int32 WAVEFORM_NULL_PARAM = 21;
38 const int32  WAVEFORM_PARAM = 22;
39 const int32 SINE_WAVE = 90;
40 const int32 TRIANGLE_WAVE = 91;
41 const int32 SAWTOOTH_WAVE = 92;
42 
43 // ----------------
44 // ToneProducer implementation
45 
46 ToneProducer::ToneProducer(BMediaAddOn* pAddOn)
47 	:	BMediaNode("ToneProducer"),
48 		BBufferProducer(B_MEDIA_RAW_AUDIO),
49 		BControllable(),
50 		BMediaEventLooper(),
51 		mWeb(NULL),
52 		mBufferGroup(NULL),
53 		mLatency(0),
54 		mInternalLatency(0),
55 		mOutputEnabled(true),
56 		mTheta(0.0),
57 		mWaveAscending(true),
58 		mFrequency(440),
59 		mGain(0.25),
60 		mWaveform(SINE_WAVE),
61 		mFramesSent(0),
62 		mStartTime(0),
63 		mGainLastChanged(0),
64 		mFreqLastChanged(0),
65 		mWaveLastChanged(0),
66 		m_pAddOn(pAddOn)
67 {
68 	// initialize our preferred format object
69 	mPreferredFormat.type = B_MEDIA_RAW_AUDIO;
70 	mPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
71 	mPreferredFormat.u.raw_audio.frame_rate = 44100;		// measured in Hertz
72 	mPreferredFormat.u.raw_audio.byte_order = (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
73 
74 	// we'll use the consumer's preferred buffer size, if any
75 	mPreferredFormat.u.raw_audio.buffer_size = media_raw_audio_format::wildcard.buffer_size;
76 
77 	// 20sep99: multiple-channel support
78 	mPreferredFormat.u.raw_audio.channel_count = media_raw_audio_format::wildcard.channel_count;
79 
80 
81 	// we're not connected yet
82 	mOutput.destination = media_destination::null;
83 
84 	// [e.moon 1dec99]
85 	mOutput.format = mPreferredFormat;
86 
87 	// set up as much information about our output as we can
88 	// +++++ wrong; can't call Node() until the node is registered!
89 	mOutput.source.port = ControlPort();
90 	mOutput.source.id = 0;
91 	mOutput.node = Node();
92 	::strcpy(mOutput.name, "ToneProducer Output");
93 }
94 
95 ToneProducer::~ToneProducer()
96 {
97 	// Stop the BMediaEventLooper thread
98 	Quit();
99 
100 	// the BControllable destructor deletes our parameter web for us; we just use
101 	// a little defensive programming here and set our web pointer to be NULL.
102 	mWeb = NULL;
103 }
104 
105 //#pragma mark -
106 
107 // BMediaNode methods
108 BMediaAddOn *
109 ToneProducer::AddOn(int32 *internal_id) const
110 {
111 	// e.moon [8jun99]
112 	if(m_pAddOn) {
113 		*internal_id = 0;
114 		return m_pAddOn;
115 	} else
116 		return NULL;
117 }
118 
119 //#pragma mark -
120 
121 // BControllable methods
122 status_t
123 ToneProducer::GetParameterValue(int32 id, bigtime_t* last_change, void* value, size_t* ioSize)
124 {
125 	FPRINTF(stderr, "ToneProducer::GetParameterValue\n");
126 
127 	// floats & int32s are the same size, so this one test of the size of the
128 	// output buffer is sufficient for all of our parameters
129 	if (*ioSize < sizeof(float)) return B_ERROR;
130 
131 	// fill in the value of the requested parameter
132 	switch (id)
133 	{
134 	case FREQUENCY_PARAM:
135 		*last_change = mFreqLastChanged;
136 		*((float*) value) = mFrequency;
137 		*ioSize = sizeof(float);
138 		break;
139 
140 	case GAIN_PARAM:
141 		*last_change = mGainLastChanged;
142 		*((float*) value) = mGain;
143 		*ioSize = sizeof(float);
144 		break;
145 
146 	case WAVEFORM_PARAM:
147 		*last_change = mWaveLastChanged;
148 		*((int32*) value) = mWaveform;
149 		*ioSize = sizeof(int32);
150 		break;
151 
152 	default:
153 		// Hmmm, we were asked for a parameter that we don't actually
154 		// support.  Report an error back to the caller.
155 		FPRINTF(stderr, "\terror - asked for illegal parameter %ld\n", id);
156 		return B_ERROR;
157 		break;
158 	}
159 
160 	return B_OK;
161 }
162 
163 void
164 ToneProducer::SetParameterValue(int32 id, bigtime_t performance_time, const void* value, size_t size)
165 {
166 	switch (id)
167 	{
168 	case FREQUENCY_PARAM:
169 	case GAIN_PARAM:
170 	case WAVEFORM_PARAM:
171 		{
172 			// floats and int32s are the same size, so we need only check the block's size once
173 			if (size > sizeof(float)) size = sizeof(float);
174 
175 			// submit the parameter change as a performance event, to be handled at the
176 			// appropriate time
177 			media_timed_event event(performance_time, _PARAMETER_EVENT,
178 				NULL, BTimedEventQueue::B_NO_CLEANUP, size, id, (char*) value, size);
179 			EventQueue()->AddEvent(event);
180 		}
181 		break;
182 
183 	default:
184 		break;
185 	}
186 }
187 
188 // e.moon [17jun99]
189 status_t ToneProducer::StartControlPanel(
190 	BMessenger* pMessenger) {
191 	PRINT(("ToneProducer::StartControlPanel(%p)\n", pMessenger));
192 	status_t err = BControllable::StartControlPanel(pMessenger);
193 	if(pMessenger && pMessenger->IsValid()) {
194 		PRINT(("\tgot valid control panel\n"));
195 	}
196 
197 	return err;
198 }
199 
200 //#pragma mark -
201 
202 // BBufferProducer methods
203 status_t
204 ToneProducer::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format)
205 {
206 	// FormatSuggestionRequested() is not necessarily part of the format negotiation
207 	// process; it's simply an interrogation -- the caller wants to see what the node's
208 	// preferred data format is, given a suggestion by the caller.
209 	FPRINTF(stderr, "ToneProducer::FormatSuggestionRequested\n");
210 
211 	if (!format)
212 	{
213 		FPRINTF(stderr, "\tERROR - NULL format pointer passed in!\n");
214 		return B_BAD_VALUE;
215 	}
216 
217 	// this is the format we'll be returning (our preferred format)
218 	*format = mPreferredFormat;
219 
220 	// a wildcard type is okay; we can specialize it
221 	if (type == B_MEDIA_UNKNOWN_TYPE) type = B_MEDIA_RAW_AUDIO;
222 
223 	// we only support raw audio
224 	if (type != B_MEDIA_RAW_AUDIO) return B_MEDIA_BAD_FORMAT;
225 	else return B_OK;
226 }
227 
228 status_t
229 ToneProducer::FormatProposal(const media_source& output, media_format* format)
230 {
231 	// FormatProposal() is the first stage in the BMediaRoster::Connect() process.  We hand
232 	// out a suggested format, with wildcards for any variations we support.
233 	FPRINTF(stderr, "ToneProducer::FormatProposal\n");
234 
235 	// is this a proposal for our one output?
236 	if (output != mOutput.source)
237 	{
238 		FPRINTF(stderr, "ToneProducer::FormatProposal returning B_MEDIA_BAD_SOURCE\n");
239 		return B_MEDIA_BAD_SOURCE;
240 	}
241 
242 	// we only support floating-point raw audio, so we always return that, but we
243 	// supply an error code depending on whether we found the proposal acceptable.
244 
245 	media_type requestedType = format->type;
246 	*format = mPreferredFormat;
247 	if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO))
248 	{
249 		FPRINTF(stderr, "ToneProducer::FormatProposal returning B_MEDIA_BAD_FORMAT\n");
250 		return B_MEDIA_BAD_FORMAT;
251 	}
252 	else return B_OK;		// raw audio or wildcard type, either is okay by us
253 }
254 
255 status_t
256 ToneProducer::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_)
257 {
258 	FPRINTF(stderr, "ToneProducer::FormatChangeRequested\n");
259 
260 	// we don't support any other formats, so we just reject any format changes.
261 	return B_ERROR;
262 }
263 
264 status_t
265 ToneProducer::GetNextOutput(int32* cookie, media_output* out_output)
266 {
267 	FPRINTF(stderr, "ToneProducer::GetNextOutput\n");
268 
269 	// we have only a single output; if we supported multiple outputs, we'd
270 	// iterate over whatever data structure we were using to keep track of
271 	// them.
272 	if (0 == *cookie)
273 	{
274 		*out_output = mOutput;
275 		*cookie += 1;
276 		return B_OK;
277 	}
278 	else return B_BAD_INDEX;
279 }
280 
281 status_t
282 ToneProducer::DisposeOutputCookie(int32 cookie)
283 {
284 	FPRINTF(stderr, "ToneProducer::DisposeOutputCookie\n");
285 
286 	// do nothing because we don't use the cookie for anything special
287 	return B_OK;
288 }
289 
290 status_t
291 ToneProducer::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup)
292 {
293 	FPRINTF(stderr, "ToneProducer::SetBufferGroup\n");
294 
295 	// verify that we didn't get bogus arguments before we proceed
296 	if (for_source != mOutput.source) return B_MEDIA_BAD_SOURCE;
297 
298 	// Are we being passed the buffer group we're already using?
299 	if (newGroup == mBufferGroup) return B_OK;
300 
301 	// Ahh, someone wants us to use a different buffer group.  At this point we delete
302 	// the one we are using and use the specified one instead.  If the specified group is
303 	// NULL, we need to recreate one ourselves, and use *that*.  Note that if we're
304 	// caching a BBuffer that we requested earlier, we have to Recycle() that buffer
305 	// *before* deleting the buffer group, otherwise we'll deadlock waiting for that
306 	// buffer to be recycled!
307 	delete mBufferGroup;		// waits for all buffers to recycle
308 	if (newGroup != NULL)
309 	{
310 		// we were given a valid group; just use that one from now on
311 		mBufferGroup = newGroup;
312 	}
313 	else
314 	{
315 		// we were passed a NULL group pointer; that means we construct
316 		// our own buffer group to use from now on
317 		size_t size = mOutput.format.u.raw_audio.buffer_size;
318 		int32 count = int32(mLatency / BufferDuration() + 1 + 1);
319 		mBufferGroup = new BBufferGroup(size, count);
320 	}
321 
322 	return B_OK;
323 }
324 
325 status_t
326 ToneProducer::GetLatency(bigtime_t* out_latency)
327 {
328 	FPRINTF(stderr, "ToneProducer::GetLatency\n");
329 
330 	// report our *total* latency:  internal plus downstream plus scheduling
331 	*out_latency = EventLatency() + SchedulingLatency();
332 	return B_OK;
333 }
334 
335 status_t
336 ToneProducer::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name)
337 {
338 	// PrepareToConnect() is the second stage of format negotiations that happens
339 	// inside BMediaRoster::Connect().  At this point, the consumer's AcceptFormat()
340 	// method has been called, and that node has potentially changed the proposed
341 	// format.  It may also have left wildcards in the format.  PrepareToConnect()
342 	// *must* fully specialize the format before returning!
343 	FPRINTF(stderr, "ToneProducer::PrepareToConnect\n");
344 
345 	// trying to connect something that isn't our source?
346 	if (what != mOutput.source) return B_MEDIA_BAD_SOURCE;
347 
348 	// are we already connected?
349 	if (mOutput.destination != media_destination::null) return B_MEDIA_ALREADY_CONNECTED;
350 
351 	// the format may not yet be fully specialized (the consumer might have
352 	// passed back some wildcards).  Finish specializing it now, and return an
353 	// error if we don't support the requested format.
354 	if (format->type != B_MEDIA_RAW_AUDIO)
355 	{
356 		FPRINTF(stderr, "\tnon-raw-audio format?!\n");
357 		return B_MEDIA_BAD_FORMAT;
358 	}
359 	else if (format->u.raw_audio.format != media_raw_audio_format::B_AUDIO_FLOAT)
360 	{
361 		FPRINTF(stderr, "\tnon-float-audio format?!\n");
362 		return B_MEDIA_BAD_FORMAT;
363 	}
364 	else if(format->u.raw_audio.channel_count > 2) {
365 		format->u.raw_audio.channel_count = 2;
366 		return B_MEDIA_BAD_FORMAT;
367 	}
368 
369 
370 	 // !!! validate all other fields except for buffer_size here, because the consumer might have
371 	// supplied different values from AcceptFormat()?
372 
373 	// ***   e.moon [11jun99]: filling in sensible field values.
374 	//       Connect() doesn't take kindly to a frame_rate of 0.
375 
376 	if(format->u.raw_audio.frame_rate == media_raw_audio_format::wildcard.frame_rate) {
377 		format->u.raw_audio.frame_rate = mPreferredFormat.u.raw_audio.frame_rate;
378 		FPRINTF(stderr, "\tno frame rate provided, suggesting %.1f\n", format->u.raw_audio.frame_rate);
379 	}
380 	if(format->u.raw_audio.channel_count == media_raw_audio_format::wildcard.channel_count) {
381 		//format->u.raw_audio.channel_count = mPreferredFormat.u.raw_audio.channel_count;
382 		format->u.raw_audio.channel_count = 1;
383 		FPRINTF(stderr, "\tno channel count provided, suggesting %lu\n", format->u.raw_audio.channel_count);
384 	}
385 	if(format->u.raw_audio.byte_order == media_raw_audio_format::wildcard.byte_order) {
386 		format->u.raw_audio.byte_order = mPreferredFormat.u.raw_audio.byte_order;
387 		FPRINTF(stderr, "\tno channel count provided, suggesting %s\n",
388 			(format->u.raw_audio.byte_order == B_MEDIA_BIG_ENDIAN) ? "B_MEDIA_BIG_ENDIAN" : "B_MEDIA_LITTLE_ENDIAN");
389 	}
390 
391 	// check the buffer size, which may still be wildcarded
392 	if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size)
393 	{
394 		format->u.raw_audio.buffer_size = 2048;		// pick something comfortable to suggest
395 		FPRINTF(stderr, "\tno buffer size provided, suggesting %lu\n", format->u.raw_audio.buffer_size);
396 	}
397 	else
398 	{
399 		FPRINTF(stderr, "\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size);
400 	}
401 
402 	// Now reserve the connection, and return information about it
403 	mOutput.destination = where;
404 	mOutput.format = *format;
405 	*out_source = mOutput.source;
406 	strncpy(out_name, mOutput.name, B_MEDIA_NAME_LENGTH);
407 
408 	char formatStr[256];
409 	string_for_format(*format, formatStr, 255);
410 	FPRINTF(stderr, "\treturning format: %s\n", formatStr);
411 
412 	return B_OK;
413 }
414 
415 void
416 ToneProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name)
417 {
418 	FPRINTF(stderr, "ToneProducer::Connect\n");
419 
420 	// If something earlier failed, Connect() might still be called, but with a non-zero
421 	// error code.  When that happens we simply unreserve the connection and do
422 	// nothing else.
423 	if (error)
424 	{
425 		mOutput.destination = media_destination::null;
426 		mOutput.format = mPreferredFormat;
427 		return;
428 	}
429 
430 // old workaround for format bug: Connect() receives the format data from the
431 // input returned from BBufferConsumer::Connected().
432 //
433 //	char formatStr[256];
434 //	string_for_format(format, formatStr, 255);
435 //	FPRINTF(stderr, "\trequested format: %s\n", formatStr);
436 //	if(format.type != B_MEDIA_RAW_AUDIO) {
437 //		// +++++ this is NOT proper behavior
438 //		//       but it works
439 //		FPRINTF(stderr, "\tcorrupted format; falling back to last suggested format\n");
440 //		format = mOutput.format;
441 //	}
442 //
443 
444 	// Okay, the connection has been confirmed.  Record the destination and format
445 	// that we agreed on, and report our connection name again.
446 	mOutput.destination = destination;
447 	mOutput.format = format;
448 	strncpy(io_name, mOutput.name, B_MEDIA_NAME_LENGTH);
449 
450 	// Now that we're connected, we can determine our downstream latency.
451 	// Do so, then make sure we get our events early enough.
452 	media_node_id id;
453 	FindLatencyFor(mOutput.destination, &mLatency, &id);
454 	FPRINTF(stderr, "\tdownstream latency = %Ld\n", mLatency);
455 
456 	// Use a dry run to see how long it takes me to fill a buffer of data
457 	bigtime_t start, produceLatency;
458 	size_t samplesPerBuffer = mOutput.format.u.raw_audio.buffer_size / sizeof(float);
459 	size_t framesPerBuffer = samplesPerBuffer / mOutput.format.u.raw_audio.channel_count;
460 	float* data = new float[samplesPerBuffer];
461 	mTheta = 0;
462 	start = ::system_time();
463 	FillSineBuffer(data, framesPerBuffer, mOutput.format.u.raw_audio.channel_count==2);
464 	produceLatency = ::system_time();
465 	mInternalLatency = produceLatency - start;
466 
467 	// +++++ e.moon [13jun99]: fiddling with latency, ick
468 	mInternalLatency += 20000LL;
469 
470 	delete [] data;
471 	FPRINTF(stderr, "\tbuffer-filling took %Ld usec on this machine\n", mInternalLatency);
472 	SetEventLatency(mLatency + mInternalLatency);
473 
474 	// reset our buffer duration, etc. to avoid later calculations
475 	// +++++ e.moon 11jun99: crashes w/ divide-by-zero when connecting to LoggingConsumer
476 	ASSERT(mOutput.format.u.raw_audio.frame_rate);
477 
478 	bigtime_t duration = bigtime_t(1000000) * samplesPerBuffer / bigtime_t(mOutput.format.u.raw_audio.frame_rate);
479 	SetBufferDuration(duration);
480 
481 	// Set up the buffer group for our connection, as long as nobody handed us a
482 	// buffer group (via SetBufferGroup()) prior to this.  That can happen, for example,
483 	// if the consumer calls SetOutputBuffersFor() on us from within its Connected()
484 	// method.
485 	if (!mBufferGroup) AllocateBuffers();
486 }
487 
488 void
489 ToneProducer::Disconnect(const media_source& what, const media_destination& where)
490 {
491 	FPRINTF(stderr, "ToneProducer::Disconnect\n");
492 
493 	// Make sure that our connection is the one being disconnected
494 	if ((where == mOutput.destination) && (what == mOutput.source))
495 	{
496 		mOutput.destination = media_destination::null;
497 		mOutput.format = mPreferredFormat;
498 		delete mBufferGroup;
499 		mBufferGroup = NULL;
500 	}
501 	else
502 	{
503 		FPRINTF(stderr, "\tDisconnect() called with wrong source/destination (%ld/%ld), ours is (%ld/%ld)\n",
504 			what.id, where.id, mOutput.source.id, mOutput.destination.id);
505 	}
506 }
507 
508 void
509 ToneProducer::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time)
510 {
511 	FPRINTF(stderr, "ToneProducer::LateNoticeReceived\n");
512 
513 	// If we're late, we need to catch up.  Respond in a manner appropriate to our
514 	// current run mode.
515 	if (what == mOutput.source)
516 	{
517 		if (RunMode() == B_RECORDING)
518 		{
519 			// A hardware capture node can't adjust; it simply emits buffers at
520 			// appropriate points.  We (partially) simulate this by not adjusting
521 			// our behavior upon receiving late notices -- after all, the hardware
522 			// can't choose to capture "sooner"....
523 		}
524 		else if (RunMode() == B_INCREASE_LATENCY)
525 		{
526 			// We're late, and our run mode dictates that we try to produce buffers
527 			// earlier in order to catch up.  This argues that the downstream nodes are
528 			// not properly reporting their latency, but there's not much we can do about
529 			// that at the moment, so we try to start producing buffers earlier to
530 			// compensate.
531 			mInternalLatency += how_much;
532 			if (mInternalLatency > 50000)
533 				mInternalLatency = 50000;
534 			SetEventLatency(mLatency + mInternalLatency);
535 
536 			FPRINTF(stderr, "\tincreasing latency to %Ld\n", mLatency + mInternalLatency);
537 		}
538 		else
539 		{
540 			// The other run modes dictate various strategies for sacrificing data quality
541 			// in the interests of timely data delivery.  The way *we* do this is to skip
542 			// a buffer, which catches us up in time by one buffer duration.
543 			size_t nSamples = mOutput.format.u.raw_audio.buffer_size / sizeof(float);
544 			mFramesSent += nSamples;
545 
546 			FPRINTF(stderr, "\tskipping a buffer to try to catch up\n");
547 		}
548 	}
549 }
550 
551 void
552 ToneProducer::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_)
553 {
554 	FPRINTF(stderr, "ToneProducer::EnableOutput\n");
555 
556 	// If I had more than one output, I'd have to walk my list of output records to see
557 	// which one matched the given source, and then enable/disable that one.  But this
558 	// node only has one output, so I just make sure the given source matches, then set
559 	// the enable state accordingly.
560 	if (what == mOutput.source)
561 	{
562 		mOutputEnabled = enabled;
563 	}
564 }
565 
566 status_t
567 ToneProducer::SetPlayRate(int32 numer, int32 denom)
568 {
569 	FPRINTF(stderr, "ToneProducer::SetPlayRate\n");
570 
571 	// Play rates are weird.  We don't support them.  Maybe we will in a
572 	// later newsletter article....
573 	return B_ERROR;
574 }
575 
576 status_t
577 ToneProducer::HandleMessage(int32 message, const void* data, size_t size)
578 {
579 	FPRINTF(stderr, "ToneProducer::HandleMessage(%ld = 0x%lx)\n", message, message);
580 	// HandleMessage() is where you implement any private message protocols
581 	// that you want to use.  When messages are written to your node's control
582 	// port that are not recognized by any of the node superclasses, they'll be
583 	// passed to this method in your node's implementation for handling.  The
584 	// ToneProducer node doesn't support any private messages, so we just
585 	// return an error, indicating that the message wasn't handled.
586 	return B_ERROR;
587 }
588 
589 void
590 ToneProducer::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag)
591 {
592 	FPRINTF(stderr, "ToneProducer::AdditionalBufferRequested\n");
593 
594 	// we don't support offline mode (yet...)
595 	return;
596 }
597 
598 void
599 ToneProducer::LatencyChanged(
600 	const media_source& source,
601 	const media_destination& destination,
602 	bigtime_t new_latency,
603 	uint32 flags)
604 {
605 	PRINT(("ToneProducer::LatencyChanged(): %Ld\n", new_latency));
606 
607 	// something downstream changed latency, so we need to start producing
608 	// buffers earlier (or later) than we were previously.  Make sure that the
609 	// connection that changed is ours, and adjust to the new downstream
610 	// latency if so.
611 	if ((source == mOutput.source) && (destination == mOutput.destination))
612 	{
613 		mLatency = new_latency;
614 		SetEventLatency(mLatency + mInternalLatency);
615 	}
616 }
617 /*
618 // Workaround for a Metrowerks PPC compiler bug
619 status_t
620 ToneProducer::DeleteHook(BMediaNode* node)
621 {
622 	return BMediaEventLooper::DeleteHook(node);
623 }
624 
625 //#pragma mark -
626 */
627 
628 // BMediaEventLooper methods
629 void
630 ToneProducer::NodeRegistered()
631 {
632 	FPRINTF(stderr, "ToneProducer::NodeRegistered\n");
633 
634 	// Start the BMediaEventLooper thread
635 	SetPriority(B_REAL_TIME_PRIORITY);
636 	Run();
637 
638 	// output init moved to ctor
639 	// e.moon [4jun99]
640 
641 	// Set up our parameter web
642 	mWeb = make_parameter_web();
643 	SetParameterWeb(mWeb);
644 }
645 
646 void
647 ToneProducer::Start(bigtime_t performance_time)
648 {
649 	PRINT(("ToneProducer::Start(%Ld): now %Ld\n", performance_time, TimeSource()->Now()));
650 
651 	// send 'data available' message
652 	if(mOutput.destination != media_destination::null)
653 		SendDataStatus(B_DATA_AVAILABLE, mOutput.destination, performance_time);
654 
655 	// A bug in the current PowerPC compiler demands that we implement
656 	// this, even though it just calls up to the inherited implementation.
657 	BMediaEventLooper::Start(performance_time);
658 }
659 
660 void
661 ToneProducer::Stop(bigtime_t performance_time, bool immediate)
662 {
663 	// send 'data not available' message
664 	if(mOutput.destination != media_destination::null) {
665 		printf("ToneProducer: B_PRODUCER_STOPPED at %" B_PRIdBIGTIME "\n",
666 			performance_time);
667 		SendDataStatus(B_PRODUCER_STOPPED, mOutput.destination, performance_time);
668 	}
669 
670 	// A bug in the current PowerPC compiler demands that we implement
671 	// this, even though it just calls up to the inherited implementation.
672 	BMediaEventLooper::Stop(performance_time, immediate);
673 }
674 
675 void
676 ToneProducer::Seek(bigtime_t media_time, bigtime_t performance_time)
677 {
678 	// A bug in the current PowerPC compiler demands that we implement
679 	// this, even though it just calls up to the inherited implementation.
680 	BMediaEventLooper::Seek(media_time, performance_time);
681 }
682 
683 void
684 ToneProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
685 {
686 	// A bug in the current PowerPC compiler demands that we implement
687 	// this, even though it just calls up to the inherited implementation.
688 	BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
689 }
690 
691 status_t
692 ToneProducer::AddTimer(bigtime_t at_performance_time, int32 cookie)
693 {
694 	// A bug in the current PowerPC compiler demands that we implement
695 	// this, even though it just calls up to the inherited implementation.
696 	return BMediaEventLooper::AddTimer(at_performance_time, cookie);
697 }
698 
699 void
700 ToneProducer::SetRunMode(run_mode mode)
701 {
702 	FPRINTF(stderr, "ToneProducer::SetRunMode\n");
703 
704 	// We don't support offline run mode, so broadcast an error if we're set to
705 	// B_OFFLINE.  Unfortunately, we can't actually reject the mode change...
706 	if (B_OFFLINE == mode)
707 	{
708 		ReportError(B_NODE_FAILED_SET_RUN_MODE);
709 	}
710 }
711 
712 void
713 ToneProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent)
714 {
715 //	FPRINTF(stderr, "ToneProducer::HandleEvent\n");
716 	switch (event->type)
717 	{
718 	case BTimedEventQueue::B_START:
719 		// don't do anything if we're already running
720 		if (RunState() != B_STARTED)
721 		{
722 			// We want to start sending buffers now, so we set up the buffer-sending bookkeeping
723 			// and fire off the first "produce a buffer" event.
724 			mFramesSent = 0;
725 			mTheta = 0;
726 			mStartTime = event->event_time;
727 			media_timed_event firstBufferEvent(mStartTime, BTimedEventQueue::B_HANDLE_BUFFER);
728 
729 			// Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through
730 			// the event queue, like this:
731 			//
732 			//		this->HandleEvent(&firstBufferEvent, 0, false);
733 			//
734 			EventQueue()->AddEvent(firstBufferEvent);
735 		}
736 		break;
737 
738 	case BTimedEventQueue::B_STOP:
739 		FPRINTF(stderr, "Handling B_STOP event\n");
740 
741 		// When we handle a stop, we must ensure that downstream consumers don't
742 		// get any more buffers from us.  This means we have to flush any pending
743 		// buffer-producing events from the queue.
744 		EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
745 		break;
746 
747 	case _PARAMETER_EVENT:
748 		{
749 			size_t dataSize = size_t(event->data);
750 			int32 param = int32(event->bigdata);
751 			if (dataSize >= sizeof(float)) switch (param)
752 			{
753 			case FREQUENCY_PARAM:
754 				{
755 					float newValue = *((float*) event->user_data);
756 					if (mFrequency != newValue)		// an actual change in the value?
757 					{
758 						mFrequency = newValue;
759 						mFreqLastChanged = TimeSource()->Now();
760 						BroadcastNewParameterValue(mFreqLastChanged, param, &mFrequency, sizeof(mFrequency));
761 					}
762 				}
763 				break;
764 
765 			case GAIN_PARAM:
766 				{
767 					float newValue = *((float*) event->user_data);
768 					if (mGain != newValue)
769 					{
770 						mGain = newValue;
771 						mGainLastChanged = TimeSource()->Now();
772 						BroadcastNewParameterValue(mGainLastChanged, param, &mGain, sizeof(mGain));
773 					}
774 				}
775 				break;
776 
777 			case WAVEFORM_PARAM:
778 				{
779 					int32 newValue = *((int32*) event->user_data);
780 					if (mWaveform != newValue)
781 					{
782 						mWaveform = newValue;
783 						mTheta = 0;			// reset the generator parameters when we change waveforms
784 						mWaveAscending = true;
785 						mWaveLastChanged = TimeSource()->Now();
786 						BroadcastNewParameterValue(mWaveLastChanged, param, &mWaveform, sizeof(mWaveform));
787 					}
788 				}
789 				break;
790 
791 			default:
792 				FPRINTF(stderr, "Hmmm... got a B_PARAMETER event for a parameter we don't have? (%ld)\n", param);
793 				break;
794 			}
795 		}
796 		break;
797 
798 	case BTimedEventQueue::B_HANDLE_BUFFER:
799 		{
800 			// make sure we're both started *and* connected before delivering a buffer
801 			if (RunState() == BMediaEventLooper::B_STARTED
802 				&& mOutput.destination != media_destination::null) {
803 				// Get the next buffer of data
804 				BBuffer* buffer = FillNextBuffer(event->event_time);
805 				if (buffer) {
806 					// send the buffer downstream if and only if output is enabled
807 					status_t err = B_ERROR;
808 					if (mOutputEnabled) {
809 						err = SendBuffer(buffer, mOutput.source,
810 							mOutput.destination);
811 					}
812 					if (err) {
813 						// we need to recycle the buffer ourselves if output is disabled or
814 						// if the call to SendBuffer() fails
815 						buffer->Recycle();
816 					}
817 				}
818 
819 				// track how much media we've delivered so far
820 				size_t nFrames = mOutput.format.u.raw_audio.buffer_size /
821 					(sizeof(float) * mOutput.format.u.raw_audio.channel_count);
822 				mFramesSent += nFrames;
823 
824 				// The buffer is on its way; now schedule the next one to go
825 				bigtime_t nextEvent = mStartTime + bigtime_t(double(mFramesSent)
826 					/ double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0);
827 				media_timed_event nextBufferEvent(nextEvent,
828 					BTimedEventQueue::B_HANDLE_BUFFER);
829 				EventQueue()->AddEvent(nextBufferEvent);
830 			}
831 		}
832 		break;
833 
834 	default:
835 		break;
836 	}
837 }
838 
839 /*
840 void
841 ToneProducer::CleanUpEvent(const media_timed_event *event)
842 {
843 	// A bug in the current PowerPC compiler demands that we implement
844 	// this, even though it just calls up to the inherited implementation.
845 	BMediaEventLooper::CleanUpEvent(event);
846 }
847 
848 bigtime_t
849 ToneProducer::OfflineTime()
850 {
851 	// A bug in the current PowerPC compiler demands that we implement
852 	// this, even though it just calls up to the inherited implementation.
853 	return BMediaEventLooper::OfflineTime();
854 }
855 
856 void
857 ToneProducer::ControlLoop()
858 {
859 	// A bug in the current PowerPC compiler demands that we implement
860 	// this, even though it just calls up to the inherited implementation.
861 	BMediaEventLooper::ControlLoop();
862 }
863 
864 //#pragma mark -
865 */
866 
867 void
868 ToneProducer::AllocateBuffers()
869 {
870 	FPRINTF(stderr, "ToneProducer::AllocateBuffers\n");
871 
872 	// allocate enough buffers to span our downstream latency, plus one
873 	size_t size = mOutput.format.u.raw_audio.buffer_size;
874 	int32 count = int32(mLatency / BufferDuration() + 1 + 1);
875 
876 	FPRINTF(stderr, "\tlatency = %Ld, buffer duration = %Ld\n", mLatency, BufferDuration());
877 	FPRINTF(stderr, "\tcreating group of %ld buffers, size = %lx\n", count, size);
878 	mBufferGroup = new BBufferGroup(size, count);
879 }
880 
881 BBuffer*
882 ToneProducer::FillNextBuffer(bigtime_t event_time)
883 {
884 	// get a buffer from our buffer group
885 	BBuffer* buf = mBufferGroup->RequestBuffer(mOutput.format.u.raw_audio.buffer_size, BufferDuration());
886 
887 	// if we fail to get a buffer (for example, if the request times out), we skip this
888 	// buffer and go on to the next, to avoid locking up the control thread
889 	if (!buf)
890 	{
891 		return NULL;
892 	}
893 
894 	// now fill it with data, continuing where the last buffer left off
895 	// 20sep99: multichannel support
896 
897 	size_t numFrames =
898 		mOutput.format.u.raw_audio.buffer_size /
899 		(sizeof(float)*mOutput.format.u.raw_audio.channel_count);
900 	bool stereo = (mOutput.format.u.raw_audio.channel_count == 2);
901 	if(!stereo) {
902 		ASSERT(mOutput.format.u.raw_audio.channel_count == 1);
903 	}
904 //	PRINT(("buffer: %ld, %ld frames, %s\n", mOutput.format.u.raw_audio.buffer_size, numFrames, stereo ? "stereo" : "mono"));
905 
906 	float* data = (float*) buf->Data();
907 
908 	switch (mWaveform)
909 	{
910 	case SINE_WAVE:
911 		FillSineBuffer(data, numFrames, stereo);
912 		break;
913 
914 	case TRIANGLE_WAVE:
915 		FillTriangleBuffer(data, numFrames, stereo);
916 		break;
917 
918 	case SAWTOOTH_WAVE:
919 		FillSawtoothBuffer(data, numFrames, stereo);
920 		break;
921 	}
922 
923 	// fill in the buffer header
924 	media_header* hdr = buf->Header();
925 	hdr->type = B_MEDIA_RAW_AUDIO;
926 	hdr->size_used = mOutput.format.u.raw_audio.buffer_size;
927 	hdr->time_source = TimeSource()->ID();
928 
929 	bigtime_t stamp;
930 	if (RunMode() == B_RECORDING)
931 	{
932 		// In B_RECORDING mode, we stamp with the capture time.  We're not
933 		// really a hardware capture node, but we simulate it by using the (precalculated)
934 		// time at which this buffer "should" have been created.
935 		stamp = event_time;
936 	}
937 	else
938 	{
939 		// okay, we're in one of the "live" performance run modes.  in these modes, we
940 		// stamp the buffer with the time at which the buffer should be rendered to the
941 		// output, not with the capture time.  mStartTime is the cached value of the
942 		// first buffer's performance time; we calculate this buffer's performance time as
943 		// an offset from that time, based on the amount of media we've created so far.
944 		// Recalculating every buffer like this avoids accumulation of error.
945 		stamp = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0);
946 	}
947 	hdr->start_time = stamp;
948 
949 	return buf;
950 }
951 
952 // waveform generators - fill buffers with various waveforms
953 void
954 ToneProducer::FillSineBuffer(float *data, size_t numFrames, bool stereo)
955 {
956 
957 
958 	// cover 2pi radians in one period
959 	double dTheta = 2*M_PI * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate;
960 
961 	// Fill the buffer!
962 	for (size_t i = 0; i < numFrames; i++, data++)
963 	{
964 		float val = mGain * float(sin(mTheta));
965 		*data = val;
966 		if(stereo) {
967 			++data;
968 			*data = val;
969 		}
970 
971 		mTheta += dTheta;
972 		if (mTheta > 2*M_PI)
973 		{
974 			mTheta -= 2*M_PI;
975 		}
976 	}
977 }
978 
979 void
980 ToneProducer::FillTriangleBuffer(float *data, size_t numFrames, bool stereo)
981 {
982 	// ramp from -1 to 1 and back in one period
983 	double dTheta = 4.0 * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate;
984 	if (!mWaveAscending) dTheta = -dTheta;
985 
986 	// fill the buffer!
987 	for (size_t i = 0; i < numFrames; i++, data++)
988 	{
989 		float val = mGain * mTheta;
990 		*data = val;
991 		if(stereo) {
992 			++data;
993 			*data = val;
994 		}
995 
996 		mTheta += dTheta;
997 		if (mTheta >= 1)
998 		{
999 			mTheta = 2 - mTheta;		// reflect across the mTheta=1 line to preserve drift
1000 			mWaveAscending = false;
1001 			dTheta = -dTheta;
1002 		}
1003 		else if (mTheta <= -1)
1004 		{
1005 			mTheta = -2 - mTheta;		// reflect across mTheta=-1
1006 			mWaveAscending = true;
1007 			dTheta = -dTheta;
1008 		}
1009 	}
1010 }
1011 
1012 void
1013 ToneProducer::FillSawtoothBuffer(float *data, size_t numFrames, bool stereo)
1014 {
1015 	// ramp from -1 to 1 in one period
1016 	double dTheta = 2 * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate;
1017 	mWaveAscending = true;
1018 
1019 	// fill the buffer!
1020 	for (size_t i = 0; i < numFrames; i++, data++)
1021 	{
1022 		float val = mGain * mTheta;
1023 		*data = val;
1024 		if(stereo) {
1025 			++data;
1026 			*data = val;
1027 		}
1028 
1029 		mTheta += dTheta;
1030 		if (mTheta > 1)
1031 		{
1032 			mTheta -= 2;		// back to the base of the sawtooth, including cumulative drift
1033 		}
1034 	}
1035 }
1036 
1037 // utility - build the ToneProducer's parameter web
1038 static BParameterWeb* make_parameter_web()
1039 {
1040 	FPRINTF(stderr, "make_parameter_web() called\n");
1041 
1042 	BParameterWeb* web = new BParameterWeb;
1043 	BParameterGroup* mainGroup = web->MakeGroup("Tone Generator Parameters");
1044 
1045 	BParameterGroup* group = mainGroup->MakeGroup("Frequency");
1046 	BParameter* nullParam = group->MakeNullParameter(FREQUENCY_NULL_PARAM, B_MEDIA_NO_TYPE, "Frequency", B_GENERIC);
1047 	BContinuousParameter* param = group->MakeContinuousParameter(FREQUENCY_PARAM, B_MEDIA_NO_TYPE, "", B_GAIN, "Hz", 0, 2500, 0.1);
1048 	nullParam->AddOutput(param);
1049 	param->AddInput(nullParam);
1050 
1051 	group = mainGroup->MakeGroup("Amplitude");
1052 	nullParam = group->MakeNullParameter(GAIN_NULL_PARAM, B_MEDIA_NO_TYPE, "Amplitude", B_GENERIC);
1053 	param = group->MakeContinuousParameter(GAIN_PARAM, B_MEDIA_NO_TYPE, "", B_GAIN, "", 0, 1, 0.01);
1054 	nullParam->AddOutput(param);
1055 	param->AddInput(nullParam);
1056 
1057 	group = mainGroup->MakeGroup("Waveform");
1058 	nullParam = group->MakeNullParameter(WAVEFORM_NULL_PARAM, B_MEDIA_NO_TYPE, "Waveform", B_GENERIC);
1059 	BDiscreteParameter* waveParam = group->MakeDiscreteParameter(WAVEFORM_PARAM, B_MEDIA_NO_TYPE, "", B_GENERIC);
1060 	waveParam->AddItem(SINE_WAVE, "Sine wave");
1061 	waveParam->AddItem(TRIANGLE_WAVE, "Triangle");
1062 	waveParam->AddItem(SAWTOOTH_WAVE, "Sawtooth");
1063 	nullParam->AddOutput(waveParam);
1064 	waveParam->AddInput(nullParam);
1065 
1066 	return web;
1067 }
1068