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
2811bb5731Sbeveloper	static inline void FPRINTF(FILE*, const char*, ...) { }
2911bb5731Sbeveloper#endif
3011bb5731Sbeveloper
3111bb5731Sbeveloper// parameter web handling
3211bb5731Sbeveloperstatic BParameterWeb* make_parameter_web();
3311bb5731Sbeveloperconst int32 FREQUENCY_NULL_PARAM = 1;
3411bb5731Sbeveloperconst int32 FREQUENCY_PARAM = 2;
3511bb5731Sbeveloperconst int32 GAIN_NULL_PARAM = 11;
3611bb5731Sbeveloperconst int32 GAIN_PARAM = 12;
3711bb5731Sbeveloperconst int32 WAVEFORM_NULL_PARAM = 21;
3811bb5731Sbeveloperconst int32  WAVEFORM_PARAM = 22;
3911bb5731Sbeveloperconst int32 SINE_WAVE = 90;
4011bb5731Sbeveloperconst int32 TRIANGLE_WAVE = 91;
4111bb5731Sbeveloperconst int32 SAWTOOTH_WAVE = 92;
4211bb5731Sbeveloper
4311bb5731Sbeveloper// ----------------
4411bb5731Sbeveloper// ToneProducer implementation
4511bb5731Sbeveloper
4611bb5731SbeveloperToneProducer::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
73b1d006d7SDario Casalinuovo	// we'll use the consumer's preferred buffer size and framerate, if any
74b1d006d7SDario 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
9511bb5731SbeveloperToneProducer::~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
10811bb5731SbeveloperBMediaAddOn *
10911bb5731SbeveloperToneProducer::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
1227729bad4SAugustin Cavalierstatus_t
12311bb5731SbeveloperToneProducer::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
1647729bad4SAugustin Cavaliervoid
16511bb5731SbeveloperToneProducer::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]
19011bb5731Sbeveloperstatus_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	}
1977729bad4SAugustin Cavalier
19811bb5731Sbeveloper	return err;
19911bb5731Sbeveloper}
20011bb5731Sbeveloper
20111bb5731Sbeveloper//#pragma mark -
20211bb5731Sbeveloper
20311bb5731Sbeveloper// BBufferProducer methods
2047729bad4SAugustin Cavalierstatus_t
20511bb5731SbeveloperToneProducer::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
2297729bad4SAugustin Cavalierstatus_t
23011bb5731SbeveloperToneProducer::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.
2457729bad4SAugustin Cavalier
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
2567729bad4SAugustin Cavalierstatus_t
25711bb5731SbeveloperToneProducer::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
2657729bad4SAugustin Cavalierstatus_t
26611bb5731SbeveloperToneProducer::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
2827729bad4SAugustin Cavalierstatus_t
28311bb5731SbeveloperToneProducer::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
2917729bad4SAugustin Cavalierstatus_t
29211bb5731SbeveloperToneProducer::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
3267729bad4SAugustin Cavalierstatus_t
32711bb5731SbeveloperToneProducer::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
3367729bad4SAugustin Cavalierstatus_t
33711bb5731SbeveloperToneProducer::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	}
3697729bad4SAugustin Cavalier
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) {
378b1d006d7SDario 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	}
4027729bad4SAugustin Cavalier
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);
4087729bad4SAugustin Cavalier
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
4167729bad4SAugustin Cavaliervoid
41711bb5731SbeveloperToneProducer::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//	}
4437729bad4SAugustin Cavalier//
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);
4797729bad4SAugustin Cavalier
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
4907729bad4SAugustin Cavaliervoid
49111bb5731SbeveloperToneProducer::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
5117729bad4SAugustin Cavaliervoid
51211bb5731SbeveloperToneProducer::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
5557729bad4SAugustin Cavaliervoid
55611bb5731SbeveloperToneProducer::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
5707729bad4SAugustin Cavalierstatus_t
57111bb5731SbeveloperToneProducer::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
5807729bad4SAugustin Cavalierstatus_t
58111bb5731SbeveloperToneProducer::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
5947729bad4SAugustin Cavaliervoid
59511bb5731SbeveloperToneProducer::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
6037729bad4SAugustin Cavaliervoid
60411bb5731SbeveloperToneProducer::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
6277729bad4SAugustin Cavaliervoid
62811bb5731SbeveloperToneProducer::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
6447729bad4SAugustin Cavaliervoid
64511bb5731SbeveloperToneProducer::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
6597729bad4SAugustin Cavaliervoid
66011bb5731SbeveloperToneProducer::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
6747729bad4SAugustin Cavaliervoid
67511bb5731SbeveloperToneProducer::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
6877729bad4SAugustin Cavaliervoid
68811bb5731SbeveloperToneProducer::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
8167729bad4SAugustin Cavaliervoid
81711bb5731SbeveloperToneProducer::AllocateBuffers()
81811bb5731Sbeveloper{
81911bb5731Sbeveloper	FPRINTF(stderr, "ToneProducer::AllocateBuffers\n");
8207729bad4SAugustin Cavalier
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
83211bb5731SbeveloperBBuffer*
83311bb5731SbeveloperToneProducer::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
8477729bad4SAugustin Cavalier
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
90411bb5731Sbevelopervoid
90511bb5731SbeveloperToneProducer::FillSineBuffer(float *data, size_t numFrames, bool stereo)
90611bb5731Sbeveloper{
9077729bad4SAugustin Cavalier
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		}
9217729bad4SAugustin Cavalier
92211bb5731Sbeveloper		mTheta += dTheta;
92311bb5731Sbeveloper		if (mTheta > 2*M_PI)
92411bb5731Sbeveloper		{
92511bb5731Sbeveloper			mTheta -= 2*M_PI;
92611bb5731Sbeveloper		}
92711bb5731Sbeveloper	}
92811bb5731Sbeveloper}
92911bb5731Sbeveloper
93011bb5731Sbevelopervoid
93111bb5731SbeveloperToneProducer::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		}
9467729bad4SAugustin Cavalier
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
96311bb5731Sbevelopervoid
96411bb5731SbeveloperToneProducer::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
98911bb5731Sbeveloperstatic 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