xref: /haiku/src/add-ons/media/media-add-ons/tone_producer_demo/ToneProducer.cpp (revision bab64f65bb775dc23060e276f1f1c4498ab7af6c)
111bb5731Sbeveloper /*
211bb5731Sbeveloper 	ToneProducer.cpp
311bb5731Sbeveloper 
411bb5731Sbeveloper 	Copyright 1999, Be Incorporated.   All Rights Reserved.
511bb5731Sbeveloper 	This file may be used under the terms of the Be Sample Code License.
611bb5731Sbeveloper 
711bb5731Sbeveloper 	NOTE:  to compile this code under Genki beta releases, do a search-
811bb5731Sbeveloper 	and-replace to change "B_PARAMETER" to "B_USER_EVENT+1"
911bb5731Sbeveloper */
1011bb5731Sbeveloper 
1111bb5731Sbeveloper #include "ToneProducer.h"
1211bb5731Sbeveloper #include <support/ByteOrder.h>
1311bb5731Sbeveloper #include <media/BufferGroup.h>
1411bb5731Sbeveloper #include <media/Buffer.h>
1511bb5731Sbeveloper #include <media/TimeSource.h>
1611bb5731Sbeveloper #include <media/ParameterWeb.h>
1711bb5731Sbeveloper #include <media/MediaDefs.h>
1811bb5731Sbeveloper #include <string.h>
1911bb5731Sbeveloper #include <stdio.h>
2011bb5731Sbeveloper #include <math.h>
2111bb5731Sbeveloper 
2211bb5731Sbeveloper #include <Messenger.h>
2311bb5731Sbeveloper 
2411bb5731Sbeveloper #include <Debug.h>
2511bb5731Sbeveloper #if DEBUG
2611bb5731Sbeveloper 	#define FPRINTF fprintf
2711bb5731Sbeveloper #else
FPRINTF(FILE *,const char *,...)2811bb5731Sbeveloper 	static inline void FPRINTF(FILE*, const char*, ...) { }
2911bb5731Sbeveloper #endif
3011bb5731Sbeveloper 
3111bb5731Sbeveloper // parameter web handling
3211bb5731Sbeveloper static BParameterWeb* make_parameter_web();
3311bb5731Sbeveloper const int32 FREQUENCY_NULL_PARAM = 1;
3411bb5731Sbeveloper const int32 FREQUENCY_PARAM = 2;
3511bb5731Sbeveloper const int32 GAIN_NULL_PARAM = 11;
3611bb5731Sbeveloper const int32 GAIN_PARAM = 12;
3711bb5731Sbeveloper const int32 WAVEFORM_NULL_PARAM = 21;
3811bb5731Sbeveloper const int32  WAVEFORM_PARAM = 22;
3911bb5731Sbeveloper const int32 SINE_WAVE = 90;
4011bb5731Sbeveloper const int32 TRIANGLE_WAVE = 91;
4111bb5731Sbeveloper const int32 SAWTOOTH_WAVE = 92;
4211bb5731Sbeveloper 
4311bb5731Sbeveloper // ----------------
4411bb5731Sbeveloper // ToneProducer implementation
4511bb5731Sbeveloper 
ToneProducer(BMediaAddOn * pAddOn)4611bb5731Sbeveloper ToneProducer::ToneProducer(BMediaAddOn* pAddOn)
4711bb5731Sbeveloper 	:	BMediaNode("ToneProducer"),
4811bb5731Sbeveloper 		BBufferProducer(B_MEDIA_RAW_AUDIO),
4911bb5731Sbeveloper 		BControllable(),
5011bb5731Sbeveloper 		BMediaEventLooper(),
5111bb5731Sbeveloper 		mWeb(NULL),
5211bb5731Sbeveloper 		mBufferGroup(NULL),
5311bb5731Sbeveloper 		mLatency(0),
5411bb5731Sbeveloper 		mInternalLatency(0),
5511bb5731Sbeveloper 		mOutputEnabled(true),
5611bb5731Sbeveloper 		mTheta(0.0),
5711bb5731Sbeveloper 		mWaveAscending(true),
5811bb5731Sbeveloper 		mFrequency(440),
5911bb5731Sbeveloper 		mGain(0.25),
6011bb5731Sbeveloper 		mWaveform(SINE_WAVE),
6111bb5731Sbeveloper 		mFramesSent(0),
6211bb5731Sbeveloper 		mStartTime(0),
6311bb5731Sbeveloper 		mGainLastChanged(0),
6411bb5731Sbeveloper 		mFreqLastChanged(0),
6511bb5731Sbeveloper 		mWaveLastChanged(0),
6611bb5731Sbeveloper 		m_pAddOn(pAddOn)
6711bb5731Sbeveloper {
6811bb5731Sbeveloper 	// initialize our preferred format object
6911bb5731Sbeveloper 	mPreferredFormat.type = B_MEDIA_RAW_AUDIO;
7011bb5731Sbeveloper 	mPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
7111bb5731Sbeveloper 	mPreferredFormat.u.raw_audio.byte_order = (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
7211bb5731Sbeveloper 
73*b1d006d7SDario Casalinuovo 	// we'll use the consumer's preferred buffer size and framerate, if any
74*b1d006d7SDario Casalinuovo 	mPreferredFormat.u.raw_audio.frame_rate = media_raw_audio_format::wildcard.frame_rate;
7511bb5731Sbeveloper 	mPreferredFormat.u.raw_audio.buffer_size = media_raw_audio_format::wildcard.buffer_size;
7611bb5731Sbeveloper 
7711bb5731Sbeveloper 	// 20sep99: multiple-channel support
7811bb5731Sbeveloper 	mPreferredFormat.u.raw_audio.channel_count = media_raw_audio_format::wildcard.channel_count;
7911bb5731Sbeveloper 
8011bb5731Sbeveloper 
8111bb5731Sbeveloper 	// we're not connected yet
8211bb5731Sbeveloper 	mOutput.destination = media_destination::null;
8311bb5731Sbeveloper 
8411bb5731Sbeveloper 	// [e.moon 1dec99]
8511bb5731Sbeveloper 	mOutput.format = mPreferredFormat;
8611bb5731Sbeveloper 
8711bb5731Sbeveloper 	// set up as much information about our output as we can
8811bb5731Sbeveloper 	// +++++ wrong; can't call Node() until the node is registered!
8911bb5731Sbeveloper 	mOutput.source.port = ControlPort();
9011bb5731Sbeveloper 	mOutput.source.id = 0;
9111bb5731Sbeveloper 	mOutput.node = Node();
9211bb5731Sbeveloper 	::strcpy(mOutput.name, "ToneProducer Output");
9311bb5731Sbeveloper }
9411bb5731Sbeveloper 
~ToneProducer()9511bb5731Sbeveloper ToneProducer::~ToneProducer()
9611bb5731Sbeveloper {
9711bb5731Sbeveloper 	// Stop the BMediaEventLooper thread
9811bb5731Sbeveloper 	Quit();
9911bb5731Sbeveloper 
10011bb5731Sbeveloper 	// the BControllable destructor deletes our parameter web for us; we just use
10111bb5731Sbeveloper 	// a little defensive programming here and set our web pointer to be NULL.
10211bb5731Sbeveloper 	mWeb = NULL;
10311bb5731Sbeveloper }
10411bb5731Sbeveloper 
10511bb5731Sbeveloper //#pragma mark -
10611bb5731Sbeveloper 
10711bb5731Sbeveloper // BMediaNode methods
10811bb5731Sbeveloper BMediaAddOn *
AddOn(int32 * internal_id) const10911bb5731Sbeveloper ToneProducer::AddOn(int32 *internal_id) const
11011bb5731Sbeveloper {
11111bb5731Sbeveloper 	// e.moon [8jun99]
11211bb5731Sbeveloper 	if(m_pAddOn) {
11311bb5731Sbeveloper 		*internal_id = 0;
11411bb5731Sbeveloper 		return m_pAddOn;
11511bb5731Sbeveloper 	} else
11611bb5731Sbeveloper 		return NULL;
11711bb5731Sbeveloper }
11811bb5731Sbeveloper 
11911bb5731Sbeveloper //#pragma mark -
12011bb5731Sbeveloper 
12111bb5731Sbeveloper // BControllable methods
12211bb5731Sbeveloper status_t
GetParameterValue(int32 id,bigtime_t * last_change,void * value,size_t * ioSize)12311bb5731Sbeveloper ToneProducer::GetParameterValue(int32 id, bigtime_t* last_change, void* value, size_t* ioSize)
12411bb5731Sbeveloper {
12511bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::GetParameterValue\n");
12611bb5731Sbeveloper 
12711bb5731Sbeveloper 	// floats & int32s are the same size, so this one test of the size of the
12811bb5731Sbeveloper 	// output buffer is sufficient for all of our parameters
12911bb5731Sbeveloper 	if (*ioSize < sizeof(float)) return B_ERROR;
13011bb5731Sbeveloper 
13111bb5731Sbeveloper 	// fill in the value of the requested parameter
13211bb5731Sbeveloper 	switch (id)
13311bb5731Sbeveloper 	{
13411bb5731Sbeveloper 	case FREQUENCY_PARAM:
13511bb5731Sbeveloper 		*last_change = mFreqLastChanged;
13611bb5731Sbeveloper 		*((float*) value) = mFrequency;
13711bb5731Sbeveloper 		*ioSize = sizeof(float);
13811bb5731Sbeveloper 		break;
13911bb5731Sbeveloper 
14011bb5731Sbeveloper 	case GAIN_PARAM:
14111bb5731Sbeveloper 		*last_change = mGainLastChanged;
14211bb5731Sbeveloper 		*((float*) value) = mGain;
14311bb5731Sbeveloper 		*ioSize = sizeof(float);
14411bb5731Sbeveloper 		break;
14511bb5731Sbeveloper 
14611bb5731Sbeveloper 	case WAVEFORM_PARAM:
14711bb5731Sbeveloper 		*last_change = mWaveLastChanged;
14811bb5731Sbeveloper 		*((int32*) value) = mWaveform;
14911bb5731Sbeveloper 		*ioSize = sizeof(int32);
15011bb5731Sbeveloper 		break;
15111bb5731Sbeveloper 
15211bb5731Sbeveloper 	default:
15311bb5731Sbeveloper 		// Hmmm, we were asked for a parameter that we don't actually
15411bb5731Sbeveloper 		// support.  Report an error back to the caller.
1551a7bcf69SOliver Tappe 		FPRINTF(stderr, "\terror - asked for illegal parameter %" B_PRId32 "\n",
1561a7bcf69SOliver Tappe 			id);
15711bb5731Sbeveloper 		return B_ERROR;
15811bb5731Sbeveloper 		break;
15911bb5731Sbeveloper 	}
16011bb5731Sbeveloper 
16111bb5731Sbeveloper 	return B_OK;
16211bb5731Sbeveloper }
16311bb5731Sbeveloper 
16411bb5731Sbeveloper void
SetParameterValue(int32 id,bigtime_t performance_time,const void * value,size_t size)16511bb5731Sbeveloper ToneProducer::SetParameterValue(int32 id, bigtime_t performance_time, const void* value, size_t size)
16611bb5731Sbeveloper {
16711bb5731Sbeveloper 	switch (id)
16811bb5731Sbeveloper 	{
16911bb5731Sbeveloper 	case FREQUENCY_PARAM:
17011bb5731Sbeveloper 	case GAIN_PARAM:
17111bb5731Sbeveloper 	case WAVEFORM_PARAM:
17211bb5731Sbeveloper 		{
17311bb5731Sbeveloper 			// floats and int32s are the same size, so we need only check the block's size once
17411bb5731Sbeveloper 			if (size > sizeof(float)) size = sizeof(float);
17511bb5731Sbeveloper 
17611bb5731Sbeveloper 			// submit the parameter change as a performance event, to be handled at the
17711bb5731Sbeveloper 			// appropriate time
17811bb5731Sbeveloper 			media_timed_event event(performance_time, _PARAMETER_EVENT,
17911bb5731Sbeveloper 				NULL, BTimedEventQueue::B_NO_CLEANUP, size, id, (char*) value, size);
18011bb5731Sbeveloper 			EventQueue()->AddEvent(event);
18111bb5731Sbeveloper 		}
18211bb5731Sbeveloper 		break;
18311bb5731Sbeveloper 
18411bb5731Sbeveloper 	default:
18511bb5731Sbeveloper 		break;
18611bb5731Sbeveloper 	}
18711bb5731Sbeveloper }
18811bb5731Sbeveloper 
18911bb5731Sbeveloper // e.moon [17jun99]
StartControlPanel(BMessenger * pMessenger)19011bb5731Sbeveloper status_t ToneProducer::StartControlPanel(
19111bb5731Sbeveloper 	BMessenger* pMessenger) {
19211bb5731Sbeveloper 	PRINT(("ToneProducer::StartControlPanel(%p)\n", pMessenger));
19311bb5731Sbeveloper 	status_t err = BControllable::StartControlPanel(pMessenger);
19411bb5731Sbeveloper 	if(pMessenger && pMessenger->IsValid()) {
19511bb5731Sbeveloper 		PRINT(("\tgot valid control panel\n"));
19611bb5731Sbeveloper 	}
19711bb5731Sbeveloper 
19811bb5731Sbeveloper 	return err;
19911bb5731Sbeveloper }
20011bb5731Sbeveloper 
20111bb5731Sbeveloper //#pragma mark -
20211bb5731Sbeveloper 
20311bb5731Sbeveloper // BBufferProducer methods
20411bb5731Sbeveloper status_t
FormatSuggestionRequested(media_type type,int32,media_format * format)20511bb5731Sbeveloper ToneProducer::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format)
20611bb5731Sbeveloper {
20711bb5731Sbeveloper 	// FormatSuggestionRequested() is not necessarily part of the format negotiation
20811bb5731Sbeveloper 	// process; it's simply an interrogation -- the caller wants to see what the node's
20911bb5731Sbeveloper 	// preferred data format is, given a suggestion by the caller.
21011bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::FormatSuggestionRequested\n");
21111bb5731Sbeveloper 
21211bb5731Sbeveloper 	if (!format)
21311bb5731Sbeveloper 	{
21411bb5731Sbeveloper 		FPRINTF(stderr, "\tERROR - NULL format pointer passed in!\n");
21511bb5731Sbeveloper 		return B_BAD_VALUE;
21611bb5731Sbeveloper 	}
21711bb5731Sbeveloper 
21811bb5731Sbeveloper 	// this is the format we'll be returning (our preferred format)
21911bb5731Sbeveloper 	*format = mPreferredFormat;
22011bb5731Sbeveloper 
22111bb5731Sbeveloper 	// a wildcard type is okay; we can specialize it
22211bb5731Sbeveloper 	if (type == B_MEDIA_UNKNOWN_TYPE) type = B_MEDIA_RAW_AUDIO;
22311bb5731Sbeveloper 
22411bb5731Sbeveloper 	// we only support raw audio
22511bb5731Sbeveloper 	if (type != B_MEDIA_RAW_AUDIO) return B_MEDIA_BAD_FORMAT;
22611bb5731Sbeveloper 	else return B_OK;
22711bb5731Sbeveloper }
22811bb5731Sbeveloper 
22911bb5731Sbeveloper status_t
FormatProposal(const media_source & output,media_format * format)23011bb5731Sbeveloper ToneProducer::FormatProposal(const media_source& output, media_format* format)
23111bb5731Sbeveloper {
23211bb5731Sbeveloper 	// FormatProposal() is the first stage in the BMediaRoster::Connect() process.  We hand
23311bb5731Sbeveloper 	// out a suggested format, with wildcards for any variations we support.
23411bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::FormatProposal\n");
23511bb5731Sbeveloper 
23611bb5731Sbeveloper 	// is this a proposal for our one output?
23711bb5731Sbeveloper 	if (output != mOutput.source)
23811bb5731Sbeveloper 	{
23911bb5731Sbeveloper 		FPRINTF(stderr, "ToneProducer::FormatProposal returning B_MEDIA_BAD_SOURCE\n");
24011bb5731Sbeveloper 		return B_MEDIA_BAD_SOURCE;
24111bb5731Sbeveloper 	}
24211bb5731Sbeveloper 
24311bb5731Sbeveloper 	// we only support floating-point raw audio, so we always return that, but we
24411bb5731Sbeveloper 	// supply an error code depending on whether we found the proposal acceptable.
24511bb5731Sbeveloper 
24611bb5731Sbeveloper 	media_type requestedType = format->type;
24711bb5731Sbeveloper 	*format = mPreferredFormat;
24811bb5731Sbeveloper 	if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO))
24911bb5731Sbeveloper 	{
25011bb5731Sbeveloper 		FPRINTF(stderr, "ToneProducer::FormatProposal returning B_MEDIA_BAD_FORMAT\n");
25111bb5731Sbeveloper 		return B_MEDIA_BAD_FORMAT;
25211bb5731Sbeveloper 	}
25311bb5731Sbeveloper 	else return B_OK;		// raw audio or wildcard type, either is okay by us
25411bb5731Sbeveloper }
25511bb5731Sbeveloper 
25611bb5731Sbeveloper status_t
FormatChangeRequested(const media_source & source,const media_destination & destination,media_format * io_format,int32 * _deprecated_)25711bb5731Sbeveloper ToneProducer::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_)
25811bb5731Sbeveloper {
25911bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::FormatChangeRequested\n");
26011bb5731Sbeveloper 
26111bb5731Sbeveloper 	// we don't support any other formats, so we just reject any format changes.
26211bb5731Sbeveloper 	return B_ERROR;
26311bb5731Sbeveloper }
26411bb5731Sbeveloper 
26511bb5731Sbeveloper status_t
GetNextOutput(int32 * cookie,media_output * out_output)26611bb5731Sbeveloper ToneProducer::GetNextOutput(int32* cookie, media_output* out_output)
26711bb5731Sbeveloper {
26811bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::GetNextOutput\n");
26911bb5731Sbeveloper 
27011bb5731Sbeveloper 	// we have only a single output; if we supported multiple outputs, we'd
27111bb5731Sbeveloper 	// iterate over whatever data structure we were using to keep track of
27211bb5731Sbeveloper 	// them.
27311bb5731Sbeveloper 	if (0 == *cookie)
27411bb5731Sbeveloper 	{
27511bb5731Sbeveloper 		*out_output = mOutput;
27611bb5731Sbeveloper 		*cookie += 1;
27711bb5731Sbeveloper 		return B_OK;
27811bb5731Sbeveloper 	}
27911bb5731Sbeveloper 	else return B_BAD_INDEX;
28011bb5731Sbeveloper }
28111bb5731Sbeveloper 
28211bb5731Sbeveloper status_t
DisposeOutputCookie(int32 cookie)28311bb5731Sbeveloper ToneProducer::DisposeOutputCookie(int32 cookie)
28411bb5731Sbeveloper {
28511bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::DisposeOutputCookie\n");
28611bb5731Sbeveloper 
28711bb5731Sbeveloper 	// do nothing because we don't use the cookie for anything special
28811bb5731Sbeveloper 	return B_OK;
28911bb5731Sbeveloper }
29011bb5731Sbeveloper 
29111bb5731Sbeveloper status_t
SetBufferGroup(const media_source & for_source,BBufferGroup * newGroup)29211bb5731Sbeveloper ToneProducer::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup)
29311bb5731Sbeveloper {
29411bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::SetBufferGroup\n");
29511bb5731Sbeveloper 
29611bb5731Sbeveloper 	// verify that we didn't get bogus arguments before we proceed
29711bb5731Sbeveloper 	if (for_source != mOutput.source) return B_MEDIA_BAD_SOURCE;
29811bb5731Sbeveloper 
29911bb5731Sbeveloper 	// Are we being passed the buffer group we're already using?
30011bb5731Sbeveloper 	if (newGroup == mBufferGroup) return B_OK;
30111bb5731Sbeveloper 
30211bb5731Sbeveloper 	// Ahh, someone wants us to use a different buffer group.  At this point we delete
30311bb5731Sbeveloper 	// the one we are using and use the specified one instead.  If the specified group is
30411bb5731Sbeveloper 	// NULL, we need to recreate one ourselves, and use *that*.  Note that if we're
30511bb5731Sbeveloper 	// caching a BBuffer that we requested earlier, we have to Recycle() that buffer
30611bb5731Sbeveloper 	// *before* deleting the buffer group, otherwise we'll deadlock waiting for that
30711bb5731Sbeveloper 	// buffer to be recycled!
30811bb5731Sbeveloper 	delete mBufferGroup;		// waits for all buffers to recycle
30911bb5731Sbeveloper 	if (newGroup != NULL)
31011bb5731Sbeveloper 	{
31111bb5731Sbeveloper 		// we were given a valid group; just use that one from now on
31211bb5731Sbeveloper 		mBufferGroup = newGroup;
31311bb5731Sbeveloper 	}
31411bb5731Sbeveloper 	else
31511bb5731Sbeveloper 	{
31611bb5731Sbeveloper 		// we were passed a NULL group pointer; that means we construct
31711bb5731Sbeveloper 		// our own buffer group to use from now on
31811bb5731Sbeveloper 		size_t size = mOutput.format.u.raw_audio.buffer_size;
31911bb5731Sbeveloper 		int32 count = int32(mLatency / BufferDuration() + 1 + 1);
32011bb5731Sbeveloper 		mBufferGroup = new BBufferGroup(size, count);
32111bb5731Sbeveloper 	}
32211bb5731Sbeveloper 
32311bb5731Sbeveloper 	return B_OK;
32411bb5731Sbeveloper }
32511bb5731Sbeveloper 
32611bb5731Sbeveloper status_t
GetLatency(bigtime_t * out_latency)32711bb5731Sbeveloper ToneProducer::GetLatency(bigtime_t* out_latency)
32811bb5731Sbeveloper {
32911bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::GetLatency\n");
33011bb5731Sbeveloper 
33111bb5731Sbeveloper 	// report our *total* latency:  internal plus downstream plus scheduling
33211bb5731Sbeveloper 	*out_latency = EventLatency() + SchedulingLatency();
33311bb5731Sbeveloper 	return B_OK;
33411bb5731Sbeveloper }
33511bb5731Sbeveloper 
33611bb5731Sbeveloper status_t
PrepareToConnect(const media_source & what,const media_destination & where,media_format * format,media_source * out_source,char * out_name)33711bb5731Sbeveloper ToneProducer::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name)
33811bb5731Sbeveloper {
33911bb5731Sbeveloper 	// PrepareToConnect() is the second stage of format negotiations that happens
34011bb5731Sbeveloper 	// inside BMediaRoster::Connect().  At this point, the consumer's AcceptFormat()
34111bb5731Sbeveloper 	// method has been called, and that node has potentially changed the proposed
34211bb5731Sbeveloper 	// format.  It may also have left wildcards in the format.  PrepareToConnect()
34311bb5731Sbeveloper 	// *must* fully specialize the format before returning!
34411bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::PrepareToConnect\n");
34511bb5731Sbeveloper 
34611bb5731Sbeveloper 	// trying to connect something that isn't our source?
34711bb5731Sbeveloper 	if (what != mOutput.source) return B_MEDIA_BAD_SOURCE;
34811bb5731Sbeveloper 
34911bb5731Sbeveloper 	// are we already connected?
35011bb5731Sbeveloper 	if (mOutput.destination != media_destination::null) return B_MEDIA_ALREADY_CONNECTED;
35111bb5731Sbeveloper 
35211bb5731Sbeveloper 	// the format may not yet be fully specialized (the consumer might have
35311bb5731Sbeveloper 	// passed back some wildcards).  Finish specializing it now, and return an
35411bb5731Sbeveloper 	// error if we don't support the requested format.
35511bb5731Sbeveloper 	if (format->type != B_MEDIA_RAW_AUDIO)
35611bb5731Sbeveloper 	{
35711bb5731Sbeveloper 		FPRINTF(stderr, "\tnon-raw-audio format?!\n");
35811bb5731Sbeveloper 		return B_MEDIA_BAD_FORMAT;
35911bb5731Sbeveloper 	}
36011bb5731Sbeveloper 	else if (format->u.raw_audio.format != media_raw_audio_format::B_AUDIO_FLOAT)
36111bb5731Sbeveloper 	{
36211bb5731Sbeveloper 		FPRINTF(stderr, "\tnon-float-audio format?!\n");
36311bb5731Sbeveloper 		return B_MEDIA_BAD_FORMAT;
36411bb5731Sbeveloper 	}
36511bb5731Sbeveloper 	else if(format->u.raw_audio.channel_count > 2) {
36611bb5731Sbeveloper 		format->u.raw_audio.channel_count = 2;
36711bb5731Sbeveloper 		return B_MEDIA_BAD_FORMAT;
36811bb5731Sbeveloper 	}
36911bb5731Sbeveloper 
37011bb5731Sbeveloper 
37111bb5731Sbeveloper 	 // !!! validate all other fields except for buffer_size here, because the consumer might have
37211bb5731Sbeveloper 	// supplied different values from AcceptFormat()?
37311bb5731Sbeveloper 
37411bb5731Sbeveloper 	// ***   e.moon [11jun99]: filling in sensible field values.
37511bb5731Sbeveloper 	//       Connect() doesn't take kindly to a frame_rate of 0.
37611bb5731Sbeveloper 
37711bb5731Sbeveloper 	if(format->u.raw_audio.frame_rate == media_raw_audio_format::wildcard.frame_rate) {
378*b1d006d7SDario Casalinuovo 		format->u.raw_audio.frame_rate = 44100.0f;
37911bb5731Sbeveloper 		FPRINTF(stderr, "\tno frame rate provided, suggesting %.1f\n", format->u.raw_audio.frame_rate);
38011bb5731Sbeveloper 	}
38111bb5731Sbeveloper 	if(format->u.raw_audio.channel_count == media_raw_audio_format::wildcard.channel_count) {
38211bb5731Sbeveloper 		//format->u.raw_audio.channel_count = mPreferredFormat.u.raw_audio.channel_count;
38311bb5731Sbeveloper 		format->u.raw_audio.channel_count = 1;
3841a7bcf69SOliver Tappe 		FPRINTF(stderr, "\tno channel count provided, suggesting %" B_PRIu32 "\n", format->u.raw_audio.channel_count);
38511bb5731Sbeveloper 	}
38611bb5731Sbeveloper 	if(format->u.raw_audio.byte_order == media_raw_audio_format::wildcard.byte_order) {
38711bb5731Sbeveloper 		format->u.raw_audio.byte_order = mPreferredFormat.u.raw_audio.byte_order;
38811bb5731Sbeveloper 		FPRINTF(stderr, "\tno channel count provided, suggesting %s\n",
38911bb5731Sbeveloper 			(format->u.raw_audio.byte_order == B_MEDIA_BIG_ENDIAN) ? "B_MEDIA_BIG_ENDIAN" : "B_MEDIA_LITTLE_ENDIAN");
39011bb5731Sbeveloper 	}
39111bb5731Sbeveloper 
39211bb5731Sbeveloper 	// check the buffer size, which may still be wildcarded
39311bb5731Sbeveloper 	if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size)
39411bb5731Sbeveloper 	{
39511bb5731Sbeveloper 		format->u.raw_audio.buffer_size = 2048;		// pick something comfortable to suggest
39611bb5731Sbeveloper 		FPRINTF(stderr, "\tno buffer size provided, suggesting %lu\n", format->u.raw_audio.buffer_size);
39711bb5731Sbeveloper 	}
39811bb5731Sbeveloper 	else
39911bb5731Sbeveloper 	{
40011bb5731Sbeveloper 		FPRINTF(stderr, "\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size);
40111bb5731Sbeveloper 	}
40211bb5731Sbeveloper 
40311bb5731Sbeveloper 	// Now reserve the connection, and return information about it
40411bb5731Sbeveloper 	mOutput.destination = where;
40511bb5731Sbeveloper 	mOutput.format = *format;
40611bb5731Sbeveloper 	*out_source = mOutput.source;
40711bb5731Sbeveloper 	strncpy(out_name, mOutput.name, B_MEDIA_NAME_LENGTH);
40811bb5731Sbeveloper 
40911bb5731Sbeveloper 	char formatStr[256];
41011bb5731Sbeveloper 	string_for_format(*format, formatStr, 255);
41111bb5731Sbeveloper 	FPRINTF(stderr, "\treturning format: %s\n", formatStr);
41211bb5731Sbeveloper 
41311bb5731Sbeveloper 	return B_OK;
41411bb5731Sbeveloper }
41511bb5731Sbeveloper 
41611bb5731Sbeveloper void
Connect(status_t error,const media_source & source,const media_destination & destination,const media_format & format,char * io_name)41711bb5731Sbeveloper ToneProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name)
41811bb5731Sbeveloper {
41911bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::Connect\n");
42011bb5731Sbeveloper 
42111bb5731Sbeveloper 	// If something earlier failed, Connect() might still be called, but with a non-zero
42211bb5731Sbeveloper 	// error code.  When that happens we simply unreserve the connection and do
42311bb5731Sbeveloper 	// nothing else.
42411bb5731Sbeveloper 	if (error)
42511bb5731Sbeveloper 	{
42611bb5731Sbeveloper 		mOutput.destination = media_destination::null;
42711bb5731Sbeveloper 		mOutput.format = mPreferredFormat;
42811bb5731Sbeveloper 		return;
42911bb5731Sbeveloper 	}
43011bb5731Sbeveloper 
43111bb5731Sbeveloper // old workaround for format bug: Connect() receives the format data from the
43211bb5731Sbeveloper // input returned from BBufferConsumer::Connected().
43311bb5731Sbeveloper //
43411bb5731Sbeveloper //	char formatStr[256];
43511bb5731Sbeveloper //	string_for_format(format, formatStr, 255);
43611bb5731Sbeveloper //	FPRINTF(stderr, "\trequested format: %s\n", formatStr);
43711bb5731Sbeveloper //	if(format.type != B_MEDIA_RAW_AUDIO) {
43811bb5731Sbeveloper //		// +++++ this is NOT proper behavior
43911bb5731Sbeveloper //		//       but it works
44011bb5731Sbeveloper //		FPRINTF(stderr, "\tcorrupted format; falling back to last suggested format\n");
44111bb5731Sbeveloper //		format = mOutput.format;
44211bb5731Sbeveloper //	}
44311bb5731Sbeveloper //
44411bb5731Sbeveloper 
44511bb5731Sbeveloper 	// Okay, the connection has been confirmed.  Record the destination and format
44611bb5731Sbeveloper 	// that we agreed on, and report our connection name again.
44711bb5731Sbeveloper 	mOutput.destination = destination;
44811bb5731Sbeveloper 	mOutput.format = format;
44911bb5731Sbeveloper 	strncpy(io_name, mOutput.name, B_MEDIA_NAME_LENGTH);
45011bb5731Sbeveloper 
45111bb5731Sbeveloper 	// Now that we're connected, we can determine our downstream latency.
45211bb5731Sbeveloper 	// Do so, then make sure we get our events early enough.
45311bb5731Sbeveloper 	media_node_id id;
45411bb5731Sbeveloper 	FindLatencyFor(mOutput.destination, &mLatency, &id);
4551a7bcf69SOliver Tappe 	FPRINTF(stderr, "\tdownstream latency = %" B_PRIdBIGTIME "\n", mLatency);
45611bb5731Sbeveloper 
45711bb5731Sbeveloper 	// Use a dry run to see how long it takes me to fill a buffer of data
45811bb5731Sbeveloper 	bigtime_t start, produceLatency;
45911bb5731Sbeveloper 	size_t samplesPerBuffer = mOutput.format.u.raw_audio.buffer_size / sizeof(float);
46011bb5731Sbeveloper 	size_t framesPerBuffer = samplesPerBuffer / mOutput.format.u.raw_audio.channel_count;
46111bb5731Sbeveloper 	float* data = new float[samplesPerBuffer];
46211bb5731Sbeveloper 	mTheta = 0;
46311bb5731Sbeveloper 	start = ::system_time();
46411bb5731Sbeveloper 	FillSineBuffer(data, framesPerBuffer, mOutput.format.u.raw_audio.channel_count==2);
46511bb5731Sbeveloper 	produceLatency = ::system_time();
46611bb5731Sbeveloper 	mInternalLatency = produceLatency - start;
46711bb5731Sbeveloper 
46811bb5731Sbeveloper 	// +++++ e.moon [13jun99]: fiddling with latency, ick
46911bb5731Sbeveloper 	mInternalLatency += 20000LL;
47011bb5731Sbeveloper 
47111bb5731Sbeveloper 	delete [] data;
4721a7bcf69SOliver Tappe 	FPRINTF(stderr, "\tbuffer-filling took %" B_PRIdBIGTIME
4731a7bcf69SOliver Tappe 			" usec on this machine\n", mInternalLatency);
47411bb5731Sbeveloper 	SetEventLatency(mLatency + mInternalLatency);
47511bb5731Sbeveloper 
47611bb5731Sbeveloper 	// reset our buffer duration, etc. to avoid later calculations
47711bb5731Sbeveloper 	// +++++ e.moon 11jun99: crashes w/ divide-by-zero when connecting to LoggingConsumer
47811bb5731Sbeveloper 	ASSERT(mOutput.format.u.raw_audio.frame_rate);
47911bb5731Sbeveloper 
48011bb5731Sbeveloper 	bigtime_t duration = bigtime_t(1000000) * samplesPerBuffer / bigtime_t(mOutput.format.u.raw_audio.frame_rate);
48111bb5731Sbeveloper 	SetBufferDuration(duration);
48211bb5731Sbeveloper 
48311bb5731Sbeveloper 	// Set up the buffer group for our connection, as long as nobody handed us a
48411bb5731Sbeveloper 	// buffer group (via SetBufferGroup()) prior to this.  That can happen, for example,
48511bb5731Sbeveloper 	// if the consumer calls SetOutputBuffersFor() on us from within its Connected()
48611bb5731Sbeveloper 	// method.
48711bb5731Sbeveloper 	if (!mBufferGroup) AllocateBuffers();
48811bb5731Sbeveloper }
48911bb5731Sbeveloper 
49011bb5731Sbeveloper void
Disconnect(const media_source & what,const media_destination & where)49111bb5731Sbeveloper ToneProducer::Disconnect(const media_source& what, const media_destination& where)
49211bb5731Sbeveloper {
49311bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::Disconnect\n");
49411bb5731Sbeveloper 
49511bb5731Sbeveloper 	// Make sure that our connection is the one being disconnected
49611bb5731Sbeveloper 	if ((where == mOutput.destination) && (what == mOutput.source))
49711bb5731Sbeveloper 	{
49811bb5731Sbeveloper 		mOutput.destination = media_destination::null;
49911bb5731Sbeveloper 		mOutput.format = mPreferredFormat;
50011bb5731Sbeveloper 		delete mBufferGroup;
50111bb5731Sbeveloper 		mBufferGroup = NULL;
50211bb5731Sbeveloper 	}
50311bb5731Sbeveloper 	else
50411bb5731Sbeveloper 	{
5051a7bcf69SOliver Tappe 		FPRINTF(stderr, "\tDisconnect() called with wrong source/destination (%"
5061a7bcf69SOliver Tappe 				B_PRId32 "/%" B_PRId32 "), ours is (%" B_PRId32 "/%" B_PRId32 ")\n",
50711bb5731Sbeveloper 			what.id, where.id, mOutput.source.id, mOutput.destination.id);
50811bb5731Sbeveloper 	}
50911bb5731Sbeveloper }
51011bb5731Sbeveloper 
51111bb5731Sbeveloper void
LateNoticeReceived(const media_source & what,bigtime_t how_much,bigtime_t performance_time)51211bb5731Sbeveloper ToneProducer::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time)
51311bb5731Sbeveloper {
51411bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::LateNoticeReceived\n");
51511bb5731Sbeveloper 
51611bb5731Sbeveloper 	// If we're late, we need to catch up.  Respond in a manner appropriate to our
51711bb5731Sbeveloper 	// current run mode.
51811bb5731Sbeveloper 	if (what == mOutput.source)
51911bb5731Sbeveloper 	{
52011bb5731Sbeveloper 		if (RunMode() == B_RECORDING)
52111bb5731Sbeveloper 		{
52211bb5731Sbeveloper 			// A hardware capture node can't adjust; it simply emits buffers at
52311bb5731Sbeveloper 			// appropriate points.  We (partially) simulate this by not adjusting
52411bb5731Sbeveloper 			// our behavior upon receiving late notices -- after all, the hardware
52511bb5731Sbeveloper 			// can't choose to capture "sooner"....
52611bb5731Sbeveloper 		}
52711bb5731Sbeveloper 		else if (RunMode() == B_INCREASE_LATENCY)
52811bb5731Sbeveloper 		{
52911bb5731Sbeveloper 			// We're late, and our run mode dictates that we try to produce buffers
53011bb5731Sbeveloper 			// earlier in order to catch up.  This argues that the downstream nodes are
53111bb5731Sbeveloper 			// not properly reporting their latency, but there's not much we can do about
53211bb5731Sbeveloper 			// that at the moment, so we try to start producing buffers earlier to
53311bb5731Sbeveloper 			// compensate.
53411bb5731Sbeveloper 			mInternalLatency += how_much;
535b63f90a8Sbeveloper 			if (mInternalLatency > 50000)
536b63f90a8Sbeveloper 				mInternalLatency = 50000;
53711bb5731Sbeveloper 			SetEventLatency(mLatency + mInternalLatency);
53811bb5731Sbeveloper 
5391a7bcf69SOliver Tappe 			FPRINTF(stderr, "\tincreasing latency to %" B_PRIdBIGTIME "\n",
5401a7bcf69SOliver Tappe 				mLatency + mInternalLatency);
54111bb5731Sbeveloper 		}
54211bb5731Sbeveloper 		else
54311bb5731Sbeveloper 		{
54411bb5731Sbeveloper 			// The other run modes dictate various strategies for sacrificing data quality
54511bb5731Sbeveloper 			// in the interests of timely data delivery.  The way *we* do this is to skip
54611bb5731Sbeveloper 			// a buffer, which catches us up in time by one buffer duration.
54711bb5731Sbeveloper 			size_t nSamples = mOutput.format.u.raw_audio.buffer_size / sizeof(float);
54811bb5731Sbeveloper 			mFramesSent += nSamples;
54911bb5731Sbeveloper 
55011bb5731Sbeveloper 			FPRINTF(stderr, "\tskipping a buffer to try to catch up\n");
55111bb5731Sbeveloper 		}
55211bb5731Sbeveloper 	}
55311bb5731Sbeveloper }
55411bb5731Sbeveloper 
55511bb5731Sbeveloper void
EnableOutput(const media_source & what,bool enabled,int32 * _deprecated_)55611bb5731Sbeveloper ToneProducer::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_)
55711bb5731Sbeveloper {
55811bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::EnableOutput\n");
55911bb5731Sbeveloper 
56011bb5731Sbeveloper 	// If I had more than one output, I'd have to walk my list of output records to see
56111bb5731Sbeveloper 	// which one matched the given source, and then enable/disable that one.  But this
56211bb5731Sbeveloper 	// node only has one output, so I just make sure the given source matches, then set
56311bb5731Sbeveloper 	// the enable state accordingly.
56411bb5731Sbeveloper 	if (what == mOutput.source)
56511bb5731Sbeveloper 	{
56611bb5731Sbeveloper 		mOutputEnabled = enabled;
56711bb5731Sbeveloper 	}
56811bb5731Sbeveloper }
56911bb5731Sbeveloper 
57011bb5731Sbeveloper status_t
SetPlayRate(int32 numer,int32 denom)57111bb5731Sbeveloper ToneProducer::SetPlayRate(int32 numer, int32 denom)
57211bb5731Sbeveloper {
57311bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::SetPlayRate\n");
57411bb5731Sbeveloper 
57511bb5731Sbeveloper 	// Play rates are weird.  We don't support them.  Maybe we will in a
57611bb5731Sbeveloper 	// later newsletter article....
57711bb5731Sbeveloper 	return B_ERROR;
57811bb5731Sbeveloper }
57911bb5731Sbeveloper 
58011bb5731Sbeveloper status_t
HandleMessage(int32 message,const void * data,size_t size)58111bb5731Sbeveloper ToneProducer::HandleMessage(int32 message, const void* data, size_t size)
58211bb5731Sbeveloper {
5831a7bcf69SOliver Tappe 	FPRINTF(stderr, "ToneProducer::HandleMessage(%" B_PRId32 " = 0x%" B_PRIx32
5841a7bcf69SOliver Tappe 		")\n", message, message);
58511bb5731Sbeveloper 	// HandleMessage() is where you implement any private message protocols
58611bb5731Sbeveloper 	// that you want to use.  When messages are written to your node's control
58711bb5731Sbeveloper 	// port that are not recognized by any of the node superclasses, they'll be
58811bb5731Sbeveloper 	// passed to this method in your node's implementation for handling.  The
58911bb5731Sbeveloper 	// ToneProducer node doesn't support any private messages, so we just
59011bb5731Sbeveloper 	// return an error, indicating that the message wasn't handled.
59111bb5731Sbeveloper 	return B_ERROR;
59211bb5731Sbeveloper }
59311bb5731Sbeveloper 
59411bb5731Sbeveloper void
AdditionalBufferRequested(const media_source & source,media_buffer_id prev_buffer,bigtime_t prev_time,const media_seek_tag * prev_tag)59511bb5731Sbeveloper ToneProducer::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag)
59611bb5731Sbeveloper {
59711bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::AdditionalBufferRequested\n");
59811bb5731Sbeveloper 
59911bb5731Sbeveloper 	// we don't support offline mode (yet...)
60011bb5731Sbeveloper 	return;
60111bb5731Sbeveloper }
60211bb5731Sbeveloper 
60311bb5731Sbeveloper void
LatencyChanged(const media_source & source,const media_destination & destination,bigtime_t new_latency,uint32 flags)60411bb5731Sbeveloper ToneProducer::LatencyChanged(
60511bb5731Sbeveloper 	const media_source& source,
60611bb5731Sbeveloper 	const media_destination& destination,
60711bb5731Sbeveloper 	bigtime_t new_latency,
60811bb5731Sbeveloper 	uint32 flags)
60911bb5731Sbeveloper {
6101a7bcf69SOliver Tappe 	PRINT(("ToneProducer::LatencyChanged(): %" B_PRIdBIGTIME "\n",
6111a7bcf69SOliver Tappe 		new_latency));
61211bb5731Sbeveloper 
61311bb5731Sbeveloper 	// something downstream changed latency, so we need to start producing
61411bb5731Sbeveloper 	// buffers earlier (or later) than we were previously.  Make sure that the
61511bb5731Sbeveloper 	// connection that changed is ours, and adjust to the new downstream
61611bb5731Sbeveloper 	// latency if so.
61711bb5731Sbeveloper 	if ((source == mOutput.source) && (destination == mOutput.destination))
61811bb5731Sbeveloper 	{
61911bb5731Sbeveloper 		mLatency = new_latency;
62011bb5731Sbeveloper 		SetEventLatency(mLatency + mInternalLatency);
62111bb5731Sbeveloper 	}
62211bb5731Sbeveloper }
62311bb5731Sbeveloper 
62411bb5731Sbeveloper //#pragma mark -
62511bb5731Sbeveloper 
62611bb5731Sbeveloper // BMediaEventLooper methods
62711bb5731Sbeveloper void
NodeRegistered()62811bb5731Sbeveloper ToneProducer::NodeRegistered()
62911bb5731Sbeveloper {
63011bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::NodeRegistered\n");
63111bb5731Sbeveloper 
63211bb5731Sbeveloper 	// output init moved to ctor
63311bb5731Sbeveloper 	// e.moon [4jun99]
63411bb5731Sbeveloper 
63511bb5731Sbeveloper 	// Set up our parameter web
63611bb5731Sbeveloper 	mWeb = make_parameter_web();
63711bb5731Sbeveloper 	SetParameterWeb(mWeb);
6386d2f2ec1SDario Casalinuovo 
6396d2f2ec1SDario Casalinuovo 	// Start the BMediaEventLooper thread
6406d2f2ec1SDario Casalinuovo 	SetPriority(B_REAL_TIME_PRIORITY);
6416d2f2ec1SDario Casalinuovo 	Run();
64211bb5731Sbeveloper }
64311bb5731Sbeveloper 
64411bb5731Sbeveloper void
Start(bigtime_t performance_time)64511bb5731Sbeveloper ToneProducer::Start(bigtime_t performance_time)
64611bb5731Sbeveloper {
6471a7bcf69SOliver Tappe 	PRINT(("ToneProducer::Start(%" B_PRIdBIGTIME "): now %" B_PRIdBIGTIME "\n",
6481a7bcf69SOliver Tappe 		performance_time, TimeSource()->Now()));
64911bb5731Sbeveloper 
65011bb5731Sbeveloper 	// send 'data available' message
65111bb5731Sbeveloper 	if(mOutput.destination != media_destination::null)
65211bb5731Sbeveloper 		SendDataStatus(B_DATA_AVAILABLE, mOutput.destination, performance_time);
65311bb5731Sbeveloper 
65411bb5731Sbeveloper 	// A bug in the current PowerPC compiler demands that we implement
65511bb5731Sbeveloper 	// this, even though it just calls up to the inherited implementation.
65611bb5731Sbeveloper 	BMediaEventLooper::Start(performance_time);
65711bb5731Sbeveloper }
65811bb5731Sbeveloper 
65911bb5731Sbeveloper void
Stop(bigtime_t performance_time,bool immediate)66011bb5731Sbeveloper ToneProducer::Stop(bigtime_t performance_time, bool immediate)
66111bb5731Sbeveloper {
66211bb5731Sbeveloper 	// send 'data not available' message
66311bb5731Sbeveloper 	if(mOutput.destination != media_destination::null) {
664e393a169SJérôme Duval 		printf("ToneProducer: B_PRODUCER_STOPPED at %" B_PRIdBIGTIME "\n",
665e393a169SJérôme Duval 			performance_time);
66611bb5731Sbeveloper 		SendDataStatus(B_PRODUCER_STOPPED, mOutput.destination, performance_time);
66711bb5731Sbeveloper 	}
66811bb5731Sbeveloper 
66911bb5731Sbeveloper 	// A bug in the current PowerPC compiler demands that we implement
67011bb5731Sbeveloper 	// this, even though it just calls up to the inherited implementation.
67111bb5731Sbeveloper 	BMediaEventLooper::Stop(performance_time, immediate);
67211bb5731Sbeveloper }
67311bb5731Sbeveloper 
67411bb5731Sbeveloper void
SetRunMode(run_mode mode)67511bb5731Sbeveloper ToneProducer::SetRunMode(run_mode mode)
67611bb5731Sbeveloper {
67711bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::SetRunMode\n");
67811bb5731Sbeveloper 
67911bb5731Sbeveloper 	// We don't support offline run mode, so broadcast an error if we're set to
68011bb5731Sbeveloper 	// B_OFFLINE.  Unfortunately, we can't actually reject the mode change...
68111bb5731Sbeveloper 	if (B_OFFLINE == mode)
68211bb5731Sbeveloper 	{
68311bb5731Sbeveloper 		ReportError(B_NODE_FAILED_SET_RUN_MODE);
68411bb5731Sbeveloper 	}
68511bb5731Sbeveloper }
68611bb5731Sbeveloper 
68711bb5731Sbeveloper void
HandleEvent(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)68811bb5731Sbeveloper ToneProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent)
68911bb5731Sbeveloper {
69011bb5731Sbeveloper //	FPRINTF(stderr, "ToneProducer::HandleEvent\n");
69111bb5731Sbeveloper 	switch (event->type)
69211bb5731Sbeveloper 	{
69311bb5731Sbeveloper 	case BTimedEventQueue::B_START:
69411bb5731Sbeveloper 		// don't do anything if we're already running
69511bb5731Sbeveloper 		if (RunState() != B_STARTED)
69611bb5731Sbeveloper 		{
69711bb5731Sbeveloper 			// We want to start sending buffers now, so we set up the buffer-sending bookkeeping
69811bb5731Sbeveloper 			// and fire off the first "produce a buffer" event.
69911bb5731Sbeveloper 			mFramesSent = 0;
70011bb5731Sbeveloper 			mTheta = 0;
70111bb5731Sbeveloper 			mStartTime = event->event_time;
70211bb5731Sbeveloper 			media_timed_event firstBufferEvent(mStartTime, BTimedEventQueue::B_HANDLE_BUFFER);
70311bb5731Sbeveloper 
70411bb5731Sbeveloper 			// Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through
70511bb5731Sbeveloper 			// the event queue, like this:
70611bb5731Sbeveloper 			//
70711bb5731Sbeveloper 			//		this->HandleEvent(&firstBufferEvent, 0, false);
70811bb5731Sbeveloper 			//
70911bb5731Sbeveloper 			EventQueue()->AddEvent(firstBufferEvent);
71011bb5731Sbeveloper 		}
71111bb5731Sbeveloper 		break;
71211bb5731Sbeveloper 
71311bb5731Sbeveloper 	case BTimedEventQueue::B_STOP:
71411bb5731Sbeveloper 		FPRINTF(stderr, "Handling B_STOP event\n");
71511bb5731Sbeveloper 
71611bb5731Sbeveloper 		// When we handle a stop, we must ensure that downstream consumers don't
71711bb5731Sbeveloper 		// get any more buffers from us.  This means we have to flush any pending
71811bb5731Sbeveloper 		// buffer-producing events from the queue.
71911bb5731Sbeveloper 		EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
72011bb5731Sbeveloper 		break;
72111bb5731Sbeveloper 
72211bb5731Sbeveloper 	case _PARAMETER_EVENT:
72311bb5731Sbeveloper 		{
72411bb5731Sbeveloper 			size_t dataSize = size_t(event->data);
72511bb5731Sbeveloper 			int32 param = int32(event->bigdata);
72611bb5731Sbeveloper 			if (dataSize >= sizeof(float)) switch (param)
72711bb5731Sbeveloper 			{
72811bb5731Sbeveloper 			case FREQUENCY_PARAM:
72911bb5731Sbeveloper 				{
73011bb5731Sbeveloper 					float newValue = *((float*) event->user_data);
73111bb5731Sbeveloper 					if (mFrequency != newValue)		// an actual change in the value?
73211bb5731Sbeveloper 					{
73311bb5731Sbeveloper 						mFrequency = newValue;
73411bb5731Sbeveloper 						mFreqLastChanged = TimeSource()->Now();
73511bb5731Sbeveloper 						BroadcastNewParameterValue(mFreqLastChanged, param, &mFrequency, sizeof(mFrequency));
73611bb5731Sbeveloper 					}
73711bb5731Sbeveloper 				}
73811bb5731Sbeveloper 				break;
73911bb5731Sbeveloper 
74011bb5731Sbeveloper 			case GAIN_PARAM:
74111bb5731Sbeveloper 				{
74211bb5731Sbeveloper 					float newValue = *((float*) event->user_data);
74311bb5731Sbeveloper 					if (mGain != newValue)
74411bb5731Sbeveloper 					{
74511bb5731Sbeveloper 						mGain = newValue;
74611bb5731Sbeveloper 						mGainLastChanged = TimeSource()->Now();
74711bb5731Sbeveloper 						BroadcastNewParameterValue(mGainLastChanged, param, &mGain, sizeof(mGain));
74811bb5731Sbeveloper 					}
74911bb5731Sbeveloper 				}
75011bb5731Sbeveloper 				break;
75111bb5731Sbeveloper 
75211bb5731Sbeveloper 			case WAVEFORM_PARAM:
75311bb5731Sbeveloper 				{
75411bb5731Sbeveloper 					int32 newValue = *((int32*) event->user_data);
75511bb5731Sbeveloper 					if (mWaveform != newValue)
75611bb5731Sbeveloper 					{
75711bb5731Sbeveloper 						mWaveform = newValue;
75811bb5731Sbeveloper 						mTheta = 0;			// reset the generator parameters when we change waveforms
75911bb5731Sbeveloper 						mWaveAscending = true;
76011bb5731Sbeveloper 						mWaveLastChanged = TimeSource()->Now();
76111bb5731Sbeveloper 						BroadcastNewParameterValue(mWaveLastChanged, param, &mWaveform, sizeof(mWaveform));
76211bb5731Sbeveloper 					}
76311bb5731Sbeveloper 				}
76411bb5731Sbeveloper 				break;
76511bb5731Sbeveloper 
76611bb5731Sbeveloper 			default:
7671a7bcf69SOliver Tappe 				FPRINTF(stderr, "Hmmm... got a B_PARAMETER event for a parameter we don't have? (%" B_PRId32 ")\n", param);
76811bb5731Sbeveloper 				break;
76911bb5731Sbeveloper 			}
77011bb5731Sbeveloper 		}
77111bb5731Sbeveloper 		break;
77211bb5731Sbeveloper 
77311bb5731Sbeveloper 	case BTimedEventQueue::B_HANDLE_BUFFER:
77411bb5731Sbeveloper 		{
77511bb5731Sbeveloper 			// make sure we're both started *and* connected before delivering a buffer
776b289aaf6SAxel Dörfler 			if (RunState() == BMediaEventLooper::B_STARTED
777b289aaf6SAxel Dörfler 				&& mOutput.destination != media_destination::null) {
77811bb5731Sbeveloper 				// Get the next buffer of data
77911bb5731Sbeveloper 				BBuffer* buffer = FillNextBuffer(event->event_time);
780b289aaf6SAxel Dörfler 				if (buffer) {
78111bb5731Sbeveloper 					// send the buffer downstream if and only if output is enabled
78211bb5731Sbeveloper 					status_t err = B_ERROR;
783b289aaf6SAxel Dörfler 					if (mOutputEnabled) {
784b289aaf6SAxel Dörfler 						err = SendBuffer(buffer, mOutput.source,
785b289aaf6SAxel Dörfler 							mOutput.destination);
786b289aaf6SAxel Dörfler 					}
787b289aaf6SAxel Dörfler 					if (err) {
78811bb5731Sbeveloper 						// we need to recycle the buffer ourselves if output is disabled or
78911bb5731Sbeveloper 						// if the call to SendBuffer() fails
79011bb5731Sbeveloper 						buffer->Recycle();
79111bb5731Sbeveloper 					}
79211bb5731Sbeveloper 				}
79311bb5731Sbeveloper 
79411bb5731Sbeveloper 				// track how much media we've delivered so far
79511bb5731Sbeveloper 				size_t nFrames = mOutput.format.u.raw_audio.buffer_size /
79611bb5731Sbeveloper 					(sizeof(float) * mOutput.format.u.raw_audio.channel_count);
79711bb5731Sbeveloper 				mFramesSent += nFrames;
79811bb5731Sbeveloper 
79911bb5731Sbeveloper 				// The buffer is on its way; now schedule the next one to go
800b289aaf6SAxel Dörfler 				bigtime_t nextEvent = mStartTime + bigtime_t(double(mFramesSent)
801b289aaf6SAxel Dörfler 					/ double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0);
802b289aaf6SAxel Dörfler 				media_timed_event nextBufferEvent(nextEvent,
803b289aaf6SAxel Dörfler 					BTimedEventQueue::B_HANDLE_BUFFER);
80411bb5731Sbeveloper 				EventQueue()->AddEvent(nextBufferEvent);
80511bb5731Sbeveloper 			}
80611bb5731Sbeveloper 		}
80711bb5731Sbeveloper 		break;
80811bb5731Sbeveloper 
80911bb5731Sbeveloper 	default:
81011bb5731Sbeveloper 		break;
81111bb5731Sbeveloper 	}
81211bb5731Sbeveloper }
81311bb5731Sbeveloper 
81411bb5731Sbeveloper //#pragma mark -
81511bb5731Sbeveloper 
81611bb5731Sbeveloper void
AllocateBuffers()81711bb5731Sbeveloper ToneProducer::AllocateBuffers()
81811bb5731Sbeveloper {
81911bb5731Sbeveloper 	FPRINTF(stderr, "ToneProducer::AllocateBuffers\n");
82011bb5731Sbeveloper 
82111bb5731Sbeveloper 	// allocate enough buffers to span our downstream latency, plus one
82211bb5731Sbeveloper 	size_t size = mOutput.format.u.raw_audio.buffer_size;
82311bb5731Sbeveloper 	int32 count = int32(mLatency / BufferDuration() + 1 + 1);
82411bb5731Sbeveloper 
8251a7bcf69SOliver Tappe 	FPRINTF(stderr, "\tlatency = %" B_PRIdBIGTIME ", buffer duration = %"
8261a7bcf69SOliver Tappe 			B_PRIdBIGTIME "\n", mLatency, BufferDuration());
8271a7bcf69SOliver Tappe 	FPRINTF(stderr, "\tcreating group of %" B_PRId32 " buffers, size = %"
8281a7bcf69SOliver Tappe 			B_PRIuSIZE "\n", count, size);
82911bb5731Sbeveloper 	mBufferGroup = new BBufferGroup(size, count);
83011bb5731Sbeveloper }
83111bb5731Sbeveloper 
83211bb5731Sbeveloper BBuffer*
FillNextBuffer(bigtime_t event_time)83311bb5731Sbeveloper ToneProducer::FillNextBuffer(bigtime_t event_time)
83411bb5731Sbeveloper {
83511bb5731Sbeveloper 	// get a buffer from our buffer group
83611bb5731Sbeveloper 	BBuffer* buf = mBufferGroup->RequestBuffer(mOutput.format.u.raw_audio.buffer_size, BufferDuration());
83711bb5731Sbeveloper 
83811bb5731Sbeveloper 	// if we fail to get a buffer (for example, if the request times out), we skip this
83911bb5731Sbeveloper 	// buffer and go on to the next, to avoid locking up the control thread
84011bb5731Sbeveloper 	if (!buf)
84111bb5731Sbeveloper 	{
84211bb5731Sbeveloper 		return NULL;
84311bb5731Sbeveloper 	}
84411bb5731Sbeveloper 
84511bb5731Sbeveloper 	// now fill it with data, continuing where the last buffer left off
84611bb5731Sbeveloper 	// 20sep99: multichannel support
84711bb5731Sbeveloper 
84811bb5731Sbeveloper 	size_t numFrames =
84911bb5731Sbeveloper 		mOutput.format.u.raw_audio.buffer_size /
85011bb5731Sbeveloper 		(sizeof(float)*mOutput.format.u.raw_audio.channel_count);
85111bb5731Sbeveloper 	bool stereo = (mOutput.format.u.raw_audio.channel_count == 2);
85211bb5731Sbeveloper 	if(!stereo) {
85311bb5731Sbeveloper 		ASSERT(mOutput.format.u.raw_audio.channel_count == 1);
85411bb5731Sbeveloper 	}
85511bb5731Sbeveloper //	PRINT(("buffer: %ld, %ld frames, %s\n", mOutput.format.u.raw_audio.buffer_size, numFrames, stereo ? "stereo" : "mono"));
85611bb5731Sbeveloper 
85711bb5731Sbeveloper 	float* data = (float*) buf->Data();
85811bb5731Sbeveloper 
85911bb5731Sbeveloper 	switch (mWaveform)
86011bb5731Sbeveloper 	{
86111bb5731Sbeveloper 	case SINE_WAVE:
86211bb5731Sbeveloper 		FillSineBuffer(data, numFrames, stereo);
86311bb5731Sbeveloper 		break;
86411bb5731Sbeveloper 
86511bb5731Sbeveloper 	case TRIANGLE_WAVE:
86611bb5731Sbeveloper 		FillTriangleBuffer(data, numFrames, stereo);
86711bb5731Sbeveloper 		break;
86811bb5731Sbeveloper 
86911bb5731Sbeveloper 	case SAWTOOTH_WAVE:
87011bb5731Sbeveloper 		FillSawtoothBuffer(data, numFrames, stereo);
87111bb5731Sbeveloper 		break;
87211bb5731Sbeveloper 	}
87311bb5731Sbeveloper 
87411bb5731Sbeveloper 	// fill in the buffer header
87511bb5731Sbeveloper 	media_header* hdr = buf->Header();
87611bb5731Sbeveloper 	hdr->type = B_MEDIA_RAW_AUDIO;
87711bb5731Sbeveloper 	hdr->size_used = mOutput.format.u.raw_audio.buffer_size;
87811bb5731Sbeveloper 	hdr->time_source = TimeSource()->ID();
87911bb5731Sbeveloper 
88011bb5731Sbeveloper 	bigtime_t stamp;
88111bb5731Sbeveloper 	if (RunMode() == B_RECORDING)
88211bb5731Sbeveloper 	{
88311bb5731Sbeveloper 		// In B_RECORDING mode, we stamp with the capture time.  We're not
88411bb5731Sbeveloper 		// really a hardware capture node, but we simulate it by using the (precalculated)
88511bb5731Sbeveloper 		// time at which this buffer "should" have been created.
88611bb5731Sbeveloper 		stamp = event_time;
88711bb5731Sbeveloper 	}
88811bb5731Sbeveloper 	else
88911bb5731Sbeveloper 	{
89011bb5731Sbeveloper 		// okay, we're in one of the "live" performance run modes.  in these modes, we
89111bb5731Sbeveloper 		// stamp the buffer with the time at which the buffer should be rendered to the
89211bb5731Sbeveloper 		// output, not with the capture time.  mStartTime is the cached value of the
89311bb5731Sbeveloper 		// first buffer's performance time; we calculate this buffer's performance time as
89411bb5731Sbeveloper 		// an offset from that time, based on the amount of media we've created so far.
89511bb5731Sbeveloper 		// Recalculating every buffer like this avoids accumulation of error.
89611bb5731Sbeveloper 		stamp = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0);
89711bb5731Sbeveloper 	}
89811bb5731Sbeveloper 	hdr->start_time = stamp;
89911bb5731Sbeveloper 
90011bb5731Sbeveloper 	return buf;
90111bb5731Sbeveloper }
90211bb5731Sbeveloper 
90311bb5731Sbeveloper // waveform generators - fill buffers with various waveforms
90411bb5731Sbeveloper void
FillSineBuffer(float * data,size_t numFrames,bool stereo)90511bb5731Sbeveloper ToneProducer::FillSineBuffer(float *data, size_t numFrames, bool stereo)
90611bb5731Sbeveloper {
90711bb5731Sbeveloper 
90811bb5731Sbeveloper 
90911bb5731Sbeveloper 	// cover 2pi radians in one period
91011bb5731Sbeveloper 	double dTheta = 2*M_PI * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate;
91111bb5731Sbeveloper 
91211bb5731Sbeveloper 	// Fill the buffer!
91311bb5731Sbeveloper 	for (size_t i = 0; i < numFrames; i++, data++)
91411bb5731Sbeveloper 	{
91511bb5731Sbeveloper 		float val = mGain * float(sin(mTheta));
91611bb5731Sbeveloper 		*data = val;
91711bb5731Sbeveloper 		if(stereo) {
91811bb5731Sbeveloper 			++data;
91911bb5731Sbeveloper 			*data = val;
92011bb5731Sbeveloper 		}
92111bb5731Sbeveloper 
92211bb5731Sbeveloper 		mTheta += dTheta;
92311bb5731Sbeveloper 		if (mTheta > 2*M_PI)
92411bb5731Sbeveloper 		{
92511bb5731Sbeveloper 			mTheta -= 2*M_PI;
92611bb5731Sbeveloper 		}
92711bb5731Sbeveloper 	}
92811bb5731Sbeveloper }
92911bb5731Sbeveloper 
93011bb5731Sbeveloper void
FillTriangleBuffer(float * data,size_t numFrames,bool stereo)93111bb5731Sbeveloper ToneProducer::FillTriangleBuffer(float *data, size_t numFrames, bool stereo)
93211bb5731Sbeveloper {
93311bb5731Sbeveloper 	// ramp from -1 to 1 and back in one period
93411bb5731Sbeveloper 	double dTheta = 4.0 * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate;
93511bb5731Sbeveloper 	if (!mWaveAscending) dTheta = -dTheta;
93611bb5731Sbeveloper 
93711bb5731Sbeveloper 	// fill the buffer!
93811bb5731Sbeveloper 	for (size_t i = 0; i < numFrames; i++, data++)
93911bb5731Sbeveloper 	{
94011bb5731Sbeveloper 		float val = mGain * mTheta;
94111bb5731Sbeveloper 		*data = val;
94211bb5731Sbeveloper 		if(stereo) {
94311bb5731Sbeveloper 			++data;
94411bb5731Sbeveloper 			*data = val;
94511bb5731Sbeveloper 		}
94611bb5731Sbeveloper 
94711bb5731Sbeveloper 		mTheta += dTheta;
94811bb5731Sbeveloper 		if (mTheta >= 1)
94911bb5731Sbeveloper 		{
95011bb5731Sbeveloper 			mTheta = 2 - mTheta;		// reflect across the mTheta=1 line to preserve drift
95111bb5731Sbeveloper 			mWaveAscending = false;
95211bb5731Sbeveloper 			dTheta = -dTheta;
95311bb5731Sbeveloper 		}
95411bb5731Sbeveloper 		else if (mTheta <= -1)
95511bb5731Sbeveloper 		{
95611bb5731Sbeveloper 			mTheta = -2 - mTheta;		// reflect across mTheta=-1
95711bb5731Sbeveloper 			mWaveAscending = true;
95811bb5731Sbeveloper 			dTheta = -dTheta;
95911bb5731Sbeveloper 		}
96011bb5731Sbeveloper 	}
96111bb5731Sbeveloper }
96211bb5731Sbeveloper 
96311bb5731Sbeveloper void
FillSawtoothBuffer(float * data,size_t numFrames,bool stereo)96411bb5731Sbeveloper ToneProducer::FillSawtoothBuffer(float *data, size_t numFrames, bool stereo)
96511bb5731Sbeveloper {
96611bb5731Sbeveloper 	// ramp from -1 to 1 in one period
96711bb5731Sbeveloper 	double dTheta = 2 * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate;
96811bb5731Sbeveloper 	mWaveAscending = true;
96911bb5731Sbeveloper 
97011bb5731Sbeveloper 	// fill the buffer!
97111bb5731Sbeveloper 	for (size_t i = 0; i < numFrames; i++, data++)
97211bb5731Sbeveloper 	{
97311bb5731Sbeveloper 		float val = mGain * mTheta;
97411bb5731Sbeveloper 		*data = val;
97511bb5731Sbeveloper 		if(stereo) {
97611bb5731Sbeveloper 			++data;
97711bb5731Sbeveloper 			*data = val;
97811bb5731Sbeveloper 		}
97911bb5731Sbeveloper 
98011bb5731Sbeveloper 		mTheta += dTheta;
98111bb5731Sbeveloper 		if (mTheta > 1)
98211bb5731Sbeveloper 		{
98311bb5731Sbeveloper 			mTheta -= 2;		// back to the base of the sawtooth, including cumulative drift
98411bb5731Sbeveloper 		}
98511bb5731Sbeveloper 	}
98611bb5731Sbeveloper }
98711bb5731Sbeveloper 
98811bb5731Sbeveloper // utility - build the ToneProducer's parameter web
make_parameter_web()98911bb5731Sbeveloper static BParameterWeb* make_parameter_web()
99011bb5731Sbeveloper {
99111bb5731Sbeveloper 	FPRINTF(stderr, "make_parameter_web() called\n");
99211bb5731Sbeveloper 
99311bb5731Sbeveloper 	BParameterWeb* web = new BParameterWeb;
99411bb5731Sbeveloper 	BParameterGroup* mainGroup = web->MakeGroup("Tone Generator Parameters");
99511bb5731Sbeveloper 
99611bb5731Sbeveloper 	BParameterGroup* group = mainGroup->MakeGroup("Frequency");
99711bb5731Sbeveloper 	BParameter* nullParam = group->MakeNullParameter(FREQUENCY_NULL_PARAM, B_MEDIA_NO_TYPE, "Frequency", B_GENERIC);
99811bb5731Sbeveloper 	BContinuousParameter* param = group->MakeContinuousParameter(FREQUENCY_PARAM, B_MEDIA_NO_TYPE, "", B_GAIN, "Hz", 0, 2500, 0.1);
99911bb5731Sbeveloper 	nullParam->AddOutput(param);
100011bb5731Sbeveloper 	param->AddInput(nullParam);
100111bb5731Sbeveloper 
100211bb5731Sbeveloper 	group = mainGroup->MakeGroup("Amplitude");
100311bb5731Sbeveloper 	nullParam = group->MakeNullParameter(GAIN_NULL_PARAM, B_MEDIA_NO_TYPE, "Amplitude", B_GENERIC);
100411bb5731Sbeveloper 	param = group->MakeContinuousParameter(GAIN_PARAM, B_MEDIA_NO_TYPE, "", B_GAIN, "", 0, 1, 0.01);
100511bb5731Sbeveloper 	nullParam->AddOutput(param);
100611bb5731Sbeveloper 	param->AddInput(nullParam);
100711bb5731Sbeveloper 
100811bb5731Sbeveloper 	group = mainGroup->MakeGroup("Waveform");
100911bb5731Sbeveloper 	nullParam = group->MakeNullParameter(WAVEFORM_NULL_PARAM, B_MEDIA_NO_TYPE, "Waveform", B_GENERIC);
101011bb5731Sbeveloper 	BDiscreteParameter* waveParam = group->MakeDiscreteParameter(WAVEFORM_PARAM, B_MEDIA_NO_TYPE, "", B_GENERIC);
101111bb5731Sbeveloper 	waveParam->AddItem(SINE_WAVE, "Sine wave");
101211bb5731Sbeveloper 	waveParam->AddItem(TRIANGLE_WAVE, "Triangle");
101311bb5731Sbeveloper 	waveParam->AddItem(SAWTOOTH_WAVE, "Sawtooth");
101411bb5731Sbeveloper 	nullParam->AddOutput(waveParam);
101511bb5731Sbeveloper 	waveParam->AddInput(nullParam);
101611bb5731Sbeveloper 
101711bb5731Sbeveloper 	return web;
101811bb5731Sbeveloper }
1019