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